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];
118 pl_get_limits(context,&slr);
120 if (gethostname(hostname, sizeof hostname) == -1)
122 PERROR("gethostname(...)");
126 /* check whether the slice has been suspended */
129 fprintf(stderr, "*** %s: %s has zero cpu resources and presumably it has been disabled/suspended ***\n", hostname, context);
133 unshare_netns = pl_unshare_netns(ctx);
135 (void) (sandbox_chroot(pwd));
137 if ((ctx_is_new = pl_chcontext(ctx, ~vc_get_insecurebcaps(),&slr,
140 PERROR("pl_chcontext(%u)", ctx);
145 fprintf(stderr, " *** %s: %s has not been started yet, please check back later ***\n", hostname, context);
153 void runas_slice_user(struct passwd *pwd)
155 char *username = pwd->pw_name;
156 char *home_env, *logname_env, *mail_env, *shell_env, *user_env;
157 int home_len, logname_len, mail_len, shell_len, user_len;
158 static char *envp[10];
160 if (setgid(pwd->pw_gid) < 0) {
161 PERROR("setgid(%d)", pwd->pw_gid);
165 if (setuid(pwd->pw_uid) < 0) {
166 PERROR("setuid(%d)", pwd->pw_uid);
170 if (chdir(pwd->pw_dir) < 0) {
171 PERROR("chdir(%s)", pwd->pw_dir);
175 home_len = strlen("HOME=") + strlen(pwd->pw_dir) + NULLBYTE_SIZE;
176 logname_len = strlen("LOGNAME=") + strlen(username) + NULLBYTE_SIZE;
177 mail_len = strlen("MAIL=/var/spool/mail/") + strlen(username)
179 shell_len = strlen("SHELL=") + strlen(pwd->pw_shell) + NULLBYTE_SIZE;
180 user_len = strlen("USER=") + strlen(username) + NULLBYTE_SIZE;
182 home_env = (char *)malloc(home_len);
183 logname_env = (char *)malloc(logname_len);
184 mail_env = (char *)malloc(mail_len);
185 shell_env = (char *)malloc(shell_len);
186 user_env = (char *)malloc(user_len);
188 if ((home_env == NULL) ||
189 (logname_env == NULL) ||
190 (mail_env == NULL) ||
191 (shell_env == NULL) ||
192 (user_env == NULL)) {
197 sprintf(home_env, "HOME=%s", pwd->pw_dir);
198 sprintf(logname_env, "LOGNAME=%s", username);
199 sprintf(mail_env, "MAIL=/var/spool/mail/%s", username);
200 sprintf(shell_env, "SHELL=%s", pwd->pw_shell);
201 sprintf(user_env, "USER=%s", username);
203 home_env[home_len - 1] = '\0';
204 logname_env[logname_len - 1] = '\0';
205 mail_env[mail_len - 1] = '\0';
206 shell_env[shell_len - 1] = '\0';
207 user_env[user_len - 1] = '\0';
210 envp[1] = logname_env;
216 if ((putenv(home_env) < 0) ||
217 (putenv(logname_env) < 0) ||
218 (putenv(mail_env) < 0) ||
219 (putenv(shell_env) < 0) ||
220 (putenv(user_env) < 0)) {
221 PERROR("vserver: putenv error ");
226 void slice_enter(struct passwd *pwd)
228 if (setuidgid_root() < 0) { /* For chroot, new_s_context */
229 fprintf(stderr, "vsh: Could not become root, check that SUID flag is set on binary\n");
233 #ifdef CONFIG_VSERVER_LEGACY
234 (void) (sandbox_chroot(pwd));
237 if (sandbox_processes((xid_t) pwd->pw_uid, pwd->pw_name, pwd) < 0) {
238 fprintf(stderr, "vsh: Could not change context to %d\n", pwd->pw_uid);
243 //--------------------------------------------------------------------
245 #define DEFAULT_SHELL "/bin/sh"
247 /* Exit statuses for programs like 'env' that exec other programs.
248 EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs. */
251 EXIT_CANNOT_INVOKE = 126,
255 int main(int argc, char **argv)
257 struct passwd pwdd, *result, *prechroot, *postchroot = &pwdd;
258 char *context, *username, *shell, *pwdBuffer;
269 if ((prechroot = getpwuid(uid)) == NULL) {
270 PERROR("getpwuid(%d)", uid);
274 context = (char*)strdup(prechroot->pw_name);
280 /* enter vserver "context" */
281 slice_enter(prechroot);
283 /* Get the /etc/passwd entry for this user, this time inside
288 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
289 if (pwdBuffer_len == -1) {
290 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX");
293 pwdBuffer = (char*)malloc(pwdBuffer_len);
294 if (pwdBuffer == NULL) {
295 PERROR("malloc(%d)", pwdBuffer_len);
300 if ((getpwnam_r(username,postchroot,pwdBuffer,pwdBuffer_len, &result) != 0) ||
301 (errno != 0) || result != postchroot) {
302 PERROR("getpwnam_r(%s)", username);
306 /* Now run as username in this context. Note that for PlanetLab's
307 vserver configuration the context name also happens to be the
308 "default" username within the vserver context.
310 runas_slice_user(postchroot);
312 /* Make sure pw->pw_shell is non-NULL.*/
313 if (postchroot->pw_shell == NULL || postchroot->pw_shell[0] == '\0') {
314 postchroot->pw_shell = (char *) DEFAULT_SHELL;
317 shell = (char *)strdup(postchroot->pw_shell);
323 /* Check whether 'su' or 'sshd' invoked us as a login shell or
324 not; did this above when testing argv[0]=='-'.
329 args = (char**)malloc(sizeof(char*)*(argc+2));
331 PERROR("malloc(%d)", sizeof(char*)*(argc+2));
336 for(i=1;i<argc+1;i++) {
341 (void) execvp(shell,argv);
343 int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
347 return 0; /* shutup compiler */