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