X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=src%2Fvsh.c;h=da93d56023b837d87aa9f58fb4c09c09c7e96bba;hb=2fd3b085966352e2e796b78d64e78fc4be01c9d2;hp=0fdd59c7170e4cb5b9068b8ddcb7cb2984ffef47;hpb=a2dc8df6ad5d79424e3c62df1479bca3c1fec752;p=util-vserver.git diff --git a/src/vsh.c b/src/vsh.c index 0fdd59c..da93d56 100644 --- a/src/vsh.c +++ b/src/vsh.c @@ -37,28 +37,49 @@ #include #include #include +#include #include #include +#include //-------------------------------------------------------------------- -#include "linuxcaps.h" #include "vserver.h" +#undef CONFIG_VSERVER_LEGACY + /* Null byte made explicit */ #define NULLBYTE_SIZE 1 /* Base for all vserver roots for chroot */ #define VSERVER_ROOT_BASE "/vservers" +static int +_PERROR(const char *format, char *file, int line, int _errno, ...) +{ + va_list ap; + + va_start(ap, _errno); + fprintf(stderr, "%s:%d: ", file, line); + vfprintf(stderr, format, ap); + if (_errno) + fprintf(stderr, ": %s (%d)", strerror(_errno), _errno); + fputs("\n", stderr); + fflush(stderr); + + return _errno; +} + +#define PERROR(format, args...) _PERROR(format, __FILE__, __LINE__, errno, ## args) + /* Change to root:root (before entering new context) */ static int setuidgid_root() { if (setgid(0) < 0) { - fprintf(stderr, "setgid error\n"); + PERROR("setgid(0)"); return -1; } if (setuid(0) < 0) { - fprintf(stderr, "setuid error\n"); + PERROR("setuid(0)"); return -1; } return 0; @@ -70,7 +91,7 @@ static void compute_new_root(char *base, char **root, uid_t uid) struct passwd *pwd; if ((pwd = getpwuid(uid)) == NULL) { - perror("vserver: getpwuid error "); + PERROR("getpwuid(%d)", uid); exit(1); } @@ -79,7 +100,7 @@ static void compute_new_root(char *base, char **root, uid_t uid) strlen(pwd->pw_name) + NULLBYTE_SIZE; (*root) = (char *)malloc(root_len); if ((*root) == NULL) { - perror("vserver: malloc error "); + PERROR("malloc(%d)", root_len); exit(1); } @@ -96,7 +117,7 @@ static int sandbox_file_exists(char *sandbox_root, char *relpath) len = strlen(sandbox_root) + strlen(relpath) + NULLBYTE_SIZE; if ((file = (char *)malloc(len)) == NULL) { - perror("vserver: malloc error "); + PERROR("malloc(%d)", len); exit(1); } sprintf(file, "%s%s", sandbox_root, relpath); @@ -120,7 +141,7 @@ static int devpts_mounted(char *sandbox_root) return sandbox_file_exists(sandbox_root, "/dev/pts/0"); } -static void mount_proc(char *sandbox_root,uid_t uid) +static void mount_proc(char *sandbox_root) { char *source = "/proc"; char *target; @@ -128,7 +149,7 @@ static void mount_proc(char *sandbox_root,uid_t uid) len = strlen(sandbox_root) + strlen("/") + strlen("proc") + NULLBYTE_SIZE; if ((target = (char *)malloc(len)) == NULL) { - perror("vserver: malloc error "); + PERROR("malloc(%d)", len); exit(1); } @@ -148,7 +169,7 @@ static void mount_devpts(char *sandbox_root) len = strlen(sandbox_root) + strlen("/") + strlen("dev/pts") + NULLBYTE_SIZE; if ((target = (char *)malloc(len)) == NULL) { - perror("vserver: malloc error "); + PERROR("malloc(%d)", len); exit(1); } @@ -165,257 +186,257 @@ static int sandbox_chroot(uid_t uid) char *sandbox_root = NULL; compute_new_root(VSERVER_ROOT_BASE,&sandbox_root, uid); - mount_proc(sandbox_root,uid); + mount_proc(sandbox_root); mount_devpts(sandbox_root); if (chroot(sandbox_root) < 0) { - fprintf(stderr,"vserver: chroot error (%s): ",sandbox_root); - perror(""); + PERROR("chroot(%s)", sandbox_root); exit(1); } if (chdir("/") < 0) { - perror("vserver: chdir error "); + PERROR("chdir(/)"); exit(1); } return 0; } -#ifndef CAP_CONTEXT -# define CAP_CONTEXT 29 -#endif +#define WHITESPACE(buffer,index,len) \ + while(isspace((int)buffer[index])) \ + if (index < len) index++; else goto out; -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_CONTEXT", CAP_CONTEXT}, - {NULL,0} +struct resources { + char *name; + unsigned long long *limit; }; #define VSERVERCONF "/etc/vservers/" -static unsigned get_remove_cap(char *name) { - FILE *fb; - unsigned remove_cap; - - char *vserverconf; - int vserverconflen; - - remove_cap = /* NOTE: keep in sync with chcontext.c */ - (1<name; r++) + if ((p=strstr(&buffer[index],r->name))!=NULL) { + /* adjust index into buffer */ + index+= (p-&buffer[index])+strlen(r->name); - /* skip over whitespace */ - while(isspace((int)buffer[index])) { - if (index < len) { - index++; - } else { - /* parse error */ - goto out; - } - } + /* skip over whitespace */ + WHITESPACE(buffer,index,len); - /* expecting to see the opening " */ - if (buffer[index]!='"') { - /* parse error */ - goto out; - } + /* expecting to see = sign */ + if (buffer[index++]!='=') goto out; - /* check to see that we are still within bounds */ - if (index < len) { - index++; - } else { - /* parse error */ - goto out; - } + /* skip over whitespace */ + WHITESPACE(buffer,index,len); - /* search for the closing " */ - if((p=strstr(&buffer[index],"\""))==NULL) { - /* parse error */ - goto out; - } + /* expecting to see a digit for number */ + if (!isdigit((int)buffer[index])) goto out; - /* ok... we should now have a bunch of - CAP keys words within the quotes */ - - for (j=0; tbcap[j].option != NULL; j++){ - if ((p=strstr(buffer,tbcap[j].option))!=NULL){ - len = strlen(tbcap[j].option); - if (((isspace(*(p-1))) || (*(p-1)=='"')) && - ((isspace(*(p+len))) || (*(p+len)=='"'))) { - cap |= (1<limit = atoi(&buffer[index]); + break; } - remove_cap &= ~cap; - break; - } } - out: - /* close the conf file */ - fclose(fb); + out: + free(buffer); + } else { + fprintf(stderr,"cannot open %s\n",conf); } - return remove_cap; + free(conf); } -static int sandbox_processes(uid_t uid, unsigned remove_cap) -{ - int context; - int flags; - /* Unique context */ - context = uid; +static int sandbox_processes(xid_t xid, char *context) +{ +#ifdef CONFIG_VSERVER_LEGACY + int flags; flags = 0; flags |= 1; /* VX_INFO_LOCK -- cannot request a new vx_id */ /* flags |= 4; VX_INFO_NPROC -- limit number of procs in a context */ - if (vc_new_s_context(context,remove_cap,flags) < 0) { - perror("vserver: new_s_context error "); + (void) vc_new_s_context(xid, 0, flags); + + /* use legacy dirty hack for capremove */ + if (vc_new_s_context(VC_SAMECTX, vc_get_insecurebcaps(), flags) == VC_NOCTX) { + PERROR("vc_new_s_context(%u, 0x%16ullx, 0x%08x)", + VC_SAMECTX, vc_get_insecurebcaps(), flags); + exit(1); + } +#else + struct vc_rlimit limits; + struct vc_ctx_caps caps; + struct vc_ctx_flags flags; + + xid_t ctx; + unsigned long long cpu = VC_LIM_KEEP; + unsigned long long mem = VC_LIM_KEEP; + unsigned long long task = VC_LIM_KEEP; + unsigned long long cpuguaranteed = 0; + struct resources list[] = + {{"MEMLIMIT", &mem}, + {"CPULIMIT", &cpu}, + {"CPUGUARANTEED", &cpuguaranteed}, + {"TASKLIMIT", &task}, + {0,0}}; + + get_limits(context,list); + (void) (sandbox_chroot(xid)); + + caps.ccaps = ~vc_get_insecureccaps(); + caps.cmask = ~0ull; + caps.bcaps = ~vc_get_insecurebcaps(); + caps.bmask = ~0ull; + + flags.flagword = VC_VXF_INFO_LOCK; + flags.mask = VC_VXF_STATE_SETUP | VC_VXF_INFO_LOCK; + + ctx = vc_ctx_create(xid); + if (ctx == VC_NOCTX && errno != EEXIST) { + PERROR("vc_ctx_create(%d)", xid); + exit(1); + } + + /* CPU */ + if (cpu != VC_LIM_KEEP) { + struct vc_set_sched sched = { + .set_mask = 0 + }; + + /* Need to distinguish between guarantee (hard) and + * best effort (share) from the vserver + * configuration. + */ +#define VC_VXF_SCHED_SHARE 0x00000800ull + flags.flagword |= VC_VXF_SCHED_HARD; + flags.mask |= VC_VXF_SCHED_HARD; + if (cpuguaranteed) { + flags.flagword |= VC_VXF_SCHED_SHARE; + flags.mask |= VC_VXF_SCHED_SHARE; + } + + /* CPULIMIT value from /etc/vservers/xyz.conf */ + sched.fill_rate = cpu; + sched.set_mask |= VC_VXSM_FILL_RATE; + + sched.interval = 1000; /* Andy's default value */ + sched.set_mask |= VC_VXSM_INTERVAL; + + /* set base token value for new contexts */ + if (ctx != VC_NOCTX) { + sched.tokens = 100; /* Andy's default value */ + sched.set_mask |= VC_VXSM_TOKENS; + } + + sched.tokens_min = 50; /* Andy's default value */ + sched.tokens_max = 100; /* Andy's default value */ + sched.set_mask |= VC_VXSM_TOKENS_MIN; + sched.set_mask |= VC_VXSM_TOKENS_MAX; + + if (vc_set_sched(xid, &sched)==-1) { + PERROR("vc_set_sched()"); + exit(1); + } + } + + /* MEM */ + limits.min = VC_LIM_KEEP; + limits.soft = VC_LIM_KEEP; + limits.hard = mem; + if (vc_set_rlimit(xid, RLIMIT_RSS, &limits)) { + PERROR("vc_set_rlimit(%d, %d, %d/%d/%d)", + xid, RLIMIT_RSS, limits.min, limits.soft, limits.hard); + exit(1); + } + + /* TASK */ + limits.min = VC_LIM_KEEP; + limits.soft = VC_LIM_KEEP; + limits.hard = task; + if (vc_set_rlimit(xid, RLIMIT_NPROC, &limits)) { + PERROR("vc_set_rlimit(%d, %d, %d/%d/%d)", + xid, RLIMIT_NPROC, limits.min, limits.soft, limits.hard); + exit(1); + } + + if (vc_set_ccaps(xid, &caps) == -1) { + PERROR("vc_set_ccaps(%d, 0x%16ullx/0x%16ullx, 0x%16ullx/0x%16ullx)", + xid, caps.ccaps, caps.cmask, caps.bcaps, caps.bmask); + exit(1); + } + + if (vc_set_cflags(xid, &flags) == -1) { + PERROR("vc_set_cflags(%d, 0x%16llx/0x%16llx)", + xid, flags.flagword, flags.mask); exit(1); } + + /* context already exists, migrate to it */ + if (ctx == VC_NOCTX && vc_ctx_migrate(xid) == -1) { + PERROR("vc_ctx_migrate(%d)", xid); + exit(1); + } +#endif return 0; } void runas_slice_user(char *username) { - struct passwd *pwd; + struct passwd pwdd, *pwd = &pwdd, *result; + char *pwdBuffer; char *home_env, *logname_env, *mail_env, *shell_env, *user_env; int home_len, logname_len, mail_len, shell_len, user_len; + long pwdBuffer_len; static char *envp[10]; - if ((pwd = getpwnam(username)) == NULL) { - perror("vserver: getpwnam error "); + + pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX); + if (pwdBuffer_len == -1) { + PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)"); + exit(1); + } + + pwdBuffer = (char*)malloc(pwdBuffer_len); + if (pwdBuffer == NULL) { + PERROR("malloc(%d)", pwdBuffer_len); + exit(1); + } + + errno = 0; + if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) { + PERROR("getpwnam_r(%s)", username); exit(1); } if (setgid(pwd->pw_gid) < 0) { - perror("vserver: setgid error "); + PERROR("setgid(%d)", pwd->pw_gid); exit(1); } if (setuid(pwd->pw_uid) < 0) { - perror("vserver: setuid error "); + PERROR("setuid(%d)", pwd->pw_uid); exit(1); } if (chdir(pwd->pw_dir) < 0) { - perror("vserver: chdir error "); + PERROR("chdir(%s)", pwd->pw_dir); exit(1); } @@ -437,7 +458,7 @@ void runas_slice_user(char *username) (mail_env == NULL) || (shell_env == NULL) || (user_env == NULL)) { - perror("vserver: malloc error "); + PERROR("malloc"); exit(1); } @@ -465,46 +486,48 @@ void runas_slice_user(char *username) (putenv(mail_env) < 0) || (putenv(shell_env) < 0) || (putenv(user_env) < 0)) { - perror("vserver: putenv error "); + PERROR("vserver: putenv error "); exit(1); } } - - void slice_enter(char *context) { - struct passwd *pwd; - unsigned remove_cap; + struct passwd pwdd, *pwd = &pwdd, *result; + char *pwdBuffer; + long pwdBuffer_len; uid_t uid; - if ((pwd = getpwnam(context)) == NULL) { - fprintf(stderr,"vserver: getpwname(%s) failed",context); - exit(2); + pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX); + if (pwdBuffer_len == -1) { + PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)"); + exit(1); } - context = (char*)malloc(strlen(pwd->pw_name)+NULLBYTE_SIZE); - if (!context) { - perror("vserver: malloc failed"); - exit(2); + pwdBuffer = (char*)malloc(pwdBuffer_len); + if (pwdBuffer == NULL) { + PERROR("malloc(%d)", pwdBuffer_len); + exit(1); } - strcpy(context,pwd->pw_name); - if (setuidgid_root() < 0) { /* For chroot, new_s_context */ - fprintf(stderr,"vserver: Could not setuid/setguid to root:root\n"); + errno = 0; + if ((getpwnam_r(context,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) { + PERROR("getpwnam_r(%s)", context); exit(2); } - - remove_cap = get_remove_cap(context); - uid = pwd->pw_uid; - if (sandbox_chroot(uid) < 0) { - fprintf(stderr, "vserver: Could not chroot to vserver root\n"); + + if (setuidgid_root() < 0) { /* For chroot, new_s_context */ + fprintf(stderr, "vsh: Could not become root, check that SUID flag is set on binary\n"); exit(2); } - if (sandbox_processes(uid, remove_cap) < 0) { - fprintf(stderr, "vserver: Could not sandbox processes in vserver\n"); +#ifdef CONFIG_VSERVER_LEGACY + (void) (sandbox_chroot(uid)); +#endif + + if (sandbox_processes((xid_t) uid, context) < 0) { + fprintf(stderr, "vsh: Could not change context to %d\n", uid); exit(2); } } @@ -521,15 +544,13 @@ enum EXIT_ENOENT = 127 }; -extern void slice_enter(char *); -extern void runas_slice_user(char *); - int main(int argc, char **argv) { - char *context, *username, *shell; - struct passwd *pwd; + struct passwd pwdd, *pwd = &pwdd, *result; + char *context, *username, *shell, *pwdBuffer; + long pwdBuffer_len; uid_t uid; - int index, i; + int index, i; if (argv[0][0]=='-') index = 1; @@ -538,13 +559,13 @@ int main(int argc, char **argv) uid = getuid(); if ((pwd = getpwuid(uid)) == NULL) { - fprintf(stderr,"vsh: getpwnam error failed for %d\n",uid); + PERROR("getpwuid(%d)", uid); exit(1); } context = (char*)strdup(pwd->pw_name); if (!context) { - perror("vsh: strdup failed"); + PERROR("strdup"); exit(2); } @@ -561,8 +582,21 @@ int main(int argc, char **argv) /* With the uid/gid appropriately set. Let's figure out what the * shell in the vserver's /etc/passwd is for the given username. */ - if ((pwd = getpwnam(username)) == NULL) { - fprintf(stderr,"vsh: getpwnam error failed for %s\n",username); + + pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX); + if (pwdBuffer_len == -1) { + PERROR("sysconf(_SC_GETPW_R_SIZE_MAX"); + exit(1); + } + pwdBuffer = (char*)malloc(pwdBuffer_len); + if (pwdBuffer == NULL) { + PERROR("malloc(%d)", pwdBuffer_len); + exit(1); + } + + errno = 0; + if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) { + PERROR("getpwnam_r(%s)", username); exit(1); } @@ -573,7 +607,7 @@ int main(int argc, char **argv) shell = (char *)strdup(pwd->pw_shell); if (!shell) { - perror("vsh: strdup failed"); + PERROR("strdup"); exit(2); } @@ -585,7 +619,8 @@ int main(int argc, char **argv) char **args; args = (char**)malloc(sizeof(char*)*(argc+2)); if (!args) { - perror("vsh: malloc failed"); + PERROR("malloc(%d)", sizeof(char*)*(argc+2)); + exit(1); } args[0] = argv[0]; args[1] = "-l";