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:vP012"))
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 '?': usage();
328     }
329   argv +=optind;
330   if (! argv || ! *argv) usage();
331 
332   if (pgrp) setsid();
333   if (env_dir) edir(env_dir);
334   if (root) {
335     if (chdir(root) == -1) fatal2("unable to change directory", root);
336     if (chroot(".") == -1) fatal("unable to change root directory");
337   }
338   if (nicelvl) {
339     errno =0;
340     if (nice(nicelvl) == -1) if (errno) fatal("unable to set nice level");
341   }
342   if (env_user) euidgid(env_user, 1);
343   if (set_user) suidgid(set_user, 1);
344   if (lock) slock(lock, lockdelay, 0);
345   if (nostdin) if (close(0) == -1) fatal("unable to close stdin");
346   if (nostdout) if (close(1) == -1) fatal("unable to close stdout");
347   if (nostderr) if (close(2) == -1) fatal("unable to close stderr");
348   slimit();
349 
350   progname =*argv;
351   if (argv0) *argv =argv0;
352   pathexec_env_run(progname, argv);
353   fatal2("unable to run", *argv);
354   return(0);
355 }
356 
357 /* argv[0] */
358 #define USAGE_SETUIDGID " account child"
359 #define USAGE_ENVUIDGID " account child"
360 #define USAGE_ENVDIR " dir child"
361 #define USAGE_PGRPHACK " child"
362 #define USAGE_SETLOCK " [ -nNxX ] file program [ arg ... ]"
363 #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"
364 
setuidgid_usage()365 void setuidgid_usage() {
366   strerr_die4x(100, "usage: ", progname, USAGE_SETUIDGID, "\n");
367 }
setuidgid(int argc,const char * const * argv)368 void setuidgid(int argc, const char *const *argv) {
369   const char *account;
370 
371   if (! (account =*++argv)) setuidgid_usage();
372   if (! *++argv) setuidgid_usage();
373   suidgid((char*)account, 0);
374   pathexec(argv);
375   fatal2("unable to run", *argv);
376 }
377 
envuidgid_usage()378 void envuidgid_usage() {
379   strerr_die4x(100, "usage: ", progname, USAGE_ENVUIDGID, "\n");
380 }
envuidgid(int argc,const char * const * argv)381 void envuidgid(int argc, const char *const *argv) {
382   const char *account;
383 
384   if (! (account =*++argv)) envuidgid_usage();
385   if (! *++argv) envuidgid_usage();
386   euidgid((char*)account, 0);
387   pathexec(argv);
388   fatal2("unable to run", *argv);
389 }
390 
envdir_usage()391 void envdir_usage() {
392   strerr_die4x(100, "usage: ", progname, USAGE_ENVDIR, "\n");
393 }
envdir(int argc,const char * const * argv)394 void envdir(int argc, const char *const *argv) {
395   const char *dir;
396 
397   if (! (dir =*++argv)) envdir_usage();
398   if (! *++argv) envdir_usage();
399   edir(dir);
400   pathexec(argv);
401   fatal2("unable to run", *argv);
402 }
403 
pgrphack_usage()404 void pgrphack_usage() {
405   strerr_die4x(100, "usage: ", progname, USAGE_PGRPHACK, "\n");
406 }
pgrphack(int argc,const char * const * argv)407 void pgrphack(int argc, const char *const *argv) {
408   if (! *++argv) pgrphack_usage();
409   setsid();
410   pathexec(argv);
411   fatal2("unable to run", *argv);
412 }
413 
setlock_usage()414 void setlock_usage() {
415   strerr_die4x(100, "usage: ", progname, USAGE_SETLOCK, "\n");
416 }
setlock(int argc,const char * const * argv)417 void setlock(int argc, const char *const *argv) {
418   int opt;
419   unsigned int delay =0;
420   unsigned int x =0;
421   const char *fn;
422 
423   while ((opt =getopt(argc, argv, "nNxX")) != opteof)
424     switch(opt) {
425       case 'n': delay =1; break;
426       case 'N': delay =0; break;
427       case 'x': x =1; break;
428       case 'X': x =0; break;
429       default: setlock_usage();
430     }
431   argv +=optind;
432   if (! (fn =*argv)) setlock_usage();
433   if (! *++argv) setlock_usage();
434 
435   slock(fn, delay, x);
436   pathexec(argv);
437   if (! x) fatal2("unable to run", *argv);
438   _exit(0);
439 }
440 
softlimit_usage()441 void softlimit_usage() {
442   strerr_die4x(100, "usage: ", progname, USAGE_SOFTLIMIT, "\n");
443 }
getlarg(long * l)444 void getlarg(long *l) {
445   unsigned long ul;
446 
447   if (str_equal(optarg, "=")) { *l =-1; return; }
448   if (optarg[scan_ulong(optarg, &ul)]) usage();
449   *l =ul;
450 }
softlimit(int argc,const char * const * argv)451 void softlimit(int argc, const char *const *argv) {
452   int opt;
453 
454   while ((opt =getopt(argc,argv,"a:c:d:f:l:m:o:p:r:s:t:")) != opteof)
455     switch(opt) {
456     case '?': softlimit_usage();
457     case 'a': getlarg(&limita); break;
458     case 'c': getlarg(&limitc); break;
459     case 'd': getlarg(&limitd); break;
460     case 'f': getlarg(&limitf); break;
461     case 'l': getlarg(&limitl); break;
462     case 'm': getlarg(&limitd); limits =limitl =limita =limitd; break;
463     case 'o': getlarg(&limito); break;
464     case 'p': getlarg(&limitp); break;
465     case 'r': getlarg(&limitr); break;
466     case 's': getlarg(&limits); break;
467     case 't': getlarg(&limitt); break;
468     }
469   argv +=optind;
470   if (!*argv) softlimit_usage();
471   slimit();
472   pathexec(argv);
473   fatal2("unable to run", *argv);
474 }
475