3 // Copyright (C) 2004-2006 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
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.
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.
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.
24 #include "compat-pivot_root.h"
25 #include "lib/internal.h"
26 #include "lib_internal/jail.h"
27 #include "lib_internal/sys_personality.h"
28 #include "lib_internal/sys_unshare.h"
34 #include <sys/socket.h>
38 #include <sys/types.h>
41 #include <sys/mount.h>
43 #include <linux/personality.h>
45 #define ENSC_WRAPPERS_PREFIX "vcontext: "
46 #define ENSC_WRAPPERS_UNISTD 1
47 #define ENSC_WRAPPERS_VSERVER 1
48 #define ENSC_WRAPPERS_FCNTL 1
49 #define ENSC_WRAPPERS_SOCKET 1
50 #define ENSC_WRAPPERS_IOSOCK 1
53 #define CMD_HELP 0x1000
54 #define CMD_VERSION 0x1001
55 #define CMD_XID 0x4000
56 #define CMD_CREATE 0x4001
57 #define CMD_MIGRATE 0x4003
58 #define CMD_INITPID 0x4002
59 #define CMD_DISCONNECT 0x4004
60 #define CMD_UID 0x4005
61 #define CMD_CHROOT 0x4006
62 #define CMD_SILENT 0x4007
63 #define CMD_SYNCSOCK 0x4008
64 #define CMD_SYNCMSG 0x4009
65 #define CMD_MIGRATESELF 0x400a
66 #define CMD_ENDSETUP 0x400b
67 #define CMD_SILENTEXIST 0x400c
68 #define CMD_NAMESPACE 0x400d
69 #define CMD_PERSTYPE 0x400e
70 #define CMD_PERSFLAG 0x400f
71 #define CMD_VLOGIN 0x4010
72 #define CMD_PIVOT_ROOT 0x4011
76 #define MNT_DETACH 0x0002
82 { "help", no_argument, 0, CMD_HELP },
83 { "version", no_argument, 0, CMD_VERSION },
84 { "ctx", required_argument, 0, CMD_XID },
85 { "xid", required_argument, 0, CMD_XID },
86 { "create", no_argument, 0, CMD_CREATE },
87 { "migrate", no_argument, 0, CMD_MIGRATE },
88 { "migrate-self", no_argument, 0, CMD_MIGRATESELF },
89 { "initpid", no_argument, 0, CMD_INITPID },
90 { "endsetup", no_argument, 0, CMD_ENDSETUP },
91 { "disconnect", no_argument, 0, CMD_DISCONNECT },
92 { "silent", no_argument, 0, CMD_SILENT },
93 { "silentexist", no_argument, 0, CMD_SILENTEXIST },
94 { "uid", required_argument, 0, CMD_UID },
95 { "chroot", no_argument, 0, CMD_CHROOT },
96 { "namespace", no_argument, 0, CMD_NAMESPACE },
97 { "syncsock", required_argument, 0, CMD_SYNCSOCK },
98 { "syncmsg", required_argument, 0, CMD_SYNCMSG },
99 { "personality-type", required_argument, 0, CMD_PERSTYPE },
100 { "personality-flags", required_argument, 0, CMD_PERSFLAG },
101 { "vlogin", no_argument, 0, CMD_VLOGIN },
102 { "pivot-root", no_argument, 0, CMD_PIVOT_ROOT },
104 { "fakeinit", no_argument, 0, CMD_INITPID }, // compatibility
119 uint_least32_t personality_flags;
120 uint_least32_t personality_type;
126 char const * sync_sock;
127 char const * sync_msg;
130 int wrapper_exit_code = 255;
132 void do_vlogin(int argc, char *argv[], int ind);
135 showHelp(int fd, char const *cmd, int res)
137 WRITE_MSG(fd, "Usage:\n ");
140 " --create [--xid <xid>] <opts>* [--] <program> <args>*\n ");
143 " [(--migrate --xid <xid>)|--migrate-self] <opts>* [--] <program> <args>*\n"
146 " --chroot ... chroot into current directory\n"
147 " --namespace ... execute namespace management operations\n"
148 " --uid <uid> ... change uid\n"
149 " --initpid ... set current process as general process reaper\n"
150 " for ctx (possible for --migrate only)\n"
151 " --endsetup ... clear the setup flag; usefully for migrate only\n"
152 " --disconnect ... start program in background\n"
153 " --personality-type <type>\n"
154 " ... execute <program> in the given execution domain\n"
155 " --personality-flags <flags>+\n"
156 " ... set special flags for the given execution domain\n"
157 " --silent ... be silent\n"
158 " --silentexist ... be silent when context exists already; usefully\n"
159 " for '--create' only\n"
160 " --syncsock <file-name>\n"
161 " ... before executing the program, send a message\n"
162 " to the socket and wait until it closes.\n"
163 " <file-name> must be a SOCK_STREAM unix socket\n"
164 " --syncmsg <message>\n"
165 " ... use <message> as synchronization message; by\n"
166 " default, 'ok' will be used\n"
167 " --vlogin ... enable terminal proxy\n"
169 "'vcontext --create' exits with code 254 iff the context exists already.\n"
171 "Please report bugs to " PACKAGE_BUGREPORT "\n");
180 "vcontext " VERSION " -- manages the creation of security contexts\n"
181 "This program is part of " PACKAGE_STRING "\n\n"
182 "Copyright (C) 2004-2006 Enrico Scholz\n"
183 VERSION_COPYRIGHT_DISCLAIMER);
187 #include "context-sync.hc"
189 static inline ALWAYSINLINE void
190 tellContext(xid_t ctx, bool do_it)
192 char buf[sizeof(xid_t)*3+2];
197 l = utilvserver_fmt_long(buf,ctx);
199 WRITE_MSG(1, "New security context is ");
205 connectExternalSync(char const *filename)
208 struct sockaddr_un addr;
210 if (filename==0) return -1;
212 ENSC_INIT_UNIX_SOCK(addr, filename);
214 fd = Esocket(PF_UNIX, SOCK_STREAM, 0);
215 Econnect(fd, &addr, sizeof(addr));
221 setFlags(struct Arguments const *args, xid_t xid)
223 struct vc_ctx_flags flags = { 0,0 };
225 if (args->is_initpid)
226 flags.mask |= VC_VXF_STATE_INIT;
228 if (args->do_endsetup)
229 flags.mask |= VC_VXF_STATE_SETUP;
232 DPRINTF("set_flags: mask=%08llx, flag=%08llx\n", flags.mask, flags.flagword);
233 Evc_set_cflags(xid, &flags);
238 doExternalSync(int fd, char const *msg)
244 if (msg) EsendAll(fd, msg, strlen(msg));
245 Eshutdown(fd, SHUT_WR);
247 if (TEMP_FAILURE_RETRY(recv(fd, &c, 1, MSG_NOSIGNAL))!=0) {
248 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "unexpected external synchronization event\n");
249 exit(wrapper_exit_code);
255 static inline ALWAYSINLINE int
256 doit(struct Arguments const *args, int argc, char *argv[])
259 pid_t pid = initSync(p, args->do_disconnect);
263 int ext_sync_fd = connectExternalSync(args->sync_sock);
265 doSyncStage0(p, args->do_disconnect);
267 if (args->do_create) {
268 xid = vc_ctx_create(args->xid, NULL);
272 if (!args->is_silentexist)
273 perror(ENSC_WRAPPERS_PREFIX "vc_ctx_create()");
276 perror(ENSC_WRAPPERS_PREFIX "vc_ctx_create()");
277 return wrapper_exit_code;
280 tellContext(xid, args->verbosity>=1);
285 if (args->do_chroot) {
287 if (args->set_namespace) {
288 if (args->do_migrateself) Evc_set_namespace(xid, CLONE_NEWNS|CLONE_FS, 0);
289 else if (args->do_migrate) Evc_enter_namespace(xid, CLONE_NEWNS|CLONE_FS, 0);
292 else if (args->do_pivot_root) {
293 if (vc_enter_namespace(xid, CLONE_NEWNS | CLONE_FS, 1) == -1) {
294 bool existed = false;
295 if (sys_unshare(CLONE_NEWNS) == -1) {
296 perror(ENSC_WRAPPERS_PREFIX "unshare(NEWNS)");
297 return wrapper_exit_code;
299 if (mkdir("./.oldroot", 0700) == -1) {
303 perror(ENSC_WRAPPERS_PREFIX "mkdir()");
304 return wrapper_exit_code;
307 if (pivot_root(".", "./.oldroot") == -1) {
308 perror(ENSC_WRAPPERS_PREFIX "pivot_root()");
309 return wrapper_exit_code;
311 if (umount2("/.oldroot", MNT_DETACH) == -1) {
312 perror(ENSC_WRAPPERS_PREFIX "umount2()");
313 return wrapper_exit_code;
315 if (!existed && rmdir("/.oldroot") == -1) {
316 perror(ENSC_WRAPPERS_PREFIX "rmdir()");
317 return wrapper_exit_code;
319 Evc_set_namespace(xid, CLONE_NEWNS | CLONE_FS, 1);
325 if (args->do_migrate && !args->do_migrateself)
326 Evc_ctx_migrate(xid, 0);
328 if (args->uid != NULL) {
332 if (!isNumberUnsigned(args->uid, &tmp, false)) {
335 pw = getpwnam(args->uid);
337 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "Username '");
338 WRITE_STR(2, args->uid);
339 WRITE_MSG(2, "' does not exist\n");
340 return wrapper_exit_code;
343 Einitgroups(args->uid, pw->pw_gid);
346 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "Uid '");
347 WRITE_STR(2, args->uid);
348 WRITE_MSG(2, "' is not a number\n");
349 return wrapper_exit_code;
355 Esetuid((uid_t) uid);
357 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "Something went wrong while changing the UID\n");
358 exit(wrapper_exit_code);
362 if (args->personality_type!=VC_BAD_PERSONALITY &&
363 sys_personality(args->personality_type | args->personality_flags)==-1) {
364 perror(ENSC_WRAPPERS_PREFIX "personality()");
365 exit(wrapper_exit_code);
368 doExternalSync(ext_sync_fd, args->sync_msg);
369 doSyncStage1(p, args->do_disconnect);
370 DPRINTF("doit: pid=%u, ppid=%u\n", getpid(), getppid());
372 if (!args->do_vlogin)
373 execvp (argv[optind],argv+optind);
375 do_vlogin(argc, argv, optind);
376 doSyncStage2(p, args->do_disconnect);
378 PERROR_Q(ENSC_WRAPPERS_PREFIX "execvp", argv[optind]);
379 exit(wrapper_exit_code);
382 assert(args->do_disconnect);
384 waitOnSync(pid, p, args->xid!=VC_DYNAMIC_XID && args->do_migrate);
388 static uint_least32_t
389 parsePersonalityType(char const *str)
391 uint_least32_t res = vc_str2personalitytype(str, 0);
392 if (res==VC_BAD_PERSONALITY) {
393 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "bad personality type\n");
394 exit(wrapper_exit_code);
400 static uint_least32_t
401 parsePersonalityFlags(char const *str)
403 struct vc_err_listparser err;
406 if (vc_list2personalityflag(str, 0, &res, &err)==-1) {
407 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "bad personality flag '");
408 Vwrite(2, err.ptr, err.len);
410 exit(wrapper_exit_code);
416 int main (int argc, char *argv[])
418 struct Arguments args = {
421 .do_migrateself = false,
422 .do_disconnect = false,
423 .do_endsetup = false,
426 .is_silentexist = false,
427 .set_namespace = false,
430 .xid = VC_DYNAMIC_XID,
431 .personality_type = VC_BAD_PERSONALITY,
432 .personality_flags = 0,
437 int c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0);
441 case CMD_HELP : showHelp(1, argv[0], 0);
442 case CMD_VERSION : showVersion();
443 case CMD_CREATE : args.do_create = true; break;
444 case CMD_MIGRATE : args.do_migrate = true; break;
445 case CMD_DISCONNECT : args.do_disconnect = true; break;
446 case CMD_ENDSETUP : args.do_endsetup = true; break;
447 case CMD_VLOGIN : args.do_vlogin = true; break;
448 case CMD_INITPID : args.is_initpid = true; break;
449 case CMD_CHROOT : args.do_chroot = true; break;
450 case CMD_PIVOT_ROOT : args.do_pivot_root = true; break;
451 case CMD_NAMESPACE : args.set_namespace = true; break;
452 case CMD_SILENTEXIST : args.is_silentexist = true; break;
453 case CMD_SYNCSOCK : args.sync_sock = optarg; break;
454 case CMD_SYNCMSG : args.sync_msg = optarg; break;
455 case CMD_UID : args.uid = optarg; break;
456 case CMD_XID : args.xid = Evc_xidopt2xid(optarg,true); break;
457 case CMD_SILENT : --args.verbosity; break;
459 args.personality_type = parsePersonalityType(optarg);
462 args.personality_flags |= parsePersonalityFlags(optarg);
464 case CMD_MIGRATESELF :
465 args.do_migrate = true;
466 args.do_migrateself = true;
470 WRITE_MSG(2, "Try '");
471 WRITE_STR(2, argv[0]);
472 WRITE_MSG(2, " --help' for more information.\n");
473 return wrapper_exit_code;
478 signal(SIGCHLD, SIG_DFL);
480 if (args.do_migrateself)
481 args.xid = Evc_get_task_xid(0);
483 if (!args.do_create && !args.do_migrate)
484 WRITE_MSG(2, "Neither '--create' nor '--migrate' specified; try '--help' for more information\n");
485 else if (args.do_create && args.do_migrate)
486 WRITE_MSG(2, "Can not specify '--create' and '--migrate' at the same time; try '--help' for more information\n");
487 else if (!args.do_migrate && args.is_initpid)
488 WRITE_MSG(2, "'--initpid' is possible in combination with '--migrate' only\n");
489 else if (!args.do_create && args.xid==VC_DYNAMIC_XID)
490 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "Can not migrate to an unknown context\n");
491 else if (optind>=argc)
492 WRITE_MSG(2, "No command given; use '--help' for more information.\n");
494 return doit(&args, argc, argv);
496 return wrapper_exit_code;