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