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 suspended */
214 fprintf(stderr, "*** %s: %s has zero cpu resources and presumably it has been disabled/suspended ***\n", hostname, context);
218 (void) (sandbox_chroot(ctx));
220 if ((ctx_is_new = pl_chcontext(ctx, ~vc_get_insecurebcaps(),&slr)) < 0)
222 PERROR("pl_chcontext(%u)", ctx);
227 pl_set_limits(ctx,&slr);
235 void runas_slice_user(char *username)
237 struct passwd pwdd, *pwd = &pwdd, *result;
239 char *home_env, *logname_env, *mail_env, *shell_env, *user_env;
240 int home_len, logname_len, mail_len, shell_len, user_len;
242 static char *envp[10];
245 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
246 if (pwdBuffer_len == -1) {
247 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
251 pwdBuffer = (char*)malloc(pwdBuffer_len);
252 if (pwdBuffer == NULL) {
253 PERROR("malloc(%d)", pwdBuffer_len);
258 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
259 PERROR("getpwnam_r(%s)", username);
263 if (setgid(pwd->pw_gid) < 0) {
264 PERROR("setgid(%d)", pwd->pw_gid);
268 if (setuid(pwd->pw_uid) < 0) {
269 PERROR("setuid(%d)", pwd->pw_uid);
273 if (chdir(pwd->pw_dir) < 0) {
274 PERROR("chdir(%s)", pwd->pw_dir);
278 home_len = strlen("HOME=") + strlen(pwd->pw_dir) + NULLBYTE_SIZE;
279 logname_len = strlen("LOGNAME=") + strlen(username) + NULLBYTE_SIZE;
280 mail_len = strlen("MAIL=/var/spool/mail/") + strlen(username)
282 shell_len = strlen("SHELL=") + strlen(pwd->pw_shell) + NULLBYTE_SIZE;
283 user_len = strlen("USER=") + strlen(username) + NULLBYTE_SIZE;
285 home_env = (char *)malloc(home_len);
286 logname_env = (char *)malloc(logname_len);
287 mail_env = (char *)malloc(mail_len);
288 shell_env = (char *)malloc(shell_len);
289 user_env = (char *)malloc(user_len);
291 if ((home_env == NULL) ||
292 (logname_env == NULL) ||
293 (mail_env == NULL) ||
294 (shell_env == NULL) ||
295 (user_env == NULL)) {
300 sprintf(home_env, "HOME=%s", pwd->pw_dir);
301 sprintf(logname_env, "LOGNAME=%s", username);
302 sprintf(mail_env, "MAIL=/var/spool/mail/%s", username);
303 sprintf(shell_env, "SHELL=%s", pwd->pw_shell);
304 sprintf(user_env, "USER=%s", username);
306 home_env[home_len - 1] = '\0';
307 logname_env[logname_len - 1] = '\0';
308 mail_env[mail_len - 1] = '\0';
309 shell_env[shell_len - 1] = '\0';
310 user_env[user_len - 1] = '\0';
313 envp[1] = logname_env;
319 if ((putenv(home_env) < 0) ||
320 (putenv(logname_env) < 0) ||
321 (putenv(mail_env) < 0) ||
322 (putenv(shell_env) < 0) ||
323 (putenv(user_env) < 0)) {
324 PERROR("vserver: putenv error ");
329 void slice_enter(char *context)
331 struct passwd pwdd, *pwd = &pwdd, *result;
336 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
337 if (pwdBuffer_len == -1) {
338 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
342 pwdBuffer = (char*)malloc(pwdBuffer_len);
343 if (pwdBuffer == NULL) {
344 PERROR("malloc(%d)", pwdBuffer_len);
349 if ((getpwnam_r(context,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
350 PERROR("getpwnam_r(%s)", context);
355 if (setuidgid_root() < 0) { /* For chroot, new_s_context */
356 fprintf(stderr, "vsh: Could not become root, check that SUID flag is set on binary\n");
360 #ifdef CONFIG_VSERVER_LEGACY
361 (void) (sandbox_chroot(uid));
364 if (sandbox_processes((xid_t) uid, context) < 0) {
365 fprintf(stderr, "vsh: Could not change context to %d\n", uid);
370 //--------------------------------------------------------------------
372 #define DEFAULT_SHELL "/bin/sh"
374 /* Exit statuses for programs like 'env' that exec other programs.
375 EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs. */
378 EXIT_CANNOT_INVOKE = 126,
382 int main(int argc, char **argv)
384 struct passwd pwdd, *pwd = &pwdd, *result;
385 char *context, *username, *shell, *pwdBuffer;
396 if ((pwd = getpwuid(uid)) == NULL) {
397 PERROR("getpwuid(%d)", uid);
401 context = (char*)strdup(pwd->pw_name);
407 /* enter vserver "context" */
408 slice_enter(context);
410 /* Now run as username in this context. Note that for PlanetLab's
411 vserver configuration the context name also happens to be the
412 "default" username within the vserver context.
415 runas_slice_user(username);
417 /* With the uid/gid appropriately set. Let's figure out what the
418 * shell in the vserver's /etc/passwd is for the given username.
421 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
422 if (pwdBuffer_len == -1) {
423 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX");
426 pwdBuffer = (char*)malloc(pwdBuffer_len);
427 if (pwdBuffer == NULL) {
428 PERROR("malloc(%d)", pwdBuffer_len);
433 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
434 PERROR("getpwnam_r(%s)", username);
438 /* Make sure pw->pw_shell is non-NULL.*/
439 if (pwd->pw_shell == NULL || pwd->pw_shell[0] == '\0') {
440 pwd->pw_shell = (char *) DEFAULT_SHELL;
443 shell = (char *)strdup(pwd->pw_shell);
449 /* Check whether 'su' or 'sshd' invoked us as a login shell or
450 not; did this above when testing argv[0]=='-'.
455 args = (char**)malloc(sizeof(char*)*(argc+2));
457 PERROR("malloc(%d)", sizeof(char*)*(argc+2));
462 for(i=1;i<argc+1;i++) {
467 (void) execvp(shell,argv);
469 int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
473 return 0; /* shutup compiler */