From 2822da4fb7f891e5320f02f1d00f64b72221ced4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 10 Dec 2014 03:16:14 +0100 Subject: util: introduce our own gperf based capability list This way, we can ensure we have a more complete, up-to-date list of capabilities around, always. --- .gitignore | 1 + Makefile.am | 34 ++++++++++++++++++++-- src/core/execute.c | 10 ++----- src/core/load-fragment.c | 11 ++++--- src/libsystemd/sd-bus/bus-dump.c | 5 ++-- src/nspawn/nspawn.c | 7 +++-- src/shared/.gitignore | 4 +++ src/shared/cap-list.c | 62 ++++++++++++++++++++++++++++++++++++++++ src/shared/cap-list.h | 25 ++++++++++++++++ src/shared/condition.c | 7 +++-- src/shared/missing.h | 25 ++++++++++++++++ src/test/test-cap-list.c | 47 ++++++++++++++++++++++++++++++ 12 files changed, 215 insertions(+), 23 deletions(-) create mode 100644 src/shared/cap-list.c create mode 100644 src/shared/cap-list.h create mode 100644 src/test/test-cap-list.c diff --git a/.gitignore b/.gitignore index 06d411a933..b0fc12fac6 100644 --- a/.gitignore +++ b/.gitignore @@ -152,6 +152,7 @@ /test-bus-zero-copy /test-calendarspec /test-capability +/test-cap-list /test-catalog /test-cgroup /test-cgroup-mask diff --git a/Makefile.am b/Makefile.am index 2af4a32aa2..23210ff33e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -863,6 +863,8 @@ libsystemd_shared_la_SOURCES = \ src/shared/af-list.h \ src/shared/arphrd-list.c \ src/shared/arphrd-list.h \ + src/shared/cap-list.c \ + src/shared/cap-list.h \ src/shared/audit.c \ src/shared/audit.h \ src/shared/xml.c \ @@ -903,7 +905,9 @@ nodist_libsystemd_shared_la_SOURCES = \ src/shared/af-from-name.h \ src/shared/af-to-name.h \ src/shared/arphrd-from-name.h \ - src/shared/arphrd-to-name.h + src/shared/arphrd-to-name.h \ + src/shared/cap-from-name.h \ + src/shared/cap-to-name.h libsystemd_shared_la_CFLAGS = \ $(AM_CFLAGS) \ @@ -1191,6 +1195,8 @@ CLEANFILES += \ src/shared/af-from-name.gperf \ src/shared/arphrd-list.txt \ src/shared/arphrd-from-name.gperf \ + src/shared/cap-list.txt \ + src/shared/cap-from-name.gperf \ src/resolve/dns_type-list.txt \ src/resolve/dns_type-from-name.gperf @@ -1201,6 +1207,8 @@ BUILT_SOURCES += \ src/shared/af-to-name.h \ src/shared/arphrd-from-name.h \ src/shared/arphrd-to-name.h \ + src/shared/cap-from-name.h \ + src/shared/cap-to-name.h \ src/resolve/dns_type-from-name.h \ src/resolve/dns_type-to-name.h @@ -1226,6 +1234,7 @@ src/shared/af-list.txt: src/shared/af-to-name.h: src/shared/af-list.txt $(AM_V_GEN)$(AWK) 'BEGIN{ print "static const char* const af_names[] = { "} !/AF_FILE/ && !/AF_ROUTE/ && !/AF_LOCAL/ { printf "[%s] = \"%s\",\n", $$1, $$1 } END{print "};"}' <$< >$@ + src/shared/arphrd-list.txt: $(AM_V_at)$(MKDIR_P) $(dir $@) $(AM_V_GEN)$(CPP) $(CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) -dM -include net/if_arp.h - $@ @@ -1237,6 +1246,20 @@ src/shared/arphrd-from-name.gperf: src/shared/arphrd-list.txt $(AM_V_GEN)$(AWK) 'BEGIN{ print "struct arphrd_name { const char* name; int id; };"; print "%null-strings"; print "%%";} { printf "%s, ARPHRD_%s\n", $$1, $$1 }' <$< >$@ +src/shared/cap-list.txt: + $(AM_V_at)$(MKDIR_P) $(dir $@) + $(AM_V_GEN)$(CPP) $(CFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) -dM -include linux/capability.h -include missing.h - $@ + +src/shared/cap-to-name.h: src/shared/cap-list.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "static const char* const capability_names[] = { "} { printf "[%s] = \"%s\",\n", $$1, $$1 } END{print "};"}' <$< >$@ + +src/shared/cap-from-name.gperf: src/shared/cap-list.txt + $(AM_V_GEN)$(AWK) 'BEGIN{ print "struct capability_name { const char* name; int id; };"; print "%null-strings"; print "%%";} { printf "%s, %s\n", $$1, $$1 }' <$< >$@ + +src/shared/cap-from-name.h: src/shared/cap-from-name.gperf + $(AM_V_GPERF)$(GPERF) -L ANSI-C -t --ignore-case -N lookup_capability -H hash_capability_name -p -C <$< >$@ + + src/resolve/dns_type-list.txt: src/resolve/dns-type.h $(AM_V_at)$(MKDIR_P) $(dir $@) $(AM_V_GEN)$(SED) -n -r 's/.* DNS_TYPE_(\w+).*/\1/p' <$< >$@ @@ -1353,7 +1376,8 @@ tests += \ test-bus-policy \ test-locale-util \ test-execute \ - test-copy + test-copy \ + test-cap-list EXTRA_DIST += \ test/a.service \ @@ -1579,6 +1603,12 @@ test_uid_range_SOURCES = \ test_uid_range_LDADD = \ libsystemd-shared.la +test_cap_list_SOURCES = \ + src/test/test-cap-list.c + +test_cap_list_LDADD = \ + libsystemd-shared.la + test_socket_util_SOURCES = \ src/test/test-socket-util.c diff --git a/src/core/execute.c b/src/core/execute.c index b7ac4c7b2b..955090c446 100644 --- a/src/core/execute.c +++ b/src/core/execute.c @@ -86,6 +86,7 @@ #include "smack-util.h" #include "bus-kernel.h" #include "label.h" +#include "cap-list.h" #ifdef HAVE_SECCOMP #include "seccomp-util.h" @@ -2296,13 +2297,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) { fprintf(f, "%sCapabilityBoundingSet:", prefix); for (l = 0; l <= cap_last_cap(); l++) - if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l))) { - _cleanup_cap_free_charp_ char *t; - - t = cap_to_name(l); - if (t) - fprintf(f, " %s", t); - } + if (!(c->capability_bounding_set_drop & ((uint64_t) 1ULL << (uint64_t) l))) + fprintf(f, " %s", strna(capability_to_name(l))); fputs("\n", f); } diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 7f109b8930..259323bd5c 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -59,6 +59,7 @@ #include "bus-error.h" #include "errno-list.h" #include "af-list.h" +#include "cap-list.h" #ifdef HAVE_SECCOMP #include "seccomp-util.h" @@ -1040,17 +1041,15 @@ int config_parse_bounding_set(const char *unit, FOREACH_WORD_QUOTED(word, l, rvalue, state) { _cleanup_free_ char *t = NULL; - int r; - cap_value_t cap; + int cap; t = strndup(word, l); if (!t) return log_oom(); - r = cap_from_name(t, &cap); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, errno, - "Failed to parse capability in bounding set, ignoring: %s", t); + cap = capability_from_name(t); + if (cap < 0) { + log_syntax(unit, LOG_ERR, filename, line, errno, "Failed to parse capability in bounding set, ignoring: %s", t); continue; } diff --git a/src/libsystemd/sd-bus/bus-dump.c b/src/libsystemd/sd-bus/bus-dump.c index 9d2aaa8293..33d0ed2df6 100644 --- a/src/libsystemd/sd-bus/bus-dump.c +++ b/src/libsystemd/sd-bus/bus-dump.c @@ -24,6 +24,7 @@ #include "strv.h" #include "audit.h" #include "macro.h" +#include "cap-list.h" #include "bus-message.h" #include "bus-internal.h" @@ -290,15 +291,13 @@ static void dump_capabilities( for (;;) { if (r > 0) { - _cleanup_cap_free_charp_ char *t; if (n > 0) fputc(' ', f); if (n % 4 == 3) fprintf(f, terse ? "\n " : "\n "); - t = cap_to_name(i); - fprintf(f, "%s", t); + fprintf(f, "%s", strna(capability_to_name(i))); n++; } diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c index 932696aa9e..0466ddbff3 100644 --- a/src/nspawn/nspawn.c +++ b/src/nspawn/nspawn.c @@ -90,6 +90,7 @@ #include "base-filesystem.h" #include "barrier.h" #include "event-util.h" +#include "cap-list.h" #ifdef HAVE_SECCOMP #include "seccomp-util.h" @@ -401,7 +402,6 @@ static int parse_argv(int argc, char *argv[]) { FOREACH_WORD_SEPARATOR(word, length, optarg, ",", state) { _cleanup_free_ char *t; - cap_value_t cap; t = strndup(word, length); if (!t) @@ -413,7 +413,10 @@ static int parse_argv(int argc, char *argv[]) { else minus = (uint64_t) -1; } else { - if (cap_from_name(t, &cap) < 0) { + int cap; + + cap = capability_from_name(t); + if (cap < 0) { log_error("Failed to parse capability %s.", t); return -EINVAL; } diff --git a/src/shared/.gitignore b/src/shared/.gitignore index 61709e8da1..e22411e484 100644 --- a/src/shared/.gitignore +++ b/src/shared/.gitignore @@ -1,3 +1,7 @@ +/cap-from-name.gperf +/cap-from-name.h +/cap-list.txt +/cap-to-name.h /errno-from-name.gperf /errno-from-name.h /errno-list.txt diff --git a/src/shared/cap-list.c b/src/shared/cap-list.c new file mode 100644 index 0000000000..56d1488f48 --- /dev/null +++ b/src/shared/cap-list.c @@ -0,0 +1,62 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include +#include + +#include "util.h" +#include "cap-list.h" +#include "missing.h" + +static const struct capability_name* lookup_capability(register const char *str, register unsigned int len); + +#include "cap-to-name.h" +#include "cap-from-name.h" + +const char *capability_to_name(int id) { + + if (id < 0) + return NULL; + + if (id >= (int) ELEMENTSOF(capability_names)) + return NULL; + + return capability_names[id]; +} + +int capability_from_name(const char *name) { + const struct capability_name *sc; + int r, i; + + assert(name); + + /* Try to parse numeric capability */ + r = safe_atoi(name, &i); + if (r >= 0 && i >= 0) + return i; + + /* Try to parse string capability */ + sc = lookup_capability(name, strlen(name)); + if (!sc) + return -EINVAL; + + return sc->id; +} diff --git a/src/shared/cap-list.h b/src/shared/cap-list.h new file mode 100644 index 0000000000..c699e466a7 --- /dev/null +++ b/src/shared/cap-list.h @@ -0,0 +1,25 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +#pragma once + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +const char *capability_to_name(int id); +int capability_from_name(const char *name); diff --git a/src/shared/condition.c b/src/shared/condition.c index 59f262244b..dcbf9a7e86 100644 --- a/src/shared/condition.c +++ b/src/shared/condition.c @@ -39,6 +39,7 @@ #include "selinux-util.h" #include "audit.h" #include "condition.h" +#include "cap-list.h" Condition* condition_new(ConditionType type, const char *parameter, bool trigger, bool negate) { Condition *c; @@ -235,7 +236,7 @@ static int condition_test_security(Condition *c) { static int condition_test_capability(Condition *c) { _cleanup_fclose_ FILE *f = NULL; - cap_value_t value; + int value; char line[LINE_MAX]; unsigned long long capabilities = -1; @@ -244,8 +245,8 @@ static int condition_test_capability(Condition *c) { assert(c->type == CONDITION_CAPABILITY); /* If it's an invalid capability, we don't have it */ - - if (cap_from_name(c->parameter, &value) < 0) + value = capability_from_name(c->parameter); + if (value < 0) return -EINVAL; /* If it's a valid capability we default to assume diff --git a/src/shared/missing.h b/src/shared/missing.h index cf7387751c..478988c8a4 100644 --- a/src/shared/missing.h +++ b/src/shared/missing.h @@ -34,6 +34,7 @@ #include #include #include +#include #ifdef HAVE_AUDIT #include @@ -606,3 +607,27 @@ static inline int setns(int fd, int nstype) { #ifndef AUDIT_NLGRP_MAX #define AUDIT_NLGRP_READLOG 1 #endif + +#ifndef CAP_MAC_OVERRIDE +#define CAP_MAC_OVERRIDE 32 +#endif + +#ifndef CAP_MAC_ADMIN +#define CAP_MAC_ADMIN 33 +#endif + +#ifndef CAP_SYSLOG +#define CAP_SYSLOG 34 +#endif + +#ifndef CAP_WAKE_ALARM +#define CAP_WAKE_ALARM 35 +#endif + +#ifndef CAP_BLOCK_SUSPEND +#define CAP_BLOCK_SUSPEND 36 +#endif + +#ifndef CAP_AUDIT_READ +#define CAP_AUDIT_READ 37 +#endif diff --git a/src/test/test-cap-list.c b/src/test/test-cap-list.c new file mode 100644 index 0000000000..dfa9a063c2 --- /dev/null +++ b/src/test/test-cap-list.c @@ -0,0 +1,47 @@ +/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/ + +/*** + This file is part of systemd. + + Copyright 2014 Lennart Poettering + + systemd is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation; either version 2.1 of the License, or + (at your option) any later version. + + systemd 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with systemd; If not, see . +***/ + +#include "log.h" +#include "cap-list.h" +#include "capability.h" + +int main(int argc, char *argv[]) { + int i; + + assert_se(!capability_to_name(-1)); + assert_se(!capability_to_name(cap_last_cap()+1)); + + for (i = 0; i <= (int) cap_last_cap(); i++) { + const char *n; + + assert_se(n = capability_to_name(i)); + assert_se(capability_from_name(n) == i); + printf("%s = %i\n", n, i); + } + + assert_se(capability_from_name("asdfbsd") == -EINVAL); + assert_se(capability_from_name("CAP_AUDIT_READ") == CAP_AUDIT_READ); + assert_se(capability_from_name("0") == 0); + assert_se(capability_from_name("15") == 15); + assert_se(capability_from_name("-1") == -EINVAL); + + return 0; +} -- cgit v1.2.3-54-g00ecf