1 /*
2     FUSE: Filesystem in Userspace
3     Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
4 
5     This program can be distributed under the terms of the GNU GPL.
6     See the file COPYING.
7 */
8 /* This program does the mounting and unmounting of FUSE filesystems */
9 
10 #include <config.h>
11 
12 #include "mount_util.h"
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <unistd.h>
18 #include <getopt.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <pwd.h>
22 #include <paths.h>
23 
24 #ifdef __SOLARIS__
25 #include <sys/mnttab.h>
26 #else /* __SOLARIS__ */
27 #include <grp.h>
28 #include <mntent.h>
29 #include <sys/fsuid.h>
30 #endif /* __SOLARIS__ */
31 
32 #include <sys/wait.h>
33 #include <sys/stat.h>
34 #include <sys/mount.h>
35 #include <sys/socket.h>
36 #include <sys/utsname.h>
37 
38 #define FUSE_DEV_NEW "/dev/fuse"
39 
40 #ifndef MS_DIRSYNC
41 #define MS_DIRSYNC 128
42 #endif
43 
44 static const char *progname = "ntfs-3g-mount";
45 
46 static int mount_max = 1000;
47 
48 int drop_privs(void);
49 int restore_privs(void);
50 
51 #ifdef __SOLARIS__
52 
53 /*
54  * fusermount is not implemented in fuse-lite for Solaris,
55  * only the minimal functions are provided.
56  */
57 
58 /*
59  * Solaris doesn't have setfsuid/setfsgid.
60  * This doesn't really matter anyway as this program shouldn't be made
61  * suid on Solaris. It should instead be used via a profile with the
62  * sys_mount privilege.
63  */
64 
drop_privs(void)65 int drop_privs(void)
66 {
67     return (0);
68 }
69 
restore_privs(void)70 int restore_privs(void)
71 {
72     return (0);
73 }
74 
75 #else /* __SOLARIS__ */
76 
get_user_name(void)77 static const char *get_user_name(void)
78 {
79     struct passwd *pw = getpwuid(getuid());
80     if (pw != NULL && pw->pw_name != NULL)
81         return pw->pw_name;
82     else {
83         fprintf(stderr, "%s: could not determine username\n", progname);
84         return NULL;
85     }
86 }
87 
drop_privs(void)88 int drop_privs(void)
89 {
90 	if (!getegid()) {
91 
92 		gid_t new_gid = getgid();
93 
94 		if (setresgid(-1, new_gid, getegid()) < 0) {
95 			perror("priv drop: setresgid failed");
96 			return -1;
97 		}
98 		if (getegid() != new_gid){
99 			perror("dropping group privilege failed");
100 			return -1;
101 		}
102 	}
103 
104 	if (!geteuid()) {
105 
106 		uid_t new_uid = getuid();
107 
108 		if (setresuid(-1, new_uid, geteuid()) < 0) {
109 			perror("priv drop: setresuid failed");
110 			return -1;
111 		}
112 		if (geteuid() != new_uid){
113 			perror("dropping user privilege failed");
114 			return -1;
115 		}
116 	}
117 
118 	return 0;
119 }
120 
restore_privs(void)121 int restore_privs(void)
122 {
123 	if (geteuid()) {
124 
125 		uid_t ruid, euid, suid;
126 
127 		if (getresuid(&ruid, &euid, &suid) < 0) {
128 			perror("priv restore: getresuid failed");
129 			return -1;
130 		}
131 		if (setresuid(-1, suid, -1) < 0) {
132 			perror("priv restore: setresuid failed");
133 			return -1;
134 		}
135 		if (geteuid() != suid) {
136 			perror("restoring privilege failed");
137 			return -1;
138 		}
139 	}
140 
141 	if (getegid()) {
142 
143 		gid_t rgid, egid, sgid;
144 
145 		if (getresgid(&rgid, &egid, &sgid) < 0) {
146 			perror("priv restore: getresgid failed");
147 			return -1;
148 		}
149 		if (setresgid(-1, sgid, -1) < 0) {
150 			perror("priv restore: setresgid failed");
151 			return -1;
152 		}
153 		if (getegid() != sgid){
154 			perror("restoring group privilege failed");
155 			return -1;
156 		}
157 	}
158 
159 	return 0;
160 }
161 
162 #ifndef IGNORE_MTAB
add_mount(const char * source,const char * mnt,const char * type,const char * opts)163 static int add_mount(const char *source, const char *mnt, const char *type,
164                      const char *opts)
165 {
166     return fuse_mnt_add_mount(progname, source, mnt, type, opts);
167 }
168 
count_fuse_fs(void)169 static int count_fuse_fs(void)
170 {
171     struct mntent *entp;
172     int count = 0;
173     const char *mtab = _PATH_MOUNTED;
174     FILE *fp = setmntent(mtab, "r");
175     if (fp == NULL) {
176         fprintf(stderr, "%s: failed to open %s: %s\n", progname, mtab,
177                 strerror(errno));
178         return -1;
179     }
180     while ((entp = getmntent(fp)) != NULL) {
181         if (strcmp(entp->mnt_type, "fuse") == 0 ||
182             strncmp(entp->mnt_type, "fuse.", 5) == 0)
183             count ++;
184     }
185     endmntent(fp);
186     return count;
187 }
188 
189 
190 #else /* IGNORE_MTAB */
count_fuse_fs()191 static int count_fuse_fs()
192 {
193     return 0;
194 }
195 
add_mount(const char * source,const char * mnt,const char * type,const char * opts)196 static int add_mount(const char *source, const char *mnt, const char *type,
197                      const char *opts)
198 {
199     (void) source;
200     (void) mnt;
201     (void) type;
202     (void) opts;
203     return 0;
204 }
205 #endif /* IGNORE_MTAB */
206 
begins_with(const char * s,const char * beg)207 static int begins_with(const char *s, const char *beg)
208 {
209     if (strncmp(s, beg, strlen(beg)) == 0)
210         return 1;
211     else
212         return 0;
213 }
214 
215 struct mount_flags {
216     const char *opt;
217     unsigned long flag;
218     int on;
219     int safe;
220 };
221 
222 static struct mount_flags mount_flags[] = {
223     {"rw",      MS_RDONLY,      0, 1},
224     {"ro",      MS_RDONLY,      1, 1},
225     {"suid",    MS_NOSUID,      0, 0},
226     {"nosuid",  MS_NOSUID,      1, 1},
227     {"dev",     MS_NODEV,       0, 0},
228     {"nodev",   MS_NODEV,       1, 1},
229     {"exec",    MS_NOEXEC,      0, 1},
230     {"noexec",  MS_NOEXEC,      1, 1},
231     {"async",   MS_SYNCHRONOUS, 0, 1},
232     {"sync",    MS_SYNCHRONOUS, 1, 1},
233     {"atime",   MS_NOATIME,     0, 1},
234     {"noatime", MS_NOATIME,     1, 1},
235     {"dirsync", MS_DIRSYNC,     1, 1},
236     {NULL,      0,              0, 0}
237 };
238 
find_mount_flag(const char * s,unsigned len,int * on,int * flag)239 static int find_mount_flag(const char *s, unsigned len, int *on, int *flag)
240 {
241     int i;
242 
243     for (i = 0; mount_flags[i].opt != NULL; i++) {
244         const char *opt = mount_flags[i].opt;
245         if (strlen(opt) == len && strncmp(opt, s, len) == 0) {
246             *on = mount_flags[i].on;
247             *flag = mount_flags[i].flag;
248             if (!mount_flags[i].safe && getuid() != 0) {
249                 *flag = 0;
250                 fprintf(stderr, "%s: unsafe option '%s' ignored\n",
251                         progname, opt);
252             }
253             return 1;
254         }
255     }
256     return 0;
257 }
258 
add_option(char ** optsp,const char * opt,unsigned expand)259 static int add_option(char **optsp, const char *opt, unsigned expand)
260 {
261     char *newopts;
262     if (*optsp == NULL)
263         newopts = strdup(opt);
264     else {
265         unsigned oldsize = strlen(*optsp);
266         unsigned newsize = oldsize + 1 + strlen(opt) + expand + 1;
267         newopts = (char *) realloc(*optsp, newsize);
268         if (newopts)
269             sprintf(newopts + oldsize, ",%s", opt);
270     }
271     if (newopts == NULL) {
272         fprintf(stderr, "%s: failed to allocate memory\n", progname);
273         return -1;
274     }
275     *optsp = newopts;
276     return 0;
277 }
278 
get_mnt_opts(int flags,char * opts,char ** mnt_optsp)279 static int get_mnt_opts(int flags, char *opts, char **mnt_optsp)
280 {
281     int i;
282     int l;
283 
284     if (!(flags & MS_RDONLY) && add_option(mnt_optsp, "rw", 0) == -1)
285         return -1;
286 
287     for (i = 0; mount_flags[i].opt != NULL; i++) {
288         if (mount_flags[i].on && (flags & mount_flags[i].flag) &&
289             add_option(mnt_optsp, mount_flags[i].opt, 0) == -1)
290             return -1;
291     }
292 
293     if (add_option(mnt_optsp, opts, 0) == -1)
294         return -1;
295     /* remove comma from end of opts*/
296     l = strlen(*mnt_optsp);
297     if ((*mnt_optsp)[l-1] == ',')
298         (*mnt_optsp)[l-1] = '\0';
299     if (getuid() != 0) {
300         const char *user = get_user_name();
301         if (user == NULL)
302             return -1;
303 
304         if (add_option(mnt_optsp, "user=", strlen(user)) == -1)
305             return -1;
306         strcat(*mnt_optsp, user);
307     }
308     return 0;
309 }
310 
opt_eq(const char * s,unsigned len,const char * opt)311 static int opt_eq(const char *s, unsigned len, const char *opt)
312 {
313     if(strlen(opt) == len && strncmp(s, opt, len) == 0)
314         return 1;
315     else
316         return 0;
317 }
318 
get_string_opt(const char * s,unsigned len,const char * opt,char ** val)319 static int get_string_opt(const char *s, unsigned len, const char *opt,
320                           char **val)
321 {
322     unsigned opt_len = strlen(opt);
323 
324     if (*val)
325         free(*val);
326     *val = (char *) malloc(len - opt_len + 1);
327     if (!*val) {
328         fprintf(stderr, "%s: failed to allocate memory\n", progname);
329         return 0;
330     }
331 
332     memcpy(*val, s + opt_len, len - opt_len);
333     (*val)[len - opt_len] = '\0';
334     return 1;
335 }
336 
do_mount(const char * mnt,char ** typep,mode_t rootmode,int fd,const char * opts,const char * dev,char ** sourcep,char ** mnt_optsp)337 static int do_mount(const char *mnt, char **typep, mode_t rootmode,
338                     int fd, const char *opts, const char *dev, char **sourcep,
339                     char **mnt_optsp)
340 {
341     int res;
342     int flags = MS_NOSUID | MS_NODEV;
343     char *optbuf;
344     char *mnt_opts = NULL;
345     const char *s;
346     char *d;
347     char *fsname = NULL;
348     char *source = NULL;
349     char *type = NULL;
350     int blkdev = 0;
351 
352     optbuf = (char *) malloc(strlen(opts) + 128);
353     if (!optbuf) {
354         fprintf(stderr, "%s: failed to allocate memory\n", progname);
355         return -1;
356     }
357 
358     for (s = opts, d = optbuf; *s;) {
359         unsigned len;
360         const char *fsname_str = "fsname=";
361         for (len = 0; s[len] && s[len] != ','; len++);
362         if (begins_with(s, fsname_str)) {
363             if (!get_string_opt(s, len, fsname_str, &fsname))
364                 goto err;
365         } else if (opt_eq(s, len, "blkdev")) {
366             blkdev = 1;
367         } else if (!begins_with(s, "fd=") &&
368                    !begins_with(s, "rootmode=") &&
369                    !begins_with(s, "user_id=") &&
370                    !begins_with(s, "group_id=")) {
371             int on;
372             int flag;
373             int skip_option = 0;
374             if (opt_eq(s, len, "large_read")) {
375                 struct utsname utsname;
376                 unsigned kmaj, kmin;
377                 res = uname(&utsname);
378                 if (res == 0 &&
379                     sscanf(utsname.release, "%u.%u", &kmaj, &kmin) == 2 &&
380                     (kmaj > 2 || (kmaj == 2 && kmin > 4))) {
381                     fprintf(stderr, "%s: note: 'large_read' mount option is "
382 			    "deprecated for %i.%i kernels\n", progname, kmaj, kmin);
383                     skip_option = 1;
384                 }
385             }
386             if (!skip_option) {
387                 if (find_mount_flag(s, len, &on, &flag)) {
388                     if (on)
389                         flags |= flag;
390                     else
391                         flags  &= ~flag;
392                 } else {
393                     memcpy(d, s, len);
394                     d += len;
395                     *d++ = ',';
396                 }
397             }
398         }
399         s += len;
400         if (*s)
401             s++;
402     }
403     *d = '\0';
404     res = get_mnt_opts(flags, optbuf, &mnt_opts);
405     if (res == -1)
406         goto err;
407 
408     sprintf(d, "fd=%i,rootmode=%o,user_id=%i,group_id=%i",
409             fd, rootmode, getuid(), getgid());
410 
411     source = malloc((fsname ? strlen(fsname) : 0) + strlen(dev) + 32);
412 
413     type = malloc(32);
414     if (!type || !source) {
415         fprintf(stderr, "%s: failed to allocate memory\n", progname);
416         goto err;
417     }
418 
419     strcpy(type, blkdev ? "fuseblk" : "fuse");
420 
421     if (fsname)
422         strcpy(source, fsname);
423     else
424         strcpy(source, dev);
425 
426     if (restore_privs())
427 	goto err;
428 
429     res = mount(source, mnt, type, flags, optbuf);
430     if (res == -1 && errno == EINVAL) {
431         /* It could be an old version not supporting group_id */
432         sprintf(d, "fd=%i,rootmode=%o,user_id=%i", fd, rootmode, getuid());
433         res = mount(source, mnt, type, flags, optbuf);
434     }
435 
436     if (drop_privs())
437 	goto err;
438 
439     if (res == -1) {
440         int errno_save = errno;
441         if (blkdev && errno == ENODEV && !fuse_mnt_check_fuseblk())
442             fprintf(stderr, "%s: 'fuseblk' support missing\n", progname);
443 	else {
444             fprintf(stderr, "%s: mount failed: %s\n", progname, strerror(errno_save));
445 	    if (errno_save == EPERM)
446 		    fprintf(stderr, "User doesn't have privilege to mount. "
447 			    "For more information\nplease see: "
448 			    "http://tuxera.com/community/ntfs-3g-faq/#unprivileged\n");
449 	}
450 	goto err;
451     } else {
452         *sourcep = source;
453         *typep = type;
454         *mnt_optsp = mnt_opts;
455     }
456 out:
457     free(fsname);
458     free(optbuf);
459     return res;
460 err:
461     free(source);
462     free(type);
463     free(mnt_opts);
464     res = -1;
465     goto out;
466 }
467 
check_perm(const char ** mntp,struct stat * stbuf,int * currdir_fd,int * mountpoint_fd)468 static int check_perm(const char **mntp, struct stat *stbuf, int *currdir_fd,
469                       int *mountpoint_fd)
470 {
471     int res;
472     const char *mnt = *mntp;
473     const char *origmnt = mnt;
474 
475     res = stat(mnt, stbuf);
476     if (res == -1) {
477         fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
478                 progname, mnt, strerror(errno));
479         return -1;
480     }
481 
482     /* No permission checking is done for root */
483     if (getuid() == 0)
484         return 0;
485 
486     if (S_ISDIR(stbuf->st_mode)) {
487         *currdir_fd = open(".", O_RDONLY);
488         if (*currdir_fd == -1) {
489             fprintf(stderr, "%s: failed to open current directory: %s\n",
490                     progname, strerror(errno));
491             return -1;
492         }
493         res = chdir(mnt);
494         if (res == -1) {
495             fprintf(stderr, "%s: failed to chdir to mountpoint: %s\n",
496                     progname, strerror(errno));
497             return -1;
498         }
499         mnt = *mntp = ".";
500         res = lstat(mnt, stbuf);
501         if (res == -1) {
502             fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
503                     progname, origmnt, strerror(errno));
504             return -1;
505         }
506 
507         if ((stbuf->st_mode & S_ISVTX) && stbuf->st_uid != getuid()) {
508             fprintf(stderr, "%s: mountpoint %s not owned by user\n",
509                     progname, origmnt);
510             return -1;
511         }
512 
513         res = access(mnt, W_OK);
514         if (res == -1) {
515             fprintf(stderr, "%s: user has no write access to mountpoint %s\n",
516                     progname, origmnt);
517             return -1;
518         }
519     } else if (S_ISREG(stbuf->st_mode)) {
520         static char procfile[256];
521         *mountpoint_fd = open(mnt, O_WRONLY);
522         if (*mountpoint_fd == -1) {
523             fprintf(stderr, "%s: failed to open %s: %s\n", progname, mnt,
524                     strerror(errno));
525             return -1;
526         }
527         res = fstat(*mountpoint_fd, stbuf);
528         if (res == -1) {
529             fprintf(stderr, "%s: failed to access mountpoint %s: %s\n",
530                     progname, mnt, strerror(errno));
531             return -1;
532         }
533         if (!S_ISREG(stbuf->st_mode)) {
534             fprintf(stderr, "%s: mountpoint %s is no longer a regular file\n",
535                     progname, mnt);
536             return -1;
537         }
538 
539         sprintf(procfile, "/proc/self/fd/%i", *mountpoint_fd);
540         *mntp = procfile;
541     } else {
542         fprintf(stderr,
543                 "%s: mountpoint %s is not a directory or a regular file\n",
544                 progname, mnt);
545         return -1;
546     }
547 
548 
549     return 0;
550 }
551 
try_open(const char * dev,char ** devp)552 static int try_open(const char *dev, char **devp)
553 {
554     int fd;
555 
556     if (restore_privs())
557 	return -1;
558     fd = open(dev, O_RDWR);
559     if (drop_privs())
560 	return -1;
561     if (fd != -1) {
562         *devp = strdup(dev);
563         if (*devp == NULL) {
564             fprintf(stderr, "%s: failed to allocate memory\n", progname);
565             close(fd);
566             fd = -1;
567         }
568     } else if (errno == ENODEV ||
569                errno == ENOENT) /* check for ENOENT too, for the udev case */
570         return -2;
571     else {
572         fprintf(stderr, "%s: failed to open %s: %s\n", progname, dev,
573                 strerror(errno));
574     }
575     return fd;
576 }
577 
open_fuse_device(char ** devp)578 static int open_fuse_device(char **devp)
579 {
580     int fd;
581 
582     fd = try_open(FUSE_DEV_NEW, devp);
583     if (fd >= -1)
584         return fd;
585 
586     fprintf(stderr, "%s: fuse device is missing, try 'modprobe fuse' as root\n",
587             progname);
588 
589     return -1;
590 }
591 
592 
mount_fuse(const char * mnt,const char * opts)593 static int mount_fuse(const char *mnt, const char *opts)
594 {
595     int res;
596     int fd;
597     char *dev;
598     struct stat stbuf;
599     char *type = NULL;
600     char *source = NULL;
601     char *mnt_opts = NULL;
602     const char *real_mnt = mnt;
603     int currdir_fd = -1;
604     int mountpoint_fd = -1;
605 
606     fd = open_fuse_device(&dev);
607     if (fd == -1)
608         return -1;
609 
610     if (getuid() != 0 && mount_max != -1) {
611         if (count_fuse_fs() >= mount_max) {
612             fprintf(stderr, "%s: too many mounted FUSE filesystems (%d+)\n",
613 		    progname, mount_max);
614 	    goto err;
615         }
616     }
617 
618     res = check_perm(&real_mnt, &stbuf, &currdir_fd, &mountpoint_fd);
619     if (res != -1)
620          res = do_mount(real_mnt, &type, stbuf.st_mode & S_IFMT, fd, opts, dev,
621 			&source, &mnt_opts);
622 
623     if (currdir_fd != -1) {
624 	 __attribute__((unused))int ignored_fchdir_status =
625 	        fchdir(currdir_fd);
626         close(currdir_fd);
627     }
628     if (mountpoint_fd != -1)
629         close(mountpoint_fd);
630 
631     if (res == -1)
632 	goto err;
633 
634     if (restore_privs())
635 	goto err;
636 
637     if (geteuid() == 0) {
638 
639 	if (setgroups(0, NULL) == -1) {
640 	    perror("priv drop: setgroups failed");
641 	    goto err;
642 	}
643 
644         res = add_mount(source, mnt, type, mnt_opts);
645         if (res == -1) {
646             umount2(mnt, 2); /* lazy umount */
647 	    drop_privs();
648 	    goto err;
649         }
650     }
651 
652     if (drop_privs())
653 	goto err;
654 out:
655     free(source);
656     free(type);
657     free(mnt_opts);
658     free(dev);
659 
660     return fd;
661 err:
662     close(fd);
663     fd = -1;
664     goto out;
665 }
666 
fusermount(int unmount,int quiet,int lazy,const char * opts,const char * origmnt)667 int fusermount(int unmount, int quiet, int lazy, const char *opts,
668 	       const char *origmnt)
669 {
670     int res = -1;
671     char *mnt;
672     mode_t old_umask;
673 
674     mnt = fuse_mnt_resolve_path(progname, origmnt);
675     if (mnt == NULL)
676 	return -1;
677 
678     old_umask = umask(033);
679 
680     if (unmount) {
681 
682 	if (restore_privs())
683 	    goto out;
684 
685         if (geteuid() == 0)
686             res = fuse_mnt_umount(progname, mnt, lazy);
687         else {
688             res = umount2(mnt, lazy ? 2 : 0);
689             if (res == -1 && !quiet)
690                 fprintf(stderr, "%s: failed to unmount %s: %s\n", progname,
691                         mnt, strerror(errno));
692         }
693 
694 	if (drop_privs())
695 	    res = -1;
696 
697     } else
698 	    res = mount_fuse(mnt, opts);
699 out:
700     umask(old_umask);
701     free(mnt);
702     return res;
703 }
704 
705 #endif /* __SOLARIS__ */
706