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
34 #include <sys/syscall.h>
35 #include <asm/unistd.h>
36 #include <sys/mount.h>
37 #include <sys/types.h>
39 #include <sys/resource.h>
44 //--------------------------------------------------------------------
46 #include "planetlab.h"
48 #undef CONFIG_VSERVER_LEGACY
50 /* Base for all vserver roots for chroot */
51 #define VSERVER_ROOT_BASE "/vservers"
53 /* Change to root:root (before entering new context) */
54 static int setuidgid_root()
67 static void compute_new_root(char *base, char **root, uid_t uid)
72 if ((pwd = getpwuid(uid)) == NULL) {
73 PERROR("getpwuid(%d)", uid);
78 strlen(base) + strlen("/") +
79 strlen(pwd->pw_name) + NULLBYTE_SIZE;
80 (*root) = (char *)malloc(root_len);
81 if ((*root) == NULL) {
82 PERROR("malloc(%d)", root_len);
86 sprintf((*root), "%s/%s", base, pwd->pw_name);
87 (*root)[root_len - 1] = '\0';
90 /* Example: sandbox_root = /vservers/bnc, relpath = /proc/1 */
91 static int sandbox_file_exists(char *sandbox_root, char *relpath)
97 len = strlen(sandbox_root) + strlen(relpath) + NULLBYTE_SIZE;
98 if ((file = (char *)malloc(len)) == NULL) {
99 PERROR("malloc(%d)", len);
102 sprintf(file, "%s%s", sandbox_root, relpath);
103 file[len - 1] = '\0';
104 if (stat(file, &stat_buf) == 0) {
113 static int proc_mounted(char *sandbox_root)
115 return sandbox_file_exists(sandbox_root, "/proc/1");
118 static int devpts_mounted(char *sandbox_root)
120 return sandbox_file_exists(sandbox_root, "/dev/pts/0");
123 static void mount_proc(char *sandbox_root)
125 char *source = "/proc";
129 len = strlen(sandbox_root) + strlen("/") + strlen("proc") + NULLBYTE_SIZE;
130 if ((target = (char *)malloc(len)) == NULL) {
131 PERROR("malloc(%d)", len);
135 sprintf(target, "%s/proc", sandbox_root);
136 target[len - 1] = '\0';
137 if (!proc_mounted(sandbox_root))
138 mount(source, target, "proc", MS_BIND | MS_RDONLY, NULL);
143 static void mount_devpts(char *sandbox_root)
145 char *source = "/dev/pts";
149 len = strlen(sandbox_root) + strlen("/") + strlen("dev/pts") + NULLBYTE_SIZE;
150 if ((target = (char *)malloc(len)) == NULL) {
151 PERROR("malloc(%d)", len);
155 sprintf(target, "%s/dev/pts", sandbox_root);
156 target[len - 1] = '\0';
157 if (!devpts_mounted(sandbox_root))
158 mount(source, target, "devpts", 0, NULL);
163 static int sandbox_chroot(uid_t uid)
165 char *sandbox_root = NULL;
167 compute_new_root(VSERVER_ROOT_BASE,&sandbox_root, uid);
168 mount_proc(sandbox_root);
169 mount_devpts(sandbox_root);
170 if (chroot(sandbox_root) < 0) {
171 PERROR("chroot(%s)", sandbox_root);
174 if (chdir("/") < 0) {
181 static int sandbox_processes(xid_t ctx, char *context)
183 #ifdef CONFIG_VSERVER_LEGACY
187 flags |= 1; /* VX_INFO_LOCK -- cannot request a new vx_id */
188 /* flags |= 4; VX_INFO_NPROC -- limit number of procs in a context */
190 (void) vc_new_s_context(ctx, 0, flags);
192 /* use legacy dirty hack for capremove */
193 if (vc_new_s_context(VC_SAMECTX, vc_get_insecurebcaps(), flags) == VC_NOCTX) {
194 PERROR("vc_new_s_context(%u, 0x%16ullx, 0x%08x)",
195 VC_SAMECTX, vc_get_insecurebcaps(), flags);
200 struct sliver_resources slr;
201 pl_get_limits(context,&slr);
203 /* check whether the slice has been taken off of the whitelist */
204 if (slr.vs_whitelisted==0)
206 fprintf(stderr, "*** %s has not been allocated resources on this node ***\n", context);
210 /* check whether the slice has been suspended */
213 fprintf(stderr, "*** %s has zero cpu resources and presumably it has been disabled/suspended ***\n");
217 (void) (sandbox_chroot(ctx));
219 if ((ctx_is_new = pl_chcontext(ctx, 0, ~vc_get_insecurebcaps(),&slr)) < 0)
221 PERROR("pl_chcontext(%u)", ctx);
226 pl_set_limits(ctx,&slr);
234 void runas_slice_user(char *username)
236 struct passwd pwdd, *pwd = &pwdd, *result;
238 char *home_env, *logname_env, *mail_env, *shell_env, *user_env;
239 int home_len, logname_len, mail_len, shell_len, user_len;
241 static char *envp[10];
244 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
245 if (pwdBuffer_len == -1) {
246 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
250 pwdBuffer = (char*)malloc(pwdBuffer_len);
251 if (pwdBuffer == NULL) {
252 PERROR("malloc(%d)", pwdBuffer_len);
257 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
258 PERROR("getpwnam_r(%s)", username);
262 if (setgid(pwd->pw_gid) < 0) {
263 PERROR("setgid(%d)", pwd->pw_gid);
267 if (setuid(pwd->pw_uid) < 0) {
268 PERROR("setuid(%d)", pwd->pw_uid);
272 if (chdir(pwd->pw_dir) < 0) {
273 PERROR("chdir(%s)", pwd->pw_dir);
277 home_len = strlen("HOME=") + strlen(pwd->pw_dir) + NULLBYTE_SIZE;
278 logname_len = strlen("LOGNAME=") + strlen(username) + NULLBYTE_SIZE;
279 mail_len = strlen("MAIL=/var/spool/mail/") + strlen(username)
281 shell_len = strlen("SHELL=") + strlen(pwd->pw_shell) + NULLBYTE_SIZE;
282 user_len = strlen("USER=") + strlen(username) + NULLBYTE_SIZE;
284 home_env = (char *)malloc(home_len);
285 logname_env = (char *)malloc(logname_len);
286 mail_env = (char *)malloc(mail_len);
287 shell_env = (char *)malloc(shell_len);
288 user_env = (char *)malloc(user_len);
290 if ((home_env == NULL) ||
291 (logname_env == NULL) ||
292 (mail_env == NULL) ||
293 (shell_env == NULL) ||
294 (user_env == NULL)) {
299 sprintf(home_env, "HOME=%s", pwd->pw_dir);
300 sprintf(logname_env, "LOGNAME=%s", username);
301 sprintf(mail_env, "MAIL=/var/spool/mail/%s", username);
302 sprintf(shell_env, "SHELL=%s", pwd->pw_shell);
303 sprintf(user_env, "USER=%s", username);
305 home_env[home_len - 1] = '\0';
306 logname_env[logname_len - 1] = '\0';
307 mail_env[mail_len - 1] = '\0';
308 shell_env[shell_len - 1] = '\0';
309 user_env[user_len - 1] = '\0';
312 envp[1] = logname_env;
318 if ((putenv(home_env) < 0) ||
319 (putenv(logname_env) < 0) ||
320 (putenv(mail_env) < 0) ||
321 (putenv(shell_env) < 0) ||
322 (putenv(user_env) < 0)) {
323 PERROR("vserver: putenv error ");
328 void slice_enter(char *context)
330 struct passwd pwdd, *pwd = &pwdd, *result;
335 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
336 if (pwdBuffer_len == -1) {
337 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
341 pwdBuffer = (char*)malloc(pwdBuffer_len);
342 if (pwdBuffer == NULL) {
343 PERROR("malloc(%d)", pwdBuffer_len);
348 if ((getpwnam_r(context,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
349 PERROR("getpwnam_r(%s)", context);
354 if (setuidgid_root() < 0) { /* For chroot, new_s_context */
355 fprintf(stderr, "vsh: Could not become root, check that SUID flag is set on binary\n");
359 #ifdef CONFIG_VSERVER_LEGACY
360 (void) (sandbox_chroot(uid));
363 if (sandbox_processes((xid_t) uid, context) < 0) {
364 fprintf(stderr, "vsh: Could not change context to %d\n", uid);
369 //--------------------------------------------------------------------
371 #define DEFAULT_SHELL "/bin/sh"
373 /* Exit statuses for programs like 'env' that exec other programs.
374 EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs. */
377 EXIT_CANNOT_INVOKE = 126,
381 int main(int argc, char **argv)
383 struct passwd pwdd, *pwd = &pwdd, *result;
384 char *context, *username, *shell, *pwdBuffer;
395 if ((pwd = getpwuid(uid)) == NULL) {
396 PERROR("getpwuid(%d)", uid);
400 context = (char*)strdup(pwd->pw_name);
406 /* enter vserver "context" */
407 slice_enter(context);
409 /* Now run as username in this context. Note that for PlanetLab's
410 vserver configuration the context name also happens to be the
411 "default" username within the vserver context.
414 runas_slice_user(username);
416 /* With the uid/gid appropriately set. Let's figure out what the
417 * shell in the vserver's /etc/passwd is for the given username.
420 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
421 if (pwdBuffer_len == -1) {
422 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX");
425 pwdBuffer = (char*)malloc(pwdBuffer_len);
426 if (pwdBuffer == NULL) {
427 PERROR("malloc(%d)", pwdBuffer_len);
432 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
433 PERROR("getpwnam_r(%s)", username);
437 /* Make sure pw->pw_shell is non-NULL.*/
438 if (pwd->pw_shell == NULL || pwd->pw_shell[0] == '\0') {
439 pwd->pw_shell = (char *) DEFAULT_SHELL;
442 shell = (char *)strdup(pwd->pw_shell);
448 /* Check whether 'su' or 'sshd' invoked us as a login shell or
449 not; did this above when testing argv[0]=='-'.
454 args = (char**)malloc(sizeof(char*)*(argc+2));
456 PERROR("malloc(%d)", sizeof(char*)*(argc+2));
461 for(i=1;i<argc+1;i++) {
466 (void) execvp(shell,argv);
468 int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
472 return 0; /* shutup compiler */