/* * Copyright (c) 2014 * 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. */ #include #include #include #include #include #include #include #include "parser.h" #include "profile.h" #include "parser_yacc.h" #include "signal.h" #define MAXMAPPED_SIG 35 #define MINRT_SIG 128 /* base of RT sigs */ #define MAXRT_SIG 32 /* Max RT above MINRT_SIG */ /* Signal names mapped to and internal ordering */ static struct signal_map { const char *name; int num; } signal_map[] = { {"hup", 1}, {"int", 2}, {"quit", 3}, {"ill", 4}, {"trap", 5}, {"abrt", 6}, {"bus", 7}, {"fpe", 8}, {"kill", 9}, {"usr1", 10}, {"segv", 11}, {"usr2", 12}, {"pipe", 13}, {"alrm", 14}, {"term", 15}, {"stkflt", 16}, {"chld", 17}, {"cont", 18}, {"stop", 19}, {"stp", 20}, {"ttin", 21}, {"ttou", 22}, {"urg", 23}, {"xcpu", 24}, {"xfsz", 25}, {"vtalrm", 26}, {"prof", 27}, {"winch", 28}, {"io", 29}, {"pwr", 30}, {"sys", 31}, {"emt", 32}, {"exists", 35}, /* terminate */ {NULL, 0} }; /* this table is ordered post sig_map[sig] mapping */ static const char *const sig_names[MAXMAPPED_SIG + 1] = { "unknown", "hup", "int", "quit", "ill", "trap", "abrt", "bus", "fpe", "kill", "usr1", "segv", "usr2", "pipe", "alrm", "term", "stkflt", "chld", "cont", "stop", "stp", "ttin", "ttou", "urg", "xcpu", "xfsz", "vtalrm", "prof", "winch", "io", "pwr", "sys", "emt", "lost", "unused", "exists", /* always last existence test mapped to MAXMAPPED_SIG */ }; int parse_signal_perms(const char *str_perms, perm32_t *perms, int fail) { return parse_X_perms("signal", AA_VALID_SIGNAL_PERMS, str_perms, perms, fail); } int find_signal_mapping(const char *sig) { if (strncmp("rtmin+", sig, 6) == 0) { char *end; unsigned long n = strtoul(sig + 6, &end, 10); if (end == sig || n > MAXRT_SIG) return -1; return MINRT_SIG + n; } else { for (int i = 0; signal_map[i].name; i++) { if (strcmp(sig, signal_map[i].name) == 0) return signal_map[i].num; } } return -1; } void signal_rule::extract_sigs(struct value_list **list) { struct value_list *entry, *tmp, *prev = NULL; list_for_each_safe(*list, entry, tmp) { int i = find_signal_mapping(entry->value); if (i != -1) { signals.insert(i); list_remove_at(*list, prev, entry); free_value_list(entry); } else { yyerror("unknown signal \"%s\"\n", entry->value); prev = entry; } } } void signal_rule::move_conditionals(struct cond_entry *conds) { struct cond_entry *cond_ent; list_for_each(conds, cond_ent) { /* for now disallow keyword 'in' (list) */ if (!cond_ent->eq) yyerror("keyword \"in\" is not allowed in signal rules\n"); if (strcmp(cond_ent->name, "set") == 0) { extract_sigs(&cond_ent->vals); } else if (strcmp(cond_ent->name, "peer") == 0) { move_conditional_value("signal", &peer_label, cond_ent); } else { yyerror("invalid signal rule conditional \"%s\"\n", cond_ent->name); } } } signal_rule::signal_rule(perm32_t perms_p, struct cond_entry *conds): perms_rule_t(AA_CLASS_SIGNAL), signals(), peer_label(NULL) { if (perms_p) { perms = perms_p; if (perms & ~AA_VALID_SIGNAL_PERMS) yyerror("perms contains invalid permission for signals\n"); } else { perms = AA_VALID_SIGNAL_PERMS; } move_conditionals(conds); free_cond_list(conds); } ostream &signal_rule::dump(ostream &os) { class_rule_t::dump(os); if (perms != AA_VALID_SIGNAL_PERMS) { os << " ("; if (perms & AA_MAY_SEND) os << "send "; if (perms & AA_MAY_RECEIVE) os << "receive "; os << ")"; } if (!signals.empty()) { os << " set=("; for (Signals::iterator i = signals.begin(); i != signals.end(); i++) { if (i != signals.begin()) os << ", "; if (*i < MINRT_SIG) os << sig_names[*i]; else os << "rtmin+" << (*i - MINRT_SIG); } os << ")"; } if (peer_label) os << " " << peer_label; os << ",\n"; return os; } int signal_rule::expand_variables(void) { return expand_entry_variables(&peer_label); } static int cmp_set_int(Signals const &lhs, Signals const &rhs) { int res = lhs.size() - rhs.size(); if (res) return res; for (Signals::iterator i = lhs.begin(), j = rhs.begin(); i != lhs.end(); i++, j++) { res = *i - *j; if (res) return res; } return 0; } int signal_rule::cmp(rule_t const &rhs) const { int res = perms_rule_t::cmp(rhs); if (res) return res; signal_rule const &trhs = rule_cast(rhs); res = null_strcmp(peer_label, trhs.peer_label); if (res) return res; return cmp_set_int(signals, trhs.signals); } void signal_rule::warn_once(const char *name) { rule_t::warn_once(name, "signal rules not enforced"); } int signal_rule::gen_policy_re(Profile &prof) { std::ostringstream buffer; std::string buf; pattern_t ptype; int pos; /* Currently do not generate the rules if the kernel doesn't support * it. We may want to switch this so that a compile could be * used for full support on kernels that don't support the feature */ if (!features_supports_signal) { warn_once(prof.name); return RULE_NOT_SUPPORTED; } if (signals.size() == 0) { /* not conditional on signal set, so will generate a label * rule as well */ buffer << "(" << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_LABEL << "\\x" << AA_CLASS_SIGNAL << "|"; } buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_SIGNAL; if (signals.size()) { buffer << "["; for (Signals::iterator i = signals.begin(); i != signals.end(); i++) { buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << *i; } buffer << "]"; } else { /* match any char */ buffer << "."; } if (signals.size() == 0) { /* close alternation */ buffer << ")"; } if (peer_label) { ptype = convert_aaregex_to_pcre(peer_label, 0, glob_default, buf, &pos); if (ptype == ePatternInvalid) goto fail; buffer << buf; } else { buffer << anyone_match_pattern; } buf = buffer.str(); if (perms & (AA_MAY_SEND | AA_MAY_RECEIVE)) { if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode, perms, audit == AUDIT_FORCE ? perms : 0, parseopts)) goto fail; } return RULE_OK; fail: return RULE_ERROR; }