busybox/runit/chpst.c
<<
>>
Prefs
   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