summaryrefslogtreecommitdiff
path: root/chpst.c
diff options
context:
space:
mode:
Diffstat (limited to 'chpst.c')
-rw-r--r--chpst.c475
1 files changed, 475 insertions, 0 deletions
diff --git a/chpst.c b/chpst.c
new file mode 100644
index 0000000..f1b8ed9
--- /dev/null
+++ b/chpst.c
@@ -0,0 +1,475 @@
1#include <sys/types.h>
2#include <time.h>
3#include <sys/time.h>
4#include <sys/resource.h>
5#include <unistd.h>
6#include "sgetopt.h"
7#include "error.h"
8#include "strerr.h"
9#include "str.h"
10#include "uidgid.h"
11#include "prot.h"
12#include "strerr.h"
13#include "scan.h"
14#include "fmt.h"
15#include "lock.h"
16#include "pathexec.h"
17#include "stralloc.h"
18#include "byte.h"
19#include "open.h"
20#include "openreadclose.h"
21#include "direntry.h"
22
23#define USAGE_MAIN " [-vP012] [-u user[:group]] [-U user[:group]] [-b argv0] [-e dir] [-/ root] [-n nice] [-l|-L lock] [-m n] [-d n] [-o n] [-p n] [-f n] [-c n] prog"
24#define FATAL "chpst: fatal: "
25#define WARNING "chpst: warning: "
26
27const char *progname;
28static stralloc sa;
29
30void fatal(const char *m) { strerr_die3sys(111, FATAL, m, ": "); }
31void fatal2(const char *m0, const char *m1) {
32 strerr_die5sys(111, FATAL, m0, ": ", m1, ": ");
33}
34void fatalx(const char *m0, const char *m1) {
35 strerr_die4x(111, FATAL, m0, ": ", m1);
36}
37void warn(const char *m) { strerr_warn2(WARNING, m, 0); }
38void die_nomem() { strerr_die2x(111, FATAL, "out of memory."); }
39void usage() { strerr_die4x(100, "usage: ", progname, USAGE_MAIN, "\n"); }
40
41char *set_user =0;
42char *env_user =0;
43const char *argv0 =0;
44const char *env_dir =0;
45unsigned int verbose =0;
46unsigned int pgrp =0;
47unsigned int nostdin =0;
48unsigned int nostdout =0;
49unsigned int nostderr =0;
50long limitd =-2;
51long limits =-2;
52long limitl =-2;
53long limita =-2;
54long limito =-2;
55long limitp =-2;
56long limitf =-2;
57long limitc =-2;
58long limitr =-2;
59long limitt =-2;
60long nicelvl =0;
61const char *lock =0;
62const char *root =0;
63unsigned int lockdelay;
64
65void suidgid(char *user, unsigned int ext) {
66 struct uidgid ugid;
67
68 if (ext) {
69 if (! uidgids_get(&ugid, user)) {
70 if (*user == ':') fatalx("invalid uid/gids", user +1);
71 if (errno) fatal("unable to get password/group file entry");
72 fatalx("unknown user/group", user);
73 }
74 }
75 else
76 if (! uidgid_get(&ugid, user)) {
77 if (errno) fatal("unable to get password file entry");
78 fatalx("unknown account", user);
79 }
80 if (setgroups(ugid.gids, ugid.gid) == -1) fatal("unable to setgroups");
81 if (setgid(*ugid.gid) == -1) fatal("unable to setgid");
82 if (prot_uid(ugid.uid) == -1) fatal("unable to setuid");
83}
84
85void euidgid(char *user, unsigned int ext) {
86 struct uidgid ugid;
87 char bufnum[FMT_ULONG];
88
89 if (ext) {
90 if (! uidgids_get(&ugid, user)) {
91 if (*user == ':') fatalx("invalid uid/gids", user +1);
92 if (errno) fatal("unable to get password/group file entry");
93 fatalx("unknown user/group", user);
94 }
95 }
96 else
97 if (! uidgid_get(&ugid, user)) {
98 if (errno) fatal("unable to get password file entry");
99 fatalx("unknown account", user);
100 }
101 bufnum[fmt_ulong(bufnum, *ugid.gid)] =0;
102 if (! pathexec_env("GID", bufnum)) die_nomem();
103 bufnum[fmt_ulong(bufnum, ugid.uid)] =0;
104 if (! pathexec_env("UID", bufnum)) die_nomem();
105}
106
107void edir(const char *dirname) {
108 int wdir;
109 DIR *dir;
110 direntry *d;
111 int i;
112
113 if ((wdir =open_read(".")) == -1)
114 fatal("unable to open current working directory");
115 if (chdir(dirname)) fatal2("unable to switch to directory", dirname);
116 if (! (dir =opendir("."))) fatal2("unable to open directory", dirname);
117 for (;;) {
118 errno =0;
119 d =readdir(dir);
120 if (! d) {
121 if (errno) fatal2("unable to read directory", dirname);
122 break;
123 }
124 if (d->d_name[0] == '.') continue;
125 if (openreadclose(d->d_name, &sa, 256) == -1) {
126 if ((errno == error_isdir) && env_dir) {
127 if (verbose)
128 strerr_warn6(WARNING, "unable to read ", dirname, "/",
129 d->d_name, ": ", &strerr_sys);
130 continue;
131 }
132 else
133 strerr_die6sys(111, FATAL, "unable to read ", dirname, "/",
134 d->d_name, ": ");
135 }
136 if (sa.len) {
137 sa.len =byte_chr(sa.s, sa.len, '\n');
138 while (sa.len && (sa.s[sa.len -1] == ' ' || sa.s[sa.len -1] == '\t'))
139 --sa.len;
140 for (i =0; i < sa.len; ++i) if (! sa.s[i]) sa.s[i] ='\n';
141 if (! stralloc_0(&sa)) die_nomem();
142 if (! pathexec_env(d->d_name, sa.s)) die_nomem();
143 }
144 else
145 if (! pathexec_env(d->d_name, 0)) die_nomem();
146 }
147 closedir(dir);
148 if (fchdir(wdir) == -1) fatal("unable to switch to starting directory");
149 close(wdir);
150}
151
152void slock_die(const char *m, const char *f, unsigned int x) {
153 if (! x) fatal2(m, f);
154 _exit(0);
155}
156void slock(const char *f, unsigned int d, unsigned int x) {
157 int fd;
158
159 if ((fd =open_append(f)) == -1) slock_die("unable to open lock", f, x);
160 if (d) {
161 if (lock_ex(fd) == -1) slock_die("unable to lock", f, x);
162 return;
163 }
164 if (lock_exnb(fd) == -1) slock_die("unable to lock", f, x);
165}
166
167void limit(int what, long l) {
168 struct rlimit r;
169
170 if (getrlimit(what, &r) == -1) fatal("unable to getrlimit()");
171 if ((l < 0) || (l > r.rlim_max))
172 r.rlim_cur =r.rlim_max;
173 else
174 r.rlim_cur =l;
175 if (setrlimit(what, &r) == -1) fatal("unable to setrlimit()");
176}
177void slimit() {
178 if (limitd >= -1) {
179#ifdef RLIMIT_DATA
180 limit(RLIMIT_DATA, limitd);
181#else
182 if (verbose) warn("system does not support RLIMIT_DATA");
183#endif
184 }
185 if (limits >= -1) {
186#ifdef RLIMIT_STACK
187 limit(RLIMIT_STACK, limits);
188#else
189 if (verbose) warn("system does not support RLIMIT_STACK");
190#endif
191 }
192 if (limitl >= -1) {
193#ifdef RLIMIT_MEMLOCK
194 limit(RLIMIT_MEMLOCK, limitl);
195#else
196 if (verbose) warn("system does not support RLIMIT_MEMLOCK");
197#endif
198 }
199 if (limita >= -1) {
200#ifdef RLIMIT_VMEM
201 limit(RLIMIT_VMEM, limita);
202#else
203#ifdef RLIMIT_AS
204 limit(RLIMIT_AS, limita);
205#else
206 if (verbose)
207 warn("system does neither support RLIMIT_VMEM nor RLIMIT_AS");
208#endif
209#endif
210 }
211 if (limito >= -1) {
212#ifdef RLIMIT_NOFILE
213 limit(RLIMIT_NOFILE, limito);
214#else
215#ifdef RLIMIT_OFILE
216 limit(RLIMIT_OFILE, limito);
217#else
218 if (verbose)
219 warn("system does neither support RLIMIT_NOFILE nor RLIMIT_OFILE");
220#endif
221#endif
222 }
223 if (limitp >= -1) {
224#ifdef RLIMIT_NPROC
225 limit(RLIMIT_NPROC, limitp);
226#else
227 if (verbose) warn("system does not support RLIMIT_NPROC");
228#endif
229 }
230 if (limitf >= -1) {
231#ifdef RLIMIT_FSIZE
232 limit(RLIMIT_FSIZE, limitf);
233#else
234 if (verbose) warn("system does not support RLIMIT_FSIZE");
235#endif
236 }
237 if (limitc >= -1) {
238#ifdef RLIMIT_CORE
239 limit(RLIMIT_CORE, limitc);
240#else
241 if (verbose) warn("system does not support RLIMIT_CORE");
242#endif
243 }
244 if (limitr >= -1) {
245#ifdef RLIMIT_RSS
246 limit(RLIMIT_RSS, limitr);
247#else
248 if (verbose) warn("system does not support RLIMIT_RSS");
249#endif
250 }
251 if (limitt >= -1) {
252#ifdef RLIMIT_CPU
253 limit(RLIMIT_CPU, limitt);
254#else
255 if (verbose) warn("system does not support RLIMIT_CPU");
256#endif
257 }
258}
259
260/* argv[0] */
261void setuidgid(int, const char *const *);
262void envuidgid(int, const char *const *);
263void envdir(int, const char *const *);
264void pgrphack(int, const char *const *);
265void setlock(int, const char *const *);
266void softlimit(int, const char *const *);
267
268int main(int argc, const char **argv) {
269 int opt;
270 int i;
271 unsigned long ul;
272
273 progname =argv[0];
274 for (i =str_len(progname); i; --i)
275 if (progname[i -1] == '/') {
276 progname +=i;
277 break;
278 }
279 if (progname[0] == 'd') ++progname;
280
281 /* argv[0] */
282 if (str_equal(progname, "setuidgid")) setuidgid(argc, argv);
283 if (str_equal(progname, "envuidgid")) envuidgid(argc, argv);
284 if (str_equal(progname, "envdir")) envdir(argc, argv);
285 if (str_equal(progname, "pgrphack")) pgrphack(argc, argv);
286 if (str_equal(progname, "setlock")) setlock(argc, argv);
287 if (str_equal(progname, "softlimit")) softlimit(argc, argv);
288
289 while ((opt =getopt(argc, argv, "u:U:b:e:m:d:o:p:f:c:r:t:/:n:l:L:vP012V"))
290 != opteof)
291 switch(opt) {
292 case 'u': set_user =(char*)optarg; break;
293 case 'U': env_user =(char*)optarg; break;
294 case 'b': argv0 =(char*)optarg; break;
295 case 'e': env_dir =optarg; break;
296 case 'm':
297 if (optarg[scan_ulong(optarg, &ul)]) usage();
298 limits =limitl =limita =limitd =ul;
299 break;
300 case 'd': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitd =ul; break;
301 case 'o': if (optarg[scan_ulong(optarg, &ul)]) usage(); limito =ul; break;
302 case 'p': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitp =ul; break;
303 case 'f': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitf =ul; break;
304 case 'c': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitc =ul; break;
305 case 'r': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitr =ul; break;
306 case 't': if (optarg[scan_ulong(optarg, &ul)]) usage(); limitt =ul; break;
307 case '/': root =optarg; break;
308 case 'n':
309 switch (*optarg) {
310 case '-':
311 if (optarg[scan_ulong(++optarg, &ul)]) usage(); nicelvl =ul;
312 nicelvl *=-1;
313 break;
314 case '+': ++optarg;
315 default:
316 if (optarg[scan_ulong(optarg, &ul)]) usage(); nicelvl =ul;
317 break;
318 }
319 break;
320 case 'l': if (lock) usage(); lock =optarg; lockdelay =1; break;
321 case 'L': if (lock) usage(); lock =optarg; lockdelay =0; break;
322 case 'v': verbose =1; break;
323 case 'P': pgrp =1; break;
324 case '0': nostdin =1; break;
325 case '1': nostdout =1; break;
326 case '2': nostderr =1; break;
327 case 'V': strerr_warn1("$Id: f279d44141c981dd7535a12260efcf1ef7beed26 $", 0);
328 case '?': usage();
329 }
330 argv +=optind;
331 if (! argv || ! *argv) usage();
332
333 if (pgrp) setsid();
334 if (env_dir) edir(env_dir);
335 if (root) {
336 if (chdir(root) == -1) fatal2("unable to change directory", root);
337 if (chroot(".") == -1) fatal("unable to change root directory");
338 }
339 if (nicelvl) {
340 errno =0;
341 if (nice(nicelvl) == -1) if (errno) fatal("unable to set nice level");
342 }
343 if (env_user) euidgid(env_user, 1);
344 if (set_user) suidgid(set_user, 1);
345 if (lock) slock(lock, lockdelay, 0);
346 if (nostdin) if (close(0) == -1) fatal("unable to close stdin");
347 if (nostdout) if (close(1) == -1) fatal("unable to close stdout");
348 if (nostderr) if (close(2) == -1) fatal("unable to close stderr");
349 slimit();
350
351 progname =*argv;
352 if (argv0) *argv =argv0;
353 pathexec_env_run(progname, argv);
354 fatal2("unable to run", *argv);
355 return(0);
356}
357
358/* argv[0] */
359#define USAGE_SETUIDGID " account child"
360#define USAGE_ENVUIDGID " account child"
361#define USAGE_ENVDIR " dir child"
362#define USAGE_PGRPHACK " child"
363#define USAGE_SETLOCK " [ -nNxX ] file program [ arg ... ]"
364#define USAGE_SOFTLIMIT " [-a allbytes] [-c corebytes] [-d databytes] [-f filebytes] [-l lockbytes] [-m membytes] [-o openfiles] [-p processes] [-r residentbytes] [-s stackbytes] [-t cpusecs] child"
365
366void setuidgid_usage() {
367 strerr_die4x(100, "usage: ", progname, USAGE_SETUIDGID, "\n");
368}
369void setuidgid(int argc, const char *const *argv) {
370 const char *account;
371
372 if (! (account =*++argv)) setuidgid_usage();
373 if (! *++argv) setuidgid_usage();
374 suidgid((char*)account, 0);
375 pathexec(argv);
376 fatal2("unable to run", *argv);
377}
378
379void envuidgid_usage() {
380 strerr_die4x(100, "usage: ", progname, USAGE_ENVUIDGID, "\n");
381}
382void envuidgid(int argc, const char *const *argv) {
383 const char *account;
384
385 if (! (account =*++argv)) envuidgid_usage();
386 if (! *++argv) envuidgid_usage();
387 euidgid((char*)account, 0);
388 pathexec(argv);
389 fatal2("unable to run", *argv);
390}
391
392void envdir_usage() {
393 strerr_die4x(100, "usage: ", progname, USAGE_ENVDIR, "\n");
394}
395void envdir(int argc, const char *const *argv) {
396 const char *dir;
397
398 if (! (dir =*++argv)) envdir_usage();
399 if (! *++argv) envdir_usage();
400 edir(dir);
401 pathexec(argv);
402 fatal2("unable to run", *argv);
403}
404
405void pgrphack_usage() {
406 strerr_die4x(100, "usage: ", progname, USAGE_PGRPHACK, "\n");
407}
408void pgrphack(int argc, const char *const *argv) {
409 if (! *++argv) pgrphack_usage();
410 setsid();
411 pathexec(argv);
412 fatal2("unable to run", *argv);
413}
414
415void setlock_usage() {
416 strerr_die4x(100, "usage: ", progname, USAGE_SETLOCK, "\n");
417}
418void setlock(int argc, const char *const *argv) {
419 int opt;
420 unsigned int delay =0;
421 unsigned int x =0;
422 const char *fn;
423
424 while ((opt =getopt(argc, argv, "nNxX")) != opteof)
425 switch(opt) {
426 case 'n': delay =1; break;
427 case 'N': delay =0; break;
428 case 'x': x =1; break;
429 case 'X': x =0; break;
430 default: setlock_usage();
431 }
432 argv +=optind;
433 if (! (fn =*argv)) setlock_usage();
434 if (! *++argv) setlock_usage();
435
436 slock(fn, delay, x);
437 pathexec(argv);
438 if (! x) fatal2("unable to run", *argv);
439 _exit(0);
440}
441
442void softlimit_usage() {
443 strerr_die4x(100, "usage: ", progname, USAGE_SOFTLIMIT, "\n");
444}
445void getlarg(long *l) {
446 unsigned long ul;
447
448 if (str_equal(optarg, "=")) { *l =-1; return; }
449 if (optarg[scan_ulong(optarg, &ul)]) usage();
450 *l =ul;
451}
452void softlimit(int argc, const char *const *argv) {
453 int opt;
454
455 while ((opt =getopt(argc,argv,"a:c:d:f:l:m:o:p:r:s:t:")) != opteof)
456 switch(opt) {
457 case '?': softlimit_usage();
458 case 'a': getlarg(&limita); break;
459 case 'c': getlarg(&limitc); break;
460 case 'd': getlarg(&limitd); break;
461 case 'f': getlarg(&limitf); break;
462 case 'l': getlarg(&limitl); break;
463 case 'm': getlarg(&limitd); limits =limitl =limita =limitd; break;
464 case 'o': getlarg(&limito); break;
465 case 'p': getlarg(&limitp); break;
466 case 'r': getlarg(&limitr); break;
467 case 's': getlarg(&limits); break;
468 case 't': getlarg(&limitt); break;
469 }
470 argv +=optind;
471 if (!*argv) softlimit_usage();
472 slimit();
473 pathexec(argv);
474 fatal2("unable to run", *argv);
475}