X-Git-Url: http://git.linux-vserver.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=util-vserver%2Fsrc%2Fsecure-mount.c;fp=util-vserver%2Fsrc%2Fsecure-mount.c;h=0000000000000000000000000000000000000000;hb=9c78378bebc1a9f3d400f422df8ede9efea57dea;hp=c3a2b48bb8e0573f3512c72910dee6fb9991b686;hpb=faa778968d1a7dfeaebc9fe4b847e691dc62fd15;p=util-vserver.git diff --git a/util-vserver/src/secure-mount.c b/util-vserver/src/secure-mount.c deleted file mode 100644 index c3a2b48..0000000 --- a/util-vserver/src/secure-mount.c +++ /dev/null @@ -1,730 +0,0 @@ -// $Id$ --*- c++ -*-- - -// Copyright (C) 2003 Enrico Scholz -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; version 2 of the License. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - - // secure-mount [--chroot] - // [--mtab ] [--fstab ] - // - // Executes mount-operations under the current directory: it assumes sources - // in the current root-dir while destinations are expected in the chroot - // environment. - - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include "util.h" -#include "pathconfig.h" - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define ENSC_WRAPPERS_FCNTL 1 -#define ENSC_WRAPPERS_UNISTD 1 -#include - -#define MNTPOINT "/etc" - -typedef enum { rfsYES, rfsNO, rfsONLY } RootFsOption; - -struct MountInfo { - char const * src; - char const * dst; - char const * type; - unsigned long flag; - unsigned long xflag; - unsigned long mask; - char * data; -}; - -struct Options { - char const * mtab; - char const * fstab; - bool do_chroot; - bool ignore_mtab; - bool mount_all; - RootFsOption rootfs; - - int cur_dir_fd; - int cur_rootdir_fd; -}; - -#define OPTION_BIND 1024 -#define OPTION_MOVE 1025 -#define OPTION_MTAB 1026 -#define OPTION_FSTAB 1027 -#define OPTION_CHROOT 1028 -#define OPTION_SECURE 1029 -#define OPTION_RBIND 1030 -#define OPTION_ROOTFS 1031 - -#define XFLAG_NOAUTO 0x01 - -static struct option const -CMDLINE_OPTIONS[] = { - { "help", no_argument, 0, 'h' }, - { "version", no_argument, 0, 'v' }, - { "bind", no_argument, 0, OPTION_BIND }, - { "move", no_argument, 0, OPTION_MOVE }, - { "mtab", required_argument, 0, OPTION_MTAB }, - { "fstab", required_argument, 0, OPTION_FSTAB }, - { "rootfs", required_argument, 0, OPTION_ROOTFS }, - { "chroot", no_argument, 0, OPTION_CHROOT }, - { "secure", no_argument, 0, OPTION_SECURE }, - { "rbind", no_argument, 0, OPTION_RBIND }, - { 0, 0, 0, 0 } -}; - -#ifndef MS_REC -# define MS_REC 0x4000 -#endif - -static struct FstabOption { - char const * const opt; - unsigned long const flag; - unsigned long const mask; - unsigned long const xflag; - bool const is_dflt; -} const FSTAB_OPTIONS[] = { - { "defaults", 0, (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC| - MS_SYNCHRONOUS), 0, false }, - { "rbind", MS_BIND|MS_REC, MS_BIND|MS_REC, 0, false }, - { "bind", MS_BIND, MS_BIND, 0, false }, - { "move", MS_MOVE, MS_MOVE, 0, false }, - { "async", 0, MS_SYNCHRONOUS, 0, false }, - { "sync", MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0, false }, - { "atime", 0, MS_NOATIME, 0, false }, - { "noatime", MS_NOATIME, MS_NOATIME, 0, false }, - { "dev", 0, MS_NODEV, 0, false }, - { "nodev", MS_NODEV, MS_NODEV, 0, false }, - { "exec", 0, MS_NOEXEC, 0, false }, - { "noexec", MS_NOEXEC, MS_NOEXEC, 0, false }, - { "suid", 0, MS_NOSUID, 0, false }, - { "nosuid", MS_NOSUID, MS_NOSUID, 0, false }, - { "ro", MS_RDONLY, MS_RDONLY, 0, false }, - { "rw", 0, MS_RDONLY, 0, false }, - - { "remount", MS_REMOUNT, MS_REMOUNT, 0, false }, - { "users", MS_NOEXEC|MS_NOSUID|MS_NODEV, - MS_NOEXEC|MS_NOSUID|MS_NODEV, 0, false }, - { "mandlock", MS_MANDLOCK, MS_MANDLOCK, 0, false }, - { "nodiratime", MS_NODIRATIME, MS_NODIRATIME, 0, false }, -#ifdef MS_DIRSYNC - { "dirsync", MS_DIRSYNC, MS_DIRSYNC, 0, false }, -#endif - { "_netdev", 0, 0, 0, false }, - { "auto", 0, 0, 0, false }, - { "noauto", 0, 0, XFLAG_NOAUTO, false }, - { "user", 0, 0, 0, false }, - { "nouser", 0, 0, 0, false }, -}; - -int wrapper_exit_code = 1; - -static void -showHelp(int fd, char const *cmd, int res) -{ - VSERVER_DECLARE_CMD(cmd); - - WRITE_MSG(fd, "Usage: "); - WRITE_STR(fd, cmd); - WRITE_MSG(fd, - " [--help] [--version] [--bind] [--move] [--rbind] [-t ] [--chroot]\n" - " [--mtab ] [--fstab ] [--rootfs yes|no|only]\n" - " [-n] -a|([-o ] [--] )\n\n" - "Executes mount-operations under the current directory: it assumes sources in\n" - "the current root-dir while destinations are expected in the chroot environment.\n\n" - "For non-trivial mount-operations it uses the external 'mount' program which\n" - "can be overridden by the $MOUNT environment variable.\n\n" - "Options:\n" - " --bind|move|rbind ... set the correspond flags; with this options\n" - " the mount will be executed internally without\n" - " calling an external mount program.\n" - " -t ... assume the given filesystem type\n" - " -o ... set additional options; see mount(2) for details\n" - " -n ... do not update the mtab-file\n" - " --mtab ... use as an alternative mtab file\n" - " [default: /etc/mtab]\n" - " --chroot ... chroot into the current directory before\n" - " mounting the filesystem\n" - " --fstab ... use as an alternative fstab file;\n" - " this option has an effect only with the '-a'\n" - " option [default: /etc/fstab]\n" - " --rootfs yes|no|only ... specifies how to handle an entry for a rootfs\n" - " ('/') when processing an fstab file. 'yes' will\n" - " mount it among the other entries, 'only' will\n" - " mount only the rootfs entry, and 'no' will ignore\n" - " it and mount only the other entries [default: yes]\n" - " -a ... mount everything listed in the fstab-file\n\n" - " ... the source-filesystem; this path is absolute\n" - " to the current root-filesystem. Only valid\n" - " without the '-a' option.\n" - " ... the destination mount-point; when used with\n" - " '--chroot', this path is relative to the current\n" - " directory. Only valid without the '-a' option\n\n" - "Please report bugs to " PACKAGE_BUGREPORT "\n"); - - exit(res); -} - -static void -showVersion() -{ - WRITE_MSG(1, - "secure-mount " VERSION " -- secure mounting of directories\n" - "This program is part of " PACKAGE_STRING "\n\n" - "Copyright (C) 2003 Enrico Scholz\n" - VERSION_COPYRIGHT_DISCLAIMER); - exit(0); -} - -inline static bool -isSameObject(struct stat const *lhs, - struct stat const *rhs) -{ - return (lhs->st_dev==rhs->st_dev && - lhs->st_ino==rhs->st_ino); -} - -static int -fchroot(int fd) -{ - if (fchdir(fd)==-1 || chroot(".")==-1) return -1; - return 0; -} - -static int -writeX(int fd, void const *buf, size_t len) -{ - if ((size_t)(write(fd, buf, len))!=len) return -1; - return 0; -} - -static int -writeStrX(int fd, char const *str) -{ - return writeX(fd, str, strlen(str)); -} - -static inline char const * -getType(struct MountInfo const *mnt) -{ - if (mnt->type==0) return "none"; - else if (strncmp(mnt->type, "ext", 3)==0) return "ufs"; - else return mnt->type; -} - -inline static void -restoreRoot(struct Options const *opt) -{ - if (opt->do_chroot!=0 && fchroot(opt->cur_rootdir_fd)==-1) { - perror("secure-mount: fchdir(\"/\")"); - WRITE_MSG(2, "Failed to restore root-directory; aborting\n"); - exit(1); - } -} - -static int -updateMtab(struct MountInfo const *mnt, struct Options const *opt) -{ - int res = -1; - int fd; - assert(opt->mtab!=0); - - if (opt->do_chroot && fchroot(opt->cur_dir_fd)==-1) { - perror("secure-mount: fchroot(\".\")"); - return -1; - } - - fd=open(opt->mtab, O_CREAT|O_APPEND|O_WRONLY, 0644); - - if (fd==-1) { - perror("secure-mount: open()"); - goto err0; - } - - if (flock(fd, LOCK_EX)==-1) { - perror("secure-mount: flock()"); - goto err1; - } - - if (writeStrX(fd, mnt->src)==-1 || - writeStrX(fd, " ")==-1 || - writeStrX(fd, mnt->dst)==-1 || - writeStrX(fd, " ")==-1 || - writeStrX(fd, getType(mnt))==-1 || - writeStrX(fd, " ")==-1 || - writeStrX(fd, mnt->data ? mnt->data : "defaults")==-1 || - writeStrX(fd, " 0 0\n")==-1) { - perror("secure-mount: write()"); - goto err1; - } - - res = 0; - - err1: close(fd); - err0: - restoreRoot(opt); - return res; -} - -static bool -callExternalMount(struct MountInfo const *mnt) -{ - char const * argv[10]; - size_t idx = 0; - pid_t pid; - int status; - char const * mount_prog = getenv("MOUNT"); - - if (mount_prog==0) mount_prog = MOUNT_PROG; - - argv[idx++] = mount_prog; - argv[idx++] = "-n"; - if (mnt->flag & MS_BIND) argv[idx++] = "--bind"; - else if (mnt->flag & MS_MOVE) argv[idx++] = "--move"; - - argv[idx++] = "-o"; - if (mnt->data && *mnt->data && - strcmp(mnt->data, "defaults")!=0) { - if (mnt->mask & MS_NODEV) - argv[idx++] = mnt->data; - else { - char * tmp = alloca(strlen(mnt->data) + sizeof("nodev,")); - strcpy(tmp, "nodev,"); - strcat(tmp, mnt->data); - argv[idx++] = tmp; - } - } - else - argv[idx++] = "nodev"; - - if (mnt->type) { - argv[idx++] = "-t"; - argv[idx++] = mnt->type; - } - - argv[idx++] = mnt->src; - argv[idx++] = "."; - argv[idx] = 0; - - pid = fork(); - if (pid==-1) { - perror("secure-mount: fork()"); - return false; - } - - if (pid==0) { - execv(mount_prog, const_cast(char **)(argv)); - PERROR_Q("secure-mount: execv", mount_prog); - exit(1); - } - - if (wait4(pid, &status, 0, 0)==-1) { - perror("secure-mount: wait4()"); - return false; - } - - return (WIFEXITED(status)) && (WEXITSTATUS(status)==0); -} - -inline static bool -secureChdir(char const *dir, struct Options const *opt) -{ - int dir_fd; - bool res = false; - - if (opt->do_chroot!=0 && fchroot(opt->cur_dir_fd)==-1) { - perror("secure-mount: fchroot(\".\")"); - return false; - } - - if (chdir(dir)==-1) { - PERROR_Q("secure-mount: chdir", dir); - goto err; - } - - dir_fd = open(".", O_RDONLY|O_DIRECTORY); - if (dir_fd==-1) { - perror("secure-mount: open(\".\")"); - goto err; - } - - restoreRoot(opt); - if (fchdir(dir_fd)==-1) - PERROR_Q("secure-mount: fchdir", dir); - else - res = true; - - close(dir_fd); - return res; - - err: - restoreRoot(opt); - return false; -} - -static bool -canHandleInternal(struct MountInfo const *mnt) -{ - static char const * FS[] = { - "tmpfs", "sysfs", "proc", "sockfs", "pipefs", "futexfs", - "inotifyfs", "devpts", "ext3", "ext2", "ramfs", - "hugetlbfs", "usbfs", "binfmt_misc", - 0 - }; - char const ** i; - - if (mnt!=0) return false; - else if ((mnt->flag & (MS_BIND|MS_MOVE))!=0) return true; - else if (mnt->type==0) return false; - - for (i=FS+0; *i!=0; ++i) - if (strcmp(mnt->type, *i)==0) return true; - - return false; -} - -static bool -mountSingle(struct MountInfo const *mnt, struct Options const *opt) -{ - assert(mnt->dst!=0); - - if (!secureChdir(mnt->dst, opt)) - return false; - - if (canHandleInternal(mnt)) { - unsigned long flag = mnt->flag; - if ((flag & MS_NODEV)!=0) flag |= MS_NODEV; - - if (mount(mnt->src, ".", - mnt->type ? mnt->type : "", - flag, mnt->data)==-1) { - perror("secure-mount: mount()"); - return false; - } - } - else if (!callExternalMount(mnt)) - return false; - - if (!opt->ignore_mtab && - updateMtab(mnt, opt)==-1) { - WRITE_MSG(2, "Failed to update mtab-file\n"); - // no error - } - - return true; -} - -static struct FstabOption const * -searchOption(char const *opt, size_t len) -{ - struct FstabOption const * i; - for (i=FSTAB_OPTIONS+0; iopt, opt, len)==0) return i; - - return 0; -} - -static bool -transformOptionList(struct MountInfo *info, size_t UNUSED *col) -{ - char const * ptr = info->data; - - do { - char const * pos = strchr(ptr, ','); - struct FstabOption const * opt; - - if (pos==0) pos = ptr+strlen(ptr); - opt = searchOption(ptr, pos-ptr); - - if (opt!=0) { - info->flag &= ~opt->mask; - info->flag |= opt->flag; - info->mask |= opt->mask; - info->xflag |= opt->xflag; - } - - if (*pos!='\0') - ptr = pos+1; - else - ptr = pos; - - } while (*ptr!='\0'); - - return true; -} - -#define MOVE_TO_NEXT_FIELD(PTR,ALLOW_EOL) \ - while (!isspace(*PTR) && *PTR!='\0') ++PTR; \ - if (col) *col = buf-start_buf+1; \ - if (!(ALLOW_EOL) && *PTR=='\0') return prFAIL; \ - *PTR++ = '\0'; \ - while (isspace(*PTR)) ++PTR - -static enum {prDOIT, prFAIL, prIGNORE} - parseFstabLine(struct MountInfo *info, char *buf, size_t *col) -{ - char const * const start_buf = buf; - size_t err_col; - - while (isspace(*buf)) ++buf; - if (*buf=='#' || *buf=='\0') return prIGNORE; - - info->src = buf; - MOVE_TO_NEXT_FIELD(buf, false); - info->dst = buf; - MOVE_TO_NEXT_FIELD(buf, false); - info->type = buf; - MOVE_TO_NEXT_FIELD(buf, false); - err_col = buf-start_buf+1; - info->data = buf; - MOVE_TO_NEXT_FIELD(buf, true); - - info->flag = MS_NODEV; - info->mask = 0; - info->xflag = 0; - - if (strcmp(info->type, "swap") ==0) return prIGNORE; - else if (strcmp(info->type, "none") ==0) info->type = 0; - else if (strcmp(info->type, "devpts")==0) { - info->mask |= MS_NODEV; - info->flag &= ~MS_NODEV; - } - - if (col) *col = err_col; - if (!transformOptionList(info,col)) return prFAIL; - if (info->xflag & XFLAG_NOAUTO) return prIGNORE; - - return prDOIT; -} - -#undef MOVE_TO_NEXT_FIELD - -static void -showFstabPosition(int fd, char const *fname, size_t line_nr, size_t col_nr) -{ - char buf[3*sizeof(line_nr)*2 + 4]; - size_t len = utilvserver_fmt_uint(buf+1, line_nr)+1; - - buf[0] = ':'; - buf[len++] = ':'; - len += utilvserver_fmt_uint(buf+len, col_nr); - WRITE_STR(fd, fname); - Vwrite(fd, buf, len); -} - - -static bool -mountFstab(struct Options const *opt) -{ - bool res = false; - int fd; - off_t len; - - assert(opt->fstab!=0); - fd = open(opt->fstab, O_RDONLY); - if (fd==-1) { - perror("secure-mount: open()"); - goto err0; - } - - len = lseek(fd, 0, SEEK_END); - if (len==-1 || - lseek(fd, 0, SEEK_SET)==-1) { - perror("secure-mount: lseek()"); - goto err1; - } - - { - char buf[len+2]; - char *ptr, *ptrptr; - size_t line_nr=0, col_nr; - - if (read(fd, buf, len+1)!=len) { - perror("secure-mount: read()"); - goto err1; - } - buf[len] = '#'; // workaround for broken dietlibc strtok_r() - // implementation - buf[len+1] = '\0'; - ptrptr = buf; - - while ((ptr=strsep(&ptrptr, "\n")) != 0) { - struct MountInfo mnt; - ++line_nr; - - switch (parseFstabLine(&mnt, ptr, &col_nr)) { - case prFAIL : - showFstabPosition(2, opt->fstab, line_nr, col_nr); - WRITE_MSG(2, ": syntax error\n"); - goto err1; - - case prIGNORE : break; - case prDOIT : { - bool is_rootfs = (strcmp(mnt.dst, "/")==0); - Echdir("/"); - if (( is_rootfs && opt->rootfs==rfsNO) || - (!is_rootfs && opt->rootfs==rfsONLY)) { /* ignore the entry */ } - else if (!mountSingle(&mnt, opt)) { - showFstabPosition(2, opt->fstab, line_nr, 1); - WRITE_MSG(2, ": failed to mount fstab-entry\n"); - } - break; - } - default : - assert(false); - } - } - } - - res = true; - - err1: close(fd); - err0: return res; -} - -static void -initFDs(struct Options *opt) -{ - opt->cur_dir_fd = Eopen(".", O_RDONLY|O_DIRECTORY, 0); - opt->cur_rootdir_fd = Eopen("/", O_RDONLY|O_DIRECTORY, 0); - - Efcntl(opt->cur_dir_fd, F_SETFD, FD_CLOEXEC); - Efcntl(opt->cur_rootdir_fd, F_SETFD, FD_CLOEXEC); -} - -static RootFsOption -parseRootFS(char const *str) -{ - if (strcasecmp(str, "yes")==0) return rfsYES; - else if (strcasecmp(str, "no")==0) return rfsNO; - else if (strcasecmp(str, "only")==0) return rfsONLY; - else { - WRITE_MSG(2, "secure-mount: invalid option for '--rootfs': '"); - WRITE_STR(2, str); - WRITE_MSG(2, "'\n"); - exit(wrapper_exit_code); - } -} - -int main(int argc, char *argv[]) -{ - struct MountInfo mnt = { - .src = 0, - .dst = 0, - .type = 0, - .flag = 0, - .xflag = 0, - .data = 0, - }; - - struct Options opt = { - .mtab = "/etc/mtab", - .fstab = "/etc/fstab", - .do_chroot = 0, - .ignore_mtab = false, - .mount_all = false, - .cur_dir_fd = -1, - .cur_rootdir_fd = -1, - .rootfs = rfsYES - }; - - while (1) { - int c = getopt_long(argc, argv, "ht:nao:", CMDLINE_OPTIONS, 0); - if (c==-1) break; - - switch (c) { - case 'h' : showHelp(1, argv[0], 0); - case 'v' : showVersion(); - case 't' : mnt.type = optarg; break; - case 'n' : opt.ignore_mtab = true; break; - case 'a' : opt.mount_all = true; break; - case 'o' : mnt.data = optarg; break; - case OPTION_RBIND : mnt.flag |= MS_REC; /*@fallthrough@*/ - case OPTION_BIND : mnt.flag |= MS_BIND; break; - case OPTION_MOVE : mnt.flag |= MS_MOVE; break; - case OPTION_MTAB : opt.mtab = optarg; break; - case OPTION_FSTAB : opt.fstab = optarg; break; - case OPTION_CHROOT: opt.do_chroot = true; break; - case OPTION_ROOTFS: opt.rootfs = parseRootFS(optarg); break; - case OPTION_SECURE: - WRITE_MSG(2, "secure-mount: The '--secure' option is deprecated...\n"); - break; - default : - WRITE_MSG(2, "Try '"); - WRITE_STR(2, argv[0]); - WRITE_MSG(2, " --help\" for more information.\n"); - return EXIT_FAILURE; - break; - } - } - - - if (opt.mount_all && optind and '-a' at the same time\n"); - return EXIT_FAILURE; - } - - initFDs(&opt); - signal(SIGCHLD, SIG_DFL); - - if (opt.mount_all) { - if (!mountFstab(&opt)) return EXIT_FAILURE; - else return EXIT_SUCCESS; - } - - if (optind+2!=argc) { - WRITE_MSG(2, "Invalid pair specified\n"); - return EXIT_FAILURE; - } - - if (mnt.data) { - mnt.data = strdup(mnt.data); - if (!transformOptionList(&mnt, 0)) { - WRITE_MSG(2, "Invalid options specified\n"); - return EXIT_FAILURE; - } - } - - mnt.src = argv[optind++]; - mnt.dst = argv[optind++]; - - if (!mountSingle(&mnt, &opt)) return EXIT_FAILURE; - - return EXIT_SUCCESS; -}