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