Re: [PATCH v2] kinit: Add drop_capabilities support.

From: Andrew G. Morgan
Date: Tue Aug 02 2011 - 22:44:36 EST


On Tue, Aug 2, 2011 at 6:16 PM, Mike Waychison <mikew@xxxxxxxxxx> wrote:
> This patch adds the ability to kinit to allow the dropping of POSIX
> capabilities.
>
> kinit is modified by this change, such that it understands the new
> kernel command line "drop_capabilities=" that specifies a comma
> separated list of capability names that should be dropped before
> switching over to the next init in the boot strap (typically on the root
> disk).
>
> When processing capabilities to drop, CAP_SETPCAP is special cased to be
> dropped last, so that the order that capabilities are given does not
> cause dropping of later enumerated capabilities to fail if it is listed
> early on.

Given your change to only touch the inheritable set, you do not need
to treat CAP_SETPCAP any differently from the other capabilities,
since dropping it from the inheritable and bounding set will not
affect the running process in any problematic way until it runs
exec().

>
> Dropping of capabilities happens in three parts. ÂWe explicitly drop the
> capability from init's inherited masks. ÂWe also drop the capability
> from the bounding set using PR_CAPBSET_DROP so that later setuid execs
> are bounded. ÂLastly, we drop the capabilities from the bset and
> inheritted masks exposed at /proc/sys/kernel/usermodehelper if available

s/tt/t/

Cheers

Andrew

> (introduced in Linux v3.0.0).
>
> In all paths, we treat errors as fatal, as we do not want to continue to
> boot if there was a problem dropping capabilities. ÂWe fail because the
> new drop_capabilities= option on the command line mandates enforcement
> of a security policy, and we should err on the side of caution if we
> ever fail to satisfy the administrator's intention.
>
> Signed-off-by: Mike Waychison <mikew@xxxxxxxxxx>
> ---
> v2
> Â Â- Only drop from the inherited capabilities mask. ÂOnly this mask
> Â Â Âwas required as per Andrew Morgan. ÂThis also allows us to
> Â Â Âcontinue having capabilities to call run-init later on, allowing
> Â Â Âthe following:
> Â Â- Moved dropping of capabilities out of run-init and into kinit.
> Â Â ÂThis was required to ensure access to the proc filesystem, which
> Â Â Âis required for manipulating the masks the kernel uses when
> Â Â Âspawning new tasks on the kernel's behalf via the
> Â Â Âcall_usermodehelper() routine.
> Â Â- Fixed paths to the usermodehelper settings files.
> Â Â- Fixed sscanf to allow for a newline character when reading the
> Â Â Âusermodehelper capability mask settings.
> Â Â- Fixed the return check of the write to the usermodehelper files.
> Â Â ÂThese proc files return 0 on successful write(2).
> Â Â- Added a format atttribute to the fail() wrapper for better
> Â Â Âcompiler warnings.
> Â Â- Removed kernel version check. ÂThe boot will now always fail if we
> Â Â Âcould not manipulate the usermodehelper settings.
> Â Â- Sent as a single patch as capset/capget are now in klibc master.
> ---
> Âusr/kinit/Kbuild     |  Â1
> Âusr/kinit/capabilities.c | Â237 ++++++++++++++++++++++++++++++++++++++++++++++
> Âusr/kinit/kinit.c    Â|  Â2
> Âusr/kinit/kinit.h    Â|  Â2
> Â4 files changed, 242 insertions(+), 0 deletions(-)
> Âcreate mode 100644 usr/kinit/capabilities.c
>
> diff --git a/usr/kinit/Kbuild b/usr/kinit/Kbuild
> index ff1d449..8f6d08e 100644
> --- a/usr/kinit/Kbuild
> +++ b/usr/kinit/Kbuild
> @@ -10,6 +10,7 @@ kinit-y Â:= lib.a
> Âkinit-y Â+= kinit.o do_mounts.o ramdisk_load.o initrd.o
> Âkinit-y Â+= getintfile.o readfile.o xpio.o
> Âkinit-y Â+= do_mounts_md.o do_mounts_mtd.o nfsroot.o
> +kinit-y Â+= capabilities.o
>
> Âkinit-y Â+= ipconfig/
> Âkinit-y Â+= nfsmount/
> diff --git a/usr/kinit/capabilities.c b/usr/kinit/capabilities.c
> new file mode 100644
> index 0000000..ad4d6ea
> --- /dev/null
> +++ b/usr/kinit/capabilities.c
> @@ -0,0 +1,237 @@
> +/*
> + * Copyright 2011 Google Inc. All Rights Reserved
> + * Author: mikew@xxxxxxxxxx (Mike Waychison)
> + */
> +
> +/*
> + * We have to include the klibc types.h here to keep the kernel's
> + * types.h from being used.
> + */
> +#include <sys/types.h>
> +
> +#include <sys/capability.h>
> +#include <sys/prctl.h>
> +#include <sys/utsname.h>
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +
> +#include "kinit.h"
> +
> +#define ARRAY_SIZE(x) Â(sizeof(x) / sizeof(x[0]))
> +
> +#define MAKE_CAP(cap) [cap] = { .cap_name = #cap }
> +
> +struct capability {
> + Â Â Â const char *cap_name;
> +} capabilities[] = {
> + Â Â Â MAKE_CAP(CAP_CHOWN),
> + Â Â Â MAKE_CAP(CAP_DAC_OVERRIDE),
> + Â Â Â MAKE_CAP(CAP_DAC_READ_SEARCH),
> + Â Â Â MAKE_CAP(CAP_FOWNER),
> + Â Â Â MAKE_CAP(CAP_FSETID),
> + Â Â Â MAKE_CAP(CAP_KILL),
> + Â Â Â MAKE_CAP(CAP_SETGID),
> + Â Â Â MAKE_CAP(CAP_SETUID),
> + Â Â Â MAKE_CAP(CAP_SETPCAP),
> + Â Â Â MAKE_CAP(CAP_LINUX_IMMUTABLE),
> + Â Â Â MAKE_CAP(CAP_NET_BIND_SERVICE),
> + Â Â Â MAKE_CAP(CAP_NET_BROADCAST),
> + Â Â Â MAKE_CAP(CAP_NET_ADMIN),
> + Â Â Â MAKE_CAP(CAP_NET_RAW),
> + Â Â Â MAKE_CAP(CAP_IPC_LOCK),
> + Â Â Â MAKE_CAP(CAP_IPC_OWNER),
> + Â Â Â MAKE_CAP(CAP_SYS_MODULE),
> + Â Â Â MAKE_CAP(CAP_SYS_RAWIO),
> + Â Â Â MAKE_CAP(CAP_SYS_CHROOT),
> + Â Â Â MAKE_CAP(CAP_SYS_PTRACE),
> + Â Â Â MAKE_CAP(CAP_SYS_PACCT),
> + Â Â Â MAKE_CAP(CAP_SYS_ADMIN),
> + Â Â Â MAKE_CAP(CAP_SYS_BOOT),
> + Â Â Â MAKE_CAP(CAP_SYS_NICE),
> + Â Â Â MAKE_CAP(CAP_SYS_RESOURCE),
> + Â Â Â MAKE_CAP(CAP_SYS_TIME),
> + Â Â Â MAKE_CAP(CAP_SYS_TTY_CONFIG),
> + Â Â Â MAKE_CAP(CAP_MKNOD),
> + Â Â Â MAKE_CAP(CAP_LEASE),
> + Â Â Â MAKE_CAP(CAP_AUDIT_WRITE),
> + Â Â Â MAKE_CAP(CAP_AUDIT_CONTROL),
> + Â Â Â MAKE_CAP(CAP_SETFCAP),
> + Â Â Â MAKE_CAP(CAP_MAC_OVERRIDE),
> + Â Â Â MAKE_CAP(CAP_MAC_ADMIN),
> + Â Â Â MAKE_CAP(CAP_SYSLOG),
> +};
> +
> +static void fail(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
> +static void fail(const char *fmt, ...)
> +{
> + Â Â Â va_list args;
> +
> + Â Â Â va_start(args, fmt);
> + Â Â Â vfprintf(stderr, fmt, args);
> + Â Â Â va_end(args);
> + Â Â Â exit(1);
> +}
> +
> +/*
> + * Find the capability ordinal by name, and return its ordinal.
> + * Returns -1 on failure.
> + */
> +static int find_capability(const char *s)
> +{
> + Â Â Â int i;
> +
> + Â Â Â for (i = 0; i < ARRAY_SIZE(capabilities); i++) {
> + Â Â Â Â Â Â Â if (capabilities[i].cap_name
> + Â Â Â Â Â Â Â Â&& strcasecmp(s, capabilities[i].cap_name) == 0) {
> + Â Â Â Â Â Â Â Â Â Â Â return i;
> + Â Â Â Â Â Â Â }
> + Â Â Â }
> + Â Â Â return -1;
> +}
> +
> +static void do_capset(int cap_ordinal)
> +{
> + Â Â Â struct __user_cap_header_struct hdr;
> + Â Â Â struct __user_cap_data_struct caps[2];
> +
> + Â Â Â /* Get the current capability mask */
> + Â Â Â hdr.version = _LINUX_CAPABILITY_VERSION_3;
> + Â Â Â hdr.pid = getpid();
> + Â Â Â if (capget(&hdr, caps)) {
> + Â Â Â Â Â Â Â perror("capget()");
> + Â Â Â Â Â Â Â exit(1);
> + Â Â Â }
> +
> + Â Â Â /* Drop the bits */
> + Â Â Â if (cap_ordinal < 32)
> + Â Â Â Â Â Â Â caps[0].inheritable &= ~(1U << cap_ordinal);
> + Â Â Â else
> + Â Â Â Â Â Â Â caps[1].inheritable &= ~(1U << (cap_ordinal - 32));
> +
> + Â Â Â /* And drop the capability. */
> + Â Â Â hdr.version = _LINUX_CAPABILITY_VERSION_3;
> + Â Â Â hdr.pid = getpid();
> + Â Â Â if (capset(&hdr, caps))
> + Â Â Â Â Â Â Â fail("Couldn't drop the capability \"%s\"\n",
> + Â Â Â Â Â Â Â Â Â Âcapabilities[cap_ordinal].cap_name);
> +}
> +
> +static void do_bset(int cap_ordinal)
> +{
> + Â Â Â int ret;
> +
> + Â Â Â ret = prctl(PR_CAPBSET_READ, cap_ordinal);
> + Â Â Â if (ret == 1) {
> + Â Â Â Â Â Â Â ret = prctl(PR_CAPBSET_DROP, cap_ordinal);
> + Â Â Â Â Â Â Â if (ret != 0)
> + Â Â Â Â Â Â Â Â Â Â Â fail("Error dropping capability %s from bset\n",
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Âcapabilities[cap_ordinal].cap_name);
> + Â Â Â } else if (ret < 0)
> + Â Â Â Â Â Â Â fail("Kernel doesn't recognize capability %d\n", cap_ordinal);
> +}
> +
> +static void do_usermodehelper_file(const char *filename, int cap_ordinal)
> +{
> + Â Â Â uint32_t lo32, hi32;
> + Â Â Â FILE *file;
> + Â Â Â static const size_t buf_size = 80;
> + Â Â Â char buf[buf_size];
> + Â Â Â char tail;
> + Â Â Â size_t bytes_read;
> + Â Â Â int ret;
> +
> + Â Â Â /* Try and open the file */
> + Â Â Â file = fopen(filename, "r+");
> + Â Â Â if (!file && errno == ENOENT)
> + Â Â Â Â Â Â Â fail("Could not disable usermode helpers capabilities as "
> + Â Â Â Â Â Â Â Â Â Â"%s is not available\n", filename);
> + Â Â Â if (!file)
> + Â Â Â Â Â Â Â fail("Failed to access file %s errno %d\n", filename, errno);
> +
> + Â Â Â /* Read and process the current bits */
> + Â Â Â bytes_read = fread(buf, 1, buf_size - 1, file);
> + Â Â Â if (bytes_read == 0)
> + Â Â Â Â Â Â Â fail("Trouble reading %s\n", filename);
> + Â Â Â buf[bytes_read] = '\0';
> + Â Â Â ret = sscanf(buf, "%u %u %c", &lo32, &hi32, &tail);
> + Â Â Â if (ret != 2)
> + Â Â Â Â Â Â Â fail("Failed to understand %s \"%s\"\n", filename, buf);
> +
> + Â Â Â /* Clear the bits in the local copy */
> + Â Â Â if (cap_ordinal < 32)
> + Â Â Â Â Â Â Â lo32 &= ~(1 << cap_ordinal);
> + Â Â Â else
> + Â Â Â Â Â Â Â hi32 &= ~(1 << (cap_ordinal - 32));
> +
> + Â Â Â /* Commit the new bit masks to the kernel */
> + Â Â Â sprintf(buf, "%u %u", lo32, hi32);
> + Â Â Â ret = fwrite(buf, 1, strlen(buf) + 1, file);
> + Â Â Â if (ret != 0)
> + Â Â Â Â Â Â Â fail("Failed to commit usermode helper bitmasks: %d\n", ret);
> +
> + Â Â Â /* Cleanup */
> + Â Â Â fclose(file);
> +}
> +
> +static void do_usermodehelper(int cap_ordinal)
> +{
> + Â Â Â static const char * const files[] = {
> + Â Â Â Â Â Â Â "/proc/sys/kernel/usermodehelper/bset",
> + Â Â Â Â Â Â Â "/proc/sys/kernel/usermodehelper/inheritable",
> + Â Â Â };
> + Â Â Â int i;
> +
> + Â Â Â for (i = 0; i < ARRAY_SIZE(files); i++)
> + Â Â Â Â Â Â Â do_usermodehelper_file(files[i], cap_ordinal);
> +}
> +
> +static void drop_capability(int cap_ordinal)
> +{
> + Â Â Â do_usermodehelper(cap_ordinal);
> + Â Â Â do_bset(cap_ordinal);
> + Â Â Â do_capset(cap_ordinal);
> +
> + Â Â Â printf("Dropped capability: %s\n", capabilities[cap_ordinal].cap_name);
> +}
> +
> +int drop_capabilities(const char *caps)
> +{
> + Â Â Â char *s, *saveptr = NULL;
> + Â Â Â char *token;
> + Â Â Â int drop_setpcap = 0;
> +
> + Â Â Â if (!caps)
> + Â Â Â Â Â Â Â return 0;
> +
> + Â Â Â /* Create a duplicate string that can be modified. */
> + Â Â Â s = strdup(caps);
> + Â Â Â if (!s)
> + Â Â Â Â Â Â Â fail("Failed to drop caps as requested. ÂExiting\n");
> +
> + Â Â Â token = strtok_r(s, ",", &saveptr);
> + Â Â Â while (token) {
> + Â Â Â Â Â Â Â int cap_ordinal = find_capability(token);
> +
> + Â Â Â Â Â Â Â if (cap_ordinal < 0)
> + Â Â Â Â Â Â Â Â Â Â Â fail("Could not understand capability name \"%s\" "
> + Â Â Â Â Â Â Â Â Â Â Â Â Â Â"on command line, failing init\n", token);
> +
> + Â Â Â Â Â Â Â /* We handle CAP_SETPCAP last because it is needed to
> + Â Â Â Â Â Â Â Â* drop all other caps. */
> + Â Â Â Â Â Â Â if (cap_ordinal == CAP_SETPCAP)
> + Â Â Â Â Â Â Â Â Â Â Â drop_setpcap = 1;
> + Â Â Â Â Â Â Â else
> + Â Â Â Â Â Â Â Â Â Â Â drop_capability(cap_ordinal);
> +
> + Â Â Â Â Â Â Â token = strtok_r(NULL, ",", &saveptr);
> + Â Â Â }
> +
> + Â Â Â if (drop_setpcap)
> + Â Â Â Â Â Â Â drop_capability(CAP_SETPCAP);
> +
> + Â Â Â free(s);
> + Â Â Â return 0;
> +}
> diff --git a/usr/kinit/kinit.c b/usr/kinit/kinit.c
> index 4a1f40b..098873b 100644
> --- a/usr/kinit/kinit.c
> +++ b/usr/kinit/kinit.c
> @@ -288,6 +288,8 @@ int main(int argc, char *argv[])
> Â Â Â Âcheck_path("/root");
> Â Â Â Âdo_mounts(cmdc, cmdv);
>
> + Â Â Â drop_capabilities(get_arg(cmdc, cmdv, "drop_capabilities="));
> +
> Â Â Â Âif (mnt_procfs) {
> Â Â Â Â Â Â Â Âumount2("/proc", 0);
> Â Â Â Â Â Â Â Âmnt_procfs = 0;
> diff --git a/usr/kinit/kinit.h b/usr/kinit/kinit.h
> index c2e67b7..85960d8 100644
> --- a/usr/kinit/kinit.h
> +++ b/usr/kinit/kinit.h
> @@ -65,4 +65,6 @@ static inline void dump_args(int argc, char *argv[])
> Â}
> Â#endif
>
> +int drop_capabilities(const char *caps);
> +
> Â#endif             /* KINIT_H */
>
>
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/