added '--migrate-self' support
[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
26 #include <vserver.h>
27 #include <getopt.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <sys/socket.h>
31 #include <sys/un.h>
32
33 #define ENSC_WRAPPERS_PREFIX    "vcontext: "
34 #define ENSC_WRAPPERS_UNISTD    1
35 #define ENSC_WRAPPERS_VSERVER   1
36 #define ENSC_WRAPPERS_FCNTL     1
37 #define ENSC_WRAPPERS_SOCKET    1
38 #define ENSC_WRAPPERS_IOSOCK    1
39 #include <wrappers.h>
40
41 #define CMD_HELP                0x1000
42 #define CMD_VERSION             0x1001
43 #define CMD_XID                 0x4000
44 #define CMD_CREATE              0x4001
45 #define CMD_MIGRATE             0x4003
46 #define CMD_FAKEINIT            0x4002
47 #define CMD_DISCONNECT          0x4004
48 #define CMD_UID                 0x4005
49 #define CMD_CHROOT              0x4006
50 #define CMD_SILENT              0x4007
51 #define CMD_SYNCSOCK            0x4008
52 #define CMD_SYNCMSG             0x4009
53 #define CMD_MIGRATESELF         0x4010
54
55 struct option const
56 CMDLINE_OPTIONS[] = {
57   { "help",       no_argument,       0, CMD_HELP },
58   { "version",    no_argument,       0, CMD_VERSION },
59   { "ctx",        required_argument, 0, CMD_XID },
60   { "xid",        required_argument, 0, CMD_XID },
61   { "create",     no_argument,       0, CMD_CREATE },
62   { "migrate",    no_argument,       0, CMD_MIGRATE },
63   { "migrate-self", no_argument,        0, CMD_MIGRATESELF },
64   { "fakeinit",     no_argument,        0, CMD_FAKEINIT },
65   { "disconnect",   no_argument,        0, CMD_DISCONNECT },
66   { "silent",       no_argument,        0, CMD_SILENT },
67   { "uid",          no_argument,        0, CMD_UID },
68   { "chroot",       no_argument,        0, CMD_CHROOT },
69   { "syncsock",     required_argument,  0, CMD_SYNCSOCK },
70   { "syncmsg",      required_argument,  0, CMD_SYNCMSG },
71   { 0,0,0,0 },
72 };
73
74 struct Arguments {
75     bool                do_create;
76     bool                do_migrate;
77     bool                do_migrateself;
78     bool                do_disconnect;
79     bool                is_fakeinit;
80     int                 verbosity;
81     bool                do_chroot;
82     uid_t               uid;
83     xid_t               xid;
84     char const *        sync_sock;
85     char const *        sync_msg;
86 };
87
88 int             wrapper_exit_code = 255;
89
90 static void
91 showHelp(int fd, char const *cmd, int res)
92 {
93   WRITE_MSG(fd, "Usage:\n    ");
94   WRITE_STR(fd, cmd);
95   WRITE_MSG(fd,
96             " --create [--xid <xid>] <opts>* [--] <program> <args>*\n    ");
97   WRITE_STR(fd, cmd);
98   WRITE_MSG(fd,
99             " [(--migrate --xid <xid>)|--migrate-self]  <opts>* [--] <program> <args>*\n"
100             "\n"
101             "<opts> can be:\n"
102             "    --chroot        ...  chroot into current directory\n"
103             "    --uid <uid>     ...  change uid\n"
104             "    --fakeinit      ...  set current process as general process reaper\n"
105             "                         for ctx (possible for --migrate only)\n"
106             "    --disconnect    ...  start program in background\n"
107             "    --silent        ...  be silent\n"
108             "    --syncsock <file-name>\n"
109             "                    ...  before executing the program, send a message\n"
110             "                         to the socket and wait until it closes.\n"
111             "                         <file-name> must be a SOCK_STREAM unix socket\n"
112             "    --syncmsg <message>\n"
113             "                    ...  use <message> as synchronization message; by\n"
114             "                         default, 'ok' will be used\n"
115             "\n"
116             "'vcontext --create' exits with code 254 iff the context exists already.\n"
117             "\n"
118             "Please report bugs to " PACKAGE_BUGREPORT "\n");
119
120   exit(res);
121 }
122
123 static void
124 showVersion()
125 {
126   WRITE_MSG(1,
127             "vcontext " VERSION " -- manages the creation of security contexts\n"
128             "This program is part of " PACKAGE_STRING "\n\n"
129             "Copyright (C) 2004 Enrico Scholz\n"
130             VERSION_COPYRIGHT_DISCLAIMER);
131   exit(0);
132 }
133
134 static inline ALWAYSINLINE int
135 initSync(int p[2], bool do_disconnect)
136 {
137   if (!do_disconnect) return 0;
138
139   Epipe(p);
140   fcntl(p[1], F_SETFD, FD_CLOEXEC);
141   return Efork();
142 }
143
144 static inline ALWAYSINLINE void
145 doSyncStage1(int p[2], bool do_disconnect)
146 {
147   int   fd;
148
149   if (!do_disconnect) return;
150   
151   fd = Eopen("/dev/null", O_RDONLY, 0);
152   Esetsid();
153   Edup2(fd, 0);
154   Eclose(p[0]);
155   if (fd!=0) Eclose(fd);
156   Ewrite(p[1], ".", 1);
157 }
158
159 static inline ALWAYSINLINE void
160 doSyncStage2(int p[2], bool do_disconnect)
161 {
162   if (!do_disconnect) return;
163
164   Ewrite(p[1], "X", 1);
165 }
166
167 static void
168 waitOnSync(pid_t pid, int p[2])
169 {
170   int           c;
171   size_t        l;
172
173   Eclose(p[1]);
174   l = Eread(p[0], &c, 1);
175   if (l!=1) exitLikeProcess(pid);
176   l = Eread(p[0], &c, 1);
177   if (l!=0) exitLikeProcess(pid);
178 }
179
180 static inline ALWAYSINLINE void
181 tellContext(xid_t ctx, bool do_it)
182 {
183   char          buf[sizeof(xid_t)*3+2];
184   size_t        l;
185
186   if (!do_it) return;
187
188   l = utilvserver_fmt_long(buf,ctx);
189
190   WRITE_MSG(2, "New security context is ");
191   write(2, buf, l);
192   WRITE_MSG(2, "\n");
193 }
194
195 static int
196 connectExternalSync(char const *filename)
197 {
198   int                   fd;
199   struct sockaddr_un    addr;
200   
201   if (filename==0) return -1;
202
203   ENSC_INIT_UNIX_SOCK(addr, filename);
204
205   fd = Esocket(PF_UNIX, SOCK_STREAM, 0);
206   Econnect(fd, &addr, sizeof(addr));
207
208   return fd;
209 }
210
211 static void
212 doExternalSync(int fd, char const *msg)
213 {
214   char          c;
215   
216   if (fd==-1) return;
217
218   if (msg) EsendAll(fd, msg, strlen(msg));
219   Eshutdown(fd, SHUT_WR);
220
221   if (TEMP_FAILURE_RETRY(recv(fd, &c, 1, MSG_NOSIGNAL))!=0) {
222     WRITE_MSG(2, "vcontext: unexpected external synchronization event\n");
223     exit(255);
224   }
225
226   Eclose(fd);
227 }
228
229 static inline ALWAYSINLINE int
230 doit(struct Arguments const *args, char *argv[])
231 {
232   int                   p[2];
233   pid_t                 pid = initSync(p, args->do_disconnect);
234   
235   if (pid==0) {
236     xid_t       xid;
237     int         ext_sync_fd = connectExternalSync(args->sync_sock);
238     
239     if (args->do_create) {
240       xid = vc_create_context(args->xid);
241       if (xid==VC_NOCTX) {
242         if (errno==EEXIST) return 254;
243         else {
244           perror(ENSC_WRAPPERS_PREFIX "vc_create_context()");
245           return wrapper_exit_code;
246         }
247       }
248       tellContext(xid, args->verbosity>=1);
249     }
250     else
251       xid = args->xid;
252
253     if (args->do_chroot)
254       Echroot(".");
255
256     if (args->uid!=(uid_t)(-1) && getuid()!=args->uid) {
257       Esetuid(args->uid);
258       if (getuid()!=args->uid) {
259         WRITE_MSG(2, "vcontext: Something went wrong while changing the UID\n");
260         exit(255);
261       }
262     }
263
264     if (args->is_fakeinit) {
265       struct vc_ctx_flags       test_flags;
266       struct vc_ctx_flags       flags = {
267         .flagword = S_CTX_INFO_INIT,
268         .mask     = S_CTX_INFO_INIT
269       };
270       
271       Evc_get_flags(xid, &test_flags);
272       if ((flags.mask & S_CTX_INFO_INIT)==0) {
273         WRITE_MSG(2, "vcontext: context has already a fakeinit-process\n");
274         exit(255);
275       }
276
277       Evc_set_flags(xid, &flags);
278     }
279
280     if (args->do_migrate && !args->do_migrateself)
281       Evc_migrate_context(xid);
282
283     doExternalSync(ext_sync_fd, args->sync_msg);
284     doSyncStage1(p, args->do_disconnect);
285     execvp (argv[optind],argv+optind);
286     doSyncStage2(p, args->do_disconnect);
287
288     PERROR_Q("chcontext: execvp", argv[optind]);
289     exit(255);
290   }
291
292   waitOnSync(pid, p);
293   return EXIT_SUCCESS;
294 }
295
296 int main (int argc, char *argv[])
297 {
298   struct Arguments              args = {
299     .do_create      = false,
300     .do_migrate     = false,
301     .do_migrateself = false,
302     .do_disconnect  = false,
303     .is_fakeinit    = false,
304     .verbosity      = 1,
305     .uid            = -1,
306     .xid            = VC_DYNAMIC_XID,
307     .sync_msg       = "ok",
308   };
309   
310   while (1) {
311     int         c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
312     if (c==-1) break;
313     
314     switch (c) {
315       case CMD_HELP             :  showHelp(1, argv[0], 0);
316       case CMD_VERSION          :  showVersion();
317       case CMD_CREATE           :  args.do_create     = true; break;
318       case CMD_MIGRATE          :  args.do_migrate    = true; break;
319       case CMD_DISCONNECT       :  args.do_disconnect = true; break;
320       case CMD_FAKEINIT         :  args.is_fakeinit   = true; break;
321       case CMD_CHROOT           :  args.do_chroot     = true; break;
322       case CMD_SILENT           :  --args.verbosity;          break;
323       case CMD_XID              :  args.xid           = atol(optarg); break;
324       case CMD_UID              :  args.uid           = atol(optarg); break;
325       case CMD_SYNCSOCK         :  args.sync_sock     = optarg; break;
326       case CMD_SYNCMSG          :  args.sync_msg      = optarg; break;
327       case CMD_MIGRATESELF      :
328         args.do_migrate     = true;
329         args.do_migrateself = true;
330         break;
331
332       default           :
333         WRITE_MSG(2, "Try '");
334         WRITE_STR(2, argv[0]);
335         WRITE_MSG(2, " --help\" for more information.\n");
336         return 255;
337         break;
338     }
339   }
340
341   if (args.do_migrateself)
342     args.xid = Evc_get_task_xid(0);
343   
344   if (!args.do_create && !args.do_migrate)
345     WRITE_MSG(2, "Neither '--create' nor '--migrate specified; try '--help' for more information\n");
346   else if (args.do_create  &&  args.do_migrate)
347     WRITE_MSG(2, "Can not specify '--create' and '--migrate' at the same time; try '--help' for more information\n");
348   else if (!args.do_migrate && args.is_fakeinit)
349     WRITE_MSG(2, "'--fakeinit' is possible in combination with '--migrate' only\n");
350   else if (!args.do_create && args.xid==VC_DYNAMIC_XID)
351     WRITE_MSG(2, "vcontext: Can not migrate to an unknown context\n");
352   else if (optind>=argc)
353     WRITE_MSG(2, "No command given; use '--help' for more information.\n");
354   else
355     return doit(&args, argv);
356
357   return 255;
358 }