fix for bind mount problem
[util-vserver.git] / src / planetlab.c
1 /*
2  * Marc E. Fiuczynski <mef@cs.princeton.edu>
3  *
4  * Copyright (c) 2004  The Trustees of Princeton University (Trustees).
5  *
6  * Portions of this file are derived from the Paul Brett's vserver.c
7  * modification to bash (vsh).
8  *
9  * It is free software; you can redistribute it and/or modify it under
10  * the terms of the GNU General Public License as published by the
11  * Free Software Foundation; either version 2, or (at your option) any
12  * later version.
13  *
14  * It is distributed in the hope that it will be useful, but WITHOUT
15  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU General Public License along
20  * with  Poptop; see the file COPYING.  If not, write to the Free Software
21  * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27 #include "compat.h"
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <stdlib.h>
33 #include <errno.h>
34 #include <pwd.h>
35 #include <unistd.h>
36 #include <syscall.h>
37 #include <sys/syscall.h>
38 #include <asm/unistd.h>
39 #include <sys/mount.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43
44 #include "linuxcaps.h"
45 #include "vserver.h"
46
47 /* Null byte made explicit */
48 #define NULLBYTE_SIZE                    1
49
50 /* Base for all vserver roots for chroot */
51 #define VSERVER_ROOT_BASE       "/vservers"
52
53
54 /* Change to root:root (before entering new context) */
55 static int setuidgid_root()
56 {
57     if (setgid(0) < 0) {
58         fprintf(stderr, "setgid error\n");
59         return -1;
60     }
61     if (setuid(0) < 0) {
62         fprintf(stderr, "setuid error\n");
63         return -1;
64     }
65     return 0;
66 }
67
68 static void compute_new_root(char *base, char **root, uid_t uid)
69 {
70     int             root_len;
71     struct passwd   *pwd;
72
73     if ((pwd = getpwuid(uid)) == NULL) {
74         perror("vserver: getpwuid error ");
75         exit(1);
76     }
77
78     root_len = 
79         strlen(base) + strlen("/") +
80         strlen(pwd->pw_name)      + NULLBYTE_SIZE;
81     (*root) = (char *)malloc(root_len);
82     if ((*root) == NULL) {
83         perror("vserver: malloc error ");
84         exit(1);
85     }
86     
87     sprintf((*root), "%s/%s", base, pwd->pw_name);
88     (*root)[root_len - 1] = '\0';
89 }
90
91 /* Example: sandbox_root = /vservers/bnc, relpath = /proc/1 */
92 static int sandbox_file_exists(char *sandbox_root, char *relpath)
93 {
94     struct stat stat_buf;
95     char   *file;
96     int    len, exists = 0;
97
98     len = strlen(sandbox_root) + strlen(relpath) + NULLBYTE_SIZE;
99     if ((file = (char *)malloc(len)) == NULL) {
100         perror("vserver: malloc error ");
101         exit(1);
102     }
103     sprintf(file, "%s%s", sandbox_root, relpath);
104     file[len - 1] = '\0';
105     if (stat(file, &stat_buf) == 0) {
106         exists = 1;
107     }
108
109
110     free(file);
111     return exists;
112 }
113
114 static int proc_mounted(char *sandbox_root)
115 {
116     return sandbox_file_exists(sandbox_root, "/proc/1");
117 }
118
119 static int devpts_mounted(char *sandbox_root)
120 {
121     return sandbox_file_exists(sandbox_root, "/dev/pts/0");
122 }
123
124 static void mount_proc(char *sandbox_root,uid_t uid)
125 {
126     char        *source = "/proc";
127     char        *target;
128     int         len;
129
130     len = strlen(sandbox_root) + strlen("/") + strlen("proc") + NULLBYTE_SIZE;
131     if ((target = (char *)malloc(len)) == NULL) {
132         perror("vserver: malloc error ");
133         exit(1);
134     }
135
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);
140
141     free(target);
142 }
143
144 static void mount_devpts(char *sandbox_root)
145 {
146     char        *source = "/dev/pts";
147     char        *target;
148     int         len;
149     
150     len = strlen(sandbox_root) + strlen("/") + strlen("dev/pts") + NULLBYTE_SIZE;
151     if ((target = (char *)malloc(len)) == NULL) {
152         perror("vserver: malloc error ");
153         exit(1);
154     }
155
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);
160
161     free(target);
162 }
163
164 static int sandbox_chroot(uid_t uid)
165 {
166     char *sandbox_root = NULL;
167
168     compute_new_root(VSERVER_ROOT_BASE,&sandbox_root, uid);
169     mount_proc(sandbox_root,uid);
170     mount_devpts(sandbox_root);
171     if (chroot(sandbox_root) < 0) {
172         fprintf(stderr,"vserver: chroot error (%s): ",sandbox_root);
173         perror("");
174         exit(1);
175     }
176     if (chdir("/") < 0) {
177         perror("vserver: chdir error ");
178         exit(1);
179     }
180     return 0;
181 }
182
183 static int sandbox_processes(uid_t uid)
184 {
185         int      context;
186         int      flags;
187         unsigned remove_cap;
188
189         /* Unique context */
190         context = uid;
191
192         flags = 0;
193         flags |= 1; /* VX_INFO_LOCK -- cannot request a new vx_id */
194         flags |= 4; /* VX_INFO_NPROC -- limit number of procs in a context */
195
196         remove_cap = /* NOTE: keep in sync with chcontext.c */
197           (1<<CAP_LINUX_IMMUTABLE)|
198           (1<<CAP_NET_BROADCAST)|
199           (1<<CAP_NET_ADMIN)|
200           (1<<CAP_NET_RAW)|
201           (1<<CAP_IPC_LOCK)|
202           (1<<CAP_IPC_OWNER)|
203           (1<<CAP_SYS_MODULE)|
204           (1<<CAP_SYS_RAWIO)|
205           (1<<CAP_SYS_PACCT)|
206           (1<<CAP_SYS_ADMIN)|
207           (1<<CAP_SYS_BOOT)|
208           (1<<CAP_SYS_NICE)|
209           (1<<CAP_SYS_RESOURCE)|
210           (1<<CAP_SYS_TIME)|
211           (1<<CAP_MKNOD)|
212 #ifdef CAP_QUOTACTL
213           (1<<CAP_QUOTACTL)|
214 #endif
215 #ifdef CAP_CONTEXT
216           (1<<CAP_CONTEXT)|
217 #endif
218           0
219           ;
220
221     if (vc_new_s_context(context,remove_cap,flags) < 0) {
222         perror("vserver: new_s_context error ");
223         exit(1);
224     }
225     return 0;
226 }
227
228
229 void runas_slice_user(char *username)
230 {
231     struct passwd *pwd;
232     char          *home_env, *logname_env, *mail_env, *shell_env, *user_env;
233     int           home_len, logname_len, mail_len, shell_len, user_len;
234     static char   *envp[10];
235
236     if ((pwd = getpwnam(username)) == NULL) {
237         perror("vserver: getpwnam error ");
238         exit(1);
239     }
240
241     if (setgid(pwd->pw_gid) < 0) {
242         perror("vserver: setgid error ");
243         exit(1);
244     }
245
246     if (setuid(pwd->pw_uid) < 0) {
247         perror("vserver: setuid error ");
248         exit(1);
249     }
250
251     if (chdir(pwd->pw_dir) < 0) {
252         perror("vserver: chdir error ");
253         exit(1);
254     }
255
256     home_len    = strlen("HOME=") + strlen(pwd->pw_dir) + NULLBYTE_SIZE;
257     logname_len = strlen("LOGNAME=") + strlen(username) + NULLBYTE_SIZE;
258     mail_len    = strlen("MAIL=/var/spool/mail/") + strlen(username) 
259         + NULLBYTE_SIZE;
260     shell_len   = strlen("SHELL=") + strlen(pwd->pw_shell) + NULLBYTE_SIZE;
261     user_len    = strlen("USER=") + strlen(username) + NULLBYTE_SIZE;
262
263     home_env    = (char *)malloc(home_len);
264     logname_env = (char *)malloc(logname_len);
265     mail_env    = (char *)malloc(mail_len);
266     shell_env   = (char *)malloc(shell_len);
267     user_env    = (char *)malloc(user_len);
268
269     if ((home_env    == NULL)  || 
270         (logname_env == NULL)  ||
271         (mail_env    == NULL)  ||
272         (shell_env   == NULL)  ||
273         (user_env    == NULL)) {
274         perror("vserver: malloc error ");
275         exit(1);
276     }
277
278     sprintf(home_env, "HOME=%s", pwd->pw_dir);
279     sprintf(logname_env, "LOGNAME=%s", username);
280     sprintf(mail_env, "MAIL=/var/spool/mail/%s", username);
281     sprintf(shell_env, "SHELL=%s", pwd->pw_shell);
282     sprintf(user_env, "USER=%s", username);
283     
284     home_env[home_len - 1]       = '\0';
285     logname_env[logname_len - 1] = '\0';
286     mail_env[mail_len - 1]       = '\0';
287     shell_env[shell_len - 1]     = '\0';
288     user_env[user_len - 1]       = '\0';
289
290     envp[0] = home_env;
291     envp[1] = logname_env;
292     envp[2] = mail_env;
293     envp[3] = shell_env;
294     envp[4] = user_env;
295     envp[5] = 0;
296
297     if ((putenv(home_env)    < 0) ||
298         (putenv(logname_env) < 0) ||
299         (putenv(mail_env)    < 0) ||
300         (putenv(shell_env)   < 0) ||
301         (putenv(user_env)    < 0)) {
302         perror("vserver: putenv error ");
303         exit(1);
304     }
305 }
306
307
308
309 void slice_enter(char *context)
310 {
311     struct passwd   *pwd;
312     uid_t uid;
313
314     if ((pwd = getpwnam(context)) == NULL) {
315         fprintf(stderr,"vserver: getpwname(%s) failed",context);
316         exit(2);
317     }
318
319     context = (char*)malloc(strlen(pwd->pw_name)+NULLBYTE_SIZE);
320     if (!context) {
321         perror("vserver: malloc failed");
322         exit(2);
323     }
324     strcpy(context,pwd->pw_name);
325
326     if (setuidgid_root() < 0) { /* For chroot, new_s_context */
327         fprintf(stderr,"vserver: Could not setuid/setguid to root:root\n");
328         exit(2);
329     }
330
331     uid = pwd->pw_uid;
332     if (sandbox_chroot(uid) < 0) {
333         fprintf(stderr, "vserver: Could not chroot to vserver root\n");
334         exit(2);
335     }
336
337     if (sandbox_processes(uid) < 0) {
338         fprintf(stderr, "vserver: Could not sandbox processes in vserver\n");
339         exit(2);
340     }
341 }