blob: eab4d937af1f6e5c22af277537be980bcdc1f9b6 [file] [log] [blame]
/*
* 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 <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;
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);
drop_capability(cap_ordinal);
token = strtok_r(NULL, ",", &saveptr);
}
free(s);
return 0;
}