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 Denis 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
42static char *set_user;
43static char *env_user;
44static const char *env_dir;
45static long limitd = -2;
46static long limits = -2;
47static long limitl = -2;
48static long limita = -2;
49static long limito = -2;
50static long limitp = -2;
51static long limitf = -2;
52static long limitc = -2;
53static long limitr = -2;
54static long limitt = -2;
55static int nicelvl;
56static const char *root;
57
58static void suidgid(char *user)
59{
60	struct bb_uidgid_t ugid;
61
62	if (!get_uidgid(&ugid, user, 1)) {
63		bb_error_msg_and_die("unknown user/group: %s", user);
64	}
65	if (setgroups(1, &ugid.gid) == -1)
66		bb_perror_msg_and_die("setgroups");
67	xsetgid(ugid.gid);
68	xsetuid(ugid.uid);
69}
70
71static void euidgid(char *user)
72{
73	struct bb_uidgid_t ugid;
74
75	if (!get_uidgid(&ugid, user, 1)) {
76		bb_error_msg_and_die("unknown user/group: %s", user);
77	}
78	xsetenv("GID", utoa(ugid.gid));
79	xsetenv("UID", utoa(ugid.uid));
80}
81
82static void edir(const char *directory_name)
83{
84	int wdir;
85	DIR *dir;
86	struct dirent *d;
87	int fd;
88
89	wdir = xopen(".", O_RDONLY | O_NDELAY);
90	xchdir(directory_name);
91	dir = opendir(".");
92	if (!dir)
93		bb_perror_msg_and_die("opendir %s", directory_name);
94	for (;;) {
95		errno = 0;
96		d = readdir(dir);
97		if (!d) {
98			if (errno)
99				bb_perror_msg_and_die("readdir %s",
100						directory_name);
101			break;
102		}
103		if (d->d_name[0] == '.') continue;
104		fd = open(d->d_name, O_RDONLY | O_NDELAY);
105		if (fd < 0) {
106			if ((errno == EISDIR) && env_dir) {
107				if (OPT_verbose)
108					bb_perror_msg("warning: %s/%s is a directory",
109						directory_name,	d->d_name);
110				continue;
111			} else
112				bb_perror_msg_and_die("open %s/%s",
113						directory_name, d->d_name);
114		}
115		if (fd >= 0) {
116			char buf[256];
117			char *tail;
118			int size;
119
120			size = safe_read(fd, buf, sizeof(buf)-1);
121			if (size < 0)
122				bb_perror_msg_and_die("read %s/%s",
123						directory_name, d->d_name);
124			if (size == 0) {
125				unsetenv(d->d_name);
126				continue;
127			}
128			buf[size] = '\n';
129			tail = memchr(buf, '\n', sizeof(buf));
130			/* skip trailing whitespace */;
131			while (1) {
132				if (tail[0]==' ') tail[0] = '\0';
133				if (tail[0]=='\t') tail[0] = '\0';
134				if (tail[0]=='\n') tail[0] = '\0';
135				if (tail == buf) break;
136				tail--;
137			}
138			xsetenv(d->d_name, buf);
139		}
140	}
141	closedir(dir);
142	if (fchdir(wdir) == -1) bb_perror_msg_and_die("fchdir");
143	close(wdir);
144}
145
146static void limit(int what, long l)
147{
148	struct rlimit r;
149
150	if (getrlimit(what, &r) == -1) bb_perror_msg_and_die("getrlimit");
151	if ((l < 0) || (l > r.rlim_max))
152		r.rlim_cur = r.rlim_max;
153	else
154		r.rlim_cur = l;
155	if (setrlimit(what, &r) == -1) bb_perror_msg_and_die("setrlimit");
156}
157
158static void slimit(void)
159{
160	if (limitd >= -1) {
161#ifdef RLIMIT_DATA
162		limit(RLIMIT_DATA, limitd);
163#else
164		if (OPT_verbose) bb_error_msg("system does not support %s",
165				"RLIMIT_DATA");
166#endif
167	}
168	if (limits >= -1) {
169#ifdef RLIMIT_STACK
170		limit(RLIMIT_STACK, limits);
171#else
172		if (OPT_verbose) bb_error_msg("system does not support %s",
173				"RLIMIT_STACK");
174#endif
175	}
176	if (limitl >= -1) {
177#ifdef RLIMIT_MEMLOCK
178		limit(RLIMIT_MEMLOCK, limitl);
179#else
180		if (OPT_verbose) bb_error_msg("system does not support %s",
181				"RLIMIT_MEMLOCK");
182#endif
183	}
184	if (limita >= -1) {
185#ifdef RLIMIT_VMEM
186		limit(RLIMIT_VMEM, limita);
187#else
188#ifdef RLIMIT_AS
189		limit(RLIMIT_AS, limita);
190#else
191		if (OPT_verbose)
192			bb_error_msg("system does not support %s",
193				"RLIMIT_VMEM");
194#endif
195#endif
196	}
197	if (limito >= -1) {
198#ifdef RLIMIT_NOFILE
199		limit(RLIMIT_NOFILE, limito);
200#else
201#ifdef RLIMIT_OFILE
202		limit(RLIMIT_OFILE, limito);
203#else
204		if (OPT_verbose)
205			bb_error_msg("system does not support %s",
206				"RLIMIT_NOFILE");
207#endif
208#endif
209	}
210	if (limitp >= -1) {
211#ifdef RLIMIT_NPROC
212		limit(RLIMIT_NPROC, limitp);
213#else
214		if (OPT_verbose) bb_error_msg("system does not support %s",
215				"RLIMIT_NPROC");
216#endif
217	}
218	if (limitf >= -1) {
219#ifdef RLIMIT_FSIZE
220		limit(RLIMIT_FSIZE, limitf);
221#else
222		if (OPT_verbose) bb_error_msg("system does not support %s",
223				"RLIMIT_FSIZE");
224#endif
225	}
226	if (limitc >= -1) {
227#ifdef RLIMIT_CORE
228		limit(RLIMIT_CORE, limitc);
229#else
230		if (OPT_verbose) bb_error_msg("system does not support %s",
231				"RLIMIT_CORE");
232#endif
233	}
234	if (limitr >= -1) {
235#ifdef RLIMIT_RSS
236		limit(RLIMIT_RSS, limitr);
237#else
238		if (OPT_verbose) bb_error_msg("system does not support %s",
239				"RLIMIT_RSS");
240#endif
241	}
242	if (limitt >= -1) {
243#ifdef RLIMIT_CPU
244		limit(RLIMIT_CPU, limitt);
245#else
246		if (OPT_verbose) bb_error_msg("system does not support %s",
247				"RLIMIT_CPU");
248#endif
249	}
250}
251
252/* argv[0] */
253static void setuidgid(int, char **);
254static void envuidgid(int, char **);
255static void envdir(int, char **);
256static void softlimit(int, char **);
257
258int chpst_main(int argc, char **argv);
259int chpst_main(int argc, char **argv)
260{
261	if (applet_name[3] == 'd') envdir(argc, argv);
262	if (applet_name[1] == 'o') softlimit(argc, argv);
263	if (applet_name[0] == 's') setuidgid(argc, argv);
264	if (applet_name[0] == 'e') envuidgid(argc, argv);
265	// otherwise we are chpst
266
267	{
268		char *m,*d,*o,*p,*f,*c,*r,*t,*n;
269		getopt32(argv, "+u:U:e:m:d:o:p:f:c:r:t:/:n:vP012",
270				&set_user,&env_user,&env_dir,
271				&m,&d,&o,&p,&f,&c,&r,&t,&root,&n);
272		// if (option_mask32 & 0x1) // -u
273		// if (option_mask32 & 0x2) // -U
274		// if (option_mask32 & 0x4) // -e
275		if (option_mask32 & 0x8) limits = limitl = limita = limitd = xatoul(m); // -m
276		if (option_mask32 & 0x10) limitd = xatoul(d); // -d
277		if (option_mask32 & 0x20) limito = xatoul(o); // -o
278		if (option_mask32 & 0x40) limitp = xatoul(p); // -p
279		if (option_mask32 & 0x80) limitf = xatoul(f); // -f
280		if (option_mask32 & 0x100) limitc = xatoul(c); // -c
281		if (option_mask32 & 0x200) limitr = xatoul(r); // -r
282		if (option_mask32 & 0x400) limitt = xatoul(t); // -t
283		// if (option_mask32 & 0x800) // -/
284		if (option_mask32 & 0x1000) nicelvl = xatoi(n); // -n
285		// The below consts should match #defines at top!
286		//if (option_mask32 & 0x2000) OPT_verbose = 1; // -v
287		//if (option_mask32 & 0x4000) OPT_pgrp = 1; // -P
288		//if (option_mask32 & 0x8000) OPT_nostdin = 1; // -0
289		//if (option_mask32 & 0x10000) OPT_nostdout = 1; // -1
290		//if (option_mask32 & 0x20000) OPT_nostderr = 1; // -2
291	}
292	argv += optind;
293	if (!argv || !*argv) bb_show_usage();
294
295	if (OPT_pgrp) setsid();
296	if (env_dir) edir(env_dir);
297	if (root) {
298		xchdir(root);
299		if (chroot(".") == -1)
300			bb_perror_msg_and_die("chroot");
301	}
302	slimit();
303	if (nicelvl) {
304		errno = 0;
305		if (nice(nicelvl) == -1)
306			bb_perror_msg_and_die("nice");
307	}
308	if (env_user) euidgid(env_user);
309	if (set_user) suidgid(set_user);
310	if (OPT_nostdin) close(0);
311	if (OPT_nostdout) close(1);
312	if (OPT_nostderr) close(2);
313	BB_EXECVP(argv[0], argv);
314	bb_perror_msg_and_die("exec %s", argv[0]);
315}
316
317static void setuidgid(int argc, char **argv)
318{
319	const char *account;
320
321	account = *++argv;
322	if (!account) bb_show_usage();
323	if (!*++argv) bb_show_usage();
324	suidgid((char*)account);
325	BB_EXECVP(argv[0], argv);
326	bb_perror_msg_and_die("exec %s", argv[0]);
327}
328
329static void envuidgid(int argc, char **argv)
330{
331	const char *account;
332
333	account = *++argv;
334	if (!account) bb_show_usage();
335	if (!*++argv) bb_show_usage();
336	euidgid((char*)account);
337	BB_EXECVP(argv[0], argv);
338	bb_perror_msg_and_die("exec %s", argv[0]);
339}
340
341static void envdir(int argc, char **argv)
342{
343	const char *dir;
344
345	dir = *++argv;
346	if (!dir) bb_show_usage();
347	if (!*++argv) bb_show_usage();
348	edir(dir);
349	BB_EXECVP(argv[0], argv);
350	bb_perror_msg_and_die("exec %s", argv[0]);
351}
352
353static void softlimit(int argc, char **argv)
354{
355	char *a,*c,*d,*f,*l,*m,*o,*p,*r,*s,*t;
356	getopt32(argv, "+a:c:d:f:l:m:o:p:r:s:t:",
357			&a,&c,&d,&f,&l,&m,&o,&p,&r,&s,&t);
358	if (option_mask32 & 0x001) limita = xatoul(a); // -a
359	if (option_mask32 & 0x002) limitc = xatoul(c); // -c
360	if (option_mask32 & 0x004) limitd = xatoul(d); // -d
361	if (option_mask32 & 0x008) limitf = xatoul(f); // -f
362	if (option_mask32 & 0x010) limitl = xatoul(l); // -l
363	if (option_mask32 & 0x020) limits = limitl = limita = limitd = xatoul(m); // -m
364	if (option_mask32 & 0x040) limito = xatoul(o); // -o
365	if (option_mask32 & 0x080) limitp = xatoul(p); // -p
366	if (option_mask32 & 0x100) limitr = xatoul(r); // -r
367	if (option_mask32 & 0x200) limits = xatoul(s); // -s
368	if (option_mask32 & 0x400) limitt = xatoul(t); // -t
369	argv += optind;
370	if (!argv[0]) bb_show_usage();
371	slimit();
372	BB_EXECVP(argv[0], argv);
373	bb_perror_msg_and_die("exec %s", argv[0]);
374}
375