chpst.c (14204B)
1 #include <sys/types.h> 2 #include <time.h> 3 #include <sys/time.h> 4 #include <sys/resource.h> 5 #include <unistd.h> 6 #include <grp.h> 7 #include <sys/types.h> 8 #include <dirent.h> 9 #include "sgetopt.h" 10 #include "error.h" 11 #include "strerr.h" 12 #include "str.h" 13 #include "uidgid.h" 14 #include "strerr.h" 15 #include "scan.h" 16 #include "fmt.h" 17 #include "lock.h" 18 #include "pathexec.h" 19 #include "stralloc.h" 20 #include "byte.h" 21 #include "open.h" 22 #include "openreadclose.h" 23 24 #define USAGE_MAIN " [-vP012] [-u user[:group]] [-U user[:group]] [-b argv0] [-e dir] [-/ root] [-C pwd] [-n nice] [-l|-L lock] [-m n] [-d n] [-o n] [-p n] [-f n] [-c n] prog" 25 #define FATAL "chpst: fatal: " 26 #define WARNING "chpst: warning: " 27 28 static const char *progname; 29 static stralloc sa; 30 31 static void fatal(const char *m) { strerr_die3sys(111, FATAL, m, ": "); } 32 static void fatal2(const char *m0, const char *m1) 33 { 34 strerr_die5sys(111, FATAL, m0, ": ", m1, ": "); 35 } 36 static void fatalx(const char *m0, const char *m1) 37 { 38 strerr_die4x(111, FATAL, m0, ": ", m1); 39 } 40 static void warn(const char *m) { strerr_warn2(WARNING, m, 0); } 41 static void die_nomem( void ) { strerr_die2x(111, FATAL, "out of memory."); } 42 static void usage( void ) { strerr_die4x(100, "usage: ", progname, USAGE_MAIN, "\n"); } 43 44 static char *set_user =0; 45 static char *env_user =0; 46 static const char *argv0 =0; 47 static const char *env_dir =0; 48 static unsigned int verbose =0; 49 static unsigned int pgrp =0; 50 static unsigned int nostdin =0; 51 static unsigned int nostdout =0; 52 static unsigned int nostderr =0; 53 static long limitd =-2; 54 static long limits =-2; 55 static long limitl =-2; 56 static long limita =-2; 57 static long limito =-2; 58 static long limitp =-2; 59 static long limitf =-2; 60 static long limitc =-2; 61 static long limitr =-2; 62 static long limitt =-2; 63 static long nicelvl =0; 64 static const char *lock =0; 65 static const char *root =0; 66 static const char *pwd =0; 67 static unsigned int lockdelay; 68 69 static void suidgid(char *user, unsigned int ext) 70 { 71 struct uidgid ugid; 72 73 if (ext) { 74 if (! uidgids_get(&ugid, user)) { 75 if (*user == ':') fatalx("invalid uid/gids", user +1); 76 if (errno) fatal("unable to get password/group file entry"); 77 fatalx("unknown user/group", user); 78 } 79 } 80 else 81 if (! uidgid_get(&ugid, user)) { 82 if (errno) fatal("unable to get password file entry"); 83 fatalx("unknown account", user); 84 } 85 if (setgroups(ugid.gids, ugid.gid) == -1) fatal("unable to setgroups"); 86 if (setgid(*ugid.gid) == -1) fatal("unable to setgid"); 87 if (setuid(ugid.uid) == -1) fatal("unable to setuid"); 88 } 89 90 static void euidgid(char *user, unsigned int ext) 91 { 92 struct uidgid ugid; 93 char bufnum[FMT_ULONG]; 94 95 if (ext) { 96 if (! uidgids_get(&ugid, user)) { 97 if (*user == ':') fatalx("invalid uid/gids", user +1); 98 if (errno) fatal("unable to get password/group file entry"); 99 fatalx("unknown user/group", user); 100 } 101 } 102 else 103 if (! uidgid_get(&ugid, user)) { 104 if (errno) fatal("unable to get password file entry"); 105 fatalx("unknown account", user); 106 } 107 bufnum[fmt_ulong(bufnum, *ugid.gid)] =0; 108 if (! pathexec_env("GID", bufnum)) die_nomem(); 109 bufnum[fmt_ulong(bufnum, ugid.uid)] =0; 110 if (! pathexec_env("UID", bufnum)) die_nomem(); 111 } 112 113 static void edir(const char *dirname) 114 { 115 int wdir; 116 DIR *dir; 117 struct dirent *d; 118 int i; 119 120 if ((wdir =open_read(".")) == -1) 121 fatal("unable to open current working directory"); 122 if (chdir(dirname)) fatal2("unable to switch to directory", dirname); 123 if (! (dir =opendir("."))) fatal2("unable to open directory", dirname); 124 for (;;) { 125 errno =0; 126 d =readdir(dir); 127 if (! d) { 128 if (errno) fatal2("unable to read directory", dirname); 129 break; 130 } 131 if (d->d_name[0] == '.') continue; 132 if (openreadclose(d->d_name, &sa, 256) == -1) { 133 if ((errno == error_isdir) && env_dir) { 134 if (verbose) 135 strerr_warn6(WARNING, "unable to read ", dirname, "/", 136 d->d_name, ": ", &strerr_sys); 137 continue; 138 } 139 else 140 strerr_die6sys(111, FATAL, "unable to read ", dirname, "/", 141 d->d_name, ": "); 142 } 143 if (sa.len) { 144 sa.len =byte_chr(sa.s, sa.len, '\n'); 145 while (sa.len && (sa.s[sa.len -1] == ' ' || sa.s[sa.len -1] == '\t')) 146 --sa.len; 147 for (i =0; i < sa.len; ++i) if (! sa.s[i]) sa.s[i] ='\n'; 148 if (! stralloc_0(&sa)) die_nomem(); 149 if (! pathexec_env(d->d_name, sa.s)) die_nomem(); 150 } 151 else 152 if (! pathexec_env(d->d_name, 0)) die_nomem(); 153 } 154 closedir(dir); 155 if (fchdir(wdir) == -1) fatal("unable to switch to starting directory"); 156 close(wdir); 157 } 158 159 static void slock_die(const char *m, const char *f, unsigned int x) 160 { 161 if (! x) fatal2(m, f); 162 _exit(0); 163 } 164 static void slock(const char *f, unsigned int d, unsigned int x) 165 { 166 int fd; 167 168 if ((fd =open_append(f)) == -1) slock_die("unable to open lock", f, x); 169 if (d) { 170 if (lock_ex(fd) == -1) slock_die("unable to lock", f, x); 171 return; 172 } 173 if (lock_exnb(fd) == -1) slock_die("unable to lock", f, x); 174 } 175 176 static void limit(int what, long l) 177 { 178 struct rlimit r; 179 180 if (getrlimit(what, &r) == -1) fatal("unable to getrlimit()"); 181 if ((l < 0) || (l > r.rlim_max)) 182 r.rlim_cur =r.rlim_max; 183 else 184 r.rlim_cur =l; 185 if (setrlimit(what, &r) == -1) fatal("unable to setrlimit()"); 186 } 187 static void slimit() 188 { 189 if (limitd >= -1) { 190 #ifdef RLIMIT_DATA 191 limit(RLIMIT_DATA, limitd); 192 #else 193 if (verbose) warn("system does not support RLIMIT_DATA"); 194 #endif 195 } 196 if (limits >= -1) { 197 #ifdef RLIMIT_STACK 198 limit(RLIMIT_STACK, limits); 199 #else 200 if (verbose) warn("system does not support RLIMIT_STACK"); 201 #endif 202 } 203 if (limitl >= -1) { 204 #ifdef RLIMIT_MEMLOCK 205 limit(RLIMIT_MEMLOCK, limitl); 206 #else 207 if (verbose) warn("system does not support RLIMIT_MEMLOCK"); 208 #endif 209 } 210 if (limita >= -1) { 211 #ifdef RLIMIT_VMEM 212 limit(RLIMIT_VMEM, limita); 213 #else 214 #ifdef RLIMIT_AS 215 limit(RLIMIT_AS, limita); 216 #else 217 if (verbose) 218 warn("system does neither support RLIMIT_VMEM nor RLIMIT_AS"); 219 #endif 220 #endif 221 } 222 if (limito >= -1) { 223 #ifdef RLIMIT_NOFILE 224 limit(RLIMIT_NOFILE, limito); 225 #else 226 #ifdef RLIMIT_OFILE 227 limit(RLIMIT_OFILE, limito); 228 #else 229 if (verbose) 230 warn("system does neither support RLIMIT_NOFILE nor RLIMIT_OFILE"); 231 #endif 232 #endif 233 } 234 if (limitp >= -1) { 235 #ifdef RLIMIT_NPROC 236 limit(RLIMIT_NPROC, limitp); 237 #else 238 if (verbose) warn("system does not support RLIMIT_NPROC"); 239 #endif 240 } 241 if (limitf >= -1) { 242 #ifdef RLIMIT_FSIZE 243 limit(RLIMIT_FSIZE, limitf); 244 #else 245 if (verbose) warn("system does not support RLIMIT_FSIZE"); 246 #endif 247 } 248 if (limitc >= -1) { 249 #ifdef RLIMIT_CORE 250 limit(RLIMIT_CORE, limitc); 251 #else 252 if (verbose) warn("system does not support RLIMIT_CORE"); 253 #endif 254 } 255 if (limitr >= -1) { 256 #ifdef RLIMIT_RSS 257 limit(RLIMIT_RSS, limitr); 258 #else 259 if (verbose) warn("system does not support RLIMIT_RSS"); 260 #endif 261 } 262 if (limitt >= -1) { 263 #ifdef RLIMIT_CPU 264 limit(RLIMIT_CPU, limitt); 265 #else 266 if (verbose) warn("system does not support RLIMIT_CPU"); 267 #endif 268 } 269 } 270 271 /* argv[0] */ 272 static void setuidgid(int, const char *const *); 273 static void envuidgid(int, const char *const *); 274 static void envdir(int, const char *const *); 275 static void pgrphack(int, const char *const *); 276 static void setlock(int, const char *const *); 277 static void softlimit(int, const char *const *); 278 279 int main(int argc, const char **argv) { 280 int opt; 281 int i; 282 unsigned long ul; 283 284 progname =argv[0]; 285 for (i =str_len(progname); i; --i) 286 if (progname[i -1] == '/') { 287 progname +=i; 288 break; 289 } 290 if (progname[0] == 'd') ++progname; 291 292 /* argv[0] */ 293 if (str_equal(progname, "setuidgid")) setuidgid(argc, argv); 294 if (str_equal(progname, "envuidgid")) envuidgid(argc, argv); 295 if (str_equal(progname, "envdir")) envdir(argc, argv); 296 if (str_equal(progname, "pgrphack")) pgrphack(argc, argv); 297 if (str_equal(progname, "setlock")) setlock(argc, argv); 298 if (str_equal(progname, "softlimit")) softlimit(argc, argv); 299 300 while ((opt =getopt(argc, argv, "u:U:b:e:m:d:o:p:f:c:r:t:/:C:n:l:L:vP012V")) 301 != opteof) 302 switch(opt) { 303 case 'u': set_user =(char const*)optarg; break; 304 case 'U': env_user =(char const*)optarg; break; 305 case 'b': argv0 =(char const*)optarg; break; 306 case 'e': env_dir =optarg; break; 307 case 'm': 308 if (optarg[scan_ulong(optarg, &ul)]) usage(); 309 limits =limitl =limita =limitd =ul; 310 break; 311 case 'd': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitd =ul; break; 312 case 'o': if (optarg[scan_ulong(optarg, &ul)]) usage(); limito =ul; break; 313 case 'p': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitp =ul; break; 314 case 'f': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitf =ul; break; 315 case 'c': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitc =ul; break; 316 case 'r': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitr =ul; break; 317 case 't': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitt =ul; break; 318 case '/': root =optarg; break; 319 case 'C': pwd =optarg; break; 320 case 'n': 321 switch (*optarg) { 322 case '-': 323 ++optarg; 324 if (optarg[scan_ulong(optarg, &ul)]) usage(); nicelvl =ul; 325 nicelvl *=-1; 326 break; 327 case '+': 328 ++optarg; 329 __attribute__((fallthrough)); 330 default: 331 if (optarg[scan_ulong(optarg, &ul)]) usage(); nicelvl =ul; 332 break; 333 } 334 break; 335 case 'l': if (lock) usage(); lock =optarg; lockdelay =1; break; 336 case 'L': if (lock) usage(); lock =optarg; lockdelay =0; break; 337 case 'v': verbose =1; break; 338 case 'P': pgrp =1; break; 339 case '0': nostdin =1; break; 340 case '1': nostdout =1; break; 341 case '2': nostderr =1; break; 342 case 'V': 343 strerr_warn1(VERSION, 0); 344 __attribute__((fallthrough)); 345 case '?': usage(); 346 } 347 argv +=optind; 348 if (! argv || ! *argv) usage(); 349 350 if (pgrp) setsid(); 351 if (env_dir) edir(env_dir); 352 if (root) { 353 if (chdir(root) == -1) fatal2("unable to change directory", root); 354 if (chroot(".") == -1) fatal("unable to change root directory"); 355 } 356 if (pwd) { 357 if (chdir(pwd) == -1) fatal2("unable to change directory", pwd); 358 } 359 if (nicelvl) { 360 errno =0; 361 if (nice(nicelvl) == -1) if (errno) fatal("unable to set nice level"); 362 } 363 if (env_user) euidgid(env_user, 1); 364 if (set_user) suidgid(set_user, 1); 365 if (lock) slock(lock, lockdelay, 0); 366 if (nostdin) if (close(0) == -1) fatal("unable to close stdin"); 367 if (nostdout) if (close(1) == -1) fatal("unable to close stdout"); 368 if (nostderr) if (close(2) == -1) fatal("unable to close stderr"); 369 slimit(); 370 371 progname =*argv; 372 if (argv0) *argv =argv0; 373 pathexec_env_run(progname, argv); 374 fatal2("unable to run", *argv); 375 return(0); 376 } 377 378 /* argv[0] */ 379 #define USAGE_SETUIDGID " account child" 380 #define USAGE_ENVUIDGID " account child" 381 #define USAGE_ENVDIR " dir child" 382 #define USAGE_PGRPHACK " child" 383 #define USAGE_SETLOCK " [ -nNxX ] file program [ arg ... ]" 384 #define USAGE_SOFTLIMIT " [-a allbytes] [-c corebytes] [-d databytes] [-f filebytes] [-l lockbytes] [-m membytes] [-o openfiles] [-p processes] [-r residentbytes] [-s stackbytes] [-t cpusecs] child" 385 386 static void setuidgid_usage() 387 { 388 strerr_die4x(100, "usage: ", progname, USAGE_SETUIDGID, "\n"); 389 } 390 static void setuidgid(int argc, const char *const *argv) 391 { 392 const char *account; 393 394 if (! (account =*++argv)) setuidgid_usage(); 395 if (! *++argv) setuidgid_usage(); 396 suidgid((char const*)account, 0); 397 pathexec(argv); 398 fatal2("unable to run", *argv); 399 } 400 401 static void envuidgid_usage() 402 { 403 strerr_die4x(100, "usage: ", progname, USAGE_ENVUIDGID, "\n"); 404 } 405 static void envuidgid(int argc, const char *const *argv) 406 { 407 const char *account; 408 409 if (! (account =*++argv)) envuidgid_usage(); 410 if (! *++argv) envuidgid_usage(); 411 euidgid((char const*)account, 0); 412 pathexec(argv); 413 fatal2("unable to run", *argv); 414 } 415 416 static void envdir_usage() 417 { 418 strerr_die4x(100, "usage: ", progname, USAGE_ENVDIR, "\n"); 419 } 420 static void envdir(int argc, const char *const *argv) 421 { 422 const char *dir; 423 424 if (! (dir =*++argv)) envdir_usage(); 425 if (! *++argv) envdir_usage(); 426 edir(dir); 427 pathexec(argv); 428 fatal2("unable to run", *argv); 429 } 430 431 static void pgrphack_usage() 432 { 433 strerr_die4x(100, "usage: ", progname, USAGE_PGRPHACK, "\n"); 434 } 435 static void pgrphack(int argc, const char *const *argv) 436 { 437 if (! *++argv) pgrphack_usage(); 438 setsid(); 439 pathexec(argv); 440 fatal2("unable to run", *argv); 441 } 442 443 static void setlock_usage() 444 { 445 strerr_die4x(100, "usage: ", progname, USAGE_SETLOCK, "\n"); 446 } 447 static void setlock(int argc, const char *const *argv) 448 { 449 int opt; 450 unsigned int delay =0; 451 unsigned int x =0; 452 const char *fn; 453 454 while ((opt =getopt(argc, argv, "nNxX")) != opteof) 455 switch(opt) { 456 case 'n': delay =1; break; 457 case 'N': delay =0; break; 458 case 'x': x =1; break; 459 case 'X': x =0; break; 460 default: setlock_usage(); 461 } 462 argv +=optind; 463 if (! (fn =*argv)) setlock_usage(); 464 if (! *++argv) setlock_usage(); 465 466 slock(fn, delay, x); 467 pathexec(argv); 468 if (! x) fatal2("unable to run", *argv); 469 _exit(0); 470 } 471 472 static void softlimit_usage() 473 { 474 strerr_die4x(100, "usage: ", progname, USAGE_SOFTLIMIT, "\n"); 475 } 476 static void getlarg(long *l) 477 { 478 unsigned long ul; 479 480 if (str_equal(optarg, "=")) { *l =-1; return; } 481 if (optarg[scan_ulong(optarg, &ul)]) usage(); 482 *l =ul; 483 } 484 static void softlimit(int argc, const char *const *argv) 485 { 486 int opt; 487 488 while ((opt =getopt(argc,argv,"a:c:d:f:l:m:o:p:r:s:t:")) != opteof) 489 switch(opt) { 490 case '?': 491 softlimit_usage(); 492 __attribute__((fallthrough)); 493 case 'a': getlarg(&limita); break; 494 case 'c': getlarg(&limitc); break; 495 case 'd': getlarg(&limitd); break; 496 case 'f': getlarg(&limitf); break; 497 case 'l': getlarg(&limitl); break; 498 case 'm': getlarg(&limitd); limits =limitl =limita =limitd; break; 499 case 'o': getlarg(&limito); break; 500 case 'p': getlarg(&limitp); break; 501 case 'r': getlarg(&limitr); break; 502 case 's': getlarg(&limits); break; 503 case 't': getlarg(&limitt); break; 504 } 505 argv +=optind; 506 if (!*argv) softlimit_usage(); 507 slimit(); 508 pathexec(argv); 509 fatal2("unable to run", *argv); 510 }