X-Git-Url: http://git.onelab.eu/?p=util-vserver.git;a=blobdiff_plain;f=src%2Fchcontext.c;h=e41203c41b5647b07d9eccf831c02aa3e8541464;hp=236e0ccec0be4efc1789b3982c3a8bd7e22dfb5c;hb=ec4370f7ebd7fb0ce7f002f5bf2c74f03acd3ec1;hpb=06e1018272502e1d15d6d8f32b80fa96420785b8 diff --git a/src/chcontext.c b/src/chcontext.c index 236e0cc..e41203c 100644 --- a/src/chcontext.c +++ b/src/chcontext.c @@ -1,6 +1,6 @@ -// $Id: chcontext.c,v 1.1.4.3 2004/01/07 16:24:01 ensc Exp $ +// $Id: chcontext.c 2403 2006-11-24 23:06:08Z dhozac $ -// 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 @@ -25,271 +25,401 @@ #ifdef HAVE_CONFIG_H # include #endif -#include "compat.h" + +#include "util.h" +#include "vserver.h" +#include "internal.h" +#include "lib_internal/jail.h" #include #include #include #include #include +#include +#include +#include +#include +#include -#include "linuxcaps.h" -#include "vserver.h" +#define ENSC_WRAPPERS_PREFIX "chcontext: " +#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 }, + { "xid", 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; -#ifndef CAP_QUOTACTL -# define CAP_QUOTACTL 29 +static void +showHelp(int fd, char const *cmd, int res) +{ + VSERVER_DECLARE_CMD(cmd); + +#if !defined(VC_ENABLE_API_COMPAT) && !defined(VC_ENABLE_API_LEGACY) + WRITE_MSG(1, "ERROR: tools were built without legacy API support; chcontext will not work!\n\n"); #endif + + WRITE_MSG(fd, "Usage: "); + WRITE_STR(fd, cmd); + WRITE_MSG(fd, + " [--cap [!]] [--secure] [--xid ] [--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" + "--xid 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 --xid 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" -static void usage() + "--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-compat " 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); +} + +#if defined(VC_ENABLE_API_COMPAT) || defined(VC_ENABLE_API_LEGACY) + +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("chcontext: 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(1, "New security context is "); + Vwrite(1, buf, l); + WRITE_MSG(1, "\n"); +} + +#include "context-sync.hc" + +#endif + int main (int argc, char *argv[]) { - int ret = -1; - int i; - int nbctx = 0; - int ctxs[16]; - int disconnect = 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){ - 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}, - {"CAP_QUOTACTL", CAP_QUOTACTL}, - {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++] = Evc_xidopt2xid(optarg, true); + break; +#else + case CMD_CAP : + case CMD_SECURE : + case CMD_FLAG : + case CMD_CTX : break; +#endif + + default : + WRITE_MSG(2, "Try '"); + WRITE_STR(2, argv[0]); + WRITE_MSG(2, " --help' for more information.\n"); + return 255; + break; + } + } + +#if defined(VC_ENABLE_API_COMPAT) || defined(VC_ENABLE_API_LEGACY) + 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_DYNAMIC_XID; + + xflags = args.flags & S_CTX_INFO_INIT; + args.flags &= ~S_CTX_INFO_INIT; + + pid = initSync(p, args.do_disconnect); + if (pid==0) { + doSyncStage0(p, args.do_disconnect); + + newctx = Evc_new_s_context(args.ctxs[0],0,args.flags); + args.remove_caps &= (~args.add_caps); + setHostname(args.hostname); + setDomainname(args.domainname); + + if (args.remove_caps!=0 || xflags!=0) + Evc_new_s_context (VC_SAMECTX,args.remove_caps,xflags); + tellContext(args.ctxs[0]==VC_DYNAMIC_XID ? newctx : args.ctxs[0]); + + doSyncStage1(p, args.do_disconnect); + execvp (argv[optind],argv+optind); + doSyncStage2(p, args.do_disconnect); + + PERROR_Q("chcontext: execvp", argv[optind]); + exit(255); + } + + waitOnSync(pid, p, args.ctxs[0]!=VC_DYNAMIC_XID); + return EXIT_SUCCESS; +#else + WRITE_MSG(2, "chcontext: tools were built without legacy API support; can not continue\n"); + return EXIT_FAILURE; +#endif } +#if defined(VC_ENABLE_API_COMPAT) || defined(VC_ENABLE_API_LEGACY) + +#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 + +#else +void test() {} +#endif