2023-03-20 12:28:53 -03:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2023
|
|
|
|
* 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
|
2023-09-21 20:39:27 -07:00
|
|
|
* along with this program; if not, contact Canonical Ltd.
|
2023-03-20 12:28:53 -03:00
|
|
|
*/
|
|
|
|
|
2023-07-05 04:33:14 -07:00
|
|
|
#include "common_optarg.h"
|
2023-03-20 12:28:53 -03:00
|
|
|
#include "parser.h"
|
|
|
|
#include "profile.h"
|
|
|
|
#include "io_uring.h"
|
|
|
|
|
|
|
|
#include <iomanip>
|
|
|
|
#include <string>
|
|
|
|
#include <iostream>
|
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
void io_uring_rule::move_conditionals(struct cond_entry *conds)
|
|
|
|
{
|
|
|
|
struct cond_entry *cond_ent;
|
|
|
|
|
|
|
|
list_for_each(conds, cond_ent) {
|
|
|
|
/* disallow keyword 'in' (list) */
|
|
|
|
if (!cond_ent->eq)
|
|
|
|
yyerror("keyword \"in\" is not allowed in io_uring rules\n");
|
|
|
|
|
|
|
|
if (list_len(cond_ent->vals) > 1)
|
|
|
|
yyerror("io_uring conditional \"%s\" only supports a single value\n",
|
|
|
|
cond_ent->name);
|
|
|
|
|
|
|
|
if (strcmp(cond_ent->name, "label") == 0) {
|
|
|
|
move_conditional_value("io_uring", &label, cond_ent);
|
|
|
|
} else {
|
|
|
|
yyerror("invalid io_uring conditional \"%s\"\n",
|
|
|
|
cond_ent->name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-02 02:07:36 -07:00
|
|
|
io_uring_rule::io_uring_rule(perm32_t perms_p, struct cond_entry *conds, struct cond_entry *ring_conds):
|
2023-03-20 12:28:53 -03:00
|
|
|
perms_rule_t(AA_CLASS_IO_URING), label(NULL)
|
|
|
|
{
|
|
|
|
if (perms_p) {
|
|
|
|
if (perms_p & ~AA_VALID_IO_URING_PERMS) {
|
|
|
|
yyerror("perms contains invalid permissions for io_uring\n");
|
|
|
|
}
|
|
|
|
perms = perms_p;
|
|
|
|
|
|
|
|
} else {
|
|
|
|
/* default to all perms */
|
|
|
|
perms = AA_VALID_IO_URING_PERMS;
|
|
|
|
}
|
|
|
|
move_conditionals(conds);
|
|
|
|
move_conditionals(ring_conds);
|
|
|
|
free_cond_list(conds);
|
|
|
|
free_cond_list(ring_conds);
|
|
|
|
}
|
|
|
|
|
|
|
|
ostream &io_uring_rule::dump(ostream &os)
|
|
|
|
{
|
|
|
|
class_rule_t::dump(os);
|
|
|
|
|
|
|
|
if (perms != AA_VALID_IO_URING_PERMS) {
|
|
|
|
os << " ( ";
|
|
|
|
|
|
|
|
if (perms & AA_IO_URING_OVERRIDE_CREDS)
|
|
|
|
os << "override_creds ";
|
|
|
|
if (perms & AA_IO_URING_SQPOLL)
|
|
|
|
os << " sqpoll ";
|
|
|
|
|
|
|
|
os << ")";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (label)
|
|
|
|
os << " label=" << label;
|
|
|
|
|
|
|
|
os << ",\n";
|
|
|
|
|
|
|
|
return os;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int io_uring_rule::expand_variables(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void io_uring_rule::warn_once(const char *name)
|
|
|
|
{
|
|
|
|
rule_t::warn_once(name, "io_uring rules not enforced");
|
|
|
|
}
|
|
|
|
|
|
|
|
int io_uring_rule::gen_policy_re(Profile &prof)
|
|
|
|
{
|
|
|
|
std::ostringstream buffer;
|
|
|
|
std::string buf, labelbuf;
|
|
|
|
|
|
|
|
if (!features_supports_io_uring) {
|
|
|
|
warn_once(prof.name);
|
|
|
|
return RULE_NOT_SUPPORTED;
|
|
|
|
}
|
|
|
|
|
|
|
|
buffer << "\\x" << std::setfill('0') << std::setw(2) << std::hex << AA_CLASS_IO_URING;
|
|
|
|
buf = buffer.str();
|
|
|
|
|
|
|
|
if (label) {
|
|
|
|
if (!convert_entry(labelbuf, label))
|
|
|
|
goto fail;
|
|
|
|
buffer << labelbuf;
|
|
|
|
} else {
|
|
|
|
buffer << default_match_pattern;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (perms & AA_VALID_IO_URING_PERMS) {
|
2024-05-10 03:06:22 -07:00
|
|
|
if (!prof.policy.rules->add_rule(buf.c_str(), priority,
|
|
|
|
rule_mode, perms,
|
|
|
|
audit == AUDIT_FORCE ? perms : 0,
|
|
|
|
parseopts))
|
2023-03-20 12:28:53 -03:00
|
|
|
goto fail;
|
parser: fix rule priority destroying rule permissions for some classes
io_uring and userns mediation are encoding permissions on the class
byte. This is a mistake that should never have been allowed.
With the addition of rule priorities the class byte mediates rule,
that ensure the kernel can determine a class is being mediated is
given the highest priority possible, to ensure class mediation can not
be removed by a deny rule. See
61b7568e1 ("parser: bug fix mediates_X stub rules.")
for details.
Unfortunately this breaks rule classes that encode permissions on the
class byte, because those rules will always have a lower priority and
the class mediates rule will always be selected over them resulting in
only the class mediates permission being on the rule class state.
Fix this by adding the mediaties class rules for these rule classes
with the lowest priority possible. This means that any rule mediating
the class will wipe out the mediates class rule. So add a new mediates
class rule at the same priority, as the rule being added.
This is a naive implementation and does result in more mediates rules
being added than necessary. The rule class could keep track of the
highest priority rule that had been added, and use that to reduce the
number of mediates rules it adds for the class.
Technically we could also get away with not adding the rules for allow
rules, as the kernel doesn't actually check the encoded permission but
whether the class state is not the trap state. But it is required with
deny rules to ensure the deny rule doesn't result in permissions being
removed from the class, resulting in the kernel thinking it is
unmediated. We also want to ensure that mediation is encoded for other
rule types like prompt, and in the future the kernel could check the
permission so we do want to guarantee that the class state has the
MAY_READ permission on it.
Note: there is another set of classes (file, mqueue, dbus, ...) which
encodes a default rule permission as
class .* <perm>
this encoding is unfortunate in that it will also add the permission
to the class byte, but also sets up following states with the permission.
thankfully, this accespt anything, including nothing generally isn't
valid in the nothing case (eg. a file without any absolute name). For
this set of classes, the high priority mediates rule just ensures
that the null match case does not have permission.
Fixes: 61b7568e1 parser: bug fix mediates_X stub rules.
Signed-off-by: John Johansen <john.johansen@canonical.com>
2024-08-15 03:51:20 -07:00
|
|
|
/* add a mediates_io_uring rule for every rule added. It
|
|
|
|
* needs to be the same priority
|
|
|
|
*/
|
|
|
|
if (!prof.policy.rules->add_rule(buf.c_str(), priority,
|
|
|
|
RULE_ALLOW, AA_MAY_READ, 0,
|
|
|
|
parseopts))
|
|
|
|
goto fail;
|
2023-03-20 12:28:53 -03:00
|
|
|
|
|
|
|
if (perms & AA_IO_URING_OVERRIDE_CREDS) {
|
|
|
|
buf = buffer.str(); /* update buf to have label */
|
2024-05-10 03:06:22 -07:00
|
|
|
if (!prof.policy.rules->add_rule(buf.c_str(),
|
|
|
|
priority, rule_mode,
|
|
|
|
perms, audit == AUDIT_FORCE ? perms : 0,
|
|
|
|
parseopts))
|
2023-03-20 12:28:53 -03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return RULE_OK;
|
|
|
|
fail:
|
|
|
|
return RULE_ERROR;
|
|
|
|
}
|