Initial release for Tizen
[platform/upstream/ecryptfs-utils.git] / src / utils / mount.ecryptfs_private.c
1 /*
2  * This is an ecryptfs private directory mount/unmount helper program
3  * for non-root users.
4  *
5  * Copyright (C) 2008 Canonical Ltd.
6  *
7  * This code was originally written by Dustin Kirkland <kirkland@ubuntu.com>.
8  * Enhanced by Serge Hallyn <hallyn@ubuntu.com>.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License along
21  * with this program; if not, see <http://www.gnu.org/licenses/>.
22  *
23  * On Debian-based systems, the complete text of the GNU General Public
24  * License can be found in /usr/share/common-licenses/GPL-2
25  *
26  */
27
28 #include <sys/file.h>
29 #include <sys/mount.h>
30 #include <sys/param.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <ctype.h>
34 #include <errno.h>
35 #include <keyutils.h>
36 #include <mntent.h>
37 #include <pwd.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <syslog.h>
43 #include <values.h>
44 #include "../include/ecryptfs.h"
45
46 /* Perhaps a future version of this program will allow these to be configurable
47  * by the system administrator (or user?) at run time.  For now, these are set
48  * to reasonable values to reduce the burden of input validation.
49  */
50 #define KEY_BYTES 16
51 #define KEY_CIPHER "aes"
52 #define FSTYPE "ecryptfs"
53 #define TMP "/dev/shm"
54
55 int read_config(char *pw_dir, uid_t uid, char *alias, char **s, char **d, char **o) {
56 /* Read an fstab(5) style config file */
57         char *fnam;
58         struct stat mstat;
59         struct mntent *e;
60         FILE *fin;
61         if (asprintf(&fnam, "%s/.ecryptfs/%s.conf", pw_dir, alias) < 0) {
62                 perror("asprintf");
63                 return -1;
64         }
65         if (stat(fnam, &mstat)!=0 || (!S_ISREG(mstat.st_mode) || mstat.st_uid!=uid)) {
66                 fputs("Bad file\n", stderr);
67                 free(fnam);
68                 return -1;
69         }
70         fin = fopen(fnam, "r");
71         free(fnam);
72         if (!fin) {
73                 perror("fopen");
74                 return -1;
75         }
76         e = getmntent(fin);
77         fclose(fin);
78         if (!e) {
79                 perror("getmntent");
80                 return -1;
81         }
82         if (strcmp(e->mnt_type, "ecryptfs") != 0) {
83                 fputs("Bad fs type\n", stderr);
84                 return -1;
85         }
86         *o = strdup(e->mnt_opts);
87         if (!*o)
88                 return -2;
89         *d = strdup(e->mnt_dir);
90         if (!*d)
91                 return -2;
92         *s = strdup(e->mnt_fsname);
93         if (!*s)
94                 return -2;
95
96         return 0;
97 }
98
99 int check_username(char *u) {
100 /* We follow the username guidelines used by the adduser program.  Quoting its
101  * error message:
102  *   adduser: To avoid problems, the username should consist only of
103  *   letters, digits, underscores, periods, at signs and dashes, and not start
104  *   with a dash (as defined by IEEE Std 1003.1-2001). For compatibility with
105  *   Samba machine accounts $ is also supported at the end of the username
106  */
107         int i;
108         char c;
109         int len;
110
111         if (u == NULL)
112                 goto empty;
113         len = strlen(u);
114         if (len == 0)
115                 goto empty;
116
117         for (i=0; i<len; i++) {
118                 c = u[i];
119                 if (    !(c>='a' && c<='z') && !(c>='A' && c<='Z') &&
120                         !(c>='0' && c<='9') &&
121                         !(c=='_') && !(c=='.') && !(c=='@') &&
122                         !(c=='-' && i!=0) &&
123                         !(c=='$' && i==(len-1))
124                 ) {
125                         fputs("Username has unsupported characters\n", stderr);
126                         return 1;
127                 }
128         }
129         return 0;
130 empty:
131         fputs("Username is empty\n", stderr);
132         return 1;
133 }
134
135 char **fetch_sig(char *pw_dir, char *alias, int mounting) {
136 /* Read ecryptfs signature from file and validate
137  * Return signature as a string, or NULL on failure
138  */
139         char *sig_file;
140         FILE *fh = NULL;
141         char **sig = NULL;
142         unsigned int i, j;
143         /* Construct sig file name */
144         if (asprintf(&sig_file, "%s/.ecryptfs/%s.sig", pw_dir, alias) < 0) {
145                 perror("asprintf");
146                 goto err;
147         }
148         fh = fopen(sig_file, "r");
149         if (fh == NULL) {
150                 perror("fopen");
151                 goto err;
152         }
153         /* Read up to 2 lines from the file */
154         if ((sig = malloc(sizeof(char*) * 2)) == NULL) {
155                 perror("malloc");
156                 goto err;
157         }
158
159         sig[0] = NULL;
160         sig[1] = NULL;
161         for (i=0; i<2; i++) {
162                 if ((sig[i] = (char *)malloc(KEY_BYTES*sizeof(char)+2)) == NULL) {
163                         perror("malloc");
164                         goto err;
165                 }
166                 memset(sig[i], '\0', KEY_BYTES+2);
167                 /* Read KEY_BYTES characters from line */
168                 if (fgets(sig[i], KEY_BYTES+2, fh) == NULL) {
169                         if (i == 0) {
170                                 fputs("Missing file encryption signature", stderr);
171                                 goto err;
172                         } else {
173                                 /* Filename encryption isn't in use */
174                                 free(sig[i]);
175                                 sig[i] = NULL;
176                                 goto out;
177                         }
178                 }
179                 /* Validate hex signature */
180                 for (j=0; j<strlen(sig[i]); j++) {
181                         if (isxdigit(sig[i][j]) == 0 && isspace(sig[i][j]) == 0) {
182                                 fputs("Invalid hex signature\n", stderr);
183                                 goto err;
184                         }
185                         if (isspace(sig[i][j]) != 0) {
186                                 /* truncate at first whitespace */
187                                 sig[i][j] = '\0';
188                         }
189                 }
190                 if (strlen(sig[i]) > 0 && strlen(sig[i]) != KEY_BYTES) {
191                         fputs("Invalid hex signature length\n", stderr);
192                         goto err;
193                 }
194                 /* Validate that signature is in the current keyring,
195                  * compile with -lkeyutils
196                  */
197                 if (keyctl_search(KEY_SPEC_USER_KEYRING, "user", sig[i], 0) < 0) {
198                         if (mounting)
199                                 fputs("Signature not found in user keyring\n"
200                                       "Perhaps try the interactive "
201                                       "'ecryptfs-mount-private'\n", stderr);
202                         goto err;
203                 }
204         }
205 out:
206         if (fh != NULL) {
207                 fclose(fh);
208         }
209         return sig;
210 err:
211         if (fh) {
212                 fclose(fh);
213         }
214         /* Clean up malloc'd memory if failure */
215         if (sig) {
216                 free(sig[0]);
217                 free(sig[1]);
218                 free(sig);
219         }
220         return NULL;
221 }
222
223 int check_ownership_mnt(uid_t uid, char **mnt) {
224 /* Check ownership of mount point, chdir into it, and
225  * canonicalize the path for use in mtab updating.
226  * Return 0 if everything is in order, 1 on error.
227  */
228         struct stat s;
229         char *cwd;
230
231         /* From here on, we'll refer to "." as our mountpoint, to avoid
232          * races.
233          */
234         if (chdir(*mnt) != 0) {
235                 fputs("Cannot chdir into mountpoint.\n", stderr);
236                 return 1;
237         }
238         if (stat(".", &s) != 0) {
239                 fputs("Cannot examine mountpoint.\n", stderr);
240                 return 1;
241         }
242         if (!S_ISDIR(s.st_mode)) {
243                 fputs("Mountpoint is not a directory.\n", stderr);
244                 return 1;
245         }
246         if (s.st_uid != uid) {
247                 fputs("You do not own that mountpoint.\n", stderr);
248                 return 1;
249         }
250
251         /* Canonicalize our pathname based on the current directory to
252          * avoid races.
253          */
254         cwd = getcwd(NULL, 0);
255         if (!cwd) {
256                 fputs("Failed to get current directory\n", stderr);
257                 return 1;
258         }
259         *mnt = cwd;
260         return 0;
261 }
262
263
264 int check_ownerships(uid_t uid, char *path) {
265 /* Check ownership of device and mount point.
266  * Return 0 if everything is in order, 1 on error.
267  */
268         struct stat s;
269         if (stat(path, &s) != 0) {
270                 fputs("Cannot examine encrypted directory\n", stderr);
271                 return 1;
272         }
273         if (!S_ISDIR(s.st_mode)) {
274                 fputs("Device or mountpoint is not a directory\n", stderr);
275                 return 1;
276         }
277         if (s.st_uid != uid) {
278                 fputs("You do not own that encrypted directory\n", stderr);
279                 return 1;
280         }
281         return 0;
282 }
283
284
285 int update_mtab(char *dev, char *mnt, char *opt) {
286 /* Update /etc/mtab with new mount entry unless it is a symbolic link
287  * Return 0 on success, 1 on failure.
288  */
289         char dummy;
290         int useMtab;
291         /* Check if mtab is a symlink */
292         useMtab = (readlink("/etc/mtab", &dummy, 1) < 0);
293         if (!useMtab) {
294                 /* No need updating mtab */
295                 return 0;
296         }
297
298         int fd;
299         FILE *old_mtab, *new_mtab;
300         struct mntent *old_ent, new_ent;
301         mode_t old_umask;
302
303         /* Make an attempt to play nice with other mount helpers
304          * by creating an /etc/mtab~ lock file. Of course this
305          * only works if those other helpers actually check for
306          * this.
307          */
308         old_umask = umask(033);
309         fd = open("/etc/mtab~", O_RDONLY | O_CREAT | O_EXCL, 0644);
310         if (fd < 0) {
311                 perror("open");
312                 return 1;
313         }
314         close(fd);
315
316         old_mtab = setmntent("/etc/mtab", "r");
317         if (old_mtab == NULL) {
318                 perror("setmntent");
319                 return 1;
320         }
321
322         new_mtab = setmntent("/etc/mtab.tmp", "w");
323         if (new_mtab == NULL) {
324                 perror("setmntent");
325                 goto fail_early;
326         }
327
328         while ((old_ent = getmntent(old_mtab))) {
329                 if (addmntent(new_mtab, old_ent) != 0) {
330                         perror("addmntent");
331                         goto fail;
332                 }
333         }
334         endmntent(old_mtab);
335
336         new_ent.mnt_fsname = dev;
337         new_ent.mnt_dir = mnt;
338         new_ent.mnt_type = FSTYPE;
339         new_ent.mnt_opts = opt;
340         new_ent.mnt_freq = 0;
341         new_ent.mnt_passno = 0;
342
343         if (addmntent(new_mtab, &new_ent) != 0) {
344                 perror("addmntent");
345                 goto fail;
346         }
347
348         if (fchmod(fileno(new_mtab), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) < 0) {
349                 perror("fchmod");
350                 goto fail;
351         }
352         endmntent(new_mtab);
353
354         if (rename("/etc/mtab.tmp", "/etc/mtab") < 0) {
355                 perror("rename");
356                 goto fail_late;
357         }
358
359         unlink("/etc/mtab~");
360
361         umask(old_umask);
362
363         return 0;
364
365 fail:
366         endmntent(new_mtab);
367 fail_late:
368         unlink("/etc/mtab.tmp");
369 fail_early:
370         endmntent(old_mtab);
371         unlink("/etc/mtab~");
372         umask(old_umask);
373         return 1;
374 }
375
376 FILE *lock_counter(char *u, uid_t uid, char *alias) {
377         char *f;
378         int fd;
379         FILE *fh;
380         struct stat s;
381         int i = 1;
382         /* We expect TMP to exist, be writeable by the user,
383          * and to be cleared on boot */
384         if (asprintf(&f, "%s/%s-%s-%s", TMP, FSTYPE, u, alias) < 0) {
385                 perror("asprintf");
386                 return NULL;
387         }
388         /* If the counter path exists, and it's either not a regular
389          * file, or it's not owned by the current user, append iterator
390          * until we find a filename we can use.
391          */
392         while (i < 50) {
393                 if (((fd = open(f, O_RDWR | O_CREAT | O_NOFOLLOW, 0600)) >= 0) &&
394                     (fstat(fd, &s)==0 && (S_ISREG(s.st_mode) && s.st_uid==uid))) {
395                         break;
396                 } else {
397                         if (fd >= 0) {
398                                 close(fd);
399                                 fd = -1;
400                         }
401                         free(f);
402                         if (asprintf(&f, "%s/%s-%s-%s-%d", TMP, FSTYPE, u,
403                             alias, i++) < 0) {
404                                 perror("asprintf");
405                                 return NULL;
406                         }
407                 }
408         }
409
410         if (fd < 0) {
411                 perror("open");
412                 return NULL;
413         }
414
415         flock(fd, LOCK_EX);
416         fh = fdopen(fd, "r+");
417         if (fh == NULL) {
418                 perror("fopen");
419                 close(fd);
420                 return NULL;
421         }
422         return fh;
423 }
424
425 void unlock_counter(FILE *fh) {
426         if (fh != NULL) {
427                 /* This should remove the lock too */
428                 fclose(fh);
429         }
430 }
431
432 int bump_counter(FILE *fh, int delta) {
433 /* Maintain a mount counter
434  *   increment on delta = 1
435  *   decrement on delta = -1
436  *   remove the counter file on delta = 0
437  *   return the updated count, negative on error
438  */
439         int count;
440         /* Read the count from file, default to 0 */
441         rewind(fh);
442         if (fscanf(fh, "%d\n", &count) != 1) {
443                 count = 0;
444         }
445         /* Increment/decrement the counter */
446         count += delta;
447         if (count < 0) {
448                 /* Never set a count less than 0 */
449                 count = 0;
450         }
451         /* Write the count to file */
452         rewind(fh);
453         fprintf(fh, "%d\n", count);
454         fflush(fh);
455         return count;
456 }
457
458
459 int increment(FILE *fh) {
460 /* Bump counter up */
461         return bump_counter(fh, 1);
462 }
463
464
465 int decrement(FILE *fh) {
466 /* Bump counter down */
467         return bump_counter(fh, -1);
468 }
469
470 int zero(FILE *fh) {
471 /* Zero the counter file */
472         return bump_counter(fh, -MAXINT+1);
473 }
474
475
476 /* This program is a setuid-executable allowing a non-privileged user to mount
477  * and unmount an ecryptfs private directory.  This program is necessary to
478  * keep from adding such entries to /etc/fstab.
479  *
480  * A single executable is created and hardlinked to two different names.
481  * The mode of operation (mounting|unmounting) is determined by examining
482  * the name of the executable.  "Mounting" mode is assumed, unless the
483  * executable contains the string "umount".
484  * Example:
485  *   /sbin/mount.ecryptfs_private
486  *   /sbin/umount.ecryptfs_private
487  *
488  * At the moment, this program:
489  *  - mounts ~/.Private onto ~/Private
490  *    - as an ecryptfs filesystem
491  *    - using the AES cipher
492  *    - with a key length of 16 bytes
493  *    - and using the signature defined in ~/.ecryptfs/Private.sig
494  *    - ONLY IF the user
495  *      - has the signature's key in his keyring
496  *      - owns both ~/.Private and ~/Private
497  *      - is not already mounted
498  *  - unmounts ~/.Private from ~/Private
499  *    - using the signature defined in ~/.ecryptfs/Private.sig
500  *    - ONLY IF the user
501  *      - owns both ~/.Private and ~/Private
502  *      - is currently ecryptfs mounted
503  *
504  * The only setuid operations in this program are:
505  *  a) mounting
506  *  b) unmounting
507  *  c) updating /etc/mtab
508  */
509 int main(int argc, char *argv[]) {
510         uid_t uid;
511         gid_t gid;
512         int mounting;
513         int force = 0;
514         struct passwd *pwd;
515         char *alias, *src, *dest, *opt, *opts2;
516         char *sig_fekek = NULL, *sig_fnek = NULL;
517         char **sigs;
518         FILE *fh_counter = NULL;
519
520         uid = getuid();
521         gid = getgid();
522         /* Non-privileged effective uid is sufficient for all but the code
523          * that mounts, unmounts, and updates /etc/mtab.
524          * Run at a lower privilege until we need it.
525          */
526         if (seteuid(uid)<0 || geteuid()!=uid) {
527                 perror("setuid");
528                 goto fail;
529         }
530         if ((pwd = getpwuid(uid)) == NULL) {
531                 perror("getpwuid");
532                 goto fail;
533         }
534
535         /* If no arguments, default to private dir; but accept at most one
536            argument, an alias for the configuration to read and use.
537          */
538         if (argc == 1) {
539                 /* Use default source and destination dirs */
540                 alias = ECRYPTFS_PRIVATE_DIR;
541                 if ((asprintf(&src, "%s/.%s", pwd->pw_dir, alias) < 0) || src == NULL) {
542                         perror("asprintf (src)");
543                         goto fail;
544                 }
545                 dest = ecryptfs_fetch_private_mnt(pwd->pw_dir);
546                 if (dest == NULL) {
547                         perror("asprintf (dest)");
548                         goto fail;
549                 }
550         } else if (argc == 2) {
551                 alias = argv[1];
552                 /* Read the source and destination dirs from .conf file */
553                 if (read_config(pwd->pw_dir, uid, alias, &src, &dest, &opts2) < 0) {
554                         fputs("Error reading configuration file\n", stderr);
555                         exit(1);
556                 }
557                 if (opts2 != NULL && strlen(opts2) != 0 && strcmp(opts2, "none") != 0) {
558                         fputs("Mount options are not supported here\n", stderr);
559                         exit(1);
560                 }
561         } else {
562                 fputs("Too many arguments\n", stderr);
563                 exit(1);
564         }
565
566         if (strstr(alias, "..")) {
567                 fputs("Invalid alias", stderr);
568                 exit(1);
569         }
570
571         /* Lock the counter through the rest of the program */
572         fh_counter = lock_counter(pwd->pw_name, uid, alias);
573         if (fh_counter == NULL) {
574                 fputs("Error locking counter\n", stderr);
575                 goto fail;
576         }
577
578         if (check_username(pwd->pw_name) != 0) {
579                 /* Must protect against a crafted user=john,suid from entering
580                  * filesystem options
581                  */
582                 goto fail;
583         }
584
585         /* Determine if mounting or unmounting by looking at the invocation */
586         if (strstr(argv[0], "umount") == NULL) {
587                 mounting = 1;
588         } else {
589                 mounting = 0;
590                 /* Determine if unmounting is forced */
591                 if (argv[1] != NULL && strncmp(argv[1], "-f", 2) == 0) {
592                         force = 1;
593                 } else {
594                         force = 0;
595                 }
596         }
597
598         /* Fetch signatures from file */
599         /* First line is the file content encryption key signature */
600         /* Second line, if present, is the filename encryption key signature */
601         sigs = fetch_sig(pwd->pw_dir, alias, mounting);
602         if (!sigs && mounting) {
603                 /* if umounting, no sig is ok */
604                 goto fail;
605         } else if (sigs) {
606                 sig_fekek = sigs[0];
607                 sig_fnek = sigs[1];
608         }
609
610         /* Build mount options */
611         if (
612             (asprintf(&opt, "ecryptfs_check_dev_ruid,ecryptfs_cipher=%s,ecryptfs_key_bytes=%d,ecryptfs_unlink_sigs%s%s%s%s",
613                       KEY_CIPHER,
614                       KEY_BYTES,
615                       sig_fekek ? ",ecryptfs_sig=" : "",
616                       sig_fekek ? sig_fekek : "",
617                       sig_fnek ? ",ecryptfs_fnek_sig=" : "",
618                       sig_fnek ? sig_fnek : ""
619                      ) < 0
620             ) || opt == NULL
621            ) {
622                 perror("asprintf (opt)");
623                 goto fail;
624         }
625
626         /* Check ownership of the mountpoint. From here on, dest refers
627          * to a canonicalized path, and the mountpoint is the cwd. */
628         if (check_ownership_mnt(uid, &dest) != 0) {
629                 goto fail;
630         }
631
632         if (mounting == 1) {
633                 /* Increment mount counter, errors non-fatal */
634                 if (increment(fh_counter) < 0) {
635                         fputs("Error incrementing mount counter\n", stderr);
636                 }
637                 /* Mounting, so exit if already mounted */
638                 if (ecryptfs_private_is_mounted(src, dest, sig_fekek, mounting) == 1) {
639                         goto success;
640                 }
641                 /* We must maintain our real uid as the user who called this
642                  * program in order to have access to their kernel keyring.
643                  * Even though root has the power to mount, only a user with
644                  * the correct key in their keyring can mount an ecryptfs
645                  * directory correctly.
646                  * Root does not necessarily have the user's key, so we need
647                  * the real uid to be that of the user.
648                  * And we need the effective uid to be root in order to mount.
649                  */
650                 if (setreuid(-1, 0) < 0) {
651                         perror("setreuid");
652                         goto fail;
653                 }
654                 if (setregid(-1, 0) < 0) {
655                         perror("setregid");
656                         goto fail;
657                 }
658                 /* Perform mount */
659                 if (mount(src, ".", FSTYPE, MS_NOSUID | MS_NODEV, opt) == 0) {
660                         if (update_mtab(src, dest, opt) != 0) {
661                                 goto fail;
662                         }
663                 } else {
664                         perror("mount");
665                         /* Drop privileges since the mount did not succeed */
666                         if (setreuid(uid, uid) < 0) {
667                                 perror("setreuid");
668                         }
669                         if (setregid(gid, gid) < 0) {
670                                 perror("setregid");
671                         }
672                         goto fail;
673                 }
674         } else {
675                 int rc = 0;
676                 /* Decrement counter, exiting if >0, and non-forced unmount */
677                 if (force == 1) {
678                         zero(fh_counter);
679                 } else if (decrement(fh_counter) > 0) {
680                         fputs("Sessions still open, not unmounting\n", stderr);
681                         goto fail;
682                 }
683                 /* Attempt to clear the user's keys from the keyring,
684                    to prevent root from mounting without the user's key.
685                    This is a best-effort basis, so we'll just print messages
686                    on error. */
687                 if (sig_fekek != NULL) {
688                         rc = ecryptfs_remove_auth_tok_from_keyring(sig_fekek);
689                         if (rc != 0 && rc != ENOKEY)
690                                 fputs("Could not remove key from keyring, try 'ecryptfs-umount-private'\n", stderr);
691                 }
692                 if (sig_fnek != NULL) {
693                         rc = ecryptfs_remove_auth_tok_from_keyring(sig_fnek);
694                         if (rc != 0 && rc != ENOKEY)
695                                 fputs("Could not remove key from keyring, try 'ecryptfs-umount-private'\n", stderr);
696                 }
697                 /* Unmounting, so exit if not mounted */
698                 if (ecryptfs_private_is_mounted(src, dest, sig_fekek, mounting) == 0) {
699                         goto fail;
700                 }
701                 /* The key is not needed for unmounting, so we set res=0.
702                  * Perform umount by calling umount utility.  This execl will
703                  * update mtab for us, and replace the current process.
704                  * Do not use the umount.ecryptfs helper (-i).
705                  */
706                 setresuid(0,0,0);
707                 setresgid(0,0,0);
708                 clearenv();
709
710                 /* Since we're doing a lazy unmount anyway, just unmount the current
711                  * directory. This avoids a lot of complexity in dealing with race
712                  * conditions, and guarantees that we're only unmounting a filesystem
713                  * that we own. */
714                 execl("/bin/umount", "umount", "-i", "-l", ".", NULL);
715                 perror("execl unmount failed");
716                 goto fail;
717         }
718 success:
719         unlock_counter(fh_counter);
720         return 0;
721 fail:
722         unlock_counter(fh_counter);
723         return 1;
724 }