blob: 9ab8580efa2dcd2245b0d59dc5a09bed1f11e5c5 [file] [log] [blame] [raw]
/*
* Copyright (C) 2000-2009, Parallels, Inc. All rights reserved.
*
* 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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <linux/capability.h>
#include <string.h>
#include <linux/vzcalluser.h>
#include "cap.h"
#include "res.h"
#include "vzerror.h"
#include "logger.h"
#include "util.h"
#ifndef CAP_AUDIT_WRITE
#define CAP_AUDIT_WRITE 29
#endif
#ifndef CAP_VE_ADMIN
#define CAP_VE_ADMIN 30
#endif
#ifndef CAP_SETFCAP
#define CAP_SETFCAP 31
#endif
#define CAPDEFAULTMASK_OLD \
CAP_TO_MASK(CAP_CHOWN) | \
CAP_TO_MASK(CAP_DAC_OVERRIDE) | \
CAP_TO_MASK(CAP_DAC_READ_SEARCH) | \
CAP_TO_MASK(CAP_FOWNER) | \
CAP_TO_MASK(CAP_FSETID) | \
CAP_TO_MASK(CAP_KILL) | \
CAP_TO_MASK(CAP_SETGID) | \
CAP_TO_MASK(CAP_SETUID) | \
CAP_TO_MASK(CAP_LINUX_IMMUTABLE) | \
CAP_TO_MASK(CAP_NET_BIND_SERVICE) | \
CAP_TO_MASK(CAP_NET_BROADCAST) | \
CAP_TO_MASK(CAP_NET_RAW) | \
CAP_TO_MASK(CAP_IPC_LOCK) | \
CAP_TO_MASK(CAP_IPC_OWNER) | \
CAP_TO_MASK(CAP_SYS_CHROOT) | \
CAP_TO_MASK(CAP_SYS_PTRACE) | \
CAP_TO_MASK(CAP_SYS_BOOT) | \
CAP_TO_MASK(CAP_SYS_NICE) | \
CAP_TO_MASK(CAP_SYS_RESOURCE) | \
CAP_TO_MASK(CAP_SYS_TTY_CONFIG) | \
CAP_TO_MASK(CAP_MKNOD) | \
CAP_TO_MASK(CAP_LEASE) | \
CAP_TO_MASK(CAP_VE_ADMIN) | \
CAP_TO_MASK(CAP_AUDIT_WRITE)
#define CAPDEFAULTMASK \
CAPDEFAULTMASK_OLD | \
CAP_TO_MASK(CAP_SETPCAP) | \
CAP_TO_MASK(CAP_SETFCAP)
static char *cap_names[] = {
"CHOWN", /* 0 */
"DAC_OVERRIDE", /* 1 */
"DAC_READ_SEARCH", /* 2 */
"FOWNER", /* 3 */
"FSETID", /* 4 */
"KILL", /* 5 */
"SETGID", /* 6 */
"SETUID", /* 7 */
"SETPCAP", /* 8 */
"LINUX_IMMUTABLE", /* 9 */
"NET_BIND_SERVICE", /* 10 */
"NET_BROADCAST", /* 11 */
"NET_ADMIN", /* 12 */
"NET_RAW", /* 13 */
"IPC_LOCK", /* 14 */
"IPC_OWNER", /* 15 */
"SYS_MODULE", /* 16 */
"SYS_RAWIO", /* 17 */
"SYS_CHROOT", /* 18 */
"SYS_PTRACE", /* 19 */
"SYS_PACCT", /* 20 */
"SYS_ADMIN", /* 21 */
"SYS_BOOT", /* 22 */
"SYS_NICE", /* 23 */
"SYS_RESOURCE", /* 24 */
"SYS_TIME", /* 25 */
"SYS_TTY_CONFIG", /* 26 */
"MKNOD", /* 27 */
"LEASE", /* 28 */
"AUDIT_WRITE", /* 29 */
"VE_ADMIN", /* 30 */
"SETFCAP", /* 31 */
"FS_MASK" /* 0x1f */
};
/* We can't include sys/capability.h since it conflicts
* with linux/capability.h, so we put this prototype here */
extern int capget(cap_user_header_t header, const cap_user_data_t data);
static inline int capset(cap_user_header_t header, cap_user_data_t data)
{
return syscall(__NR_capset, header, data);
}
/** Add capability name to capability mask.
*
* @param name capability name.
* @param mask capability mask.
* @return 0 on success.
*/
int get_cap_mask(char *name, unsigned long *mask)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE(cap_names); i++) {
if (!strcasecmp(name, cap_names[i])) {
cap_raise(*mask, i);
return 0;
}
}
return -1;
}
/** merge capabilities and return in string format.
*
* @param new new capability mask.
* @param old old capability mask.
* @param buf merged capabilities in string format.
* @param len max size of buf string.
*/
void build_cap_str(cap_param *new, cap_param *old, const char *delim,
char *buf, int len)
{
unsigned int i;
int r;
char *sp, *ep;
sp = buf;
ep = buf + len;
for (i = 0; i < ARRAY_SIZE(cap_names); i++) {
int op = 0;
if (CAP_TO_MASK(i) & new->on)
op = 1;
else if (CAP_TO_MASK(i) & new->off)
op = 2;
else if (old == NULL)
continue;
else if (CAP_TO_MASK(i) & old->on)
op = 1;
else if (CAP_TO_MASK(i) & old->off)
op = 2;
else
continue;
r = snprintf(sp, ep - sp, "%s%s:%s",
i == 0 ? "" : delim,
cap_names[i],
op == 1 ? "on" : "off");
if (r < 0 || sp + r >= ep)
break;
sp += r;
}
}
static int set_cap(envid_t veid, cap_t mask, int pid)
{
struct __user_cap_header_struct header;
struct __user_cap_data_struct data;
memset(&header, 0, sizeof(header));
header.version = _LINUX_CAPABILITY_VERSION;
capget(&header, NULL); /* Get linux capability version from kernel */
header.pid = pid;
memset(&data, 0, sizeof(data));
data.effective = mask;
data.permitted = mask;
data.inheritable = mask;
return capset(&header, &data);
}
static inline cap_t make_cap_mask(cap_t def, cap_t on, cap_t off)
{
return (def | on) & ~off;
}
int vps_set_cap(envid_t veid, struct env_param *env, cap_param *cap)
{
cap_t mask;
if ((env->features_known & env->features_mask) & VE_FEATURE_BRIDGE)
cap_raise(cap->on, CAP_NET_ADMIN);
mask = make_cap_mask(CAPDEFAULTMASK, cap->on, cap->off);
if (set_cap(veid, mask, 0) == 0)
return 0;
/* Probably old kernel -- retry with old default mask */
mask = make_cap_mask(CAPDEFAULTMASK_OLD, cap->on, cap->off);
if (set_cap(veid, mask, 0) == 0)
return 0;
logger(-1, errno, "Unable to set capability");
return VZ_SET_CAP;
}