3cae6ea9a1e9f37b069df74eac4336d236239847
[util-vserver.git] / 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 #include "lib_internal/sys_personality.h"
27
28 #include <vserver.h>
29 #include <getopt.h>
30 #include <fcntl.h>
31 #include <errno.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <assert.h>
35 #include <signal.h>
36
37 #include <linux/personality.h>
38
39 #define ENSC_WRAPPERS_PREFIX    "vcontext: "
40 #define ENSC_WRAPPERS_UNISTD    1
41 #define ENSC_WRAPPERS_VSERVER   1
42 #define ENSC_WRAPPERS_FCNTL     1
43 #define ENSC_WRAPPERS_SOCKET    1
44 #define ENSC_WRAPPERS_IOSOCK    1
45 #include <wrappers.h>
46
47 #define CMD_HELP                0x1000
48 #define CMD_VERSION             0x1001
49 #define CMD_XID                 0x4000
50 #define CMD_CREATE              0x4001
51 #define CMD_MIGRATE             0x4003
52 #define CMD_INITPID             0x4002
53 #define CMD_DISCONNECT          0x4004
54 #define CMD_UID                 0x4005
55 #define CMD_CHROOT              0x4006
56 #define CMD_SILENT              0x4007
57 #define CMD_SYNCSOCK            0x4008
58 #define CMD_SYNCMSG             0x4009
59 #define CMD_MIGRATESELF         0x400a
60 #define CMD_ENDSETUP            0x400b
61 #define CMD_SILENTEXIST         0x400c
62 #define CMD_NAMESPACE           0x400d
63 #define CMD_PERSTYPE            0x400e
64 #define CMD_PERSFLAG            0x400f
65 #define CMD_VLOGIN              0x4010
66
67
68 struct option const
69 CMDLINE_OPTIONS[] = {
70   { "help",       no_argument,       0, CMD_HELP },
71   { "version",    no_argument,       0, CMD_VERSION },
72   { "ctx",        required_argument, 0, CMD_XID },
73   { "xid",        required_argument, 0, CMD_XID },
74   { "create",     no_argument,       0, CMD_CREATE },
75   { "migrate",    no_argument,       0, CMD_MIGRATE },
76   { "migrate-self", no_argument,        0, CMD_MIGRATESELF },
77   { "initpid",      no_argument,        0, CMD_INITPID },
78   { "endsetup",     no_argument,        0, CMD_ENDSETUP },
79   { "disconnect",   no_argument,        0, CMD_DISCONNECT },
80   { "silent",       no_argument,        0, CMD_SILENT },
81   { "silentexist",  no_argument,        0, CMD_SILENTEXIST },
82   { "uid",          required_argument,  0, CMD_UID },
83   { "chroot",       no_argument,        0, CMD_CHROOT },
84   { "namespace",    no_argument,        0, CMD_NAMESPACE },
85   { "syncsock",     required_argument,  0, CMD_SYNCSOCK },
86   { "syncmsg",      required_argument,  0, CMD_SYNCMSG },
87   { "personality-type",  required_argument, 0, CMD_PERSTYPE },
88   { "personality-flags", required_argument, 0, CMD_PERSFLAG },
89   { "vlogin",       no_argument,        0, CMD_VLOGIN },
90 #if 1  
91   { "fakeinit",     no_argument,        0, CMD_INITPID },       // compatibility
92 #endif  
93   { 0,0,0,0 },
94 };
95
96 struct Arguments {
97     bool                do_create;
98     bool                do_migrate;
99     bool                do_migrateself;
100     bool                do_disconnect;
101     bool                do_endsetup;
102     bool                is_initpid;
103     bool                is_silentexist;
104     bool                set_namespace;
105     bool                do_vlogin;
106     uint_least32_t      personality_flags;
107     uint_least32_t      personality_type;
108     int                 verbosity;
109     bool                do_chroot;
110     uid_t               uid;
111     xid_t               xid;
112     char const *        sync_sock;
113     char const *        sync_msg;
114 };
115
116 int             wrapper_exit_code = 255;
117
118 void do_vlogin(int argc, char *argv[], int ind);
119
120 static void
121 showHelp(int fd, char const *cmd, int res)
122 {
123   WRITE_MSG(fd, "Usage:\n    ");
124   WRITE_STR(fd, cmd);
125   WRITE_MSG(fd,
126             " --create [--xid <xid>] <opts>* [--] <program> <args>*\n    ");
127   WRITE_STR(fd, cmd);
128   WRITE_MSG(fd,
129             " [(--migrate --xid <xid>)|--migrate-self]  <opts>* [--] <program> <args>*\n"
130             "\n"
131             "<opts> can be:\n"
132             "    --chroot        ...  chroot into current directory\n"
133             "    --namespace     ...  execute namespace management operations\n"
134             "    --uid <uid>     ...  change uid\n"
135             "    --initpid       ...  set current process as general process reaper\n"
136             "                         for ctx (possible for --migrate only)\n"
137             "    --endsetup      ...  clear the setup flag; usefully for migrate only\n"
138             "    --disconnect    ...  start program in background\n"
139             "    --personality-type <type>\n"
140             "                    ...  execute <program> in the given execution domain\n"
141             "    --personality-flags <flags>+\n"
142             "                    ...  set special flags for the given execution domain\n"
143             "    --silent        ...  be silent\n"
144             "    --silentexist   ...  be silent when context exists already; usefully\n"
145             "                         for '--create' only\n"
146             "    --syncsock <file-name>\n"
147             "                    ...  before executing the program, send a message\n"
148             "                         to the socket and wait until it closes.\n"
149             "                         <file-name> must be a SOCK_STREAM unix socket\n"
150             "    --syncmsg <message>\n"
151             "                    ...  use <message> as synchronization message; by\n"
152             "                         default, 'ok' will be used\n"
153             "    --vlogin        ...  enable terminal proxy\n"
154             "\n"
155             "'vcontext --create' exits with code 254 iff the context exists already.\n"
156             "\n"
157             "Please report bugs to " PACKAGE_BUGREPORT "\n");
158
159   exit(res);
160 }
161
162 static void
163 showVersion()
164 {
165   WRITE_MSG(1,
166             "vcontext " VERSION " -- manages the creation of security contexts\n"
167             "This program is part of " PACKAGE_STRING "\n\n"
168             "Copyright (C) 2004 Enrico Scholz\n"
169             VERSION_COPYRIGHT_DISCLAIMER);
170   exit(0);
171 }
172
173 #include "context-sync.hc"
174
175 static inline ALWAYSINLINE void
176 tellContext(xid_t ctx, bool do_it)
177 {
178   char          buf[sizeof(xid_t)*3+2];
179   size_t        l;
180
181   if (!do_it) return;
182
183   l = utilvserver_fmt_long(buf,ctx);
184
185   WRITE_MSG(1, "New security context is ");
186   Vwrite   (1, buf, l);
187   WRITE_MSG(1, "\n");
188 }
189
190 static int
191 connectExternalSync(char const *filename)
192 {
193   int                   fd;
194   struct sockaddr_un    addr;
195   
196   if (filename==0) return -1;
197
198   ENSC_INIT_UNIX_SOCK(addr, filename);
199
200   fd = Esocket(PF_UNIX, SOCK_STREAM, 0);
201   Econnect(fd, &addr, sizeof(addr));
202
203   return fd;
204 }
205
206 static void
207 setFlags(struct Arguments const *args, xid_t xid)
208 {
209   struct vc_ctx_flags   flags = { 0,0 };
210
211   if (args->is_initpid)
212     flags.mask |=  VC_VXF_STATE_INIT;
213
214   if (args->do_endsetup)
215     flags.mask |=  VC_VXF_STATE_SETUP;
216
217   if (flags.mask!=0) {
218     DPRINTF("set_flags: mask=%08llx, flag=%08llx\n", flags.mask, flags.flagword);
219     Evc_set_cflags(xid, &flags);
220   }
221 }
222
223 static void
224 doExternalSync(int fd, char const *msg)
225 {
226   char          c;
227   
228   if (fd==-1) return;
229
230   if (msg) EsendAll(fd, msg, strlen(msg));
231   Eshutdown(fd, SHUT_WR);
232
233   if (TEMP_FAILURE_RETRY(recv(fd, &c, 1, MSG_NOSIGNAL))!=0) {
234     WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "unexpected external synchronization event\n");
235     exit(wrapper_exit_code);
236   }
237
238   Eclose(fd);
239 }
240
241 static inline ALWAYSINLINE int
242 doit(struct Arguments const *args, int argc, char *argv[])
243 {
244   int                   p[2][2];
245   pid_t                 pid = initSync(p, args->do_disconnect);
246   
247   if (pid==0) {
248     xid_t                       xid;
249     int                         ext_sync_fd = connectExternalSync(args->sync_sock);
250
251     doSyncStage0(p, args->do_disconnect);  
252     
253     if (args->do_create) {
254       xid = vc_ctx_create(args->xid);
255       if (xid==VC_NOCTX) {
256         switch (errno) {
257           case EEXIST   :
258             if (!args->is_silentexist)
259               perror(ENSC_WRAPPERS_PREFIX "vc_create_context()");
260             return 254;
261           default       :
262             perror(ENSC_WRAPPERS_PREFIX "vc_create_context()");
263             return wrapper_exit_code;
264         }
265       }
266       tellContext(xid, args->verbosity>=1);
267     }
268     else
269       xid = args->xid;
270
271     if (args->do_chroot) {
272       Echroot(".");
273       if (args->set_namespace) {
274         if (args->do_migrateself)  Evc_set_namespace();
275         else if (args->do_migrate) Evc_enter_namespace(xid);
276       }
277     }
278
279     setFlags(args, xid);
280
281     if (args->do_migrate && !args->do_migrateself)
282       Evc_ctx_migrate(xid);
283
284     if (args->uid!=(uid_t)(-1) && getuid()!=args->uid) {
285       Esetuid(args->uid);
286       if (getuid()!=args->uid) {
287         WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "Something went wrong while changing the UID\n");
288         exit(wrapper_exit_code);
289       }
290     }
291
292     if (args->personality_type!=VC_BAD_PERSONALITY &&
293         sys_personality(args->personality_type | args->personality_flags)==-1) {
294       perror(ENSC_WRAPPERS_PREFIX "personality()");
295       exit(wrapper_exit_code);
296     }
297
298     doExternalSync(ext_sync_fd, args->sync_msg);
299     doSyncStage1(p, args->do_disconnect);
300     DPRINTF("doit: pid=%u, ppid=%u\n", getpid(), getppid());
301     if (!args->do_vlogin)
302       execvp (argv[optind],argv+optind);
303     else
304       do_vlogin(argc, argv, optind);
305     doSyncStage2(p, args->do_disconnect);
306
307     PERROR_Q(ENSC_WRAPPERS_PREFIX "execvp", argv[optind]);
308     exit(wrapper_exit_code);
309   }
310
311   assert(args->do_disconnect);
312     
313   waitOnSync(pid, p, args->xid!=VC_DYNAMIC_XID && args->do_migrate);
314   return EXIT_SUCCESS;
315 }
316
317 static uint_least32_t
318 parsePersonalityType(char const *str)
319 {
320   uint_least32_t        res = vc_str2personalitytype(str, 0);
321   if (res==VC_BAD_PERSONALITY) {
322     WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "bad personality type\n");
323     exit(wrapper_exit_code);
324   }
325
326   return res;
327 }
328
329 static uint_least32_t
330 parsePersonalityFlags(char const *str)
331 {
332   struct vc_err_listparser      err;
333   uint_least32_t                res;
334
335   if (vc_list2personalityflag(str, 0, &res, &err)==-1) {
336     WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "bad personality flag '");
337     Vwrite(2, err.ptr, err.len);
338     WRITE_MSG(2, "'\n");
339     exit(wrapper_exit_code);
340   }
341
342   return res;
343 }
344
345 int main (int argc, char *argv[])
346 {
347   struct Arguments              args = {
348     .do_create         = false,
349     .do_migrate        = false,
350     .do_migrateself    = false,
351     .do_disconnect     = false,
352     .do_endsetup       = false,
353     .do_vlogin         = false,
354     .is_initpid        = false,
355     .is_silentexist    = false,
356     .set_namespace     = false,
357     .verbosity         = 1,
358     .uid               = -1,
359     .xid               = VC_DYNAMIC_XID,
360     .personality_type  = VC_BAD_PERSONALITY,
361     .personality_flags = 0,
362     .sync_msg          = "ok",
363   };
364   
365   while (1) {
366     int         c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
367     if (c==-1) break;
368     
369     switch (c) {
370       case CMD_HELP             :  showHelp(1, argv[0], 0);
371       case CMD_VERSION          :  showVersion();
372       case CMD_CREATE           :  args.do_create      = true;   break;
373       case CMD_MIGRATE          :  args.do_migrate     = true;   break;
374       case CMD_DISCONNECT       :  args.do_disconnect  = true;   break;
375       case CMD_ENDSETUP         :  args.do_endsetup    = true;   break;
376       case CMD_VLOGIN           :  args.do_vlogin      = true;   break;
377       case CMD_INITPID          :  args.is_initpid     = true;   break;
378       case CMD_CHROOT           :  args.do_chroot      = true;   break;
379       case CMD_NAMESPACE        :  args.set_namespace  = true;   break;
380       case CMD_SILENTEXIST      :  args.is_silentexist = true;   break;
381       case CMD_SYNCSOCK         :  args.sync_sock      = optarg; break;
382       case CMD_SYNCMSG          :  args.sync_msg       = optarg; break;
383       case CMD_UID              :  args.uid = atol(optarg);      break;
384       case CMD_XID              :  args.xid = Evc_xidopt2xid(optarg,true); break;
385       case CMD_SILENT           :  --args.verbosity; break;
386       case CMD_PERSTYPE         :
387         args.personality_type   = parsePersonalityType(optarg);
388         break;
389       case CMD_PERSFLAG         :
390         args.personality_flags |= parsePersonalityFlags(optarg);
391         break;
392       case CMD_MIGRATESELF      :
393         args.do_migrate     = true;
394         args.do_migrateself = true;
395         break;
396
397       default           :
398         WRITE_MSG(2, "Try '");
399         WRITE_STR(2, argv[0]);
400         WRITE_MSG(2, " --help\" for more information.\n");
401         return wrapper_exit_code;
402         break;
403     }
404   }
405
406   signal(SIGCHLD, SIG_DFL);
407   
408   if (args.do_migrateself)
409     args.xid = Evc_get_task_xid(0);
410   
411   if (!args.do_create && !args.do_migrate)
412     WRITE_MSG(2, "Neither '--create' nor '--migrate specified; try '--help' for more information\n");
413   else if (args.do_create  &&  args.do_migrate)
414     WRITE_MSG(2, "Can not specify '--create' and '--migrate' at the same time; try '--help' for more information\n");
415   else if (!args.do_migrate && args.is_initpid)
416     WRITE_MSG(2, "'--initpid' is possible in combination with '--migrate' only\n");
417   else if (!args.do_create && args.xid==VC_DYNAMIC_XID)
418     WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "Can not migrate to an unknown context\n");
419   else if (optind>=argc)
420     WRITE_MSG(2, "No command given; use '--help' for more information.\n");
421   else
422     return doit(&args, argc, argv);
423
424   return wrapper_exit_code;
425 }