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>
44 //--------------------------------------------------------------------
47 #undef CONFIG_VSERVER_LEGACY
49 /* Null byte made explicit */
50 #define NULLBYTE_SIZE 1
52 /* Base for all vserver roots for chroot */
53 #define VSERVER_ROOT_BASE "/vservers"
56 _PERROR(const char *format, char *file, int line, int _errno, ...)
61 fprintf(stderr, "%s:%d: ", file, line);
62 vfprintf(stderr, format, ap);
64 fprintf(stderr, ": %s (%d)", strerror(_errno), _errno);
71 #define PERROR(format, args...) _PERROR(format, __FILE__, __LINE__, errno, ## args)
73 /* Change to root:root (before entering new context) */
74 static int setuidgid_root()
87 static void compute_new_root(char *base, char **root, uid_t uid)
92 if ((pwd = getpwuid(uid)) == NULL) {
93 PERROR("getpwuid(%d)", uid);
98 strlen(base) + strlen("/") +
99 strlen(pwd->pw_name) + NULLBYTE_SIZE;
100 (*root) = (char *)malloc(root_len);
101 if ((*root) == NULL) {
102 PERROR("malloc(%d)", root_len);
106 sprintf((*root), "%s/%s", base, pwd->pw_name);
107 (*root)[root_len - 1] = '\0';
110 /* Example: sandbox_root = /vservers/bnc, relpath = /proc/1 */
111 static int sandbox_file_exists(char *sandbox_root, char *relpath)
113 struct stat stat_buf;
117 len = strlen(sandbox_root) + strlen(relpath) + NULLBYTE_SIZE;
118 if ((file = (char *)malloc(len)) == NULL) {
119 PERROR("malloc(%d)", len);
122 sprintf(file, "%s%s", sandbox_root, relpath);
123 file[len - 1] = '\0';
124 if (stat(file, &stat_buf) == 0) {
133 static int proc_mounted(char *sandbox_root)
135 return sandbox_file_exists(sandbox_root, "/proc/1");
138 static int devpts_mounted(char *sandbox_root)
140 return sandbox_file_exists(sandbox_root, "/dev/pts/0");
143 static void mount_proc(char *sandbox_root)
145 char *source = "/proc";
149 len = strlen(sandbox_root) + strlen("/") + strlen("proc") + NULLBYTE_SIZE;
150 if ((target = (char *)malloc(len)) == NULL) {
151 PERROR("malloc(%d)", len);
155 sprintf(target, "%s/proc", sandbox_root);
156 target[len - 1] = '\0';
157 if (!proc_mounted(sandbox_root))
158 mount(source, target, "proc", MS_BIND | MS_RDONLY, NULL);
163 static void mount_devpts(char *sandbox_root)
165 char *source = "/dev/pts";
169 len = strlen(sandbox_root) + strlen("/") + strlen("dev/pts") + NULLBYTE_SIZE;
170 if ((target = (char *)malloc(len)) == NULL) {
171 PERROR("malloc(%d)", len);
175 sprintf(target, "%s/dev/pts", sandbox_root);
176 target[len - 1] = '\0';
177 if (!devpts_mounted(sandbox_root))
178 mount(source, target, "devpts", 0, NULL);
183 static int sandbox_chroot(uid_t uid)
185 char *sandbox_root = NULL;
187 compute_new_root(VSERVER_ROOT_BASE,&sandbox_root, uid);
188 mount_proc(sandbox_root);
189 mount_devpts(sandbox_root);
190 if (chroot(sandbox_root) < 0) {
191 PERROR("chroot(%s)", sandbox_root);
194 if (chdir("/") < 0) {
201 static int sandbox_processes(xid_t xid)
203 #ifdef CONFIG_VSERVER_LEGACY
207 flags |= 1; /* VX_INFO_LOCK -- cannot request a new vx_id */
208 /* flags |= 4; VX_INFO_NPROC -- limit number of procs in a context */
210 (void) vc_new_s_context(xid, 0, flags);
212 /* use legacy dirty hack for capremove */
213 if (vc_new_s_context(VC_SAMECTX, vc_get_insecurebcaps(), flags) == VC_NOCTX) {
214 PERROR("vc_new_s_context(%u, 0x%16ullx, 0x%08x)",
215 VC_SAMECTX, vc_get_insecurebcaps(), flags);
219 struct vc_ctx_caps caps;
220 struct vc_ctx_flags flags;
222 caps.ccaps = ~vc_get_insecureccaps();
224 caps.bcaps = ~vc_get_insecurebcaps();
227 flags.flagword = VC_VXF_INFO_LOCK;
228 flags.mask = VC_VXF_STATE_SETUP | VC_VXF_INFO_LOCK;
230 if (vc_ctx_create(xid) == VC_NOCTX) {
231 PERROR("vc_ctx_create(%d)", xid);
235 if (vc_set_ccaps(xid, &caps) == -1) {
236 PERROR("vc_set_ccaps(%d, 0x%16ullx/0x%16ullx, 0x%16ullx/0x%16ullx)\n",
237 xid, caps.ccaps, caps.cmask, caps.bcaps, caps.bmask);
241 if (vc_set_cflags(xid, &flags) == -1) {
242 PERROR("vc_set_cflags(%d, 0x%16llx/0x%16llx)\n",
243 xid, flags.flagword, flags.mask);
251 void runas_slice_user(char *username)
253 struct passwd pwdd, *pwd = &pwdd, *result;
255 char *home_env, *logname_env, *mail_env, *shell_env, *user_env;
256 int home_len, logname_len, mail_len, shell_len, user_len;
258 static char *envp[10];
261 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
262 if (pwdBuffer_len == -1) {
263 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
267 pwdBuffer = (char*)malloc(pwdBuffer_len);
268 if (pwdBuffer == NULL) {
269 PERROR("malloc(%d)", pwdBuffer_len);
274 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
275 PERROR("getpwnam_r(%s)", username);
279 if (setgid(pwd->pw_gid) < 0) {
280 PERROR("setgid(%d)", pwd->pw_gid);
284 if (setuid(pwd->pw_uid) < 0) {
285 PERROR("setuid(%d)", pwd->pw_uid);
289 if (chdir(pwd->pw_dir) < 0) {
290 PERROR("chdir(%s)", pwd->pw_dir);
294 home_len = strlen("HOME=") + strlen(pwd->pw_dir) + NULLBYTE_SIZE;
295 logname_len = strlen("LOGNAME=") + strlen(username) + NULLBYTE_SIZE;
296 mail_len = strlen("MAIL=/var/spool/mail/") + strlen(username)
298 shell_len = strlen("SHELL=") + strlen(pwd->pw_shell) + NULLBYTE_SIZE;
299 user_len = strlen("USER=") + strlen(username) + NULLBYTE_SIZE;
301 home_env = (char *)malloc(home_len);
302 logname_env = (char *)malloc(logname_len);
303 mail_env = (char *)malloc(mail_len);
304 shell_env = (char *)malloc(shell_len);
305 user_env = (char *)malloc(user_len);
307 if ((home_env == NULL) ||
308 (logname_env == NULL) ||
309 (mail_env == NULL) ||
310 (shell_env == NULL) ||
311 (user_env == NULL)) {
316 sprintf(home_env, "HOME=%s", pwd->pw_dir);
317 sprintf(logname_env, "LOGNAME=%s", username);
318 sprintf(mail_env, "MAIL=/var/spool/mail/%s", username);
319 sprintf(shell_env, "SHELL=%s", pwd->pw_shell);
320 sprintf(user_env, "USER=%s", username);
322 home_env[home_len - 1] = '\0';
323 logname_env[logname_len - 1] = '\0';
324 mail_env[mail_len - 1] = '\0';
325 shell_env[shell_len - 1] = '\0';
326 user_env[user_len - 1] = '\0';
329 envp[1] = logname_env;
335 if ((putenv(home_env) < 0) ||
336 (putenv(logname_env) < 0) ||
337 (putenv(mail_env) < 0) ||
338 (putenv(shell_env) < 0) ||
339 (putenv(user_env) < 0)) {
340 PERROR("vserver: putenv error ");
345 void slice_enter(char *context)
347 struct passwd pwdd, *pwd = &pwdd, *result;
353 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
354 if (pwdBuffer_len == -1) {
355 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX)");
359 pwdBuffer = (char*)malloc(pwdBuffer_len);
360 if (pwdBuffer == NULL) {
361 PERROR("malloc(%d)", pwdBuffer_len);
366 if ((getpwnam_r(context,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
367 PERROR("getpwnam_r(%s)", context);
371 context = (char*)malloc(strlen(pwd->pw_name)+NULLBYTE_SIZE);
373 PERROR("malloc(%d)");
376 strcpy(context,pwd->pw_name);
378 if (setuidgid_root() < 0) { /* For chroot, new_s_context */
379 fprintf(stderr, "vsh: Could not become root, check that SUID flag is set on binary\n");
385 if (sandbox_chroot(uid) < 0) {
386 fprintf(stderr, "vsh: Could not chroot\n");
390 if (sandbox_processes((xid_t) uid) < 0) {
391 fprintf(stderr, "vsh: Could not change context to %d\n", uid);
396 //--------------------------------------------------------------------
398 #define DEFAULT_SHELL "/bin/sh"
400 /* Exit statuses for programs like 'env' that exec other programs.
401 EXIT_FAILURE might not be 1, so use EXIT_FAIL in such programs. */
404 EXIT_CANNOT_INVOKE = 126,
408 int main(int argc, char **argv)
410 struct passwd pwdd, *pwd = &pwdd, *result;
411 char *context, *username, *shell, *pwdBuffer;
422 if ((pwd = getpwuid(uid)) == NULL) {
423 PERROR("getpwuid(%d)", uid);
427 context = (char*)strdup(pwd->pw_name);
433 /* enter vserver "context" */
434 slice_enter(context);
436 /* Now run as username in this context. Note that for PlanetLab's
437 vserver configuration the context name also happens to be the
438 "default" username within the vserver context.
441 runas_slice_user(username);
443 /* With the uid/gid appropriately set. Let's figure out what the
444 * shell in the vserver's /etc/passwd is for the given username.
447 pwdBuffer_len = sysconf(_SC_GETPW_R_SIZE_MAX);
448 if (pwdBuffer_len == -1) {
449 PERROR("sysconf(_SC_GETPW_R_SIZE_MAX");
452 pwdBuffer = (char*)malloc(pwdBuffer_len);
453 if (pwdBuffer == NULL) {
454 PERROR("malloc(%d)", pwdBuffer_len);
459 if ((getpwnam_r(username,pwd,pwdBuffer,pwdBuffer_len, &result) != 0) || (errno != 0)) {
460 PERROR("getpwnam_r(%s)", username);
464 /* Make sure pw->pw_shell is non-NULL.*/
465 if (pwd->pw_shell == NULL || pwd->pw_shell[0] == '\0') {
466 pwd->pw_shell = (char *) DEFAULT_SHELL;
469 shell = (char *)strdup(pwd->pw_shell);
475 /* Check whether 'su' or 'sshd' invoked us as a login shell or
476 not; did this above when testing argv[0]=='-'.
481 args = (char**)malloc(sizeof(char*)*(argc+2));
483 PERROR("malloc(%d)", sizeof(char*)*(argc+2));
488 for(i=1;i<argc+1;i++) {
493 (void) execvp(shell,argv);
495 int exit_status = (errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE);
499 return 0; /* shutup compiler */