diff --git a/.gitignore b/.gitignore index bac42bf27..b7c913981 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,7 @@ parser/ptrace.o parser/rule.o parser/signal.o parser/userns.o +parser/io_uring.o parser/*.7 parser/*.5 parser/*.8 diff --git a/parser/Makefile b/parser/Makefile index c91794dd2..3280ce528 100644 --- a/parser/Makefile +++ b/parser/Makefile @@ -102,11 +102,11 @@ SRCS = parser_common.c parser_include.c parser_interface.c parser_lex.c \ parser_alias.c common_optarg.c lib.c network.c \ mount.cc dbus.cc profile.cc rule.cc signal.cc ptrace.cc \ af_rule.cc af_unix.cc policy_cache.c default_features.c userns.cc \ - mqueue.cc + mqueue.cc io_uring.cc STATIC_HDRS = af_rule.h af_unix.h capability.h common_optarg.h dbus.h \ file_cache.h immunix.h lib.h mount.h network.h parser.h \ parser_include.h parser_version.h policy_cache.h policydb.h \ - profile.h ptrace.h rule.h signal.h userns.h mqueue.h + profile.h ptrace.h rule.h signal.h userns.h mqueue.h io_uring.h SPECIAL_HDRS = parser_yacc.h unit_test.h base_cap_names.h GENERATED_HDRS = af_names.h generated_af_names.h \ @@ -318,6 +318,9 @@ userns.o: userns.cc $(HDRS) mqueue.o: mqueue.cc $(HDRS) $(CXX) $(EXTRA_CFLAGS) -c -o $@ $< +io_uring.o: io_uring.cc $(HDRS) + $(CXX) $(EXTRA_CFLAGS) -c -o $@ $< + parser_version.h: Makefile @echo \#define PARSER_VERSION \"$(VERSION)\" > .ver @mv -f .ver $@ diff --git a/parser/io_uring.cc b/parser/io_uring.cc new file mode 100644 index 000000000..aef4b8899 --- /dev/null +++ b/parser/io_uring.cc @@ -0,0 +1,141 @@ +/* + * 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 + * along with this program; if not, contact or Canonical Ltd. + */ + +#include "parser.h" +#include "profile.h" +#include "io_uring.h" + +#include +#include +#include +#include + +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); + } + } +} + +io_uring_rule::io_uring_rule(perms_t perms_p, struct cond_entry *conds, struct cond_entry *ring_conds): + 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) { + if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, perms, + audit == AUDIT_FORCE ? perms : 0, + dfaflags)) + goto fail; + + if (perms & AA_IO_URING_OVERRIDE_CREDS) { + buf = buffer.str(); /* update buf to have label */ + if (!prof.policy.rules->add_rule(buf.c_str(), rule_mode == RULE_DENY, + perms, audit == AUDIT_FORCE ? perms : 0, + dfaflags)) + goto fail; + } + + } + return RULE_OK; +fail: + return RULE_ERROR; +} diff --git a/parser/io_uring.h b/parser/io_uring.h new file mode 100644 index 000000000..7644403c7 --- /dev/null +++ b/parser/io_uring.h @@ -0,0 +1,56 @@ +/* + * 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 + * along with this program; if not, contact or Canonical Ltd. + */ + +#ifndef __AA_IO_URING_H +#define __AA_IO_URING_H + +#include "parser.h" + +#define AA_IO_URING_OVERRIDE_CREDS AA_MAY_APPEND +#define AA_IO_URING_SQPOLL AA_MAY_CREATE + +#define AA_VALID_IO_URING_PERMS (AA_IO_URING_OVERRIDE_CREDS | \ + AA_IO_URING_SQPOLL) + +class io_uring_rule: public perms_rule_t { + void move_conditionals(struct cond_entry *conds); +public: + char *label; + + io_uring_rule(perms_t perms, struct cond_entry *conds, struct cond_entry *ring_conds); + virtual ~io_uring_rule() + { + free(label); + }; + + virtual bool valid_prefix(const prefixes &p, const char *&error) { + if (p.owner) { + error = _("owner prefix not allowed on io_uring rules"); + return false; + } + return true; + }; + + virtual ostream &dump(ostream &os); + virtual int expand_variables(void); + virtual int gen_policy_re(Profile &prof); + +protected: + virtual void warn_once(const char *name) override; +}; + +#endif /* __AA_IO_URING_H */ diff --git a/parser/parser.h b/parser/parser.h index a115ff92a..0399db8be 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -353,6 +353,7 @@ extern int features_supports_domain_xattr; extern int features_supports_userns; extern int features_supports_posix_mqueue; extern int features_supports_sysv_mqueue; +extern int features_supports_io_uring; extern int kernel_supports_oob; extern int conf_verbose; extern int conf_quiet; diff --git a/parser/parser_common.c b/parser/parser_common.c index 3f798ecfc..75b637e19 100644 --- a/parser/parser_common.c +++ b/parser/parser_common.c @@ -81,6 +81,7 @@ int features_supports_domain_xattr = 0; /* x attachment cond */ int features_supports_userns = 0; /* kernel supports user namespace */ int features_supports_posix_mqueue = 0; /* kernel supports mqueue rules */ int features_supports_sysv_mqueue = 0; /* kernel supports mqueue rules */ +int features_supports_io_uring = 0; /* kernel supports io_uring rules */ int kernel_supports_oob = 0; /* out of band transitions */ int conf_verbose = 0; int conf_quiet = 0; diff --git a/parser/parser_lex.l b/parser/parser_lex.l index 1a07ca91c..8abc47b4c 100644 --- a/parser/parser_lex.l +++ b/parser/parser_lex.l @@ -329,6 +329,7 @@ GT > %x ABI_MODE %x USERNS_MODE %x MQUEUE_MODE +%x IOURING_MODE %% @@ -341,7 +342,7 @@ GT > } %} -{ +{ {WS}+ { DUMP_PREPROCESS; /* Ignoring whitespace */ } } @@ -615,7 +616,12 @@ GT > {ARROW} { RETURN_TOKEN(TOK_ARROW); } } -{ +{ + override_creds { RETURN_TOKEN(TOK_OVERRIDE_CREDS); } + sqpoll { RETURN_TOKEN(TOK_SQPOLL); } +} + +{ ({IDS_NOEQ}|{LABEL}|{QUOTED_ID}) { yylval.id = processid(yytext, yyleng); RETURN_TOKEN(TOK_ID); @@ -749,7 +755,7 @@ include/{WS} { PUSH_AND_RETURN(state, token); } -{ +{ {END_OF_RULE} { if (YY_START != INITIAL) POP_NODUMP(); @@ -757,14 +763,14 @@ include/{WS} { } } -{ +{ \r?\n { DUMP_PREPROCESS; current_lineno++; } } -{ +{ (.|\n) { DUMP_PREPROCESS; /* Something we didn't expect */ @@ -801,4 +807,5 @@ unordered_map state_names = { STATE_TABLE_ENT(ABI_MODE), STATE_TABLE_ENT(USERNS_MODE), STATE_TABLE_ENT(MQUEUE_MODE), + STATE_TABLE_ENT(IOURING_MODE), }; diff --git a/parser/parser_main.c b/parser/parser_main.c index b1731bed8..64cbe4654 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -950,6 +950,9 @@ void set_supported_features() features_supports_sysv_mqueue = features_intersect(kernel_features, policy_features, "ipc/sysv_mqueue"); + features_supports_io_uring = features_intersect(kernel_features, + policy_features, + "io_uring"); } static bool do_print_cache_dir(aa_features *features, int dirfd, const char *path) diff --git a/parser/parser_misc.c b/parser/parser_misc.c index 960d4977c..651b4ce98 100644 --- a/parser/parser_misc.c +++ b/parser/parser_misc.c @@ -124,6 +124,9 @@ static struct keyword_table keyword_table[] = { {"mqueue", TOK_MQUEUE}, {"delete", TOK_DELETE}, {"open", TOK_OPEN}, + {"io_uring", TOK_IO_URING}, + {"override_creds", TOK_OVERRIDE_CREDS}, + {"sqpoll", TOK_SQPOLL}, /* terminate */ {NULL, 0} diff --git a/parser/parser_regex.c b/parser/parser_regex.c index f5df1635d..db1b775b3 100644 --- a/parser/parser_regex.c +++ b/parser/parser_regex.c @@ -942,6 +942,7 @@ static const char *mediates_net_unix = CLASS_SUB_STR(AA_CLASS_NET, AF_UNIX); static const char *mediates_ns = CLASS_STR(AA_CLASS_NS); static const char *mediates_posix_mqueue = CLASS_STR(AA_CLASS_POSIX_MQUEUE); static const char *mediates_sysv_mqueue = CLASS_STR(AA_CLASS_SYSV_MQUEUE); +static const char *mediates_io_uring = CLASS_STR(AA_CLASS_IO_URING); int process_profile_policydb(Profile *prof) { @@ -993,6 +994,9 @@ int process_profile_policydb(Profile *prof) if (features_supports_sysv_mqueue && !prof->policy.rules->add_rule(mediates_sysv_mqueue, 0, AA_MAY_READ, 0, dfaflags)) goto out; + if (features_supports_io_uring && + !prof->policy.rules->add_rule(mediates_io_uring, 0, AA_MAY_READ, 0, dfaflags)) + goto out; if (prof->policy.rules->rule_count > 0) { int xmatch_len = 0; diff --git a/parser/parser_yacc.y b/parser/parser_yacc.y index 91a4ffaff..ca1105ace 100644 --- a/parser/parser_yacc.y +++ b/parser/parser_yacc.y @@ -146,6 +146,9 @@ bool check_x_qualifier(struct cod_entry *entry, const char *&errror); %token TOK_USERNS %token TOK_MQUEUE %token TOK_DELETE +%token TOK_IO_URING +%token TOK_OVERRIDE_CREDS +%token TOK_SQPOLL /* rlimits */ %token TOK_RLIMIT @@ -183,6 +186,7 @@ bool check_x_qualifier(struct cod_entry *entry, const char *&errror); #include "af_unix.h" #include "userns.h" #include "mqueue.h" + #include "io_uring.h" } %union { @@ -201,6 +205,7 @@ bool check_x_qualifier(struct cod_entry *entry, const char *&errror); unix_rule *unix_entry; userns_rule *userns_entry; mqueue_rule *mqueue_entry; + io_uring_rule *io_uring_entry; prefix_rule_t *prefix_entry; flagvals flags; @@ -293,6 +298,10 @@ bool check_x_qualifier(struct cod_entry *entry, const char *&errror); %type mqueue_perms %type opt_mqueue_perm %type mqueue_rule +%type io_uring_perm +%type io_uring_perms +%type opt_io_uring_perm +%type io_uring_rule %% @@ -783,6 +792,7 @@ prefix_rule : mnt_rule { $$ = $1; } | unix_rule { $$ = $1; } | userns_rule { $$ = $1; } | mqueue_rule { $$ = $1; } + | io_uring_rule { $$ = $1; } rules: rules opt_prefix prefix_rule { @@ -1558,6 +1568,38 @@ mqueue_rule: TOK_MQUEUE opt_mqueue_perm opt_conds TOK_END_OF_RULE $$ = ent; } +io_uring_perm: TOK_VALUE + { + if (strcmp($1, "override_creds") == 0) + $$ = AA_IO_URING_OVERRIDE_CREDS; + else if (strcmp($1, "sqpoll") == 0) + $$ = AA_IO_URING_SQPOLL; + else + $$ = 0; + + if ($1) + free($1); + } + | TOK_OVERRIDE_CREDS { $$ = AA_IO_URING_OVERRIDE_CREDS; } + | TOK_SQPOLL { $$ = AA_IO_URING_SQPOLL; } + +io_uring_perms: { /* nothing */ $$ = 0; } + | io_uring_perms io_uring_perm { $$ = $1 | $2; } + | io_uring_perms TOK_COMMA io_uring_perm { $$ = $1 | $3; } + +opt_io_uring_perm: { /* nothing */ $$ = 0; } + | io_uring_perm { $$ = $1; } + | TOK_OPENPAREN io_uring_perms TOK_CLOSEPAREN { $$ = $2; } + +io_uring_rule: TOK_IO_URING opt_io_uring_perm opt_conds opt_cond_list TOK_END_OF_RULE + { + io_uring_rule *ent; + ent = new io_uring_rule($2, $3, $4.list); + if (!ent) + yyerror(_("Memory allocation error.")); + $$ = ent; + } + hat_start: TOK_CARET {} | TOK_HAT {}