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 /* Base for all vserver roots for chroot */
52 #define VSERVER_ROOT_BASE "/vservers"
54 /* Change to root:root (before entering new context) */
55 static int setuidgid_root()
68 static void compute_new_root(char *base, char **root, uid_t uid)
73 if ((pwd = getpwuid(uid)) == NULL) {
74 PERROR("getpwuid(%d)", uid);
79 strlen(base) + strlen("/") +
80 strlen(pwd->pw_name) + NULLBYTE_SIZE;
81 (*root) = (char *)malloc(root_len);
82 if ((*root) == NULL) {
83 PERROR("malloc(%d)", root_len);
87 sprintf((*root), "%s/%s", base, pwd->pw_name);
88 (*root)[root_len - 1] = '\0';
91 /* Example: sandbox_root = /vservers/bnc, relpath = /proc/1 */
92 static int sandbox_file_exists(char *sandbox_root, char *relpath)
98 len = strlen(sandbox_root) + strlen(relpath) + NULLBYTE_SIZE;
99 if ((file = (char *)malloc(len)) == NULL) {
100 PERROR("malloc(%d)", len);
103 sprintf(file, "%s%s", sandbox_root, relpath);
104 file[len - 1] = '\0';
105 if (stat(file, &stat_buf) == 0) {
114 static int proc_mounted(char *sandbox_root)
116 return sandbox_file_exists(sandbox_root, "/proc/1");
119 static int devpts_mounted(char *sandbox_root)
121 return sandbox_file_exists(sandbox_root, "/dev/pts/0");
124 static void mount_proc(char *sandbox_root)
126 char *source = "/proc";
130 len = strlen(sandbox_root) + strlen("/") + strlen("proc") + NULLBYTE_SIZE;
131 if ((target = (char *)malloc(len)) == NULL) {
132 PERROR("malloc(%d)", len);
136 sprintf(target, "%s/proc", sandbox_root);
137 target[len - 1] = '\0';
138 if (!proc_mounted(sandbox_root))
139 mount(source, target, "proc", MS_BIND | MS_RDONLY, NULL);
144 static void mount_devpts(char *sandbox_root)
146 char *source = "/dev/pts";
150 len = strlen(sandbox_root) + strlen("/") + strlen("dev/pts") + NULLBYTE_SIZE;
151 if ((target = (char *)malloc(len)) == NULL) {
152 PERROR("malloc(%d)", len);
156 sprintf(target, "%s/dev/pts", sandbox_root);
157 target[len - 1] = '\0';
158 if (!devpts_mounted(sandbox_root))
159 mount(source, target, "devpts", 0, NULL);
164 static int sandbox_chroot(uid_t uid)
166 char *sandbox_root = NULL;
168 compute_new_root(VSERVER_ROOT_BASE,&sandbox_root, uid);
169 mount_proc(sandbox_root);
170 mount_devpts(sandbox_root);
171 if (chroot(sandbox_root) < 0) {
172 PERROR("chroot(%s)", sandbox_root);
175 if (chdir("/") < 0) {
182 static int sandbox_processes(xid_t ctx, char *context)
184 #ifdef CONFIG_VSERVER_LEGACY
188 flags |= 1; /* VX_INFO_LOCK -- cannot request a new vx_id */
189 /* flags |= 4; VX_INFO_NPROC -- limit number of procs in a context */
191 (void) vc_new_s_context(ctx, 0, flags);
193 /* use legacy dirty hack for capremove */
194 if (vc_new_s_context(VC_SAMECTX, vc_get_insecurebcaps(), flags) == VC_NOCTX) {
195 PERROR("vc_new_s_context(%u, 0x%16ullx, 0x%08x)",
196 VC_SAMECTX, vc_get_insecurebcaps(), flags);
201 struct sliver_resources slr;
202 char hostname[HOST_NAME_MAX+1];
203 pl_get_limits(context,&slr);
205 if (gethostname(hostname, sizeof hostname) == -1)
207 PERROR("gethostname(...)");
211 /* check whether the slice has been taken off of the whitelist */
212 if (slr.vs_whitelisted==0)
214 fprintf(stderr, "*** %s: %s has not been allocated resources on this node ***\n", hostname, context);
218 /* check whether the slice has been suspended */
221 fprintf(stderr, "*** %s: %s has zero cpu resources and presumably it has been disabled/suspended ***\n", hostname, context);
225 (void) (sandbox_chroot(ctx));
227 if ((ctx_is_new = pl_chcontext(ctx, ~vc_get_insecurebcaps(),&slr)) < 0)
229 PERROR("pl_chcontext(%u)", ctx);
234 pl_set_limits(ctx,&slr);
242 void runas_slice_user(char *username)
244 struct passwd pwdd, *pwd = &pwdd, *result;
246 char *home_env, *logname_env, *mail_env, *shell_env, *user_env;
247 int home_len, logname_len, mail_len, shell_len, user_len;
249 static char *envp[10];
252 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
253 if (pwdBuffer_len == -1) {
254 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
258 pwdBuffer = (char*)malloc(pwdBuffer_len);
259 if (pwdBuffer == NULL) {
260 PERROR("malloc(%d)", pwdBuffer_len);
265 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
266 PERROR("getpwnam_r(%s)", username);
270 if (setgid(pwd->pw_gid) < 0) {
271 PERROR("setgid(%d)", pwd->pw_gid);
275 if (setuid(pwd->pw_uid) < 0) {
276 PERROR("setuid(%d)", pwd->pw_uid);
280 if (chdir(pwd->pw_dir) < 0) {
281 PERROR("chdir(%s)", pwd->pw_dir);
285 home_len = strlen("HOME=") + strlen(pwd->pw_dir) + NULLBYTE_SIZE;
286 logname_len = strlen("LOGNAME=") + strlen(username) + NULLBYTE_SIZE;
287 mail_len = strlen("MAIL=/var/spool/mail/") + strlen(username)
289 shell_len = strlen("SHELL=") + strlen(pwd->pw_shell) + NULLBYTE_SIZE;
290 user_len = strlen("USER=") + strlen(username) + NULLBYTE_SIZE;
292 home_env = (char *)malloc(home_len);
293 logname_env = (char *)malloc(logname_len);
294 mail_env = (char *)malloc(mail_len);
295 shell_env = (char *)malloc(shell_len);
296 user_env = (char *)malloc(user_len);
298 if ((home_env == NULL) ||
299 (logname_env == NULL) ||
300 (mail_env == NULL) ||
301 (shell_env == NULL) ||
302 (user_env == NULL)) {
307 sprintf(home_env, "HOME=%s", pwd->pw_dir);
308 sprintf(logname_env, "LOGNAME=%s", username);
309 sprintf(mail_env, "MAIL=/var/spool/mail/%s", username);
310 sprintf(shell_env, "SHELL=%s", pwd->pw_shell);
311 sprintf(user_env, "USER=%s", username);
313 home_env[home_len - 1] = '\0';
314 logname_env[logname_len - 1] = '\0';
315 mail_env[mail_len - 1] = '\0';
316 shell_env[shell_len - 1] = '\0';
317 user_env[user_len - 1] = '\0';
320 envp[1] = logname_env;
326 if ((putenv(home_env) < 0) ||
327 (putenv(logname_env) < 0) ||
328 (putenv(mail_env) < 0) ||
329 (putenv(shell_env) < 0) ||
330 (putenv(user_env) < 0)) {
331 PERROR("vserver: putenv error ");
336 void slice_enter(char *context)
338 struct passwd pwdd, *pwd = &pwdd, *result;
343 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
344 if (pwdBuffer_len == -1) {
345 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
349 pwdBuffer = (char*)malloc(pwdBuffer_len);
350 if (pwdBuffer == NULL) {
351 PERROR("malloc(%d)", pwdBuffer_len);
356 if ((getpwnam_r(context,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
357 PERROR("getpwnam_r(%s)", context);
362 if (setuidgid_root() < 0) { /* For chroot, new_s_context */
363 fprintf(stderr, "vsh: Could not become root, check that SUID flag is set on binary\n");
367 #ifdef CONFIG_VSERVER_LEGACY
368 (void) (sandbox_chroot(uid));
371 if (sandbox_processes((xid_t) uid, context) < 0) {
372 fprintf(stderr, "vsh: Could not change context to %d\n", uid);
377 //--------------------------------------------------------------------
379 #define DEFAULT_SHELL "/bin/sh"
381 /* Exit statuses for programs like 'env' that exec other programs.
382 EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs. */
385 EXIT_CANNOT_INVOKE = 126,
389 int main(int argc, char **argv)
391 struct passwd pwdd, *pwd = &pwdd, *result;
392 char *context, *username, *shell, *pwdBuffer;
403 if ((pwd = getpwuid(uid)) == NULL) {
404 PERROR("getpwuid(%d)", uid);
408 context = (char*)strdup(pwd->pw_name);
414 /* enter vserver "context" */
415 slice_enter(context);
417 /* Now run as username in this context. Note that for PlanetLab's
418 vserver configuration the context name also happens to be the
419 "default" username within the vserver context.
422 runas_slice_user(username);
424 /* With the uid/gid appropriately set. Let's figure out what the
425 * shell in the vserver's /etc/passwd is for the given username.
428 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
429 if (pwdBuffer_len == -1) {
430 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX");
433 pwdBuffer = (char*)malloc(pwdBuffer_len);
434 if (pwdBuffer == NULL) {
435 PERROR("malloc(%d)", pwdBuffer_len);
440 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
441 PERROR("getpwnam_r(%s)", username);
445 /* Make sure pw->pw_shell is non-NULL.*/
446 if (pwd->pw_shell == NULL || pwd->pw_shell[0] == '\0') {
447 pwd->pw_shell = (char *) DEFAULT_SHELL;
450 shell = (char *)strdup(pwd->pw_shell);
456 /* Check whether 'su' or 'sshd' invoked us as a login shell or
457 not; did this above when testing argv[0]=='-'.
462 args = (char**)malloc(sizeof(char*)*(argc+2));
464 PERROR("malloc(%d)", sizeof(char*)*(argc+2));
469 for(i=1;i<argc+1;i++) {
474 (void) execvp(shell,argv);
476 int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
480 return 0; /* shutup compiler */