All of lore.kernel.org
 help / color / mirror / Atom feed
From: Maximilian Attems <max@stro.at>
To: Mike Waychison <mikew@google.com>
Cc: "Andrew G. Morgan" <agm@google.com>,
	Eric Northup <digitaleric@google.com>,
	Alan Cox <alan@lxorguk.ukuu.org.uk>,
	"H. Peter Anvin" <hpa@zytor.com>,
	Eric Paris <eparis@parisplace.org>,
	klibc@zytor.com, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v1 2/2] run-init: Add drop_capabilities support.
Date: Fri, 29 Jul 2011 22:45:29 +0200	[thread overview]
Message-ID: <20110729204529.GB3207@stro.at> (raw)
In-Reply-To: <20110719203853.646.50974.stgit@mike2.sea.corp.google.com>

On Tue, 19 Jul 2011, Mike Waychison wrote:

> This patch adds the ability to run-init to allow the dropping of
> POSIX capabilities.
> 
> This works by adding a "-d" flag to run-init, which takes a comma
> separated list of capability names that should be dropped right before
> exec'ing the real init binary.
> 
> kinit is also modified by this change, such that it understands the same
> argument when prepended with "drop_capabilities=" on the kernel command
> line.
> 
> 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.
> 
> Dropping of capabilities happens in three parts.  We explicitly drop the
> capability from init's inherited, permitted and effective masks.  We
> also drop the capability from the bounding set using PR_CAPBSET_DROP.
> Lastly, if available, we drop the capabilities from the bset and
> inheritted masks exposed at /proc/sys/kernel/usermodehelper if available
> (introduced in v3.0.0).

hmm as 3.0 is out, I don't think we need more backward compatibility.
do you have a strong arg for it?
especially since this is an *optional* calling arg I really don't see
the need of that backward crap.
 
> In all paths, we treat errors as fatal, as we do not want to continue to
> boot if there was a problem dropping capabilities.   The only exception
> to this rule is the handling of /proc/sys/kernel/usermodehelper, where
> we print out a warning if we notice that the kernel is new enough to
> support this interface, but could not find the proc file (as it may or
> may not be available after the pivot, depending on early portions of the
> boot strap process).
> 
> Signed-off-by: Mike Waychison <mikew@google.com>
> ---
>  usr/kinit/kinit.c                 |    4 -
>  usr/kinit/run-init/Kbuild         |    2 
>  usr/kinit/run-init/capabilities.c |  278 +++++++++++++++++++++++++++++++++++++
>  usr/kinit/run-init/capabilities.h |    6 +
>  usr/kinit/run-init/run-init.c     |   11 +
>  usr/kinit/run-init/run-init.h     |    3 
>  usr/kinit/run-init/runinitlib.c   |   11 +
>  7 files changed, 307 insertions(+), 8 deletions(-)
>  create mode 100644 usr/kinit/run-init/capabilities.c
>  create mode 100644 usr/kinit/run-init/capabilities.h
> 
> diff --git a/usr/kinit/kinit.c b/usr/kinit/kinit.c
> index 4a1f40b..ae50ed6 100644
> --- a/usr/kinit/kinit.c
> +++ b/usr/kinit/kinit.c
> @@ -307,7 +307,9 @@ int main(int argc, char *argv[])
>  
>  	init_argv[0] = strrchr(init_path, '/') + 1;
>  
> -	errmsg = run_init("/root", "/dev/console", init_path, init_argv);
> +	errmsg = run_init("/root", "/dev/console",
> +			  get_arg(cmdc, cmdv, "drop_capabilities="),
> +			  init_path, init_argv);
>  
>  	/* If run_init returned, something went bad */
>  	fprintf(stderr, "%s: %s: %s\n", progname, errmsg, strerror(errno));
> diff --git a/usr/kinit/run-init/Kbuild b/usr/kinit/run-init/Kbuild
> index bf6e140..6451dd4 100644
> --- a/usr/kinit/run-init/Kbuild
> +++ b/usr/kinit/run-init/Kbuild
> @@ -6,7 +6,7 @@ static-y := static/run-init
>  shared-y := shared/run-init
>  
>  # common .o files
> -objs := run-init.o runinitlib.o
> +objs := run-init.o runinitlib.o capabilities.o
>  
>  # TODO - do we want a stripped version
>  # TODO - do we want the static.g + shared.g directories?
> diff --git a/usr/kinit/run-init/capabilities.c b/usr/kinit/run-init/capabilities.c
> new file mode 100644
> index 0000000..d262c01
> --- /dev/null
> +++ b/usr/kinit/run-init/capabilities.c
> @@ -0,0 +1,278 @@
> +/*
> + * Copyright 2011 Google Inc. All Rights Reserved
> + * Author: mikew@google.com (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 <linux/version.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 "capabilities.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, ...)
> +{
> +	va_list args;
> +
> +	va_start(args, fmt);
> +	vfprintf(stderr, fmt, args);
> +	va_end(args);
> +	exit(1);
> +}
> +
> +/*
> + * Returns the currently running kernel version X.Y.Z in a format
> + * compatible with the KERNEL_VERSION macro.
> + */
> +static unsigned kernel_version(void)
> +{
> +	struct utsname utsname;
> +	int ret;
> +	unsigned char version, patchlevel, sublevel;
> +
> +	ret = uname(&utsname);
> +	if (ret != 0)
> +		fail("uname returned %d\n", ret);
> +
> +	ret = sscanf(utsname.release, "%hhu.%hhu.%hhu",
> +		     &version, &patchlevel, &sublevel);
> +	if (ret != 3) {
> +		/* Try two level name? */
> +		sublevel = 0;
> +		ret = sscanf(utsname.release, "%hhu.%hhu",
> +			     &version, &patchlevel);
> +		if (ret != 2)
> +			fail("Couldn't parse kernel version \"%s\"\n",
> +			     utsname.release);
> +	}
> +
> +	return KERNEL_VERSION(version, patchlevel, sublevel);
> +}
> +
> +/*
> + * 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].effective   &= ~(1U << cap_ordinal);
> +		caps[0].permitted   &= ~(1U << cap_ordinal);
> +		caps[0].inheritable &= ~(1U << cap_ordinal);
> +	} else {
> +		caps[1].effective   &= ~(1U << (cap_ordinal - 32));
> +		caps[1].permitted   &= ~(1U << (cap_ordinal - 32));
> +		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) {
> +		/* Check if this kernel even supports this interface. */
> +		if (kernel_version() >= KERNEL_VERSION(3, 0, 0)) {
> +			static int printed_once;
> +			if (!printed_once++)
> +				fprintf(stderr, "WARNING: Could not disable "
> +					"capabilities for usermode helpers!\n");
> +		}
> +		return;
> +	}
> +	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\n", filename);
> +
> +	/* 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 != strlen(buf) + 1)
> +		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/bset",
> +		"/proc/sys/kernel/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 do_capabilities(const char *drop_capabilities)
> +{
> +	char *s, *saveptr = NULL;
> +	char *token;
> +	int drop_setpcap = 0;
> +
> +	if (!drop_capabilities)
> +		return 0;
> +
> +	/* Create a duplicate string that can be modified. */
> +	s = strdup(drop_capabilities);
> +	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/run-init/capabilities.h b/usr/kinit/run-init/capabilities.h
> new file mode 100644
> index 0000000..bf51eec
> --- /dev/null
> +++ b/usr/kinit/run-init/capabilities.h
> @@ -0,0 +1,6 @@
> +#ifndef CAPABILITIES_H
> +#define CAPABILITIES_H
> +
> +int do_capabilities(const char *drop_capabilities);
> +
> +#endif				/* CAPABILITIES_H */
> diff --git a/usr/kinit/run-init/run-init.c b/usr/kinit/run-init/run-init.c
> index 0f150dd..cc602ef 100644
> --- a/usr/kinit/run-init/run-init.c
> +++ b/usr/kinit/run-init/run-init.c
> @@ -35,6 +35,7 @@
>   * - Remounts /real-root onto the root filesystem;
>   * - Chroots;
>   * - Opens /dev/console;
> + * - Drops capabilities
>   * - Spawns the specified init program (with arguments.)
>   */
>  
> @@ -50,7 +51,8 @@ static const char *program;
>  static void __attribute__ ((noreturn)) usage(void)
>  {
>  	fprintf(stderr,
> -		"Usage: exec %s [-c consoledev] /real-root /sbin/init [args]\n",
> +		"Usage: exec %s [-c consoledev] [-d <CAP_NAME,...>] "
> +		"/real-root /sbin/init [args]\n",
>  		program);
>  	exit(1);
>  }
> @@ -62,6 +64,7 @@ int main(int argc, char *argv[])
>  	const char *realroot;
>  	const char *init;
>  	const char *error;
> +	const char *drop_capabilities = NULL;
>  	char **initargs;
>  
>  	/* Variables... */
> @@ -70,9 +73,11 @@ int main(int argc, char *argv[])
>  	/* Parse the command line */
>  	program = argv[0];
>  
> -	while ((o = getopt(argc, argv, "c:")) != -1) {
> +	while ((o = getopt(argc, argv, "c:d:")) != -1) {
>  		if (o == 'c') {
>  			console = optarg;
> +		} else if (o == 'd') {
> +			drop_capabilities = optarg;
>  		} else {
>  			usage();
>  		}
> @@ -85,7 +90,7 @@ int main(int argc, char *argv[])
>  	init = argv[optind + 1];
>  	initargs = argv + optind + 1;
>  
> -	error = run_init(realroot, console, init, initargs);
> +	error = run_init(realroot, console, drop_capabilities, init, initargs);
>  
>  	/* If run_init returns, something went wrong */
>  	fprintf(stderr, "%s: %s: %s\n", program, error, strerror(errno));
> diff --git a/usr/kinit/run-init/run-init.h b/usr/kinit/run-init/run-init.h
> index a95328e..30f78bf 100644
> --- a/usr/kinit/run-init/run-init.h
> +++ b/usr/kinit/run-init/run-init.h
> @@ -29,6 +29,7 @@
>  #define RUN_INIT_H
>  
>  const char *run_init(const char *realroot, const char *console,
> -		     const char *init, char **initargs);
> +		     const char *drop_capabilities, const char *init,
> +		     char **initargs);
>  
>  #endif
> diff --git a/usr/kinit/run-init/runinitlib.c b/usr/kinit/run-init/runinitlib.c
> index 8f1562f..a5cb10c 100644
> --- a/usr/kinit/run-init/runinitlib.c
> +++ b/usr/kinit/run-init/runinitlib.c
> @@ -26,7 +26,7 @@
>   * ----------------------------------------------------------------------- */
>  
>  /*
> - * run_init(consoledev, realroot, init, initargs)
> + * run_init(consoledev, realroot, drop_capabilities, init, initargs)
>   *
>   * This function should be called as the last thing in kinit,
>   * from initramfs, it does the following:
> @@ -35,6 +35,7 @@
>   * - Remounts /real-root onto the root filesystem;
>   * - Chroots;
>   * - Opens /dev/console;
> + * - Drops capabilities if needed;
>   * - Spawns the specified init program (with arguments.)
>   *
>   * On failure, returns a human-readable error message.
> @@ -52,7 +53,9 @@
>  #include <sys/stat.h>
>  #include <sys/types.h>
>  #include <sys/vfs.h>
> +
>  #include "run-init.h"
> +#include "capabilities.h"
>  
>  /* Make it possible to compile on glibc by including constants that the
>     always-behind shipped glibc headers may not include.  Classic example
> @@ -154,7 +157,8 @@ static int nuke(const char *what)
>  }
>  
>  const char *run_init(const char *realroot, const char *console,
> -		     const char *init, char **initargs)
> +		     const char *drop_capabilities, const char *init,
> +		     char **initargs)
>  {
>  	struct stat rst, cst;
>  	struct statfs sfs;
> @@ -203,6 +207,9 @@ const char *run_init(const char *realroot, const char *console,
>  	dup2(confd, 2);
>  	close(confd);
>  
> +	/* Drop capabilities */
> +	do_capabilities(drop_capabilities);
> +
>  	/* Spawn init */
>  	execv(init, initargs);
>  	return init;		/* Failed to spawn init */
-- 
maks

  reply	other threads:[~2011-07-29 20:45 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-07-19 20:38 [PATCH v1 0/2] Support dropping of capabilities from early userspace Mike Waychison
2011-07-19 20:38 ` [PATCH v1 1/2] syscalls: Add capset and capget Mike Waychison
2011-07-29 20:41   ` Maximilian Attems
2011-07-29 23:06   ` Maximilian Attems
2011-07-19 20:38 ` [PATCH v1 2/2] run-init: Add drop_capabilities support Mike Waychison
2011-07-29 20:45   ` Maximilian Attems [this message]
2011-07-29 20:46     ` Mike Waychison
2011-08-02 21:09       ` Maximilian Attems
2011-08-02 21:42         ` Mike Waychison
2011-08-02 22:50           ` Andrew G. Morgan
2011-08-02 22:56             ` Mike Waychison
2011-08-02 23:37               ` Mike Waychison
2011-08-03  0:48                 ` H. Peter Anvin

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20110729204529.GB3207@stro.at \
    --to=max@stro.at \
    --cc=agm@google.com \
    --cc=alan@lxorguk.ukuu.org.uk \
    --cc=digitaleric@google.com \
    --cc=eparis@parisplace.org \
    --cc=hpa@zytor.com \
    --cc=klibc@zytor.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=mikew@google.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.