/* * Copyright (C) 2013 Andrew Ayer * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Except as contained in this notice, the name(s) of the above copyright * holders shall not be used in advertising or otherwise to promote the * sale, use or other dealings in this Software without prior written * authorization. */ /* * with-fuse -- Launch the program specified on the command line with gid fuse * and in a separate mount namespace such that any mounts created by this * process (or its descendants) are not visible to other processes. * * Example: * $ with-fuse /bin/sh * $ sshfs ... * $ exit * * This lets the program create FUSE mounts that aren't visible to the rest of * the system, which is useful because FUSE mounts have non-standard semantics * which may, for example, cause problems with rsync-based backups. * * For with-fuse to work properly, the following command must be run at system * boot (e.g. from /etc/rc.local): * * mount --make-rshared / * * You can compile with-fuse with any C compiler: * * cc -o with-fuse with-fuse.c * * with-fuse can be safely installed setuid-root to permit users on the system * to use FUSE in an isolated namespace. It drops all privileges before * executing the command. To make it setuid root: * * chown root with-fuse * chmod u+s with-fuse */ #ifndef _GNU_SOURCE #define _GNU_SOURCE /* needed for unshare() */ #endif #include #include #include #include #include #include #include #include #ifndef FUSE_GROUP #define FUSE_GROUP "fuse" /* The group with access to /dev/fuse */ #endif int main (int argc, char **argv) { /* Determine the GID of the fuse group */ struct group* fuse_grp = getgrnam(FUSE_GROUP); if (!fuse_grp) { if (errno == 0) { fprintf(stderr, "FUSE group '" FUSE_GROUP "' not found\n"); } else { perror("getgrnam"); } return 1; } /* Create a new mount namespace */ if (unshare(CLONE_NEWNS) == -1) { perror("unshare"); return 1; } /* Recursively make the root directory (in this namespace) a shared+slave mount. * * As a slave mount, mounts/unmounts in our parent namespace WILL be visible to * us, but OUR mounts/unmounts will be kept private. * * Also make / a shared mount, so our mount events will be visible in any child * namespaces, allowing these namespaces to be infinitely and transparently nested. * * The mounts have to be done in this order (slave then shared) to work. * This code is based on http://lwn.net/Articles/159092/ */ if (mount("", "/", "", MS_SLAVE|MS_REC, "") == -1) { perror("mount(MS_SLAVE|MS_REC)"); return 1; } if (mount("", "/", "", MS_SHARED|MS_REC, "") == -1) { perror("mount(MS_SHARED|MS_REC)"); return 1; } /* Become the fuse group */ if (setgid(fuse_grp->gr_gid) == -1) { perror("setgid"); return 1; } /* Drop from root privileges to our REAL uid */ if (setuid(getuid()) == -1) { perror("setuid"); return 1; } /* Exec the program specified on the command line */ if (argc < 2) { fprintf(stderr, "Usage: %s PROGRAM [ARGS...]\n", argv[0]); return 2; } execvp(argv[1], argv + 1); perror(argv[1]); return 127; }