X-Git-Url: http://git.linux-vserver.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=util-vserver%2Fsrc%2Fchcontext.c;h=8f40ad3598c65c3d5135124e1fd4e14539d572c1;hb=86a23427cca76a5a9befd60c85535a0e738bc3c6;hp=af60dec4aab8b754770c4c5a96b03bcfcb111eb6;hpb=da691fd4bad5e187b307b7fd86bf5fd61e47c3fc;p=util-vserver.git diff --git a/util-vserver/src/chcontext.c b/util-vserver/src/chcontext.c index af60dec..8f40ad3 100644 --- a/util-vserver/src/chcontext.c +++ b/util-vserver/src/chcontext.c @@ -1,6 +1,6 @@ // $Id$ -// Copyright (C) 2003 Enrico Scholz +// Copyright (C) 2003,2004 Enrico Scholz // based on chcontext.cc by Jacques Gelinas // // This program is free software; you can redistribute it and/or modify @@ -22,262 +22,424 @@ does little more than mapping command line option to the system call arguments. */ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "util.h" +#include "vserver.h" +#include "internal.h" + #include #include #include #include #include +#include +#include +#include -#include "linuxcaps.h" -#include "vserver.h" +#define ENSC_WRAPPERS_VSERVER 1 +#define ENSC_WRAPPERS_UNISTD 1 +#define ENSC_WRAPPERS_FCNTL 1 +#include + +#define CMD_HELP 0x1000 +#define CMD_VERSION 0x1001 +#define CMD_CAP 0x4000 +#define CMD_CTX 0x4001 +#define CMD_DISCONNECT 0x4002 +#define CMD_DOMAINNAME 0x4003 +#define CMD_FLAG 0x4004 +#define CMD_HOSTNAME 0x4005 +#define CMD_SECURE 0x4006 +#define CMD_SILENT 0x4007 + +int wrapper_exit_code = 255; + +struct option const +CMDLINE_OPTIONS[] = { + { "help", no_argument, 0, CMD_HELP }, + { "version", no_argument, 0, CMD_VERSION }, + { "cap", required_argument, 0, CMD_CAP }, + { "ctx", required_argument, 0, CMD_CTX }, + { "disconnect", no_argument, 0, CMD_DISCONNECT }, + { "domainname", required_argument, 0, CMD_DOMAINNAME }, + { "flag", required_argument, 0, CMD_FLAG }, + { "hostname", required_argument, 0, CMD_HOSTNAME }, + { "secure", no_argument, 0, CMD_SECURE }, + { "silent", no_argument, 0, CMD_SILENT }, + { 0,0,0,0 } +}; + +struct Arguments { + size_t nbctx; + xid_t ctxs[16]; + bool do_disconnect; + bool do_silent; + unsigned int flags; + uint32_t remove_caps; + uint32_t add_caps; + char const * hostname; + char const * domainname; +}; + +static struct Arguments const * global_args = 0; -static void usage() +static void +showHelp(int fd, char const *cmd, int res) { - fprintf (stderr,"chcontext version %s\n",VERSION); - fprintf (stderr - ,"chcontext [ options ] command arguments ...\n" - "\n" - "chcontext allocate a new security context and executes\n" - "a command in that context.\n" - "By default, a new/unused context is allocated\n" - "\n" - - "--cap CAP_NAME\n" - "\tAdd a capability from the command. This option may be\n" - "\trepeated several time.\n" - "\tSee /usr/include/linux/capability.h\n" - "\tIn general, this option is used with the --secure option\n" - "\t--secure removes most critical capabilities and --cap\n" - "\tadds specific ones.\n" - "\n" - - "--cap !CAP_NAME\n" - "\tRemove a capability from the command. This option may be\n" - "\trepeated several time.\n" - "\tSee /usr/include/linux/capability.h\n" - "\n" - "--ctx num\n" - "\tSelect the context. On root in context 0 is allowed to\n" - "\tselect a specific context.\n" - "\tContext number 1 is special. It can see all processes\n" - "\tin any contexts, but can't kill them though.\n" - "\tOption --ctx may be repeated several times to specify up to 16 contexts.\n" - - "--disconnect\n" - "\tStart the command in background and make the process\n" - "\ta child of process 1.\n" - - "--domainname new_domainname\n" - "\tSet the domainname (NIS) in the new security context.\n" - "\tUse \"none\" to unset the domain name.\n" - - "--flag\n" - "\tSet one flag in the new or current security context. The following\n" - "\tflags are supported. The option may be used several time.\n" - "\n" - "\tfakeinit: The new process will believe it is process number 1.\n" - " Useful to run a real /sbin/init in a vserver.\n" - "\tlock: The new process is trapped and can't use chcontext anymore.\n" - "\tsched: The new process and its children will share a common \n" - " execution priority.\n" - "\tnproc: Limit the number of process in the vserver according to\n" - " ulimit setting. Normally, ulimit is a per user thing.\n" - " With this flag, it becomes a per vserver thing.\n" - "\tprivate: No one can join this security context once created.\n" - "\tulimit: Apply the current ulimit to the whole context\n" - - "--hostname new_hostname\n" - "\tSet the hostname in the new security context\n" - "\tThis is need because if you create a less privileged\n" - "\tsecurity context, it may be unable to change its hostname\n" - - "--secure\n" - "\tRemove all the capabilities to make a virtual server trustable\n" - - "--silent\n" - "\tDo not print the allocated context number.\n" - "\n" - "Information about context is found in /proc/self/status\n"); + WRITE_MSG(fd, "Usage: "); + WRITE_STR(fd, cmd); + WRITE_MSG(fd, + " [--cap [!]] [--secure] [--ctx ] [--disconnect]\n" + " [--domainname ] [--hostname ] [--flag +]\n" + " [--silent] [--] command arguments ...\n" + "\n" + "chcontext allocate a new security context and executes\n" + "a command in that context.\n" + "By default, a new/unused context is allocated\n" + "\n" + "--cap CAP_NAME\n" + " Add a capability from the command. This option may be\n" + " repeated several time.\n" + " See /usr/include/linux/capability.h\n" + " In general, this option is used with the --secure option\n" + " --secure removes most critical capabilities and --cap\n" + " adds specific ones.\n" + "\n" + + "--cap !CAP_NAME\n" + " Remove a capability from the command. This option may be\n" + " repeated several time.\n" + " See /usr/include/linux/capability.h\n" + "\n" + "--ctx num\n" + " Select the context. On root in context 0 is allowed to\n" + " select a specific context.\n" + " Context number 1 is special. It can see all processes\n" + " in any contexts, but can't kill them though.\n" + " Option --ctx may be repeated several times to specify up to 16 contexts.\n" + + "--disconnect\n" + " Start the command in background and make the process\n" + " a child of process 1.\n" + + "--domainname new_domainname\n" + " Set the domainname (NIS) in the new security context.\n" + " Use \"none\" to unset the domain name.\n" + + "--flag\n" + " Set one flag in the new or current security context. The following\n" + " flags are supported. The option may be used several time.\n" + "\n" + " fakeinit: The new process will believe it is process number 1.\n" + " Useful to run a real /sbin/init in a vserver.\n" + " lock: The new process is trapped and can't use chcontext anymore.\n" + " sched: The new process and its children will share a common \n" + " execution priority.\n" + " nproc: Limit the number of process in the vserver according to\n" + " ulimit setting. Normally, ulimit is a per user thing.\n" + " With this flag, it becomes a per vserver thing.\n" + " private: No one can join this security context once created.\n" + " ulimit: Apply the current ulimit to the whole context\n" + + "--hostname new_hostname\n" + " Set the hostname in the new security context\n" + " This is need because if you create a less privileged\n" + " security context, it may be unable to change its hostname\n" + + "--secure\n" + " Remove all the capabilities to make a virtual server trustable\n" + + "--silent\n" + " Do not print the allocated context number.\n" + "\n" + "Please report bugs to " PACKAGE_BUGREPORT "\n"); + + exit(res); } +static void +showVersion() +{ + WRITE_MSG(1, + "chcontext " VERSION " -- allocates/enters a security context\n" + "This program is part of " PACKAGE_STRING "\n\n" + "Copyright (C) 2003,2004 Enrico Scholz\n" + VERSION_COPYRIGHT_DISCLAIMER); + exit(0); +} + +static inline void +setCap(char const *str, uint32_t *add_caps, uint32_t *remove_caps) +{ + uint32_t *cap; + int bit; + + if (str[0] != '!') + cap = add_caps; + else { + cap = remove_caps; + str++; + } + + bit = vc_text2cap(str); + + if (bit!=-1) *cap |= (1<do_silent) { + WRITE_MSG(1, "Host name is now "); + WRITE_STR(1, name); + WRITE_MSG(1, "\n"); + } +} + +static inline ALWAYSINLINE void +setDomainname(char const *name) +{ + if (name == NULL) return; + + if (setdomainname(name, strlen(name))==-1) { + perror("setdomainname()"); + exit(255); + } + if (!global_args->do_silent) { + WRITE_MSG(1, "Domain name is now "); + WRITE_STR(1, name); + WRITE_MSG(1, "\n"); + } +} + +static inline ALWAYSINLINE void +tellContext(xid_t ctx) +{ + char buf[sizeof(xid_t)*3+2]; + size_t l; + + if (global_args->do_silent) return; + + l = utilvserver_fmt_long(buf,ctx); + + WRITE_MSG(2, "New security context is "); + write(2, buf, l); + WRITE_MSG(2, "\n"); +} + +static inline ALWAYSINLINE int +initSync(int p[2]) +{ + if (!global_args->do_disconnect) return 0; + + Epipe(p); + fcntl(p[1], F_SETFD, FD_CLOEXEC); + return Efork(); +} + +static inline ALWAYSINLINE void +doSyncStage1(int p[2]) +{ + int fd; + + if (!global_args->do_disconnect) return; + + fd = Eopen("/dev/null", O_RDONLY, 0); + Esetsid(); + Edup2(fd, 0); + Eclose(p[0]); + if (fd!=0) Eclose(fd); + Ewrite(p[1], ".", 1); +} + +static inline ALWAYSINLINE void +doSyncStage2(int p[2]) +{ + if (!global_args->do_disconnect) return; + + Ewrite(p[1], "X", 1); +} + +static void +waitOnSync(pid_t pid, int p[2]) +{ + int c; + size_t l; + + assert(global_args->do_disconnect); + assert(pid!=0); + + Eclose(p[1]); + l = Eread(p[0], &c, 1); + if (l!=1) exitLikeProcess(pid); + l = Eread(p[0], &c, 1); + if (l!=0) exitLikeProcess(pid); +} int main (int argc, char *argv[]) { - int ret = -1; - int i; - int nbctx = 0; - int ctxs[16]; - int disconnect = 0; - int fakeinit = 0; - int silent = 0; - int flags = 0; - unsigned remove_cap = 0; - unsigned add_cap = 0; - unsigned long secure = (1<= 16){ - fprintf (stderr,"Too many context, max 16, ignored.\n"); - }else{ - ctxs[nbctx++] = atoi(opt); - } - i++; - }else if (strcmp(arg,"--disconnect")==0){ - disconnect = 1; - }else if (strcmp(arg,"--silent")==0){ - silent = 1; - }else if (strcmp(arg,"--flag")==0){ - if (strcmp(opt,"lock")==0){ - flags |= 1; - }else if (strcmp(opt,"sched")==0){ - flags |= 2; - }else if (strcmp(opt,"nproc")==0){ - flags |= 4; - }else if (strcmp(opt,"private")==0){ - flags |= 8; - }else if (strcmp(opt,"fakeinit")==0){ - fakeinit = 1; - flags |= 16; - }else if (strcmp(opt,"hideinfo")==0){ - flags |= 32; - }else if (strcmp(opt,"ulimit")==0){ - flags |= 64; - }else{ - fprintf (stderr,"Unknown flag %s\n",opt); - } - i++; - }else if (strcmp(arg,"--cap")==0){ - static struct { - const char *option; - int bit; - }tbcap[]={ - // The following capabilities are normally available - // to vservers administrator, but are place for - // completeness - {"CAP_CHOWN",CAP_CHOWN}, - {"CAP_DAC_OVERRIDE",CAP_DAC_OVERRIDE}, - {"CAP_DAC_READ_SEARCH",CAP_DAC_READ_SEARCH}, - {"CAP_FOWNER",CAP_FOWNER}, - {"CAP_FSETID",CAP_FSETID}, - {"CAP_KILL",CAP_KILL}, - {"CAP_SETGID",CAP_SETGID}, - {"CAP_SETUID",CAP_SETUID}, - {"CAP_SETPCAP",CAP_SETPCAP}, - {"CAP_SYS_TTY_CONFIG",CAP_SYS_TTY_CONFIG}, - {"CAP_LEASE",CAP_LEASE}, - {"CAP_SYS_CHROOT",CAP_SYS_CHROOT}, - - // Those capabilities are not normally available - // to vservers because they are not needed and - // may represent a security risk - {"CAP_LINUX_IMMUTABLE",CAP_LINUX_IMMUTABLE}, - {"CAP_NET_BIND_SERVICE",CAP_NET_BIND_SERVICE}, - {"CAP_NET_BROADCAST",CAP_NET_BROADCAST}, - {"CAP_NET_ADMIN", CAP_NET_ADMIN}, - {"CAP_NET_RAW", CAP_NET_RAW}, - {"CAP_IPC_LOCK", CAP_IPC_LOCK}, - {"CAP_IPC_OWNER", CAP_IPC_OWNER}, - {"CAP_SYS_MODULE",CAP_SYS_MODULE}, - {"CAP_SYS_RAWIO", CAP_SYS_RAWIO}, - {"CAP_SYS_PACCT", CAP_SYS_PACCT}, - {"CAP_SYS_ADMIN", CAP_SYS_ADMIN}, - {"CAP_SYS_BOOT", CAP_SYS_BOOT}, - {"CAP_SYS_NICE", CAP_SYS_NICE}, - {"CAP_SYS_RESOURCE",CAP_SYS_RESOURCE}, - {"CAP_SYS_TIME", CAP_SYS_TIME}, - {"CAP_MKNOD", CAP_MKNOD}, - {NULL,0} - }; - int j; - unsigned *cap = &add_cap; - if (opt[0] == '!'){ - cap = &remove_cap; - opt++; - } - for (j=0; tbcap[j].option != NULL; j++){ - if (strcasecmp(tbcap[j].option,opt)==0){ - *cap |= (1<0) + WRITE_MSG(2, "WARNING: More than one ctx not supported by this version\n"); + if (args.nbctx>=DIM_OF(args.ctxs)) { + WRITE_MSG(2, "Too many contexts given\n"); + exit(255); } - return ret; + args.ctxs[args.nbctx++] = atoi(optarg); + break; + + + default : + WRITE_MSG(2, "Try '"); + WRITE_STR(2, argv[0]); + WRITE_MSG(2, " --help\" for more information.\n"); + return 255; + break; + } + } + + if (optind>=argc) { + WRITE_MSG(2, "No command given; use '--help' for more information.\n"); + exit(255); + } + + if (args.domainname && strcmp(args.domainname, "none")==0) + args.domainname = ""; + + if (args.nbctx == 0) + args.ctxs[args.nbctx++] = VC_RANDCTX; + + xflags = args.flags & 16; + + newctx = Evc_new_s_context(args.ctxs[0],0,args.flags&~16); + args.remove_caps &= (~args.add_caps); + setHostname(args.hostname); + setDomainname(args.domainname); + + pid = initSync(p); + + if (pid==0) { + if (args.remove_caps!=0 || xflags!=0) + Evc_new_s_context (VC_SAMECTX,args.remove_caps,xflags); + tellContext(args.ctxs[0]==VC_RANDCTX ? newctx : args.ctxs[0]); + + doSyncStage1(p); + execvp (argv[optind],argv+optind); + doSyncStage2(p); + + perror("execvp()"); + exit(255); + } + + waitOnSync(pid, p); + return EXIT_SUCCESS; } +#ifdef ENSC_TESTSUITE +#define FLAG_TEST(STR,EXP) \ + { \ + uint32_t flag=0; \ + setFlags(STR, &flag); \ + assert(flag==(EXP)); \ + } + +#define CAP_TEST(STR,EXP_ADD,EXP_DEL) \ + { \ + uint32_t add=0,del=0; \ + setCap(STR, &add, &del); \ + assert(add==(EXP_ADD)); \ + assert(del==(EXP_DEL)); \ + } + +void +test() +{ + FLAG_TEST("lock", 1); + FLAG_TEST("lock,sched", 3); + + CAP_TEST("CHOWN", 1, 0); + CAP_TEST("CAP_CHOWN", 1, 0); + CAP_TEST("!CHOWN", 0, 1); + CAP_TEST("!CAP_CHOWN", 0, 1); +} +#endif