- first pass to unify/cleanup uid handling (-236b)
[platform/upstream/busybox.git] / runit / chpst.c
1 /*
2 Copyright (c) 2001-2006, Gerrit Pape
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, 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
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19 EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22 OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25 ADVISED 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
33 #include <dirent.h>
34
35 // Must match constants in chpst_main!
36 #define OPT_verbose  (option_mask32 & 0x2000)
37 #define OPT_pgrp     (option_mask32 & 0x4000)
38 #define OPT_nostdin  (option_mask32 & 0x8000)
39 #define OPT_nostdout (option_mask32 & 0x10000)
40 #define OPT_nostderr (option_mask32 & 0x20000)
41
42 struct globals {
43         char *set_user;
44         char *env_user;
45         const char *env_dir;
46         const char *root;
47         long limitd; /* limitX are initialized to -2 */
48         long limits;
49         long limitl;
50         long limita;
51         long limito;
52         long limitp;
53         long limitf;
54         long limitc;
55         long limitr;
56         long limitt;
57         int nicelvl;
58 };
59 #define G (*(struct globals*)&bb_common_bufsiz1)
60 #define set_user (G.set_user)
61 #define env_user (G.env_user)
62 #define env_dir  (G.env_dir )
63 #define root     (G.root    )
64 #define limitd   (G.limitd  )
65 #define limits   (G.limits  )
66 #define limitl   (G.limitl  )
67 #define limita   (G.limita  )
68 #define limito   (G.limito  )
69 #define limitp   (G.limitp  )
70 #define limitf   (G.limitf  )
71 #define limitc   (G.limitc  )
72 #define limitr   (G.limitr  )
73 #define limitt   (G.limitt  )
74 #define nicelvl  (G.nicelvl )
75 #define INIT_G() do { \
76         long *p = &limitd; \
77         do *p++ = -2; while (p <= &limitt); \
78 } while (0)
79
80 static void suidgid(char *user)
81 {
82         struct bb_uidgid_t ugid;
83
84         xget_uidgid(&ugid, user);
85         if (setgroups(1, &ugid.gid) == -1)
86                 bb_perror_msg_and_die("setgroups");
87         xsetgid(ugid.gid);
88         xsetuid(ugid.uid);
89 }
90
91 static void euidgid(char *user)
92 {
93         struct bb_uidgid_t ugid;
94
95         xget_uidgid(&ugid, user);
96         xsetenv("GID", utoa(ugid.gid));
97         xsetenv("UID", utoa(ugid.uid));
98 }
99
100 static void edir(const char *directory_name)
101 {
102         int wdir;
103         DIR *dir;
104         struct dirent *d;
105         int fd;
106
107         wdir = xopen(".", O_RDONLY | O_NDELAY);
108         xchdir(directory_name);
109         dir = opendir(".");
110         if (!dir)
111                 bb_perror_msg_and_die("opendir %s", directory_name);
112         for (;;) {
113                 char buf[256];
114                 char *tail;
115                 int size;
116
117                 errno = 0;
118                 d = readdir(dir);
119                 if (!d) {
120                         if (errno)
121                                 bb_perror_msg_and_die("readdir %s",
122                                                 directory_name);
123                         break;
124                 }
125                 if (d->d_name[0] == '.')
126                         continue;
127                 fd = open(d->d_name, O_RDONLY | O_NDELAY);
128                 if (fd < 0) {
129                         if ((errno == EISDIR) && env_dir) {
130                                 if (OPT_verbose)
131                                         bb_perror_msg("warning: %s/%s is a directory",
132                                                 directory_name, d->d_name);
133                                 continue;
134                         } else
135                                 bb_perror_msg_and_die("open %s/%s",
136                                                 directory_name, d->d_name);
137                 }
138                 size = full_read(fd, buf, sizeof(buf)-1);
139                 close(fd);
140                 if (size < 0)
141                         bb_perror_msg_and_die("read %s/%s",
142                                         directory_name, d->d_name);
143                 if (size == 0) {
144                         unsetenv(d->d_name);
145                         continue;
146                 }
147                 buf[size] = '\n';
148                 tail = strchr(buf, '\n');
149                 /* skip trailing whitespace */
150                 while (1) {
151                         *tail = '\0';
152                         tail--;
153                         if (tail < buf || !isspace(*tail))
154                                 break;
155                 }
156                 xsetenv(d->d_name, buf);
157         }
158         closedir(dir);
159         if (fchdir(wdir) == -1)
160                 bb_perror_msg_and_die("fchdir");
161         close(wdir);
162 }
163
164 static void limit(int what, long l)
165 {
166         struct rlimit r;
167
168         /* Never fails under Linux (except if you pass it bad arguments) */
169         getrlimit(what, &r);
170         if ((l < 0) || (l > r.rlim_max))
171                 r.rlim_cur = r.rlim_max;
172         else
173                 r.rlim_cur = l;
174         if (setrlimit(what, &r) == -1)
175                 bb_perror_msg_and_die("setrlimit");
176 }
177
178 static void slimit(void)
179 {
180         if (limitd >= -1) {
181 #ifdef RLIMIT_DATA
182                 limit(RLIMIT_DATA, limitd);
183 #else
184                 if (OPT_verbose)
185                         bb_error_msg("system does not support RLIMIT_%s",
186                                 "DATA");
187 #endif
188         }
189         if (limits >= -1) {
190 #ifdef RLIMIT_STACK
191                 limit(RLIMIT_STACK, limits);
192 #else
193                 if (OPT_verbose)
194                         bb_error_msg("system does not support RLIMIT_%s",
195                                 "STACK");
196 #endif
197         }
198         if (limitl >= -1) {
199 #ifdef RLIMIT_MEMLOCK
200                 limit(RLIMIT_MEMLOCK, limitl);
201 #else
202                 if (OPT_verbose)
203                         bb_error_msg("system does not support RLIMIT_%s",
204                                 "MEMLOCK");
205 #endif
206         }
207         if (limita >= -1) {
208 #ifdef RLIMIT_VMEM
209                 limit(RLIMIT_VMEM, limita);
210 #else
211 #ifdef RLIMIT_AS
212                 limit(RLIMIT_AS, limita);
213 #else
214                 if (OPT_verbose)
215                         bb_error_msg("system does not support RLIMIT_%s",
216                                 "VMEM");
217 #endif
218 #endif
219         }
220         if (limito >= -1) {
221 #ifdef RLIMIT_NOFILE
222                 limit(RLIMIT_NOFILE, limito);
223 #else
224 #ifdef RLIMIT_OFILE
225                 limit(RLIMIT_OFILE, limito);
226 #else
227                 if (OPT_verbose)
228                         bb_error_msg("system does not support RLIMIT_%s",
229                                 "NOFILE");
230 #endif
231 #endif
232         }
233         if (limitp >= -1) {
234 #ifdef RLIMIT_NPROC
235                 limit(RLIMIT_NPROC, limitp);
236 #else
237                 if (OPT_verbose)
238                         bb_error_msg("system does not support RLIMIT_%s",
239                                 "NPROC");
240 #endif
241         }
242         if (limitf >= -1) {
243 #ifdef RLIMIT_FSIZE
244                 limit(RLIMIT_FSIZE, limitf);
245 #else
246                 if (OPT_verbose)
247                         bb_error_msg("system does not support RLIMIT_%s",
248                                 "FSIZE");
249 #endif
250         }
251         if (limitc >= -1) {
252 #ifdef RLIMIT_CORE
253                 limit(RLIMIT_CORE, limitc);
254 #else
255                 if (OPT_verbose)
256                         bb_error_msg("system does not support RLIMIT_%s",
257                                 "CORE");
258 #endif
259         }
260         if (limitr >= -1) {
261 #ifdef RLIMIT_RSS
262                 limit(RLIMIT_RSS, limitr);
263 #else
264                 if (OPT_verbose)
265                         bb_error_msg("system does not support RLIMIT_%s",
266                                 "RSS");
267 #endif
268         }
269         if (limitt >= -1) {
270 #ifdef RLIMIT_CPU
271                 limit(RLIMIT_CPU, limitt);
272 #else
273                 if (OPT_verbose)
274                         bb_error_msg("system does not support RLIMIT_%s",
275                                 "CPU");
276 #endif
277         }
278 }
279
280 /* argv[0] */
281 static void setuidgid(int, char **) NORETURN;
282 static void envuidgid(int, char **) NORETURN;
283 static void envdir(int, char **) NORETURN;
284 static void softlimit(int, char **) NORETURN;
285
286 int chpst_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
287 int chpst_main(int argc UNUSED_PARAM, char **argv)
288 {
289         INIT_G();
290
291         if (applet_name[3] == 'd') envdir(argc, argv);
292         if (applet_name[1] == 'o') softlimit(argc, argv);
293         if (applet_name[0] == 's') setuidgid(argc, argv);
294         if (applet_name[0] == 'e') envuidgid(argc, argv);
295         // otherwise we are chpst
296
297         {
298                 char *m,*d,*o,*p,*f,*c,*r,*t,*n;
299                 getopt32(argv, "+u:U:e:m:d:o:p:f:c:r:t:/:n:vP012",
300                                 &set_user,&env_user,&env_dir,
301                                 &m,&d,&o,&p,&f,&c,&r,&t,&root,&n);
302                 // if (option_mask32 & 0x1) // -u
303                 // if (option_mask32 & 0x2) // -U
304                 // if (option_mask32 & 0x4) // -e
305                 if (option_mask32 & 0x8) limits = limitl = limita = limitd = xatoul(m); // -m
306                 if (option_mask32 & 0x10) limitd = xatoul(d); // -d
307                 if (option_mask32 & 0x20) limito = xatoul(o); // -o
308                 if (option_mask32 & 0x40) limitp = xatoul(p); // -p
309                 if (option_mask32 & 0x80) limitf = xatoul(f); // -f
310                 if (option_mask32 & 0x100) limitc = xatoul(c); // -c
311                 if (option_mask32 & 0x200) limitr = xatoul(r); // -r
312                 if (option_mask32 & 0x400) limitt = xatoul(t); // -t
313                 // if (option_mask32 & 0x800) // -/
314                 if (option_mask32 & 0x1000) nicelvl = xatoi(n); // -n
315                 // The below consts should match #defines at top!
316                 //if (option_mask32 & 0x2000) OPT_verbose = 1; // -v
317                 //if (option_mask32 & 0x4000) OPT_pgrp = 1; // -P
318                 //if (option_mask32 & 0x8000) OPT_nostdin = 1; // -0
319                 //if (option_mask32 & 0x10000) OPT_nostdout = 1; // -1
320                 //if (option_mask32 & 0x20000) OPT_nostderr = 1; // -2
321         }
322         argv += optind;
323         if (!argv || !*argv) bb_show_usage();
324
325         if (OPT_pgrp) setsid();
326         if (env_dir) edir(env_dir);
327         if (root) {
328                 xchdir(root);
329                 xchroot(".");
330         }
331         slimit();
332         if (nicelvl) {
333                 errno = 0;
334                 if (nice(nicelvl) == -1)
335                         bb_perror_msg_and_die("nice");
336         }
337         if (env_user) euidgid(env_user);
338         if (set_user) suidgid(set_user);
339         if (OPT_nostdin) close(0);
340         if (OPT_nostdout) close(1);
341         if (OPT_nostderr) close(2);
342         BB_EXECVP(argv[0], argv);
343         bb_perror_msg_and_die("exec %s", argv[0]);
344 }
345
346 static void setuidgid(int argc UNUSED_PARAM, char **argv)
347 {
348         const char *account;
349
350         account = *++argv;
351         if (!account) bb_show_usage();
352         if (!*++argv) bb_show_usage();
353         suidgid((char*)account);
354         BB_EXECVP(argv[0], argv);
355         bb_perror_msg_and_die("exec %s", argv[0]);
356 }
357
358 static void envuidgid(int argc UNUSED_PARAM, char **argv)
359 {
360         const char *account;
361
362         account = *++argv;
363         if (!account) bb_show_usage();
364         if (!*++argv) bb_show_usage();
365         euidgid((char*)account);
366         BB_EXECVP(argv[0], argv);
367         bb_perror_msg_and_die("exec %s", argv[0]);
368 }
369
370 static void envdir(int argc UNUSED_PARAM, char **argv)
371 {
372         const char *dir;
373
374         dir = *++argv;
375         if (!dir) bb_show_usage();
376         if (!*++argv) bb_show_usage();
377         edir(dir);
378         BB_EXECVP(argv[0], argv);
379         bb_perror_msg_and_die("exec %s", argv[0]);
380 }
381
382 static void softlimit(int argc UNUSED_PARAM, char **argv)
383 {
384         char *a,*c,*d,*f,*l,*m,*o,*p,*r,*s,*t;
385         getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:",
386                         &a,&c,&d,&f,&l,&m,&o,&p,&r,&s,&t);
387         if (option_mask32 & 0x001) limita = xatoul(a); // -a
388         if (option_mask32 & 0x002) limitc = xatoul(c); // -c
389         if (option_mask32 & 0x004) limitd = xatoul(d); // -d
390         if (option_mask32 & 0x008) limitf = xatoul(f); // -f
391         if (option_mask32 & 0x010) limitl = xatoul(l); // -l
392         if (option_mask32 & 0x020) limits = limitl = limita = limitd = xatoul(m); // -m
393         if (option_mask32 & 0x040) limito = xatoul(o); // -o
394         if (option_mask32 & 0x080) limitp = xatoul(p); // -p
395         if (option_mask32 & 0x100) limitr = xatoul(r); // -r
396         if (option_mask32 & 0x200) limits = xatoul(s); // -s
397         if (option_mask32 & 0x400) limitt = xatoul(t); // -t
398         argv += optind;
399         if (!argv[0]) bb_show_usage();
400         slimit();
401         BB_EXECVP(argv[0], argv);
402         bb_perror_msg_and_die("exec %s", argv[0]);
403 }