fixed '--uid' option (it's required_argument but not no_argument)
[util-vserver.git] / util-vserver / src / vcontext.c
1 // $Id$    --*- c -*--
2
3 // Copyright (C) 2004 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 //  
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; version 2 of the License.
8 //  
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //  
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17
18
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22
23 #include "util.h"
24 #include "lib/internal.h"
25 #include "lib_internal/jail.h"
26
27 #include "linuxcaps.h"
28
29 #include <vserver.h>
30 #include <getopt.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <sys/socket.h>
34 #include <sys/un.h>
35 #include <assert.h>
36
37 #define ENSC_WRAPPERS_PREFIX    "vcontext: "
38 #define ENSC_WRAPPERS_UNISTD    1
39 #define ENSC_WRAPPERS_VSERVER   1
40 #define ENSC_WRAPPERS_FCNTL     1
41 #define ENSC_WRAPPERS_SOCKET    1
42 #define ENSC_WRAPPERS_IOSOCK    1
43 #include <wrappers.h>
44
45 #define CMD_HELP                0x1000
46 #define CMD_VERSION             0x1001
47 #define CMD_XID                 0x4000
48 #define CMD_CREATE              0x4001
49 #define CMD_MIGRATE             0x4003
50 #define CMD_FAKEINIT            0x4002
51 #define CMD_DISCONNECT          0x4004
52 #define CMD_UID                 0x4005
53 #define CMD_CHROOT              0x4006
54 #define CMD_SILENT              0x4007
55 #define CMD_SYNCSOCK            0x4008
56 #define CMD_SYNCMSG             0x4009
57 #define CMD_MIGRATESELF         0x400a
58 #define CMD_ENDSETUP            0x400b
59
60 #if 0
61 #define CMD_CCAP                0x2002
62 #define CMD_BCAP                0x2005
63 #endif
64
65 struct option const
66 CMDLINE_OPTIONS[] = {
67   { "help",       no_argument,       0, CMD_HELP },
68   { "version",    no_argument,       0, CMD_VERSION },
69   { "ctx",        required_argument, 0, CMD_XID },
70   { "xid",        required_argument, 0, CMD_XID },
71   { "create",     no_argument,       0, CMD_CREATE },
72   { "migrate",    no_argument,       0, CMD_MIGRATE },
73   { "migrate-self", no_argument,        0, CMD_MIGRATESELF },
74   { "fakeinit",     no_argument,        0, CMD_FAKEINIT },
75   { "endsetup",     no_argument,        0, CMD_ENDSETUP },
76   { "disconnect",   no_argument,        0, CMD_DISCONNECT },
77   { "silent",       no_argument,        0, CMD_SILENT },
78   { "uid",          required_argument,  0, CMD_UID },
79   { "chroot",       no_argument,        0, CMD_CHROOT },
80   { "syncsock",     required_argument,  0, CMD_SYNCSOCK },
81   { "syncmsg",      required_argument,  0, CMD_SYNCMSG },
82 //  { "ccap",         required_argument,  0, CMD_CCAP },
83 //  { "bcap",         required_argument,  0, CMD_BCAP },
84   { 0,0,0,0 },
85 };
86
87 struct Arguments {
88     bool                do_create;
89     bool                do_migrate;
90     bool                do_migrateself;
91     bool                do_disconnect;
92     bool                do_endsetup;
93     bool                is_fakeinit;
94     int                 verbosity;
95     bool                do_chroot;
96     uid_t               uid;
97     xid_t               xid;
98     char const *        sync_sock;
99     char const *        sync_msg;
100     //struct vc_ctx_caps  caps;
101 };
102
103 int             wrapper_exit_code = 255;
104
105 static void
106 showHelp(int fd, char const *cmd, int res)
107 {
108   WRITE_MSG(fd, "Usage:\n    ");
109   WRITE_STR(fd, cmd);
110   WRITE_MSG(fd,
111             " --create [--xid <xid>] <opts>* [--] <program> <args>*\n    ");
112   WRITE_STR(fd, cmd);
113   WRITE_MSG(fd,
114             " [(--migrate --xid <xid>)|--migrate-self]  <opts>* [--] <program> <args>*\n"
115             "\n"
116             "<opts> can be:\n"
117             "    --chroot        ...  chroot into current directory\n"
118             "    --uid <uid>     ...  change uid\n"
119             "    --fakeinit      ...  set current process as general process reaper\n"
120             "                         for ctx (possible for --migrate only)\n"
121             "    --endsetup      ...  clear the setup flag; usefully for migrate only\n"
122             "    --disconnect    ...  start program in background\n"
123             "    --silent        ...  be silent\n"
124             "    --syncsock <file-name>\n"
125             "                    ...  before executing the program, send a message\n"
126             "                         to the socket and wait until it closes.\n"
127             "                         <file-name> must be a SOCK_STREAM unix socket\n"
128             "    --syncmsg <message>\n"
129             "                    ...  use <message> as synchronization message; by\n"
130             "                         default, 'ok' will be used\n"
131             "\n"
132             "'vcontext --create' exits with code 254 iff the context exists already.\n"
133             "\n"
134             "Please report bugs to " PACKAGE_BUGREPORT "\n");
135
136   exit(res);
137 }
138
139 static void
140 showVersion()
141 {
142   WRITE_MSG(1,
143             "vcontext " VERSION " -- manages the creation of security contexts\n"
144             "This program is part of " PACKAGE_STRING "\n\n"
145             "Copyright (C) 2004 Enrico Scholz\n"
146             VERSION_COPYRIGHT_DISCLAIMER);
147   exit(0);
148 }
149
150 static inline ALWAYSINLINE int
151 initSync(int p[2], bool do_disconnect)
152 {
153   if (!do_disconnect) return 0;
154
155   Epipe(p);
156   fcntl(p[1], F_SETFD, FD_CLOEXEC);
157   return Efork();
158 }
159
160 static inline ALWAYSINLINE void
161 doSyncStage1(int p[2], bool do_disconnect)
162 {
163   int   fd;
164
165   if (!do_disconnect) return;
166   
167   fd = Eopen("/dev/null", O_RDONLY, 0);
168   Esetsid();
169   Edup2(fd, 0);
170   Eclose(p[0]);
171   if (fd!=0) Eclose(fd);
172   Ewrite(p[1], ".", 1);
173 }
174
175 static inline ALWAYSINLINE void
176 doSyncStage2(int p[2], bool do_disconnect)
177 {
178   if (!do_disconnect) return;
179
180   Ewrite(p[1], "X", 1);
181 }
182
183 static void
184 waitOnSync(pid_t pid, int p[2])
185 {
186   int           c;
187   size_t        l;
188
189   Eclose(p[1]);
190   l = Eread(p[0], &c, 1);
191   if (l!=1) exitLikeProcess(pid);
192   l = Eread(p[0], &c, 1);
193   if (l!=0) exitLikeProcess(pid);
194 }
195
196 static inline ALWAYSINLINE void
197 tellContext(xid_t ctx, bool do_it)
198 {
199   char          buf[sizeof(xid_t)*3+2];
200   size_t        l;
201
202   if (!do_it) return;
203
204   l = utilvserver_fmt_long(buf,ctx);
205
206   WRITE_MSG(2, "New security context is ");
207   write(2, buf, l);
208   WRITE_MSG(2, "\n");
209 }
210
211 static int
212 connectExternalSync(char const *filename)
213 {
214   int                   fd;
215   struct sockaddr_un    addr;
216   
217   if (filename==0) return -1;
218
219   ENSC_INIT_UNIX_SOCK(addr, filename);
220
221   fd = Esocket(PF_UNIX, SOCK_STREAM, 0);
222   Econnect(fd, &addr, sizeof(addr));
223
224   return fd;
225 }
226
227 static void
228 setFlags(struct Arguments const *args, xid_t xid)
229 {
230   struct vc_ctx_flags   flags = { 0,0 };
231
232   if (args->is_fakeinit)
233     flags.mask |= VC_VXF_STATE_INIT;
234
235   if (args->do_endsetup)
236     flags.mask |= VC_VXF_STATE_SETUP;
237
238   if (flags.mask!=0) 
239     Evc_set_flags(xid, &flags);
240 }
241
242 static void
243 doExternalSync(int fd, char const *msg)
244 {
245   char          c;
246   
247   if (fd==-1) return;
248
249   if (msg) EsendAll(fd, msg, strlen(msg));
250   Eshutdown(fd, SHUT_WR);
251
252   if (TEMP_FAILURE_RETRY(recv(fd, &c, 1, MSG_NOSIGNAL))!=0) {
253     WRITE_MSG(2, "vcontext: unexpected external synchronization event\n");
254     exit(255);
255   }
256
257   Eclose(fd);
258 }
259
260 static void UNUSED
261 dropPrivs(xid_t xid)
262 {
263   extern int capget (struct __user_cap_header_struct *,
264                      struct __user_cap_data_struct *);
265   extern int capset (struct __user_cap_header_struct *,
266                      struct __user_cap_data_struct *);
267   struct vc_ctx_caps                    caps;
268   struct __user_cap_header_struct       header;
269   struct __user_cap_data_struct         user;
270
271   header.version = _LINUX_CAPABILITY_VERSION;
272   header.pid     = 0;
273   
274   Evc_get_ccaps(xid, &caps);
275   if (capget(&header, &user)==-1)
276     perror(ENSC_WRAPPERS_PREFIX "capget()");
277   else if ( (user.permitted   &= caps.ccaps),
278             (user.effective   &= user.permitted),
279             (user.inheritable &= user.permitted),
280             capset(&header, &user)==-1 )
281     perror(ENSC_WRAPPERS_PREFIX "capset()");
282   else
283     return;
284
285   exit(wrapper_exit_code);
286 }
287
288 #if 0
289 static void
290 parseBCaps(char const *str, struct vc_ctx_caps *caps)
291 {
292   struct vc_err_listparser      err;
293   int                           rc;
294
295   rc = vc_list2bcap(str,0, &err, caps);
296   
297   if (rc==-1) {
298     WRITE_MSG(2, "Unknown bcap '");
299     write(2, err.ptr, err.len);
300     WRITE_MSG(2, "'\n");
301     exit(wrapper_exit_code);
302   }
303 }
304
305 static void
306 parseCCaps(char const *str, struct vc_ctx_caps *caps)
307 {
308   struct vc_err_listparser      err;
309   int                           rc;
310
311   rc = vc_list2ccap(str,0, &err, caps);
312   
313   if (rc==-1) {
314     WRITE_MSG(2, "Unknown ccap '");
315     write(2, err.ptr, err.len);
316     WRITE_MSG(2, "'\n");
317     exit(wrapper_exit_code);
318   }
319 }
320
321 static void
322 parseSecure(struct vc_ctx_flags UNUSED * flags,
323             struct vc_ctx_caps  UNUSED * caps)
324 {
325   caps->ccaps = ~0;
326   caps->cmask = ~0;
327   caps->bcaps = ~vc_get_securecaps();
328 }
329 #endif
330
331 static inline ALWAYSINLINE int
332 doit(struct Arguments const *args, char *argv[])
333 {
334   int                   p[2];
335   pid_t                 pid = initSync(p, args->do_disconnect);
336   
337   if (pid==0) {
338     xid_t                       xid;
339     int                         ext_sync_fd = connectExternalSync(args->sync_sock);
340     
341     if (args->do_create) {
342       xid = vc_create_context(args->xid);
343       if (xid==VC_NOCTX) {
344         if (errno==EEXIST) return 254;
345         else {
346           perror(ENSC_WRAPPERS_PREFIX "vc_create_context()");
347           return wrapper_exit_code;
348         }
349       }
350       tellContext(xid, args->verbosity>=1);
351     }
352     else
353       xid = args->xid;
354
355     if (args->do_chroot)
356       Echroot(".");
357
358     setFlags(args, xid);
359
360 #if 0
361     if (args->do_migrate ||  args->do_migrateself)
362       dropPrivs(xid);
363 #endif
364
365     if (args->do_migrate && !args->do_migrateself)
366       Evc_migrate_context(xid);
367
368     if (args->uid!=(uid_t)(-1) && getuid()!=args->uid) {
369       Esetuid(args->uid);
370       if (getuid()!=args->uid) {
371         WRITE_MSG(2, "vcontext: Something went wrong while changing the UID\n");
372         exit(255);
373       }
374     }
375     
376     doExternalSync(ext_sync_fd, args->sync_msg);
377     doSyncStage1(p, args->do_disconnect);
378     execvp (argv[optind],argv+optind);
379     doSyncStage2(p, args->do_disconnect);
380
381     PERROR_Q("chcontext: execvp", argv[optind]);
382     exit(255);
383   }
384
385   assert(args->do_disconnect);
386   
387   if (!jailIntoTempDir()) {
388     perror(ENSC_WRAPPERS_PREFIX "jailIntoTempDir()");
389     exit(255);
390   }
391     
392   waitOnSync(pid, p);
393   return EXIT_SUCCESS;
394 }
395
396 int main (int argc, char *argv[])
397 {
398   struct Arguments              args = {
399     .do_create      = false,
400     .do_migrate     = false,
401     .do_migrateself = false,
402     .do_disconnect  = false,
403     .do_endsetup    = false,
404     .is_fakeinit    = false,
405     .verbosity      = 1,
406     .uid            = -1,
407     .xid            = VC_DYNAMIC_XID,
408     .sync_msg       = "ok",
409   };
410   
411   while (1) {
412     int         c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
413     if (c==-1) break;
414     
415     switch (c) {
416       case CMD_HELP             :  showHelp(1, argv[0], 0);
417       case CMD_VERSION          :  showVersion();
418       case CMD_CREATE           :  args.do_create     = true; break;
419       case CMD_MIGRATE          :  args.do_migrate    = true; break;
420       case CMD_DISCONNECT       :  args.do_disconnect = true; break;
421       case CMD_ENDSETUP         :  args.do_endsetup   = true; break;
422       case CMD_FAKEINIT         :  args.is_fakeinit   = true; break;
423       case CMD_CHROOT           :  args.do_chroot     = true; break;
424       case CMD_SILENT           :  --args.verbosity;          break;
425       case CMD_XID              :  args.xid           = atol(optarg); break;
426       case CMD_UID              :  args.uid           = atol(optarg); break;
427       case CMD_SYNCSOCK         :  args.sync_sock     = optarg;    break;
428       case CMD_SYNCMSG          :  args.sync_msg      = optarg;    break;
429 #if 0   
430       case CMD_CCAP             :  parseCCaps(optarg, &args.caps); break;
431       case CMD_BCAP             :  parseBCaps(optarg, &args.caps); break;
432       case CMD_SECURE           :  parseSecure(&args.caps);        break;
433 #endif
434       case CMD_MIGRATESELF      :
435         args.do_migrate     = true;
436         args.do_migrateself = true;
437         break;
438
439       default           :
440         WRITE_MSG(2, "Try '");
441         WRITE_STR(2, argv[0]);
442         WRITE_MSG(2, " --help\" for more information.\n");
443         return 255;
444         break;
445     }
446   }
447
448   if (args.do_migrateself)
449     args.xid = Evc_get_task_xid(0);
450   
451   if (!args.do_create && !args.do_migrate)
452     WRITE_MSG(2, "Neither '--create' nor '--migrate specified; try '--help' for more information\n");
453   else if (args.do_create  &&  args.do_migrate)
454     WRITE_MSG(2, "Can not specify '--create' and '--migrate' at the same time; try '--help' for more information\n");
455   else if (!args.do_migrate && args.is_fakeinit)
456     WRITE_MSG(2, "'--fakeinit' is possible in combination with '--migrate' only\n");
457   else if (!args.do_create && args.xid==VC_DYNAMIC_XID)
458     WRITE_MSG(2, "vcontext: Can not migrate to an unknown context\n");
459   else if (optind>=argc)
460     WRITE_MSG(2, "No command given; use '--help' for more information.\n");
461   else
462     return doit(&args, argv);
463
464   return 255;
465 }