Implement full decoding of 64-bit capabilities
Unlike v1 capabilities which are 32-bit, v2 and v3 are 64-bit, but before this change only lower 32 capability bits were decoded for v2 and v3. * xlat/capabilities1.in: New file. * capability.c: Define v2/v3 CAP_* constants. Include xlat/capabilities1.h. (get_cap_header): New function. (print_cap_header): Update to use get_cap_header result. (print_cap_data): Decoder higher capability bits for v2 and v3. (sys_capget, sys_capset): Use get_cap_header, update print_cap_header and print_cap_data calls. * tests/caps.c: New file. * tests/caps.awk: New file. * tests/caps.test: New test. * tests/Makefile.am (CHECK_PROGRAMS): Add caps. (TESTS): Add caps.test. (EXTRA_DIST): Add caps.awk.
This commit is contained in:
parent
bf7fdfa96e
commit
4b9c68b9a3
126
capability.c
126
capability.c
@ -1,5 +1,6 @@
|
||||
#include "defs.h"
|
||||
|
||||
/* these constants are the same as in <linux/capability.h> */
|
||||
enum {
|
||||
CAP_CHOWN,
|
||||
CAP_DAC_OVERRIDE,
|
||||
@ -37,6 +38,19 @@ enum {
|
||||
|
||||
#include "xlat/capabilities.h"
|
||||
|
||||
/* these constants are CAP_TO_INDEX'ed constants from <linux/capability.h> */
|
||||
enum {
|
||||
CAP_MAC_OVERRIDE,
|
||||
CAP_MAC_ADMIN,
|
||||
CAP_SYSLOG,
|
||||
CAP_WAKE_ALARM,
|
||||
CAP_BLOCK_SUSPEND,
|
||||
CAP_AUDIT_READ
|
||||
};
|
||||
|
||||
#include "xlat/capabilities1.h"
|
||||
|
||||
/* these constants are the same as in <linux/capability.h> */
|
||||
enum {
|
||||
_LINUX_CAPABILITY_VERSION_1 = 0x19980330,
|
||||
_LINUX_CAPABILITY_VERSION_2 = 0x20071026,
|
||||
@ -56,58 +70,101 @@ typedef struct user_cap_data_struct {
|
||||
uint32_t inheritable;
|
||||
} *cap_user_data_t;
|
||||
|
||||
static void
|
||||
print_cap_header(struct tcb *tcp, unsigned long addr)
|
||||
static cap_user_header_t
|
||||
get_cap_header(struct tcb *tcp, unsigned long addr)
|
||||
{
|
||||
union { cap_user_header_t p; long *a; char *c; } arg;
|
||||
long a[sizeof(*arg.p) / sizeof(long) + 1];
|
||||
arg.a = a;
|
||||
static struct user_cap_header_struct header;
|
||||
|
||||
if (!addr)
|
||||
if (!addr || !verbose(tcp))
|
||||
return NULL;
|
||||
|
||||
if (umove(tcp, addr, &header) < 0)
|
||||
return NULL;
|
||||
|
||||
return &header;
|
||||
}
|
||||
|
||||
static void
|
||||
print_cap_header(struct tcb *tcp, unsigned long addr, cap_user_header_t h)
|
||||
{
|
||||
if (!addr) {
|
||||
tprints("NULL");
|
||||
else if (!verbose(tcp) ||
|
||||
umoven(tcp, addr, sizeof(*arg.p), arg.c) < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
if (!h) {
|
||||
tprintf("%#lx", addr);
|
||||
else {
|
||||
tprints("{");
|
||||
printxval(cap_version, arg.p->version,
|
||||
"_LINUX_CAPABILITY_VERSION_???");
|
||||
tprintf(", %d}", arg.p->pid);
|
||||
return;
|
||||
}
|
||||
|
||||
tprints("{");
|
||||
printxval(cap_version, h->version,
|
||||
"_LINUX_CAPABILITY_VERSION_???");
|
||||
tprintf(", %d}", h->pid);
|
||||
}
|
||||
|
||||
static void
|
||||
print_cap_bits(const uint32_t lo, const uint32_t hi)
|
||||
{
|
||||
if (lo || !hi)
|
||||
printflags(capabilities, lo, "CAP_???");
|
||||
|
||||
if (hi) {
|
||||
if (lo)
|
||||
tprints("|");
|
||||
printflags(capabilities1, hi, "CAP_???");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_cap_data(struct tcb *tcp, unsigned long addr)
|
||||
print_cap_data(struct tcb *tcp, unsigned long addr, const cap_user_header_t h)
|
||||
{
|
||||
union { cap_user_data_t p; long *a; char *c; } arg;
|
||||
long a[sizeof(*arg.p) / sizeof(long) + 1];
|
||||
arg.a = a;
|
||||
struct user_cap_data_struct data[2];
|
||||
unsigned int len;
|
||||
|
||||
if (!addr)
|
||||
if (!addr) {
|
||||
tprints("NULL");
|
||||
else if (!verbose(tcp) ||
|
||||
(exiting(tcp) && syserror(tcp)) ||
|
||||
umoven(tcp, addr, sizeof(*arg.p), arg.c) < 0)
|
||||
tprintf("%#lx", addr);
|
||||
else {
|
||||
tprints("{");
|
||||
printflags(capabilities, arg.p->effective, "CAP_???");
|
||||
tprints(", ");
|
||||
printflags(capabilities, arg.p->permitted, "CAP_???");
|
||||
tprints(", ");
|
||||
printflags(capabilities, arg.p->inheritable, "CAP_???");
|
||||
tprints("}");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!h || !verbose(tcp) ||
|
||||
(exiting(tcp) && syserror(tcp))) {
|
||||
tprintf("%#lx", addr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_LINUX_CAPABILITY_VERSION_2 == h->version ||
|
||||
_LINUX_CAPABILITY_VERSION_3 == h->version)
|
||||
len = 2;
|
||||
else
|
||||
len = 1;
|
||||
|
||||
if (umoven(tcp, addr, len * sizeof(data[0]), (char *) data) < 0) {
|
||||
tprintf("%#lx", addr);
|
||||
return;
|
||||
}
|
||||
|
||||
tprints("{");
|
||||
print_cap_bits(data[0].effective, len > 1 ? data[1].effective : 0);
|
||||
tprints(", ");
|
||||
print_cap_bits(data[0].permitted, len > 1 ? data[1].permitted : 0);
|
||||
tprints(", ");
|
||||
print_cap_bits(data[0].inheritable, len > 1 ? data[1].inheritable : 0);
|
||||
tprints("}");
|
||||
}
|
||||
|
||||
int
|
||||
sys_capget(struct tcb *tcp)
|
||||
{
|
||||
cap_user_header_t h;
|
||||
|
||||
if (entering(tcp)) {
|
||||
print_cap_header(tcp, tcp->u_arg[0]);
|
||||
h = get_cap_header(tcp, tcp->u_arg[0]);
|
||||
print_cap_header(tcp, tcp->u_arg[0], h);
|
||||
tprints(", ");
|
||||
} else {
|
||||
print_cap_data(tcp, tcp->u_arg[1]);
|
||||
h = syserror(tcp) ? NULL : get_cap_header(tcp, tcp->u_arg[0]);
|
||||
print_cap_data(tcp, tcp->u_arg[1], h);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -116,9 +173,10 @@ int
|
||||
sys_capset(struct tcb *tcp)
|
||||
{
|
||||
if (entering(tcp)) {
|
||||
print_cap_header(tcp, tcp->u_arg[0]);
|
||||
cap_user_header_t h = get_cap_header(tcp, tcp->u_arg[0]);
|
||||
print_cap_header(tcp, tcp->u_arg[0], h);
|
||||
tprints(", ");
|
||||
print_cap_data(tcp, tcp->u_arg[1]);
|
||||
print_cap_data(tcp, tcp->u_arg[1], h);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ AM_CFLAGS = $(WARN_CFLAGS)
|
||||
|
||||
check_PROGRAMS = \
|
||||
inet-accept-connect-send-recv \
|
||||
caps \
|
||||
mmsg \
|
||||
net-accept-connect \
|
||||
netlink_inet_diag \
|
||||
@ -23,6 +24,7 @@ TESTS = \
|
||||
ptrace_setoptions.test \
|
||||
strace-f.test \
|
||||
qual_syscall.test \
|
||||
caps.test \
|
||||
getdents.test \
|
||||
scm_rights-fd.test \
|
||||
sigaction.test \
|
||||
@ -44,6 +46,7 @@ net-fd.log: net.log
|
||||
TEST_LOG_COMPILER = $(srcdir)/run.sh
|
||||
|
||||
EXTRA_DIST = init.sh run.sh \
|
||||
caps.awk \
|
||||
getdents.awk \
|
||||
mmsg.expected \
|
||||
net-yy-accept.awk \
|
||||
|
25
tests/caps.awk
Normal file
25
tests/caps.awk
Normal file
@ -0,0 +1,25 @@
|
||||
BEGIN {
|
||||
fail = 0
|
||||
lines = 3
|
||||
cap = "(0|CAP_[A-Z_]+(\\|CAP_[A-Z_]+)*)"
|
||||
capget = "^capget\\({_LINUX_CAPABILITY_VERSION_3, 0}, {" cap ", " cap ", " cap "}\\) = 0$"
|
||||
}
|
||||
|
||||
NR == 1 {if (match($0, capget)) next}
|
||||
|
||||
NR == 2 && $0 == "capset({_LINUX_CAPABILITY_VERSION_3, 0}, {CAP_DAC_OVERRIDE|CAP_WAKE_ALARM, CAP_DAC_READ_SEARCH|CAP_BLOCK_SUSPEND, 0}) = -1 EPERM (Operation not permitted)" {next}
|
||||
|
||||
NR == lines && $0 == "+++ exited with 0 +++" {next}
|
||||
|
||||
{
|
||||
print "Line " NR " does not match."
|
||||
fail = 1
|
||||
exit 1
|
||||
}
|
||||
|
||||
END {
|
||||
if (fail == 0 && NR != lines) {
|
||||
print "Expected " lines " lines, found " NR " line(s)."
|
||||
exit 1
|
||||
}
|
||||
}
|
19
tests/caps.c
Normal file
19
tests/caps.c
Normal file
@ -0,0 +1,19 @@
|
||||
#include <errno.h>
|
||||
|
||||
extern int capget(int *, int *);
|
||||
extern int capset(int *, const int *);
|
||||
|
||||
int
|
||||
main(void)
|
||||
{
|
||||
int unused[6];
|
||||
const int data[] = { 2, 4, 0, 8, 16, 0 };
|
||||
const int v3 = 0x20080522;
|
||||
int head[] = { v3, 0 };
|
||||
|
||||
if (capget(head, unused) || head[0] != v3 ||
|
||||
capset(head, data) == 0 || errno != EPERM)
|
||||
return 77;
|
||||
|
||||
return 0;
|
||||
}
|
26
tests/caps.test
Executable file
26
tests/caps.test
Executable file
@ -0,0 +1,26 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Check capget/capset syscalls decoding.
|
||||
|
||||
. "${srcdir=.}/init.sh"
|
||||
|
||||
check_prog awk
|
||||
|
||||
./caps || {
|
||||
if [ $? -eq 77 ]; then
|
||||
framework_skip_ 'capget/capset syscalls do not behave as expected'
|
||||
else
|
||||
fail_ 'caps failed'
|
||||
fi
|
||||
}
|
||||
|
||||
args="-e trace=capget,capset ./caps"
|
||||
$STRACE -o "$LOG" $args || {
|
||||
cat "$LOG"
|
||||
fail_ "$STRACE $args failed"
|
||||
}
|
||||
|
||||
awk -f "$srcdir"/caps.awk "$LOG" ||
|
||||
{ cat "$LOG"; fail_ 'unexpected output'; }
|
||||
|
||||
exit 0
|
7
xlat/capabilities1.in
Normal file
7
xlat/capabilities1.in
Normal file
@ -0,0 +1,7 @@
|
||||
#unconditional
|
||||
1<<CAP_MAC_OVERRIDE
|
||||
1<<CAP_MAC_ADMIN
|
||||
1<<CAP_SYSLOG
|
||||
1<<CAP_WAKE_ALARM
|
||||
1<<CAP_BLOCK_SUSPEND
|
||||
1<<CAP_AUDIT_READ
|
Loading…
x
Reference in New Issue
Block a user