- PL2479 and PL2299 fix: no access to restricted ports if --secure is
[util-vserver.git] / src / chcontext.c
1 // $Id: chcontext.c,v 1.1.4.3 2004/01/07 16:24:01 ensc Exp $
2
3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 // based on chcontext.cc by Jacques Gelinas
5 //  
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2, or (at your option)
9 // any later version.
10 //  
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 // GNU General Public License for more details.
15 //  
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 /*
21         chcontext is a wrapper to user the new_s_context system call. It
22         does little more than mapping command line option to the system call
23         arguments.
24 */
25 #ifdef HAVE_CONFIG_H
26 #  include <config.h>
27 #endif
28 #include "compat.h"
29
30 #include <stdio.h>
31 #include <unistd.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <errno.h>
35
36 #include "linuxcaps.h"
37 #include "vserver.h"
38
39 #ifndef CAP_QUOTACTL
40 #  define CAP_QUOTACTL  29
41 #endif
42
43 static void usage()
44 {
45         fprintf (stderr,"chcontext version %s\n",VERSION);
46         fprintf (stderr
47                 ,"chcontext [ options ] command arguments ...\n"
48                  "\n"
49                  "chcontext allocate a new security context and executes\n"
50                  "a command in that context.\n"
51                  "By default, a new/unused context is allocated\n"
52                  "\n"
53
54                  "--cap CAP_NAME\n"
55                  "\tAdd a capability from the command. This option may be\n"
56                  "\trepeated several time.\n"
57                  "\tSee /usr/include/linux/capability.h\n"
58                  "\tIn general, this option is used with the --secure option\n"
59                  "\t--secure removes most critical capabilities and --cap\n"
60                  "\tadds specific ones.\n"
61                  "\n"
62
63                  "--cap !CAP_NAME\n"
64                  "\tRemove a capability from the command. This option may be\n"
65                  "\trepeated several time.\n"
66                  "\tSee /usr/include/linux/capability.h\n"
67                  "\n"
68                  "--ctx num\n"
69                  "\tSelect the context. On root in context 0 is allowed to\n"
70                  "\tselect a specific context.\n"
71                  "\tContext number 1 is special. It can see all processes\n"
72                  "\tin any contexts, but can't kill them though.\n"
73                  "\tOption --ctx may be repeated several times to specify up to 16 contexts.\n"
74
75                  "--disconnect\n"
76                  "\tStart the command in background and make the process\n"
77                  "\ta child of process 1.\n"
78
79                  "--domainname new_domainname\n"
80                  "\tSet the domainname (NIS) in the new security context.\n"
81                  "\tUse \"none\" to unset the domain name.\n"
82
83                  "--flag\n"
84                  "\tSet one flag in the new or current security context. The following\n"
85                  "\tflags are supported. The option may be used several time.\n"
86                  "\n"
87                  "\tfakeinit: The new process will believe it is process number 1.\n"
88                  "            Useful to run a real /sbin/init in a vserver.\n"
89                  "\tlock: The new process is trapped and can't use chcontext anymore.\n"
90                  "\tsched: The new process and its children will share a common \n"
91                  "         execution priority.\n"
92                  "\tnproc: Limit the number of process in the vserver according to\n"
93                  "         ulimit setting. Normally, ulimit is a per user thing.\n"
94                  "         With this flag, it becomes a per vserver thing.\n"
95                  "\tprivate: No one can join this security context once created.\n"
96                  "\tulimit: Apply the current ulimit to the whole context\n"
97
98                  "--hostname new_hostname\n"
99                  "\tSet the hostname in the new security context\n"
100                  "\tThis is need because if you create a less privileged\n"
101                  "\tsecurity context, it may be unable to change its hostname\n"
102
103                  "--secure\n"
104                  "\tRemove all the capabilities to make a virtual server trustable\n"
105
106                  "--silent\n"
107                  "\tDo not print the allocated context number.\n"
108                  "\n"
109                  "Information about context is found in /proc/self/status\n");
110 }
111
112
113 int main (int argc, char *argv[])
114 {
115         int ret = -1;
116         int i;
117         int nbctx = 0;
118         int ctxs[16];
119         int disconnect = 0;
120         int silent = 0;
121         int flags = 0;
122         unsigned remove_cap = 0;
123         unsigned add_cap = 0;
124         unsigned long secure = (1<<CAP_LINUX_IMMUTABLE)
125                 |(1<<CAP_NET_BIND_SERVICE)
126                 |(1<<CAP_NET_BROADCAST)
127                 |(1<<CAP_NET_ADMIN)
128                 |(1<<CAP_NET_RAW)
129                 |(1<<CAP_IPC_LOCK)
130                 |(1<<CAP_IPC_OWNER)
131                 |(1<<CAP_SYS_MODULE)
132                 |(1<<CAP_SYS_RAWIO)
133                 |(1<<CAP_SYS_PACCT)
134                 |(1<<CAP_SYS_ADMIN)
135                 |(1<<CAP_SYS_BOOT)
136                 |(1<<CAP_SYS_NICE)
137                 |(1<<CAP_SYS_RESOURCE)
138                 |(1<<CAP_SYS_TIME)
139                 |(1<<CAP_MKNOD)
140                 |(1<<CAP_QUOTACTL);
141         const char *hostname=NULL, *domainname=NULL;
142
143         for (i=1; i<argc; i++){
144                 const char *arg = argv[i];
145                 const char *opt = argv[i+1];
146                 if (strcmp(arg,"--ctx")==0){
147                         if (nbctx >= 16){
148                                 fprintf (stderr,"Too many context, max 16, ignored.\n");
149                         }else{
150                                 ctxs[nbctx++] = atoi(opt);
151                         }
152                         i++;
153                 }else if (strcmp(arg,"--disconnect")==0){
154                         disconnect = 1;
155                 }else if (strcmp(arg,"--silent")==0){
156                         silent = 1;
157                 }else if (strcmp(arg,"--flag")==0){
158                         if (strcmp(opt,"lock")==0){
159                                 flags |= 1;
160                         }else if (strcmp(opt,"sched")==0){
161                                 flags |= 2;
162                         }else if (strcmp(opt,"nproc")==0){
163                                 flags |= 4;
164                         }else if (strcmp(opt,"private")==0){
165                                 flags |= 8;
166                         }else if (strcmp(opt,"fakeinit")==0){
167                                 flags |= 16;
168                         }else if (strcmp(opt,"hideinfo")==0){
169                                 flags |= 32;
170                         }else if (strcmp(opt,"ulimit")==0){
171                                 flags |= 64;
172                         }else{
173                                 fprintf (stderr,"Unknown flag %s\n",opt);
174                         }
175                         i++;
176                 }else if (strcmp(arg,"--cap")==0){
177                         static struct {
178                                 const char *option;
179                                 int bit;
180                         }tbcap[]={
181                                 // The following capabilities are normally available
182                                 // to vservers administrator, but are place for
183                                 // completeness
184                                 {"CAP_CHOWN",CAP_CHOWN},
185                                 {"CAP_DAC_OVERRIDE",CAP_DAC_OVERRIDE},
186                                 {"CAP_DAC_READ_SEARCH",CAP_DAC_READ_SEARCH},
187                                 {"CAP_FOWNER",CAP_FOWNER},
188                                 {"CAP_FSETID",CAP_FSETID},
189                                 {"CAP_KILL",CAP_KILL},
190                                 {"CAP_SETGID",CAP_SETGID},
191                                 {"CAP_SETUID",CAP_SETUID},
192                                 {"CAP_SETPCAP",CAP_SETPCAP},
193                                 {"CAP_SYS_TTY_CONFIG",CAP_SYS_TTY_CONFIG},
194                                 {"CAP_LEASE",CAP_LEASE},
195                                 {"CAP_SYS_CHROOT",CAP_SYS_CHROOT},
196
197                                 // Those capabilities are not normally available
198                                 // to vservers because they are not needed and
199                                 // may represent a security risk
200                                 {"CAP_LINUX_IMMUTABLE",CAP_LINUX_IMMUTABLE},
201                                 {"CAP_NET_BIND_SERVICE",CAP_NET_BIND_SERVICE},
202                                 {"CAP_NET_BROADCAST",CAP_NET_BROADCAST},
203                                 {"CAP_NET_ADMIN",       CAP_NET_ADMIN},
204                                 {"CAP_NET_RAW", CAP_NET_RAW},
205                                 {"CAP_IPC_LOCK",        CAP_IPC_LOCK},
206                                 {"CAP_IPC_OWNER",       CAP_IPC_OWNER},
207                                 {"CAP_SYS_MODULE",CAP_SYS_MODULE},
208                                 {"CAP_SYS_RAWIO",       CAP_SYS_RAWIO},
209                                 {"CAP_SYS_PACCT",       CAP_SYS_PACCT},
210                                 {"CAP_SYS_ADMIN",       CAP_SYS_ADMIN},
211                                 {"CAP_SYS_BOOT",        CAP_SYS_BOOT},
212                                 {"CAP_SYS_NICE",        CAP_SYS_NICE},
213                                 {"CAP_SYS_RESOURCE",CAP_SYS_RESOURCE},
214                                 {"CAP_SYS_TIME",        CAP_SYS_TIME},
215                                 {"CAP_MKNOD",           CAP_MKNOD},
216                                 {"CAP_QUOTACTL",        CAP_QUOTACTL},
217                                 {NULL,0}
218                         };
219                         int j;
220                         unsigned *cap = &add_cap;
221                         if (opt[0] == '!'){
222                                 cap = &remove_cap;
223                                 opt++;
224                         }
225                         for (j=0; tbcap[j].option != NULL; j++){
226                                 if (strcasecmp(tbcap[j].option,opt)==0){
227                                         *cap |= (1<<tbcap[j].bit);
228                                         break;
229                                 }
230                         }
231                         if (tbcap[j].option == NULL){
232                                 fprintf (stderr,"Unknown capability %s\n",opt);
233                         }
234                         i++;
235                 }else if (strcmp(arg,"--secure")==0){
236                         remove_cap |= secure;
237                 }else if (strcmp(arg,"--hostname")==0){
238                         hostname = opt;
239                         i++;
240                 }else if (strcmp(arg,"--domainname")==0){
241                         if (opt != NULL && strcmp(opt,"none")==0) opt = "";
242                         domainname = opt;
243                         i++;
244                 }else{
245                         break;
246                 }
247         }
248         if (i == argc){
249                 usage();
250         }else if (argv[i][0] == '-'){
251                 usage();
252         }else{
253                 /*
254                         We must fork early because fakeinit set the current
255                         process as the special init process
256                 */
257                 if (disconnect == 0 || fork()==0){
258                         int newctx;
259                         int xflags = flags & 16;
260
261                         if (nbctx == 0) ctxs[nbctx++] = -1;
262                         newctx = vc_new_s_context(ctxs[0],0,flags&~16);
263                         if (newctx != -1){
264                                 if (hostname != NULL){
265                                         if (sethostname (hostname,strlen(hostname))==-1){
266                                                 fprintf (stderr,"Can't set the host name (%s)\n"
267                                                         ,strerror(errno));
268                                         }else if (!silent){
269                                                 printf ("Host name is now %s\n",hostname);
270                                         }
271                                 }
272                                 if (domainname != NULL){
273                                         setdomainname (domainname,strlen(domainname));
274                                         if (!silent){
275                                                 printf ("Domain name is now %s\n",domainname);
276                                         }
277                                 }
278                                 remove_cap &= (~add_cap);
279                                 if (remove_cap!=0 || xflags!=0)
280                                         vc_new_s_context (-2,remove_cap,xflags);
281                                 if (!silent){
282                                         printf ("New security context is %d\n"
283                                                 ,ctxs[0] == -1 ? newctx : ctxs[0]);
284                                 }
285                                 execvp (argv[i],argv+i);
286                                 fprintf (stderr,"Can't exec %s (%s)\n",argv[i]
287                                         ,strerror(errno));
288                         }else{
289                                 perror ("Can't set the new security context\n");
290                         }
291                         if (disconnect != 0) _exit(0);
292                 }
293         }
294         return ret;
295 }
296