diff options
Diffstat (limited to 'chpst.c')
-rw-r--r-- | chpst.c | 475 |
1 files changed, 475 insertions, 0 deletions
@@ -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 | |||
27 | const char *progname; | ||
28 | static stralloc sa; | ||
29 | |||
30 | void fatal(const char *m) { strerr_die3sys(111, FATAL, m, ": "); } | ||
31 | void fatal2(const char *m0, const char *m1) { | ||
32 | strerr_die5sys(111, FATAL, m0, ": ", m1, ": "); | ||
33 | } | ||
34 | void fatalx(const char *m0, const char *m1) { | ||
35 | strerr_die4x(111, FATAL, m0, ": ", m1); | ||
36 | } | ||
37 | void warn(const char *m) { strerr_warn2(WARNING, m, 0); } | ||
38 | void die_nomem() { strerr_die2x(111, FATAL, "out of memory."); } | ||
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 | |||
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 | |||
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 | |||
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 | |||
152 | void slock_die(const char *m, const char *f, unsigned int x) { | ||
153 | if (! x) fatal2(m, f); | ||
154 | _exit(0); | ||
155 | } | ||
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 | |||
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 | } | ||
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 | |||
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 | |||
366 | void setuidgid_usage() { | ||
367 | strerr_die4x(100, "usage: ", progname, USAGE_SETUIDGID, "\n"); | ||
368 | } | ||
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 | |||
379 | void envuidgid_usage() { | ||
380 | strerr_die4x(100, "usage: ", progname, USAGE_ENVUIDGID, "\n"); | ||
381 | } | ||
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 | |||
392 | void envdir_usage() { | ||
393 | strerr_die4x(100, "usage: ", progname, USAGE_ENVDIR, "\n"); | ||
394 | } | ||
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 | |||
405 | void pgrphack_usage() { | ||
406 | strerr_die4x(100, "usage: ", progname, USAGE_PGRPHACK, "\n"); | ||
407 | } | ||
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 | |||
415 | void setlock_usage() { | ||
416 | strerr_die4x(100, "usage: ", progname, USAGE_SETLOCK, "\n"); | ||
417 | } | ||
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 | |||
442 | void softlimit_usage() { | ||
443 | strerr_die4x(100, "usage: ", progname, USAGE_SOFTLIMIT, "\n"); | ||
444 | } | ||
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 | } | ||
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 | } | ||