nsjail/caps.c

289 lines
8.2 KiB
C
Raw Normal View History

/*
nsjail - capability-related operations
-----------------------------------------
Copyright 2014 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "caps.h"
#include <linux/capability.h>
#include <string.h>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include "common.h"
#include "log.h"
2017-07-06 17:37:41 +08:00
#include "util.h"
static struct {
const int val;
const char* const name;
} const capNames[] = {
2017-10-09 05:06:40 +08:00
NS_VALSTR_STRUCT(CAP_CHOWN),
NS_VALSTR_STRUCT(CAP_DAC_OVERRIDE),
NS_VALSTR_STRUCT(CAP_DAC_READ_SEARCH),
NS_VALSTR_STRUCT(CAP_FOWNER),
NS_VALSTR_STRUCT(CAP_FSETID),
NS_VALSTR_STRUCT(CAP_KILL),
NS_VALSTR_STRUCT(CAP_SETGID),
NS_VALSTR_STRUCT(CAP_SETUID),
NS_VALSTR_STRUCT(CAP_SETPCAP),
NS_VALSTR_STRUCT(CAP_LINUX_IMMUTABLE),
NS_VALSTR_STRUCT(CAP_NET_BIND_SERVICE),
NS_VALSTR_STRUCT(CAP_NET_BROADCAST),
NS_VALSTR_STRUCT(CAP_NET_ADMIN),
NS_VALSTR_STRUCT(CAP_NET_RAW),
NS_VALSTR_STRUCT(CAP_IPC_LOCK),
NS_VALSTR_STRUCT(CAP_IPC_OWNER),
NS_VALSTR_STRUCT(CAP_SYS_MODULE),
NS_VALSTR_STRUCT(CAP_SYS_RAWIO),
NS_VALSTR_STRUCT(CAP_SYS_CHROOT),
NS_VALSTR_STRUCT(CAP_SYS_PTRACE),
NS_VALSTR_STRUCT(CAP_SYS_PACCT),
NS_VALSTR_STRUCT(CAP_SYS_ADMIN),
NS_VALSTR_STRUCT(CAP_SYS_BOOT),
NS_VALSTR_STRUCT(CAP_SYS_NICE),
NS_VALSTR_STRUCT(CAP_SYS_RESOURCE),
NS_VALSTR_STRUCT(CAP_SYS_TIME),
NS_VALSTR_STRUCT(CAP_SYS_TTY_CONFIG),
NS_VALSTR_STRUCT(CAP_MKNOD),
NS_VALSTR_STRUCT(CAP_LEASE),
NS_VALSTR_STRUCT(CAP_AUDIT_WRITE),
NS_VALSTR_STRUCT(CAP_AUDIT_CONTROL),
NS_VALSTR_STRUCT(CAP_SETFCAP),
NS_VALSTR_STRUCT(CAP_MAC_OVERRIDE),
NS_VALSTR_STRUCT(CAP_MAC_ADMIN),
NS_VALSTR_STRUCT(CAP_SYSLOG),
NS_VALSTR_STRUCT(CAP_WAKE_ALARM),
NS_VALSTR_STRUCT(CAP_BLOCK_SUSPEND),
#if defined(CAP_AUDIT_READ)
2017-10-09 05:06:40 +08:00
NS_VALSTR_STRUCT(CAP_AUDIT_READ),
#endif /* defined(CAP_AUDIT_READ) */
};
2017-10-09 05:00:45 +08:00
int capsNameToVal(const char* name)
{
for (size_t i = 0; i < ARRAYSIZE(capNames); i++) {
if (strcmp(name, capNames[i].name) == 0) {
return capNames[i].val;
}
}
LOG_W("Uknown capability: '%s'", name);
return -1;
}
2017-10-09 05:00:45 +08:00
static const char* capsValToStr(int val)
{
static __thread char capsStr[1024];
for (size_t i = 0; i < ARRAYSIZE(capNames); i++) {
if (val == capNames[i].val) {
snprintf(capsStr, sizeof(capsStr), "%s", capNames[i].name);
return capsStr;
}
}
snprintf(capsStr, sizeof(capsStr), "CAP_UNKNOWN(%d)", val);
return capsStr;
}
static cap_user_data_t capsGet()
{
static __thread struct __user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_3];
const struct __user_cap_header_struct cap_hdr = {
.version = _LINUX_CAPABILITY_VERSION_3,
.pid = 0,
};
if (syscall(__NR_capget, &cap_hdr, &cap_data) == -1) {
PLOG_W("capget() failed");
return NULL;
}
return cap_data;
2017-07-05 21:57:07 +08:00
}
static bool capsSet(const cap_user_data_t cap_data)
2017-07-05 21:57:07 +08:00
{
const struct __user_cap_header_struct cap_hdr = {
.version = _LINUX_CAPABILITY_VERSION_3,
.pid = 0,
};
if (syscall(__NR_capset, &cap_hdr, cap_data) == -1) {
PLOG_W("capset() failed");
return false;
2017-07-05 21:57:07 +08:00
}
return true;
2017-07-05 21:57:07 +08:00
}
static void capsClearInheritable(cap_user_data_t cap_data)
2017-07-05 21:57:07 +08:00
{
for (size_t i = 0; i < _LINUX_CAPABILITY_U32S_3; i++) {
cap_data[i].inheritable = 0U;
2017-07-05 21:57:07 +08:00
}
}
static bool capsGetPermitted(cap_user_data_t cap_data, unsigned int cap)
2017-07-05 21:57:07 +08:00
{
size_t off_byte = cap / (sizeof(cap_data->permitted) * 8);
size_t off_bit = cap % (sizeof(cap_data->permitted) * 8);
return cap_data[off_byte].permitted & (1U << off_bit);
2017-07-05 21:57:07 +08:00
}
static bool capsGetEffective(cap_user_data_t cap_data, unsigned int cap)
{
size_t off_byte = cap / (sizeof(cap_data->effective) * 8);
size_t off_bit = cap % (sizeof(cap_data->effective) * 8);
return cap_data[off_byte].effective & (1U << off_bit);
}
static bool capsGetInheritable(cap_user_data_t cap_data, unsigned int cap)
2017-07-05 21:57:07 +08:00
{
size_t off_byte = cap / (sizeof(cap_data->inheritable) * 8);
size_t off_bit = cap % (sizeof(cap_data->inheritable) * 8);
return cap_data[off_byte].inheritable & (1U << off_bit);
}
static void capsSetInheritable(cap_user_data_t cap_data, unsigned int cap)
{
size_t off_byte = cap / (sizeof(cap_data->inheritable) * 8);
size_t off_bit = cap % (sizeof(cap_data->inheritable) * 8);
cap_data[off_byte].inheritable |= (1U << off_bit);
2017-07-05 21:57:07 +08:00
}
#if !defined(PR_CAP_AMBIENT)
#define PR_CAP_AMBIENT 47
#define PR_CAP_AMBIENT_RAISE 2
#define PR_CAP_AMBIENT_CLEAR_ALL 4
2017-10-09 05:00:45 +08:00
#endif /* !defined(PR_CAP_AMBIENT) */
static bool CapsInitNsKeepCaps(cap_user_data_t cap_data)
{
char dbgmsg[4096];
/* Copy all permitted caps to the inheritable set */
dbgmsg[0] = '\0';
for (size_t i = 0; i < ARRAYSIZE(capNames); i++) {
2017-10-18 21:41:16 +08:00
if (capsGetPermitted(cap_data, capNames[i].val)) {
utilSSnPrintf(dbgmsg, sizeof(dbgmsg), " %s", capNames[i].name);
capsSetInheritable(cap_data, capNames[i].val);
}
}
LOG_D("Adding the following capabilities to the inheritable set:%s", dbgmsg);
if (capsSet(cap_data) == false) {
return false;
}
/* Make sure the inheritable set is preserved across execve via the ambient set */
dbgmsg[0] = '\0';
for (size_t i = 0; i < ARRAYSIZE(capNames); i++) {
if (capsGetPermitted(cap_data, capNames[i].val) == false) {
continue;
}
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, (unsigned long)capNames[i].val, 0UL,
2017-10-09 05:00:45 +08:00
0UL)
== -1) {
PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %s)", capNames[i].name);
} else {
utilSSnPrintf(dbgmsg, sizeof(dbgmsg), " %s", capNames[i].name);
}
}
LOG_D("Added the following capabilities to the ambient set:%s", dbgmsg);
return true;
}
2017-10-09 05:00:45 +08:00
bool capsInitNs(struct nsjconf_t* nsjconf)
2017-07-05 21:57:07 +08:00
{
char dbgmsg[4096];
2017-10-09 05:00:45 +08:00
struct ints_t* p;
cap_user_data_t cap_data = capsGet();
if (cap_data == NULL) {
return false;
}
2017-10-01 11:49:13 +08:00
/* Let's start with an empty inheritable set to avoid any mistakes */
capsClearInheritable(cap_data);
/*
2017-10-01 11:49:13 +08:00
* Remove all capabilities from the ambient set first. It works with newer kernel versions
* only, so don't panic() if it fails
*/
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL, 0UL, 0UL, 0UL) == -1) {
PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_CLEAR_ALL)");
}
if (nsjconf->keep_caps) {
return CapsInitNsKeepCaps(cap_data);
}
/* Set all requested caps in the inheritable set if these are present in the permitted set
*/
dbgmsg[0] = '\0';
2017-10-09 05:00:45 +08:00
TAILQ_FOREACH(p, &nsjconf->caps, pointers)
{
if (capsGetPermitted(cap_data, p->val) == false) {
LOG_W("Capability %s is not permitted in the namespace",
2017-10-09 05:00:45 +08:00
capsValToStr(p->val));
return false;
}
utilSSnPrintf(dbgmsg, sizeof(dbgmsg), " %s", capsValToStr(p->val));
capsSetInheritable(cap_data, p->val);
}
2017-07-06 17:37:41 +08:00
LOG_D("Adding the following capabilities to the inheritable set:%s", dbgmsg);
if (capsSet(cap_data) == false) {
return false;
}
/*
* Make sure all other caps (those which were not explicitly requested) are removed from the
2017-10-01 11:49:13 +08:00
* bounding set. We need to have CAP_SETPCAP to do that now
*/
2017-10-18 21:41:16 +08:00
if (capsGetEffective(cap_data, CAP_SETPCAP)) {
dbgmsg[0] = '\0';
for (size_t i = 0; i < ARRAYSIZE(capNames); i++) {
2017-10-18 21:41:16 +08:00
if (capsGetInheritable(cap_data, capNames[i].val)) {
continue;
}
utilSSnPrintf(dbgmsg, sizeof(dbgmsg), " %s", capNames[i].name);
if (prctl(PR_CAPBSET_DROP, (unsigned long)capNames[i].val, 0UL, 0UL, 0UL)
== -1) {
PLOG_W("prctl(PR_CAPBSET_DROP, %s)", capNames[i].name);
return false;
}
}
LOG_D("Dropped the following capabilities from the bounding set:%s", dbgmsg);
}
/* Make sure inheritable set is preserved across execve via the modified ambient set */
dbgmsg[0] = '\0';
2017-10-09 05:00:45 +08:00
TAILQ_FOREACH(p, &nsjconf->caps, pointers)
{
if (prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, (unsigned long)p->val, 0UL, 0UL)
== -1) {
PLOG_W("prctl(PR_CAP_AMBIENT, PR_CAP_AMBIENT_RAISE, %s)",
2017-10-09 05:00:45 +08:00
capsValToStr(p->val));
} else {
utilSSnPrintf(dbgmsg, sizeof(dbgmsg), " %s", capsValToStr(p->val));
}
}
LOG_D("Added the following capabilities to the ambient set:%s", dbgmsg);
return true;
}