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
27 const char *progname;
28 static stralloc sa;
29
fatal(const char * m)30 void fatal(const char *m) { strerr_die3sys(111, FATAL, m, ": "); }
fatal2(const char * m0,const char * m1)31 void fatal2(const char *m0, const char *m1) {
32 strerr_die5sys(111, FATAL, m0, ": ", m1, ": ");
33 }
fatalx(const char * m0,const char * m1)34 void fatalx(const char *m0, const char *m1) {
35 strerr_die4x(111, FATAL, m0, ": ", m1);
36 }
warn(const char * m)37 void warn(const char *m) { strerr_warn2(WARNING, m, 0); }
die_nomem()38 void die_nomem() { strerr_die2x(111, FATAL, "out of memory."); }
usage()39 void usage() { strerr_die4x(100, "usage: ", progname, USAGE_MAIN, "\n"); }
40
41 char *set_user =0;
42 char *env_user =0;
43 const char *argv0 =0;
44 const char *env_dir =0;
45 unsigned int verbose =0;
46 unsigned int pgrp =0;
47 unsigned int nostdin =0;
48 unsigned int nostdout =0;
49 unsigned int nostderr =0;
50 long limitd =-2;
51 long limits =-2;
52 long limitl =-2;
53 long limita =-2;
54 long limito =-2;
55 long limitp =-2;
56 long limitf =-2;
57 long limitc =-2;
58 long limitr =-2;
59 long limitt =-2;
60 long nicelvl =0;
61 const char *lock =0;
62 const char *root =0;
63 unsigned int lockdelay;
64
suidgid(char * user,unsigned int ext)65 void 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
euidgid(char * user,unsigned int ext)85 void 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
edir(const char * dirname)107 void 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
slock_die(const char * m,const char * f,unsigned int x)152 void slock_die(const char *m, const char *f, unsigned int x) {
153 if (! x) fatal2(m, f);
154 _exit(0);
155 }
slock(const char * f,unsigned int d,unsigned int x)156 void 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
limit(int what,long l)167 void 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 }
slimit()177 void 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] */
261 void setuidgid(int, const char *const *);
262 void envuidgid(int, const char *const *);
263 void envdir(int, const char *const *);
264 void pgrphack(int, const char *const *);
265 void setlock(int, const char *const *);
266 void softlimit(int, const char *const *);
267
main(int argc,const char ** argv)268 int 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
setuidgid_usage()366 void setuidgid_usage() {
367 strerr_die4x(100, "usage: ", progname, USAGE_SETUIDGID, "\n");
368 }
setuidgid(int argc,const char * const * argv)369 void 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
envuidgid_usage()379 void envuidgid_usage() {
380 strerr_die4x(100, "usage: ", progname, USAGE_ENVUIDGID, "\n");
381 }
envuidgid(int argc,const char * const * argv)382 void 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
envdir_usage()392 void envdir_usage() {
393 strerr_die4x(100, "usage: ", progname, USAGE_ENVDIR, "\n");
394 }
envdir(int argc,const char * const * argv)395 void 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
pgrphack_usage()405 void pgrphack_usage() {
406 strerr_die4x(100, "usage: ", progname, USAGE_PGRPHACK, "\n");
407 }
pgrphack(int argc,const char * const * argv)408 void 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
setlock_usage()415 void setlock_usage() {
416 strerr_die4x(100, "usage: ", progname, USAGE_SETLOCK, "\n");
417 }
setlock(int argc,const char * const * argv)418 void 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
softlimit_usage()442 void softlimit_usage() {
443 strerr_die4x(100, "usage: ", progname, USAGE_SOFTLIMIT, "\n");
444 }
getlarg(long * l)445 void 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 }
softlimit(int argc,const char * const * argv)452 void 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 }
476