chpst.c (7089B) download
1
2#include "parse.h"
3
4#include <arg.h>
5#include <common.h>
6#include <fcntl.h>
7#include <fmt.h>
8#include <grp.h>
9#include <stdbool.h>
10#include <stdlib.h>
11#include <string.h>
12#include <sys/file.h>
13#include <sys/resource.h>
14#include <unistd.h>
15
16void usage(void) {
17 print("Usage:\n"
18 " chpst [-vP012] [-u user[:group]] [-U user[:group]] [-b argv0] [-e dir]\n"
19 " [-/ root] [-C pwd] [-n nice] [-l|-L lock] [-m n] [-d n] [-o n] [-p n] [-f n]\n"
20 " [-c n] command ...\n"
21 " softlimit [-m n] [-a n] [-d n] [-o n] [-p n] [-f n] [-c n] [-r n] [-t n]\n"
22 " [-l n] [-s n] command ...\n"
23 " setuidgid user[:group] command ...\n"
24 " envuidgid user[:group] command ...\n"
25 " pgrphack command ...\n"
26 " setlock [-nNxX] lock command ...\n");
27}
28
29void limit(int what, long l) {
30 struct rlimit r;
31
32 if (getrlimit(what, &r) == -1)
33 fprint(1, "error: unable to getrlimit: %r\n");
34
35 if (l < 0) {
36 r.rlim_cur = 0;
37 } else if ((unsigned long) l > r.rlim_max)
38 r.rlim_cur = r.rlim_max;
39 else
40 r.rlim_cur = l;
41
42 if (setrlimit(what, &r) == -1)
43 fprint(1, "error: unable to setrlimit: %r\n");
44}
45
46
47int main(int argc, char** argv) {
48 int lockfd, lockflags = 0, gid_len = 0;
49 char *arg0 = NULL, *root = NULL, *cd = NULL, *lock = NULL;
50 uid_t uid = 0;
51 gid_t gid[61];
52
53 argv0 = argv[0];
54
55 long limitd = -2,
56 limits = -2,
57 limitl = -2,
58 limita = -2,
59 limito = -2,
60 limitp = -2,
61 limitf = -2,
62 limitc = -2,
63 limitr = -2,
64 limitt = -2;
65 long nicelevel = 0;
66 bool ssid = false;
67 bool closestd[3] = { false, false, false };
68
69 if (streq(argv0, "setuidgid") || streq(argv0, "envuidgid")) {
70 if (argc < 2) {
71 fprint(1, "%s <uid-gid> command...", argv0);
72 return 1;
73 }
74 gid_len = parse_ugid(argv[1], &uid, gid);
75 SHIFT(2);
76 } else if (streq(argv0, "pgrphack")) {
77 ssid = true;
78 SHIFT(1);
79 } else if (streq(argv0, "setlock")) {
80 ARGBEGIN {
81 case 'n':
82 lockflags = LOCK_EX | LOCK_NB;
83 break;
84 case 'N':
85 lockflags = LOCK_EX;
86 break;
87 case 'x':
88 case 'X':
89 fprint(1, "warning: '-%c' is ignored\n", ARGC());
90 break;
91 default:
92 usage();
93 return 1;
94 }
95 ARGEND;
96
97 if (argc < 1) {
98 fprint(1, "%s [-xXnN] command...", argv0);
99 return 1;
100 }
101 lock = argv[1];
102 SHIFT(1);
103 } else if (streq(argv0, "softlimit")) {
104 ARGBEGIN {
105 case 'm':
106 limits = limitl = limita = limitd = atol(EARGF(usage()));
107 break;
108 case 'a':
109 limita = atol(EARGF(usage()));
110 break;
111 case 'd':
112 limitd = atol(EARGF(usage()));
113 break;
114 case 'o':
115 limito = atol(EARGF(usage()));
116 break;
117 case 'p':
118 limitp = atol(EARGF(usage()));
119 break;
120 case 'f':
121 limitf = atol(EARGF(usage()));
122 break;
123 case 'c':
124 limitc = atol(EARGF(usage()));
125 break;
126 case 'r':
127 limitr = atol(EARGF(usage()));
128 break;
129 case 't':
130 limitt = atol(EARGF(usage()));
131 break;
132 case 'l':
133 limitl = atol(EARGF(usage()));
134 break;
135 case 's':
136 limits = atol(EARGF(usage()));
137 break;
138 default:
139 usage();
140 return 1;
141 }
142 ARGEND;
143 } else {
144 ARGBEGIN {
145 case 'u':
146 case 'U':
147 gid_len = parse_ugid(EARGF(usage()), &uid, gid);
148 break;
149 case 'b':
150 arg0 = EARGF(usage());
151 break;
152 case '/':
153 root = EARGF(usage());
154 break;
155 case 'C':
156 cd = EARGF(usage());
157 break;
158 case 'n':
159 nicelevel = atol(EARGF(usage()));
160 break;
161 case 'l':
162 lock = EARGF(usage());
163 lockflags = LOCK_EX | LOCK_NB;
164 break;
165 case 'L':
166 lock = EARGF(usage());
167 lockflags = LOCK_EX;
168 break;
169 case 'v': // ignored
170 break;
171 case 'P':
172 ssid = true;
173 break;
174 case '0':
175 case '1':
176 case '2':
177 closestd[ARGC() - '0'] = true;
178 break;
179 case 'm':
180 limits = limitl = limita = limitd = atol(EARGF(usage()));
181 break;
182 case 'd':
183 limitd = atol(EARGF(usage()));
184 break;
185 case 'o':
186 limito = atol(EARGF(usage()));
187 break;
188 case 'p':
189 limitp = atol(EARGF(usage()));
190 break;
191 case 'f':
192 limitf = atol(EARGF(usage()));
193 break;
194 case 'c':
195 limitc = atol(EARGF(usage()));
196 break;
197 case 'r':
198 limitr = atol(EARGF(usage()));
199 break;
200 case 't':
201 limitt = atol(EARGF(usage()));
202 break;
203 case 'e':
204 fprint(1, "warning: '-%c' is ignored\n", ARGC());
205 break;
206 default:
207 usage();
208 return 1;
209 }
210 ARGEND;
211 }
212
213 if (argc == 0) {
214 fprint(1, "%s: command required\n", argv0);
215 return 1;
216 }
217
218 if (ssid) {
219 setsid();
220 }
221
222 if (uid) {
223 setgroups(gid_len, gid);
224 setgid(gid[0]);
225 setuid(uid);
226 // $EUID
227 }
228
229 if (root) {
230 if (chroot(root) == -1)
231 fprint(1, "unable to change root directory: %r\n");
232
233 // chdir to '/', otherwise the next command will complain 'directory not found'
234 chdir("/");
235 }
236
237 if (cd) {
238 chdir(cd);
239 }
240
241 if (nicelevel != 0) {
242 if (nice(nicelevel) == -1)
243 fprint(1, "unable to set nice level: %r\n");
244 }
245
246 if (limitd >= -1) {
247#ifdef RLIMIT_DATA
248 limit(RLIMIT_DATA, limitd);
249#else
250 if (verbose)
251 fprint(1, "system does not support RLIMIT_DATA\n");
252#endif
253 }
254 if (limits >= -1) {
255#ifdef RLIMIT_STACK
256 limit(RLIMIT_STACK, limits);
257#else
258 if (verbose)
259 fprint(1, "system does not support RLIMIT_STACK\n");
260#endif
261 }
262 if (limitl >= -1) {
263#ifdef RLIMIT_MEMLOCK
264 limit(RLIMIT_MEMLOCK, limitl);
265#else
266 if (verbose)
267 fprint(1, "system does not support RLIMIT_MEMLOCK\n");
268#endif
269 }
270 if (limita >= -1) {
271#ifdef RLIMIT_VMEM
272 limit(RLIMIT_VMEM, limita);
273#else
274# ifdef RLIMIT_AS
275 limit(RLIMIT_AS, limita);
276# else
277 if (verbose)
278 fprint(1, "system does neither support RLIMIT_VMEM nor RLIMIT_AS\n");
279# endif
280#endif
281 }
282 if (limito >= -1) {
283#ifdef RLIMIT_NOFILE
284 limit(RLIMIT_NOFILE, limito);
285#else
286# ifdef RLIMIT_OFILE
287 limit(RLIMIT_OFILE, limito);
288# else
289 if (verbose)
290 fprint(1, "system does neither support RLIMIT_NOFILE nor RLIMIT_OFILE\n");
291# endif
292#endif
293 }
294 if (limitp >= -1) {
295#ifdef RLIMIT_NPROC
296 limit(RLIMIT_NPROC, limitp);
297#else
298 if (verbose)
299 fprint(1, "system does not support RLIMIT_NPROC\n");
300#endif
301 }
302 if (limitf >= -1) {
303#ifdef RLIMIT_FSIZE
304 limit(RLIMIT_FSIZE, limitf);
305#else
306 if (verbose)
307 fprint(1, "system does not support RLIMIT_FSIZE\n");
308#endif
309 }
310 if (limitc >= -1) {
311#ifdef RLIMIT_CORE
312 limit(RLIMIT_CORE, limitc);
313#else
314 if (verbose)
315 fprint(1, "system does not support RLIMIT_CORE\n");
316#endif
317 }
318 if (limitr >= -1) {
319#ifdef RLIMIT_RSS
320 limit(RLIMIT_RSS, limitr);
321#else
322 if (verbose)
323 fprint(1, "system does not support RLIMIT_RSS\n");
324#endif
325 }
326 if (limitt >= -1) {
327#ifdef RLIMIT_CPU
328 limit(RLIMIT_CPU, limitt);
329#else
330 if (verbose)
331 fprint(1, "system does not support RLIMIT_CPU\n");
332#endif
333 }
334
335 if (lock) {
336 if ((lockfd = open(lock, O_WRONLY | O_APPEND)) == -1)
337 fprint(1, "unable to open lock: %r\n");
338
339 if (flock(lockfd, lockflags) == -1)
340 fprint(1, "unable to lock: %r\n");
341 }
342
343 for (int i = 0; i < 3; i++)
344 if (closestd[i] && close(i) == -1)
345 fprint(1, "unable to close std-%d: %r\n", i);
346
347 execvp(arg0 ? arg0 : *argv, argv);
348 fprint(1, "cannot execute: %r\n");
349}