22 #include <sys/prctl.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))
49 static char *progname;
51 static char *xstrdup(
const char *s)
55 fprintf(stderr,
"%s: failed to allocate memory\n", progname);
61 static void *xrealloc(
void *oldptr,
size_t size)
63 void *ptr = realloc(oldptr, size);
65 fprintf(stderr,
"%s: failed to allocate memory\n", progname);
71 static void add_arg(
char **cmdp,
const char *opt)
73 size_t optlen = strlen(opt);
74 size_t cmdlen = *cmdp ? strlen(*cmdp) : 0;
75 if (optlen >= (SIZE_MAX - cmdlen - 4)/4) {
76 fprintf(stderr,
"%s: argument too long\n", progname);
79 char *cmd = xrealloc(*cmdp, cmdlen + optlen * 4 + 4);
100 static char *add_option(
const char *opt,
char *options)
102 int oldlen = options ? strlen(options) : 0;
104 options = xrealloc(options, oldlen + 1 + strlen(opt) + 1);
106 strcpy(options, opt);
108 strcat(options,
",");
109 strcat(options, opt);
114 static int prepare_fuse_fd(
const char *mountpoint,
const char* subtype,
119 int subtype_len = strlen(subtype) + 9;
120 char* options_copy = xrealloc(NULL, subtype_len);
122 snprintf(options_copy, subtype_len,
"subtype=%s", subtype);
123 options_copy = add_option(options, options_copy);
129 flags = fcntl(fuse_fd, F_GETFD);
130 if (flags == -1 || fcntl(fuse_fd, F_SETFD, flags & ~FD_CLOEXEC) == 1) {
131 fprintf(stderr,
"%s: Failed to clear CLOEXEC: %s\n",
132 progname, strerror(errno));
140 static uint64_t get_capabilities(
void)
146 struct __user_cap_header_struct header = {
147 .version = _LINUX_CAPABILITY_VERSION_3,
150 struct __user_cap_data_struct data[2];
151 memset(data, 0,
sizeof(data));
152 if (syscall(SYS_capget, &header, data) == -1) {
153 fprintf(stderr,
"%s: Failed to get capabilities: %s\n",
154 progname, strerror(errno));
158 return data[0].effective | ((uint64_t) data[1].effective << 32);
161 static void set_capabilities(uint64_t caps)
167 struct __user_cap_header_struct header = {
168 .version = _LINUX_CAPABILITY_VERSION_3,
171 struct __user_cap_data_struct data[2];
172 memset(data, 0,
sizeof(data));
173 data[0].effective = data[0].permitted = caps;
174 data[1].effective = data[1].permitted = caps >> 32;
175 if (syscall(SYS_capset, &header, data) == -1) {
176 fprintf(stderr,
"%s: Failed to set capabilities: %s\n",
177 progname, strerror(errno));
182 static void drop_and_lock_capabilities(
void)
185 if (prctl(PR_SET_SECUREBITS,
186 SECBIT_KEEP_CAPS_LOCKED |
187 SECBIT_NO_SETUID_FIXUP |
188 SECBIT_NO_SETUID_FIXUP_LOCKED |
190 SECBIT_NOROOT_LOCKED) == -1) {
191 fprintf(stderr,
"%s: Failed to set securebits %s\n",
192 progname, strerror(errno));
198 for (cap = 0; ; cap++) {
199 int cap_status = prctl(PR_CAPBSET_READ, cap);
200 if (cap_status == 0) {
203 if (cap_status == -1 && errno == EINVAL) {
207 if (cap_status != 1) {
209 "%s: Failed to get capability %u: %s\n",
210 progname, cap, strerror(errno));
213 if (prctl(PR_CAPBSET_DROP, cap) == -1) {
215 "%s: Failed to drop capability %u: %s\n",
216 progname, cap, strerror(errno));
224 if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) {
225 fprintf(stderr,
"%s: Failed to set no_new_privs: %s\n",
226 progname, strerror(errno));
232 int main(
int argc,
char *argv[])
236 char *dup_source = NULL;
237 const char *mountpoint;
239 char *options = NULL;
240 char *command = NULL;
241 char *setuid_name = NULL;
245 int pass_fuse_fd = 0;
247 int drop_privileges = 0;
248 char *dev_fd_mountpoint = NULL;
251 basename = strrchr(argv[0],
'/');
257 if (strncmp(basename,
"mount.fuse.", 11) == 0)
258 type = basename + 11;
259 if (strncmp(basename,
"mount.fuseblk.", 14) == 0)
260 type = basename + 14;
262 if (type && !type[0])
267 "usage: %s %s destination [-t type] [-o opt[,opts...]]\n",
268 progname, type ?
"source" :
"type#[source]");
276 mountpoint = argv[2];
278 for (i = 3; i < argc; i++) {
279 if (strcmp(argv[i],
"-v") == 0) {
281 }
else if (strcmp(argv[i],
"-t") == 0) {
286 "%s: missing argument to option '-t'\n",
291 if (strncmp(type,
"fuse.", 5) == 0)
293 else if (strncmp(type,
"fuseblk.", 8) == 0)
298 "%s: empty type given as argument to option '-t'\n",
302 }
else if (strcmp(argv[i],
"-o") == 0) {
309 opts = xstrdup(argv[i]);
310 opt = strtok(opts,
",");
314 const char *ignore_opts[] = {
"",
323 if (strncmp(opt,
"setuid=", 7) == 0) {
324 setuid_name = xstrdup(opt + 7);
326 }
else if (strcmp(opt,
327 "drop_privileges") == 0) {
332 for (j = 0; ignore_opts[j]; j++)
333 if (strcmp(opt, ignore_opts[j]) == 0)
337 if (strcmp(opt,
"nodev") == 0)
339 else if (strcmp(opt,
"nosuid") == 0)
342 options = add_option(opt, options);
344 opt = strtok(NULL,
",");
350 if (drop_privileges) {
351 uint64_t required_caps = CAP_TO_MASK(CAP_SETPCAP) |
352 CAP_TO_MASK(CAP_SYS_ADMIN);
353 if ((get_capabilities() & required_caps) != required_caps) {
354 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",
361 options = add_option(
"dev", options);
363 options = add_option(
"suid", options);
367 dup_source = xstrdup(source);
369 source = strchr(type,
'#');
373 fprintf(stderr,
"%s: empty filesystem type\n",
378 fprintf(stderr,
"%s: empty source\n", progname);
383 if (setuid_name && setuid_name[0]) {
385 if (drop_privileges) {
394 if (prctl(PR_SET_SECUREBITS,
396 SECBIT_NO_SETUID_FIXUP) == -1) {
398 "%s: Failed to set securebits %s\n",
399 progname, strerror(errno));
405 struct passwd *pwd = getpwnam(setuid_name);
406 if (!pwd || setgid(pwd->pw_gid) == -1 || setuid(pwd->pw_uid) == -1) {
407 fprintf(stderr,
"%s: Failed to setuid to %s: %s\n",
408 progname, setuid_name, strerror(errno));
411 }
else if (!getenv(
"HOME")) {
413 setenv(
"HOME",
"/root", 0);
417 fuse_fd = prepare_fuse_fd(mountpoint, type, options);
418 dev_fd_mountpoint = xrealloc(NULL, 20);
419 snprintf(dev_fd_mountpoint, 20,
"/dev/fd/%u", fuse_fd);
420 mountpoint = dev_fd_mountpoint;
424 if (drop_privileges) {
425 drop_and_lock_capabilities();
428 add_arg(&command, type);
430 add_arg(&command, source);
431 add_arg(&command, mountpoint);
433 add_arg(&command,
"-o");
434 add_arg(&command, options);
438 free(dev_fd_mountpoint);
442 execl(
"/bin/sh",
"/bin/sh",
"-c", command, NULL);
443 fprintf(stderr,
"%s: failed to execute /bin/sh: %s\n", progname,
int fuse_open_channel(const char *mountpoint, const char *options)