1/* 2Copyright (c) 2001-2006, Gerrit Pape 3All rights reserved. 4 5Redistribution and use in source and binary forms, with or without 6modification, are permitted provided that the following conditions are met: 7 8 1. Redistributions of source code must retain the above copyright notice, 9 this list of conditions and the following disclaimer. 10 2. Redistributions in binary form must reproduce the above copyright 11 notice, this list of conditions and the following disclaimer in the 12 documentation and/or other materials provided with the distribution. 13 3. The name of the author may not be used to endorse or promote products 14 derived from this software without specific prior written permission. 15 16THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26*/ 27 28/* Busyboxed by Denys Vlasenko <vda.linux@googlemail.com> */ 29/* Dependencies on runit_lib.c removed */ 30 31#include "libbb.h" 32#include <dirent.h> 33 34/* 35Five applets here: chpst, envdir, envuidgid, setuidgid, softlimit. 36 37Only softlimit and chpst are taking options: 38 39# common 40-o N Limit number of open files per process 41-p N Limit number of processes per uid 42-m BYTES Same as -d BYTES -s BYTES -l BYTES [-a BYTES] 43-d BYTES Limit data segment 44-f BYTES Limit output file sizes 45-c BYTES Limit core file size 46# softlimit 47-a BYTES Limit total size of all segments 48-s BYTES Limit stack segment 49-l BYTES Limit locked memory size 50-r BYTES Limit resident set size 51-t N Limit CPU time 52# chpst 53-u USER[:GRP] Set uid and gid 54-U USER[:GRP] Set $UID and $GID in environment 55-e DIR Set environment variables as specified by files in DIR 56-/ DIR Chroot to DIR 57-n NICE Add NICE to nice value 58-v Verbose 59-P Create new process group 60-0 -1 -2 Close fd 0,1,2 61 62Even though we accept all these options for both softlimit and chpst, 63they are not to be advertised on their help texts. 64We have enough problems with feature creep in other people's 65software, don't want to add our own. 66 67envdir, envuidgid, setuidgid take no options, but they reuse code which 68handles -e, -U and -u. 69*/ 70 71enum { 72 OPT_a = (1 << 0) * ENABLE_SOFTLIMIT, 73 OPT_c = (1 << 1) * (ENABLE_SOFTLIMIT || ENABLE_CHPST), 74 OPT_d = (1 << 2) * (ENABLE_SOFTLIMIT || ENABLE_CHPST), 75 OPT_f = (1 << 3) * (ENABLE_SOFTLIMIT || ENABLE_CHPST), 76 OPT_l = (1 << 4) * ENABLE_SOFTLIMIT, 77 OPT_m = (1 << 5) * (ENABLE_SOFTLIMIT || ENABLE_CHPST), 78 OPT_o = (1 << 6) * (ENABLE_SOFTLIMIT || ENABLE_CHPST), 79 OPT_p = (1 << 7) * (ENABLE_SOFTLIMIT || ENABLE_CHPST), 80 OPT_r = (1 << 8) * ENABLE_SOFTLIMIT, 81 OPT_s = (1 << 9) * ENABLE_SOFTLIMIT, 82 OPT_t = (1 << 10) * ENABLE_SOFTLIMIT, 83 OPT_u = (1 << 11) * (ENABLE_CHPST || ENABLE_SETUIDGID), 84 OPT_U = (1 << 12) * (ENABLE_CHPST || ENABLE_ENVUIDGID), 85 OPT_e = (1 << 13) * (ENABLE_CHPST || ENABLE_ENVDIR), 86 OPT_root = (1 << 14) * ENABLE_CHPST, 87 OPT_n = (1 << 15) * ENABLE_CHPST, 88 OPT_v = (1 << 16) * ENABLE_CHPST, 89 OPT_P = (1 << 17) * ENABLE_CHPST, 90 OPT_0 = (1 << 18) * ENABLE_CHPST, 91 OPT_1 = (1 << 19) * ENABLE_CHPST, 92 OPT_2 = (1 << 20) * ENABLE_CHPST, 93}; 94 95/* TODO: use recursive_action? */ 96static NOINLINE void edir(const char *directory_name) 97{ 98 int wdir; 99 DIR *dir; 100 struct dirent *d; 101 int fd; 102 103 wdir = xopen(".", O_RDONLY | O_NDELAY); 104 xchdir(directory_name); 105 dir = xopendir("."); 106 for (;;) { 107 char buf[256]; 108 char *tail; 109 int size; 110 111 errno = 0; 112 d = readdir(dir); 113 if (!d) { 114 if (errno) 115 bb_perror_msg_and_die("readdir %s", 116 directory_name); 117 break; 118 } 119 if (d->d_name[0] == '.') 120 continue; 121 fd = open(d->d_name, O_RDONLY | O_NDELAY); 122 if (fd < 0) { 123 if ((errno == EISDIR) && directory_name) { 124 if (option_mask32 & OPT_v) 125 bb_perror_msg("warning: %s/%s is a directory", 126 directory_name, d->d_name); 127 continue; 128 } else 129 bb_perror_msg_and_die("open %s/%s", 130 directory_name, d->d_name); 131 } 132 size = full_read(fd, buf, sizeof(buf)-1); 133 close(fd); 134 if (size < 0) 135 bb_perror_msg_and_die("read %s/%s", 136 directory_name, d->d_name); 137 if (size == 0) { 138 unsetenv(d->d_name); 139 continue; 140 } 141 buf[size] = '\n'; 142 tail = strchr(buf, '\n'); 143 /* skip trailing whitespace */ 144 while (1) { 145 *tail = '\0'; 146 tail--; 147 if (tail < buf || !isspace(*tail)) 148 break; 149 } 150 xsetenv(d->d_name, buf); 151 } 152 closedir(dir); 153 if (fchdir(wdir) == -1) 154 bb_perror_msg_and_die("fchdir"); 155 close(wdir); 156} 157 158static void limit(int what, long l) 159{ 160 struct rlimit r; 161 162 /* Never fails under Linux (except if you pass it bad arguments) */ 163 getrlimit(what, &r); 164 if ((l < 0) || (l > r.rlim_max)) 165 r.rlim_cur = r.rlim_max; 166 else 167 r.rlim_cur = l; 168 if (setrlimit(what, &r) == -1) 169 bb_perror_msg_and_die("setrlimit"); 170} 171 172int chpst_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 173int chpst_main(int argc UNUSED_PARAM, char **argv) 174{ 175 struct bb_uidgid_t ugid; 176 char *set_user = set_user; /* for compiler */ 177 char *env_user = env_user; 178 char *env_dir = env_dir; 179 char *root; 180 char *nicestr; 181 unsigned limita; 182 unsigned limitc; 183 unsigned limitd; 184 unsigned limitf; 185 unsigned limitl; 186 unsigned limitm; 187 unsigned limito; 188 unsigned limitp; 189 unsigned limitr; 190 unsigned limits; 191 unsigned limitt; 192 unsigned opt; 193 194 if ((ENABLE_CHPST && applet_name[0] == 'c') 195 || (ENABLE_SOFTLIMIT && applet_name[1] == 'o') 196 ) { 197 // FIXME: can we live with int-sized limits? 198 // can we live with 40000 days? 199 // if yes -> getopt converts strings to numbers for us 200 opt_complementary = "-1:a+:c+:d+:f+:l+:m+:o+:p+:r+:s+:t+"; 201 opt = getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:u:U:e:" 202 IF_CHPST("/:n:vP012"), 203 &limita, &limitc, &limitd, &limitf, &limitl, 204 &limitm, &limito, &limitp, &limitr, &limits, &limitt, 205 &set_user, &env_user, &env_dir 206 IF_CHPST(, &root, &nicestr)); 207 argv += optind; 208 if (opt & OPT_m) { // -m means -asld 209 limita = limits = limitl = limitd = limitm; 210 opt |= (OPT_s | OPT_l | OPT_a | OPT_d); 211 } 212 } else { 213 option_mask32 = opt = 0; 214 argv++; 215 if (!*argv) 216 bb_show_usage(); 217 } 218 219 // envdir? 220 if (ENABLE_ENVDIR && applet_name[3] == 'd') { 221 env_dir = *argv++; 222 opt |= OPT_e; 223 } 224 225 // setuidgid? 226 if (ENABLE_SETUIDGID && applet_name[1] == 'e') { 227 set_user = *argv++; 228 opt |= OPT_u; 229 } 230 231 // envuidgid? 232 if (ENABLE_ENVUIDGID && applet_name[0] == 'e' && applet_name[3] == 'u') { 233 env_user = *argv++; 234 opt |= OPT_U; 235 } 236 237 // we must have PROG [ARGS] 238 if (!*argv) 239 bb_show_usage(); 240 241 // set limits 242 if (opt & OPT_d) { 243#ifdef RLIMIT_DATA 244 limit(RLIMIT_DATA, limitd); 245#else 246 if (opt & OPT_v) 247 bb_error_msg("system does not support RLIMIT_%s", 248 "DATA"); 249#endif 250 } 251 if (opt & OPT_s) { 252#ifdef RLIMIT_STACK 253 limit(RLIMIT_STACK, limits); 254#else 255 if (opt & OPT_v) 256 bb_error_msg("system does not support RLIMIT_%s", 257 "STACK"); 258#endif 259 } 260 if (opt & OPT_l) { 261#ifdef RLIMIT_MEMLOCK 262 limit(RLIMIT_MEMLOCK, limitl); 263#else 264 if (opt & OPT_v) 265 bb_error_msg("system does not support RLIMIT_%s", 266 "MEMLOCK"); 267#endif 268 } 269 if (opt & OPT_a) { 270#ifdef RLIMIT_VMEM 271 limit(RLIMIT_VMEM, limita); 272#else 273#ifdef RLIMIT_AS 274 limit(RLIMIT_AS, limita); 275#else 276 if (opt & OPT_v) 277 bb_error_msg("system does not support RLIMIT_%s", 278 "VMEM"); 279#endif 280#endif 281 } 282 if (opt & OPT_o) { 283#ifdef RLIMIT_NOFILE 284 limit(RLIMIT_NOFILE, limito); 285#else 286#ifdef RLIMIT_OFILE 287 limit(RLIMIT_OFILE, limito); 288#else 289 if (opt & OPT_v) 290 bb_error_msg("system does not support RLIMIT_%s", 291 "NOFILE"); 292#endif 293#endif 294 } 295 if (opt & OPT_p) { 296#ifdef RLIMIT_NPROC 297 limit(RLIMIT_NPROC, limitp); 298#else 299 if (opt & OPT_v) 300 bb_error_msg("system does not support RLIMIT_%s", 301 "NPROC"); 302#endif 303 } 304 if (opt & OPT_f) { 305#ifdef RLIMIT_FSIZE 306 limit(RLIMIT_FSIZE, limitf); 307#else 308 if (opt & OPT_v) 309 bb_error_msg("system does not support RLIMIT_%s", 310 "FSIZE"); 311#endif 312 } 313 if (opt & OPT_c) { 314#ifdef RLIMIT_CORE 315 limit(RLIMIT_CORE, limitc); 316#else 317 if (opt & OPT_v) 318 bb_error_msg("system does not support RLIMIT_%s", 319 "CORE"); 320#endif 321 } 322 if (opt & OPT_r) { 323#ifdef RLIMIT_RSS 324 limit(RLIMIT_RSS, limitr); 325#else 326 if (opt & OPT_v) 327 bb_error_msg("system does not support RLIMIT_%s", 328 "RSS"); 329#endif 330 } 331 if (opt & OPT_t) { 332#ifdef RLIMIT_CPU 333 limit(RLIMIT_CPU, limitt); 334#else 335 if (opt & OPT_v) 336 bb_error_msg("system does not support RLIMIT_%s", 337 "CPU"); 338#endif 339 } 340 341 if (opt & OPT_P) 342 setsid(); 343 344 if (opt & OPT_e) 345 edir(env_dir); 346 347 // FIXME: chrooted jail must have /etc/passwd if we move this after chroot! 348 // OTOH chroot fails for non-roots! 349 // SOLUTION: cache uid/gid before chroot, apply uid/gid after 350 if (opt & OPT_U) { 351 xget_uidgid(&ugid, env_user); 352 xsetenv("GID", utoa(ugid.gid)); 353 xsetenv("UID", utoa(ugid.uid)); 354 } 355 356 if (opt & OPT_u) { 357 xget_uidgid(&ugid, set_user); 358 } 359 360 if (opt & OPT_root) { 361 xchdir(root); 362 xchroot("."); 363 } 364 365 if (opt & OPT_u) { 366 if (setgroups(1, &ugid.gid) == -1) 367 bb_perror_msg_and_die("setgroups"); 368 xsetgid(ugid.gid); 369 xsetuid(ugid.uid); 370 } 371 372 if (opt & OPT_n) { 373 errno = 0; 374 if (nice(xatoi(nicestr)) == -1) 375 bb_perror_msg_and_die("nice"); 376 } 377 378 if (opt & OPT_0) 379 close(STDIN_FILENO); 380 if (opt & OPT_1) 381 close(STDOUT_FILENO); 382 if (opt & OPT_2) 383 close(STDERR_FILENO); 384 385 BB_EXECVP_or_die(argv); 386} 387