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);
297 (void) (sandbox_chroot(ctx));
299 rspec.cpu_share = cpu;
300 rspec.cpu_sched_flags = (VC_VXF_SCHED_HARD |
301 (cpuguaranteed ? 0 : VC_VXF_SCHED_SHARE));
302 rspec.mem_limit = mem;
303 rspec.task_limit = task;
304 if (pl_chcontext(ctx, 0, ~vc_get_insecurebcaps(), &rspec))
306 PERROR("pl_chcontext(%u)", ctx);
314 void runas_slice_user(char *username)
316 struct passwd pwdd, *pwd = &pwdd, *result;
318 char *home_env, *logname_env, *mail_env, *shell_env, *user_env;
319 int home_len, logname_len, mail_len, shell_len, user_len;
321 static char *envp[10];
324 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
325 if (pwdBuffer_len == -1) {
326 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
330 pwdBuffer = (char*)malloc(pwdBuffer_len);
331 if (pwdBuffer == NULL) {
332 PERROR("malloc(%d)", pwdBuffer_len);
337 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
338 PERROR("getpwnam_r(%s)", username);
342 if (setgid(pwd->pw_gid) < 0) {
343 PERROR("setgid(%d)", pwd->pw_gid);
347 if (setuid(pwd->pw_uid) < 0) {
348 PERROR("setuid(%d)", pwd->pw_uid);
352 if (chdir(pwd->pw_dir) < 0) {
353 PERROR("chdir(%s)", pwd->pw_dir);
357 home_len = strlen("HOME=") + strlen(pwd->pw_dir) + NULLBYTE_SIZE;
358 logname_len = strlen("LOGNAME=") + strlen(username) + NULLBYTE_SIZE;
359 mail_len = strlen("MAIL=/var/spool/mail/") + strlen(username)
361 shell_len = strlen("SHELL=") + strlen(pwd->pw_shell) + NULLBYTE_SIZE;
362 user_len = strlen("USER=") + strlen(username) + NULLBYTE_SIZE;
364 home_env = (char *)malloc(home_len);
365 logname_env = (char *)malloc(logname_len);
366 mail_env = (char *)malloc(mail_len);
367 shell_env = (char *)malloc(shell_len);
368 user_env = (char *)malloc(user_len);
370 if ((home_env == NULL) ||
371 (logname_env == NULL) ||
372 (mail_env == NULL) ||
373 (shell_env == NULL) ||
374 (user_env == NULL)) {
379 sprintf(home_env, "HOME=%s", pwd->pw_dir);
380 sprintf(logname_env, "LOGNAME=%s", username);
381 sprintf(mail_env, "MAIL=/var/spool/mail/%s", username);
382 sprintf(shell_env, "SHELL=%s", pwd->pw_shell);
383 sprintf(user_env, "USER=%s", username);
385 home_env[home_len - 1] = '\0';
386 logname_env[logname_len - 1] = '\0';
387 mail_env[mail_len - 1] = '\0';
388 shell_env[shell_len - 1] = '\0';
389 user_env[user_len - 1] = '\0';
392 envp[1] = logname_env;
398 if ((putenv(home_env) < 0) ||
399 (putenv(logname_env) < 0) ||
400 (putenv(mail_env) < 0) ||
401 (putenv(shell_env) < 0) ||
402 (putenv(user_env) < 0)) {
403 PERROR("vserver: putenv error ");
408 void slice_enter(char *context)
410 struct passwd pwdd, *pwd = &pwdd, *result;
415 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
416 if (pwdBuffer_len == -1) {
417 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
421 pwdBuffer = (char*)malloc(pwdBuffer_len);
422 if (pwdBuffer == NULL) {
423 PERROR("malloc(%d)", pwdBuffer_len);
428 if ((getpwnam_r(context,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
429 PERROR("getpwnam_r(%s)", context);
434 if (setuidgid_root() < 0) { /* For chroot, new_s_context */
435 fprintf(stderr, "vsh: Could not become root, check that SUID flag is set on binary\n");
439 #ifdef CONFIG_VSERVER_LEGACY
440 (void) (sandbox_chroot(uid));
443 if (sandbox_processes((xid_t) uid, context) < 0) {
444 fprintf(stderr, "vsh: Could not change context to %d\n", uid);
449 //--------------------------------------------------------------------
451 #define DEFAULT_SHELL "/bin/sh"
453 /* Exit statuses for programs like 'env' that exec other programs.
454 EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs. */
457 EXIT_CANNOT_INVOKE = 126,
461 int main(int argc, char **argv)
463 struct passwd pwdd, *pwd = &pwdd, *result;
464 char *context, *username, *shell, *pwdBuffer;
475 if ((pwd = getpwuid(uid)) == NULL) {
476 PERROR("getpwuid(%d)", uid);
480 context = (char*)strdup(pwd->pw_name);
486 /* enter vserver "context" */
487 slice_enter(context);
489 /* Now run as username in this context. Note that for PlanetLab's
490 vserver configuration the context name also happens to be the
491 "default" username within the vserver context.
494 runas_slice_user(username);
496 /* With the uid/gid appropriately set. Let's figure out what the
497 * shell in the vserver's /etc/passwd is for the given username.
500 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
501 if (pwdBuffer_len == -1) {
502 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX");
505 pwdBuffer = (char*)malloc(pwdBuffer_len);
506 if (pwdBuffer == NULL) {
507 PERROR("malloc(%d)", pwdBuffer_len);
512 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
513 PERROR("getpwnam_r(%s)", username);
517 /* Make sure pw->pw_shell is non-NULL.*/
518 if (pwd->pw_shell == NULL || pwd->pw_shell[0] == '\0') {
519 pwd->pw_shell = (char *) DEFAULT_SHELL;
522 shell = (char *)strdup(pwd->pw_shell);
528 /* Check whether 'su' or 'sshd' invoked us as a login shell or
529 not; did this above when testing argv[0]=='-'.
534 args = (char**)malloc(sizeof(char*)*(argc+2));
536 PERROR("malloc(%d)", sizeof(char*)*(argc+2));
541 for(i=1;i<argc+1;i++) {
546 (void) execvp(shell,argv);
548 int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
552 return 0; /* shutup compiler */