diff --git a/parser/capability.h b/parser/capability.h new file mode 100644 index 000000000..7cb5a2f89 --- /dev/null +++ b/parser/capability.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2020 + * Canonical Ltd. (All rights reserved) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License published by the Free Software Foundation. + * + * 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, contact Novell, Inc. or Canonical + * Ltd. + */ + +#ifndef __AA_CAPABILITY_H +#define __AA_CAPABILITY_H + +#define NO_BACKMAP_CAP 0xff + +#ifndef CAP_PERFMON +#define CAP_PERFMON 38 +#endif + +#ifndef CAP_BPF +#define CAP_BPF 39 +#endif + +typedef enum capability_flags { + CAPFLAGS_CLEAR = 0, + CAPFLAG_BASE_FEATURE = 1, + CAPFLAG_KERNEL_FEATURE = 2, + CAPFLAG_POLICY_FEATURE = 4, + CAPFLAG_EXTERNAL_FEATURE = 8, +} capability_flags; + +int name_to_capability(const char *keyword); +void capabilities_init(void); +void __debug_capabilities(uint64_t capset, const char *name); +bool add_cap_feature_mask(struct aa_features *features, capability_flags flags); +void clear_cap_flag(capability_flags flags); + + +#endif /* __AA_CAPABILITY_H */ diff --git a/parser/parser.h b/parser/parser.h index 377bc9514..ef1ca032f 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -397,8 +397,6 @@ extern char *processid(const char *string, int len); extern char *processquoted(const char *string, int len); extern char *processunquoted(const char *string, int len); extern int get_keyword_token(const char *keyword); -extern int name_to_capability(const char *keyword); -extern void capabilities_init(void); extern int get_rlimit(const char *name); extern char *process_var(const char *var); extern int parse_mode(const char *mode); @@ -412,7 +410,6 @@ extern struct cod_entry *new_entry(char *id, int mode, char *link_id); extern int str_to_boolean(const char* str); extern struct cod_entry *copy_cod_entry(struct cod_entry *cod); extern void free_cod_entries(struct cod_entry *list); -extern void __debug_capabilities(uint64_t capset, const char *name); void debug_cod_entries(struct cod_entry *list); #define SECONDS_P_MS (1000LL * 1000LL) diff --git a/parser/parser_main.c b/parser/parser_main.c index 1d7bdb8fa..d807bab25 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -41,7 +41,7 @@ #include - +#include "capability.h" #include "lib.h" #include "features.h" #include "parser.h" @@ -923,6 +923,7 @@ void reset_parser(const char *filename) if (!specified_policy_features) { aa_features_unref(policy_features); policy_features = NULL; + clear_cap_flag(CAPFLAG_POLICY_FEATURE); } } @@ -1356,6 +1357,10 @@ int main(int argc, char *argv[]) PERROR(_("Kernel features abi not found")); return 1; } + if (!add_cap_feature_mask(kernel_features, CAPFLAG_KERNEL_FEATURE)) { + PERROR(_("Failed to add kernel capabilities to known capabilities set")); + return 1; + } if (!(UNPRIVILEGED_OPS) && aa_kernel_interface_new(&kernel_interface, kernel_features, apparmorfs) == -1) { diff --git a/parser/parser_misc.c b/parser/parser_misc.c index 16779a882..39cb1aae8 100644 --- a/parser/parser_misc.c +++ b/parser/parser_misc.c @@ -34,6 +34,7 @@ #include #include +#include "capability.h" #include "lib.h" #include "parser.h" #include "profile.h" @@ -177,27 +178,9 @@ int get_rlimit(const char *name) } -#define NO_BACKMAP_CAP 0xff - -#ifndef CAP_PERFMON -#define CAP_PERFMON 38 -#endif - -#ifndef CAP_BPF -#define CAP_BPF 39 -#endif - -typedef enum capability_flags { - CAPFLAGS_CLEAR = 0, - CAPFLAG_BASE_FEATURE = 1, - CAPFLAG_KERNEL_FEATURE = 2, - CAPFLAG_POLICY_FEATURE = 4, - CAPFLAG_EXTERNAL_FEATURE = 8, -} capability_flags; - struct capability_table { - const char *cap; - unsigned int token; + const char *name; + unsigned int cap; unsigned int backmap; capability_flags flags; }; @@ -222,35 +205,149 @@ void capabilities_init(void) cap_table_size = sizeof(base_capability_table)/sizeof(struct capability_table); } -static int get_cap_token(const char *name unused, struct capability_table *table, - const char *cap) +struct capability_table *find_cap_entry_by_name(const char *name) { int i; - for (i = 0; table[i].cap; i++) { - PDEBUG("Checking %s %s\n", name, table[i].cap); - if (strcmp(cap, table[i].cap) == 0) { - PDEBUG("Found %s %s\n", name, table[i].cap); - return table[i].token; + for (i = 0; cap_table[i].name; i++) { + PDEBUG("Checking %s %s\n", name, cap_table[i].name); + if (strcmp(name, cap_table[i].name) == 0) { + PDEBUG("Found %s %s\n", name, cap_table[i].name); + return &cap_table[i]; } } - PDEBUG("Unable to find %s %s\n", name, cap); - return -1; + return NULL; } -int name_to_capability(const char *keyword) +/* don't mark up str with \0 */ +static const char *strn_token(const char *str, size_t &len) { - return get_cap_token("capability", cap_table, keyword); + const char *start; + + while (isspace(*str)) + str++; + start = str; + while (*str && !isspace(*str)) + str++; + if (start == str) + return NULL; + + len = str - start; + return start; +} + +/* + * Returns: -1: error + * 0: no change - capability already in table + * 1: added flag to capability in table + * 2: added new capability + */ +static int capable_add_cap(const char *str, int len, unsigned int cap, + capability_flags flag) +{ + /* extract name from str so we can treat as a string */ + autofree char *name = strndup(str, len); + + if (!name) { + yyerror(_("Out of memory")); + return -1; + } + struct capability_table *ent = find_cap_entry_by_name(name); + if (ent) { + if (ent->cap != cap) { + pwarn("feature capability '%s:%d' does not equal expected %d. Ignoring ...\n", name, cap, ent->cap); + /* TODO: make warn to error config */ + return 0; + } + if (ent->flags & flag) + return 0; /* no change */ + ent->flags = (capability_flags) (ent->flags | flag); + return 1; /* modified */ + } else { + struct capability_table *tmp; + + tmp = (struct capability_table *) reallocarray(cap_table, sizeof(struct capability_table), cap_table_size+1); + if (!tmp) { + yyerror(_("Out of memory")); + /* TODO: change away from yyerror */ + return -1; + } + cap_table = tmp; + ent = &cap_table[cap_table_size - 1]; /* overwrite null */ + ent->name = strndup(name, len); + if (!ent->name) { + /* TODO: change away from yyerror */ + yyerror(_("Out of memory")); + return -1; + } + ent->cap = cap; + ent->flags = flag; + cap_table[cap_table_size].name = NULL; /* new null */ + cap_table_size++; + } + + return 2; /* added */ +} + +bool add_cap_feature_mask(struct aa_features *features, capability_flags flags) +{ + autofree char *value = NULL; + const char *capstr; + size_t valuelen, len = 0; + int n; + + value = aa_features_value(features, "caps/mask", &valuelen); + if (!value) + return false; + + n = 0; + for (capstr = strn_token(value, len); + capstr; + capstr = strn_token(capstr + len, len)) { + if (capable_add_cap(capstr, len, n, flags) < 0) + return false; + n++; + if (len > valuelen) { + PDEBUG("caplen is > remaining feature string"); + return false; + } + valuelen -= len; + PDEBUG("Adding %d capabilities\n", n); + } + + return true; +} + +void clear_cap_flag(capability_flags flags) +{ + int i; + + for (i = 0; cap_table[i].name; i++) { + PDEBUG("Checking %s %s\n", name, cap_table[i].name); + cap_table[i].flags = (capability_flags) (cap_table[i].flags & ~flags); + } +} + +int name_to_capability(const char *cap) +{ + struct capability_table *ent; + + ent = find_cap_entry_by_name(cap); + if (ent) + return ent->cap; + + PDEBUG("Unable to find %s %s\n", "capability", cap); + return -1; } const char *capability_to_name(unsigned int cap) { int i; - for (i = 0; cap_table[i].cap; i++) { - if (cap_table[i].token == cap) - return cap_table[i].cap; + for (i = 0; cap_table[i].name; i++) { + if (cap_table[i].cap == cap) + return cap_table[i].name; } return "invalid-capability"; @@ -262,9 +359,9 @@ void __debug_capabilities(uint64_t capset, const char *name) printf("%s:", name); - for (i = 0; cap_table[i].cap; i++) { - if ((1ull << cap_table[i].token) & capset) - printf (" %s", cap_table[i].cap); + for (i = 0; cap_table[i].name; i++) { + if ((1ull << cap_table[i].cap) & capset) + printf (" %s", cap_table[i].name); } printf("\n"); } diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y index cee31a0cf..d4904e1b2 100644 --- a/parser/parser_yacc.y +++ b/parser/parser_yacc.y @@ -32,6 +32,7 @@ /* #define DEBUG */ +#include "capability.h" #include "lib.h" #include "parser.h" #include "profile.h" @@ -299,6 +300,9 @@ list: preamble } pwarn(_("%s: File '%s' missing feature abi, falling back to default policy feature abi\n"), progname, current_filename); } + if (!add_cap_feature_mask(policy_features, + CAPFLAG_POLICY_FEATURE)) + yyerror(_("Failed to add policy capabilities to known capabilities set")); set_supported_features(); } diff --git a/parser/profile.h b/parser/profile.h index cafa66cf0..f54467c07 100644 --- a/parser/profile.h +++ b/parser/profile.h @@ -18,6 +18,7 @@ #include #include +#include "capability.h" #include "parser.h" #include "rule.h" #include "libapparmor_re/aare_rules.h"