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 rspec.cpu_share = cpu;
308 rspec.cpu_sched_flags = (VC_VXF_SCHED_HARD |
309 (cpuguaranteed ? 0 : VC_VXF_SCHED_SHARE));
310 rspec.mem_limit = mem;
311 rspec.task_limit = task;
312 if (pl_chcontext(ctx, 0, ~vc_get_insecurebcaps(), &rspec))
314 PERROR("pl_chcontext(%u)", ctx);
322 void runas_slice_user(char *username)
324 struct passwd pwdd, *pwd = &pwdd, *result;
326 char *home_env, *logname_env, *mail_env, *shell_env, *user_env;
327 int home_len, logname_len, mail_len, shell_len, user_len;
329 static char *envp[10];
332 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
333 if (pwdBuffer_len == -1) {
334 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
338 pwdBuffer = (char*)malloc(pwdBuffer_len);
339 if (pwdBuffer == NULL) {
340 PERROR("malloc(%d)", pwdBuffer_len);
345 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
346 PERROR("getpwnam_r(%s)", username);
350 if (setgid(pwd->pw_gid) < 0) {
351 PERROR("setgid(%d)", pwd->pw_gid);
355 if (setuid(pwd->pw_uid) < 0) {
356 PERROR("setuid(%d)", pwd->pw_uid);
360 if (chdir(pwd->pw_dir) < 0) {
361 PERROR("chdir(%s)", pwd->pw_dir);
365 home_len = strlen("HOME=") + strlen(pwd->pw_dir) + NULLBYTE_SIZE;
366 logname_len = strlen("LOGNAME=") + strlen(username) + NULLBYTE_SIZE;
367 mail_len = strlen("MAIL=/var/spool/mail/") + strlen(username)
369 shell_len = strlen("SHELL=") + strlen(pwd->pw_shell) + NULLBYTE_SIZE;
370 user_len = strlen("USER=") + strlen(username) + NULLBYTE_SIZE;
372 home_env = (char *)malloc(home_len);
373 logname_env = (char *)malloc(logname_len);
374 mail_env = (char *)malloc(mail_len);
375 shell_env = (char *)malloc(shell_len);
376 user_env = (char *)malloc(user_len);
378 if ((home_env == NULL) ||
379 (logname_env == NULL) ||
380 (mail_env == NULL) ||
381 (shell_env == NULL) ||
382 (user_env == NULL)) {
387 sprintf(home_env, "HOME=%s", pwd->pw_dir);
388 sprintf(logname_env, "LOGNAME=%s", username);
389 sprintf(mail_env, "MAIL=/var/spool/mail/%s", username);
390 sprintf(shell_env, "SHELL=%s", pwd->pw_shell);
391 sprintf(user_env, "USER=%s", username);
393 home_env[home_len - 1] = '\0';
394 logname_env[logname_len - 1] = '\0';
395 mail_env[mail_len - 1] = '\0';
396 shell_env[shell_len - 1] = '\0';
397 user_env[user_len - 1] = '\0';
400 envp[1] = logname_env;
406 if ((putenv(home_env) < 0) ||
407 (putenv(logname_env) < 0) ||
408 (putenv(mail_env) < 0) ||
409 (putenv(shell_env) < 0) ||
410 (putenv(user_env) < 0)) {
411 PERROR("vserver: putenv error ");
416 void slice_enter(char *context)
418 struct passwd pwdd, *pwd = &pwdd, *result;
423 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
424 if (pwdBuffer_len == -1) {
425 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
429 pwdBuffer = (char*)malloc(pwdBuffer_len);
430 if (pwdBuffer == NULL) {
431 PERROR("malloc(%d)", pwdBuffer_len);
436 if ((getpwnam_r(context,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
437 PERROR("getpwnam_r(%s)", context);
442 if (setuidgid_root() < 0) { /* For chroot, new_s_context */
443 fprintf(stderr, "vsh: Could not become root, check that SUID flag is set on binary\n");
447 #ifdef CONFIG_VSERVER_LEGACY
448 (void) (sandbox_chroot(uid));
451 if (sandbox_processes((xid_t) uid, context) < 0) {
452 fprintf(stderr, "vsh: Could not change context to %d\n", uid);
457 //--------------------------------------------------------------------
459 #define DEFAULT_SHELL "/bin/sh"
461 /* Exit statuses for programs like 'env' that exec other programs.
462 EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs. */
465 EXIT_CANNOT_INVOKE = 126,
469 int main(int argc, char **argv)
471 struct passwd pwdd, *pwd = &pwdd, *result;
472 char *context, *username, *shell, *pwdBuffer;
483 if ((pwd = getpwuid(uid)) == NULL) {
484 PERROR("getpwuid(%d)", uid);
488 context = (char*)strdup(pwd->pw_name);
494 /* enter vserver "context" */
495 slice_enter(context);
497 /* Now run as username in this context. Note that for PlanetLab's
498 vserver configuration the context name also happens to be the
499 "default" username within the vserver context.
502 runas_slice_user(username);
504 /* With the uid/gid appropriately set. Let's figure out what the
505 * shell in the vserver's /etc/passwd is for the given username.
508 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
509 if (pwdBuffer_len == -1) {
510 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX");
513 pwdBuffer = (char*)malloc(pwdBuffer_len);
514 if (pwdBuffer == NULL) {
515 PERROR("malloc(%d)", pwdBuffer_len);
520 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
521 PERROR("getpwnam_r(%s)", username);
525 /* Make sure pw->pw_shell is non-NULL.*/
526 if (pwd->pw_shell == NULL || pwd->pw_shell[0] == '\0') {
527 pwd->pw_shell = (char *) DEFAULT_SHELL;
530 shell = (char *)strdup(pwd->pw_shell);
536 /* Check whether 'su' or 'sshd' invoked us as a login shell or
537 not; did this above when testing argv[0]=='-'.
542 args = (char**)malloc(sizeof(char*)*(argc+2));
544 PERROR("malloc(%d)", sizeof(char*)*(argc+2));
549 for(i=1;i<argc+1;i++) {
554 (void) execvp(shell,argv);
556 int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
560 return 0; /* shutup compiler */