2013-01-14 09:58:57 -06:00
|
|
|
/*
|
|
|
|
* setpriv(1) - set various kernel privilege bits and run something
|
|
|
|
*
|
|
|
|
* Copyright (C) 2012 Andy Lutomirski <luto@amacapital.net>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
|
|
* under the terms of the GNU General Public License as published by the
|
|
|
|
* Free Software Foundation; either version 2, or (at your option) any
|
|
|
|
* later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful, but
|
|
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License along
|
|
|
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <cap-ng.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <getopt.h>
|
|
|
|
#include <grp.h>
|
|
|
|
#include <linux/securebits.h>
|
2013-04-13 14:54:34 -05:00
|
|
|
#include <pwd.h>
|
2013-01-14 09:58:57 -06:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/prctl.h>
|
2013-04-13 14:54:34 -05:00
|
|
|
#include <sys/types.h>
|
2013-01-14 09:58:57 -06:00
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "c.h"
|
2019-01-17 16:16:54 -06:00
|
|
|
#include "caputils.h"
|
2013-01-14 09:58:57 -06:00
|
|
|
#include "closestream.h"
|
|
|
|
#include "nls.h"
|
|
|
|
#include "optutils.h"
|
|
|
|
#include "strutils.h"
|
|
|
|
#include "xalloc.h"
|
2013-02-05 04:48:55 -06:00
|
|
|
#include "pathnames.h"
|
2018-04-10 06:08:21 -05:00
|
|
|
#include "signames.h"
|
2018-08-22 04:43:32 -05:00
|
|
|
#include "env.h"
|
2013-01-14 09:58:57 -06:00
|
|
|
|
|
|
|
#ifndef PR_SET_NO_NEW_PRIVS
|
|
|
|
# define PR_SET_NO_NEW_PRIVS 38
|
|
|
|
#endif
|
|
|
|
#ifndef PR_GET_NO_NEW_PRIVS
|
|
|
|
# define PR_GET_NO_NEW_PRIVS 39
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define SETPRIV_EXIT_PRIVERR 127 /* how we exit when we fail to set privs */
|
|
|
|
|
2018-08-22 04:43:32 -05:00
|
|
|
/* The shell to set SHELL env.variable if none is given in the user's passwd entry. */
|
|
|
|
#define DEFAULT_SHELL "/bin/sh"
|
|
|
|
|
2018-08-22 03:29:09 -05:00
|
|
|
static gid_t get_group(const char *s, const char *err);
|
|
|
|
|
setpriv: introduce indirection for `capng_type` enum
The capng_type is used to distinguish the different types of capability
sets, that is the effective, inheratibale, permitted capabilities as
well as the capability bounding set. In Linux 4.3, a new set of
capabilities was introduced with ambient capabilities. Unfortunately,
libcap-ng does not provide any support for these kind of capabilities
and as such, we will have to roll our own support.
As a first step, we introduce an indirection for the `capng_type` enum,
allowing us to add the ambient capability type later on. Right now, no
functional change is expected from this change and in fact, each of the
newly introduce enums should have the same value as respective enum of
libcap-ng.
Reviewed-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
2017-06-24 09:04:30 -05:00
|
|
|
enum cap_type {
|
|
|
|
CAP_TYPE_EFFECTIVE = CAPNG_EFFECTIVE,
|
|
|
|
CAP_TYPE_PERMITTED = CAPNG_PERMITTED,
|
|
|
|
CAP_TYPE_INHERITABLE = CAPNG_INHERITABLE,
|
2017-06-24 09:04:33 -05:00
|
|
|
CAP_TYPE_BOUNDING = CAPNG_BOUNDING_SET,
|
|
|
|
CAP_TYPE_AMBIENT = (1 << 4)
|
setpriv: introduce indirection for `capng_type` enum
The capng_type is used to distinguish the different types of capability
sets, that is the effective, inheratibale, permitted capabilities as
well as the capability bounding set. In Linux 4.3, a new set of
capabilities was introduced with ambient capabilities. Unfortunately,
libcap-ng does not provide any support for these kind of capabilities
and as such, we will have to roll our own support.
As a first step, we introduce an indirection for the `capng_type` enum,
allowing us to add the ambient capability type later on. Right now, no
functional change is expected from this change and in fact, each of the
newly introduce enums should have the same value as respective enum of
libcap-ng.
Reviewed-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
2017-06-24 09:04:30 -05:00
|
|
|
};
|
|
|
|
|
2013-01-14 09:58:57 -06:00
|
|
|
/*
|
|
|
|
* Note: We are subject to https://bugzilla.redhat.com/show_bug.cgi?id=895105
|
|
|
|
* and we will therefore have problems if new capabilities are added. Once
|
|
|
|
* that bug is fixed, I'll (Andy Lutomirski) submit a corresponding fix to
|
|
|
|
* setpriv. In the mean time, the code here tries to work reasonably well.
|
|
|
|
*/
|
|
|
|
|
|
|
|
struct privctx {
|
|
|
|
unsigned int
|
|
|
|
nnp:1, /* no_new_privs */
|
|
|
|
have_ruid:1, /* real uid */
|
|
|
|
have_euid:1, /* effective uid */
|
|
|
|
have_rgid:1, /* real gid */
|
|
|
|
have_egid:1, /* effective gid */
|
2017-06-01 09:52:10 -05:00
|
|
|
have_passwd:1, /* passwd entry */
|
2013-01-14 09:58:57 -06:00
|
|
|
have_groups:1, /* add groups */
|
|
|
|
keep_groups:1, /* keep groups */
|
|
|
|
clear_groups:1, /* remove groups */
|
2017-06-01 09:52:10 -05:00
|
|
|
init_groups:1, /* initialize groups */
|
2018-08-22 04:43:32 -05:00
|
|
|
reset_env:1, /* reset environment */
|
2013-01-14 09:58:57 -06:00
|
|
|
have_securebits:1; /* remove groups */
|
|
|
|
|
|
|
|
/* uids and gids */
|
|
|
|
uid_t ruid, euid;
|
|
|
|
gid_t rgid, egid;
|
|
|
|
|
2017-06-01 09:52:10 -05:00
|
|
|
/* real user passwd entry */
|
|
|
|
struct passwd passwd;
|
|
|
|
|
2013-01-14 09:58:57 -06:00
|
|
|
/* supplementary groups */
|
|
|
|
size_t num_groups;
|
|
|
|
gid_t *groups;
|
|
|
|
|
|
|
|
/* caps */
|
|
|
|
const char *caps_to_inherit;
|
2017-06-24 09:04:34 -05:00
|
|
|
const char *ambient_caps;
|
2013-01-14 09:58:57 -06:00
|
|
|
const char *bounding_set;
|
|
|
|
|
|
|
|
/* securebits */
|
|
|
|
int securebits;
|
2018-04-10 06:08:21 -05:00
|
|
|
/* parent death signal (<0 clear, 0 nothing, >0 signal) */
|
|
|
|
int pdeathsig;
|
2013-01-14 09:58:57 -06:00
|
|
|
|
|
|
|
/* LSMs */
|
|
|
|
const char *selinux_label;
|
|
|
|
const char *apparmor_profile;
|
|
|
|
};
|
|
|
|
|
2017-06-20 13:20:29 -05:00
|
|
|
static void __attribute__((__noreturn__)) usage(void)
|
2013-01-14 09:58:57 -06:00
|
|
|
{
|
2017-06-20 13:20:29 -05:00
|
|
|
FILE *out = stdout;
|
2013-01-14 09:58:57 -06:00
|
|
|
fputs(USAGE_HEADER, out);
|
2014-09-22 15:15:39 -05:00
|
|
|
fprintf(out, _(" %s [options] <program> [<argument>...]\n"),
|
|
|
|
program_invocation_short_name);
|
|
|
|
|
2014-12-22 15:57:17 -06:00
|
|
|
fputs(USAGE_SEPARATOR, out);
|
|
|
|
fputs(_("Run a program with different privilege settings.\n"), out);
|
|
|
|
|
2013-01-14 09:58:57 -06:00
|
|
|
fputs(USAGE_OPTIONS, out);
|
2017-06-27 08:10:09 -05:00
|
|
|
fputs(_(" -d, --dump show current state (and do not exec)\n"), out);
|
|
|
|
fputs(_(" --nnp, --no-new-privs disallow granting new privileges\n"), out);
|
|
|
|
fputs(_(" --ambient-caps <caps,...> set ambient capabilities\n"), out);
|
|
|
|
fputs(_(" --inh-caps <caps,...> set inheritable capabilities\n"), out);
|
|
|
|
fputs(_(" --bounding-set <caps> set capability bounding set\n"), out);
|
2018-08-22 03:35:51 -05:00
|
|
|
fputs(_(" --ruid <uid|user> set real uid\n"), out);
|
|
|
|
fputs(_(" --euid <uid|user> set effective uid\n"), out);
|
|
|
|
fputs(_(" --rgid <gid|user> set real gid\n"), out);
|
|
|
|
fputs(_(" --egid <gid|group> set effective gid\n"), out);
|
|
|
|
fputs(_(" --reuid <uid|user> set real and effective uid\n"), out);
|
|
|
|
fputs(_(" --regid <gid|group> set real and effective gid\n"), out);
|
2017-06-27 08:10:09 -05:00
|
|
|
fputs(_(" --clear-groups clear supplementary groups\n"), out);
|
|
|
|
fputs(_(" --keep-groups keep supplementary groups\n"), out);
|
|
|
|
fputs(_(" --init-groups initialize supplementary groups\n"), out);
|
2018-08-22 03:29:09 -05:00
|
|
|
fputs(_(" --groups <group,...> set supplementary groups by UID or name\n"), out);
|
2017-06-27 08:10:09 -05:00
|
|
|
fputs(_(" --securebits <bits> set securebits\n"), out);
|
2018-04-10 06:08:21 -05:00
|
|
|
fputs(_(" --pdeathsig keep|clear|<signame>\n"
|
|
|
|
" set or clear parent death signal\n"), out);
|
2017-06-27 08:10:09 -05:00
|
|
|
fputs(_(" --selinux-label <label> set SELinux label\n"), out);
|
|
|
|
fputs(_(" --apparmor-profile <pr> set AppArmor profile\n"), out);
|
2018-08-22 04:43:32 -05:00
|
|
|
fputs(_(" --reset-env clear all environment and initialize\n"
|
|
|
|
" HOME, SHELL, USER, LOGNAME and PATH\n"), out);
|
2014-09-22 15:15:39 -05:00
|
|
|
|
2013-01-14 09:58:57 -06:00
|
|
|
fputs(USAGE_SEPARATOR, out);
|
2017-06-29 08:52:16 -05:00
|
|
|
printf(USAGE_HELP_OPTIONS(29));
|
2013-01-14 09:58:57 -06:00
|
|
|
fputs(USAGE_SEPARATOR, out);
|
|
|
|
fputs(_(" This tool can be dangerous. Read the manpage, and be careful.\n"), out);
|
2017-06-29 08:52:16 -05:00
|
|
|
printf(USAGE_MAN_TAIL("setpriv(1)"));
|
2013-01-14 09:58:57 -06:00
|
|
|
|
2017-06-20 13:20:29 -05:00
|
|
|
exit(EXIT_SUCCESS);
|
2013-01-14 09:58:57 -06:00
|
|
|
}
|
|
|
|
|
2017-06-24 09:04:31 -05:00
|
|
|
static int has_cap(enum cap_type which, unsigned int i)
|
|
|
|
{
|
|
|
|
switch (which) {
|
|
|
|
case CAP_TYPE_EFFECTIVE:
|
|
|
|
case CAP_TYPE_BOUNDING:
|
|
|
|
case CAP_TYPE_INHERITABLE:
|
|
|
|
case CAP_TYPE_PERMITTED:
|
2017-06-29 10:28:01 -05:00
|
|
|
return capng_have_capability((capng_type_t)which, i);
|
2017-06-24 09:04:33 -05:00
|
|
|
case CAP_TYPE_AMBIENT:
|
|
|
|
return prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_IS_SET,
|
|
|
|
(unsigned long) i, 0UL, 0UL);
|
2017-06-24 09:04:31 -05:00
|
|
|
default:
|
|
|
|
warnx(_("invalid capability type"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-14 09:58:57 -06:00
|
|
|
/* Returns the number of capabilities printed. */
|
setpriv: introduce indirection for `capng_type` enum
The capng_type is used to distinguish the different types of capability
sets, that is the effective, inheratibale, permitted capabilities as
well as the capability bounding set. In Linux 4.3, a new set of
capabilities was introduced with ambient capabilities. Unfortunately,
libcap-ng does not provide any support for these kind of capabilities
and as such, we will have to roll our own support.
As a first step, we introduce an indirection for the `capng_type` enum,
allowing us to add the ambient capability type later on. Right now, no
functional change is expected from this change and in fact, each of the
newly introduce enums should have the same value as respective enum of
libcap-ng.
Reviewed-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
2017-06-24 09:04:30 -05:00
|
|
|
static int print_caps(FILE *f, enum cap_type which)
|
2013-01-14 09:58:57 -06:00
|
|
|
{
|
2019-01-17 16:16:54 -06:00
|
|
|
int i, n = 0, max = cap_last_cap();
|
2013-01-14 09:58:57 -06:00
|
|
|
|
|
|
|
for (i = 0; i <= max; i++) {
|
2017-06-24 09:04:31 -05:00
|
|
|
int ret = has_cap(which, i);
|
|
|
|
|
|
|
|
if (i == 0 && ret < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (ret == 1) {
|
2013-01-14 09:58:57 -06:00
|
|
|
const char *name = capng_capability_to_name(i);
|
|
|
|
if (n)
|
|
|
|
fputc(',', f);
|
|
|
|
if (name)
|
|
|
|
fputs(name, f);
|
|
|
|
else
|
|
|
|
/* cap-ng has very poor handling of
|
|
|
|
* CAP_LAST_CAP changes. This is the
|
|
|
|
* best we can do. */
|
|
|
|
printf("cap_%d", i);
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
}
|
setpriv: introduce indirection for `capng_type` enum
The capng_type is used to distinguish the different types of capability
sets, that is the effective, inheratibale, permitted capabilities as
well as the capability bounding set. In Linux 4.3, a new set of
capabilities was introduced with ambient capabilities. Unfortunately,
libcap-ng does not provide any support for these kind of capabilities
and as such, we will have to roll our own support.
As a first step, we introduce an indirection for the `capng_type` enum,
allowing us to add the ambient capability type later on. Right now, no
functional change is expected from this change and in fact, each of the
newly introduce enums should have the same value as respective enum of
libcap-ng.
Reviewed-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
2017-06-24 09:04:30 -05:00
|
|
|
|
2013-01-14 09:58:57 -06:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dump_one_secbit(int *first, int *bits, int bit, const char *name)
|
|
|
|
{
|
|
|
|
if (*bits & bit) {
|
2016-07-03 06:42:58 -05:00
|
|
|
if (*first)
|
2013-01-14 09:58:57 -06:00
|
|
|
*first = 0;
|
2016-07-03 06:42:58 -05:00
|
|
|
else
|
|
|
|
printf(",");
|
2013-01-14 09:58:57 -06:00
|
|
|
fputs(name, stdout);
|
|
|
|
*bits &= ~bit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dump_securebits(void)
|
|
|
|
{
|
|
|
|
int first = 1;
|
|
|
|
int bits = prctl(PR_GET_SECUREBITS, 0, 0, 0, 0);
|
|
|
|
|
|
|
|
if (bits < 0) {
|
|
|
|
warnx(_("getting process secure bits failed"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf(_("Securebits: "));
|
|
|
|
|
|
|
|
dump_one_secbit(&first, &bits, SECBIT_NOROOT, "noroot");
|
|
|
|
dump_one_secbit(&first, &bits, SECBIT_NOROOT_LOCKED, "noroot_locked");
|
|
|
|
dump_one_secbit(&first, &bits, SECBIT_NO_SETUID_FIXUP,
|
|
|
|
"no_setuid_fixup");
|
|
|
|
dump_one_secbit(&first, &bits, SECBIT_NO_SETUID_FIXUP_LOCKED,
|
|
|
|
"no_setuid_fixup_locked");
|
|
|
|
bits &= ~SECBIT_KEEP_CAPS;
|
|
|
|
dump_one_secbit(&first, &bits, SECBIT_KEEP_CAPS_LOCKED,
|
|
|
|
"keep_caps_locked");
|
|
|
|
if (bits) {
|
2016-07-03 06:42:58 -05:00
|
|
|
if (first)
|
2013-01-14 09:58:57 -06:00
|
|
|
first = 0;
|
2016-07-03 06:42:58 -05:00
|
|
|
else
|
|
|
|
printf(",");
|
2013-01-14 09:58:57 -06:00
|
|
|
printf("0x%x", (unsigned)bits);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (first)
|
|
|
|
printf(_("[none]\n"));
|
|
|
|
else
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dump_label(const char *name)
|
|
|
|
{
|
|
|
|
char buf[4097];
|
|
|
|
ssize_t len;
|
|
|
|
int fd, e;
|
|
|
|
|
2013-02-05 04:48:55 -06:00
|
|
|
fd = open(_PATH_PROC_ATTR_CURRENT, O_RDONLY);
|
2013-01-14 09:58:57 -06:00
|
|
|
if (fd == -1) {
|
2013-02-05 04:48:55 -06:00
|
|
|
warn(_("cannot open %s"), _PATH_PROC_ATTR_CURRENT);
|
2013-01-14 09:58:57 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = read(fd, buf, sizeof(buf));
|
|
|
|
e = errno;
|
|
|
|
close(fd);
|
|
|
|
if (len < 0) {
|
|
|
|
errno = e;
|
2013-10-08 10:04:22 -05:00
|
|
|
warn(_("cannot read %s"), name);
|
2013-01-14 09:58:57 -06:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (sizeof(buf) - 1 <= (size_t)len) {
|
|
|
|
warnx(_("%s: too long"), name);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
buf[len] = 0;
|
|
|
|
if (0 < len && buf[len - 1] == '\n')
|
|
|
|
buf[len - 1] = 0;
|
|
|
|
printf("%s: %s\n", name, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dump_groups(void)
|
|
|
|
{
|
2017-02-11 14:23:26 -06:00
|
|
|
int n = getgroups(0, NULL);
|
2013-01-14 09:58:57 -06:00
|
|
|
gid_t *groups;
|
2013-02-05 04:48:55 -06:00
|
|
|
|
2013-01-14 09:58:57 -06:00
|
|
|
if (n < 0) {
|
|
|
|
warn("getgroups failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-13 11:58:36 -05:00
|
|
|
groups = xmalloc(n * sizeof(gid_t));
|
2013-01-14 09:58:57 -06:00
|
|
|
n = getgroups(n, groups);
|
|
|
|
if (n < 0) {
|
2014-07-13 11:58:36 -05:00
|
|
|
free(groups);
|
2013-01-14 09:58:57 -06:00
|
|
|
warn("getgroups failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf(_("Supplementary groups: "));
|
|
|
|
if (n == 0)
|
|
|
|
printf(_("[none]"));
|
|
|
|
else {
|
|
|
|
int i;
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
if (0 < i)
|
|
|
|
printf(",");
|
|
|
|
printf("%ld", (long)groups[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
printf("\n");
|
2014-07-13 11:58:36 -05:00
|
|
|
free(groups);
|
2013-01-14 09:58:57 -06:00
|
|
|
}
|
|
|
|
|
2018-04-10 06:08:21 -05:00
|
|
|
static void dump_pdeathsig(void)
|
|
|
|
{
|
|
|
|
int pdeathsig;
|
|
|
|
|
|
|
|
if (prctl(PR_GET_PDEATHSIG, &pdeathsig) != 0) {
|
|
|
|
warn(_("get pdeathsig failed"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("Parent death signal: ");
|
|
|
|
if (pdeathsig && signum_to_signame(pdeathsig) != NULL)
|
|
|
|
printf("%s\n", signum_to_signame(pdeathsig));
|
|
|
|
else if (pdeathsig)
|
|
|
|
printf("%d\n", pdeathsig);
|
|
|
|
else
|
|
|
|
printf("[none]\n");
|
|
|
|
}
|
|
|
|
|
2013-01-14 09:58:57 -06:00
|
|
|
static void dump(int dumplevel)
|
|
|
|
{
|
|
|
|
int x;
|
|
|
|
uid_t ru, eu, su;
|
|
|
|
gid_t rg, eg, sg;
|
|
|
|
|
|
|
|
if (getresuid(&ru, &eu, &su) == 0) {
|
|
|
|
printf(_("uid: %u\n"), ru);
|
|
|
|
printf(_("euid: %u\n"), eu);
|
|
|
|
/* Saved and fs uids always equal euid. */
|
|
|
|
if (3 <= dumplevel)
|
|
|
|
printf(_("suid: %u\n"), su);
|
|
|
|
} else
|
|
|
|
warn(_("getresuid failed"));
|
|
|
|
|
|
|
|
if (getresgid(&rg, &eg, &sg) == 0) {
|
|
|
|
printf("gid: %ld\n", (long)rg);
|
|
|
|
printf("egid: %ld\n", (long)eg);
|
|
|
|
/* Saved and fs gids always equal egid. */
|
|
|
|
if (dumplevel >= 3)
|
|
|
|
printf("sgid: %ld\n", (long)sg);
|
|
|
|
} else
|
|
|
|
warn(_("getresgid failed"));
|
|
|
|
|
|
|
|
dump_groups();
|
|
|
|
|
|
|
|
x = prctl(PR_GET_NO_NEW_PRIVS, 0, 0, 0, 0);
|
|
|
|
if (0 <= x)
|
|
|
|
printf("no_new_privs: %d\n", x);
|
|
|
|
else
|
|
|
|
warn("setting no_new_privs failed");
|
|
|
|
|
|
|
|
if (2 <= dumplevel) {
|
|
|
|
printf(_("Effective capabilities: "));
|
setpriv: introduce indirection for `capng_type` enum
The capng_type is used to distinguish the different types of capability
sets, that is the effective, inheratibale, permitted capabilities as
well as the capability bounding set. In Linux 4.3, a new set of
capabilities was introduced with ambient capabilities. Unfortunately,
libcap-ng does not provide any support for these kind of capabilities
and as such, we will have to roll our own support.
As a first step, we introduce an indirection for the `capng_type` enum,
allowing us to add the ambient capability type later on. Right now, no
functional change is expected from this change and in fact, each of the
newly introduce enums should have the same value as respective enum of
libcap-ng.
Reviewed-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
2017-06-24 09:04:30 -05:00
|
|
|
if (print_caps(stdout, CAP_TYPE_EFFECTIVE) == 0)
|
2013-01-14 09:58:57 -06:00
|
|
|
printf(_("[none]"));
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
printf(_("Permitted capabilities: "));
|
setpriv: introduce indirection for `capng_type` enum
The capng_type is used to distinguish the different types of capability
sets, that is the effective, inheratibale, permitted capabilities as
well as the capability bounding set. In Linux 4.3, a new set of
capabilities was introduced with ambient capabilities. Unfortunately,
libcap-ng does not provide any support for these kind of capabilities
and as such, we will have to roll our own support.
As a first step, we introduce an indirection for the `capng_type` enum,
allowing us to add the ambient capability type later on. Right now, no
functional change is expected from this change and in fact, each of the
newly introduce enums should have the same value as respective enum of
libcap-ng.
Reviewed-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
2017-06-24 09:04:30 -05:00
|
|
|
if (print_caps(stdout, CAP_TYPE_PERMITTED) == 0)
|
2013-01-14 09:58:57 -06:00
|
|
|
printf(_("[none]"));
|
|
|
|
printf("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
printf(_("Inheritable capabilities: "));
|
setpriv: introduce indirection for `capng_type` enum
The capng_type is used to distinguish the different types of capability
sets, that is the effective, inheratibale, permitted capabilities as
well as the capability bounding set. In Linux 4.3, a new set of
capabilities was introduced with ambient capabilities. Unfortunately,
libcap-ng does not provide any support for these kind of capabilities
and as such, we will have to roll our own support.
As a first step, we introduce an indirection for the `capng_type` enum,
allowing us to add the ambient capability type later on. Right now, no
functional change is expected from this change and in fact, each of the
newly introduce enums should have the same value as respective enum of
libcap-ng.
Reviewed-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
2017-06-24 09:04:30 -05:00
|
|
|
if (print_caps(stdout, CAP_TYPE_INHERITABLE) == 0)
|
2013-01-14 09:58:57 -06:00
|
|
|
printf(_("[none]"));
|
|
|
|
printf("\n");
|
|
|
|
|
2017-06-24 09:04:33 -05:00
|
|
|
printf(_("Ambient capabilities: "));
|
|
|
|
x = print_caps(stdout, CAP_TYPE_AMBIENT);
|
|
|
|
if (x == 0)
|
|
|
|
printf(_("[none]"));
|
|
|
|
if (x < 0)
|
|
|
|
printf(_("[unsupported]"));
|
|
|
|
printf("\n");
|
|
|
|
|
2013-01-14 09:58:57 -06:00
|
|
|
printf(_("Capability bounding set: "));
|
setpriv: introduce indirection for `capng_type` enum
The capng_type is used to distinguish the different types of capability
sets, that is the effective, inheratibale, permitted capabilities as
well as the capability bounding set. In Linux 4.3, a new set of
capabilities was introduced with ambient capabilities. Unfortunately,
libcap-ng does not provide any support for these kind of capabilities
and as such, we will have to roll our own support.
As a first step, we introduce an indirection for the `capng_type` enum,
allowing us to add the ambient capability type later on. Right now, no
functional change is expected from this change and in fact, each of the
newly introduce enums should have the same value as respective enum of
libcap-ng.
Reviewed-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
2017-06-24 09:04:30 -05:00
|
|
|
if (print_caps(stdout, CAP_TYPE_BOUNDING) == 0)
|
2013-01-14 09:58:57 -06:00
|
|
|
printf(_("[none]"));
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
dump_securebits();
|
2018-04-10 06:08:21 -05:00
|
|
|
dump_pdeathsig();
|
2013-01-14 09:58:57 -06:00
|
|
|
|
2013-02-05 04:48:55 -06:00
|
|
|
if (access(_PATH_SYS_SELINUX, F_OK) == 0)
|
2013-01-14 09:58:57 -06:00
|
|
|
dump_label(_("SELinux label"));
|
|
|
|
|
2013-02-05 04:48:55 -06:00
|
|
|
if (access(_PATH_SYS_APPARMOR, F_OK) == 0) {
|
2013-01-14 09:58:57 -06:00
|
|
|
dump_label(_("AppArmor profile"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void list_known_caps(void)
|
|
|
|
{
|
2019-01-17 16:16:54 -06:00
|
|
|
int i, max = cap_last_cap();
|
2013-01-14 09:58:57 -06:00
|
|
|
|
|
|
|
for (i = 0; i <= max; i++) {
|
|
|
|
const char *name = capng_capability_to_name(i);
|
|
|
|
if (name)
|
|
|
|
printf("%s\n", name);
|
|
|
|
else
|
|
|
|
warnx(_("cap %d: libcap-ng is broken"), i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_groups(struct privctx *opts, const char *str)
|
|
|
|
{
|
|
|
|
char *groups = xstrdup(str);
|
|
|
|
char *buf = groups; /* We'll reuse it */
|
|
|
|
char *c;
|
|
|
|
size_t i = 0;
|
|
|
|
|
|
|
|
opts->have_groups = 1;
|
|
|
|
opts->num_groups = 0;
|
|
|
|
while ((c = strsep(&groups, ",")))
|
|
|
|
opts->num_groups++;
|
|
|
|
|
|
|
|
/* Start again */
|
|
|
|
strcpy(buf, str); /* It's exactly the right length */
|
|
|
|
groups = buf;
|
|
|
|
|
|
|
|
opts->groups = xcalloc(opts->num_groups, sizeof(gid_t));
|
|
|
|
while ((c = strsep(&groups, ",")))
|
2018-08-22 03:29:09 -05:00
|
|
|
opts->groups[i++] = get_group(c, _("Invalid supplementary group id"));
|
2013-01-14 09:58:57 -06:00
|
|
|
|
|
|
|
free(groups);
|
|
|
|
}
|
|
|
|
|
2018-04-10 06:08:21 -05:00
|
|
|
static void parse_pdeathsig(struct privctx *opts, const char *str)
|
|
|
|
{
|
|
|
|
if (!strcmp(str, "keep")) {
|
|
|
|
if (prctl(PR_GET_PDEATHSIG, &opts->pdeathsig) != 0)
|
|
|
|
errx(SETPRIV_EXIT_PRIVERR,
|
|
|
|
_("failed to get parent death signal"));
|
|
|
|
} else if (!strcmp(str, "clear")) {
|
|
|
|
opts->pdeathsig = -1;
|
|
|
|
} else if ((opts->pdeathsig = signame_to_signum(str)) < 0) {
|
|
|
|
errx(EXIT_FAILURE, _("unknown signal: %s"), str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-14 09:58:57 -06:00
|
|
|
static void do_setresuid(const struct privctx *opts)
|
|
|
|
{
|
|
|
|
uid_t ruid, euid, suid;
|
|
|
|
if (getresuid(&ruid, &euid, &suid) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("getresuid failed"));
|
|
|
|
if (opts->have_ruid)
|
|
|
|
ruid = opts->ruid;
|
|
|
|
if (opts->have_euid)
|
|
|
|
euid = opts->euid;
|
|
|
|
|
|
|
|
/* Also copy effective to saved (for paranoia). */
|
|
|
|
if (setresuid(ruid, euid, euid) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("setresuid failed"));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void do_setresgid(const struct privctx *opts)
|
|
|
|
{
|
|
|
|
gid_t rgid, egid, sgid;
|
|
|
|
if (getresgid(&rgid, &egid, &sgid) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("getresgid failed"));
|
|
|
|
if (opts->have_rgid)
|
|
|
|
rgid = opts->rgid;
|
|
|
|
if (opts->have_egid)
|
|
|
|
egid = opts->egid;
|
|
|
|
|
|
|
|
/* Also copy effective to saved (for paranoia). */
|
|
|
|
if (setresgid(rgid, egid, egid) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("setresgid failed"));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bump_cap(unsigned int cap)
|
|
|
|
{
|
|
|
|
if (capng_have_capability(CAPNG_PERMITTED, cap))
|
|
|
|
capng_update(CAPNG_ADD, CAPNG_EFFECTIVE, cap);
|
|
|
|
}
|
|
|
|
|
2017-06-24 09:04:32 -05:00
|
|
|
static int cap_update(capng_act_t action,
|
|
|
|
enum cap_type type, unsigned int cap)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case CAP_TYPE_EFFECTIVE:
|
|
|
|
case CAP_TYPE_BOUNDING:
|
|
|
|
case CAP_TYPE_INHERITABLE:
|
|
|
|
case CAP_TYPE_PERMITTED:
|
|
|
|
return capng_update(action, (capng_type_t) type, cap);
|
2017-06-24 09:04:34 -05:00
|
|
|
case CAP_TYPE_AMBIENT:
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (action == CAPNG_ADD)
|
|
|
|
ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE,
|
|
|
|
(unsigned long) cap, 0UL, 0UL);
|
|
|
|
else
|
|
|
|
ret = prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_LOWER,
|
|
|
|
(unsigned long) cap, 0UL, 0UL);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
2017-06-24 09:04:32 -05:00
|
|
|
default:
|
|
|
|
errx(EXIT_FAILURE, _("unsupported capability type"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
setpriv: introduce indirection for `capng_type` enum
The capng_type is used to distinguish the different types of capability
sets, that is the effective, inheratibale, permitted capabilities as
well as the capability bounding set. In Linux 4.3, a new set of
capabilities was introduced with ambient capabilities. Unfortunately,
libcap-ng does not provide any support for these kind of capabilities
and as such, we will have to roll our own support.
As a first step, we introduce an indirection for the `capng_type` enum,
allowing us to add the ambient capability type later on. Right now, no
functional change is expected from this change and in fact, each of the
newly introduce enums should have the same value as respective enum of
libcap-ng.
Reviewed-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
2017-06-24 09:04:30 -05:00
|
|
|
static void do_caps(enum cap_type type, const char *caps)
|
2013-01-14 09:58:57 -06:00
|
|
|
{
|
|
|
|
char *my_caps = xstrdup(caps);
|
|
|
|
char *c;
|
|
|
|
|
|
|
|
while ((c = strsep(&my_caps, ","))) {
|
|
|
|
capng_act_t action;
|
|
|
|
if (*c == '+')
|
|
|
|
action = CAPNG_ADD;
|
|
|
|
else if (*c == '-')
|
|
|
|
action = CAPNG_DROP;
|
|
|
|
else
|
|
|
|
errx(EXIT_FAILURE, _("bad capability string"));
|
|
|
|
|
|
|
|
if (!strcmp(c + 1, "all")) {
|
|
|
|
int i;
|
|
|
|
/* It would be really bad if -all didn't drop all
|
|
|
|
* caps. It's better to just fail. */
|
2019-01-17 16:16:54 -06:00
|
|
|
if (cap_last_cap() > CAP_LAST_CAP)
|
2013-01-14 09:58:57 -06:00
|
|
|
errx(SETPRIV_EXIT_PRIVERR,
|
|
|
|
_("libcap-ng is too old for \"all\" caps"));
|
|
|
|
for (i = 0; i <= CAP_LAST_CAP; i++)
|
2017-06-24 09:04:32 -05:00
|
|
|
cap_update(action, type, i);
|
2013-01-14 09:58:57 -06:00
|
|
|
} else {
|
|
|
|
int cap = capng_name_to_capability(c + 1);
|
|
|
|
if (0 <= cap)
|
2017-06-24 09:04:32 -05:00
|
|
|
cap_update(action, type, cap);
|
setpriv: support setting unnamed capabilities
When setting capabilities, we accept human readable names like for
example `sys_rawio` or `net_admin`. To do so the translation between the
capability name and its in-kernel index, we rely on the function
`capng_name_to_capability`. When the function does not know the named
capability, it will return an error value and we abort setting the
capability.
This relies upon the ability of libcap to know all capabilities inside
of the kernel. But actually, it is possible that new capabilities are
introduced inside of the Linux kernel which are not recognized yet by
the library. When dumping these unknown capabilities, libcap will simply
return a string like "cap_38", that is it will append the capability's
in-kernel index to the prefix "cap_". This may lead a user to also think
that "cap_38" may be passed to the switches "--inh-caps" or
"--ambient-caps", which is unfortunately not the case.
We can do better here by instead accepting strings in the form of
"cap_N". To do so, we can simply rely on the fact that capability
indices are steadily increasing and that the highest index known to the
kernel is stored inside of the kernel's procfs, made readily available
by our function `real_cap_last_cap()`. So in case libcap does not know a
capability name, we can simply parse the string and, if it is in the
correct format, check whether the detected index is between 0 and the
highest capability index. If so, we can treat it as a valid capability
string and apply it.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
2017-07-17 16:14:09 -05:00
|
|
|
else if (sscanf(c + 1, "cap_%d", &cap) == 1
|
2019-01-17 16:16:54 -06:00
|
|
|
&& 0 <= cap && cap <= cap_last_cap())
|
setpriv: support setting unnamed capabilities
When setting capabilities, we accept human readable names like for
example `sys_rawio` or `net_admin`. To do so the translation between the
capability name and its in-kernel index, we rely on the function
`capng_name_to_capability`. When the function does not know the named
capability, it will return an error value and we abort setting the
capability.
This relies upon the ability of libcap to know all capabilities inside
of the kernel. But actually, it is possible that new capabilities are
introduced inside of the Linux kernel which are not recognized yet by
the library. When dumping these unknown capabilities, libcap will simply
return a string like "cap_38", that is it will append the capability's
in-kernel index to the prefix "cap_". This may lead a user to also think
that "cap_38" may be passed to the switches "--inh-caps" or
"--ambient-caps", which is unfortunately not the case.
We can do better here by instead accepting strings in the form of
"cap_N". To do so, we can simply rely on the fact that capability
indices are steadily increasing and that the highest index known to the
kernel is stored inside of the kernel's procfs, made readily available
by our function `real_cap_last_cap()`. So in case libcap does not know a
capability name, we can simply parse the string and, if it is in the
correct format, check whether the detected index is between 0 and the
highest capability index. If so, we can treat it as a valid capability
string and apply it.
Signed-off-by: Patrick Steinhardt <ps@pks.im>
2017-07-17 16:14:09 -05:00
|
|
|
cap_update(action, type, cap);
|
2013-01-14 09:58:57 -06:00
|
|
|
else
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("unknown capability \"%s\""), c + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(my_caps);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void parse_securebits(struct privctx *opts, const char *arg)
|
|
|
|
{
|
|
|
|
char *buf = xstrdup(arg);
|
|
|
|
char *c;
|
|
|
|
|
|
|
|
opts->have_securebits = 1;
|
|
|
|
opts->securebits = prctl(PR_GET_SECUREBITS, 0, 0, 0, 0);
|
|
|
|
if (opts->securebits < 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("getting process secure bits failed"));
|
|
|
|
|
|
|
|
if (opts->securebits & ~(int)(SECBIT_NOROOT |
|
|
|
|
SECBIT_NOROOT_LOCKED |
|
|
|
|
SECBIT_NO_SETUID_FIXUP |
|
|
|
|
SECBIT_NO_SETUID_FIXUP_LOCKED |
|
|
|
|
SECBIT_KEEP_CAPS |
|
|
|
|
SECBIT_KEEP_CAPS_LOCKED))
|
|
|
|
errx(SETPRIV_EXIT_PRIVERR,
|
|
|
|
_("unrecognized securebit set -- refusing to adjust"));
|
|
|
|
|
|
|
|
while ((c = strsep(&buf, ","))) {
|
|
|
|
if (*c != '+' && *c != '-')
|
|
|
|
errx(EXIT_FAILURE, _("bad securebits string"));
|
|
|
|
|
|
|
|
if (!strcmp(c + 1, "all")) {
|
|
|
|
if (*c == '-')
|
|
|
|
opts->securebits = 0;
|
|
|
|
else
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("+all securebits is not allowed"));
|
|
|
|
} else {
|
|
|
|
int bit;
|
|
|
|
if (!strcmp(c + 1, "noroot"))
|
|
|
|
bit = SECBIT_NOROOT;
|
|
|
|
else if (!strcmp(c + 1, "noroot_locked"))
|
|
|
|
bit = SECBIT_NOROOT_LOCKED;
|
|
|
|
else if (!strcmp(c + 1, "no_setuid_fixup"))
|
|
|
|
bit = SECBIT_NO_SETUID_FIXUP;
|
|
|
|
else if (!strcmp(c + 1, "no_setuid_fixup_locked"))
|
|
|
|
bit = SECBIT_NO_SETUID_FIXUP_LOCKED;
|
|
|
|
else if (!strcmp(c + 1, "keep_caps"))
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("adjusting keep_caps does not make sense"));
|
|
|
|
else if (!strcmp(c + 1, "keep_caps_locked"))
|
|
|
|
bit = SECBIT_KEEP_CAPS_LOCKED; /* sigh */
|
|
|
|
else
|
|
|
|
errx(EXIT_FAILURE, _("unrecognized securebit"));
|
|
|
|
|
|
|
|
if (*c == '+')
|
|
|
|
opts->securebits |= bit;
|
|
|
|
else
|
|
|
|
opts->securebits &= ~bit;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
opts->securebits |= SECBIT_KEEP_CAPS; /* We need it, and it's reset on exec */
|
|
|
|
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void do_selinux_label(const char *label)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
size_t len;
|
|
|
|
|
2013-02-05 04:48:55 -06:00
|
|
|
if (access(_PATH_SYS_SELINUX, F_OK) != 0)
|
2013-01-14 09:58:57 -06:00
|
|
|
errx(SETPRIV_EXIT_PRIVERR, _("SELinux is not running"));
|
|
|
|
|
2013-02-05 04:48:55 -06:00
|
|
|
fd = open(_PATH_PROC_ATTR_EXEC, O_RDWR);
|
2013-01-14 09:58:57 -06:00
|
|
|
if (fd == -1)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR,
|
2013-02-05 04:48:55 -06:00
|
|
|
_("cannot open %s"), _PATH_PROC_ATTR_EXEC);
|
2013-01-14 09:58:57 -06:00
|
|
|
|
|
|
|
len = strlen(label);
|
|
|
|
errno = 0;
|
|
|
|
if (write(fd, label, len) != (ssize_t) len)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR,
|
2013-02-05 04:48:55 -06:00
|
|
|
_("write failed: %s"), _PATH_PROC_ATTR_EXEC);
|
2013-01-14 09:58:57 -06:00
|
|
|
|
2014-05-01 00:10:13 -05:00
|
|
|
if (close(fd) != 0)
|
2013-04-13 14:54:59 -05:00
|
|
|
err(SETPRIV_EXIT_PRIVERR,
|
2014-05-01 00:10:13 -05:00
|
|
|
_("close failed: %s"), _PATH_PROC_ATTR_EXEC);
|
2013-01-14 09:58:57 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
static void do_apparmor_profile(const char *label)
|
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
|
2013-02-05 04:48:55 -06:00
|
|
|
if (access(_PATH_SYS_APPARMOR, F_OK) != 0)
|
2013-01-14 09:58:57 -06:00
|
|
|
errx(SETPRIV_EXIT_PRIVERR, _("AppArmor is not running"));
|
|
|
|
|
2014-01-24 14:02:59 -06:00
|
|
|
f = fopen(_PATH_PROC_ATTR_EXEC, "r+");
|
2013-01-14 09:58:57 -06:00
|
|
|
if (!f)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR,
|
2013-02-05 04:48:55 -06:00
|
|
|
_("cannot open %s"), _PATH_PROC_ATTR_EXEC);
|
2013-01-14 09:58:57 -06:00
|
|
|
|
2014-01-24 14:02:59 -06:00
|
|
|
fprintf(f, "exec %s", label);
|
2013-04-13 14:54:39 -05:00
|
|
|
|
|
|
|
if (close_stream(f) != 0)
|
2013-01-14 09:58:57 -06:00
|
|
|
err(SETPRIV_EXIT_PRIVERR,
|
2013-02-05 04:48:55 -06:00
|
|
|
_("write failed: %s"), _PATH_PROC_ATTR_EXEC);
|
2013-01-14 09:58:57 -06:00
|
|
|
}
|
|
|
|
|
2018-08-22 04:43:32 -05:00
|
|
|
|
|
|
|
static void do_reset_environ(struct passwd *pw)
|
|
|
|
{
|
|
|
|
char *term = getenv("TERM");
|
|
|
|
|
|
|
|
if (term)
|
|
|
|
term = xstrdup(term);
|
|
|
|
#ifdef HAVE_CLEARENV
|
|
|
|
clearenv();
|
|
|
|
#else
|
|
|
|
environ = NULL;
|
|
|
|
#endif
|
2019-05-28 06:11:14 -05:00
|
|
|
if (term) {
|
2018-08-22 04:43:32 -05:00
|
|
|
xsetenv("TERM", term, 1);
|
2019-05-28 06:11:14 -05:00
|
|
|
free(term);
|
|
|
|
}
|
2018-08-22 04:43:32 -05:00
|
|
|
|
|
|
|
if (pw->pw_shell && *pw->pw_shell)
|
|
|
|
xsetenv("SHELL", pw->pw_shell, 1);
|
|
|
|
else
|
|
|
|
xsetenv("SHELL", DEFAULT_SHELL, 1);
|
|
|
|
|
|
|
|
xsetenv("HOME", pw->pw_dir, 1);
|
|
|
|
xsetenv("USER", pw->pw_name, 1);
|
|
|
|
xsetenv("LOGNAME", pw->pw_name, 1);
|
|
|
|
|
|
|
|
if (pw->pw_uid)
|
|
|
|
xsetenv("PATH", _PATH_DEFPATH, 1);
|
|
|
|
else
|
|
|
|
xsetenv("PATH", _PATH_DEFPATH_ROOT, 1);
|
|
|
|
}
|
|
|
|
|
2013-04-13 14:54:34 -05:00
|
|
|
static uid_t get_user(const char *s, const char *err)
|
|
|
|
{
|
|
|
|
struct passwd *pw;
|
|
|
|
long tmp;
|
|
|
|
pw = getpwnam(s);
|
|
|
|
if (pw)
|
|
|
|
return pw->pw_uid;
|
|
|
|
tmp = strtol_or_err(s, err);
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gid_t get_group(const char *s, const char *err)
|
|
|
|
{
|
|
|
|
struct group *gr;
|
|
|
|
long tmp;
|
|
|
|
gr = getgrnam(s);
|
|
|
|
if (gr)
|
|
|
|
return gr->gr_gid;
|
|
|
|
tmp = strtol_or_err(s, err);
|
|
|
|
return tmp;
|
|
|
|
}
|
|
|
|
|
2017-06-01 09:52:10 -05:00
|
|
|
static struct passwd *get_passwd(const char *s, uid_t *uid, const char *err)
|
|
|
|
{
|
|
|
|
struct passwd *pw;
|
|
|
|
long tmp;
|
|
|
|
pw = getpwnam(s);
|
|
|
|
if (pw) {
|
|
|
|
*uid = pw->pw_uid;
|
|
|
|
} else {
|
|
|
|
tmp = strtol_or_err(s, err);
|
|
|
|
*uid = tmp;
|
|
|
|
pw = getpwuid(*uid);
|
|
|
|
}
|
|
|
|
return pw;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct passwd *passwd_copy(struct passwd *dst, const struct passwd *src)
|
|
|
|
{
|
|
|
|
struct passwd *rv;
|
|
|
|
rv = memcpy(dst, src, sizeof(*dst));
|
|
|
|
rv->pw_name = xstrdup(rv->pw_name);
|
|
|
|
rv->pw_passwd = xstrdup(rv->pw_passwd);
|
|
|
|
rv->pw_gecos = xstrdup(rv->pw_gecos);
|
|
|
|
rv->pw_dir = xstrdup(rv->pw_dir);
|
|
|
|
rv->pw_shell = xstrdup(rv->pw_shell);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2013-01-14 09:58:57 -06:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
|
|
|
enum {
|
|
|
|
NNP = CHAR_MAX + 1,
|
|
|
|
RUID,
|
|
|
|
EUID,
|
|
|
|
RGID,
|
|
|
|
EGID,
|
|
|
|
REUID,
|
|
|
|
REGID,
|
|
|
|
CLEAR_GROUPS,
|
|
|
|
KEEP_GROUPS,
|
2017-06-01 09:52:10 -05:00
|
|
|
INIT_GROUPS,
|
2013-01-14 09:58:57 -06:00
|
|
|
GROUPS,
|
|
|
|
INHCAPS,
|
2017-06-24 09:04:34 -05:00
|
|
|
AMBCAPS,
|
2013-01-14 09:58:57 -06:00
|
|
|
LISTCAPS,
|
|
|
|
CAPBSET,
|
|
|
|
SECUREBITS,
|
2018-04-10 06:08:21 -05:00
|
|
|
PDEATHSIG,
|
2013-01-14 09:58:57 -06:00
|
|
|
SELINUX_LABEL,
|
2018-08-22 04:43:32 -05:00
|
|
|
APPARMOR_PROFILE,
|
|
|
|
RESET_ENV
|
2013-01-14 09:58:57 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
static const struct option longopts[] = {
|
2017-02-11 14:23:26 -06:00
|
|
|
{ "dump", no_argument, NULL, 'd' },
|
|
|
|
{ "nnp", no_argument, NULL, NNP },
|
|
|
|
{ "no-new-privs", no_argument, NULL, NNP },
|
|
|
|
{ "inh-caps", required_argument, NULL, INHCAPS },
|
2017-06-24 09:04:34 -05:00
|
|
|
{ "ambient-caps", required_argument, NULL, AMBCAPS },
|
2017-02-11 14:23:26 -06:00
|
|
|
{ "list-caps", no_argument, NULL, LISTCAPS },
|
|
|
|
{ "ruid", required_argument, NULL, RUID },
|
|
|
|
{ "euid", required_argument, NULL, EUID },
|
|
|
|
{ "rgid", required_argument, NULL, RGID },
|
|
|
|
{ "egid", required_argument, NULL, EGID },
|
|
|
|
{ "reuid", required_argument, NULL, REUID },
|
|
|
|
{ "regid", required_argument, NULL, REGID },
|
|
|
|
{ "clear-groups", no_argument, NULL, CLEAR_GROUPS },
|
|
|
|
{ "keep-groups", no_argument, NULL, KEEP_GROUPS },
|
2017-06-01 09:52:10 -05:00
|
|
|
{ "init-groups", no_argument, NULL, INIT_GROUPS },
|
2017-02-11 14:23:26 -06:00
|
|
|
{ "groups", required_argument, NULL, GROUPS },
|
|
|
|
{ "bounding-set", required_argument, NULL, CAPBSET },
|
|
|
|
{ "securebits", required_argument, NULL, SECUREBITS },
|
2018-04-10 06:08:21 -05:00
|
|
|
{ "pdeathsig", required_argument, NULL, PDEATHSIG, },
|
2017-02-11 14:23:26 -06:00
|
|
|
{ "selinux-label", required_argument, NULL, SELINUX_LABEL },
|
|
|
|
{ "apparmor-profile", required_argument, NULL, APPARMOR_PROFILE },
|
|
|
|
{ "help", no_argument, NULL, 'h' },
|
2018-08-22 04:43:32 -05:00
|
|
|
{ "reset-env", no_argument, NULL, RESET_ENV, },
|
2017-02-11 14:23:26 -06:00
|
|
|
{ "version", no_argument, NULL, 'V' },
|
|
|
|
{ NULL, 0, NULL, 0 }
|
2013-01-14 09:58:57 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
static const ul_excl_t excl[] = {
|
|
|
|
/* keep in same order with enum definitions */
|
2017-06-01 09:52:10 -05:00
|
|
|
{CLEAR_GROUPS, KEEP_GROUPS, INIT_GROUPS, GROUPS},
|
2013-01-14 09:58:57 -06:00
|
|
|
{0}
|
|
|
|
};
|
|
|
|
int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
|
|
|
|
|
|
|
|
int c;
|
|
|
|
struct privctx opts;
|
2017-06-01 09:52:10 -05:00
|
|
|
struct passwd *pw = NULL;
|
2013-01-14 09:58:57 -06:00
|
|
|
int dumplevel = 0;
|
|
|
|
int total_opts = 0;
|
|
|
|
int list_caps = 0;
|
|
|
|
|
2013-10-18 03:56:56 -05:00
|
|
|
setlocale(LC_ALL, "");
|
2013-01-14 09:58:57 -06:00
|
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
|
|
textdomain(PACKAGE);
|
2019-04-16 08:14:13 -05:00
|
|
|
close_stdout_atexit();
|
2013-01-14 09:58:57 -06:00
|
|
|
|
|
|
|
memset(&opts, 0, sizeof(opts));
|
|
|
|
|
|
|
|
while ((c = getopt_long(argc, argv, "+dhV", longopts, NULL)) != -1) {
|
|
|
|
err_exclusive_options(c, longopts, excl, excl_st);
|
|
|
|
total_opts++;
|
|
|
|
switch (c) {
|
|
|
|
case 'd':
|
|
|
|
dumplevel++;
|
|
|
|
break;
|
|
|
|
case NNP:
|
|
|
|
if (opts.nnp)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("duplicate --no-new-privs option"));
|
|
|
|
opts.nnp = 1;
|
|
|
|
break;
|
|
|
|
case RUID:
|
|
|
|
if (opts.have_ruid)
|
|
|
|
errx(EXIT_FAILURE, _("duplicate ruid"));
|
|
|
|
opts.have_ruid = 1;
|
2017-06-01 09:52:10 -05:00
|
|
|
pw = get_passwd(optarg, &opts.ruid, _("failed to parse ruid"));
|
|
|
|
if (pw) {
|
|
|
|
passwd_copy(&opts.passwd, pw);
|
|
|
|
opts.have_passwd = 1;
|
|
|
|
}
|
2013-01-14 09:58:57 -06:00
|
|
|
break;
|
|
|
|
case EUID:
|
|
|
|
if (opts.have_euid)
|
|
|
|
errx(EXIT_FAILURE, _("duplicate euid"));
|
|
|
|
opts.have_euid = 1;
|
2013-04-13 14:54:34 -05:00
|
|
|
opts.euid = get_user(optarg, _("failed to parse euid"));
|
2013-01-14 09:58:57 -06:00
|
|
|
break;
|
|
|
|
case REUID:
|
|
|
|
if (opts.have_ruid || opts.have_euid)
|
|
|
|
errx(EXIT_FAILURE, _("duplicate ruid or euid"));
|
|
|
|
opts.have_ruid = opts.have_euid = 1;
|
2017-06-01 09:52:10 -05:00
|
|
|
pw = get_passwd(optarg, &opts.ruid, _("failed to parse reuid"));
|
|
|
|
opts.euid = opts.ruid;
|
|
|
|
if (pw) {
|
|
|
|
passwd_copy(&opts.passwd, pw);
|
|
|
|
opts.have_passwd = 1;
|
|
|
|
}
|
2013-01-14 09:58:57 -06:00
|
|
|
break;
|
|
|
|
case RGID:
|
|
|
|
if (opts.have_rgid)
|
|
|
|
errx(EXIT_FAILURE, _("duplicate rgid"));
|
|
|
|
opts.have_rgid = 1;
|
2013-04-13 14:54:34 -05:00
|
|
|
opts.rgid = get_group(optarg, _("failed to parse rgid"));
|
2013-01-14 09:58:57 -06:00
|
|
|
break;
|
|
|
|
case EGID:
|
|
|
|
if (opts.have_egid)
|
|
|
|
errx(EXIT_FAILURE, _("duplicate egid"));
|
|
|
|
opts.have_egid = 1;
|
2013-04-13 14:54:34 -05:00
|
|
|
opts.egid = get_group(optarg, _("failed to parse egid"));
|
2013-01-14 09:58:57 -06:00
|
|
|
break;
|
|
|
|
case REGID:
|
|
|
|
if (opts.have_rgid || opts.have_egid)
|
|
|
|
errx(EXIT_FAILURE, _("duplicate rgid or egid"));
|
|
|
|
opts.have_rgid = opts.have_egid = 1;
|
2013-04-13 14:54:34 -05:00
|
|
|
opts.rgid = opts.egid = get_group(optarg, _("failed to parse regid"));
|
2013-01-14 09:58:57 -06:00
|
|
|
break;
|
|
|
|
case CLEAR_GROUPS:
|
|
|
|
if (opts.clear_groups)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("duplicate --clear-groups option"));
|
|
|
|
opts.clear_groups = 1;
|
|
|
|
break;
|
|
|
|
case KEEP_GROUPS:
|
|
|
|
if (opts.keep_groups)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("duplicate --keep-groups option"));
|
|
|
|
opts.keep_groups = 1;
|
|
|
|
break;
|
2017-06-01 09:52:10 -05:00
|
|
|
case INIT_GROUPS:
|
|
|
|
if (opts.init_groups)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("duplicate --init-groups option"));
|
|
|
|
opts.init_groups = 1;
|
|
|
|
break;
|
2013-01-14 09:58:57 -06:00
|
|
|
case GROUPS:
|
|
|
|
if (opts.have_groups)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("duplicate --groups option"));
|
|
|
|
parse_groups(&opts, optarg);
|
|
|
|
break;
|
2018-04-10 06:08:21 -05:00
|
|
|
case PDEATHSIG:
|
|
|
|
if (opts.pdeathsig)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("duplicate --keep-pdeathsig option"));
|
|
|
|
parse_pdeathsig(&opts, optarg);
|
|
|
|
break;
|
2013-01-14 09:58:57 -06:00
|
|
|
case LISTCAPS:
|
|
|
|
list_caps = 1;
|
|
|
|
break;
|
|
|
|
case INHCAPS:
|
|
|
|
if (opts.caps_to_inherit)
|
|
|
|
errx(EXIT_FAILURE,
|
2013-02-05 19:07:26 -06:00
|
|
|
_("duplicate --inh-caps option"));
|
2013-01-14 09:58:57 -06:00
|
|
|
opts.caps_to_inherit = optarg;
|
|
|
|
break;
|
2017-06-24 09:04:34 -05:00
|
|
|
case AMBCAPS:
|
|
|
|
if (opts.ambient_caps)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("duplicate --ambient-caps option"));
|
|
|
|
opts.ambient_caps = optarg;
|
|
|
|
break;
|
2013-01-14 09:58:57 -06:00
|
|
|
case CAPBSET:
|
|
|
|
if (opts.bounding_set)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("duplicate --bounding-set option"));
|
|
|
|
opts.bounding_set = optarg;
|
|
|
|
break;
|
|
|
|
case SECUREBITS:
|
|
|
|
if (opts.have_securebits)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("duplicate --securebits option"));
|
|
|
|
parse_securebits(&opts, optarg);
|
|
|
|
break;
|
|
|
|
case SELINUX_LABEL:
|
|
|
|
if (opts.selinux_label)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("duplicate --selinux-label option"));
|
|
|
|
opts.selinux_label = optarg;
|
|
|
|
break;
|
|
|
|
case APPARMOR_PROFILE:
|
|
|
|
if (opts.apparmor_profile)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("duplicate --apparmor-profile option"));
|
|
|
|
opts.apparmor_profile = optarg;
|
|
|
|
break;
|
2018-08-22 04:43:32 -05:00
|
|
|
case RESET_ENV:
|
|
|
|
opts.reset_env = 1;
|
|
|
|
break;
|
2019-04-16 08:14:13 -05:00
|
|
|
|
2013-01-14 09:58:57 -06:00
|
|
|
case 'h':
|
2017-06-20 13:20:29 -05:00
|
|
|
usage();
|
2013-01-14 09:58:57 -06:00
|
|
|
case 'V':
|
2019-04-16 08:14:13 -05:00
|
|
|
print_version(EXIT_SUCCESS);
|
2013-01-14 09:58:57 -06:00
|
|
|
default:
|
2016-12-19 06:13:34 -06:00
|
|
|
errtryhelp(EXIT_FAILURE);
|
2013-01-14 09:58:57 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dumplevel) {
|
|
|
|
if (total_opts != dumplevel || optind < argc)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("--dump is incompatible with all other options"));
|
|
|
|
dump(dumplevel);
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (list_caps) {
|
|
|
|
if (total_opts != 1 || optind < argc)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("--list-caps must be specified alone"));
|
|
|
|
list_known_caps();
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (argc <= optind)
|
|
|
|
errx(EXIT_FAILURE, _("No program specified"));
|
|
|
|
|
|
|
|
if ((opts.have_rgid || opts.have_egid)
|
2017-06-01 09:52:10 -05:00
|
|
|
&& !opts.keep_groups && !opts.clear_groups && !opts.init_groups
|
|
|
|
&& !opts.have_groups)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("--[re]gid requires --keep-groups, --clear-groups, --init-groups, or --groups"));
|
|
|
|
|
|
|
|
if (opts.init_groups && !opts.have_ruid)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("--init-groups requires --ruid or --reuid"));
|
|
|
|
|
|
|
|
if (opts.init_groups && !opts.have_passwd)
|
2013-01-14 09:58:57 -06:00
|
|
|
errx(EXIT_FAILURE,
|
2017-06-01 09:52:10 -05:00
|
|
|
_("uid %ld not found, --init-groups requires an user that "
|
|
|
|
"can be found on the system"),
|
|
|
|
(long) opts.ruid);
|
2013-01-14 09:58:57 -06:00
|
|
|
|
2018-08-22 04:43:32 -05:00
|
|
|
if (opts.reset_env) {
|
|
|
|
if (opts.have_passwd)
|
|
|
|
/* pwd according to --ruid or --reuid */
|
|
|
|
pw = &opts.passwd;
|
|
|
|
else
|
|
|
|
/* pwd for the current user */
|
|
|
|
pw = getpwuid(getuid());
|
|
|
|
do_reset_environ(pw);
|
|
|
|
}
|
|
|
|
|
2016-07-04 16:09:10 -05:00
|
|
|
if (opts.nnp && prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1)
|
|
|
|
err(EXIT_FAILURE, _("disallow granting new privileges failed"));
|
2013-01-14 09:58:57 -06:00
|
|
|
|
|
|
|
if (opts.selinux_label)
|
|
|
|
do_selinux_label(opts.selinux_label);
|
|
|
|
if (opts.apparmor_profile)
|
|
|
|
do_apparmor_profile(opts.apparmor_profile);
|
|
|
|
|
|
|
|
if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1)
|
|
|
|
err(EXIT_FAILURE, _("keep process capabilities failed"));
|
|
|
|
|
|
|
|
/* We're going to want CAP_SETPCAP, CAP_SETUID, and CAP_SETGID if
|
|
|
|
* possible. */
|
|
|
|
bump_cap(CAP_SETPCAP);
|
|
|
|
bump_cap(CAP_SETUID);
|
|
|
|
bump_cap(CAP_SETGID);
|
|
|
|
if (capng_apply(CAPNG_SELECT_CAPS) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("activate capabilities"));
|
|
|
|
|
|
|
|
if (opts.have_ruid || opts.have_euid) {
|
|
|
|
do_setresuid(&opts);
|
|
|
|
/* KEEPCAPS doesn't work for the effective mask. */
|
|
|
|
if (capng_apply(CAPNG_SELECT_CAPS) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("reactivate capabilities"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts.have_rgid || opts.have_egid)
|
|
|
|
do_setresgid(&opts);
|
|
|
|
|
|
|
|
if (opts.have_groups) {
|
|
|
|
if (setgroups(opts.num_groups, opts.groups) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("setgroups failed"));
|
2017-06-01 09:52:10 -05:00
|
|
|
} else if (opts.init_groups) {
|
|
|
|
if (initgroups(opts.passwd.pw_name, opts.passwd.pw_gid) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("initgroups failed"));
|
2013-01-14 09:58:57 -06:00
|
|
|
} else if (opts.clear_groups) {
|
|
|
|
gid_t x = 0;
|
|
|
|
if (setgroups(0, &x) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("setgroups failed"));
|
|
|
|
}
|
|
|
|
|
2016-07-04 16:09:10 -05:00
|
|
|
if (opts.have_securebits && prctl(PR_SET_SECUREBITS, opts.securebits, 0, 0, 0) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("set process securebits failed"));
|
2013-01-14 09:58:57 -06:00
|
|
|
|
|
|
|
if (opts.bounding_set) {
|
setpriv: introduce indirection for `capng_type` enum
The capng_type is used to distinguish the different types of capability
sets, that is the effective, inheratibale, permitted capabilities as
well as the capability bounding set. In Linux 4.3, a new set of
capabilities was introduced with ambient capabilities. Unfortunately,
libcap-ng does not provide any support for these kind of capabilities
and as such, we will have to roll our own support.
As a first step, we introduce an indirection for the `capng_type` enum,
allowing us to add the ambient capability type later on. Right now, no
functional change is expected from this change and in fact, each of the
newly introduce enums should have the same value as respective enum of
libcap-ng.
Reviewed-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
2017-06-24 09:04:30 -05:00
|
|
|
do_caps(CAP_TYPE_BOUNDING, opts.bounding_set);
|
2013-01-14 09:58:57 -06:00
|
|
|
errno = EPERM; /* capng doesn't set errno if we're missing CAP_SETPCAP */
|
|
|
|
if (capng_apply(CAPNG_SELECT_BOUNDS) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("apply bounding set"));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opts.caps_to_inherit) {
|
setpriv: introduce indirection for `capng_type` enum
The capng_type is used to distinguish the different types of capability
sets, that is the effective, inheratibale, permitted capabilities as
well as the capability bounding set. In Linux 4.3, a new set of
capabilities was introduced with ambient capabilities. Unfortunately,
libcap-ng does not provide any support for these kind of capabilities
and as such, we will have to roll our own support.
As a first step, we introduce an indirection for the `capng_type` enum,
allowing us to add the ambient capability type later on. Right now, no
functional change is expected from this change and in fact, each of the
newly introduce enums should have the same value as respective enum of
libcap-ng.
Reviewed-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Patrick Steinhardt <ps@pks.im>
2017-06-24 09:04:30 -05:00
|
|
|
do_caps(CAP_TYPE_INHERITABLE, opts.caps_to_inherit);
|
2013-01-14 09:58:57 -06:00
|
|
|
if (capng_apply(CAPNG_SELECT_CAPS) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("apply capabilities"));
|
|
|
|
}
|
|
|
|
|
2017-06-24 09:04:34 -05:00
|
|
|
if (opts.ambient_caps) {
|
|
|
|
do_caps(CAP_TYPE_AMBIENT, opts.ambient_caps);
|
|
|
|
}
|
|
|
|
|
2018-04-10 06:08:21 -05:00
|
|
|
/* Clear or set parent death signal */
|
|
|
|
if (opts.pdeathsig && prctl(PR_SET_PDEATHSIG, opts.pdeathsig < 0 ? 0 : opts.pdeathsig) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("set parent death signal failed"));
|
|
|
|
|
2013-01-14 09:58:57 -06:00
|
|
|
execvp(argv[optind], argv + optind);
|
2018-02-01 08:44:25 -06:00
|
|
|
errexec(argv[optind]);
|
2013-01-14 09:58:57 -06:00
|
|
|
}
|