vcontext.c (15225B)
1 // $Id$ --*- c -*-- 2 3 // Copyright (C) 2004-2006 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 "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" 29 30 #include <vserver.h> 31 #include <getopt.h> 32 #include <fcntl.h> 33 #include <errno.h> 34 #include <sys/socket.h> 35 #include <sys/un.h> 36 #include <assert.h> 37 #include <signal.h> 38 #include <sys/types.h> 39 #include <pwd.h> 40 #include <grp.h> 41 #include <sys/mount.h> 42 43 #include <linux/personality.h> 44 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 51 #include <wrappers.h> 52 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 73 #define CMD_CLOSE_FD 0x4012 74 75 76 #ifndef MNT_DETACH 77 #define MNT_DETACH 0x0002 78 #endif 79 80 81 struct option const 82 CMDLINE_OPTIONS[] = { 83 { "help", no_argument, 0, CMD_HELP }, 84 { "version", no_argument, 0, CMD_VERSION }, 85 { "ctx", required_argument, 0, CMD_XID }, 86 { "xid", required_argument, 0, CMD_XID }, 87 { "create", no_argument, 0, CMD_CREATE }, 88 { "migrate", no_argument, 0, CMD_MIGRATE }, 89 { "migrate-self", no_argument, 0, CMD_MIGRATESELF }, 90 { "initpid", no_argument, 0, CMD_INITPID }, 91 { "endsetup", no_argument, 0, CMD_ENDSETUP }, 92 { "disconnect", no_argument, 0, CMD_DISCONNECT }, 93 { "silent", no_argument, 0, CMD_SILENT }, 94 { "silentexist", no_argument, 0, CMD_SILENTEXIST }, 95 { "uid", required_argument, 0, CMD_UID }, 96 { "chroot", no_argument, 0, CMD_CHROOT }, 97 { "namespace", no_argument, 0, CMD_NAMESPACE }, 98 { "syncsock", required_argument, 0, CMD_SYNCSOCK }, 99 { "syncmsg", required_argument, 0, CMD_SYNCMSG }, 100 { "personality-type", required_argument, 0, CMD_PERSTYPE }, 101 { "personality-flags", required_argument, 0, CMD_PERSFLAG }, 102 { "vlogin", no_argument, 0, CMD_VLOGIN }, 103 { "pivot-root", no_argument, 0, CMD_PIVOT_ROOT }, 104 { "closefd", no_argument, 0, CMD_CLOSE_FD }, 105 #if 1 106 { "fakeinit", no_argument, 0, CMD_INITPID }, // compatibility 107 #endif 108 { 0,0,0,0 }, 109 }; 110 111 struct Arguments { 112 bool do_create; 113 bool do_migrate; 114 bool do_migrateself; 115 bool do_disconnect; 116 bool do_endsetup; 117 bool is_initpid; 118 bool is_silentexist; 119 bool set_namespace; 120 bool do_vlogin; 121 uint_least32_t personality_flags; 122 uint_least32_t personality_type; 123 int verbosity; 124 bool do_chroot; 125 bool do_pivot_root; 126 bool do_close_fd; 127 char const * uid; 128 xid_t xid; 129 char const * sync_sock; 130 char const * sync_msg; 131 }; 132 133 int wrapper_exit_code = 255; 134 135 void do_vlogin(int argc, char *argv[], int ind); 136 137 static void 138 showHelp(int fd, char const *cmd, int res) 139 { 140 WRITE_MSG(fd, "Usage:\n "); 141 WRITE_STR(fd, cmd); 142 WRITE_MSG(fd, 143 " --create [--xid <xid>] <opts>* [--] <program> <args>*\n "); 144 WRITE_STR(fd, cmd); 145 WRITE_MSG(fd, 146 " [(--migrate --xid <xid>)|--migrate-self] <opts>* [--] <program> <args>*\n" 147 "\n" 148 "<opts> can be:\n" 149 " --chroot ... chroot into current directory\n" 150 " --namespace ... execute namespace management operations\n" 151 " --uid <uid> ... change uid\n" 152 " --initpid ... set current process as general process reaper\n" 153 " for ctx (possible for --migrate only)\n" 154 " --endsetup ... clear the setup flag; usefully for migrate only\n" 155 " --disconnect ... start program in background\n" 156 " --personality-type <type>\n" 157 " ... execute <program> in the given execution domain\n" 158 " --personality-flags <flags>+\n" 159 " ... set special flags for the given execution domain\n" 160 " --silent ... be silent\n" 161 " --silentexist ... be silent when context exists already; usefully\n" 162 " for '--create' only\n" 163 " --syncsock <file-name>\n" 164 " ... before executing the program, send a message\n" 165 " to the socket and wait until it closes.\n" 166 " <file-name> must be a SOCK_STREAM unix socket\n" 167 " --syncmsg <message>\n" 168 " ... use <message> as synchronization message; by\n" 169 " default, 'ok' will be used\n" 170 " --vlogin ... enable terminal proxy\n" 171 " --closefd ... close all open file descriptors >2\n" 172 "\n" 173 "'vcontext --create' exits with code 254 iff the context exists already.\n" 174 "\n" 175 "Please report bugs to " PACKAGE_BUGREPORT "\n"); 176 177 exit(res); 178 } 179 180 static void 181 showVersion() 182 { 183 WRITE_MSG(1, 184 "vcontext " VERSION " -- manages the creation of security contexts\n" 185 "This program is part of " PACKAGE_STRING "\n\n" 186 "Copyright (C) 2004-2006 Enrico Scholz\n" 187 VERSION_COPYRIGHT_DISCLAIMER); 188 exit(0); 189 } 190 191 #include "context-sync.hc" 192 193 static inline ALWAYSINLINE void 194 tellContext(xid_t ctx, bool do_it) 195 { 196 char buf[sizeof(xid_t)*3+2]; 197 size_t l; 198 199 if (!do_it) return; 200 201 l = utilvserver_fmt_long(buf,ctx); 202 203 WRITE_MSG(1, "New security context is "); 204 Vwrite (1, buf, l); 205 WRITE_MSG(1, "\n"); 206 } 207 208 static int 209 connectExternalSync(char const *filename) 210 { 211 int fd; 212 struct sockaddr_un addr; 213 214 if (filename==0) return -1; 215 216 ENSC_INIT_UNIX_SOCK(addr, filename); 217 218 fd = Esocket(PF_UNIX, SOCK_STREAM, 0); 219 Econnect(fd, &addr, sizeof(addr)); 220 221 return fd; 222 } 223 224 static void 225 setFlags(struct Arguments const *args, xid_t xid) 226 { 227 struct vc_ctx_flags flags = { 0,0 }; 228 229 if (args->is_initpid) 230 flags.mask |= VC_VXF_STATE_INIT; 231 232 if (args->do_endsetup) 233 flags.mask |= VC_VXF_STATE_SETUP; 234 235 if (flags.mask!=0) { 236 DPRINTF("set_flags: mask=%08llx, flag=%08llx\n", flags.mask, flags.flagword); 237 Evc_set_cflags(xid, &flags); 238 } 239 } 240 241 static void 242 doExternalSync(int fd, char const *msg) 243 { 244 char c; 245 246 if (fd==-1) return; 247 248 if (msg) EsendAll(fd, msg, strlen(msg)); 249 Eshutdown(fd, SHUT_WR); 250 251 if (TEMP_FAILURE_RETRY(recv(fd, &c, 1, MSG_NOSIGNAL))!=0) { 252 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "unexpected external synchronization event\n"); 253 exit(wrapper_exit_code); 254 } 255 256 Eclose(fd); 257 } 258 259 static inline ALWAYSINLINE int 260 doit(struct Arguments const *args, int argc, char *argv[]) 261 { 262 int p[2][2]; 263 pid_t pid = initSync(p, args->do_disconnect); 264 265 if (pid==0) { 266 xid_t xid; 267 int ext_sync_fd = connectExternalSync(args->sync_sock); 268 269 doSyncStage0(p, args->do_disconnect); 270 271 if (args->do_close_fd) { 272 int fd; 273 for (fd = 3; fd < sysconf(_SC_OPEN_MAX); fd++) { 274 if (fd == ext_sync_fd || 275 fd == p[0][0] || 276 fd == p[0][1] || 277 fd == p[1][0] || 278 fd == p[1][1]) 279 continue; 280 close(fd); 281 } 282 } 283 284 if (args->do_create) { 285 xid = vc_ctx_create(args->xid, NULL); 286 if (xid==VC_NOCTX) { 287 switch (errno) { 288 case EEXIST : 289 if (!args->is_silentexist) 290 perror(ENSC_WRAPPERS_PREFIX "vc_ctx_create()"); 291 return 254; 292 default : 293 perror(ENSC_WRAPPERS_PREFIX "vc_ctx_create()"); 294 return wrapper_exit_code; 295 } 296 } 297 tellContext(xid, args->verbosity>=1); 298 } 299 else 300 xid = args->xid; 301 302 if (args->do_chroot) { 303 Echroot("."); 304 if (args->set_namespace) { 305 if (args->do_migrateself) Evc_set_namespace(xid, CLONE_NEWNS|CLONE_FS, 0); 306 else if (args->do_migrate) Evc_enter_namespace(xid, CLONE_NEWNS|CLONE_FS, 0); 307 } 308 } 309 else if (args->do_pivot_root) { 310 if (vc_enter_namespace(xid, CLONE_NEWNS | CLONE_FS, 1) == -1) { 311 bool existed = false; 312 if (sys_unshare(CLONE_NEWNS) == -1) { 313 perror(ENSC_WRAPPERS_PREFIX "unshare(NEWNS)"); 314 return wrapper_exit_code; 315 } 316 if (mkdir("./.oldroot", 0700) == -1) { 317 if (errno == EEXIST) 318 existed = true; 319 else { 320 perror(ENSC_WRAPPERS_PREFIX "mkdir()"); 321 return wrapper_exit_code; 322 } 323 } 324 if (pivot_root(".", "./.oldroot") == -1) { 325 perror(ENSC_WRAPPERS_PREFIX "pivot_root()"); 326 return wrapper_exit_code; 327 } 328 if (umount2("/.oldroot", MNT_DETACH) == -1) { 329 perror(ENSC_WRAPPERS_PREFIX "umount2()"); 330 return wrapper_exit_code; 331 } 332 if (!existed && rmdir("/.oldroot") == -1) { 333 perror(ENSC_WRAPPERS_PREFIX "rmdir()"); 334 return wrapper_exit_code; 335 } 336 Evc_set_namespace(xid, CLONE_NEWNS | CLONE_FS, 1); 337 } 338 } 339 340 setFlags(args, xid); 341 342 if (args->do_migrate && !args->do_migrateself) 343 Evc_ctx_migrate(xid, 0); 344 345 if (args->uid != NULL) { 346 uid_t uid = 0; 347 unsigned long tmp; 348 349 if (!isNumberUnsigned(args->uid, &tmp, false)) { 350 #ifdef __dietlibc__ 351 struct passwd *pw; 352 pw = getpwnam(args->uid); 353 if (pw == NULL) { 354 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "Username '"); 355 WRITE_STR(2, args->uid); 356 WRITE_MSG(2, "' does not exist\n"); 357 return wrapper_exit_code; 358 } 359 uid = pw->pw_uid; 360 Einitgroups(args->uid, pw->pw_gid); 361 Esetgid(pw->pw_gid); 362 #else 363 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "Uid '"); 364 WRITE_STR(2, args->uid); 365 WRITE_MSG(2, "' is not a number\n"); 366 return wrapper_exit_code; 367 #endif 368 } 369 else 370 uid = (uid_t) tmp; 371 372 Esetuid((uid_t) uid); 373 if (getuid()!=uid) { 374 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "Something went wrong while changing the UID\n"); 375 exit(wrapper_exit_code); 376 } 377 } 378 379 if (args->personality_type!=VC_BAD_PERSONALITY && 380 sys_personality(args->personality_type | args->personality_flags)==-1) { 381 perror(ENSC_WRAPPERS_PREFIX "personality()"); 382 exit(wrapper_exit_code); 383 } 384 385 doExternalSync(ext_sync_fd, args->sync_msg); 386 doSyncStage1(p, args->do_disconnect); 387 DPRINTF("doit: pid=%u, ppid=%u\n", getpid(), getppid()); 388 389 if (!args->do_vlogin) 390 execvp (argv[optind],argv+optind); 391 else 392 do_vlogin(argc, argv, optind); 393 doSyncStage2(p, args->do_disconnect); 394 395 PERROR_Q(ENSC_WRAPPERS_PREFIX "execvp", argv[optind]); 396 exit(wrapper_exit_code); 397 } 398 399 assert(args->do_disconnect); 400 401 waitOnSync(pid, p, args->xid!=VC_DYNAMIC_XID && args->do_migrate); 402 return EXIT_SUCCESS; 403 } 404 405 static uint_least32_t 406 parsePersonalityType(char const *str) 407 { 408 uint_least32_t res = vc_str2personalitytype(str, 0); 409 if (res==VC_BAD_PERSONALITY) { 410 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "bad personality type\n"); 411 exit(wrapper_exit_code); 412 } 413 414 return res; 415 } 416 417 static uint_least32_t 418 parsePersonalityFlags(char const *str) 419 { 420 struct vc_err_listparser err; 421 uint_least32_t res; 422 423 if (vc_list2personalityflag(str, 0, &res, &err)==-1) { 424 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "bad personality flag '"); 425 Vwrite(2, err.ptr, err.len); 426 WRITE_MSG(2, "'\n"); 427 exit(wrapper_exit_code); 428 } 429 430 return res; 431 } 432 433 int main (int argc, char *argv[]) 434 { 435 struct Arguments args = { 436 .do_create = false, 437 .do_migrate = false, 438 .do_migrateself = false, 439 .do_disconnect = false, 440 .do_endsetup = false, 441 .do_vlogin = false, 442 .do_close_fd = false, 443 .is_initpid = false, 444 .is_silentexist = false, 445 .set_namespace = false, 446 .verbosity = 1, 447 .uid = NULL, 448 .xid = VC_DYNAMIC_XID, 449 .personality_type = VC_BAD_PERSONALITY, 450 .personality_flags = 0, 451 .sync_msg = "ok", 452 }; 453 454 while (1) { 455 int c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0); 456 if (c==-1) break; 457 458 switch (c) { 459 case CMD_HELP : showHelp(1, argv[0], 0); 460 case CMD_VERSION : showVersion(); 461 case CMD_CREATE : args.do_create = true; break; 462 case CMD_MIGRATE : args.do_migrate = true; break; 463 case CMD_DISCONNECT : args.do_disconnect = true; break; 464 case CMD_ENDSETUP : args.do_endsetup = true; break; 465 case CMD_VLOGIN : args.do_vlogin = true; break; 466 case CMD_INITPID : args.is_initpid = true; break; 467 case CMD_CHROOT : args.do_chroot = true; break; 468 case CMD_PIVOT_ROOT : args.do_pivot_root = true; break; 469 case CMD_NAMESPACE : args.set_namespace = true; break; 470 case CMD_SILENTEXIST : args.is_silentexist = true; break; 471 case CMD_CLOSE_FD : args.do_close_fd = true; break; 472 case CMD_SYNCSOCK : args.sync_sock = optarg; break; 473 case CMD_SYNCMSG : args.sync_msg = optarg; break; 474 case CMD_UID : args.uid = optarg; break; 475 case CMD_XID : args.xid = Evc_xidopt2xid(optarg,true); break; 476 case CMD_SILENT : --args.verbosity; break; 477 case CMD_PERSTYPE : 478 args.personality_type = parsePersonalityType(optarg); 479 break; 480 case CMD_PERSFLAG : 481 args.personality_flags |= parsePersonalityFlags(optarg); 482 break; 483 case CMD_MIGRATESELF : 484 args.do_migrate = true; 485 args.do_migrateself = true; 486 break; 487 488 default : 489 WRITE_MSG(2, "Try '"); 490 WRITE_STR(2, argv[0]); 491 WRITE_MSG(2, " --help' for more information.\n"); 492 return wrapper_exit_code; 493 break; 494 } 495 } 496 497 signal(SIGCHLD, SIG_DFL); 498 499 if (args.do_migrateself) 500 args.xid = Evc_get_task_xid(0); 501 502 if (!args.do_create && !args.do_migrate) 503 WRITE_MSG(2, "Neither '--create' nor '--migrate' specified; try '--help' for more information\n"); 504 else if (args.do_create && args.do_migrate) 505 WRITE_MSG(2, "Can not specify '--create' and '--migrate' at the same time; try '--help' for more information\n"); 506 else if (!args.do_migrate && args.is_initpid) 507 WRITE_MSG(2, "'--initpid' is possible in combination with '--migrate' only\n"); 508 else if (!args.do_create && args.xid==VC_DYNAMIC_XID) 509 WRITE_MSG(2, ENSC_WRAPPERS_PREFIX "Can not migrate to an unknown context\n"); 510 else if (optind>=argc) 511 WRITE_MSG(2, "No command given; use '--help' for more information.\n"); 512 else 513 return doit(&args, argc, argv); 514 515 return wrapper_exit_code; 516 }