9#include "fuse_config.h" 
   23#include <sys/syscall.h> 
   24#include <linux/capability.h> 
   25#include <linux/securebits.h> 
   27#if !defined(SECBIT_KEEP_CAPS) && defined(SECURE_KEEP_CAPS) 
   28#define SECBIT_KEEP_CAPS (issecure_mask(SECURE_KEEP_CAPS)) 
   30#if !defined(SECBIT_KEEP_CAPS_LOCKED) && defined(SECURE_KEEP_CAPS_LOCKED) 
   31#define SECBIT_KEEP_CAPS_LOCKED (issecure_mask(SECURE_KEEP_CAPS_LOCKED)) 
   33#if !defined(SECBIT_NO_SETUID_FIXUP) && defined(SECURE_NO_SETUID_FIXUP) 
   34#define SECBIT_NO_SETUID_FIXUP (issecure_mask(SECURE_NO_SETUID_FIXUP)) 
   36#if !defined(SECBIT_NO_SETUID_FIXUP_LOCKED) && defined(SECURE_NO_SETUID_FIXUP_LOCKED) 
   37#define SECBIT_NO_SETUID_FIXUP_LOCKED (issecure_mask(SECURE_NO_SETUID_FIXUP_LOCKED)) 
   39#if !defined(SECBIT_NOROOT) && defined(SECURE_NOROOT) 
   40#define SECBIT_NOROOT (issecure_mask(SECURE_NOROOT)) 
   42#if !defined(SECBIT_NOROOT_LOCKED) && defined(SECURE_NOROOT_LOCKED) 
   43#define SECBIT_NOROOT_LOCKED (issecure_mask(SECURE_NOROOT_LOCKED)) 
   47#ifndef PR_SET_NO_NEW_PRIVS 
   48#define PR_SET_NO_NEW_PRIVS 38 
   55static char *xstrdup(
const char *s)
 
   59                fprintf(stderr, 
"%s: failed to allocate memory\n", progname);
 
   65static void *xrealloc(
void *oldptr, 
size_t size)
 
   67        void *ptr = realloc(oldptr, size);
 
   69                fprintf(stderr, 
"%s: failed to allocate memory\n", progname);
 
   75static void add_arg(
char **cmdp, 
const char *opt)
 
   77        size_t optlen = strlen(opt);
 
   78        size_t cmdlen = *cmdp ? strlen(*cmdp) : 0;
 
   79        if (optlen >= (SIZE_MAX - cmdlen - 4)/4) {
 
   80                fprintf(stderr, 
"%s: argument too long\n", progname);
 
   83        char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4);
 
  104static char *add_option(
const char *opt, 
char *options)
 
  106        int oldlen = options ? strlen(options) : 0;
 
  108        options = xrealloc(options, oldlen + 1 + strlen(opt) + 1);
 
  110                strcpy(options, opt);
 
  112                strcat(options, 
",");
 
  113                strcat(options, opt);
 
  118static int prepare_fuse_fd(
const char *mountpoint, 
const char* subtype,
 
  123        int subtype_len = strlen(subtype) + 9;
 
  124        char* options_copy = xrealloc(NULL, subtype_len);
 
  126        snprintf(options_copy, subtype_len, 
"subtype=%s", subtype);
 
  127        options_copy = add_option(options, options_copy);
 
  133        flags = fcntl(fuse_fd, F_GETFD);
 
  134        if (flags == -1 || fcntl(fuse_fd, F_SETFD, flags & ~FD_CLOEXEC) == 1) {
 
  135                fprintf(stderr, 
"%s: Failed to clear CLOEXEC: %s\n",
 
  136                        progname, strerror(errno));
 
  144static uint64_t get_capabilities(
void)
 
  150        struct __user_cap_header_struct header = {
 
  151                .version = _LINUX_CAPABILITY_VERSION_3,
 
  154        struct __user_cap_data_struct data[2];
 
  155        memset(data, 0, 
sizeof(data));
 
  156        if (syscall(SYS_capget, &header, data) == -1) {
 
  157                fprintf(stderr, 
"%s: Failed to get capabilities: %s\n",
 
  158                        progname, strerror(errno));
 
  162        return data[0].effective | ((uint64_t) data[1].effective << 32);
 
  165static void set_capabilities(uint64_t caps)
 
  171        struct __user_cap_header_struct header = {
 
  172                .version = _LINUX_CAPABILITY_VERSION_3,
 
  175        struct __user_cap_data_struct data[2];
 
  176        memset(data, 0, 
sizeof(data));
 
  177        data[0].effective = data[0].permitted = caps;
 
  178        data[1].effective = data[1].permitted = caps >> 32;
 
  179        if (syscall(SYS_capset, &header, data) == -1) {
 
  180                fprintf(stderr, 
"%s: Failed to set capabilities: %s\n",
 
  181                        progname, strerror(errno));
 
  186static void drop_and_lock_capabilities(
void)
 
  189        if (prctl(PR_SET_SECUREBITS,
 
  190                  SECBIT_KEEP_CAPS_LOCKED |
 
  191                  SECBIT_NO_SETUID_FIXUP |
 
  192                  SECBIT_NO_SETUID_FIXUP_LOCKED |
 
  194                  SECBIT_NOROOT_LOCKED) == -1) {
 
  195                fprintf(stderr, 
"%s: Failed to set securebits %s\n",
 
  196                        progname, strerror(errno));
 
  202        for (cap = 0; ; cap++) {
 
  203                int cap_status = prctl(PR_CAPBSET_READ, cap);
 
  204                if (cap_status == 0) {
 
  207                if (cap_status == -1 && errno == EINVAL) {
 
  211                if (cap_status != 1) {
 
  213                                "%s: Failed to get capability %u: %s\n",
 
  214                                progname, cap, strerror(errno));
 
  217                if (prctl(PR_CAPBSET_DROP, cap) == -1) {
 
  219                                "%s: Failed to drop capability %u: %s\n",
 
  220                                progname, cap, strerror(errno));
 
  228        if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
 
  229                fprintf(stderr, 
"%s: Failed to set no_new_privs: %s\n",
 
  230                        progname, strerror(errno));
 
  236int main(
int argc, 
char *argv[])
 
  240        char *dup_source = NULL;
 
  241        const char *mountpoint;
 
  243        char *options = NULL;
 
  244        char *command = NULL;
 
  245        char *setuid_name = NULL;
 
  249        int pass_fuse_fd = 0;
 
  251        int drop_privileges = 0;
 
  252        char *dev_fd_mountpoint = NULL;
 
  255        basename = strrchr(argv[0], 
'/');
 
  261        if (strncmp(basename, 
"mount.fuse.", 11) == 0)
 
  262                type = basename + 11;
 
  263        if (strncmp(basename, 
"mount.fuseblk.", 14) == 0)
 
  264                type = basename + 14;
 
  266        if (type && !type[0])
 
  271                        "usage: %s %s destination [-t type] [-o opt[,opts...]]\n",
 
  272                        progname, type ? 
"source" : 
"type#[source]");
 
  280        mountpoint = argv[2];
 
  282        for (i = 3; i < argc; i++) {
 
  283                if (strcmp(argv[i], 
"-v") == 0) {
 
  285                } 
else if (strcmp(argv[i], 
"-t") == 0) {
 
  290                                        "%s: missing argument to option '-t'\n",
 
  295                        if (strncmp(type, 
"fuse.", 5) == 0)
 
  297                        else if (strncmp(type, 
"fuseblk.", 8) == 0)
 
  302                                        "%s: empty type given as argument to option '-t'\n",
 
  306                } 
else  if (strcmp(argv[i], 
"-o") == 0) {
 
  313                        opts = xstrdup(argv[i]);
 
  314                        opt = strtok(opts, 
",");
 
  318                                const char *ignore_opts[] = { 
"",
 
  327                                if (strncmp(opt, 
"setuid=", 7) == 0) {
 
  328                                        setuid_name = xstrdup(opt + 7);
 
  330                                } 
else if (strcmp(opt,
 
  331                                                  "drop_privileges") == 0) {
 
  336                                for (j = 0; ignore_opts[j]; j++)
 
  337                                        if (strcmp(opt, ignore_opts[j]) == 0)
 
  341                                        if (strcmp(opt, 
"nodev") == 0)
 
  343                                        else if (strcmp(opt, 
"nosuid") == 0)
 
  346                                        options = add_option(opt, options);
 
  348                                opt = strtok(NULL, 
",");
 
  354        if (drop_privileges) {
 
  355                uint64_t required_caps = CAP_TO_MASK(CAP_SETPCAP) |
 
  356                                CAP_TO_MASK(CAP_SYS_ADMIN);
 
  357                if ((get_capabilities() & required_caps) != required_caps) {
 
  358                        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",
 
  365                options = add_option(
"dev", options);
 
  367                options = add_option(
"suid", options);
 
  371                        dup_source = xstrdup(source);
 
  373                        source = strchr(type, 
'#');
 
  377                                fprintf(stderr, 
"%s: empty filesystem type\n",
 
  382                        fprintf(stderr, 
"%s: empty source\n", progname);
 
  387        if (setuid_name && setuid_name[0]) {
 
  389                if (drop_privileges) {
 
  398                        if (prctl(PR_SET_SECUREBITS,
 
  400                                  SECBIT_NO_SETUID_FIXUP) == -1) {
 
  402                                        "%s: Failed to set securebits %s\n",
 
  403                                        progname, strerror(errno));
 
  409                struct passwd *pwd = getpwnam(setuid_name);
 
  410                if (!pwd || setgid(pwd->pw_gid) == -1 || setuid(pwd->pw_uid) == -1) {
 
  411                        fprintf(stderr, 
"%s: Failed to setuid to %s: %s\n",
 
  412                                progname, setuid_name, strerror(errno));
 
  415        } 
else if (!getenv(
"HOME")) {
 
  417                setenv(
"HOME", 
"/root", 0);
 
  421                fuse_fd = prepare_fuse_fd(mountpoint, type, options);
 
  422                dev_fd_mountpoint = xrealloc(NULL, 20);
 
  423                snprintf(dev_fd_mountpoint, 20, 
"/dev/fd/%u", fuse_fd);
 
  424                mountpoint = dev_fd_mountpoint;
 
  428        if (drop_privileges) {
 
  429                drop_and_lock_capabilities();
 
  432        add_arg(&command, type);
 
  434                add_arg(&command, source);
 
  435        add_arg(&command, mountpoint);
 
  437                add_arg(&command, 
"-o");
 
  438                add_arg(&command, options);
 
  442        free(dev_fd_mountpoint);
 
  446        execl(
"/bin/sh", 
"/bin/sh", 
"-c", command, NULL);
 
  447        fprintf(stderr, 
"%s: failed to execute /bin/sh: %s\n", progname,
 
int fuse_open_channel(const char *mountpoint, const char *options)