]>
Commit | Line | Data |
---|---|---|
5600c405 AL |
1 | /* |
2 | * setpriv(1) - set various kernel privilege bits and run something | |
3 | * | |
4 | * Copyright (C) 2012 Andy Lutomirski <luto@amacapital.net> | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify it | |
7 | * under the terms of the GNU General Public License as published by the | |
8 | * Free Software Foundation; either version 2, or (at your option) any | |
9 | * later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along | |
17 | * with this program; if not, write to the Free Software Foundation, Inc., | |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | |
19 | */ | |
20 | ||
21 | #include <cap-ng.h> | |
22 | #include <errno.h> | |
23 | #include <getopt.h> | |
24 | #include <grp.h> | |
25 | #include <linux/securebits.h> | |
637fa4c6 | 26 | #include <pwd.h> |
5600c405 AL |
27 | #include <stdarg.h> |
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <sys/prctl.h> | |
637fa4c6 | 31 | #include <sys/types.h> |
5600c405 AL |
32 | #include <unistd.h> |
33 | ||
5600c405 AL |
34 | #include "c.h" |
35 | #include "closestream.h" | |
36 | #include "nls.h" | |
37 | #include "optutils.h" | |
38 | #include "strutils.h" | |
39 | #include "xalloc.h" | |
59c68b43 | 40 | #include "pathnames.h" |
23f54ce7 | 41 | #include "signames.h" |
89f95425 | 42 | #include "env.h" |
5600c405 AL |
43 | |
44 | #ifndef PR_SET_NO_NEW_PRIVS | |
45 | # define PR_SET_NO_NEW_PRIVS 38 | |
46 | #endif | |
47 | #ifndef PR_GET_NO_NEW_PRIVS | |
48 | # define PR_GET_NO_NEW_PRIVS 39 | |
49 | #endif | |
50 | ||
05a22eac PS |
51 | #ifndef PR_CAP_AMBIENT |
52 | # define PR_CAP_AMBIENT 47 | |
53 | # define PR_CAP_AMBIENT_IS_SET 1 | |
0c92194e PS |
54 | # define PR_CAP_AMBIENT_RAISE 2 |
55 | # define PR_CAP_AMBIENT_LOWER 3 | |
05a22eac PS |
56 | #endif |
57 | ||
5600c405 AL |
58 | #define SETPRIV_EXIT_PRIVERR 127 /* how we exit when we fail to set privs */ |
59 | ||
89f95425 KZ |
60 | /* The shell to set SHELL env.variable if none is given in the user's passwd entry. */ |
61 | #define DEFAULT_SHELL "/bin/sh" | |
62 | ||
85c15c1f KZ |
63 | static gid_t get_group(const char *s, const char *err); |
64 | ||
9e5dd89d PS |
65 | enum cap_type { |
66 | CAP_TYPE_EFFECTIVE = CAPNG_EFFECTIVE, | |
67 | CAP_TYPE_PERMITTED = CAPNG_PERMITTED, | |
68 | CAP_TYPE_INHERITABLE = CAPNG_INHERITABLE, | |
05a22eac PS |
69 | CAP_TYPE_BOUNDING = CAPNG_BOUNDING_SET, |
70 | CAP_TYPE_AMBIENT = (1 << 4) | |
9e5dd89d PS |
71 | }; |
72 | ||
5600c405 AL |
73 | /* |
74 | * Note: We are subject to https://bugzilla.redhat.com/show_bug.cgi?id=895105 | |
75 | * and we will therefore have problems if new capabilities are added. Once | |
76 | * that bug is fixed, I'll (Andy Lutomirski) submit a corresponding fix to | |
77 | * setpriv. In the mean time, the code here tries to work reasonably well. | |
78 | */ | |
79 | ||
80 | struct privctx { | |
5600c405 AL |
81 | unsigned int |
82 | nnp:1, /* no_new_privs */ | |
83 | have_ruid:1, /* real uid */ | |
84 | have_euid:1, /* effective uid */ | |
85 | have_rgid:1, /* real gid */ | |
86 | have_egid:1, /* effective gid */ | |
94826d0d | 87 | have_passwd:1, /* passwd entry */ |
5600c405 AL |
88 | have_groups:1, /* add groups */ |
89 | keep_groups:1, /* keep groups */ | |
90 | clear_groups:1, /* remove groups */ | |
94826d0d | 91 | init_groups:1, /* initialize groups */ |
89f95425 | 92 | reset_env:1, /* reset environment */ |
5600c405 AL |
93 | have_securebits:1; /* remove groups */ |
94 | ||
95 | /* uids and gids */ | |
96 | uid_t ruid, euid; | |
97 | gid_t rgid, egid; | |
98 | ||
94826d0d SS |
99 | /* real user passwd entry */ |
100 | struct passwd passwd; | |
101 | ||
5600c405 AL |
102 | /* supplementary groups */ |
103 | size_t num_groups; | |
104 | gid_t *groups; | |
105 | ||
106 | /* caps */ | |
107 | const char *caps_to_inherit; | |
0c92194e | 108 | const char *ambient_caps; |
5600c405 AL |
109 | const char *bounding_set; |
110 | ||
111 | /* securebits */ | |
112 | int securebits; | |
23f54ce7 PS |
113 | /* parent death signal (<0 clear, 0 nothing, >0 signal) */ |
114 | int pdeathsig; | |
5600c405 AL |
115 | |
116 | /* LSMs */ | |
117 | const char *selinux_label; | |
118 | const char *apparmor_profile; | |
119 | }; | |
120 | ||
86be6a32 | 121 | static void __attribute__((__noreturn__)) usage(void) |
5600c405 | 122 | { |
86be6a32 | 123 | FILE *out = stdout; |
5600c405 | 124 | fputs(USAGE_HEADER, out); |
298dc4ff BS |
125 | fprintf(out, _(" %s [options] <program> [<argument>...]\n"), |
126 | program_invocation_short_name); | |
127 | ||
451dbcfa BS |
128 | fputs(USAGE_SEPARATOR, out); |
129 | fputs(_("Run a program with different privilege settings.\n"), out); | |
130 | ||
5600c405 | 131 | fputs(USAGE_OPTIONS, out); |
4fb515f9 KZ |
132 | fputs(_(" -d, --dump show current state (and do not exec)\n"), out); |
133 | fputs(_(" --nnp, --no-new-privs disallow granting new privileges\n"), out); | |
134 | fputs(_(" --ambient-caps <caps,...> set ambient capabilities\n"), out); | |
135 | fputs(_(" --inh-caps <caps,...> set inheritable capabilities\n"), out); | |
136 | fputs(_(" --bounding-set <caps> set capability bounding set\n"), out); | |
99179a0a KZ |
137 | fputs(_(" --ruid <uid|user> set real uid\n"), out); |
138 | fputs(_(" --euid <uid|user> set effective uid\n"), out); | |
139 | fputs(_(" --rgid <gid|user> set real gid\n"), out); | |
140 | fputs(_(" --egid <gid|group> set effective gid\n"), out); | |
141 | fputs(_(" --reuid <uid|user> set real and effective uid\n"), out); | |
142 | fputs(_(" --regid <gid|group> set real and effective gid\n"), out); | |
4fb515f9 KZ |
143 | fputs(_(" --clear-groups clear supplementary groups\n"), out); |
144 | fputs(_(" --keep-groups keep supplementary groups\n"), out); | |
145 | fputs(_(" --init-groups initialize supplementary groups\n"), out); | |
85c15c1f | 146 | fputs(_(" --groups <group,...> set supplementary groups by UID or name\n"), out); |
4fb515f9 | 147 | fputs(_(" --securebits <bits> set securebits\n"), out); |
23f54ce7 PS |
148 | fputs(_(" --pdeathsig keep|clear|<signame>\n" |
149 | " set or clear parent death signal\n"), out); | |
4fb515f9 KZ |
150 | fputs(_(" --selinux-label <label> set SELinux label\n"), out); |
151 | fputs(_(" --apparmor-profile <pr> set AppArmor profile\n"), out); | |
89f95425 KZ |
152 | fputs(_(" --reset-env clear all environment and initialize\n" |
153 | " HOME, SHELL, USER, LOGNAME and PATH\n"), out); | |
298dc4ff | 154 | |
5600c405 | 155 | fputs(USAGE_SEPARATOR, out); |
f45f3ec3 | 156 | printf(USAGE_HELP_OPTIONS(29)); |
5600c405 AL |
157 | fputs(USAGE_SEPARATOR, out); |
158 | fputs(_(" This tool can be dangerous. Read the manpage, and be careful.\n"), out); | |
f45f3ec3 | 159 | printf(USAGE_MAN_TAIL("setpriv(1)")); |
5600c405 | 160 | |
86be6a32 | 161 | exit(EXIT_SUCCESS); |
5600c405 AL |
162 | } |
163 | ||
164 | static int real_cap_last_cap(void) | |
165 | { | |
166 | /* CAP_LAST_CAP is untrustworthy. */ | |
167 | static int ret = -1; | |
168 | int matched; | |
169 | FILE *f; | |
170 | ||
171 | if (ret != -1) | |
172 | return ret; | |
173 | ||
59c68b43 | 174 | f = fopen(_PATH_PROC_CAPLASTCAP, "r"); |
5600c405 AL |
175 | if (!f) { |
176 | ret = CAP_LAST_CAP; /* guess */ | |
177 | return ret; | |
178 | } | |
179 | ||
180 | matched = fscanf(f, "%d", &ret); | |
181 | fclose(f); | |
182 | ||
183 | if (matched != 1) | |
184 | ret = CAP_LAST_CAP; /* guess */ | |
185 | ||
186 | return ret; | |
187 | } | |
188 | ||
30129e2f PS |
189 | static int has_cap(enum cap_type which, unsigned int i) |
190 | { | |
191 | switch (which) { | |
192 | case CAP_TYPE_EFFECTIVE: | |
193 | case CAP_TYPE_BOUNDING: | |
194 | case CAP_TYPE_INHERITABLE: | |
195 | case CAP_TYPE_PERMITTED: | |
d14bcd09 | 196 | return capng_have_capability((capng_type_t)which, i); |
05a22eac PS |
197 | case CAP_TYPE_AMBIENT: |
198 | return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET, | |
199 | (unsigned long) i, 0UL, 0UL); | |
30129e2f PS |
200 | default: |
201 | warnx(_("invalid capability type")); | |
202 | return -1; | |
203 | } | |
204 | } | |
205 | ||
5600c405 | 206 | /* Returns the number of capabilities printed. */ |
9e5dd89d | 207 | static int print_caps(FILE *f, enum cap_type which) |
5600c405 AL |
208 | { |
209 | int i, n = 0, max = real_cap_last_cap(); | |
210 | ||
211 | for (i = 0; i <= max; i++) { | |
30129e2f PS |
212 | int ret = has_cap(which, i); |
213 | ||
214 | if (i == 0 && ret < 0) | |
215 | return -1; | |
216 | ||
217 | if (ret == 1) { | |
5600c405 AL |
218 | const char *name = capng_capability_to_name(i); |
219 | if (n) | |
220 | fputc(',', f); | |
221 | if (name) | |
222 | fputs(name, f); | |
223 | else | |
224 | /* cap-ng has very poor handling of | |
225 | * CAP_LAST_CAP changes. This is the | |
226 | * best we can do. */ | |
227 | printf("cap_%d", i); | |
228 | n++; | |
229 | } | |
230 | } | |
9e5dd89d | 231 | |
5600c405 AL |
232 | return n; |
233 | } | |
234 | ||
235 | static void dump_one_secbit(int *first, int *bits, int bit, const char *name) | |
236 | { | |
237 | if (*bits & bit) { | |
7d55b2df | 238 | if (*first) |
5600c405 | 239 | *first = 0; |
7d55b2df SK |
240 | else |
241 | printf(","); | |
5600c405 AL |
242 | fputs(name, stdout); |
243 | *bits &= ~bit; | |
244 | } | |
245 | } | |
246 | ||
247 | static void dump_securebits(void) | |
248 | { | |
249 | int first = 1; | |
250 | int bits = prctl(PR_GET_SECUREBITS, 0, 0, 0, 0); | |
251 | ||
252 | if (bits < 0) { | |
253 | warnx(_("getting process secure bits failed")); | |
254 | return; | |
255 | } | |
256 | ||
257 | printf(_("Securebits: ")); | |
258 | ||
259 | dump_one_secbit(&first, &bits, SECBIT_NOROOT, "noroot"); | |
260 | dump_one_secbit(&first, &bits, SECBIT_NOROOT_LOCKED, "noroot_locked"); | |
261 | dump_one_secbit(&first, &bits, SECBIT_NO_SETUID_FIXUP, | |
262 | "no_setuid_fixup"); | |
263 | dump_one_secbit(&first, &bits, SECBIT_NO_SETUID_FIXUP_LOCKED, | |
264 | "no_setuid_fixup_locked"); | |
265 | bits &= ~SECBIT_KEEP_CAPS; | |
266 | dump_one_secbit(&first, &bits, SECBIT_KEEP_CAPS_LOCKED, | |
267 | "keep_caps_locked"); | |
268 | if (bits) { | |
7d55b2df | 269 | if (first) |
5600c405 | 270 | first = 0; |
7d55b2df SK |
271 | else |
272 | printf(","); | |
5600c405 AL |
273 | printf("0x%x", (unsigned)bits); |
274 | } | |
275 | ||
276 | if (first) | |
277 | printf(_("[none]\n")); | |
278 | else | |
279 | printf("\n"); | |
280 | } | |
281 | ||
282 | static void dump_label(const char *name) | |
283 | { | |
284 | char buf[4097]; | |
285 | ssize_t len; | |
286 | int fd, e; | |
287 | ||
59c68b43 | 288 | fd = open(_PATH_PROC_ATTR_CURRENT, O_RDONLY); |
5600c405 | 289 | if (fd == -1) { |
59c68b43 | 290 | warn(_("cannot open %s"), _PATH_PROC_ATTR_CURRENT); |
5600c405 AL |
291 | return; |
292 | } | |
293 | ||
294 | len = read(fd, buf, sizeof(buf)); | |
295 | e = errno; | |
296 | close(fd); | |
297 | if (len < 0) { | |
298 | errno = e; | |
47481cbd | 299 | warn(_("cannot read %s"), name); |
5600c405 AL |
300 | return; |
301 | } | |
302 | if (sizeof(buf) - 1 <= (size_t)len) { | |
303 | warnx(_("%s: too long"), name); | |
304 | return; | |
305 | } | |
306 | ||
307 | buf[len] = 0; | |
308 | if (0 < len && buf[len - 1] == '\n') | |
309 | buf[len - 1] = 0; | |
310 | printf("%s: %s\n", name, buf); | |
311 | } | |
312 | ||
313 | static void dump_groups(void) | |
314 | { | |
87918040 | 315 | int n = getgroups(0, NULL); |
5600c405 | 316 | gid_t *groups; |
59c68b43 | 317 | |
5600c405 AL |
318 | if (n < 0) { |
319 | warn("getgroups failed"); | |
320 | return; | |
321 | } | |
322 | ||
7370501f | 323 | groups = xmalloc(n * sizeof(gid_t)); |
5600c405 AL |
324 | n = getgroups(n, groups); |
325 | if (n < 0) { | |
7370501f | 326 | free(groups); |
5600c405 AL |
327 | warn("getgroups failed"); |
328 | return; | |
329 | } | |
330 | ||
331 | printf(_("Supplementary groups: ")); | |
332 | if (n == 0) | |
333 | printf(_("[none]")); | |
334 | else { | |
335 | int i; | |
336 | for (i = 0; i < n; i++) { | |
337 | if (0 < i) | |
338 | printf(","); | |
339 | printf("%ld", (long)groups[i]); | |
340 | } | |
341 | } | |
342 | printf("\n"); | |
7370501f | 343 | free(groups); |
5600c405 AL |
344 | } |
345 | ||
23f54ce7 PS |
346 | static void dump_pdeathsig(void) |
347 | { | |
348 | int pdeathsig; | |
349 | ||
350 | if (prctl(PR_GET_PDEATHSIG, &pdeathsig) != 0) { | |
351 | warn(_("get pdeathsig failed")); | |
352 | return; | |
353 | } | |
354 | ||
355 | printf("Parent death signal: "); | |
356 | if (pdeathsig && signum_to_signame(pdeathsig) != NULL) | |
357 | printf("%s\n", signum_to_signame(pdeathsig)); | |
358 | else if (pdeathsig) | |
359 | printf("%d\n", pdeathsig); | |
360 | else | |
361 | printf("[none]\n"); | |
362 | } | |
363 | ||
5600c405 AL |
364 | static void dump(int dumplevel) |
365 | { | |
366 | int x; | |
367 | uid_t ru, eu, su; | |
368 | gid_t rg, eg, sg; | |
369 | ||
370 | if (getresuid(&ru, &eu, &su) == 0) { | |
371 | printf(_("uid: %u\n"), ru); | |
372 | printf(_("euid: %u\n"), eu); | |
373 | /* Saved and fs uids always equal euid. */ | |
374 | if (3 <= dumplevel) | |
375 | printf(_("suid: %u\n"), su); | |
376 | } else | |
377 | warn(_("getresuid failed")); | |
378 | ||
379 | if (getresgid(&rg, &eg, &sg) == 0) { | |
380 | printf("gid: %ld\n", (long)rg); | |
381 | printf("egid: %ld\n", (long)eg); | |
382 | /* Saved and fs gids always equal egid. */ | |
383 | if (dumplevel >= 3) | |
384 | printf("sgid: %ld\n", (long)sg); | |
385 | } else | |
386 | warn(_("getresgid failed")); | |
387 | ||
388 | dump_groups(); | |
389 | ||
390 | x = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0); | |
391 | if (0 <= x) | |
392 | printf("no_new_privs: %d\n", x); | |
393 | else | |
394 | warn("setting no_new_privs failed"); | |
395 | ||
396 | if (2 <= dumplevel) { | |
397 | printf(_("Effective capabilities: ")); | |
9e5dd89d | 398 | if (print_caps(stdout, CAP_TYPE_EFFECTIVE) == 0) |
5600c405 AL |
399 | printf(_("[none]")); |
400 | printf("\n"); | |
401 | ||
402 | printf(_("Permitted capabilities: ")); | |
9e5dd89d | 403 | if (print_caps(stdout, CAP_TYPE_PERMITTED) == 0) |
5600c405 AL |
404 | printf(_("[none]")); |
405 | printf("\n"); | |
406 | } | |
407 | ||
408 | printf(_("Inheritable capabilities: ")); | |
9e5dd89d | 409 | if (print_caps(stdout, CAP_TYPE_INHERITABLE) == 0) |
5600c405 AL |
410 | printf(_("[none]")); |
411 | printf("\n"); | |
412 | ||
05a22eac PS |
413 | printf(_("Ambient capabilities: ")); |
414 | x = print_caps(stdout, CAP_TYPE_AMBIENT); | |
415 | if (x == 0) | |
416 | printf(_("[none]")); | |
417 | if (x < 0) | |
418 | printf(_("[unsupported]")); | |
419 | printf("\n"); | |
420 | ||
5600c405 | 421 | printf(_("Capability bounding set: ")); |
9e5dd89d | 422 | if (print_caps(stdout, CAP_TYPE_BOUNDING) == 0) |
5600c405 AL |
423 | printf(_("[none]")); |
424 | printf("\n"); | |
425 | ||
426 | dump_securebits(); | |
23f54ce7 | 427 | dump_pdeathsig(); |
5600c405 | 428 | |
59c68b43 | 429 | if (access(_PATH_SYS_SELINUX, F_OK) == 0) |
5600c405 AL |
430 | dump_label(_("SELinux label")); |
431 | ||
59c68b43 | 432 | if (access(_PATH_SYS_APPARMOR, F_OK) == 0) { |
5600c405 AL |
433 | dump_label(_("AppArmor profile")); |
434 | } | |
435 | } | |
436 | ||
437 | static void list_known_caps(void) | |
438 | { | |
439 | int i, max = real_cap_last_cap(); | |
440 | ||
441 | for (i = 0; i <= max; i++) { | |
442 | const char *name = capng_capability_to_name(i); | |
443 | if (name) | |
444 | printf("%s\n", name); | |
445 | else | |
446 | warnx(_("cap %d: libcap-ng is broken"), i); | |
447 | } | |
448 | } | |
449 | ||
450 | static void parse_groups(struct privctx *opts, const char *str) | |
451 | { | |
452 | char *groups = xstrdup(str); | |
453 | char *buf = groups; /* We'll reuse it */ | |
454 | char *c; | |
455 | size_t i = 0; | |
456 | ||
457 | opts->have_groups = 1; | |
458 | opts->num_groups = 0; | |
459 | while ((c = strsep(&groups, ","))) | |
460 | opts->num_groups++; | |
461 | ||
462 | /* Start again */ | |
463 | strcpy(buf, str); /* It's exactly the right length */ | |
464 | groups = buf; | |
465 | ||
466 | opts->groups = xcalloc(opts->num_groups, sizeof(gid_t)); | |
467 | while ((c = strsep(&groups, ","))) | |
85c15c1f | 468 | opts->groups[i++] = get_group(c, _("Invalid supplementary group id")); |
5600c405 AL |
469 | |
470 | free(groups); | |
471 | } | |
472 | ||
23f54ce7 PS |
473 | static void parse_pdeathsig(struct privctx *opts, const char *str) |
474 | { | |
475 | if (!strcmp(str, "keep")) { | |
476 | if (prctl(PR_GET_PDEATHSIG, &opts->pdeathsig) != 0) | |
477 | errx(SETPRIV_EXIT_PRIVERR, | |
478 | _("failed to get parent death signal")); | |
479 | } else if (!strcmp(str, "clear")) { | |
480 | opts->pdeathsig = -1; | |
481 | } else if ((opts->pdeathsig = signame_to_signum(str)) < 0) { | |
482 | errx(EXIT_FAILURE, _("unknown signal: %s"), str); | |
483 | } | |
484 | } | |
485 | ||
5600c405 AL |
486 | static void do_setresuid(const struct privctx *opts) |
487 | { | |
488 | uid_t ruid, euid, suid; | |
489 | if (getresuid(&ruid, &euid, &suid) != 0) | |
490 | err(SETPRIV_EXIT_PRIVERR, _("getresuid failed")); | |
491 | if (opts->have_ruid) | |
492 | ruid = opts->ruid; | |
493 | if (opts->have_euid) | |
494 | euid = opts->euid; | |
495 | ||
496 | /* Also copy effective to saved (for paranoia). */ | |
497 | if (setresuid(ruid, euid, euid) != 0) | |
498 | err(SETPRIV_EXIT_PRIVERR, _("setresuid failed")); | |
499 | } | |
500 | ||
501 | static void do_setresgid(const struct privctx *opts) | |
502 | { | |
503 | gid_t rgid, egid, sgid; | |
504 | if (getresgid(&rgid, &egid, &sgid) != 0) | |
505 | err(SETPRIV_EXIT_PRIVERR, _("getresgid failed")); | |
506 | if (opts->have_rgid) | |
507 | rgid = opts->rgid; | |
508 | if (opts->have_egid) | |
509 | egid = opts->egid; | |
510 | ||
511 | /* Also copy effective to saved (for paranoia). */ | |
512 | if (setresgid(rgid, egid, egid) != 0) | |
513 | err(SETPRIV_EXIT_PRIVERR, _("setresgid failed")); | |
514 | } | |
515 | ||
516 | static void bump_cap(unsigned int cap) | |
517 | { | |
518 | if (capng_have_capability(CAPNG_PERMITTED, cap)) | |
519 | capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, cap); | |
520 | } | |
521 | ||
8a5af72c PS |
522 | static int cap_update(capng_act_t action, |
523 | enum cap_type type, unsigned int cap) | |
524 | { | |
525 | switch (type) { | |
526 | case CAP_TYPE_EFFECTIVE: | |
527 | case CAP_TYPE_BOUNDING: | |
528 | case CAP_TYPE_INHERITABLE: | |
529 | case CAP_TYPE_PERMITTED: | |
530 | return capng_update(action, (capng_type_t) type, cap); | |
0c92194e PS |
531 | case CAP_TYPE_AMBIENT: |
532 | { | |
533 | int ret; | |
534 | ||
535 | if (action == CAPNG_ADD) | |
536 | ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, | |
537 | (unsigned long) cap, 0UL, 0UL); | |
538 | else | |
539 | ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER, | |
540 | (unsigned long) cap, 0UL, 0UL); | |
541 | ||
542 | return ret; | |
543 | } | |
8a5af72c PS |
544 | default: |
545 | errx(EXIT_FAILURE, _("unsupported capability type")); | |
546 | return -1; | |
547 | } | |
548 | } | |
549 | ||
9e5dd89d | 550 | static void do_caps(enum cap_type type, const char *caps) |
5600c405 AL |
551 | { |
552 | char *my_caps = xstrdup(caps); | |
553 | char *c; | |
554 | ||
555 | while ((c = strsep(&my_caps, ","))) { | |
556 | capng_act_t action; | |
557 | if (*c == '+') | |
558 | action = CAPNG_ADD; | |
559 | else if (*c == '-') | |
560 | action = CAPNG_DROP; | |
561 | else | |
562 | errx(EXIT_FAILURE, _("bad capability string")); | |
563 | ||
564 | if (!strcmp(c + 1, "all")) { | |
565 | int i; | |
566 | /* It would be really bad if -all didn't drop all | |
567 | * caps. It's better to just fail. */ | |
568 | if (real_cap_last_cap() > CAP_LAST_CAP) | |
569 | errx(SETPRIV_EXIT_PRIVERR, | |
570 | _("libcap-ng is too old for \"all\" caps")); | |
571 | for (i = 0; i <= CAP_LAST_CAP; i++) | |
8a5af72c | 572 | cap_update(action, type, i); |
5600c405 AL |
573 | } else { |
574 | int cap = capng_name_to_capability(c + 1); | |
575 | if (0 <= cap) | |
8a5af72c | 576 | cap_update(action, type, cap); |
fbd15c4d PS |
577 | else if (sscanf(c + 1, "cap_%d", &cap) == 1 |
578 | && 0 <= cap && cap <= real_cap_last_cap()) | |
579 | cap_update(action, type, cap); | |
5600c405 AL |
580 | else |
581 | errx(EXIT_FAILURE, | |
582 | _("unknown capability \"%s\""), c + 1); | |
583 | } | |
584 | } | |
585 | ||
586 | free(my_caps); | |
587 | } | |
588 | ||
589 | static void parse_securebits(struct privctx *opts, const char *arg) | |
590 | { | |
591 | char *buf = xstrdup(arg); | |
592 | char *c; | |
593 | ||
594 | opts->have_securebits = 1; | |
595 | opts->securebits = prctl(PR_GET_SECUREBITS, 0, 0, 0, 0); | |
596 | if (opts->securebits < 0) | |
597 | err(SETPRIV_EXIT_PRIVERR, _("getting process secure bits failed")); | |
598 | ||
599 | if (opts->securebits & ~(int)(SECBIT_NOROOT | | |
600 | SECBIT_NOROOT_LOCKED | | |
601 | SECBIT_NO_SETUID_FIXUP | | |
602 | SECBIT_NO_SETUID_FIXUP_LOCKED | | |
603 | SECBIT_KEEP_CAPS | | |
604 | SECBIT_KEEP_CAPS_LOCKED)) | |
605 | errx(SETPRIV_EXIT_PRIVERR, | |
606 | _("unrecognized securebit set -- refusing to adjust")); | |
607 | ||
608 | while ((c = strsep(&buf, ","))) { | |
609 | if (*c != '+' && *c != '-') | |
610 | errx(EXIT_FAILURE, _("bad securebits string")); | |
611 | ||
612 | if (!strcmp(c + 1, "all")) { | |
613 | if (*c == '-') | |
614 | opts->securebits = 0; | |
615 | else | |
616 | errx(EXIT_FAILURE, | |
617 | _("+all securebits is not allowed")); | |
618 | } else { | |
619 | int bit; | |
620 | if (!strcmp(c + 1, "noroot")) | |
621 | bit = SECBIT_NOROOT; | |
622 | else if (!strcmp(c + 1, "noroot_locked")) | |
623 | bit = SECBIT_NOROOT_LOCKED; | |
624 | else if (!strcmp(c + 1, "no_setuid_fixup")) | |
625 | bit = SECBIT_NO_SETUID_FIXUP; | |
626 | else if (!strcmp(c + 1, "no_setuid_fixup_locked")) | |
627 | bit = SECBIT_NO_SETUID_FIXUP_LOCKED; | |
628 | else if (!strcmp(c + 1, "keep_caps")) | |
629 | errx(EXIT_FAILURE, | |
630 | _("adjusting keep_caps does not make sense")); | |
631 | else if (!strcmp(c + 1, "keep_caps_locked")) | |
632 | bit = SECBIT_KEEP_CAPS_LOCKED; /* sigh */ | |
633 | else | |
634 | errx(EXIT_FAILURE, _("unrecognized securebit")); | |
635 | ||
636 | if (*c == '+') | |
637 | opts->securebits |= bit; | |
638 | else | |
639 | opts->securebits &= ~bit; | |
640 | } | |
641 | } | |
642 | ||
643 | opts->securebits |= SECBIT_KEEP_CAPS; /* We need it, and it's reset on exec */ | |
644 | ||
645 | free(buf); | |
646 | } | |
647 | ||
648 | static void do_selinux_label(const char *label) | |
649 | { | |
650 | int fd; | |
651 | size_t len; | |
652 | ||
59c68b43 | 653 | if (access(_PATH_SYS_SELINUX, F_OK) != 0) |
5600c405 AL |
654 | errx(SETPRIV_EXIT_PRIVERR, _("SELinux is not running")); |
655 | ||
59c68b43 | 656 | fd = open(_PATH_PROC_ATTR_EXEC, O_RDWR); |
5600c405 AL |
657 | if (fd == -1) |
658 | err(SETPRIV_EXIT_PRIVERR, | |
59c68b43 | 659 | _("cannot open %s"), _PATH_PROC_ATTR_EXEC); |
5600c405 AL |
660 | |
661 | len = strlen(label); | |
662 | errno = 0; | |
663 | if (write(fd, label, len) != (ssize_t) len) | |
664 | err(SETPRIV_EXIT_PRIVERR, | |
59c68b43 | 665 | _("write failed: %s"), _PATH_PROC_ATTR_EXEC); |
5600c405 | 666 | |
cc89383b | 667 | if (close(fd) != 0) |
05cef8ea | 668 | err(SETPRIV_EXIT_PRIVERR, |
cc89383b | 669 | _("close failed: %s"), _PATH_PROC_ATTR_EXEC); |
5600c405 AL |
670 | } |
671 | ||
672 | static void do_apparmor_profile(const char *label) | |
673 | { | |
674 | FILE *f; | |
675 | ||
59c68b43 | 676 | if (access(_PATH_SYS_APPARMOR, F_OK) != 0) |
5600c405 AL |
677 | errx(SETPRIV_EXIT_PRIVERR, _("AppArmor is not running")); |
678 | ||
d359c62c | 679 | f = fopen(_PATH_PROC_ATTR_EXEC, "r+"); |
5600c405 AL |
680 | if (!f) |
681 | err(SETPRIV_EXIT_PRIVERR, | |
59c68b43 | 682 | _("cannot open %s"), _PATH_PROC_ATTR_EXEC); |
5600c405 | 683 | |
d359c62c | 684 | fprintf(f, "exec %s", label); |
f99b58b3 SK |
685 | |
686 | if (close_stream(f) != 0) | |
5600c405 | 687 | err(SETPRIV_EXIT_PRIVERR, |
59c68b43 | 688 | _("write failed: %s"), _PATH_PROC_ATTR_EXEC); |
5600c405 AL |
689 | } |
690 | ||
89f95425 KZ |
691 | |
692 | static void do_reset_environ(struct passwd *pw) | |
693 | { | |
694 | char *term = getenv("TERM"); | |
695 | ||
696 | if (term) | |
697 | term = xstrdup(term); | |
698 | #ifdef HAVE_CLEARENV | |
699 | clearenv(); | |
700 | #else | |
701 | environ = NULL; | |
702 | #endif | |
da14a74d | 703 | if (term) { |
89f95425 | 704 | xsetenv("TERM", term, 1); |
da14a74d KZ |
705 | free(term); |
706 | } | |
89f95425 KZ |
707 | |
708 | if (pw->pw_shell && *pw->pw_shell) | |
709 | xsetenv("SHELL", pw->pw_shell, 1); | |
710 | else | |
711 | xsetenv("SHELL", DEFAULT_SHELL, 1); | |
712 | ||
713 | xsetenv("HOME", pw->pw_dir, 1); | |
714 | xsetenv("USER", pw->pw_name, 1); | |
715 | xsetenv("LOGNAME", pw->pw_name, 1); | |
716 | ||
717 | if (pw->pw_uid) | |
718 | xsetenv("PATH", _PATH_DEFPATH, 1); | |
719 | else | |
720 | xsetenv("PATH", _PATH_DEFPATH_ROOT, 1); | |
721 | } | |
722 | ||
637fa4c6 SK |
723 | static uid_t get_user(const char *s, const char *err) |
724 | { | |
725 | struct passwd *pw; | |
726 | long tmp; | |
727 | pw = getpwnam(s); | |
728 | if (pw) | |
729 | return pw->pw_uid; | |
730 | tmp = strtol_or_err(s, err); | |
731 | return tmp; | |
732 | } | |
733 | ||
734 | static gid_t get_group(const char *s, const char *err) | |
735 | { | |
736 | struct group *gr; | |
737 | long tmp; | |
738 | gr = getgrnam(s); | |
739 | if (gr) | |
740 | return gr->gr_gid; | |
741 | tmp = strtol_or_err(s, err); | |
742 | return tmp; | |
743 | } | |
744 | ||
94826d0d SS |
745 | static struct passwd *get_passwd(const char *s, uid_t *uid, const char *err) |
746 | { | |
747 | struct passwd *pw; | |
748 | long tmp; | |
749 | pw = getpwnam(s); | |
750 | if (pw) { | |
751 | *uid = pw->pw_uid; | |
752 | } else { | |
753 | tmp = strtol_or_err(s, err); | |
754 | *uid = tmp; | |
755 | pw = getpwuid(*uid); | |
756 | } | |
757 | return pw; | |
758 | } | |
759 | ||
760 | static struct passwd *passwd_copy(struct passwd *dst, const struct passwd *src) | |
761 | { | |
762 | struct passwd *rv; | |
763 | rv = memcpy(dst, src, sizeof(*dst)); | |
764 | rv->pw_name = xstrdup(rv->pw_name); | |
765 | rv->pw_passwd = xstrdup(rv->pw_passwd); | |
766 | rv->pw_gecos = xstrdup(rv->pw_gecos); | |
767 | rv->pw_dir = xstrdup(rv->pw_dir); | |
768 | rv->pw_shell = xstrdup(rv->pw_shell); | |
769 | return rv; | |
770 | } | |
771 | ||
5600c405 AL |
772 | int main(int argc, char **argv) |
773 | { | |
774 | enum { | |
775 | NNP = CHAR_MAX + 1, | |
776 | RUID, | |
777 | EUID, | |
778 | RGID, | |
779 | EGID, | |
780 | REUID, | |
781 | REGID, | |
782 | CLEAR_GROUPS, | |
783 | KEEP_GROUPS, | |
94826d0d | 784 | INIT_GROUPS, |
5600c405 AL |
785 | GROUPS, |
786 | INHCAPS, | |
0c92194e | 787 | AMBCAPS, |
5600c405 AL |
788 | LISTCAPS, |
789 | CAPBSET, | |
790 | SECUREBITS, | |
23f54ce7 | 791 | PDEATHSIG, |
5600c405 | 792 | SELINUX_LABEL, |
89f95425 KZ |
793 | APPARMOR_PROFILE, |
794 | RESET_ENV | |
5600c405 AL |
795 | }; |
796 | ||
797 | static const struct option longopts[] = { | |
87918040 SK |
798 | { "dump", no_argument, NULL, 'd' }, |
799 | { "nnp", no_argument, NULL, NNP }, | |
800 | { "no-new-privs", no_argument, NULL, NNP }, | |
801 | { "inh-caps", required_argument, NULL, INHCAPS }, | |
0c92194e | 802 | { "ambient-caps", required_argument, NULL, AMBCAPS }, |
87918040 SK |
803 | { "list-caps", no_argument, NULL, LISTCAPS }, |
804 | { "ruid", required_argument, NULL, RUID }, | |
805 | { "euid", required_argument, NULL, EUID }, | |
806 | { "rgid", required_argument, NULL, RGID }, | |
807 | { "egid", required_argument, NULL, EGID }, | |
808 | { "reuid", required_argument, NULL, REUID }, | |
809 | { "regid", required_argument, NULL, REGID }, | |
810 | { "clear-groups", no_argument, NULL, CLEAR_GROUPS }, | |
811 | { "keep-groups", no_argument, NULL, KEEP_GROUPS }, | |
94826d0d | 812 | { "init-groups", no_argument, NULL, INIT_GROUPS }, |
87918040 SK |
813 | { "groups", required_argument, NULL, GROUPS }, |
814 | { "bounding-set", required_argument, NULL, CAPBSET }, | |
815 | { "securebits", required_argument, NULL, SECUREBITS }, | |
23f54ce7 | 816 | { "pdeathsig", required_argument, NULL, PDEATHSIG, }, |
87918040 SK |
817 | { "selinux-label", required_argument, NULL, SELINUX_LABEL }, |
818 | { "apparmor-profile", required_argument, NULL, APPARMOR_PROFILE }, | |
819 | { "help", no_argument, NULL, 'h' }, | |
89f95425 | 820 | { "reset-env", no_argument, NULL, RESET_ENV, }, |
87918040 SK |
821 | { "version", no_argument, NULL, 'V' }, |
822 | { NULL, 0, NULL, 0 } | |
5600c405 AL |
823 | }; |
824 | ||
825 | static const ul_excl_t excl[] = { | |
826 | /* keep in same order with enum definitions */ | |
94826d0d | 827 | {CLEAR_GROUPS, KEEP_GROUPS, INIT_GROUPS, GROUPS}, |
5600c405 AL |
828 | {0} |
829 | }; | |
830 | int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT; | |
831 | ||
832 | int c; | |
833 | struct privctx opts; | |
94826d0d | 834 | struct passwd *pw = NULL; |
5600c405 AL |
835 | int dumplevel = 0; |
836 | int total_opts = 0; | |
837 | int list_caps = 0; | |
838 | ||
a7a5c470 | 839 | setlocale(LC_ALL, ""); |
5600c405 AL |
840 | bindtextdomain(PACKAGE, LOCALEDIR); |
841 | textdomain(PACKAGE); | |
2c308875 | 842 | close_stdout_atexit(); |
5600c405 AL |
843 | |
844 | memset(&opts, 0, sizeof(opts)); | |
845 | ||
846 | while ((c = getopt_long(argc, argv, "+dhV", longopts, NULL)) != -1) { | |
847 | err_exclusive_options(c, longopts, excl, excl_st); | |
848 | total_opts++; | |
849 | switch (c) { | |
850 | case 'd': | |
851 | dumplevel++; | |
852 | break; | |
853 | case NNP: | |
854 | if (opts.nnp) | |
855 | errx(EXIT_FAILURE, | |
856 | _("duplicate --no-new-privs option")); | |
857 | opts.nnp = 1; | |
858 | break; | |
859 | case RUID: | |
860 | if (opts.have_ruid) | |
861 | errx(EXIT_FAILURE, _("duplicate ruid")); | |
862 | opts.have_ruid = 1; | |
94826d0d SS |
863 | pw = get_passwd(optarg, &opts.ruid, _("failed to parse ruid")); |
864 | if (pw) { | |
865 | passwd_copy(&opts.passwd, pw); | |
866 | opts.have_passwd = 1; | |
867 | } | |
5600c405 AL |
868 | break; |
869 | case EUID: | |
870 | if (opts.have_euid) | |
871 | errx(EXIT_FAILURE, _("duplicate euid")); | |
872 | opts.have_euid = 1; | |
637fa4c6 | 873 | opts.euid = get_user(optarg, _("failed to parse euid")); |
5600c405 AL |
874 | break; |
875 | case REUID: | |
876 | if (opts.have_ruid || opts.have_euid) | |
877 | errx(EXIT_FAILURE, _("duplicate ruid or euid")); | |
878 | opts.have_ruid = opts.have_euid = 1; | |
94826d0d SS |
879 | pw = get_passwd(optarg, &opts.ruid, _("failed to parse reuid")); |
880 | opts.euid = opts.ruid; | |
881 | if (pw) { | |
882 | passwd_copy(&opts.passwd, pw); | |
883 | opts.have_passwd = 1; | |
884 | } | |
5600c405 AL |
885 | break; |
886 | case RGID: | |
887 | if (opts.have_rgid) | |
888 | errx(EXIT_FAILURE, _("duplicate rgid")); | |
889 | opts.have_rgid = 1; | |
637fa4c6 | 890 | opts.rgid = get_group(optarg, _("failed to parse rgid")); |
5600c405 AL |
891 | break; |
892 | case EGID: | |
893 | if (opts.have_egid) | |
894 | errx(EXIT_FAILURE, _("duplicate egid")); | |
895 | opts.have_egid = 1; | |
637fa4c6 | 896 | opts.egid = get_group(optarg, _("failed to parse egid")); |
5600c405 AL |
897 | break; |
898 | case REGID: | |
899 | if (opts.have_rgid || opts.have_egid) | |
900 | errx(EXIT_FAILURE, _("duplicate rgid or egid")); | |
901 | opts.have_rgid = opts.have_egid = 1; | |
637fa4c6 | 902 | opts.rgid = opts.egid = get_group(optarg, _("failed to parse regid")); |
5600c405 AL |
903 | break; |
904 | case CLEAR_GROUPS: | |
905 | if (opts.clear_groups) | |
906 | errx(EXIT_FAILURE, | |
907 | _("duplicate --clear-groups option")); | |
908 | opts.clear_groups = 1; | |
909 | break; | |
910 | case KEEP_GROUPS: | |
911 | if (opts.keep_groups) | |
912 | errx(EXIT_FAILURE, | |
913 | _("duplicate --keep-groups option")); | |
914 | opts.keep_groups = 1; | |
915 | break; | |
94826d0d SS |
916 | case INIT_GROUPS: |
917 | if (opts.init_groups) | |
918 | errx(EXIT_FAILURE, | |
919 | _("duplicate --init-groups option")); | |
920 | opts.init_groups = 1; | |
921 | break; | |
5600c405 AL |
922 | case GROUPS: |
923 | if (opts.have_groups) | |
924 | errx(EXIT_FAILURE, | |
925 | _("duplicate --groups option")); | |
926 | parse_groups(&opts, optarg); | |
927 | break; | |
23f54ce7 PS |
928 | case PDEATHSIG: |
929 | if (opts.pdeathsig) | |
930 | errx(EXIT_FAILURE, | |
931 | _("duplicate --keep-pdeathsig option")); | |
932 | parse_pdeathsig(&opts, optarg); | |
933 | break; | |
5600c405 AL |
934 | case LISTCAPS: |
935 | list_caps = 1; | |
936 | break; | |
937 | case INHCAPS: | |
938 | if (opts.caps_to_inherit) | |
939 | errx(EXIT_FAILURE, | |
db663995 | 940 | _("duplicate --inh-caps option")); |
5600c405 AL |
941 | opts.caps_to_inherit = optarg; |
942 | break; | |
0c92194e PS |
943 | case AMBCAPS: |
944 | if (opts.ambient_caps) | |
945 | errx(EXIT_FAILURE, | |
946 | _("duplicate --ambient-caps option")); | |
947 | opts.ambient_caps = optarg; | |
948 | break; | |
5600c405 AL |
949 | case CAPBSET: |
950 | if (opts.bounding_set) | |
951 | errx(EXIT_FAILURE, | |
952 | _("duplicate --bounding-set option")); | |
953 | opts.bounding_set = optarg; | |
954 | break; | |
955 | case SECUREBITS: | |
956 | if (opts.have_securebits) | |
957 | errx(EXIT_FAILURE, | |
958 | _("duplicate --securebits option")); | |
959 | parse_securebits(&opts, optarg); | |
960 | break; | |
961 | case SELINUX_LABEL: | |
962 | if (opts.selinux_label) | |
963 | errx(EXIT_FAILURE, | |
964 | _("duplicate --selinux-label option")); | |
965 | opts.selinux_label = optarg; | |
966 | break; | |
967 | case APPARMOR_PROFILE: | |
968 | if (opts.apparmor_profile) | |
969 | errx(EXIT_FAILURE, | |
970 | _("duplicate --apparmor-profile option")); | |
971 | opts.apparmor_profile = optarg; | |
972 | break; | |
89f95425 KZ |
973 | case RESET_ENV: |
974 | opts.reset_env = 1; | |
975 | break; | |
2c308875 | 976 | |
5600c405 | 977 | case 'h': |
86be6a32 | 978 | usage(); |
5600c405 | 979 | case 'V': |
2c308875 | 980 | print_version(EXIT_SUCCESS); |
5600c405 | 981 | default: |
677ec86c | 982 | errtryhelp(EXIT_FAILURE); |
5600c405 AL |
983 | } |
984 | } | |
985 | ||
986 | if (dumplevel) { | |
987 | if (total_opts != dumplevel || optind < argc) | |
988 | errx(EXIT_FAILURE, | |
989 | _("--dump is incompatible with all other options")); | |
990 | dump(dumplevel); | |
991 | return EXIT_SUCCESS; | |
992 | } | |
993 | ||
994 | if (list_caps) { | |
995 | if (total_opts != 1 || optind < argc) | |
996 | errx(EXIT_FAILURE, | |
997 | _("--list-caps must be specified alone")); | |
998 | list_known_caps(); | |
999 | return EXIT_SUCCESS; | |
1000 | } | |
1001 | ||
1002 | if (argc <= optind) | |
1003 | errx(EXIT_FAILURE, _("No program specified")); | |
1004 | ||
1005 | if ((opts.have_rgid || opts.have_egid) | |
94826d0d SS |
1006 | && !opts.keep_groups && !opts.clear_groups && !opts.init_groups |
1007 | && !opts.have_groups) | |
1008 | errx(EXIT_FAILURE, | |
1009 | _("--[re]gid requires --keep-groups, --clear-groups, --init-groups, or --groups")); | |
1010 | ||
1011 | if (opts.init_groups && !opts.have_ruid) | |
1012 | errx(EXIT_FAILURE, | |
1013 | _("--init-groups requires --ruid or --reuid")); | |
1014 | ||
1015 | if (opts.init_groups && !opts.have_passwd) | |
5600c405 | 1016 | errx(EXIT_FAILURE, |
94826d0d SS |
1017 | _("uid %ld not found, --init-groups requires an user that " |
1018 | "can be found on the system"), | |
1019 | (long) opts.ruid); | |
5600c405 | 1020 | |
89f95425 KZ |
1021 | if (opts.reset_env) { |
1022 | if (opts.have_passwd) | |
1023 | /* pwd according to --ruid or --reuid */ | |
1024 | pw = &opts.passwd; | |
1025 | else | |
1026 | /* pwd for the current user */ | |
1027 | pw = getpwuid(getuid()); | |
1028 | do_reset_environ(pw); | |
1029 | } | |
1030 | ||
74ce680a SK |
1031 | if (opts.nnp && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) |
1032 | err(EXIT_FAILURE, _("disallow granting new privileges failed")); | |
5600c405 AL |
1033 | |
1034 | if (opts.selinux_label) | |
1035 | do_selinux_label(opts.selinux_label); | |
1036 | if (opts.apparmor_profile) | |
1037 | do_apparmor_profile(opts.apparmor_profile); | |
1038 | ||
1039 | if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) | |
1040 | err(EXIT_FAILURE, _("keep process capabilities failed")); | |
1041 | ||
1042 | /* We're going to want CAP_SETPCAP, CAP_SETUID, and CAP_SETGID if | |
1043 | * possible. */ | |
1044 | bump_cap(CAP_SETPCAP); | |
1045 | bump_cap(CAP_SETUID); | |
1046 | bump_cap(CAP_SETGID); | |
1047 | if (capng_apply(CAPNG_SELECT_CAPS) != 0) | |
1048 | err(SETPRIV_EXIT_PRIVERR, _("activate capabilities")); | |
1049 | ||
1050 | if (opts.have_ruid || opts.have_euid) { | |
1051 | do_setresuid(&opts); | |
1052 | /* KEEPCAPS doesn't work for the effective mask. */ | |
1053 | if (capng_apply(CAPNG_SELECT_CAPS) != 0) | |
1054 | err(SETPRIV_EXIT_PRIVERR, _("reactivate capabilities")); | |
1055 | } | |
1056 | ||
1057 | if (opts.have_rgid || opts.have_egid) | |
1058 | do_setresgid(&opts); | |
1059 | ||
1060 | if (opts.have_groups) { | |
1061 | if (setgroups(opts.num_groups, opts.groups) != 0) | |
1062 | err(SETPRIV_EXIT_PRIVERR, _("setgroups failed")); | |
94826d0d SS |
1063 | } else if (opts.init_groups) { |
1064 | if (initgroups(opts.passwd.pw_name, opts.passwd.pw_gid) != 0) | |
1065 | err(SETPRIV_EXIT_PRIVERR, _("initgroups failed")); | |
5600c405 AL |
1066 | } else if (opts.clear_groups) { |
1067 | gid_t x = 0; | |
1068 | if (setgroups(0, &x) != 0) | |
1069 | err(SETPRIV_EXIT_PRIVERR, _("setgroups failed")); | |
1070 | } | |
1071 | ||
74ce680a SK |
1072 | if (opts.have_securebits && prctl(PR_SET_SECUREBITS, opts.securebits, 0, 0, 0) != 0) |
1073 | err(SETPRIV_EXIT_PRIVERR, _("set process securebits failed")); | |
5600c405 AL |
1074 | |
1075 | if (opts.bounding_set) { | |
9e5dd89d | 1076 | do_caps(CAP_TYPE_BOUNDING, opts.bounding_set); |
5600c405 AL |
1077 | errno = EPERM; /* capng doesn't set errno if we're missing CAP_SETPCAP */ |
1078 | if (capng_apply(CAPNG_SELECT_BOUNDS) != 0) | |
1079 | err(SETPRIV_EXIT_PRIVERR, _("apply bounding set")); | |
1080 | } | |
1081 | ||
1082 | if (opts.caps_to_inherit) { | |
9e5dd89d | 1083 | do_caps(CAP_TYPE_INHERITABLE, opts.caps_to_inherit); |
5600c405 AL |
1084 | if (capng_apply(CAPNG_SELECT_CAPS) != 0) |
1085 | err(SETPRIV_EXIT_PRIVERR, _("apply capabilities")); | |
1086 | } | |
1087 | ||
0c92194e PS |
1088 | if (opts.ambient_caps) { |
1089 | do_caps(CAP_TYPE_AMBIENT, opts.ambient_caps); | |
1090 | } | |
1091 | ||
23f54ce7 PS |
1092 | /* Clear or set parent death signal */ |
1093 | if (opts.pdeathsig && prctl(PR_SET_PDEATHSIG, opts.pdeathsig < 0 ? 0 : opts.pdeathsig) != 0) | |
1094 | err(SETPRIV_EXIT_PRIVERR, _("set parent death signal failed")); | |
1095 | ||
5600c405 | 1096 | execvp(argv[optind], argv + optind); |
fd777151 | 1097 | errexec(argv[optind]); |
5600c405 | 1098 | } |