2013-01-14 07:58:57 -08: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 20:54:34 +01:00
|
|
|
#include <pwd.h>
|
2013-01-14 07:58:57 -08:00
|
|
|
#include <stdarg.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/prctl.h>
|
2013-04-13 20:54:34 +01:00
|
|
|
#include <sys/types.h>
|
2013-01-14 07:58:57 -08:00
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "c.h"
|
|
|
|
#include "closestream.h"
|
|
|
|
#include "nls.h"
|
|
|
|
#include "optutils.h"
|
|
|
|
#include "strutils.h"
|
|
|
|
#include "xalloc.h"
|
2013-02-05 11:48:55 +01:00
|
|
|
#include "pathnames.h"
|
2013-01-14 07:58:57 -08: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 */
|
|
|
|
|
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 16:04:30 +02:00
|
|
|
enum cap_type {
|
|
|
|
CAP_TYPE_EFFECTIVE = CAPNG_EFFECTIVE,
|
|
|
|
CAP_TYPE_PERMITTED = CAPNG_PERMITTED,
|
|
|
|
CAP_TYPE_INHERITABLE = CAPNG_INHERITABLE,
|
|
|
|
CAP_TYPE_BOUNDING = CAPNG_BOUNDING_SET
|
|
|
|
};
|
|
|
|
|
2013-01-14 07:58:57 -08: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 16:52:10 +02:00
|
|
|
have_passwd:1, /* passwd entry */
|
2013-01-14 07:58:57 -08:00
|
|
|
have_groups:1, /* add groups */
|
|
|
|
keep_groups:1, /* keep groups */
|
|
|
|
clear_groups:1, /* remove groups */
|
2017-06-01 16:52:10 +02:00
|
|
|
init_groups:1, /* initialize groups */
|
2013-01-14 07:58:57 -08:00
|
|
|
have_securebits:1; /* remove groups */
|
|
|
|
|
|
|
|
/* uids and gids */
|
|
|
|
uid_t ruid, euid;
|
|
|
|
gid_t rgid, egid;
|
|
|
|
|
2017-06-01 16:52:10 +02:00
|
|
|
/* real user passwd entry */
|
|
|
|
struct passwd passwd;
|
|
|
|
|
2013-01-14 07:58:57 -08:00
|
|
|
/* supplementary groups */
|
|
|
|
size_t num_groups;
|
|
|
|
gid_t *groups;
|
|
|
|
|
|
|
|
/* caps */
|
|
|
|
const char *caps_to_inherit;
|
|
|
|
const char *bounding_set;
|
|
|
|
|
|
|
|
/* securebits */
|
|
|
|
int securebits;
|
|
|
|
|
|
|
|
/* LSMs */
|
|
|
|
const char *selinux_label;
|
|
|
|
const char *apparmor_profile;
|
|
|
|
};
|
|
|
|
|
2017-06-20 20:20:29 +02:00
|
|
|
static void __attribute__((__noreturn__)) usage(void)
|
2013-01-14 07:58:57 -08:00
|
|
|
{
|
2017-06-20 20:20:29 +02:00
|
|
|
FILE *out = stdout;
|
2013-01-14 07:58:57 -08:00
|
|
|
fputs(USAGE_HEADER, out);
|
2014-09-22 22:15:39 +02:00
|
|
|
fprintf(out, _(" %s [options] <program> [<argument>...]\n"),
|
|
|
|
program_invocation_short_name);
|
|
|
|
|
2014-12-22 22:57:17 +01:00
|
|
|
fputs(USAGE_SEPARATOR, out);
|
|
|
|
fputs(_("Run a program with different privilege settings.\n"), out);
|
|
|
|
|
2013-01-14 07:58:57 -08:00
|
|
|
fputs(USAGE_OPTIONS, out);
|
|
|
|
fputs(_(" -d, --dump show current state (and do not exec anything)\n"), out);
|
|
|
|
fputs(_(" --nnp, --no-new-privs disallow granting new privileges\n"), out);
|
|
|
|
fputs(_(" --inh-caps <caps,...> set inheritable capabilities\n"), out);
|
|
|
|
fputs(_(" --bounding-set <caps> set capability bounding set\n"), out);
|
|
|
|
fputs(_(" --ruid <uid> set real uid\n"), out);
|
|
|
|
fputs(_(" --euid <uid> set effective uid\n"), out);
|
|
|
|
fputs(_(" --rgid <gid> set real gid\n"), out);
|
|
|
|
fputs(_(" --egid <gid> set effective gid\n"), out);
|
|
|
|
fputs(_(" --reuid <uid> set real and effective uid\n"), out);
|
|
|
|
fputs(_(" --regid <gid> set real and effective gid\n"), out);
|
|
|
|
fputs(_(" --clear-groups clear supplementary groups\n"), out);
|
|
|
|
fputs(_(" --keep-groups keep supplementary groups\n"), out);
|
2017-06-01 16:52:10 +02:00
|
|
|
fputs(_(" --init-groups initialize supplementary groups\n"), out);
|
2013-01-14 07:58:57 -08:00
|
|
|
fputs(_(" --groups <group,...> set supplementary groups\n"), out);
|
|
|
|
fputs(_(" --securebits <bits> set securebits\n"), out);
|
2013-11-19 14:42:22 +01:00
|
|
|
fputs(_(" --selinux-label <label> set SELinux label\n"), out);
|
|
|
|
fputs(_(" --apparmor-profile <pr> set AppArmor profile\n"), out);
|
2014-09-22 22:15:39 +02:00
|
|
|
|
2013-01-14 07:58:57 -08:00
|
|
|
fputs(USAGE_SEPARATOR, out);
|
2017-06-25 00:19:50 +02:00
|
|
|
print_usage_help_options(16);
|
2013-01-14 07:58:57 -08:00
|
|
|
fputs(USAGE_SEPARATOR, out);
|
|
|
|
fputs(_(" This tool can be dangerous. Read the manpage, and be careful.\n"), out);
|
|
|
|
fprintf(out, USAGE_MAN_TAIL("setpriv(1)"));
|
|
|
|
|
2017-06-20 20:20:29 +02:00
|
|
|
exit(EXIT_SUCCESS);
|
2013-01-14 07:58:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int real_cap_last_cap(void)
|
|
|
|
{
|
|
|
|
/* CAP_LAST_CAP is untrustworthy. */
|
|
|
|
static int ret = -1;
|
|
|
|
int matched;
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
if (ret != -1)
|
|
|
|
return ret;
|
|
|
|
|
2013-02-05 11:48:55 +01:00
|
|
|
f = fopen(_PATH_PROC_CAPLASTCAP, "r");
|
2013-01-14 07:58:57 -08:00
|
|
|
if (!f) {
|
|
|
|
ret = CAP_LAST_CAP; /* guess */
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
matched = fscanf(f, "%d", &ret);
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
if (matched != 1)
|
|
|
|
ret = CAP_LAST_CAP; /* guess */
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-06-24 16:04:31 +02: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:
|
|
|
|
return capng_have_capability(which, i);
|
|
|
|
default:
|
|
|
|
warnx(_("invalid capability type"));
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-14 07:58:57 -08: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 16:04:30 +02:00
|
|
|
static int print_caps(FILE *f, enum cap_type which)
|
2013-01-14 07:58:57 -08:00
|
|
|
{
|
|
|
|
int i, n = 0, max = real_cap_last_cap();
|
|
|
|
|
|
|
|
for (i = 0; i <= max; i++) {
|
2017-06-24 16:04:31 +02:00
|
|
|
int ret = has_cap(which, i);
|
|
|
|
|
|
|
|
if (i == 0 && ret < 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (ret == 1) {
|
2013-01-14 07:58:57 -08: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 16:04:30 +02:00
|
|
|
|
2013-01-14 07:58:57 -08:00
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dump_one_secbit(int *first, int *bits, int bit, const char *name)
|
|
|
|
{
|
|
|
|
if (*bits & bit) {
|
2016-07-03 12:42:58 +01:00
|
|
|
if (*first)
|
2013-01-14 07:58:57 -08:00
|
|
|
*first = 0;
|
2016-07-03 12:42:58 +01:00
|
|
|
else
|
|
|
|
printf(",");
|
2013-01-14 07:58:57 -08: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 12:42:58 +01:00
|
|
|
if (first)
|
2013-01-14 07:58:57 -08:00
|
|
|
first = 0;
|
2016-07-03 12:42:58 +01:00
|
|
|
else
|
|
|
|
printf(",");
|
2013-01-14 07:58:57 -08: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 11:48:55 +01:00
|
|
|
fd = open(_PATH_PROC_ATTR_CURRENT, O_RDONLY);
|
2013-01-14 07:58:57 -08:00
|
|
|
if (fd == -1) {
|
2013-02-05 11:48:55 +01:00
|
|
|
warn(_("cannot open %s"), _PATH_PROC_ATTR_CURRENT);
|
2013-01-14 07:58:57 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
len = read(fd, buf, sizeof(buf));
|
|
|
|
e = errno;
|
|
|
|
close(fd);
|
|
|
|
if (len < 0) {
|
|
|
|
errno = e;
|
2013-10-08 17:04:22 +02:00
|
|
|
warn(_("cannot read %s"), name);
|
2013-01-14 07:58:57 -08: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 20:23:26 +00:00
|
|
|
int n = getgroups(0, NULL);
|
2013-01-14 07:58:57 -08:00
|
|
|
gid_t *groups;
|
2013-02-05 11:48:55 +01:00
|
|
|
|
2013-01-14 07:58:57 -08:00
|
|
|
if (n < 0) {
|
|
|
|
warn("getgroups failed");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2014-07-13 17:58:36 +01:00
|
|
|
groups = xmalloc(n * sizeof(gid_t));
|
2013-01-14 07:58:57 -08:00
|
|
|
n = getgroups(n, groups);
|
|
|
|
if (n < 0) {
|
2014-07-13 17:58:36 +01:00
|
|
|
free(groups);
|
2013-01-14 07:58:57 -08: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 17:58:36 +01:00
|
|
|
free(groups);
|
2013-01-14 07:58:57 -08: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 16:04:30 +02:00
|
|
|
if (print_caps(stdout, CAP_TYPE_EFFECTIVE) == 0)
|
2013-01-14 07:58:57 -08: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 16:04:30 +02:00
|
|
|
if (print_caps(stdout, CAP_TYPE_PERMITTED) == 0)
|
2013-01-14 07:58:57 -08: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 16:04:30 +02:00
|
|
|
if (print_caps(stdout, CAP_TYPE_INHERITABLE) == 0)
|
2013-01-14 07:58:57 -08:00
|
|
|
printf(_("[none]"));
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
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 16:04:30 +02:00
|
|
|
if (print_caps(stdout, CAP_TYPE_BOUNDING) == 0)
|
2013-01-14 07:58:57 -08:00
|
|
|
printf(_("[none]"));
|
|
|
|
printf("\n");
|
|
|
|
|
|
|
|
dump_securebits();
|
|
|
|
|
2013-02-05 11:48:55 +01:00
|
|
|
if (access(_PATH_SYS_SELINUX, F_OK) == 0)
|
2013-01-14 07:58:57 -08:00
|
|
|
dump_label(_("SELinux label"));
|
|
|
|
|
2013-02-05 11:48:55 +01:00
|
|
|
if (access(_PATH_SYS_APPARMOR, F_OK) == 0) {
|
2013-01-14 07:58:57 -08:00
|
|
|
dump_label(_("AppArmor profile"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void list_known_caps(void)
|
|
|
|
{
|
|
|
|
int i, max = real_cap_last_cap();
|
|
|
|
|
|
|
|
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, ",")))
|
|
|
|
opts->groups[i++] = (gid_t) strtol_or_err(c,
|
2013-02-05 11:48:55 +01:00
|
|
|
_("Invalid supplementary group id"));
|
2013-01-14 07:58:57 -08:00
|
|
|
|
|
|
|
free(groups);
|
|
|
|
}
|
|
|
|
|
|
|
|
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 16:04:32 +02: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);
|
|
|
|
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 16:04:30 +02:00
|
|
|
static void do_caps(enum cap_type type, const char *caps)
|
2013-01-14 07:58:57 -08: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. */
|
|
|
|
if (real_cap_last_cap() > CAP_LAST_CAP)
|
|
|
|
errx(SETPRIV_EXIT_PRIVERR,
|
|
|
|
_("libcap-ng is too old for \"all\" caps"));
|
|
|
|
for (i = 0; i <= CAP_LAST_CAP; i++)
|
2017-06-24 16:04:32 +02:00
|
|
|
cap_update(action, type, i);
|
2013-01-14 07:58:57 -08:00
|
|
|
} else {
|
|
|
|
int cap = capng_name_to_capability(c + 1);
|
|
|
|
if (0 <= cap)
|
2017-06-24 16:04:32 +02:00
|
|
|
cap_update(action, type, cap);
|
2013-01-14 07:58:57 -08: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 11:48:55 +01:00
|
|
|
if (access(_PATH_SYS_SELINUX, F_OK) != 0)
|
2013-01-14 07:58:57 -08:00
|
|
|
errx(SETPRIV_EXIT_PRIVERR, _("SELinux is not running"));
|
|
|
|
|
2013-02-05 11:48:55 +01:00
|
|
|
fd = open(_PATH_PROC_ATTR_EXEC, O_RDWR);
|
2013-01-14 07:58:57 -08:00
|
|
|
if (fd == -1)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR,
|
2013-02-05 11:48:55 +01:00
|
|
|
_("cannot open %s"), _PATH_PROC_ATTR_EXEC);
|
2013-01-14 07:58:57 -08:00
|
|
|
|
|
|
|
len = strlen(label);
|
|
|
|
errno = 0;
|
|
|
|
if (write(fd, label, len) != (ssize_t) len)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR,
|
2013-02-05 11:48:55 +01:00
|
|
|
_("write failed: %s"), _PATH_PROC_ATTR_EXEC);
|
2013-01-14 07:58:57 -08:00
|
|
|
|
2014-04-30 22:10:13 -07:00
|
|
|
if (close(fd) != 0)
|
2013-04-13 20:54:59 +01:00
|
|
|
err(SETPRIV_EXIT_PRIVERR,
|
2014-04-30 22:10:13 -07:00
|
|
|
_("close failed: %s"), _PATH_PROC_ATTR_EXEC);
|
2013-01-14 07:58:57 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void do_apparmor_profile(const char *label)
|
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
|
2013-02-05 11:48:55 +01:00
|
|
|
if (access(_PATH_SYS_APPARMOR, F_OK) != 0)
|
2013-01-14 07:58:57 -08:00
|
|
|
errx(SETPRIV_EXIT_PRIVERR, _("AppArmor is not running"));
|
|
|
|
|
2014-01-24 12:02:59 -08:00
|
|
|
f = fopen(_PATH_PROC_ATTR_EXEC, "r+");
|
2013-01-14 07:58:57 -08:00
|
|
|
if (!f)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR,
|
2013-02-05 11:48:55 +01:00
|
|
|
_("cannot open %s"), _PATH_PROC_ATTR_EXEC);
|
2013-01-14 07:58:57 -08:00
|
|
|
|
2014-01-24 12:02:59 -08:00
|
|
|
fprintf(f, "exec %s", label);
|
2013-04-13 20:54:39 +01:00
|
|
|
|
|
|
|
if (close_stream(f) != 0)
|
2013-01-14 07:58:57 -08:00
|
|
|
err(SETPRIV_EXIT_PRIVERR,
|
2013-02-05 11:48:55 +01:00
|
|
|
_("write failed: %s"), _PATH_PROC_ATTR_EXEC);
|
2013-01-14 07:58:57 -08:00
|
|
|
}
|
|
|
|
|
2013-04-13 20:54:34 +01: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 16:52:10 +02: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 07:58:57 -08: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 16:52:10 +02:00
|
|
|
INIT_GROUPS,
|
2013-01-14 07:58:57 -08:00
|
|
|
GROUPS,
|
|
|
|
INHCAPS,
|
|
|
|
LISTCAPS,
|
|
|
|
CAPBSET,
|
|
|
|
SECUREBITS,
|
|
|
|
SELINUX_LABEL,
|
|
|
|
APPARMOR_PROFILE
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct option longopts[] = {
|
2017-02-11 20:23:26 +00:00
|
|
|
{ "dump", no_argument, NULL, 'd' },
|
|
|
|
{ "nnp", no_argument, NULL, NNP },
|
|
|
|
{ "no-new-privs", no_argument, NULL, NNP },
|
|
|
|
{ "inh-caps", required_argument, NULL, INHCAPS },
|
|
|
|
{ "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 16:52:10 +02:00
|
|
|
{ "init-groups", no_argument, NULL, INIT_GROUPS },
|
2017-02-11 20:23:26 +00:00
|
|
|
{ "groups", required_argument, NULL, GROUPS },
|
|
|
|
{ "bounding-set", required_argument, NULL, CAPBSET },
|
|
|
|
{ "securebits", required_argument, NULL, SECUREBITS },
|
|
|
|
{ "selinux-label", required_argument, NULL, SELINUX_LABEL },
|
|
|
|
{ "apparmor-profile", required_argument, NULL, APPARMOR_PROFILE },
|
|
|
|
{ "help", no_argument, NULL, 'h' },
|
|
|
|
{ "version", no_argument, NULL, 'V' },
|
|
|
|
{ NULL, 0, NULL, 0 }
|
2013-01-14 07:58:57 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
static const ul_excl_t excl[] = {
|
|
|
|
/* keep in same order with enum definitions */
|
2017-06-01 16:52:10 +02:00
|
|
|
{CLEAR_GROUPS, KEEP_GROUPS, INIT_GROUPS, GROUPS},
|
2013-01-14 07:58:57 -08:00
|
|
|
{0}
|
|
|
|
};
|
|
|
|
int excl_st[ARRAY_SIZE(excl)] = UL_EXCL_STATUS_INIT;
|
|
|
|
|
|
|
|
int c;
|
|
|
|
struct privctx opts;
|
2017-06-01 16:52:10 +02:00
|
|
|
struct passwd *pw = NULL;
|
2013-01-14 07:58:57 -08:00
|
|
|
int dumplevel = 0;
|
|
|
|
int total_opts = 0;
|
|
|
|
int list_caps = 0;
|
|
|
|
|
2013-10-18 10:56:56 +02:00
|
|
|
setlocale(LC_ALL, "");
|
2013-01-14 07:58:57 -08:00
|
|
|
bindtextdomain(PACKAGE, LOCALEDIR);
|
|
|
|
textdomain(PACKAGE);
|
|
|
|
atexit(close_stdout);
|
|
|
|
|
|
|
|
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 16:52:10 +02: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 07:58:57 -08:00
|
|
|
break;
|
|
|
|
case EUID:
|
|
|
|
if (opts.have_euid)
|
|
|
|
errx(EXIT_FAILURE, _("duplicate euid"));
|
|
|
|
opts.have_euid = 1;
|
2013-04-13 20:54:34 +01:00
|
|
|
opts.euid = get_user(optarg, _("failed to parse euid"));
|
2013-01-14 07:58:57 -08: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 16:52:10 +02: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 07:58:57 -08:00
|
|
|
break;
|
|
|
|
case RGID:
|
|
|
|
if (opts.have_rgid)
|
|
|
|
errx(EXIT_FAILURE, _("duplicate rgid"));
|
|
|
|
opts.have_rgid = 1;
|
2013-04-13 20:54:34 +01:00
|
|
|
opts.rgid = get_group(optarg, _("failed to parse rgid"));
|
2013-01-14 07:58:57 -08:00
|
|
|
break;
|
|
|
|
case EGID:
|
|
|
|
if (opts.have_egid)
|
|
|
|
errx(EXIT_FAILURE, _("duplicate egid"));
|
|
|
|
opts.have_egid = 1;
|
2013-04-13 20:54:34 +01:00
|
|
|
opts.egid = get_group(optarg, _("failed to parse egid"));
|
2013-01-14 07:58:57 -08: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 20:54:34 +01:00
|
|
|
opts.rgid = opts.egid = get_group(optarg, _("failed to parse regid"));
|
2013-01-14 07:58:57 -08: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 16:52:10 +02:00
|
|
|
case INIT_GROUPS:
|
|
|
|
if (opts.init_groups)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("duplicate --init-groups option"));
|
|
|
|
opts.init_groups = 1;
|
|
|
|
break;
|
2013-01-14 07:58:57 -08:00
|
|
|
case GROUPS:
|
|
|
|
if (opts.have_groups)
|
|
|
|
errx(EXIT_FAILURE,
|
|
|
|
_("duplicate --groups option"));
|
|
|
|
parse_groups(&opts, optarg);
|
|
|
|
break;
|
|
|
|
case LISTCAPS:
|
|
|
|
list_caps = 1;
|
|
|
|
break;
|
|
|
|
case INHCAPS:
|
|
|
|
if (opts.caps_to_inherit)
|
|
|
|
errx(EXIT_FAILURE,
|
2013-02-05 17:07:26 -08:00
|
|
|
_("duplicate --inh-caps option"));
|
2013-01-14 07:58:57 -08:00
|
|
|
opts.caps_to_inherit = optarg;
|
|
|
|
break;
|
|
|
|
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;
|
|
|
|
case 'h':
|
2017-06-20 20:20:29 +02:00
|
|
|
usage();
|
2013-01-14 07:58:57 -08:00
|
|
|
case 'V':
|
|
|
|
printf(UTIL_LINUX_VERSION);
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
default:
|
2016-12-19 13:13:34 +01:00
|
|
|
errtryhelp(EXIT_FAILURE);
|
2013-01-14 07:58:57 -08: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 16:52:10 +02: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 07:58:57 -08:00
|
|
|
errx(EXIT_FAILURE,
|
2017-06-01 16:52:10 +02:00
|
|
|
_("uid %ld not found, --init-groups requires an user that "
|
|
|
|
"can be found on the system"),
|
|
|
|
(long) opts.ruid);
|
2013-01-14 07:58:57 -08:00
|
|
|
|
2016-07-04 22:09:10 +01: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 07:58:57 -08: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 16:52:10 +02: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 07:58:57 -08:00
|
|
|
} else if (opts.clear_groups) {
|
|
|
|
gid_t x = 0;
|
|
|
|
if (setgroups(0, &x) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("setgroups failed"));
|
|
|
|
}
|
|
|
|
|
2016-07-04 22:09:10 +01: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 07:58:57 -08: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 16:04:30 +02:00
|
|
|
do_caps(CAP_TYPE_BOUNDING, opts.bounding_set);
|
2013-01-14 07:58:57 -08: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 16:04:30 +02:00
|
|
|
do_caps(CAP_TYPE_INHERITABLE, opts.caps_to_inherit);
|
2013-01-14 07:58:57 -08:00
|
|
|
if (capng_apply(CAPNG_SELECT_CAPS) != 0)
|
|
|
|
err(SETPRIV_EXIT_PRIVERR, _("apply capabilities"));
|
|
|
|
}
|
|
|
|
|
|
|
|
execvp(argv[optind], argv + optind);
|
|
|
|
|
|
|
|
err(EXIT_FAILURE, _("cannot execute: %s"), argv[optind]);
|
|
|
|
}
|