| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
|
|---|
| 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 |
|---|