Skip to content

Commit

Permalink
Implement full decoding of 64-bit capabilities
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ldv-alt committed Dec 6, 2014
1 parent bf7fdfa commit 4b9c68b
Show file tree
Hide file tree
Showing 6 changed files with 171 additions and 33 deletions.
124 changes: 91 additions & 33 deletions 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,
Expand Down Expand Up @@ -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,
Expand All @@ -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 || !verbose(tcp))
return NULL;

if (!addr)
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)
return;
}

if (!h || !verbose(tcp) ||
(exiting(tcp) && syserror(tcp))) {
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 (_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;
}
Expand All @@ -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;
}
3 changes: 3 additions & 0 deletions tests/Makefile.am
Expand Up @@ -4,6 +4,7 @@ AM_CFLAGS = $(WARN_CFLAGS)

check_PROGRAMS = \
inet-accept-connect-send-recv \
caps \
mmsg \
net-accept-connect \
netlink_inet_diag \
Expand All @@ -23,6 +24,7 @@ TESTS = \
ptrace_setoptions.test \
strace-f.test \
qual_syscall.test \
caps.test \
getdents.test \
scm_rights-fd.test \
sigaction.test \
Expand All @@ -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 \
Expand Down
25 changes: 25 additions & 0 deletions tests/caps.awk
@@ -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 changes: 19 additions & 0 deletions tests/caps.c
@@ -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 changes: 26 additions & 0 deletions tests/caps.test
@@ -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 changes: 7 additions & 0 deletions xlat/capabilities1.in
@@ -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

0 comments on commit 4b9c68b

Please sign in to comment.