133
fputs("Username is empty\n", stderr);
133
char *fetch_sig(char *pw_dir, int entry, char *alias) {
137
char **fetch_sig(char *pw_dir, char *alias, int mounting) {
134
138
/* Read ecryptfs signature from file and validate
135
139
* Return signature as a string, or NULL on failure
141
145
/* Construct sig file name */
143
asprintf(&sig_file, "%s/.ecryptfs/%s.sig", pw_dir,
146
if (asprintf(&sig_file, "%s/.ecryptfs/%s.sig", pw_dir, alias) < 0) {
146
147
perror("asprintf");
149
150
fh = fopen(sig_file, "r");
150
151
if (fh == NULL) {
154
if ((sig = (char *)malloc(KEY_BYTES*sizeof(char)+1)) == NULL) {
155
/* Read up to 2 lines from the file */
156
if ((sig = malloc(sizeof(char*) * 2)) == NULL) {
155
157
perror("malloc");
158
/* Move to the correct line in the file */
160
while ((c = fgetc(fh)) != EOF) {
167
/* Read KEY_BYTES characters from file */
168
while ((c = fgetc(fh)) != EOF && i < KEY_BYTES) {
169
if ((c>='0' && c<='9') || (c>='a' && c<='f') ||
170
(c>='A' && c<='F')) {
174
fputs("Invalid hex signature\n", stderr);
179
/* Check signature length */
180
if (i != KEY_BYTES) {
181
if (entry == 1 && i == 0) {
182
/* This means that we have no fnek sig; tis okay */
163
for (i=0; i<2; i++) {
164
if ((sig[i] = (char *)malloc(KEY_BYTES*sizeof(char)+2)) == NULL) {
168
memset(sig[i], '\0', KEY_BYTES+2);
169
/* Read KEY_BYTES characters from line */
170
if (fgets(sig[i], KEY_BYTES+2, fh) == NULL) {
172
fputs("Missing file encryption signature", stderr);
175
/* Filename encryption isn't in use */
181
/* Validate hex signature */
182
for (j=0; j<strlen(sig[i]); j++) {
183
if (isxdigit(sig[i][j]) == 0 && isspace(sig[i][j]) == 0) {
184
fputs("Invalid hex signature\n", stderr);
187
if (isspace(sig[i][j]) != 0) {
188
/* truncate at first whitespace */
192
if (strlen(sig[i]) > 0 && strlen(sig[i]) != KEY_BYTES) {
184
193
fputs("Invalid hex signature length\n", stderr);
196
/* Validate that signature is in the current keyring,
197
* compile with -lkeyutils
199
if (keyctl_search(KEY_SPEC_USER_KEYRING, "user", sig[i], 0) < 0) {
201
fputs("Signature not found in user keyring\n"
202
"Perhaps try the interactive "
203
"'ecryptfs-mount-private'\n", stderr);
188
sig[KEY_BYTES] = '\0';
189
/* Validate that signature is in the current keyring,
190
* compile with -lkeyutils
192
if (keyctl_search(KEY_SPEC_USER_KEYRING, "user", sig, 0) < 0) {
199
int check_ownerships(int uid, char *path) {
216
/* Clean up malloc'd memory if failure */
225
static int check_cwd_f_type()
228
* This is *not* a list of compatible lower filesystems list for
229
* eCryptfs. This is a list of filesystems that we reasonably expect to
230
* see mount.ecryptfs_private users mounting on top of. In other words,
231
* the filesystem type of the 'target' parameter of mount(2).
233
* This whitelist is to prevent malicious mount.ecryptfs_private users
234
* from mounting over filesystem types such as PROC_SUPER_MAGIC to
235
* deceive other programs with a crafted /proc/self/*. See
236
* https://launchpad.net/bugs/1530566 for more details.
238
__SWORD_TYPE f_type_whitelist[] = {
239
0x61756673 /* AUFS_SUPER_MAGIC */,
240
0x9123683E /* BTRFS_SUPER_MAGIC */,
241
0x00C36400 /* CEPH_SUPER_MAGIC */,
242
0xFF534D42 /* CIFS_MAGIC_NUMBER */,
243
0x0000F15F /* ECRYPTFS_SUPER_MAGIC */,
244
0x0000EF53 /* EXT[234]_SUPER_MAGIC */,
245
0xF2F52010 /* F2FS_SUPER_MAGIC */,
246
0x65735546 /* FUSE_SUPER_MAGIC */,
247
0x01161970 /* GFS2_MAGIC */,
248
0x3153464A /* JFS_SUPER_MAGIC */,
249
0x000072B6 /* JFFS2_SUPER_MAGIC */,
250
0x0000564C /* NCP_SUPER_MAGIC */,
251
0x00006969 /* NFS_SUPER_MAGIC */,
252
0x00003434 /* NILFS_SUPER_MAGIC */,
253
0x5346544E /* NTFS_SB_MAGIC */,
254
0x794C7630 /* OVERLAYFS_SUPER_MAGIC */,
255
0x52654973 /* REISERFS_SUPER_MAGIC */,
256
0x73717368 /* SQUASHFS_MAGIC */,
257
0x01021994 /* TMPFS_MAGIC */,
258
0x24051905 /* UBIFS_SUPER_MAGIC */,
259
0x58465342 /* XFS_SB_MAGIC */,
260
0x2FC12FC1 /* ZFS_SUPER_MAGIC */,
263
size_t i, whitelist_len;
265
if (statfs(".", &buf) != 0) {
266
fprintf(stderr, "Failed to check filesystem type: %m\n");
270
whitelist_len = sizeof(f_type_whitelist) / sizeof(*f_type_whitelist);
271
for (i = 0; i < whitelist_len; i++) {
272
if (buf.f_type == f_type_whitelist[i]) {
278
"Refusing to mount over an unapproved filesystem type: %#lx\n",
283
int check_ownership_mnt(uid_t uid, char **mnt) {
284
/* Check ownership of mount point, chdir into it, and
285
* canonicalize the path for use in mtab updating.
286
* Return 0 if everything is in order, 1 on error.
291
/* From here on, we'll refer to "." as our mountpoint, to avoid
294
if (chdir(*mnt) != 0) {
295
fputs("Cannot chdir into mountpoint.\n", stderr);
298
if (stat(".", &s) != 0) {
299
fputs("Cannot examine mountpoint.\n", stderr);
302
if (!S_ISDIR(s.st_mode)) {
303
fputs("Mountpoint is not a directory.\n", stderr);
306
if (s.st_uid != uid) {
307
fputs("You do not own that mountpoint.\n", stderr);
311
/* Canonicalize our pathname based on the current directory to
314
cwd = getcwd(NULL, 0);
316
fputs("Failed to get current directory\n", stderr);
324
int check_ownerships(uid_t uid, char *path) {
200
325
/* Check ownership of device and mount point.
201
326
* Return 0 if everything is in order, 1 on error.
220
345
int update_mtab(char *dev, char *mnt, char *opt) {
221
/* Update /etc/mtab with new mount entry.
346
/* Update /etc/mtab with new mount entry unless it is a symbolic link
222
347
* Return 0 on success, 1 on failure.
226
fh = setmntent("/etc/mtab", "a");
229
/* Unmount if mtab cannot be updated */
240
if (addmntent(fh, &m) != 0) {
351
/* Check if mtab is a symlink */
352
useMtab = (readlink("/etc/mtab", &dummy, 1) < 0);
354
/* No need updating mtab */
359
FILE *old_mtab, *new_mtab;
360
struct mntent *old_ent, new_ent;
363
/* Make an attempt to play nice with other mount helpers
364
* by creating an /etc/mtab~ lock file. Of course this
365
* only works if those other helpers actually check for
368
old_umask = umask(033);
369
fd = open("/etc/mtab~", O_RDONLY | O_CREAT | O_EXCL, 0644);
376
old_mtab = setmntent("/etc/mtab", "r");
377
if (old_mtab == NULL) {
382
new_mtab = setmntent("/etc/mtab.tmp", "w");
383
if (new_mtab == NULL) {
388
while ((old_ent = getmntent(old_mtab))) {
389
if (addmntent(new_mtab, old_ent) != 0) {
396
new_ent.mnt_fsname = dev;
397
new_ent.mnt_dir = mnt;
398
new_ent.mnt_type = FSTYPE;
399
new_ent.mnt_opts = opt;
400
new_ent.mnt_freq = 0;
401
new_ent.mnt_passno = 0;
403
if (addmntent(new_mtab, &new_ent) != 0) {
241
404
perror("addmntent");
243
/* Unmount if mtab cannot be updated */
408
if (fchmod(fileno(new_mtab), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
414
if (rename("/etc/mtab.tmp", "/etc/mtab") < 0) {
419
unlink("/etc/mtab~");
428
unlink("/etc/mtab.tmp");
431
unlink("/etc/mtab~");
251
FILE *lock_counter(char *u, int uid, char *alias) {
436
FILE *lock_counter(char *u, uid_t uid, char *alias) {
344
532
return bump_counter(fh, -MAXINT+1);
535
/* Returns -1 on error, 0 on success, and 1 if the program should exit with 0 */
536
static int parse_options(int argc, char *argv[], int *mounting, int *force,
537
int *nonzero_decrement_is_error, char **alias)
539
const char *optstr, *usagestr;
540
int opt, usage = 0, rc = -1;
543
*nonzero_decrement_is_error = 1;
546
/* Determine if mounting or unmounting by looking at the invocation */
547
if (strstr(argv[0], "umount") == NULL) {
550
usagestr = "[ALIAS]\n"
551
"Mount the default private directory or ALIAS, if specified.\n"
553
" -h display this help and exit\n";
557
usagestr = "[-f] [-d] [ALIAS]\n"
558
"Unmount the default private directory or ALIAS, if specified.\n"
560
" -h display this help and exit\n"
561
" -f forcibly unmount\n"
562
" -d don't treat a non-zero session counter as an error\n";
565
while ((opt = getopt(argc, argv, optstr)) != -1) {
575
*nonzero_decrement_is_error = 0;
583
if (optind < (argc - 1)) {
586
} else if (optind == (argc - 1)) {
587
*alias = argv[optind];
593
fprintf(stderr, "Usage: %s %s", argv[0], usagestr);
348
597
/* This program is a setuid-executable allowing a non-privileged user to mount
349
598
* and unmount an ecryptfs private directory. This program is necessary to
415
677
perror("asprintf (dest)");
418
} else if (argc == 2) {
420
681
/* Read the source and destination dirs from .conf file */
421
682
if (read_config(pwd->pw_dir, uid, alias, &src, &dest, &opts2) < 0) {
422
fputs("Error reading configuration file", stderr);
683
fputs("Error reading configuration file\n", stderr);
425
686
if (opts2 != NULL && strlen(opts2) != 0 && strcmp(opts2, "none") != 0) {
426
fputs("Mount options are not supported here", stderr);
687
fputs("Mount options are not supported here\n", stderr);
430
fputs("Too many arguments", stderr);
692
if (strstr(alias, "..")) {
693
fputs("Invalid alias", stderr);
434
697
/* Lock the counter through the rest of the program */
435
698
fh_counter = lock_counter(pwd->pw_name, uid, alias);
436
699
if (fh_counter == NULL) {
437
fputs("Error locking counter", stderr);
700
fputs("Error locking counter\n", stderr);
448
/* Determine if mounting or unmounting by looking at the invocation */
449
if (strstr(argv[0], "umount") == NULL) {
453
/* Determine if unmounting is forced */
454
if (argv[1] != NULL && strncmp(argv[1], "-f", 2) == 0) {
461
711
/* Fetch signatures from file */
462
712
/* First line is the file content encryption key signature */
463
sig = fetch_sig(pwd->pw_dir, 0, alias);
713
/* Second line, if present, is the filename encryption key signature */
714
sigs = fetch_sig(pwd->pw_dir, alias, mounting);
715
if (!sigs && mounting) {
465
716
/* if umounting, no sig is ok */
468
perror("keyctl_search");
469
fputs("Perhaps try the interactive 'ecryptfs-mount-private'\n",
474
/* Second line, if present, is the filename encryption key signature */
475
sig_fnek = fetch_sig(pwd->pw_dir, 1, alias);
477
723
/* Build mount options */
479
(asprintf(&opt, "ecryptfs_cipher=%s,ecryptfs_key_bytes=%d,ecryptfs_unlink_sigs%s%s%s%s",
725
(asprintf(&opt, "ecryptfs_check_dev_ruid,ecryptfs_cipher=%s,ecryptfs_key_bytes=%d,ecryptfs_unlink_sigs%s%s%s%s",
482
sig ? ",ecryptfs_sig=" : "",
728
sig_fekek ? ",ecryptfs_sig=" : "",
729
sig_fekek ? sig_fekek : "",
484
730
sig_fnek ? ",ecryptfs_fnek_sig=" : "",
485
731
sig_fnek ? sig_fnek : ""