mirror of
https://gitlab.com/apparmor/apparmor.git
synced 2025-03-04 08:24:42 +01:00

Add a flag that allows setting the signal used to kill the process. This should not be normally used but can be very useful when debugging applications, interaction with apparmor. Signed-off-by: John Johansen <john.johansen@canonical.com>
328 lines
6.8 KiB
C++
328 lines
6.8 KiB
C++
/*
|
|
* 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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <sys/apparmor.h>
|
|
|
|
#include <iomanip>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <map>
|
|
|
|
#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, perms_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(perms_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<signal_rule const &>(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 == RULE_DENY, perms, audit == AUDIT_FORCE ? perms : 0,
|
|
parseopts))
|
|
goto fail;
|
|
}
|
|
|
|
return RULE_OK;
|
|
|
|
fail:
|
|
return RULE_ERROR;
|
|
}
|