2 * Marc E. Fiuczynski <mef@cs.princeton.edu>
4 * Copyright (c) 2004 The Trustees of Princeton University (Trustees).
6 * vsh is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
11 * vsh is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 * License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with Poptop; see the file COPYING. If not, write to the Free
18 * Software Foundation, 59 Temple Place - Suite 330, Boston, MA
35 #include <sys/syscall.h>
36 #include <asm/unistd.h>
37 #include <sys/mount.h>
38 #include <sys/types.h>
40 #include <sys/resource.h>
45 //--------------------------------------------------------------------
47 #include "planetlab.h"
49 #undef CONFIG_VSERVER_LEGACY
51 /* Null byte made explicit */
52 #define NULLBYTE_SIZE 1
54 /* Base for all vserver roots for chroot */
55 #define VSERVER_ROOT_BASE "/vservers"
58 _PERROR(const char *format, char *file, int line, int _errno, ...)
63 fprintf(stderr, "%s:%d: ", file, line);
64 vfprintf(stderr, format, ap);
66 fprintf(stderr, ": %s (%d)", strerror(_errno), _errno);
73 #define PERROR(format, args...) _PERROR(format, __FILE__, __LINE__, errno, ## args)
75 /* Change to root:root (before entering new context) */
76 static int setuidgid_root()
89 static void compute_new_root(char *base, char **root, uid_t uid)
94 if ((pwd = getpwuid(uid)) == NULL) {
95 PERROR("getpwuid(%d)", uid);
100 strlen(base) + strlen("/") +
101 strlen(pwd->pw_name) + NULLBYTE_SIZE;
102 (*root) = (char *)malloc(root_len);
103 if ((*root) == NULL) {
104 PERROR("malloc(%d)", root_len);
108 sprintf((*root), "%s/%s", base, pwd->pw_name);
109 (*root)[root_len - 1] = '\0';
112 /* Example: sandbox_root = /vservers/bnc, relpath = /proc/1 */
113 static int sandbox_file_exists(char *sandbox_root, char *relpath)
115 struct stat stat_buf;
119 len = strlen(sandbox_root) + strlen(relpath) + NULLBYTE_SIZE;
120 if ((file = (char *)malloc(len)) == NULL) {
121 PERROR("malloc(%d)", len);
124 sprintf(file, "%s%s", sandbox_root, relpath);
125 file[len - 1] = '\0';
126 if (stat(file, &stat_buf) == 0) {
135 static int proc_mounted(char *sandbox_root)
137 return sandbox_file_exists(sandbox_root, "/proc/1");
140 static int devpts_mounted(char *sandbox_root)
142 return sandbox_file_exists(sandbox_root, "/dev/pts/0");
145 static void mount_proc(char *sandbox_root)
147 char *source = "/proc";
151 len = strlen(sandbox_root) + strlen("/") + strlen("proc") + NULLBYTE_SIZE;
152 if ((target = (char *)malloc(len)) == NULL) {
153 PERROR("malloc(%d)", len);
157 sprintf(target, "%s/proc", sandbox_root);
158 target[len - 1] = '\0';
159 if (!proc_mounted(sandbox_root))
160 mount(source, target, "proc", MS_BIND | MS_RDONLY, NULL);
165 static void mount_devpts(char *sandbox_root)
167 char *source = "/dev/pts";
171 len = strlen(sandbox_root) + strlen("/") + strlen("dev/pts") + NULLBYTE_SIZE;
172 if ((target = (char *)malloc(len)) == NULL) {
173 PERROR("malloc(%d)", len);
177 sprintf(target, "%s/dev/pts", sandbox_root);
178 target[len - 1] = '\0';
179 if (!devpts_mounted(sandbox_root))
180 mount(source, target, "devpts", 0, NULL);
185 static int sandbox_chroot(uid_t uid)
187 char *sandbox_root = NULL;
189 compute_new_root(VSERVER_ROOT_BASE,&sandbox_root, uid);
190 mount_proc(sandbox_root);
191 mount_devpts(sandbox_root);
192 if (chroot(sandbox_root) < 0) {
193 PERROR("chroot(%s)", sandbox_root);
196 if (chdir("/") < 0) {
203 #define WHITESPACE(buffer,index,len) \
204 while(isspace((int)buffer[index])) \
205 if (index < len) index++; else goto out;
209 unsigned long long *limit;
212 #define VSERVERCONF "/etc/vservers/"
213 static void get_limits(char *context, struct resources *list){
215 size_t len = strlen(VSERVERCONF) + strlen(context) + strlen(".conf") + NULLBYTE_SIZE;
216 char *conf = (char *)malloc(len);
219 sprintf(conf, "%s%s.conf", VSERVERCONF, context);
221 /* open the conf file for reading */
222 fb = fopen(conf,"r");
225 char *buffer = malloc(1000);
228 /* the conf file exist */
229 while((p=fgets(buffer,1000-1,fb))!=NULL) {
231 len = strnlen(buffer,1000);
232 WHITESPACE(buffer,index,len);
233 if (buffer[index] == '#')
236 for (r=list; r->name; r++)
237 if ((p=strstr(&buffer[index],r->name))!=NULL) {
238 /* adjust index into buffer */
239 index+= (p-&buffer[index])+strlen(r->name);
241 /* skip over whitespace */
242 WHITESPACE(buffer,index,len);
244 /* expecting to see = sign */
245 if (buffer[index++]!='=') goto out;
247 /* skip over whitespace */
248 WHITESPACE(buffer,index,len);
250 /* expecting to see a digit for number */
251 if (!isdigit((int)buffer[index])) goto out;
253 *r->limit = atoi(&buffer[index]);
260 fprintf(stderr,"cannot open %s\n",conf);
266 static int sandbox_processes(xid_t ctx, char *context)
268 #ifdef CONFIG_VSERVER_LEGACY
272 flags |= 1; /* VX_INFO_LOCK -- cannot request a new vx_id */
273 /* flags |= 4; VX_INFO_NPROC -- limit number of procs in a context */
275 (void) vc_new_s_context(ctx, 0, flags);
277 /* use legacy dirty hack for capremove */
278 if (vc_new_s_context(VC_SAMECTX, vc_get_insecurebcaps(), flags) == VC_NOCTX) {
279 PERROR("vc_new_s_context(%u, 0x%16ullx, 0x%08x)",
280 VC_SAMECTX, vc_get_insecurebcaps(), flags);
285 unsigned long long cpu = VC_LIM_KEEP;
286 unsigned long long mem = VC_LIM_KEEP;
287 unsigned long long task = VC_LIM_KEEP;
288 unsigned long long cpuguaranteed = 0;
289 struct resources list[] =
292 {"CPUGUARANTEED", &cpuguaranteed},
293 {"TASKLIMIT", &task},
296 get_limits(context,list);
298 /* check whether the slice has been disabled */
301 fprintf(stderr, "*** this slice has been suspended ***\n");
305 (void) (sandbox_chroot(ctx));
307 if ((ctx_is_new = pl_chcontext(ctx, 0, ~vc_get_insecurebcaps())) < 0)
309 PERROR("pl_chcontext(%u)", ctx);
315 struct vc_rlimit limits;
317 limits.min = VC_LIM_KEEP;
318 limits.soft = VC_LIM_KEEP;
320 if (vc_set_rlimit(ctx, RLIMIT_RSS, &limits))
322 PERROR("pl_setrlimit(%u, RLIMIT_RSS)", ctx);
326 if (vc_set_rlimit(ctx, RLIMIT_NPROC, &limits))
328 PERROR("pl_setrlimit(%u, RLIMIT_NPROC)", ctx);
331 cpuguaranteed &= VS_SCHED_CPU_GUARANTEED;
332 if (pl_setsched(ctx, cpu, cpuguaranteed) < 0)
334 PERROR("pl_setsched(&u)", ctx);
344 void runas_slice_user(char *username)
346 struct passwd pwdd, *pwd = &pwdd, *result;
348 char *home_env, *logname_env, *mail_env, *shell_env, *user_env;
349 int home_len, logname_len, mail_len, shell_len, user_len;
351 static char *envp[10];
354 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
355 if (pwdBuffer_len == -1) {
356 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
360 pwdBuffer = (char*)malloc(pwdBuffer_len);
361 if (pwdBuffer == NULL) {
362 PERROR("malloc(%d)", pwdBuffer_len);
367 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
368 PERROR("getpwnam_r(%s)", username);
372 if (setgid(pwd->pw_gid) < 0) {
373 PERROR("setgid(%d)", pwd->pw_gid);
377 if (setuid(pwd->pw_uid) < 0) {
378 PERROR("setuid(%d)", pwd->pw_uid);
382 if (chdir(pwd->pw_dir) < 0) {
383 PERROR("chdir(%s)", pwd->pw_dir);
387 home_len = strlen("HOME=") + strlen(pwd->pw_dir) + NULLBYTE_SIZE;
388 logname_len = strlen("LOGNAME=") + strlen(username) + NULLBYTE_SIZE;
389 mail_len = strlen("MAIL=/var/spool/mail/") + strlen(username)
391 shell_len = strlen("SHELL=") + strlen(pwd->pw_shell) + NULLBYTE_SIZE;
392 user_len = strlen("USER=") + strlen(username) + NULLBYTE_SIZE;
394 home_env = (char *)malloc(home_len);
395 logname_env = (char *)malloc(logname_len);
396 mail_env = (char *)malloc(mail_len);
397 shell_env = (char *)malloc(shell_len);
398 user_env = (char *)malloc(user_len);
400 if ((home_env == NULL) ||
401 (logname_env == NULL) ||
402 (mail_env == NULL) ||
403 (shell_env == NULL) ||
404 (user_env == NULL)) {
409 sprintf(home_env, "HOME=%s", pwd->pw_dir);
410 sprintf(logname_env, "LOGNAME=%s", username);
411 sprintf(mail_env, "MAIL=/var/spool/mail/%s", username);
412 sprintf(shell_env, "SHELL=%s", pwd->pw_shell);
413 sprintf(user_env, "USER=%s", username);
415 home_env[home_len - 1] = '\0';
416 logname_env[logname_len - 1] = '\0';
417 mail_env[mail_len - 1] = '\0';
418 shell_env[shell_len - 1] = '\0';
419 user_env[user_len - 1] = '\0';
422 envp[1] = logname_env;
428 if ((putenv(home_env) < 0) ||
429 (putenv(logname_env) < 0) ||
430 (putenv(mail_env) < 0) ||
431 (putenv(shell_env) < 0) ||
432 (putenv(user_env) < 0)) {
433 PERROR("vserver: putenv error ");
438 void slice_enter(char *context)
440 struct passwd pwdd, *pwd = &pwdd, *result;
445 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
446 if (pwdBuffer_len == -1) {
447 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
451 pwdBuffer = (char*)malloc(pwdBuffer_len);
452 if (pwdBuffer == NULL) {
453 PERROR("malloc(%d)", pwdBuffer_len);
458 if ((getpwnam_r(context,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
459 PERROR("getpwnam_r(%s)", context);
464 if (setuidgid_root() < 0) { /* For chroot, new_s_context */
465 fprintf(stderr, "vsh: Could not become root, check that SUID flag is set on binary\n");
469 #ifdef CONFIG_VSERVER_LEGACY
470 (void) (sandbox_chroot(uid));
473 if (sandbox_processes((xid_t) uid, context) < 0) {
474 fprintf(stderr, "vsh: Could not change context to %d\n", uid);
479 //--------------------------------------------------------------------
481 #define DEFAULT_SHELL "/bin/sh"
483 /* Exit statuses for programs like 'env' that exec other programs.
484 EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs. */
487 EXIT_CANNOT_INVOKE = 126,
491 int main(int argc, char **argv)
493 struct passwd pwdd, *pwd = &pwdd, *result;
494 char *context, *username, *shell, *pwdBuffer;
505 if ((pwd = getpwuid(uid)) == NULL) {
506 PERROR("getpwuid(%d)", uid);
510 context = (char*)strdup(pwd->pw_name);
516 /* enter vserver "context" */
517 slice_enter(context);
519 /* Now run as username in this context. Note that for PlanetLab's
520 vserver configuration the context name also happens to be the
521 "default" username within the vserver context.
524 runas_slice_user(username);
526 /* With the uid/gid appropriately set. Let's figure out what the
527 * shell in the vserver's /etc/passwd is for the given username.
530 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
531 if (pwdBuffer_len == -1) {
532 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX");
535 pwdBuffer = (char*)malloc(pwdBuffer_len);
536 if (pwdBuffer == NULL) {
537 PERROR("malloc(%d)", pwdBuffer_len);
542 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
543 PERROR("getpwnam_r(%s)", username);
547 /* Make sure pw->pw_shell is non-NULL.*/
548 if (pwd->pw_shell == NULL || pwd->pw_shell[0] == '\0') {
549 pwd->pw_shell = (char *) DEFAULT_SHELL;
552 shell = (char *)strdup(pwd->pw_shell);
558 /* Check whether 'su' or 'sshd' invoked us as a login shell or
559 not; did this above when testing argv[0]=='-'.
564 args = (char**)malloc(sizeof(char*)*(argc+2));
566 PERROR("malloc(%d)", sizeof(char*)*(argc+2));
571 for(i=1;i<argc+1;i++) {
576 (void) execvp(shell,argv);
578 int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
582 return 0; /* shutup compiler */