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, const struct passwd *pwd)
73 strlen(base) + strlen("/") +
74 strlen(pwd->pw_name) + NULLBYTE_SIZE;
75 (*root) = (char *)malloc(root_len);
76 if ((*root) == NULL) {
77 PERROR("malloc(%d)", root_len);
81 sprintf((*root), "%s/%s", base, pwd->pw_name);
82 (*root)[root_len - 1] = '\0';
85 static int sandbox_chroot(const struct passwd *pwd)
87 char *sandbox_root = NULL;
89 compute_new_root(VSERVER_ROOT_BASE,&sandbox_root, pwd);
90 if (chroot(sandbox_root) < 0) {
91 PERROR("chroot(%s)", sandbox_root);
101 static int sandbox_processes(xid_t ctx, const char *context, const struct passwd *pwd)
103 #ifdef CONFIG_VSERVER_LEGACY
107 flags |= 1; /* VX_INFO_LOCK -- cannot request a new vx_id */
108 /* flags |= 4; VX_INFO_NPROC -- limit number of procs in a context */
110 (void) vc_new_s_context(ctx, 0, flags);
112 /* use legacy dirty hack for capremove */
113 if (vc_new_s_context(VC_SAMECTX, vc_get_insecurebcaps(), flags) == VC_NOCTX) {
114 PERROR("vc_new_s_context(%u, 0x%16llx, 0x%08x)",
115 VC_SAMECTX, vc_get_insecurebcaps(), flags);
120 struct sliver_resources slr;
121 char hostname[HOST_NAME_MAX+1];
122 pl_get_limits(context,&slr);
124 if (gethostname(hostname, sizeof hostname) == -1)
126 PERROR("gethostname(...)");
130 /* check whether the slice has been suspended */
133 fprintf(stderr, "*** %s: %s has zero cpu resources and presumably it has been disabled/suspended ***\n", hostname, context);
137 (void) (sandbox_chroot(pwd));
139 if ((ctx_is_new = pl_chcontext(ctx, ~vc_get_insecurebcaps(),&slr)) < 0)
141 PERROR("pl_chcontext(%u)", ctx);
146 fprintf(stderr, " *** %s: %s has not been started yet, please check back later ***\n", hostname, context);
154 void runas_slice_user(struct passwd *pwd)
156 char *username = pwd->pw_name;
157 char *home_env, *logname_env, *mail_env, *shell_env, *user_env;
158 int home_len, logname_len, mail_len, shell_len, user_len;
159 static char *envp[10];
161 if (setgid(pwd->pw_gid) < 0) {
162 PERROR("setgid(%d)", pwd->pw_gid);
166 if (setuid(pwd->pw_uid) < 0) {
167 PERROR("setuid(%d)", pwd->pw_uid);
171 if (chdir(pwd->pw_dir) < 0) {
172 PERROR("chdir(%s)", pwd->pw_dir);
176 home_len = strlen("HOME=") + strlen(pwd->pw_dir) + NULLBYTE_SIZE;
177 logname_len = strlen("LOGNAME=") + strlen(username) + NULLBYTE_SIZE;
178 mail_len = strlen("MAIL=/var/spool/mail/") + strlen(username)
180 shell_len = strlen("SHELL=") + strlen(pwd->pw_shell) + NULLBYTE_SIZE;
181 user_len = strlen("USER=") + strlen(username) + NULLBYTE_SIZE;
183 home_env = (char *)malloc(home_len);
184 logname_env = (char *)malloc(logname_len);
185 mail_env = (char *)malloc(mail_len);
186 shell_env = (char *)malloc(shell_len);
187 user_env = (char *)malloc(user_len);
189 if ((home_env == NULL) ||
190 (logname_env == NULL) ||
191 (mail_env == NULL) ||
192 (shell_env == NULL) ||
193 (user_env == NULL)) {
198 sprintf(home_env, "HOME=%s", pwd->pw_dir);
199 sprintf(logname_env, "LOGNAME=%s", username);
200 sprintf(mail_env, "MAIL=/var/spool/mail/%s", username);
201 sprintf(shell_env, "SHELL=%s", pwd->pw_shell);
202 sprintf(user_env, "USER=%s", username);
204 home_env[home_len - 1] = '\0';
205 logname_env[logname_len - 1] = '\0';
206 mail_env[mail_len - 1] = '\0';
207 shell_env[shell_len - 1] = '\0';
208 user_env[user_len - 1] = '\0';
211 envp[1] = logname_env;
217 if ((putenv(home_env) < 0) ||
218 (putenv(logname_env) < 0) ||
219 (putenv(mail_env) < 0) ||
220 (putenv(shell_env) < 0) ||
221 (putenv(user_env) < 0)) {
222 PERROR("vserver: putenv error ");
227 void slice_enter(struct passwd *pwd)
229 if (setuidgid_root() < 0) { /* For chroot, new_s_context */
230 fprintf(stderr, "vsh: Could not become root, check that SUID flag is set on binary\n");
234 #ifdef CONFIG_VSERVER_LEGACY
235 (void) (sandbox_chroot(pwd));
238 if (sandbox_processes((xid_t) pwd->pw_uid, pwd->pw_name, pwd) < 0) {
239 fprintf(stderr, "vsh: Could not change context to %d\n", pwd->pw_uid);
244 //--------------------------------------------------------------------
246 #define DEFAULT_SHELL "/bin/sh"
248 /* Exit statuses for programs like 'env' that exec other programs.
249 EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs. */
252 EXIT_CANNOT_INVOKE = 126,
256 int main(int argc, char **argv)
258 struct passwd pwdd, *result, *prechroot, *postchroot = &pwdd;
259 char *context, *username, *shell, *pwdBuffer;
270 if ((prechroot = getpwuid(uid)) == NULL) {
271 PERROR("getpwuid(%d)", uid);
275 context = (char*)strdup(prechroot->pw_name);
281 /* enter vserver "context" */
282 slice_enter(prechroot);
284 /* Get the /etc/passwd entry for this user, this time inside
289 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
290 if (pwdBuffer_len == -1) {
291 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX");
294 pwdBuffer = (char*)malloc(pwdBuffer_len);
295 if (pwdBuffer == NULL) {
296 PERROR("malloc(%d)", pwdBuffer_len);
301 if ((getpwnam_r(username,postchroot,pwdBuffer,pwdBuffer_len, &result) != 0) ||
302 (errno != 0) || result != postchroot) {
303 PERROR("getpwnam_r(%s)", username);
307 /* Now run as username in this context. Note that for PlanetLab's
308 vserver configuration the context name also happens to be the
309 "default" username within the vserver context.
311 runas_slice_user(postchroot);
313 /* Make sure pw->pw_shell is non-NULL.*/
314 if (postchroot->pw_shell == NULL || postchroot->pw_shell[0] == '\0') {
315 postchroot->pw_shell = (char *) DEFAULT_SHELL;
318 shell = (char *)strdup(postchroot->pw_shell);
324 /* Check whether 'su' or 'sshd' invoked us as a login shell or
325 not; did this above when testing argv[0]=='-'.
330 args = (char**)malloc(sizeof(char*)*(argc+2));
332 PERROR("malloc(%d)", sizeof(char*)*(argc+2));
337 for(i=1;i<argc+1;i++) {
342 (void) execvp(shell,argv);
344 int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
348 return 0; /* shutup compiler */