I am looking into the Linux Capabilities, which have been part of the
kernel since 2.2. There was a security hole which was patched in
2.2.19, and they are used throughout the kernel. Because of this hole
it is not possible for one process to change the capabilities of a
different process, and calling exec() to load a new application resets
the capabilities. However everything I have read says that if you jump
through the right hoops a setuid-root application should be able to
drop its capabilities and then setuid to a user level and keep the
capabilities that it retained. I am not seeing that behavior on this
stock MDK9.1 installation:
UID=501 EffUID=0
PR_SET_KEEPCAPS=0
Now PR_SET_KEEPCAPS=1
cap CAP_NET_RAW = effective SET, permitted SET, inheritable SET
cap CAP_NET_ADMIN = effective SET, permitted SET, inheritable SET
Removed CAP_NET_RAW.
PR_SET_KEEPCAPS=1
cap CAP_NET_RAW = effective CLEAR, permitted CLEAR, inheritable CLEAR
cap CAP_NET_ADMIN = effective SET, permitted SET, inheritable SET
As user: UID=501 EffUID=501
cap CAP_NET_RAW = effective CLEAR, permitted CLEAR, inheritable CLEAR
cap CAP_NET_ADMIN = effective CLEAR, permitted CLEAR, inheritable CLEAR
I have checked the kernel source and cap_emulate_setxuid() seems to
implement the behavior I expect, cap-bounding is only used on exec(),
and I cannot see why setting anything in CAP_INIT_INH_SET should make a
difference since, as seen above, the bits I am testing are inheritable
now.
What, in MDK9.1 prevents it from working?
--
Richard Urwin
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <sys/prctl.h>
#include <linux/unistd.h>
#include <linux/capability.h>
_syscall2(int, capget, cap_user_header_t, header, cap_user_data_t, dataptr);
_syscall2(int, capset, cap_user_header_t, header, cap_user_data_t, dataptr);
typedef struct __user_cap_header_struct capheader_t;
typedef struct __user_cap_data_struct capdata_t;
char * isbit (__u32 word, int bit)
{
static char buffer[10];
sprintf (buffer, word & (1<< bit) ?"SET": "CLEAR");
return buffer;
}
char * describe_cap (capdata_t *data, int cap)
{
static char buffer[80];
sprintf (buffer, "effective %s, permitted %s, inheritable %s",
isbit(data->effective,cap),
isbit(data->permitted,cap),
isbit(data->inheritable,cap));
return buffer;
}
void remove_cap(capdata_t *data, int cap) {
data->effective &= ~(1 << cap);
data->permitted &= ~(1 << cap);
data->inheritable &= ~(1 << cap);
}
void cap_get(capheader_t *header, capdata_t *data) {
if (capget(header, data) == 0) return;
perror("capget");
exit(-1);
}
void cap_set(capheader_t *header, capdata_t *data) {
if (capset(header, data) == 0) return;
perror("capset");
exit(-1);
}
main() {
capheader_t header;
capdata_t data;
if (geteuid()) {
printf("Run me as root please\n");
exit(1);
}
header.version = _LINUX_CAPABILITY_VERSION;
header.pid = 0;
data.effective = data.permitted = data.inheritable = 0;
printf("UID=%d EffUID=%d\n", getuid(), geteuid());
printf("PR_SET_KEEPCAPS=%d\n", prctl(PR_GET_KEEPCAPS));
if (prctl(PR_SET_KEEPCAPS, 1) < 0)
{
perror ("prctl");
exit(1);
}
printf("Now PR_SET_KEEPCAPS=%d\n", prctl(PR_GET_KEEPCAPS));
cap_get(&header, &data);
printf ("cap CAP_NET_RAW = %s\n\n", describe_cap (&data, CAP_NET_RAW));
printf ("cap CAP_NET_ADMIN = %s\n\n", describe_cap (&data, CAP_NET_ADMIN));
remove_cap(&data, CAP_NET_RAW);
cap_set(&header, &data);
printf ("Removed CAP_NET_RAW.\n");
printf("PR_SET_KEEPCAPS=%d\n", prctl(PR_GET_KEEPCAPS));
cap_get(&header, &data);
printf ("cap CAP_NET_RAW = %s\n", describe_cap (&data, CAP_NET_RAW));
printf ("cap CAP_NET_ADMIN = %s\n\n", describe_cap (&data, CAP_NET_ADMIN));
setuid(getuid());
printf("As user: UID=%d EffUID=%d\n", getuid(), geteuid());
cap_get(&header, &data);
printf ("cap CAP_NET_RAW = %s\n", describe_cap (&data, CAP_NET_RAW));
printf ("cap CAP_NET_ADMIN = %s\n\n", describe_cap (&data, CAP_NET_ADMIN));
}
Want to buy your Pack or Services from MandrakeSoft?
Go to http://www.mandrakestore.com