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 /* Change to root:root (before entering new context) */
49 static int setuidgid_root()
62 static void compute_new_root(char *base, char **root, const struct passwd *pwd)
67 strlen(base) + strlen("/") +
68 strlen(pwd->pw_name) + NULLBYTE_SIZE;
69 (*root) = (char *)malloc(root_len);
70 if ((*root) == NULL) {
71 PERROR("malloc(%d)", root_len);
75 sprintf((*root), "%s/%s", base, pwd->pw_name);
76 (*root)[root_len - 1] = '\0';
79 static int sandbox_chroot(const struct passwd *pwd)
81 char *sandbox_root = NULL;
83 compute_new_root(DEFAULT_VSERVERDIR,&sandbox_root, pwd);
84 if (chroot(sandbox_root) < 0) {
85 PERROR("chroot(%s)", sandbox_root);
95 static int sandbox_processes(xid_t ctx, const char *context, const struct passwd *pwd)
97 #ifdef CONFIG_VSERVER_LEGACY
101 flags |= 1; /* VX_INFO_LOCK -- cannot request a new vx_id */
102 /* flags |= 4; VX_INFO_NPROC -- limit number of procs in a context */
104 (void) vc_new_s_context(ctx, 0, flags);
106 /* use legacy dirty hack for capremove */
107 if (vc_new_s_context(VC_SAMECTX, vc_get_insecurebcaps(), flags) == VC_NOCTX) {
108 PERROR("vc_new_s_context(%u, 0x%16llx, 0x%08x)",
109 VC_SAMECTX, vc_get_insecurebcaps(), flags);
114 struct sliver_resources slr;
115 char hostname[HOST_NAME_MAX+1];
116 pl_get_limits(context,&slr);
118 if (gethostname(hostname, sizeof hostname) == -1)
120 PERROR("gethostname(...)");
124 /* check whether the slice has been suspended */
127 fprintf(stderr, "*** %s: %s has zero cpu resources and presumably it has been disabled/suspended ***\n", hostname, context);
131 (void) (sandbox_chroot(pwd));
133 if ((ctx_is_new = pl_chcontext(ctx, ~vc_get_insecurebcaps(),&slr)) < 0)
135 PERROR("pl_chcontext(%u)", ctx);
140 fprintf(stderr, " *** %s: %s has not been started yet, please check back later ***\n", hostname, context);
148 void runas_slice_user(struct passwd *pwd)
150 char *username = pwd->pw_name;
151 char *home_env, *logname_env, *mail_env, *shell_env, *user_env;
152 int home_len, logname_len, mail_len, shell_len, user_len;
153 static char *envp[10];
155 if (setgid(pwd->pw_gid) < 0) {
156 PERROR("setgid(%d)", pwd->pw_gid);
160 if (setuid(pwd->pw_uid) < 0) {
161 PERROR("setuid(%d)", pwd->pw_uid);
165 if (chdir(pwd->pw_dir) < 0) {
166 PERROR("chdir(%s)", pwd->pw_dir);
170 home_len = strlen("HOME=") + strlen(pwd->pw_dir) + NULLBYTE_SIZE;
171 logname_len = strlen("LOGNAME=") + strlen(username) + NULLBYTE_SIZE;
172 mail_len = strlen("MAIL=/var/spool/mail/") + strlen(username)
174 shell_len = strlen("SHELL=") + strlen(pwd->pw_shell) + NULLBYTE_SIZE;
175 user_len = strlen("USER=") + strlen(username) + NULLBYTE_SIZE;
177 home_env = (char *)malloc(home_len);
178 logname_env = (char *)malloc(logname_len);
179 mail_env = (char *)malloc(mail_len);
180 shell_env = (char *)malloc(shell_len);
181 user_env = (char *)malloc(user_len);
183 if ((home_env == NULL) ||
184 (logname_env == NULL) ||
185 (mail_env == NULL) ||
186 (shell_env == NULL) ||
187 (user_env == NULL)) {
192 sprintf(home_env, "HOME=%s", pwd->pw_dir);
193 sprintf(logname_env, "LOGNAME=%s", username);
194 sprintf(mail_env, "MAIL=/var/spool/mail/%s", username);
195 sprintf(shell_env, "SHELL=%s", pwd->pw_shell);
196 sprintf(user_env, "USER=%s", username);
198 home_env[home_len - 1] = '\0';
199 logname_env[logname_len - 1] = '\0';
200 mail_env[mail_len - 1] = '\0';
201 shell_env[shell_len - 1] = '\0';
202 user_env[user_len - 1] = '\0';
205 envp[1] = logname_env;
211 if ((putenv(home_env) < 0) ||
212 (putenv(logname_env) < 0) ||
213 (putenv(mail_env) < 0) ||
214 (putenv(shell_env) < 0) ||
215 (putenv(user_env) < 0)) {
216 PERROR("vserver: putenv error ");
221 void slice_enter(struct passwd *pwd)
223 if (setuidgid_root() < 0) { /* For chroot, new_s_context */
224 fprintf(stderr, "vsh: Could not become root, check that SUID flag is set on binary\n");
228 #ifdef CONFIG_VSERVER_LEGACY
229 (void) (sandbox_chroot(pwd));
232 if (sandbox_processes((xid_t) pwd->pw_uid, pwd->pw_name, pwd) < 0) {
233 fprintf(stderr, "vsh: Could not change context to %d\n", pwd->pw_uid);
238 //--------------------------------------------------------------------
240 #define DEFAULT_SHELL "/bin/sh"
242 /* Exit statuses for programs like 'env' that exec other programs.
243 EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs. */
246 EXIT_CANNOT_INVOKE = 126,
250 int main(int argc, char **argv)
252 struct passwd pwdd, *result, *prechroot, *postchroot = &pwdd;
253 char *context, *username, *shell, *pwdBuffer;
264 if ((prechroot = getpwuid(uid)) == NULL) {
265 PERROR("getpwuid(%d)", uid);
269 context = (char*)strdup(prechroot->pw_name);
275 /* enter vserver "context" */
276 slice_enter(prechroot);
278 /* Get the /etc/passwd entry for this user, this time inside
283 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
284 if (pwdBuffer_len == -1) {
285 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX");
288 pwdBuffer = (char*)malloc(pwdBuffer_len);
289 if (pwdBuffer == NULL) {
290 PERROR("malloc(%d)", pwdBuffer_len);
295 if ((getpwnam_r(username,postchroot,pwdBuffer,pwdBuffer_len, &result) != 0) ||
296 (errno != 0) || result != postchroot) {
297 PERROR("getpwnam_r(%s)", username);
301 /* Now run as username in this context. Note that for PlanetLab's
302 vserver configuration the context name also happens to be the
303 "default" username within the vserver context.
305 runas_slice_user(postchroot);
307 /* Make sure pw->pw_shell is non-NULL.*/
308 if (postchroot->pw_shell == NULL || postchroot->pw_shell[0] == '\0') {
309 postchroot->pw_shell = (char *) DEFAULT_SHELL;
312 shell = (char *)strdup(postchroot->pw_shell);
318 /* Check whether 'su' or 'sshd' invoked us as a login shell or
319 not; did this above when testing argv[0]=='-'.
324 args = (char**)malloc(sizeof(char*)*(argc+2));
326 PERROR("malloc(%d)", sizeof(char*)*(argc+2));
331 for(i=1;i<argc+1;i++) {
336 (void) execvp(shell,argv);
338 int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
342 return 0; /* shutup compiler */