chcontext.c (11737B)
1 // $Id$ 2 3 // Copyright (C) 2003,2004 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de> 4 // based on chcontext.cc by Jacques Gelinas 5 // 6 // This program is free software; you can redistribute it and/or modify 7 // it under the terms of the GNU General Public License as published by 8 // the Free Software Foundation; either version 2, or (at your option) 9 // any later version. 10 // 11 // This program is distributed in the hope that it will be useful, 12 // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 // GNU General Public License for more details. 15 // 16 // You should have received a copy of the GNU General Public License 17 // along with this program; if not, write to the Free Software 18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 19 20 /* 21 chcontext is a wrapper to user the new_s_context system call. It 22 does little more than mapping command line option to the system call 23 arguments. 24 */ 25 #ifdef HAVE_CONFIG_H 26 # include <config.h> 27 #endif 28 29 #include "util.h" 30 #include "vserver.h" 31 #include "internal.h" 32 #include "lib_internal/jail.h" 33 34 #include <stdio.h> 35 #include <unistd.h> 36 #include <string.h> 37 #include <stdlib.h> 38 #include <errno.h> 39 #include <getopt.h> 40 #include <assert.h> 41 #include <fcntl.h> 42 #include <libgen.h> 43 #include <signal.h> 44 45 #define ENSC_WRAPPERS_PREFIX "chcontext: " 46 #define ENSC_WRAPPERS_VSERVER 1 47 #define ENSC_WRAPPERS_UNISTD 1 48 #define ENSC_WRAPPERS_FCNTL 1 49 #include <wrappers.h> 50 51 #define CMD_HELP 0x1000 52 #define CMD_VERSION 0x1001 53 #define CMD_CAP 0x4000 54 #define CMD_CTX 0x4001 55 #define CMD_DISCONNECT 0x4002 56 #define CMD_DOMAINNAME 0x4003 57 #define CMD_FLAG 0x4004 58 #define CMD_HOSTNAME 0x4005 59 #define CMD_SECURE 0x4006 60 #define CMD_SILENT 0x4007 61 62 int wrapper_exit_code = 255; 63 64 struct option const 65 CMDLINE_OPTIONS[] = { 66 { "help", no_argument, 0, CMD_HELP }, 67 { "version", no_argument, 0, CMD_VERSION }, 68 { "cap", required_argument, 0, CMD_CAP }, 69 { "ctx", required_argument, 0, CMD_CTX }, 70 { "xid", required_argument, 0, CMD_CTX }, 71 { "disconnect", no_argument, 0, CMD_DISCONNECT }, 72 { "domainname", required_argument, 0, CMD_DOMAINNAME }, 73 { "flag", required_argument, 0, CMD_FLAG }, 74 { "hostname", required_argument, 0, CMD_HOSTNAME }, 75 { "secure", no_argument, 0, CMD_SECURE }, 76 { "silent", no_argument, 0, CMD_SILENT }, 77 { 0,0,0,0 } 78 }; 79 80 struct Arguments { 81 size_t nbctx; 82 xid_t ctxs[16]; 83 bool do_disconnect; 84 bool do_silent; 85 unsigned int flags; 86 uint32_t remove_caps; 87 uint32_t add_caps; 88 char const * hostname; 89 char const * domainname; 90 }; 91 92 static struct Arguments const * global_args = 0; 93 94 static void 95 showHelp(int fd, char const *cmd, int res) 96 { 97 VSERVER_DECLARE_CMD(cmd); 98 99 #if !defined(VC_ENABLE_API_COMPAT) && !defined(VC_ENABLE_API_LEGACY) 100 WRITE_MSG(1, "ERROR: tools were built without legacy API support; chcontext will not work!\n\n"); 101 #endif 102 103 WRITE_MSG(fd, "Usage: "); 104 WRITE_STR(fd, cmd); 105 WRITE_MSG(fd, 106 " [--cap [!]<cap_name>] [--secure] [--xid <num>] [--disconnect]\n" 107 " [--domainname <name>] [--hostname <name>] [--flag <flags>+]\n" 108 " [--silent] [--] command arguments ...\n" 109 "\n" 110 "chcontext allocate a new security context and executes\n" 111 "a command in that context.\n" 112 "By default, a new/unused context is allocated\n" 113 "\n" 114 "--cap CAP_NAME\n" 115 " Add a capability from the command. This option may be\n" 116 " repeated several time.\n" 117 " See /usr/include/linux/capability.h\n" 118 " In general, this option is used with the --secure option\n" 119 " --secure removes most critical capabilities and --cap\n" 120 " adds specific ones.\n" 121 "\n" 122 123 "--cap !CAP_NAME\n" 124 " Remove a capability from the command. This option may be\n" 125 " repeated several time.\n" 126 " See /usr/include/linux/capability.h\n" 127 "\n" 128 "--xid num\n" 129 " Select the context. On root in context 0 is allowed to\n" 130 " select a specific context.\n" 131 " Context number 1 is special. It can see all processes\n" 132 " in any contexts, but can't kill them though.\n" 133 " Option --xid may be repeated several times to specify up to 16 contexts.\n" 134 135 "--disconnect\n" 136 " Start the command in background and make the process\n" 137 " a child of process 1.\n" 138 139 "--domainname new_domainname\n" 140 " Set the domainname (NIS) in the new security context.\n" 141 " Use \"none\" to unset the domain name.\n" 142 143 "--flag\n" 144 " Set one flag in the new or current security context. The following\n" 145 " flags are supported. The option may be used several time.\n" 146 "\n" 147 " fakeinit: The new process will believe it is process number 1.\n" 148 " Useful to run a real /sbin/init in a vserver.\n" 149 " lock: The new process is trapped and can't use chcontext anymore.\n" 150 " sched: The new process and its children will share a common \n" 151 " execution priority.\n" 152 " nproc: Limit the number of process in the vserver according to\n" 153 " ulimit setting. Normally, ulimit is a per user thing.\n" 154 " With this flag, it becomes a per vserver thing.\n" 155 " private: No one can join this security context once created.\n" 156 " ulimit: Apply the current ulimit to the whole context\n" 157 158 "--hostname new_hostname\n" 159 " Set the hostname in the new security context\n" 160 " This is need because if you create a less privileged\n" 161 " security context, it may be unable to change its hostname\n" 162 163 "--secure\n" 164 " Remove all the capabilities to make a virtual server trustable\n" 165 166 "--silent\n" 167 " Do not print the allocated context number.\n" 168 "\n" 169 "Please report bugs to " PACKAGE_BUGREPORT "\n"); 170 171 exit(res); 172 } 173 174 static void 175 showVersion() 176 { 177 WRITE_MSG(1, 178 "chcontext-compat " VERSION " -- allocates/enters a security context\n" 179 "This program is part of " PACKAGE_STRING "\n\n" 180 "Copyright (C) 2003,2004 Enrico Scholz\n" 181 VERSION_COPYRIGHT_DISCLAIMER); 182 exit(0); 183 } 184 185 #if defined(VC_ENABLE_API_COMPAT) || defined(VC_ENABLE_API_LEGACY) 186 187 static inline void 188 setCap(char const *str, uint32_t *add_caps, uint32_t *remove_caps) 189 { 190 uint32_t *cap; 191 int bit; 192 193 if (str[0] != '!') 194 cap = add_caps; 195 else { 196 cap = remove_caps; 197 str++; 198 } 199 200 bit = vc_text2cap(str); 201 202 if (bit!=-1) *cap |= (1<<bit); 203 else { 204 WRITE_MSG(2, "Unknown capability '"); 205 WRITE_STR(2, str); 206 WRITE_MSG(2, "'\n"); 207 exit(wrapper_exit_code); 208 } 209 } 210 211 static inline void 212 setFlags(char const *str, uint32_t *flags) 213 { 214 struct vc_err_listparser err; 215 216 *flags = vc_list2cflag_compat(str, 0, &err); 217 218 if (err.ptr!=0) { 219 WRITE_MSG(2, "Unknown flag '"); 220 Vwrite(2, err.ptr, err.len); 221 WRITE_MSG(2, "'\n"); 222 exit(wrapper_exit_code); 223 } 224 } 225 226 static inline ALWAYSINLINE void 227 setHostname(char const *name) 228 { 229 if (name == NULL) return; 230 231 if (sethostname(name, strlen(name))==-1) { 232 perror("chcontext: sethostname()"); 233 exit(255); 234 } 235 if (!global_args->do_silent) { 236 WRITE_MSG(1, "Host name is now "); 237 WRITE_STR(1, name); 238 WRITE_MSG(1, "\n"); 239 } 240 } 241 242 static inline ALWAYSINLINE void 243 setDomainname(char const *name) 244 { 245 if (name == NULL) return; 246 247 if (setdomainname(name, strlen(name))==-1) { 248 perror("chcontext: setdomainname()"); 249 exit(255); 250 } 251 if (!global_args->do_silent) { 252 WRITE_MSG(1, "Domain name is now "); 253 WRITE_STR(1, name); 254 WRITE_MSG(1, "\n"); 255 } 256 } 257 258 static inline ALWAYSINLINE void 259 tellContext(xid_t ctx) 260 { 261 char buf[sizeof(xid_t)*3+2]; 262 size_t l; 263 264 if (global_args->do_silent) return; 265 266 l = utilvserver_fmt_long(buf,ctx); 267 268 WRITE_MSG(1, "New security context is "); 269 Vwrite(1, buf, l); 270 WRITE_MSG(1, "\n"); 271 } 272 273 #include "context-sync.hc" 274 275 #endif 276 277 278 int main (int argc, char *argv[]) 279 { 280 struct Arguments args = { 281 .nbctx = 0, 282 .do_disconnect = false, 283 .do_silent = false, 284 .flags = 0, 285 .remove_caps = 0, 286 .add_caps = 0, 287 .hostname = 0, 288 .domainname = 0 289 }; 290 291 #if defined(VC_ENABLE_API_COMPAT) || defined(VC_ENABLE_API_LEGACY) 292 xid_t newctx; 293 int xflags; 294 int p[2][2]; 295 pid_t pid; 296 #endif 297 298 global_args = &args; 299 signal(SIGCHLD, SIG_DFL); 300 301 while (1) { 302 int c = getopt_long(argc, argv, "+", CMDLINE_OPTIONS, 0); 303 if (c==-1) break; 304 305 switch (c) { 306 case CMD_HELP : showHelp(1, argv[0], 0); 307 case CMD_VERSION : showVersion(); 308 case CMD_DISCONNECT : args.do_disconnect = true; break; 309 case CMD_SILENT : args.do_silent = true; break; 310 case CMD_DOMAINNAME : args.domainname = optarg; break; 311 case CMD_HOSTNAME : args.hostname = optarg; break; 312 313 #if defined(VC_ENABLE_API_COMPAT) || defined(VC_ENABLE_API_LEGACY) 314 case CMD_CAP : 315 setCap(optarg, &args.add_caps, &args.remove_caps); 316 break; 317 case CMD_SECURE : 318 args.remove_caps |= vc_get_insecurebcaps(); 319 break; 320 case CMD_FLAG : 321 setFlags(optarg, &args.flags); 322 break; 323 case CMD_CTX : 324 if (args.nbctx>0) 325 WRITE_MSG(2, "WARNING: More than one ctx not supported by this version\n"); 326 if (args.nbctx>=DIM_OF(args.ctxs)) { 327 WRITE_MSG(2, "Too many contexts given\n"); 328 exit(255); 329 } 330 args.ctxs[args.nbctx++] = Evc_xidopt2xid(optarg, true); 331 break; 332 #else 333 case CMD_CAP : 334 case CMD_SECURE : 335 case CMD_FLAG : 336 case CMD_CTX : break; 337 #endif 338 339 default : 340 WRITE_MSG(2, "Try '"); 341 WRITE_STR(2, argv[0]); 342 WRITE_MSG(2, " --help' for more information.\n"); 343 return 255; 344 break; 345 } 346 } 347 348 #if defined(VC_ENABLE_API_COMPAT) || defined(VC_ENABLE_API_LEGACY) 349 if (optind>=argc) { 350 WRITE_MSG(2, "No command given; use '--help' for more information.\n"); 351 exit(255); 352 } 353 354 if (args.domainname && strcmp(args.domainname, "none")==0) 355 args.domainname = ""; 356 357 if (args.nbctx == 0) 358 args.ctxs[args.nbctx++] = VC_DYNAMIC_XID; 359 360 xflags = args.flags & S_CTX_INFO_INIT; 361 args.flags &= ~S_CTX_INFO_INIT; 362 363 pid = initSync(p, args.do_disconnect); 364 if (pid==0) { 365 doSyncStage0(p, args.do_disconnect); 366 367 newctx = Evc_new_s_context(args.ctxs[0],0,args.flags); 368 args.remove_caps &= (~args.add_caps); 369 setHostname(args.hostname); 370 setDomainname(args.domainname); 371 372 if (args.remove_caps!=0 || xflags!=0) 373 Evc_new_s_context (VC_SAMECTX,args.remove_caps,xflags); 374 tellContext(args.ctxs[0]==VC_DYNAMIC_XID ? newctx : args.ctxs[0]); 375 376 doSyncStage1(p, args.do_disconnect); 377 execvp (argv[optind],argv+optind); 378 doSyncStage2(p, args.do_disconnect); 379 380 PERROR_Q("chcontext: execvp", argv[optind]); 381 exit(255); 382 } 383 384 waitOnSync(pid, p, args.ctxs[0]!=VC_DYNAMIC_XID); 385 return EXIT_SUCCESS; 386 #else 387 WRITE_MSG(2, "chcontext: tools were built without legacy API support; can not continue\n"); 388 return EXIT_FAILURE; 389 #endif 390 } 391 392 #if defined(VC_ENABLE_API_COMPAT) || defined(VC_ENABLE_API_LEGACY) 393 394 #ifdef ENSC_TESTSUITE 395 #define FLAG_TEST(STR,EXP) \ 396 { \ 397 uint32_t flag=0; \ 398 setFlags(STR, &flag); \ 399 assert(flag==(EXP)); \ 400 } 401 402 #define CAP_TEST(STR,EXP_ADD,EXP_DEL) \ 403 { \ 404 uint32_t add=0,del=0; \ 405 setCap(STR, &add, &del); \ 406 assert(add==(EXP_ADD)); \ 407 assert(del==(EXP_DEL)); \ 408 } 409 410 void 411 test() 412 { 413 FLAG_TEST("lock", 1); 414 FLAG_TEST("lock,sched", 3); 415 416 CAP_TEST("CHOWN", 1, 0); 417 CAP_TEST("CAP_CHOWN", 1, 0); 418 CAP_TEST("!CHOWN", 0, 1); 419 CAP_TEST("!CAP_CHOWN", 0, 1); 420 } 421 #endif 422 423 #else 424 void test() {} 425 #endif