22 #include <sys/prctl.h>    23 #include <sys/syscall.h>    24 #include <linux/capability.h>    25 #include <linux/securebits.h>    30 static char *progname;
    32 static char *xstrdup(
const char *s)
    36                 fprintf(stderr, 
"%s: failed to allocate memory\n", progname);
    42 static void *xrealloc(
void *oldptr, 
size_t size)
    44         void *ptr = realloc(oldptr, size);
    46                 fprintf(stderr, 
"%s: failed to allocate memory\n", progname);
    52 static void add_arg(
char **cmdp, 
const char *opt)
    54         size_t optlen = strlen(opt);
    55         size_t cmdlen = *cmdp ? strlen(*cmdp) : 0;
    56         if (optlen >= (SIZE_MAX - cmdlen - 4)/4) {
    57                 fprintf(stderr, 
"%s: argument too long\n", progname);
    60         char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4);
    81 static char *add_option(
const char *opt, 
char *options)
    83         int oldlen = options ? strlen(options) : 0;
    85         options = xrealloc(options, oldlen + 1 + strlen(opt) + 1);
    95 static int prepare_fuse_fd(
const char *mountpoint, 
const char* subtype,
   100         int subtype_len = strlen(subtype) + 9;
   101         char* options_copy = xrealloc(NULL, subtype_len);
   103         snprintf(options_copy, subtype_len, 
"subtype=%s", subtype);
   104         options_copy = add_option(options, options_copy);
   110         flags = fcntl(fuse_fd, F_GETFD);
   111         if (flags == -1 || fcntl(fuse_fd, F_SETFD, flags & ~FD_CLOEXEC) == 1) {
   112                 fprintf(stderr, 
"%s: Failed to clear CLOEXEC: %s\n",
   113                         progname, strerror(errno));
   121 static uint64_t get_capabilities(
void)
   127         struct __user_cap_header_struct header = {
   128                 .version = _LINUX_CAPABILITY_VERSION_3,
   131         struct __user_cap_data_struct data[2];
   132         memset(data, 0, 
sizeof(data));
   133         if (syscall(SYS_capget, &header, data) == -1) {
   134                 fprintf(stderr, 
"%s: Failed to get capabilities: %s\n",
   135                         progname, strerror(errno));
   139         return data[0].effective | ((uint64_t) data[1].effective << 32);
   142 static void set_capabilities(uint64_t caps)
   148         struct __user_cap_header_struct header = {
   149                 .version = _LINUX_CAPABILITY_VERSION_3,
   152         struct __user_cap_data_struct data[2];
   153         memset(data, 0, 
sizeof(data));
   154         data[0].effective = data[0].permitted = caps;
   155         data[1].effective = data[1].permitted = caps >> 32;
   156         if (syscall(SYS_capset, &header, data) == -1) {
   157                 fprintf(stderr, 
"%s: Failed to set capabilities: %s\n",
   158                         progname, strerror(errno));
   163 static void drop_and_lock_capabilities(
void)
   166         if (prctl(PR_SET_SECUREBITS,
   167                   SECBIT_KEEP_CAPS_LOCKED |
   168                   SECBIT_NO_SETUID_FIXUP |
   169                   SECBIT_NO_SETUID_FIXUP_LOCKED |
   171                   SECBIT_NOROOT_LOCKED) == -1) {
   172                 fprintf(stderr, 
"%s: Failed to set securebits %s\n",
   173                         progname, strerror(errno));
   179         for (cap = 0; ; cap++) {
   180                 int cap_status = prctl(PR_CAPBSET_READ, cap);
   181                 if (cap_status == 0) {
   184                 if (cap_status == -1 && errno == EINVAL) {
   188                 if (cap_status != 1) {
   190                                 "%s: Failed to get capability %u: %s\n",
   191                                 progname, cap, strerror(errno));
   194                 if (prctl(PR_CAPBSET_DROP, cap) == -1) {
   196                                 "%s: Failed to drop capability %u: %s\n",
   197                                 progname, cap, strerror(errno));
   205         if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
   206                 fprintf(stderr, 
"%s: Failed to set no_new_privs: %s\n",
   207                         progname, strerror(errno));
   213 int main(
int argc, 
char *argv[])
   217         const char *mountpoint;
   219         char *options = NULL;
   220         char *command = NULL;
   221         char *setuid_name = NULL;
   225         int pass_fuse_fd = 0;
   226         int drop_privileges = 0;
   229         basename = strrchr(argv[0], 
'/');
   235         if (strncmp(basename, 
"mount.fuse.", 11) == 0)
   236                 type = basename + 11;
   237         if (strncmp(basename, 
"mount.fuseblk.", 14) == 0)
   238                 type = basename + 14;
   240         if (type && !type[0])
   245                         "usage: %s %s destination [-t type] [-o opt[,opts...]]\n",
   246                         progname, type ? 
"source" : 
"type#[source]");
   254         mountpoint = argv[2];
   256         for (i = 3; i < argc; i++) {
   257                 if (strcmp(argv[i], 
"-v") == 0) {
   259                 } 
else if (strcmp(argv[i], 
"-t") == 0) {
   264                                         "%s: missing argument to option '-t'\n",
   269                         if (strncmp(type, 
"fuse.", 5) == 0)
   271                         else if (strncmp(type, 
"fuseblk.", 8) == 0)
   276                                         "%s: empty type given as argument to option '-t'\n",
   280                 } 
else  if (strcmp(argv[i], 
"-o") == 0) {
   287                         opts = xstrdup(argv[i]);
   288                         opt = strtok(opts, 
",");
   292                                 const char *ignore_opts[] = { 
"",
   301                                 if (strncmp(opt, 
"setuid=", 7) == 0) {
   302                                         setuid_name = xstrdup(opt + 7);
   304                                 } 
else if (strcmp(opt,
   305                                                   "drop_privileges") == 0) {
   310                                 for (j = 0; ignore_opts[j]; j++)
   311                                         if (strcmp(opt, ignore_opts[j]) == 0)
   315                                         if (strcmp(opt, 
"nodev") == 0)
   317                                         else if (strcmp(opt, 
"nosuid") == 0)
   320                                         options = add_option(opt, options);
   322                                 opt = strtok(NULL, 
",");
   327         if (drop_privileges) {
   328                 uint64_t required_caps = CAP_TO_MASK(CAP_SETPCAP) |
   329                                 CAP_TO_MASK(CAP_SYS_ADMIN);
   330                 if ((get_capabilities() & required_caps) != required_caps) {
   331                         fprintf(stderr, 
"%s: drop_privileges was requested, which launches the FUSE file system fully unprivileged. In order to do so %s must be run with privileges, please invoke with CAP_SYS_ADMIN and CAP_SETPCAP (e.g. as root).\n",
   338                 options = add_option(
"dev", options);
   340                 options = add_option(
"suid", options);
   344                         type = xstrdup(source);
   345                         source = strchr(type, 
'#');
   349                                 fprintf(stderr, 
"%s: empty filesystem type\n",
   354                         fprintf(stderr, 
"%s: empty source\n", progname);
   359         if (setuid_name && setuid_name[0]) {
   361                 if (drop_privileges) {
   370                         if (prctl(PR_SET_SECUREBITS,
   372                                   SECBIT_NO_SETUID_FIXUP) == -1) {
   374                                         "%s: Failed to set securebits %s\n",
   375                                         progname, strerror(errno));
   381                 struct passwd *pwd = getpwnam(setuid_name);
   382                 if (setgid(pwd->pw_gid) == -1 || setuid(pwd->pw_uid) == -1) {
   383                         fprintf(stderr, 
"%s: Failed to setuid to %s: %s\n",
   384                                 progname, setuid_name, strerror(errno));
   387         } 
else if (!getenv(
"HOME")) {
   389                 setenv(
"HOME", 
"/root", 0);
   393                 int fuse_fd = prepare_fuse_fd(mountpoint, type, options);
   394                 char *dev_fd_mountpoint = xrealloc(NULL, 20);
   395                 snprintf(dev_fd_mountpoint, 20, 
"/dev/fd/%u", fuse_fd);
   396                 mountpoint = dev_fd_mountpoint;
   400         if (drop_privileges) {
   401                 drop_and_lock_capabilities();
   404         add_arg(&command, type);
   406                 add_arg(&command, source);
   407         add_arg(&command, mountpoint);
   409                 add_arg(&command, 
"-o");
   410                 add_arg(&command, options);
   413         execl(
"/bin/sh", 
"/bin/sh", 
"-c", command, NULL);
   414         fprintf(stderr, 
"%s: failed to execute /bin/sh: %s\n", progname,
 int fuse_open_channel(const char *mountpoint, const char *options)