From a066f80372e184f610d5dff4fcb038f561f255d9 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Mon, 7 Apr 2014 03:16:50 -0700 Subject: [PATCH] Convert mount and dbus to be subclasses of a generic rule class This will simplify add new features as most of the code can reside in its own class. There are still things to improve but its a start. Signed-off-by: John Johansen Acked-by: Steve Beattie --- parser/Makefile | 13 +- parser/dbus.c | 399 ++++++++++++++++++++------- parser/dbus.h | 32 ++- parser/mount.c | 575 ++++++++++++++++++++++++++++++--------- parser/mount.h | 32 ++- parser/parser.h | 22 +- parser/parser_misc.c | 101 +------ parser/parser_policy.c | 29 +- parser/parser_regex.c | 491 +-------------------------------- parser/parser_variable.c | 66 +---- parser/parser_yacc.y | 51 ++-- parser/profile.cc | 6 +- parser/profile.h | 7 +- 13 files changed, 875 insertions(+), 949 deletions(-) diff --git a/parser/Makefile b/parser/Makefile index df7c989c3..5088806e1 100644 --- a/parser/Makefile +++ b/parser/Makefile @@ -79,8 +79,8 @@ EXTRA_CFLAGS+=-DSUBDOMAIN_CONFDIR=\"${CONFDIR}\" SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \ parser_main.c parser_misc.c parser_merge.c parser_symtab.c \ parser_yacc.c parser_regex.c parser_variable.c parser_policy.c \ - parser_alias.c mount.c dbus.c lib.c profile.cc -HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h + parser_alias.c mount.c dbus.c lib.c profile.cc rule.c +HDRS = parser.h parser_include.h immunix.h mount.h dbus.h lib.h profile.h rule.h TOOLS = apparmor_parser OBJECTS = $(SRCS:.c=.o) @@ -188,7 +188,7 @@ apparmor_parser: $(OBJECTS) $(AAREOBJECTS) $(LIBAPPARMOR_A) parser_yacc.c parser_yacc.h: parser_yacc.y parser.h profile.h $(YACC) $(YFLAGS) -o parser_yacc.c parser_yacc.y -parser_lex.c: parser_lex.l parser_yacc.h parser.h profile.h +parser_lex.c: parser_lex.l parser_yacc.h parser.h profile.h mount.h dbus.h $(LEX) ${LEXFLAGS} -o$@ $< parser_lex.o: parser_lex.c parser.h parser_yacc.h @@ -230,18 +230,21 @@ parser_alias.o: parser_alias.c parser.h profile.h parser_common.o: parser_common.c parser.h $(CXX) $(EXTRA_CFLAGS) -c -o $@ $< -mount.o: mount.c mount.h parser.h immunix.h +mount.o: mount.c mount.h parser.h immunix.h rule.h $(CXX) $(EXTRA_CFLAGS) -c -o $@ $< lib.o: lib.c lib.h parser.h $(CXX) $(EXTRA_CFLAGS) -c -o $@ $< -dbus.o: dbus.c dbus.h parser.h immunix.h parser_yacc.h $(APPARMOR_H) +dbus.o: dbus.c dbus.h parser.h immunix.h parser_yacc.h rule.h $(APPARMOR_H) $(CXX) $(EXTRA_CFLAGS) -c -o $@ $< profile.o: profile.cc profile.h parser.h $(CXX) $(EXTRA_CFLAGS) -c -o $@ $< +rule.o: rule.c rule.h policydb.h + $(CXX) $(EXTRA_CFLAGS) -c -o $@ $< + parser_version.h: Makefile @echo \#define PARSER_VERSION \"$(VERSION)\" > .ver @mv -f .ver $@ diff --git a/parser/dbus.c b/parser/dbus.c index 1e1b4c42c..ba20ea043 100644 --- a/parser/dbus.c +++ b/parser/dbus.c @@ -20,23 +20,91 @@ #include #include +#include +#include +#include +#include + #include "parser.h" #include "profile.h" #include "parser_yacc.h" #include "dbus.h" -void free_dbus_entry(struct dbus_entry *ent) -{ - if (!ent) - return; - free(ent->bus); - free(ent->name); - free(ent->peer_label); - free(ent->path); - free(ent->interface); - free(ent->member); +#define _(s) gettext(s) - free(ent); +static int parse_dbus_sub_mode(const char *str_mode, int *result, int fail, const char *mode_desc __unused) +{ + int mode = 0; + const char *p; + + PDEBUG("Parsing DBus mode: %s\n", str_mode); + + if (!str_mode) + return 0; + + p = str_mode; + while (*p) { + char current = *p; + char lower; + +reeval: + switch (current) { + case COD_READ_CHAR: + PDEBUG("Parsing DBus mode: found %s READ\n", mode_desc); + mode |= AA_DBUS_RECEIVE; + break; + + case COD_WRITE_CHAR: + PDEBUG("Parsing DBus mode: found %s WRITE\n", + mode_desc); + mode |= AA_DBUS_SEND; + break; + + /* error cases */ + + default: + lower = tolower(current); + switch (lower) { + case COD_READ_CHAR: + case COD_WRITE_CHAR: + PDEBUG("Parsing DBus mode: found invalid upper case char %c\n", + current); + warn_uppercase(); + current = lower; + goto reeval; + break; + default: + if (fail) + yyerror(_("Internal: unexpected DBus mode character '%c' in input"), + current); + else + return 0; + break; + } + break; + } + p++; + } + + PDEBUG("Parsed DBus mode: %s 0x%x\n", str_mode, mode); + + *result = mode; + return 1; +} + +int parse_dbus_mode(const char *str_mode, int *mode, int fail) +{ + *mode = 0; + if (!parse_dbus_sub_mode(str_mode, mode, fail, "")) + return 0; + if (*mode & ~AA_VALID_DBUS_PERMS) { + if (fail) + yyerror(_("Internal error generated invalid DBus perm 0x%x\n"), + mode); + else + return 0; + } + return 1; } static int list_len(struct value_list *v) @@ -60,7 +128,7 @@ static void move_conditional_value(char **dst_ptr, struct cond_entry *cond_ent) cond_ent->vals->value = NULL; } -static void move_conditionals(struct dbus_entry *ent, struct cond_entry *conds) +void dbus_rule::move_conditionals(struct cond_entry *conds) { struct cond_entry *cond_ent; @@ -73,17 +141,17 @@ static void move_conditionals(struct dbus_entry *ent, struct cond_entry *conds) cond_ent->name); if (strcmp(cond_ent->name, "bus") == 0) { - move_conditional_value(&ent->bus, cond_ent); + move_conditional_value(&bus, cond_ent); } else if (strcmp(cond_ent->name, "name") == 0) { - move_conditional_value(&ent->name, cond_ent); + move_conditional_value(&name, cond_ent); } else if (strcmp(cond_ent->name, "label") == 0) { - move_conditional_value(&ent->peer_label, cond_ent); + move_conditional_value(&peer_label, cond_ent); } else if (strcmp(cond_ent->name, "path") == 0) { - move_conditional_value(&ent->path, cond_ent); + move_conditional_value(&path, cond_ent); } else if (strcmp(cond_ent->name, "interface") == 0) { - move_conditional_value(&ent->interface, cond_ent); + move_conditional_value(&interface, cond_ent); } else if (strcmp(cond_ent->name, "member") == 0) { - move_conditional_value(&ent->member, cond_ent); + move_conditional_value(&member, cond_ent); } else { yyerror("invalid dbus conditional \"%s\"\n", cond_ent->name); @@ -91,129 +159,252 @@ static void move_conditionals(struct dbus_entry *ent, struct cond_entry *conds) } } -struct dbus_entry *new_dbus_entry(int mode, struct cond_entry *conds, - struct cond_entry *peer_conds) +dbus_rule::dbus_rule(int mode_p, struct cond_entry *conds, + struct cond_entry *peer_conds): + bus(NULL), name(NULL), peer_label(NULL), path(NULL), interface(NULL), member(NULL), + mode(0), audit(0), deny(0) { - struct dbus_entry *ent; int name_is_subject_cond = 0, message_rule = 0, service_rule = 0; - ent = (struct dbus_entry*) calloc(1, sizeof(struct dbus_entry)); - if (!ent) - goto out; - /* Move the global/subject conditionals over & check the results */ - move_conditionals(ent, conds); - if (ent->name) + move_conditionals(conds); + if (name) name_is_subject_cond = 1; - if (ent->peer_label) + if (peer_label) yyerror("dbus \"label\" conditional can only be used inside of the \"peer=()\" grouping\n"); /* Move the peer conditionals */ - move_conditionals(ent, peer_conds); + move_conditionals(peer_conds); - if (ent->path || ent->interface || ent->member || ent->peer_label || - (ent->name && !name_is_subject_cond)) + if (path || interface || member || peer_label || + (name && !name_is_subject_cond)) message_rule = 1; - if (ent->name && name_is_subject_cond) + if (name && name_is_subject_cond) service_rule = 1; if (message_rule && service_rule) yyerror("dbus rule contains message conditionals and service conditionals\n"); /* Copy mode. If no mode was specified, assign an implied mode. */ - if (mode) { - ent->mode = mode; - if (ent->mode & ~AA_VALID_DBUS_PERMS) + if (mode_p) { + mode = mode_p; + if (mode & ~AA_VALID_DBUS_PERMS) yyerror("mode contains unknown dbus accesss\n"); - else if (message_rule && (ent->mode & AA_DBUS_BIND)) + else if (message_rule && (mode & AA_DBUS_BIND)) yyerror("dbus \"bind\" access cannot be used with message rule conditionals\n"); - else if (service_rule && (ent->mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE))) + else if (service_rule && (mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE))) yyerror("dbus \"send\" and/or \"receive\" accesses cannot be used with service rule conditionals\n"); - else if (ent->mode & AA_DBUS_EAVESDROP && - (ent->path || ent->interface || ent->member || - ent->peer_label || ent->name)) { + else if (mode & AA_DBUS_EAVESDROP && + (path || interface || member || + peer_label || name)) { yyerror("dbus \"eavesdrop\" access can only contain a bus conditional\n"); } } else { if (message_rule) - ent->mode = (AA_DBUS_SEND | AA_DBUS_RECEIVE); + mode = (AA_DBUS_SEND | AA_DBUS_RECEIVE); else if (service_rule) - ent->mode = (AA_DBUS_BIND); + mode = (AA_DBUS_BIND); else - ent->mode = AA_VALID_DBUS_PERMS; + mode = AA_VALID_DBUS_PERMS; } -out: free_cond_list(conds); free_cond_list(peer_conds); - return ent; } -struct dbus_entry *dup_dbus_entry(struct dbus_entry *orig) +ostream &dbus_rule::dump(ostream &os) { - struct dbus_entry *ent = NULL; - ent = (struct dbus_entry *) calloc(1, sizeof(struct dbus_entry)); - if (!ent) - return NULL; + if (audit) + os << "audit "; + if (deny) + os << "deny "; - DUP_STRING(orig, ent, bus, err); - DUP_STRING(orig, ent, name, err); - DUP_STRING(orig, ent, peer_label, err); - DUP_STRING(orig, ent, path, err); - DUP_STRING(orig, ent, interface, err); - DUP_STRING(orig, ent, member, err); - ent->mode = orig->mode; - ent->audit = orig->audit; - ent->deny = orig->deny; + os << "dbus ( "; - ent->next = orig->next; + if (mode & AA_DBUS_SEND) + os << "send "; + if (mode & AA_DBUS_RECEIVE) + os << "receive "; + if (mode & AA_DBUS_BIND) + os << "bind "; + if (mode & AA_DBUS_EAVESDROP) + os << "eavesdrop "; + os << ")"; - return ent; + if (bus) + os << " bus=\"" << bus << "\""; + if ((mode & AA_DBUS_BIND) && name) + os << " name=\"" << name << "\""; + if (path) + os << " path=\"" << path << "\""; + if (interface) + os << " interface=\"" << interface << "\""; + if (member) + os << " member=\"" << member << os << "\""; -err: - free_dbus_entry(ent); - return NULL; -} - -void print_dbus_entry(struct dbus_entry *ent) -{ - if (ent->audit) - fprintf(stderr, "audit "); - if (ent->deny) - fprintf(stderr, "deny "); - - fprintf(stderr, "dbus ( "); - - if (ent->mode & AA_DBUS_SEND) - fprintf(stderr, "send "); - if (ent->mode & AA_DBUS_RECEIVE) - fprintf(stderr, "receive "); - if (ent->mode & AA_DBUS_BIND) - fprintf(stderr, "bind "); - if (ent->mode & AA_DBUS_EAVESDROP) - fprintf(stderr, "eavesdrop "); - fprintf(stderr, ")"); - - if (ent->bus) - fprintf(stderr, " bus=\"%s\"", ent->bus); - if ((ent->mode & AA_DBUS_BIND) && ent->name) - fprintf(stderr, " name=\"%s\"", ent->name); - if (ent->path) - fprintf(stderr, " path=\"%s\"", ent->path); - if (ent->interface) - fprintf(stderr, " interface=\"%s\"", ent->interface); - if (ent->member) - fprintf(stderr, " member=\"%s\"", ent->member); - - if (!(ent->mode & AA_DBUS_BIND) && (ent->peer_label || ent->name)) { - fprintf(stderr, " peer=( "); - if (ent->peer_label) - fprintf(stderr, "label=\"%s\" ", ent->peer_label); - if (ent->name) - fprintf(stderr, "name=\"%s\" ", ent->name); - fprintf(stderr, ")"); + if (!(mode & AA_DBUS_BIND) && (peer_label || name)) { + os << " peer=( "; + if (peer_label) + os << "label=\"" << peer_label << "\" "; + if (name) + os << "name=\"" << name << "\" "; + os << ")"; } - fprintf(stderr, ",\n"); + os << ",\n"; + + return os; +} + +int dbus_rule::expand_variables(void) +{ + int error = expand_entry_variables(&bus); + if (error) + return error; + error = expand_entry_variables(&name); + if (error) + return error; + error = expand_entry_variables(&peer_label); + if (error) + return error; + error = expand_entry_variables(&path); + if (error) + return error; + error = expand_entry_variables(&interface); + if (error) + return error; + error = expand_entry_variables(&member); + if (error) + return error; + + return 0; +} + +/* do we want to warn once/profile or just once per compile?? */ +static void warn_once(const char *name) +{ + static const char *warned_name = NULL; + + if (warned_name != name) { + cerr << "Warning from profile " << name << " ("; + if (current_filename) + cerr << current_filename; + else + cerr << "stdin"; + cerr << ") dbus rules not enforced\n"; + warned_name = name; + } +} + +int dbus_rule::gen_policy_re(Profile &prof) +{ + std::string busbuf; + std::string namebuf; + std::string peer_labelbuf; + std::string pathbuf; + std::string ifacebuf; + std::string memberbuf; + std::ostringstream buffer; + const char *vec[6]; + + pattern_t ptype; + int pos; + + if (!kernel_supports_dbus) { + warn_once(prof.name); + return RULE_NOT_SUPPORTED; + } + + buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_DBUS; + busbuf.append(buffer.str()); + + if (bus) { + ptype = convert_aaregex_to_pcre(bus, 0, busbuf, &pos); + if (ptype == ePatternInvalid) + goto fail; + } else { + /* match any char except \000 0 or more times */ + busbuf.append(default_match_pattern); + } + vec[0] = busbuf.c_str(); + + if (name) { + ptype = convert_aaregex_to_pcre(name, 0, namebuf, &pos); + if (ptype == ePatternInvalid) + goto fail; + vec[1] = namebuf.c_str(); + } else { + /* match any char except \000 0 or more times */ + vec[1] = default_match_pattern; + } + + if (peer_label) { + ptype = convert_aaregex_to_pcre(peer_label, 0, + peer_labelbuf, &pos); + if (ptype == ePatternInvalid) + goto fail; + vec[2] = peer_labelbuf.c_str(); + } else { + /* match any char except \000 0 or more times */ + vec[2] = default_match_pattern; + } + + if (path) { + ptype = convert_aaregex_to_pcre(path, 0, pathbuf, &pos); + if (ptype == ePatternInvalid) + goto fail; + vec[3] = pathbuf.c_str(); + } else { + /* match any char except \000 0 or more times */ + vec[3] = default_match_pattern; + } + + if (interface) { + ptype = convert_aaregex_to_pcre(interface, 0, ifacebuf, &pos); + if (ptype == ePatternInvalid) + goto fail; + vec[4] = ifacebuf.c_str(); + } else { + /* match any char except \000 0 or more times */ + vec[4] = default_match_pattern; + } + + if (member) { + ptype = convert_aaregex_to_pcre(member, 0, memberbuf, &pos); + if (ptype == ePatternInvalid) + goto fail; + vec[5] = memberbuf.c_str(); + } else { + /* match any char except \000 0 or more times */ + vec[5] = default_match_pattern; + } + + if (mode & AA_DBUS_BIND) { + if (!aare_add_rule_vec(prof.policy.rules, deny, + mode & AA_DBUS_BIND, + audit & AA_DBUS_BIND, + 2, vec, dfaflags)) + goto fail; + } + if (mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE)) { + if (!aare_add_rule_vec(prof.policy.rules, deny, + mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE), + audit & (AA_DBUS_SEND | AA_DBUS_RECEIVE), + 6, vec, dfaflags)) + goto fail; + } + if (mode & AA_DBUS_EAVESDROP) { + if (!aare_add_rule_vec(prof.policy.rules, deny, + mode & AA_DBUS_EAVESDROP, + audit & AA_DBUS_EAVESDROP, + 1, vec, dfaflags)) + goto fail; + } + + prof.policy.count++; + return RULE_OK; + +fail: + return RULE_ERROR; } diff --git a/parser/dbus.h b/parser/dbus.h index bec072cc6..e777522d0 100644 --- a/parser/dbus.h +++ b/parser/dbus.h @@ -20,8 +20,14 @@ #define __AA_DBUS_H #include "parser.h" +#include "rule.h" +#include "profile.h" -struct dbus_entry { +extern int parse_dbus_mode(const char *str_mode, int *mode, int fail); + +class dbus_rule: public rule_t { + void move_conditionals(struct cond_entry *conds); +public: char *bus; /** * Be careful! ->name can be the subject or the peer name, depending on @@ -37,13 +43,23 @@ struct dbus_entry { int audit; int deny; - struct dbus_entry *next; + dbus_rule(int mode_p, struct cond_entry *conds, + struct cond_entry *peer_conds); + virtual ~dbus_rule() { + free(bus); + free(name); + free(peer_label); + free(path); + free(interface); + free(member); + }; + + virtual ostream &dump(ostream &os); + virtual int expand_variables(void); + virtual int gen_policy_re(Profile &prof); + virtual void post_process(Profile &prof __unused) { }; + + }; -void free_dbus_entry(struct dbus_entry *ent); -struct dbus_entry *new_dbus_entry(int mode, struct cond_entry *conds, - struct cond_entry *peer_conds); -struct dbus_entry *dup_dbus_entry(struct dbus_entry *ent); -void print_dbus_entry(struct dbus_entry *ent); - #endif /* __AA_DBUS_H */ diff --git a/parser/mount.c b/parser/mount.c index d317e53bd..a923472e2 100644 --- a/parser/mount.c +++ b/parser/mount.c @@ -215,8 +215,12 @@ #include #include +#include +#include #include "parser.h" +#include "policydb.h" +#include "profile.h" #include "mount.h" struct mnt_keyword_table { @@ -389,146 +393,469 @@ static struct value_list *extract_options(struct cond_entry **conds, int eq) return list; } -struct mnt_entry *new_mnt_entry(struct cond_entry *src_conds, char *device, - struct cond_entry *dst_conds __unused, char *mnt_point, - int allow) +mnt_rule::mnt_rule(struct cond_entry *src_conds, char *device_p, + struct cond_entry *dst_conds __unused, char *mnt_point_p, + int allow_p): + mnt_point(mnt_point_p), device(device_p), trans(NULL), opts(NULL), + audit(0), deny(0) { /* FIXME: dst_conds are ignored atm */ + dev_type = extract_fstype(&src_conds); - struct mnt_entry *ent; - ent = (struct mnt_entry *) calloc(1, sizeof(struct mnt_entry)); - if (ent) { - ent->mnt_point = mnt_point; - ent->device = device; - ent->dev_type = extract_fstype(&src_conds); + if (src_conds) { + struct value_list *list = extract_options(&src_conds, 0); - ent->flags = 0; - ent->inv_flags = 0; + opts = extract_options(&src_conds, 1); + if (opts) + flags = extract_flags(&opts, &inv_flags); - if (src_conds) { - unsigned int flags = 0, inv_flags = 0; - struct value_list *list = extract_options(&src_conds, 0); + if (list) { + unsigned int tmpflags, tmpinv_flags = 0; - ent->opts = extract_options(&src_conds, 1); - if (ent->opts) - ent->flags = extract_flags(&ent->opts, - &ent->inv_flags); + tmpflags = extract_flags(&list, &tmpinv_flags); + /* these flags are optional so set both */ + tmpflags |= tmpinv_flags; + tmpinv_flags |= tmpflags; - if (list) { - flags = extract_flags(&list, &inv_flags); - /* these flags are optional so set both */ - flags |= inv_flags; - inv_flags |= flags; + flags |= tmpflags; + inv_flags |= tmpinv_flags; - ent->flags |= flags; - ent->inv_flags |= inv_flags; - - if (ent->opts) - list_append(ent->opts, list); - else if (list) - ent->opts = list; - } + if (opts) + list_append(opts, list); + else if (list) + opts = list; } + } - if (allow & AA_DUMMY_REMOUNT) { - allow = AA_MAY_MOUNT; - ent->flags |= MS_REMOUNT; - ent->inv_flags = 0; - } else if (!(ent->flags | ent->inv_flags)) { - /* no flag options, and not remount, allow everything */ - ent->flags = MS_ALL_FLAGS; - ent->inv_flags = MS_ALL_FLAGS; + if (allow_p & AA_DUMMY_REMOUNT) { + allow_p = AA_MAY_MOUNT; + flags |= MS_REMOUNT; + inv_flags = 0; + } else if (!(flags | inv_flags)) { + /* no flag options, and not remount, allow everything */ + flags = MS_ALL_FLAGS; + inv_flags = MS_ALL_FLAGS; + } + + allow = allow_p; + + if (src_conds) { + PERROR(" unsupported mount conditions\n"); + exit(1); + } +} + +ostream &mnt_rule::dump(ostream &os) +{ + if (allow & AA_MAY_MOUNT) + os << "mount"; + else if (allow & AA_MAY_UMOUNT) + os << "umount"; + else if (allow & AA_MAY_PIVOTROOT) + os << "pivotroot"; + else + os << "error: unknonwn mount perm"; + + os << " (0x" << hex << flags << " - 0x" << inv_flags << ") "; + if (dev_type) { + os << " type="; + print_value_list(dev_type); + } + if (opts) { + os << " options="; + print_value_list(opts); + } + if (device) + os << " " << device; + if (mnt_point) + os << " -> " << mnt_point; + if (trans) + os << " -> " << trans; + + const char *prefix = deny ? "deny" : ""; + os << " " << prefix << "(0x" << hex << allow << "/0x" << audit << ")"; + os << ",\n"; + + return os; +} + +/* does not currently support expansion of vars in options */ +int mnt_rule::expand_variables(void) +{ + int error = 0; + + error = expand_entry_variables(&mnt_point); + if (error) + return error; + error = expand_entry_variables(&device); + if (error) + return error; + error = expand_entry_variables(&trans); + if (error) + return error; + + return 0; +} + +static int build_mnt_flags(char *buffer, int size, unsigned int flags, + unsigned int inv_flags) +{ + char *p = buffer; + int i, len = 0; + + if (flags == MS_ALL_FLAGS) { + /* all flags are optional */ + len = snprintf(p, size, "%s", default_match_pattern); + if (len < 0 || len >= size) + return FALSE; + return TRUE; + } + for (i = 0; i <= 31; ++i) { + if ((flags & inv_flags) & (1 << i)) + len = snprintf(p, size, "(\\x%02x|)", i + 1); + else if (flags & (1 << i)) + len = snprintf(p, size, "\\x%02x", i + 1); + else /* no entry = not set */ + continue; + + if (len < 0 || len >= size) + return FALSE; + p += len; + size -= len; + } + + /* this needs to go once the backend is updated. */ + if (buffer == p) { + /* match nothing - use impossible 254 as regex parser doesn't + * like the empty string + */ + if (size < 9) + return FALSE; + + strcpy(p, "(\\xfe|)"); + } + + return TRUE; +} + +static int build_mnt_opts(std::string& buffer, struct value_list *opts) +{ + struct value_list *ent; + pattern_t ptype; + int pos; + + if (!opts) { + buffer.append(default_match_pattern); + return TRUE; + } + + list_for_each(opts, ent) { + ptype = convert_aaregex_to_pcre(ent->value, 0, buffer, &pos); + if (ptype == ePatternInvalid) + return FALSE; + + if (ent->next) + buffer.append(","); + } + + return TRUE; +} + +/* do we want to warn once/profile or just once per compile?? */ +static void warn_once(const char *name) +{ + static const char *warned_name = NULL; + + if (warned_name != name) { + cerr << "Warning from profile " << name << " ("; + if (current_filename) + cerr << current_filename; + else + cerr << "stdin"; + cerr << ") mount rules not enforced\n"; + warned_name = name; + } +} + +int mnt_rule::gen_policy_re(Profile &prof) +{ + std::string mntbuf; + std::string devbuf; + std::string typebuf; + char flagsbuf[PATH_MAX + 3]; + std::string optsbuf; + char class_mount_hdr[64]; + const char *vec[5]; + int count = 0; + unsigned int tmpflags, tmpinv_flags; + + if (!kernel_supports_mount) { + warn_once(prof.name); + return RULE_NOT_SUPPORTED; + } + + sprintf(class_mount_hdr, "\\x%02x", AA_CLASS_MOUNT); + + /* a single mount rule may result in multiple matching rules being + * created in the backend to cover all the possible choices + */ + + if ((allow & AA_MAY_MOUNT) && (flags & MS_REMOUNT) + && !device && !dev_type) { + int tmpallow; + /* remount can't be conditional on device and type */ + /* rule class single byte header */ + mntbuf.assign(class_mount_hdr); + if (mnt_point) { + /* both device && mnt_point or just mnt_point */ + if (!convert_entry(mntbuf, mnt_point)) + goto fail; + vec[0] = mntbuf.c_str(); + } else { + if (!convert_entry(mntbuf, device)) + goto fail; + vec[0] = mntbuf.c_str(); } + /* skip device */ + vec[1] = default_match_pattern; + /* skip type */ + vec[2] = default_match_pattern; - ent->allow = allow; + tmpflags = flags; + tmpinv_flags = inv_flags; + if (tmpflags != MS_ALL_FLAGS) + tmpflags &= MS_REMOUNT_FLAGS; + if (tmpinv_flags != MS_ALL_FLAGS) + tmpflags &= MS_REMOUNT_FLAGS; + if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags)) + goto fail; + vec[3] = flagsbuf; - if (src_conds) { - PERROR(" unsupported mount conditions\n"); + if (opts) + tmpallow = AA_MATCH_CONT; + else + tmpallow = allow; + + /* rule for match without required data || data MATCH_CONT */ + if (!aare_add_rule_vec(prof.policy.rules, deny, tmpallow, + audit | AA_AUDIT_MNT_DATA, 4, + vec, dfaflags)) + goto fail; + count++; + + if (opts) { + /* rule with data match required */ + optsbuf.clear(); + if (!build_mnt_opts(optsbuf, opts)) + goto fail; + vec[4] = optsbuf.c_str(); + if (!aare_add_rule_vec(prof.policy.rules, deny, + allow, + audit | AA_AUDIT_MNT_DATA, + 5, vec, dfaflags)) + goto fail; + count++; + } + } + if ((allow & AA_MAY_MOUNT) && (flags & MS_BIND) + && !dev_type && !opts) { + /* bind mount rules can't be conditional on dev_type or data */ + /* rule class single byte header */ + mntbuf.assign(class_mount_hdr); + if (!convert_entry(mntbuf, mnt_point)) + goto fail; + vec[0] = mntbuf.c_str(); + if (!clear_and_convert_entry(devbuf, device)) + goto fail; + vec[1] = devbuf.c_str(); + /* skip type */ + vec[2] = default_match_pattern; + + tmpflags = flags; + tmpinv_flags = inv_flags; + if (tmpflags != MS_ALL_FLAGS) + tmpflags &= MS_BIND_FLAGS; + if (tmpinv_flags != MS_ALL_FLAGS) + tmpflags &= MS_BIND_FLAGS; + if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags)) + goto fail; + vec[3] = flagsbuf; + if (!aare_add_rule_vec(prof.policy.rules, deny, allow, + audit, 4, vec, dfaflags)) + goto fail; + count++; + } + if ((allow & AA_MAY_MOUNT) && + (flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED)) + && !device && !dev_type && !opts) { + /* change type base rules can not be conditional on device, + * device type or data + */ + /* rule class single byte header */ + mntbuf.assign(class_mount_hdr); + if (!convert_entry(mntbuf, mnt_point)) + goto fail; + vec[0] = mntbuf.c_str(); + /* skip device and type */ + vec[1] = default_match_pattern; + vec[2] = default_match_pattern; + + tmpflags = flags; + tmpinv_flags = inv_flags; + if (tmpflags != MS_ALL_FLAGS) + tmpflags &= MS_MAKE_FLAGS; + if (tmpinv_flags != MS_ALL_FLAGS) + tmpflags &= MS_MAKE_FLAGS; + if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags)) + goto fail; + vec[3] = flagsbuf; + if (!aare_add_rule_vec(prof.policy.rules, deny, allow, + audit, 4, vec, dfaflags)) + goto fail; + count++; + } + if ((allow & AA_MAY_MOUNT) && (flags & MS_MOVE) + && !dev_type && !opts) { + /* mount move rules can not be conditional on dev_type, + * or data + */ + /* rule class single byte header */ + mntbuf.assign(class_mount_hdr); + if (!convert_entry(mntbuf, mnt_point)) + goto fail; + vec[0] = mntbuf.c_str(); + if (!clear_and_convert_entry(devbuf, device)) + goto fail; + vec[1] = devbuf.c_str(); + /* skip type */ + vec[2] = default_match_pattern; + + tmpflags = flags; + tmpinv_flags = inv_flags; + if (tmpflags != MS_ALL_FLAGS) + tmpflags &= MS_MOVE_FLAGS; + if (tmpinv_flags != MS_ALL_FLAGS) + tmpflags &= MS_MOVE_FLAGS; + if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags)) + goto fail; + vec[3] = flagsbuf; + if (!aare_add_rule_vec(prof.policy.rules, deny, allow, + audit, 4, vec, dfaflags)) + goto fail; + count++; + } + if ((allow & AA_MAY_MOUNT) && + (flags | inv_flags) & ~MS_CMDS) { + int tmpallow; + /* generic mount if flags are set that are not covered by + * above commands + */ + /* rule class single byte header */ + mntbuf.assign(class_mount_hdr); + if (!convert_entry(mntbuf, mnt_point)) + goto fail; + vec[0] = mntbuf.c_str(); + if (!clear_and_convert_entry(devbuf, device)) + goto fail; + vec[1] = devbuf.c_str(); + typebuf.clear(); + if (!build_list_val_expr(typebuf, dev_type)) + goto fail; + vec[2] = typebuf.c_str(); + + tmpflags = flags; + tmpinv_flags = inv_flags; + if (tmpflags != MS_ALL_FLAGS) + tmpflags &= ~MS_CMDS; + if (tmpinv_flags != MS_ALL_FLAGS) + tmpinv_flags &= ~MS_CMDS; + if (!build_mnt_flags(flagsbuf, PATH_MAX, tmpflags, tmpinv_flags)) + goto fail; + vec[3] = flagsbuf; + + if (opts) + tmpallow = AA_MATCH_CONT; + else + tmpallow = allow; + + /* rule for match without required data || data MATCH_CONT */ + if (!aare_add_rule_vec(prof.policy.rules, deny, tmpallow, + audit | AA_AUDIT_MNT_DATA, 4, + vec, dfaflags)) + goto fail; + count++; + + if (opts) { + /* rule with data match required */ + optsbuf.clear(); + if (!build_mnt_opts(optsbuf, opts)) + goto fail; + vec[4] = optsbuf.c_str(); + if (!aare_add_rule_vec(prof.policy.rules, deny, + allow, + audit | AA_AUDIT_MNT_DATA, + 5, vec, dfaflags)) + goto fail; + count++; + } + } + if (allow & AA_MAY_UMOUNT) { + /* rule class single byte header */ + mntbuf.assign(class_mount_hdr); + if (!convert_entry(mntbuf, mnt_point)) + goto fail; + vec[0] = mntbuf.c_str(); + if (!aare_add_rule_vec(prof.policy.rules, deny, allow, + audit, 1, vec, dfaflags)) + goto fail; + count++; + } + if (allow & AA_MAY_PIVOTROOT) { + /* rule class single byte header */ + mntbuf.assign(class_mount_hdr); + if (!convert_entry(mntbuf, mnt_point)) + goto fail; + vec[0] = mntbuf.c_str(); + if (!clear_and_convert_entry(devbuf, device)) + goto fail; + vec[1] = devbuf.c_str(); + if (!aare_add_rule_vec(prof.policy.rules, deny, allow, + audit, 2, vec, dfaflags)) + goto fail; + count++; + } + + if (!count) + /* didn't actually encode anything */ + goto fail; + + prof.policy.count++; + return RULE_OK; + +fail: + PERROR("Enocoding of mount rule failed\n"); + return RULE_ERROR; +} + +void mnt_rule::post_process(Profile &prof) +{ + if (trans) { + unsigned int mode = 0; + int n = add_entry_to_x_table(&prof, trans); + if (!n) { + PERROR("Profile %s has too many specified profile transitions.\n", prof.name); exit(1); } + + if (allow & AA_USER_EXEC) + mode |= SHIFT_MODE(n << 10, AA_USER_SHIFT); + if (allow & AA_OTHER_EXEC) + mode |= SHIFT_MODE(n << 10, AA_OTHER_SHIFT); + allow = ((allow & ~AA_ALL_EXEC_MODIFIERS) | + (mode & AA_ALL_EXEC_MODIFIERS)); + + trans = NULL; } - - return ent; } -void free_mnt_entry(struct mnt_entry *ent) -{ - if (!ent) - return; - free_mnt_entry(ent->next); - free_value_list(ent->opts); - free_value_list(ent->dev_type); - free(ent->device); - free(ent->mnt_point); - free(ent->trans); - - free(ent); -} - -struct mnt_entry *dup_mnt_entry(struct mnt_entry *orig) -{ - struct mnt_entry *entry = NULL; - - entry = (struct mnt_entry *) calloc(1, sizeof(struct mnt_entry)); - if (!entry) - return NULL; - - DUP_STRING(orig, entry, mnt_point, err); - DUP_STRING(orig, entry, device, err); - DUP_STRING(orig, entry, trans, err); - - entry->dev_type = dup_value_list(orig->dev_type); - if (orig->dev_type && !(entry->dev_type)) - goto err; - - entry->opts = dup_value_list(orig->opts); - if (orig->opts && !(entry->opts)) - goto err; - - entry->flags = orig->flags; - entry->inv_flags = orig->inv_flags; - - entry->allow = orig->allow; - entry->audit = orig->audit; - entry->deny = orig->deny; - - entry->next = orig->next; - - return entry; - -err: - free_mnt_entry(entry); - return NULL; -} - -void print_mnt_entry(struct mnt_entry *entry) -{ - if (entry->allow & AA_MAY_MOUNT) - fprintf(stderr, "mount"); - else if (entry->allow & AA_MAY_UMOUNT) - fprintf(stderr, "umount"); - else if (entry->allow & AA_MAY_PIVOTROOT) - fprintf(stderr, "pivotroot"); - else - fprintf(stderr, "error: unknonwn mount perm"); - - fprintf(stderr, " (0x%x - 0x%x) ", entry->flags, entry->inv_flags); - if (entry->dev_type) { - fprintf(stderr, " type="); - print_value_list(entry->dev_type); - } - if (entry->opts) { - fprintf(stderr, " options="); - print_value_list(entry->opts); - } - if (entry->device) - fprintf(stderr, " %s", entry->device); - if (entry->mnt_point) - fprintf(stderr, " -> %s", entry->mnt_point); - if (entry->trans) - fprintf(stderr, " -> %s", entry->trans); - - fprintf(stderr, " %s (0x%x/0x%x)", entry->deny ? "deny" : "", entry->allow, entry->audit); - fprintf(stderr, ",\n"); -} diff --git a/parser/mount.h b/parser/mount.h index 166e67228..0331da8be 100644 --- a/parser/mount.h +++ b/parser/mount.h @@ -19,7 +19,11 @@ #ifndef __AA_MOUNT_H #define __AA_MOUNT_H +#include + #include "parser.h" +#include "rule.h" + #define MS_RDONLY (1 << 0) #define MS_RW 0 @@ -109,7 +113,8 @@ * remapped to a mount option*/ -struct mnt_entry { +class mnt_rule: public rule_t { +public: char *mnt_point; char *device; char *trans; @@ -120,17 +125,26 @@ struct mnt_entry { int allow, audit; int deny; - struct mnt_entry *next; + + mnt_rule(struct cond_entry *src_conds, char *device_p, + struct cond_entry *dst_conds __unused, char *mnt_point_p, + int allow_p); + virtual ~mnt_rule() + { + free_value_list(opts); + free_value_list(dev_type); + free(device); + free(mnt_point); + free(trans); + } + + virtual ostream &dump(ostream &os); + virtual int expand_variables(void); + virtual int gen_policy_re(Profile &prof); + virtual void post_process(Profile &prof __unused); }; -void print_mnt_entry(struct mnt_entry *entry); - int is_valid_mnt_cond(const char *name, int src); -struct mnt_entry *new_mnt_entry(struct cond_entry *sconds, char *device, - struct cond_entry *dconds, char *mnt_point, - int mode); -struct mnt_entry *dup_mnt_entry(struct mnt_entry *orig); -void free_mnt_entry(struct mnt_entry *ent); #endif /* __AA_MOUNT_H */ diff --git a/parser/parser.h b/parser/parser.h index 91c32fbdd..9bf7d768e 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -30,12 +30,13 @@ #include "libapparmor_re/apparmor_re.h" #include "libapparmor_re/aare_rules.h" +#include + using namespace std; #include class Profile; - -struct mnt_ent; +class rule_t; /* Global variable to pass token to lexer. Will be replaced by parameter * when lexer and parser are made reentrant @@ -256,9 +257,14 @@ extern int yylex(void); extern const char *basedir; /* parser_regex.c */ +extern const char *default_match_pattern; +extern pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, + std::string& pcre, int *first_re_pos); +extern int build_list_val_expr(std::string& buffer, struct value_list *list); +extern int convert_entry(std::string& buffer, char *entry); +extern int clear_and_convert_entry(std::string& buffer, char *entry); extern int process_regex(Profile *prof); extern int post_process_entry(struct cod_entry *entry); -extern int process_dbus(Profile *prof); extern void reset_regex(void); @@ -267,11 +273,13 @@ extern int process_policydb(Profile *prof); extern int process_policy_ents(Profile *prof); /* parser_variable.c */ +int expand_entry_variables(char **name); extern int process_variables(Profile *prof); extern struct var_string *split_out_var(const char *string); extern void free_var_string(struct var_string *var); /* parser_misc.c */ +extern void warn_uppercase(void); extern int is_blacklisted(const char *name, const char *path); extern struct value_list *new_value_list(char *value); extern struct value_list *dup_value_list(struct value_list *list); @@ -289,7 +297,6 @@ extern int name_to_capability(const char *keyword); extern int get_rlimit(const char *name); extern char *process_var(const char *var); extern int parse_mode(const char *mode); -extern int parse_dbus_mode(const char *str_mode, int *mode, int fail); extern struct cod_entry *new_entry(char *ns, char *id, int mode, char *link_id); extern struct aa_network_entry *new_network_ent(unsigned int family, unsigned int type, @@ -303,15 +310,13 @@ extern size_t get_af_max(void); 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 free_mnt_entries(struct mnt_entry *list); -extern void free_dbus_entries(struct dbus_entry *list); extern void __debug_capabilities(uint64_t capset, const char *name); void __debug_network(unsigned int *array, const char *name); void debug_cod_entries(struct cod_entry *list); /* parser_symtab.c */ -struct set_value {; +struct set_value { char *val; struct set_value *next; }; @@ -345,9 +350,10 @@ extern int cache_fd; /* parser_policy.c */ extern void add_to_list(Profile *profile); extern void add_hat_to_policy(Profile *policy, Profile *hat); +extern int add_entry_to_x_table(Profile *prof, char *name); extern void add_entry_to_policy(Profile *policy, struct cod_entry *entry); extern void post_process_file_entries(Profile *prof); -extern void post_process_mnt_entries(Profile *prof); +extern void post_process_rule_entries(Profile *prof); extern int post_process_policy(int debug_only); extern int process_profile_regex(Profile *prof); extern int process_profile_variables(Profile *prof); diff --git a/parser/parser_misc.c b/parser/parser_misc.c index 2dfb1bb6b..9a764cfc0 100644 --- a/parser/parser_misc.c +++ b/parser/parser_misc.c @@ -596,7 +596,7 @@ int str_to_boolean(const char *value) static int warned_uppercase = 0; -static void warn_uppercase(void) +void warn_uppercase(void) { if (!warned_uppercase) { pwarn(_("Uppercase qualifiers \"RWLIMX\" are deprecated, please convert to lowercase\n" @@ -792,81 +792,6 @@ int parse_mode(const char *str_mode) return mode; } -static int parse_dbus_sub_mode(const char *str_mode, int *result, int fail, const char *mode_desc __unused) -{ - int mode = 0; - const char *p; - - PDEBUG("Parsing DBus mode: %s\n", str_mode); - - if (!str_mode) - return 0; - - p = str_mode; - while (*p) { - char current = *p; - char lower; - -reeval: - switch (current) { - case COD_READ_CHAR: - PDEBUG("Parsing DBus mode: found %s READ\n", mode_desc); - mode |= AA_DBUS_RECEIVE; - break; - - case COD_WRITE_CHAR: - PDEBUG("Parsing DBus mode: found %s WRITE\n", - mode_desc); - mode |= AA_DBUS_SEND; - break; - - /* error cases */ - - default: - lower = tolower(current); - switch (lower) { - case COD_READ_CHAR: - case COD_WRITE_CHAR: - PDEBUG("Parsing DBus mode: found invalid upper case char %c\n", - current); - warn_uppercase(); - current = lower; - goto reeval; - break; - default: - if (fail) - yyerror(_("Internal: unexpected DBus mode character '%c' in input"), - current); - else - return 0; - break; - } - break; - } - p++; - } - - PDEBUG("Parsed DBus mode: %s 0x%x\n", str_mode, mode); - - *result = mode; - return 1; -} - -int parse_dbus_mode(const char *str_mode, int *mode, int fail) -{ - *mode = 0; - if (!parse_dbus_sub_mode(str_mode, mode, fail, "")) - return 0; - if (*mode & ~AA_VALID_DBUS_PERMS) { - if (fail) - yyerror(_("Internal error generated invalid DBus perm 0x%x\n"), - mode); - else - return 0; - } - return 1; -} - struct cod_entry *new_entry(char *ns, char *id, int mode, char *link_id) { struct cod_entry *entry = NULL; @@ -938,30 +863,6 @@ void free_cod_entries(struct cod_entry *list) free(list); } -void free_mnt_entries(struct mnt_entry *list) -{ - if (!list) - return; - if (list->next) - free_mnt_entries(list->next); - free(list->mnt_point); - free(list->device); - free_value_list(list->dev_type); - free_value_list(list->opts); - - free(list); -} - -void free_dbus_entries(struct dbus_entry *list) -{ - if (!list) - return; - if (list->next) - free_dbus_entries(list->next); - - free_dbus_entry(list); -} - static void debug_base_perm_mask(int mask) { if (HAS_MAY_READ(mask)) diff --git a/parser/parser_policy.c b/parser/parser_policy.c index 90bf982cb..233520378 100644 --- a/parser/parser_policy.c +++ b/parser/parser_policy.c @@ -32,8 +32,6 @@ #include "parser.h" #include "profile.h" -#include "mount.h" -#include "dbus.h" #include "parser_yacc.h" /* #define DEBUG */ @@ -70,7 +68,7 @@ void add_hat_to_policy(Profile *prof, Profile *hat) } } -static int add_entry_to_x_table(Profile *prof, char *name) +int add_entry_to_x_table(Profile *prof, char *name) { int i; for (i = (AA_EXEC_LOCAL >> 10) + 1; i < AA_EXEC_COUNT; i++) { @@ -192,29 +190,10 @@ void post_process_file_entries(Profile *prof) } } -void post_process_mnt_entries(Profile *prof) +void post_process_rule_entries(Profile *prof) { - struct mnt_entry *entry; - - list_for_each(prof->mnt_ents, entry) { - if (entry->trans) { - unsigned int mode = 0; - int n = add_entry_to_x_table(prof, entry->trans); - if (!n) { - PERROR("Profile %s has too many specified profile transitions.\n", prof->name); - exit(1); - } - - if (entry->allow & AA_USER_EXEC) - mode |= SHIFT_MODE(n << 10, AA_USER_SHIFT); - if (entry->allow & AA_OTHER_EXEC) - mode |= SHIFT_MODE(n << 10, AA_OTHER_SHIFT); - entry->allow = ((entry->allow & ~AA_ALL_EXEC_MODIFIERS) | - (mode & AA_ALL_EXEC_MODIFIERS)); - - entry->trans = NULL; - } - } + for (RuleList::iterator i = prof->rule_ents.begin(); i != prof->rule_ents.end(); i++) + (*i)->post_process(*prof); } diff --git a/parser/parser_regex.c b/parser/parser_regex.c index 2d5a24dc7..882610e7e 100644 --- a/parser/parser_regex.c +++ b/parser/parser_regex.c @@ -35,9 +35,8 @@ #include "profile.h" #include "libapparmor_re/apparmor_re.h" #include "libapparmor_re/aare_rules.h" -#include "mount.h" -#include "dbus.h" #include "policydb.h" +#include "rule.h" enum error_type { e_no_error, @@ -45,7 +44,7 @@ enum error_type { }; /* match any char except \000 0 or more times */ -static const char *default_match_pattern = "[^\\000]*"; +const char *default_match_pattern = "[^\\000]*"; /* Filters out multiple slashes (except if the first two are slashes, * that's a distinct namespace in linux) and trailing slashes. @@ -90,8 +89,8 @@ static void filter_slashes(char *path) /* converts the apparmor regex in aare and appends pcre regex output * to pcre string */ -static pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, - std::string& pcre, int *first_re_pos) +pattern_t convert_aaregex_to_pcre(const char *aare, int anchor, + std::string& pcre, int *first_re_pos) { #define update_re_pos(X) if (!(*first_re_pos)) { *first_re_pos = (X); } #define MAX_ALT_DEPTH 50 @@ -612,7 +611,7 @@ out: return error; } -static int build_list_val_expr(std::string& buffer, struct value_list *list) +int build_list_val_expr(std::string& buffer, struct value_list *list) { struct value_list *ent; pattern_t ptype; @@ -642,7 +641,7 @@ fail: return FALSE; } -static int convert_entry(std::string& buffer, char *entry) +int convert_entry(std::string& buffer, char *entry) { pattern_t ptype; int pos; @@ -658,486 +657,18 @@ static int convert_entry(std::string& buffer, char *entry) return TRUE; } -static int clear_and_convert_entry(std::string& buffer, char *entry) +int clear_and_convert_entry(std::string& buffer, char *entry) { buffer.clear(); return convert_entry(buffer, entry); } -static int build_mnt_flags(char *buffer, int size, unsigned int flags, - unsigned int inv_flags) -{ - char *p = buffer; - int i, len = 0; - - if (flags == MS_ALL_FLAGS) { - /* all flags are optional */ - len = snprintf(p, size, "%s", default_match_pattern); - if (len < 0 || len >= size) - return FALSE; - return TRUE; - } - for (i = 0; i <= 31; ++i) { - if ((flags & inv_flags) & (1 << i)) - len = snprintf(p, size, "(\\x%02x|)", i + 1); - else if (flags & (1 << i)) - len = snprintf(p, size, "\\x%02x", i + 1); - else /* no entry = not set */ - continue; - - if (len < 0 || len >= size) - return FALSE; - p += len; - size -= len; - } - - /* this needs to go once the backend is updated. */ - if (buffer == p) { - /* match nothing - use impossible 254 as regex parser doesn't - * like the empty string - */ - if (size < 9) - return FALSE; - - strcpy(p, "(\\xfe|)"); - } - - return TRUE; -} - -static int build_mnt_opts(std::string& buffer, struct value_list *opts) -{ - struct value_list *ent; - pattern_t ptype; - int pos; - - if (!opts) { - buffer.append(default_match_pattern); - return TRUE; - } - - list_for_each(opts, ent) { - ptype = convert_aaregex_to_pcre(ent->value, 0, buffer, &pos); - if (ptype == ePatternInvalid) - return FALSE; - - if (ent->next) - buffer.append(","); - } - - return TRUE; -} - -static int process_mnt_entry(aare_ruleset_t *dfarules, struct mnt_entry *entry) -{ - std::string mntbuf; - std::string devbuf; - std::string typebuf; - char flagsbuf[PATH_MAX + 3]; - std::string optsbuf; - char class_mount_hdr[64]; - const char *vec[5]; - int count = 0; - unsigned int flags, inv_flags; - - sprintf(class_mount_hdr, "\\x%02x", AA_CLASS_MOUNT); - - /* a single mount rule may result in multiple matching rules being - * created in the backend to cover all the possible choices - */ - - if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_REMOUNT) - && !entry->device && !entry->dev_type) { - int allow; - /* remount can't be conditional on device and type */ - /* rule class single byte header */ - mntbuf.assign(class_mount_hdr); - if (entry->mnt_point) { - /* both device && mnt_point or just mnt_point */ - if (!convert_entry(mntbuf, entry->mnt_point)) - goto fail; - vec[0] = mntbuf.c_str(); - } else { - if (!convert_entry(mntbuf, entry->device)) - goto fail; - vec[0] = mntbuf.c_str(); - } - /* skip device */ - vec[1] = default_match_pattern; - /* skip type */ - vec[2] = default_match_pattern; - - flags = entry->flags; - inv_flags = entry->inv_flags; - if (flags != MS_ALL_FLAGS) - flags &= MS_REMOUNT_FLAGS; - if (inv_flags != MS_ALL_FLAGS) - flags &= MS_REMOUNT_FLAGS; - if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags)) - goto fail; - vec[3] = flagsbuf; - - if (entry->opts) - allow = AA_MATCH_CONT; - else - allow = entry->allow; - - /* rule for match without required data || data MATCH_CONT */ - if (!aare_add_rule_vec(dfarules, entry->deny, allow, - entry->audit | AA_AUDIT_MNT_DATA, 4, - vec, dfaflags)) - goto fail; - count++; - - if (entry->opts) { - /* rule with data match required */ - optsbuf.clear(); - if (!build_mnt_opts(optsbuf, entry->opts)) - goto fail; - vec[4] = optsbuf.c_str(); - if (!aare_add_rule_vec(dfarules, entry->deny, - entry->allow, - entry->audit | AA_AUDIT_MNT_DATA, - 5, vec, dfaflags)) - goto fail; - count++; - } - } - if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_BIND) - && !entry->dev_type && !entry->opts) { - /* bind mount rules can't be conditional on dev_type or data */ - /* rule class single byte header */ - mntbuf.assign(class_mount_hdr); - if (!convert_entry(mntbuf, entry->mnt_point)) - goto fail; - vec[0] = mntbuf.c_str(); - if (!clear_and_convert_entry(devbuf, entry->device)) - goto fail; - vec[1] = devbuf.c_str(); - /* skip type */ - vec[2] = default_match_pattern; - - flags = entry->flags; - inv_flags = entry->inv_flags; - if (flags != MS_ALL_FLAGS) - flags &= MS_BIND_FLAGS; - if (inv_flags != MS_ALL_FLAGS) - flags &= MS_BIND_FLAGS; - if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags)) - goto fail; - vec[3] = flagsbuf; - if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow, - entry->audit, 4, vec, dfaflags)) - goto fail; - count++; - } - if ((entry->allow & AA_MAY_MOUNT) && - (entry->flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED)) - && !entry->device && !entry->dev_type && !entry->opts) { - /* change type base rules can not be conditional on device, - * device type or data - */ - /* rule class single byte header */ - mntbuf.assign(class_mount_hdr); - if (!convert_entry(mntbuf, entry->mnt_point)) - goto fail; - vec[0] = mntbuf.c_str(); - /* skip device and type */ - vec[1] = default_match_pattern; - vec[2] = default_match_pattern; - - flags = entry->flags; - inv_flags = entry->inv_flags; - if (flags != MS_ALL_FLAGS) - flags &= MS_MAKE_FLAGS; - if (inv_flags != MS_ALL_FLAGS) - flags &= MS_MAKE_FLAGS; - if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags)) - goto fail; - vec[3] = flagsbuf; - if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow, - entry->audit, 4, vec, dfaflags)) - goto fail; - count++; - } - if ((entry->allow & AA_MAY_MOUNT) && (entry->flags & MS_MOVE) - && !entry->dev_type && !entry->opts) { - /* mount move rules can not be conditional on dev_type, - * or data - */ - /* rule class single byte header */ - mntbuf.assign(class_mount_hdr); - if (!convert_entry(mntbuf, entry->mnt_point)) - goto fail; - vec[0] = mntbuf.c_str(); - if (!clear_and_convert_entry(devbuf, entry->device)) - goto fail; - vec[1] = devbuf.c_str(); - /* skip type */ - vec[2] = default_match_pattern; - - flags = entry->flags; - inv_flags = entry->inv_flags; - if (flags != MS_ALL_FLAGS) - flags &= MS_MOVE_FLAGS; - if (inv_flags != MS_ALL_FLAGS) - flags &= MS_MOVE_FLAGS; - if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags)) - goto fail; - vec[3] = flagsbuf; - if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow, - entry->audit, 4, vec, dfaflags)) - goto fail; - count++; - } - if ((entry->allow & AA_MAY_MOUNT) && - (entry->flags | entry->inv_flags) & ~MS_CMDS) { - int allow; - /* generic mount if flags are set that are not covered by - * above commands - */ - /* rule class single byte header */ - mntbuf.assign(class_mount_hdr); - if (!convert_entry(mntbuf, entry->mnt_point)) - goto fail; - vec[0] = mntbuf.c_str(); - if (!clear_and_convert_entry(devbuf, entry->device)) - goto fail; - vec[1] = devbuf.c_str(); - typebuf.clear(); - if (!build_list_val_expr(typebuf, entry->dev_type)) - goto fail; - vec[2] = typebuf.c_str(); - - flags = entry->flags; - inv_flags = entry->inv_flags; - if (flags != MS_ALL_FLAGS) - flags &= ~MS_CMDS; - if (inv_flags != MS_ALL_FLAGS) - flags &= ~MS_CMDS; - if (!build_mnt_flags(flagsbuf, PATH_MAX, flags, inv_flags)) - goto fail; - vec[3] = flagsbuf; - - if (entry->opts) - allow = AA_MATCH_CONT; - else - allow = entry->allow; - - /* rule for match without required data || data MATCH_CONT */ - if (!aare_add_rule_vec(dfarules, entry->deny, allow, - entry->audit | AA_AUDIT_MNT_DATA, 4, - vec, dfaflags)) - goto fail; - count++; - - if (entry->opts) { - /* rule with data match required */ - optsbuf.clear(); - if (!build_mnt_opts(optsbuf, entry->opts)) - goto fail; - vec[4] = optsbuf.c_str(); - if (!aare_add_rule_vec(dfarules, entry->deny, - entry->allow, - entry->audit | AA_AUDIT_MNT_DATA, - 5, vec, dfaflags)) - goto fail; - count++; - } - } - if (entry->allow & AA_MAY_UMOUNT) { - /* rule class single byte header */ - mntbuf.assign(class_mount_hdr); - if (!convert_entry(mntbuf, entry->mnt_point)) - goto fail; - vec[0] = mntbuf.c_str(); - if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow, - entry->audit, 1, vec, dfaflags)) - goto fail; - count++; - } - if (entry->allow & AA_MAY_PIVOTROOT) { - /* rule class single byte header */ - mntbuf.assign(class_mount_hdr); - if (!convert_entry(mntbuf, entry->mnt_point)) - goto fail; - vec[0] = mntbuf.c_str(); - if (!clear_and_convert_entry(devbuf, entry->device)) - goto fail; - vec[1] = devbuf.c_str(); - if (!aare_add_rule_vec(dfarules, entry->deny, entry->allow, - entry->audit, 2, vec, dfaflags)) - goto fail; - count++; - } - - if (!count) - /* didn't actually encode anything */ - goto fail; - - return TRUE; - -fail: - PERROR("Enocoding of mount rule failed\n"); - return FALSE; -} - - -static int process_dbus_entry(aare_ruleset_t *dfarules, struct dbus_entry *entry) -{ - std::string busbuf; - std::string namebuf; - std::string peer_labelbuf; - std::string pathbuf; - std::string ifacebuf; - std::string memberbuf; - std::ostringstream buffer; - const char *vec[6]; - - pattern_t ptype; - int pos; - - if (!entry) /* shouldn't happen */ - return TRUE; - - buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_DBUS; - busbuf.append(buffer.str()); - - if (entry->bus) { - ptype = convert_aaregex_to_pcre(entry->bus, 0, busbuf, &pos); - if (ptype == ePatternInvalid) - goto fail; - } else { - /* match any char except \000 0 or more times */ - busbuf.append(default_match_pattern); - } - vec[0] = busbuf.c_str(); - - if (entry->name) { - ptype = convert_aaregex_to_pcre(entry->name, 0, namebuf, &pos); - if (ptype == ePatternInvalid) - goto fail; - vec[1] = namebuf.c_str(); - } else { - /* match any char except \000 0 or more times */ - vec[1] = default_match_pattern; - } - - if (entry->peer_label) { - ptype = convert_aaregex_to_pcre(entry->peer_label, 0, - peer_labelbuf, &pos); - if (ptype == ePatternInvalid) - goto fail; - vec[2] = peer_labelbuf.c_str(); - } else { - /* match any char except \000 0 or more times */ - vec[2] = default_match_pattern; - } - - if (entry->path) { - ptype = convert_aaregex_to_pcre(entry->path, 0, pathbuf, &pos); - if (ptype == ePatternInvalid) - goto fail; - vec[3] = pathbuf.c_str(); - } else { - /* match any char except \000 0 or more times */ - vec[3] = default_match_pattern; - } - - if (entry->interface) { - ptype = convert_aaregex_to_pcre(entry->interface, 0, ifacebuf, &pos); - if (ptype == ePatternInvalid) - goto fail; - vec[4] = ifacebuf.c_str(); - } else { - /* match any char except \000 0 or more times */ - vec[4] = default_match_pattern; - } - - if (entry->member) { - ptype = convert_aaregex_to_pcre(entry->member, 0, memberbuf, &pos); - if (ptype == ePatternInvalid) - goto fail; - vec[5] = memberbuf.c_str(); - } else { - /* match any char except \000 0 or more times */ - vec[5] = default_match_pattern; - } - - if (entry->mode & AA_DBUS_BIND) { - if (!aare_add_rule_vec(dfarules, entry->deny, - entry->mode & AA_DBUS_BIND, - entry->audit & AA_DBUS_BIND, - 2, vec, dfaflags)) - goto fail; - } - if (entry->mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE)) { - if (!aare_add_rule_vec(dfarules, entry->deny, - entry->mode & (AA_DBUS_SEND | AA_DBUS_RECEIVE), - entry->audit & (AA_DBUS_SEND | AA_DBUS_RECEIVE), - 6, vec, dfaflags)) - goto fail; - } - if (entry->mode & AA_DBUS_EAVESDROP) { - if (!aare_add_rule_vec(dfarules, entry->deny, - entry->mode & AA_DBUS_EAVESDROP, - entry->audit & AA_DBUS_EAVESDROP, - 1, vec, dfaflags)) - goto fail; - } - return TRUE; - -fail: - return FALSE; -} - -static int post_process_mnt_ents(Profile *prof) -{ - int ret = TRUE; - int count = 0; - - /* Add fns for rules that should be added to policydb here */ - if (prof->mnt_ents && kernel_supports_mount) { - struct mnt_entry *entry; - list_for_each(prof->mnt_ents, entry) { - if (!process_mnt_entry(prof->policy.rules, entry)) - ret = FALSE; - count++; - } - } else if (prof->mnt_ents && !kernel_supports_mount) - pwarn("profile %s mount rules not enforced\n", prof->name); - - prof->policy.count += count; - - return ret; -} - -static int post_process_dbus_ents(Profile *prof) -{ - int ret = TRUE; - int count = 0; - - if (prof->dbus_ents && kernel_supports_dbus) { - struct dbus_entry *entry; - - list_for_each(prof->dbus_ents, entry) { - if (!process_dbus_entry(prof->policy.rules, entry)) - ret = FALSE; - count++; - } - } else if (prof->dbus_ents && !kernel_supports_dbus) - pwarn("profile %s dbus rules not enforced\n", prof->name); - - prof->policy.count += count; - return ret; -} - int post_process_policydb_ents(Profile *prof) { - if (!post_process_mnt_ents(prof)) - return FALSE; - if (!post_process_dbus_ents(prof)) - return FALSE; + for (RuleList::iterator i = prof->rule_ents.begin(); i != prof->rule_ents.end(); i++) { + if ((*i)->gen_policy_re(*prof) == RULE_ERROR) + return FALSE; + } return TRUE; } diff --git a/parser/parser_variable.c b/parser/parser_variable.c index 1e76258aa..d7939763b 100644 --- a/parser/parser_variable.c +++ b/parser/parser_variable.c @@ -15,6 +15,7 @@ * along with this program; if not, contact Novell, Inc. */ +#include #include #include #include @@ -213,13 +214,15 @@ static int expand_by_alternations(struct set_value **valuelist, } /* doesn't handle variables in options atm */ -static int expand_entry_variables(char **name, void *entry) +int expand_entry_variables(char **name) { struct set_value *valuelist; struct var_string *split_var; int ret; - if (!entry) /* can happen when entry is optional */ + assert(name); + + if (!*name) /* can happen when entry is optional */ return 0; while ((split_var = split_out_var(*name))) { @@ -251,7 +254,7 @@ static int process_variables_in_entries(struct cod_entry *entry_list) struct cod_entry *entry; list_for_each(entry_list, entry) { - error = expand_entry_variables(&entry->name, entry); + error = expand_entry_variables(&entry->name); if (error) return error; } @@ -259,69 +262,24 @@ static int process_variables_in_entries(struct cod_entry *entry_list) return 0; } -/* does not currently support expansion of vars in options */ -static int process_variables_in_mnt_entries(struct mnt_entry *entry_list) +static int process_variables_in_rules(Profile &prof) { - int error = 0; - struct mnt_entry *entry; - - list_for_each(entry_list, entry) { - error = expand_entry_variables(&entry->mnt_point, entry); + for (RuleList::iterator i = prof.rule_ents.begin(); i != prof.rule_ents.end(); i++) { + int error = (*i)->expand_variables(); if (error) return error; - error = expand_entry_variables(&entry->device, entry); - if (error) - return error; - error = expand_entry_variables(&entry->trans, entry); - if (error) - return error; - } return 0; } -static int process_dbus_variables(struct dbus_entry *entry_list) -{ - int error = 0; - struct dbus_entry *entry; - - list_for_each(entry_list, entry) { - error = expand_entry_variables(&entry->bus, entry); - if (error) - return error; - error = expand_entry_variables(&entry->name, entry); - if (error) - return error; - error = expand_entry_variables(&entry->peer_label, entry); - if (error) - return error; - error = expand_entry_variables(&entry->path, entry); - if (error) - return error; - error = expand_entry_variables(&entry->interface, entry); - if (error) - return error; - error = expand_entry_variables(&entry->member, entry); - if (error) - return error; - - } - - return 0; -} int process_profile_variables(Profile *prof) { int error = 0; - - error = process_variables_in_entries(prof->entries); - - if (!error) - error = process_variables_in_mnt_entries(prof->mnt_ents); - - if (!error) - error = process_dbus_variables(prof->dbus_ents); + error = process_variables_in_entries(prof->entries); + if (!error) + error = process_variables_in_rules(*prof); return error; } diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y index a62956a60..27cfa10d1 100644 --- a/parser/parser_yacc.y +++ b/parser/parser_yacc.y @@ -72,11 +72,11 @@ int parser_token = 0; struct cod_entry *do_file_rule(char *ns, char *id, int mode, char *link_id, char *nt); -struct mnt_entry *do_mnt_rule(struct cond_entry *src_conds, char *src, - struct cond_entry *dst_conds, char *dst, - int mode); -struct mnt_entry *do_pivot_rule(struct cond_entry *old, char *root, - char *transition); +mnt_rule *do_mnt_rule(struct cond_entry *src_conds, char *src, + struct cond_entry *dst_conds, char *dst, + int mode); +mnt_rule *do_pivot_rule(struct cond_entry *old, char *root, + char *transition); void add_local_entry(Profile *prof); @@ -162,6 +162,12 @@ void add_local_entry(Profile *prof); /* debug flag values */ %token TOK_FLAGS +%code requires { + #include "profile.h" + #include "mount.h" + #include "dbus.h" +} + %union { char *id; char *flag_id; @@ -170,8 +176,9 @@ void add_local_entry(Profile *prof); Profile *prof; struct cod_net_entry *net_entry; struct cod_entry *user_entry; - struct mnt_entry *mnt_entry; - struct dbus_entry *dbus_entry; + + mnt_rule *mnt_entry; + dbus_rule *dbus_entry; flagvals flags; int fmode; @@ -281,7 +288,7 @@ profile_base: TOK_ID opt_id flags TOK_OPEN rules TOK_CLOSE prof->flags.complain = 1; post_process_file_entries(prof); - post_process_mnt_entries(prof); + post_process_rule_entries(prof); PDEBUG("%s: flags='%s%s'\n", $2, prof->flags.complain ? "complain, " : "", @@ -667,8 +674,8 @@ rules: rules opt_prefix mnt_rule } else if ($2.audit) { $3->audit = $3->allow; } - $3->next = $1->mnt_ents; - $1->mnt_ents = $3; + + $1->rule_ents.push_back($3); $$ = $1; } @@ -684,8 +691,7 @@ rules: rules opt_prefix dbus_rule } else if ($2.audit) { $3->audit = $3->mode; } - $3->next = $1->dbus_ents; - $1->dbus_ents = $3; + $1->rule_ents.push_back($3); $$ = $1; } @@ -1196,9 +1202,9 @@ opt_dbus_perm: { /* nothing */ $$ = 0; } dbus_rule: TOK_DBUS opt_dbus_perm opt_conds opt_cond_list TOK_END_OF_RULE { - struct dbus_entry *ent; + dbus_rule *ent; - ent = new_dbus_entry($2, $3, $4); + ent = new dbus_rule($2, $3, $4); if (!ent) { yyerror(_("Memory allocation error.")); } @@ -1366,12 +1372,10 @@ int verify_mnt_conds(struct cond_entry *conds, int src) return error; } -struct mnt_entry *do_mnt_rule(struct cond_entry *src_conds, char *src, - struct cond_entry *dst_conds, char *dst, - int mode) +mnt_rule *do_mnt_rule(struct cond_entry *src_conds, char *src, + struct cond_entry *dst_conds, char *dst, + int mode) { - struct mnt_entry *ent; - if (verify_mnt_conds(src_conds, MNT_SRC_OPT) != 0) yyerror(_("bad mount rule")); @@ -1382,7 +1386,7 @@ struct mnt_entry *do_mnt_rule(struct cond_entry *src_conds, char *src, if (dst_conds) yyerror(_("mount point conditions not currently supported")); - ent = new_mnt_entry(src_conds, src, dst_conds, dst, mode); + mnt_rule *ent = new mnt_rule(src_conds, src, dst_conds, dst, mode); if (!ent) { yyerror(_("Memory allocation error.")); } @@ -1390,10 +1394,8 @@ struct mnt_entry *do_mnt_rule(struct cond_entry *src_conds, char *src, return ent; } -struct mnt_entry *do_pivot_rule(struct cond_entry *old, char *root, - char *transition) +mnt_rule *do_pivot_rule(struct cond_entry *old, char *root, char *transition) { - struct mnt_entry *ent = NULL; char *device = NULL; if (old) { if (strcmp(old->name, "oldroot") != 0) @@ -1405,8 +1407,7 @@ struct mnt_entry *do_pivot_rule(struct cond_entry *old, char *root, free_cond_entry(old); } - ent = new_mnt_entry(NULL, device, NULL, root, - AA_MAY_PIVOTROOT); + mnt_rule *ent = new mnt_rule(NULL, device, NULL, root, AA_MAY_PIVOTROOT); ent->trans = transition; return ent; diff --git a/parser/profile.cc b/parser/profile.cc index 62da9939d..ce8d69e72 100644 --- a/parser/profile.cc +++ b/parser/profile.cc @@ -13,6 +13,7 @@ */ #include "profile.h" +#include "rule.h" #include #include @@ -62,8 +63,9 @@ Profile::~Profile() { hat_table.clear(); free_cod_entries(entries); - free_mnt_entries(mnt_ents); - free_dbus_entries(dbus_ents); + + for (RuleList::iterator i = rule_ents.begin(); i != rule_ents.end(); i++) + delete *i; if (dfa.rules) aare_delete_ruleset(dfa.rules); if (dfa.dfa) diff --git a/parser/profile.h b/parser/profile.h index 1b9dea796..5bb45ec0d 100644 --- a/parser/profile.h +++ b/parser/profile.h @@ -17,6 +17,7 @@ #include #include "parser.h" +#include "rule.h" class Profile; @@ -153,8 +154,7 @@ public: char *exec_table[AA_EXEC_COUNT]; struct cod_entry *entries; - struct mnt_entry *mnt_ents; - struct dbus_entry *dbus_ents; + RuleList rule_ents; ProfileList hat_table; @@ -179,9 +179,6 @@ public: std::fill(exec_table, exec_table + AA_EXEC_COUNT, (char *)NULL); entries = NULL; - mnt_ents = NULL; - dbus_ents = NULL; - }; virtual ~Profile();