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
52 /* Base for all vserver roots for chroot */
53 #define VSERVER_ROOT_BASE "/vservers"
55 /* Change to root:root (before entering new context) */
56 static int setuidgid_root()
69 static void compute_new_root(char *base, char **root, uid_t uid)
74 if ((pwd = getpwuid(uid)) == NULL) {
75 PERROR("getpwuid(%d)", uid);
80 strlen(base) + strlen("/") +
81 strlen(pwd->pw_name) + NULLBYTE_SIZE;
82 (*root) = (char *)malloc(root_len);
83 if ((*root) == NULL) {
84 PERROR("malloc(%d)", root_len);
88 sprintf((*root), "%s/%s", base, pwd->pw_name);
89 (*root)[root_len - 1] = '\0';
92 /* Example: sandbox_root = /vservers/bnc, relpath = /proc/1 */
93 static int sandbox_file_exists(char *sandbox_root, char *relpath)
99 len = strlen(sandbox_root) + strlen(relpath) + NULLBYTE_SIZE;
100 if ((file = (char *)malloc(len)) == NULL) {
101 PERROR("malloc(%d)", len);
104 sprintf(file, "%s%s", sandbox_root, relpath);
105 file[len - 1] = '\0';
106 if (stat(file, &stat_buf) == 0) {
115 static int proc_mounted(char *sandbox_root)
117 return sandbox_file_exists(sandbox_root, "/proc/1");
120 static int devpts_mounted(char *sandbox_root)
122 return sandbox_file_exists(sandbox_root, "/dev/pts/0");
125 static void mount_proc(char *sandbox_root)
127 char *source = "/proc";
131 len = strlen(sandbox_root) + strlen("/") + strlen("proc") + NULLBYTE_SIZE;
132 if ((target = (char *)malloc(len)) == NULL) {
133 PERROR("malloc(%d)", len);
137 sprintf(target, "%s/proc", sandbox_root);
138 target[len - 1] = '\0';
139 if (!proc_mounted(sandbox_root))
140 mount(source, target, "proc", MS_BIND | MS_RDONLY, NULL);
145 static void mount_devpts(char *sandbox_root)
147 char *source = "/dev/pts";
151 len = strlen(sandbox_root) + strlen("/") + strlen("dev/pts") + NULLBYTE_SIZE;
152 if ((target = (char *)malloc(len)) == NULL) {
153 PERROR("malloc(%d)", len);
157 sprintf(target, "%s/dev/pts", sandbox_root);
158 target[len - 1] = '\0';
159 if (!devpts_mounted(sandbox_root))
160 mount(source, target, "devpts", 0, NULL);
165 static int sandbox_chroot(uid_t uid)
167 char *sandbox_root = NULL;
169 compute_new_root(VSERVER_ROOT_BASE,&sandbox_root, uid);
170 mount_proc(sandbox_root);
171 mount_devpts(sandbox_root);
172 if (chroot(sandbox_root) < 0) {
173 PERROR("chroot(%s)", sandbox_root);
176 if (chdir("/") < 0) {
183 static int sandbox_processes(xid_t ctx, char *context)
185 #ifdef CONFIG_VSERVER_LEGACY
189 flags |= 1; /* VX_INFO_LOCK -- cannot request a new vx_id */
190 /* flags |= 4; VX_INFO_NPROC -- limit number of procs in a context */
192 (void) vc_new_s_context(ctx, 0, flags);
194 /* use legacy dirty hack for capremove */
195 if (vc_new_s_context(VC_SAMECTX, vc_get_insecurebcaps(), flags) == VC_NOCTX) {
196 PERROR("vc_new_s_context(%u, 0x%16ullx, 0x%08x)",
197 VC_SAMECTX, vc_get_insecurebcaps(), flags);
202 struct sliver_resources slr;
203 char hostname[HOST_NAME_MAX+1];
204 pl_get_limits(context,&slr);
206 if (gethostname(hostname, sizeof hostname) == -1)
208 PERROR("gethostname(...)");
212 /* check whether the slice has been suspended */
215 fprintf(stderr, "*** %s: %s has zero cpu resources and presumably it has been disabled/suspended ***\n", hostname, context);
219 (void) (sandbox_chroot(ctx));
221 if ((ctx_is_new = pl_chcontext(ctx, ~vc_get_insecurebcaps(),&slr)) < 0)
223 PERROR("pl_chcontext(%u)", ctx);
228 pl_set_limits(ctx,&slr);
236 void runas_slice_user(char *username)
238 struct passwd pwdd, *pwd = &pwdd, *result;
240 char *home_env, *logname_env, *mail_env, *shell_env, *user_env;
241 int home_len, logname_len, mail_len, shell_len, user_len;
243 static char *envp[10];
246 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
247 if (pwdBuffer_len == -1) {
248 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
252 pwdBuffer = (char*)malloc(pwdBuffer_len);
253 if (pwdBuffer == NULL) {
254 PERROR("malloc(%d)", pwdBuffer_len);
259 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
260 PERROR("getpwnam_r(%s)", username);
264 if (setgid(pwd->pw_gid) < 0) {
265 PERROR("setgid(%d)", pwd->pw_gid);
269 if (setuid(pwd->pw_uid) < 0) {
270 PERROR("setuid(%d)", pwd->pw_uid);
274 if (chdir(pwd->pw_dir) < 0) {
275 PERROR("chdir(%s)", pwd->pw_dir);
279 home_len = strlen("HOME=") + strlen(pwd->pw_dir) + NULLBYTE_SIZE;
280 logname_len = strlen("LOGNAME=") + strlen(username) + NULLBYTE_SIZE;
281 mail_len = strlen("MAIL=/var/spool/mail/") + strlen(username)
283 shell_len = strlen("SHELL=") + strlen(pwd->pw_shell) + NULLBYTE_SIZE;
284 user_len = strlen("USER=") + strlen(username) + NULLBYTE_SIZE;
286 home_env = (char *)malloc(home_len);
287 logname_env = (char *)malloc(logname_len);
288 mail_env = (char *)malloc(mail_len);
289 shell_env = (char *)malloc(shell_len);
290 user_env = (char *)malloc(user_len);
292 if ((home_env == NULL) ||
293 (logname_env == NULL) ||
294 (mail_env == NULL) ||
295 (shell_env == NULL) ||
296 (user_env == NULL)) {
301 sprintf(home_env, "HOME=%s", pwd->pw_dir);
302 sprintf(logname_env, "LOGNAME=%s", username);
303 sprintf(mail_env, "MAIL=/var/spool/mail/%s", username);
304 sprintf(shell_env, "SHELL=%s", pwd->pw_shell);
305 sprintf(user_env, "USER=%s", username);
307 home_env[home_len - 1] = '\0';
308 logname_env[logname_len - 1] = '\0';
309 mail_env[mail_len - 1] = '\0';
310 shell_env[shell_len - 1] = '\0';
311 user_env[user_len - 1] = '\0';
314 envp[1] = logname_env;
320 if ((putenv(home_env) < 0) ||
321 (putenv(logname_env) < 0) ||
322 (putenv(mail_env) < 0) ||
323 (putenv(shell_env) < 0) ||
324 (putenv(user_env) < 0)) {
325 PERROR("vserver: putenv error ");
330 void slice_enter(char *context)
332 struct passwd pwdd, *pwd = &pwdd, *result;
337 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
338 if (pwdBuffer_len == -1) {
339 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
343 pwdBuffer = (char*)malloc(pwdBuffer_len);
344 if (pwdBuffer == NULL) {
345 PERROR("malloc(%d)", pwdBuffer_len);
350 if ((getpwnam_r(context,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
351 PERROR("getpwnam_r(%s)", context);
356 if (setuidgid_root() < 0) { /* For chroot, new_s_context */
357 fprintf(stderr, "vsh: Could not become root, check that SUID flag is set on binary\n");
361 #ifdef CONFIG_VSERVER_LEGACY
362 (void) (sandbox_chroot(uid));
365 if (sandbox_processes((xid_t) uid, context) < 0) {
366 fprintf(stderr, "vsh: Could not change context to %d\n", uid);
371 //--------------------------------------------------------------------
373 #define DEFAULT_SHELL "/bin/sh"
375 /* Exit statuses for programs like 'env' that exec other programs.
376 EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs. */
379 EXIT_CANNOT_INVOKE = 126,
383 int main(int argc, char **argv)
385 struct passwd pwdd, *pwd = &pwdd, *result;
386 char *context, *username, *shell, *pwdBuffer;
397 if ((pwd = getpwuid(uid)) == NULL) {
398 PERROR("getpwuid(%d)", uid);
402 context = (char*)strdup(pwd->pw_name);
408 /* enter vserver "context" */
409 slice_enter(context);
411 /* Now run as username in this context. Note that for PlanetLab's
412 vserver configuration the context name also happens to be the
413 "default" username within the vserver context.
416 runas_slice_user(username);
418 /* With the uid/gid appropriately set. Let's figure out what the
419 * shell in the vserver's /etc/passwd is for the given username.
422 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
423 if (pwdBuffer_len == -1) {
424 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX");
427 pwdBuffer = (char*)malloc(pwdBuffer_len);
428 if (pwdBuffer == NULL) {
429 PERROR("malloc(%d)", pwdBuffer_len);
434 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
435 PERROR("getpwnam_r(%s)", username);
439 /* Make sure pw->pw_shell is non-NULL.*/
440 if (pwd->pw_shell == NULL || pwd->pw_shell[0] == '\0') {
441 pwd->pw_shell = (char *) DEFAULT_SHELL;
444 shell = (char *)strdup(pwd->pw_shell);
450 /* Check whether 'su' or 'sshd' invoked us as a login shell or
451 not; did this above when testing argv[0]=='-'.
456 args = (char**)malloc(sizeof(char*)*(argc+2));
458 PERROR("malloc(%d)", sizeof(char*)*(argc+2));
463 for(i=1;i<argc+1;i++) {
468 (void) execvp(shell,argv);
470 int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
474 return 0; /* shutup compiler */
477 int main(int argc, char *argv[])