Add support for new nft object secmark holding security context strings. The following should demonstrate its usage (based on SELinux context): # define a tag containing a context string nft add secmark inet filter sshtag \"system_u:object_r:ssh_server_packet_t:s0\" nft list secmarks # set the secmark nft add rule inet filter input tcp dport 22 meta secmark set sshtag # map usage nft add map inet filter secmapping { type inet_service : secmark \; } nft add element inet filter secmapping { 22 : sshtag } nft list maps nft list map inet filter secmapping nft add rule inet filter input meta secmark set tcp dport map @secmapping Based on v0.9.0 Signed-off-by: Christian Göttsche <cgzones@xxxxxxxxxxxxxx> --- include/linux/netfilter/nf_tables.h | 18 ++++- include/rule.h | 9 +++ src/evaluate.c | 5 ++ src/json.c | 10 +++ src/meta.c | 1 + src/netlink.c | 8 +++ src/netlink_delinearize.c | 2 +- src/parser_bison.y | 104 ++++++++++++++++++++++++++-- src/parser_json.c | 19 ++++- src/rule.c | 16 +++++ src/scanner.l | 3 + src/statement.c | 1 + 12 files changed, 189 insertions(+), 7 deletions(-) diff --git a/include/linux/netfilter/nf_tables.h b/include/linux/netfilter/nf_tables.h index 88e0ca1..e834e51 100644 --- a/include/linux/netfilter/nf_tables.h +++ b/include/linux/netfilter/nf_tables.h @@ -1150,6 +1150,21 @@ enum nft_quota_attributes { }; #define NFTA_QUOTA_MAX (__NFTA_QUOTA_MAX - 1) +/** + * enum nft_secmark_attributes - nf_tables secmark expression netlink attributes + * + * @NFTA_SECMARK_CTX: security context (NLA_STRING) + */ +enum nft_secmark_attributes { + NFTA_SECMARK_UNSPEC, + NFTA_SECMARK_CTX, + __NFTA_SECMARK_MAX, +}; +#define NFTA_SECMARK_MAX (__NFTA_SECMARK_MAX - 1) + +/* Max security context length */ +#define NFT_SECMARK_CTX_MAXLEN 256 + /** * enum nft_reject_types - nf_tables reject expression reject types * @@ -1379,7 +1394,8 @@ enum nft_ct_helper_attributes { #define NFT_OBJECT_CT_HELPER 3 #define NFT_OBJECT_LIMIT 4 #define NFT_OBJECT_CONNLIMIT 5 -#define __NFT_OBJECT_MAX 6 +#define NFT_OBJECT_SECMARK 6 +#define __NFT_OBJECT_MAX 7 #define NFT_OBJECT_MAX (__NFT_OBJECT_MAX - 1) /** diff --git a/include/rule.h b/include/rule.h index 909ff36..76ac0ab 100644 --- a/include/rule.h +++ b/include/rule.h @@ -316,6 +316,10 @@ struct limit { uint32_t flags; }; +struct secmark { + char ctx[NFT_SECMARK_CTX_MAXLEN]; +}; + /** * struct obj - nftables stateful object statement * @@ -336,6 +340,7 @@ struct obj { struct quota quota; struct ct_helper ct_helper; struct limit limit; + struct secmark secmark; }; }; @@ -434,6 +439,8 @@ enum cmd_ops { * @CMD_OBJ_LIMIT: limit * @CMD_OBJ_LIMITS: multiple limits * @CMD_OBJ_FLOWTABLES: flow tables + * @CMD_OBJ_SECMARK: secmark + * @CMD_OBJ_SECMARKS: multiple secmarks */ enum cmd_obj { CMD_OBJ_INVALID, @@ -462,6 +469,8 @@ enum cmd_obj { CMD_OBJ_LIMITS, CMD_OBJ_FLOWTABLE, CMD_OBJ_FLOWTABLES, + CMD_OBJ_SECMARK, + CMD_OBJ_SECMARKS, }; struct markup { diff --git a/src/evaluate.c b/src/evaluate.c index c4ee3cc..d7d2775 100644 --- a/src/evaluate.c +++ b/src/evaluate.c @@ -3131,6 +3131,7 @@ static int cmd_evaluate_add(struct eval_ctx *ctx, struct cmd *cmd) case CMD_OBJ_QUOTA: case CMD_OBJ_CT_HELPER: case CMD_OBJ_LIMIT: + case CMD_OBJ_SECMARK: return 0; default: BUG("invalid command object type %u\n", cmd->obj); @@ -3158,6 +3159,7 @@ static int cmd_evaluate_delete(struct eval_ctx *ctx, struct cmd *cmd) case CMD_OBJ_QUOTA: case CMD_OBJ_CT_HELPER: case CMD_OBJ_LIMIT: + case CMD_OBJ_SECMARK: return 0; default: BUG("invalid command object type %u\n", cmd->obj); @@ -3291,12 +3293,15 @@ static int cmd_evaluate_list(struct eval_ctx *ctx, struct cmd *cmd) return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_CT_HELPER); case CMD_OBJ_LIMIT: return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_LIMIT); + case CMD_OBJ_SECMARK: + return cmd_evaluate_list_obj(ctx, cmd, NFT_OBJECT_SECMARK); case CMD_OBJ_COUNTERS: case CMD_OBJ_QUOTAS: case CMD_OBJ_CT_HELPERS: case CMD_OBJ_LIMITS: case CMD_OBJ_SETS: case CMD_OBJ_FLOWTABLES: + case CMD_OBJ_SECMARKS: if (cmd->handle.table.name == NULL) return 0; if (table_lookup(&cmd->handle, ctx->cache) == NULL) diff --git a/src/json.c b/src/json.c index b6e6ca9..a6fe334 100644 --- a/src/json.c +++ b/src/json.c @@ -263,6 +263,12 @@ static json_t *obj_print_json(struct output_ctx *octx, const struct obj *obj) json_object_update(root, tmp); json_decref(tmp); break; + case NFT_OBJECT_SECMARK: + tmp = json_pack("{s:s}", + "context", obj->secmark.ctx); + json_object_update(root, tmp); + json_decref(tmp); + break; case NFT_OBJECT_CT_HELPER: type = "ct helper"; tmp = json_pack("{s:s, s:o, s:s}", @@ -1539,6 +1545,10 @@ int do_command_list_json(struct netlink_ctx *ctx, struct cmd *cmd) case CMD_OBJ_LIMITS: root = do_list_obj_json(ctx, cmd, NFT_OBJECT_LIMIT); break; + case CMD_OBJ_SECMARK: + case CMD_OBJ_SECMARKS: + root = do_list_obj_json(ctx, cmd, NFT_OBJECT_SECMARK); + break; case CMD_OBJ_FLOWTABLES: root = do_list_flowtables_json(ctx, cmd); break; diff --git a/src/meta.c b/src/meta.c index ff0cb12..19066a1 100644 --- a/src/meta.c +++ b/src/meta.c @@ -458,6 +458,7 @@ static bool meta_key_is_qualified(enum nft_meta_keys key) case NFT_META_SECPATH: case NFT_META_BRI_IIFNAME: case NFT_META_BRI_OIFNAME: + case NFT_META_SECMARK: return true; default: return false; diff --git a/src/netlink.c b/src/netlink.c index 864947b..8528011 100644 --- a/src/netlink.c +++ b/src/netlink.c @@ -325,6 +325,10 @@ alloc_nftnl_obj(const struct handle *h, struct obj *obj) nftnl_obj_set_u32(nlo, NFTNL_OBJ_QUOTA_FLAGS, obj->quota.flags); break; + case NFT_OBJECT_SECMARK: + nftnl_obj_set_str(nlo, NFTNL_OBJ_SECMARK_TEXT, + obj->secmark.ctx); + break; case NFT_OBJECT_CT_HELPER: nftnl_obj_set_str(nlo, NFTNL_OBJ_CT_HELPER_NAME, obj->ct_helper.name); @@ -1431,6 +1435,10 @@ struct obj *netlink_delinearize_obj(struct netlink_ctx *ctx, obj->quota.flags = nftnl_obj_get_u32(nlo, NFTNL_OBJ_QUOTA_FLAGS); break; + case NFT_OBJECT_SECMARK: + snprintf(obj->secmark.ctx, sizeof(obj->secmark.ctx), "%s", + nftnl_obj_get_str(nlo, NFTNL_OBJ_SECMARK_TEXT)); + break; case NFT_OBJECT_CT_HELPER: snprintf(obj->ct_helper.name, sizeof(obj->ct_helper.name), "%s", nftnl_obj_get_str(nlo, NFTNL_OBJ_CT_HELPER_NAME)); diff --git a/src/netlink_delinearize.c b/src/netlink_delinearize.c index 31d6242..3402110 100644 --- a/src/netlink_delinearize.c +++ b/src/netlink_delinearize.c @@ -171,7 +171,7 @@ static void netlink_parse_immediate(struct netlink_parse_ctx *ctx, struct expr *expr; if (nftnl_expr_is_set(nle, NFTNL_EXPR_IMM_VERDICT)) { - nld.verdict = nftnl_expr_get_u32(nle, NFTNL_EXPR_IMM_VERDICT); + nld.verdict = nftnl_expr_get_u32(nle, NFTNL_EXPR_IMM_VERDICT); if (nftnl_expr_is_set(nle, NFTNL_EXPR_IMM_CHAIN)) { nld.chain = nftnl_expr_get(nle, NFTNL_EXPR_IMM_CHAIN, &nld.len); diff --git a/src/parser_bison.y b/src/parser_bison.y index 33915ed..a42bc05 100644 --- a/src/parser_bison.y +++ b/src/parser_bison.y @@ -148,6 +148,7 @@ int nft_lex(void *, void *, void *); struct flowtable *flowtable; struct counter *counter; struct quota *quota; + struct secmark *secmark; struct ct *ct; struct limit *limit; const struct datatype *datatype; @@ -449,6 +450,9 @@ int nft_lex(void *, void *, void *); %token QUOTA "quota" %token USED "used" +%token SECMARK "secmark" +%token SECMARKS "secmarks" + %token NANOSECOND "nanosecond" %token MICROSECOND "microsecond" %token MILLISECOND "millisecond" @@ -545,7 +549,7 @@ int nft_lex(void *, void *, void *); %type <flowtable> flowtable_block_alloc flowtable_block %destructor { flowtable_free($$); } flowtable_block_alloc -%type <obj> obj_block_alloc counter_block quota_block ct_helper_block limit_block +%type <obj> obj_block_alloc counter_block quota_block ct_helper_block limit_block secmark_block %destructor { obj_free($$); } obj_block_alloc %type <list> stmt_list @@ -647,8 +651,8 @@ int nft_lex(void *, void *, void *); %type <expr> and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr %destructor { expr_free($$); } and_rhs_expr exclusive_or_rhs_expr inclusive_or_rhs_expr -%type <obj> counter_obj quota_obj ct_obj_alloc limit_obj -%destructor { obj_free($$); } counter_obj quota_obj ct_obj_alloc limit_obj +%type <obj> counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj +%destructor { obj_free($$); } counter_obj quota_obj ct_obj_alloc limit_obj secmark_obj %type <expr> relational_expr %destructor { expr_free($$); } relational_expr @@ -726,6 +730,8 @@ int nft_lex(void *, void *, void *); %destructor { xfree($$); } quota_config %type <limit> limit_config %destructor { xfree($$); } limit_config +%type <secmark> secmark_config +%destructor { xfree($$); } secmark_config %type <expr> tcp_hdr_expr %destructor { expr_free($$); } tcp_hdr_expr @@ -953,6 +959,10 @@ add_cmd : TABLE table_spec { $$ = cmd_alloc(CMD_ADD, CMD_OBJ_LIMIT, &$2, &@$, $3); } + | SECMARK obj_spec secmark_obj + { + $$ = cmd_alloc(CMD_ADD, CMD_OBJ_SECMARK, &$2, &@$, $3); + } ; replace_cmd : RULE ruleid_spec rule @@ -1034,6 +1044,10 @@ create_cmd : TABLE table_spec { $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_LIMIT, &$2, &@$, $3); } + | SECMARK obj_spec secmark_obj + { + $$ = cmd_alloc(CMD_CREATE, CMD_OBJ_SECMARK, &$2, &@$, $3); + } ; insert_cmd : RULE rule_position rule @@ -1110,6 +1124,14 @@ delete_cmd : TABLE table_spec { $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_LIMIT, &$2, &@$, NULL); } + | SECMARK obj_spec + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SECMARK, &$2, &@$, NULL); + } + | SECMARK objid_spec + { + $$ = cmd_alloc(CMD_DELETE, CMD_OBJ_SECMARK, &$2, &@$, NULL); + } ; get_cmd : ELEMENT set_spec set_block_expr @@ -1182,6 +1204,18 @@ list_cmd : TABLE table_spec { $$ = cmd_alloc(CMD_LIST, CMD_OBJ_LIMIT, &$2, &@$, NULL); } + | SECMARKS ruleset_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARKS, &$2, &@$, NULL); + } + | SECMARKS TABLE table_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARKS, &$3, &@$, NULL); + } + | SECMARK obj_spec + { + $$ = cmd_alloc(CMD_LIST, CMD_OBJ_SECMARK, &$2, &@$, NULL); + } | RULESET ruleset_spec { $$ = cmd_alloc(CMD_LIST, CMD_OBJ_RULESET, &$2, &@$, NULL); @@ -1464,6 +1498,17 @@ table_block : /* empty */ { $$ = $<table>-1; } list_add_tail(&$4->list, &$1->objs); $$ = $1; } + | table_block SECMARK obj_identifier + obj_block_alloc '{' secmark_block '}' + stmt_separator + { + $4->location = @3; + $4->type = NFT_OBJECT_SECMARK; + handle_merge(&$4->handle, &$3); + handle_free(&$3); + list_add_tail(&$4->list, &$1->objs); + $$ = $1; + } ; chain_block_alloc : /* empty */ @@ -1595,6 +1640,15 @@ map_block : /* empty */ { $$ = $<set>-1; } $1->flags |= NFT_SET_OBJECT; $$ = $1; } + | map_block TYPE + data_type_expr COLON SECMARK + stmt_separator + { + $1->key = $3; + $1->objtype = NFT_OBJECT_SECMARK; + $1->flags |= NFT_SET_OBJECT; + $$ = $1; + } | map_block FLAGS set_flag_list stmt_separator { $1->flags |= $3; @@ -1757,6 +1811,16 @@ limit_block : /* empty */ { $$ = $<obj>-1; } } ; +secmark_block : /* empty */ { $$ = $<obj>-1; } + | secmark_block common_block + | secmark_block stmt_separator + | secmark_block secmark_config + { + $1->secmark = *$2; + $$ = $1; + } + ; + type_identifier : STRING { $$ = $1; } | MARK { $$ = xstrdup("mark"); } | DSCP { $$ = xstrdup("dscp"); } @@ -3168,6 +3232,28 @@ quota_obj : quota_config } ; +secmark_config : string + { + int ret; + struct secmark *secmark; + secmark = xzalloc(sizeof(*secmark)); + ret = snprintf(secmark->ctx, sizeof(secmark->ctx), "%s", $1); + if (ret <= 0 || ret >= (int)sizeof(secmark->ctx)) { + erec_queue(error(&@1, "invalid context '%s', max length is %u\n", $1, (int)sizeof(secmark->ctx)), state->msgs); + YYERROR; + } + $$ = secmark; + } + ; + +secmark_obj : secmark_config + { + $$ = obj_alloc(&@$); + $$->type = NFT_OBJECT_SECMARK; + $$->secmark = *$1; + } + ; + ct_obj_type : HELPER { $$ = NFT_OBJECT_CT_HELPER; } ; @@ -3510,6 +3596,7 @@ meta_key_qualified : LENGTH { $$ = NFT_META_LEN; } | PROTOCOL { $$ = NFT_META_PROTOCOL; } | PRIORITY { $$ = NFT_META_PRIORITY; } | RANDOM { $$ = NFT_META_PRANDOM; } + | SECMARK { $$ = NFT_META_SECMARK; } ; meta_key_unqualified : MARK { $$ = NFT_META_MARK; } @@ -3536,7 +3623,16 @@ meta_key_unqualified : MARK { $$ = NFT_META_MARK; } meta_stmt : META meta_key SET stmt_expr { - $$ = meta_stmt_alloc(&@$, $2, $4); + switch ($2) { + case NFT_META_SECMARK: + $$ = objref_stmt_alloc(&@$); + $$->objref.type = NFT_OBJECT_SECMARK; + $$->objref.expr = $4; + break; + default: + $$ = meta_stmt_alloc(&@$, $2, $4); + break; + } } | meta_key_unqualified SET stmt_expr { diff --git a/src/parser_json.c b/src/parser_json.c index 6e14fb7..1301c3f 100644 --- a/src/parser_json.c +++ b/src/parser_json.c @@ -2260,6 +2260,7 @@ static int string_to_nft_object(const char *str) [NFT_OBJECT_QUOTA] = "quota", [NFT_OBJECT_CT_HELPER] = "ct helper", [NFT_OBJECT_LIMIT] = "limit", + [NFT_OBJECT_SECMARK] = "secmark", }; unsigned int i; @@ -2584,6 +2585,19 @@ static struct cmd *json_parse_cmd_add_object(struct json_ctx *ctx, if (obj->quota.flags) obj->quota.flags = NFT_QUOTA_F_INV; break; + case CMD_OBJ_SECMARK: + obj->type = NFT_OBJECT_SECMARK; + if (!json_unpack(root, "{s:s}", "context", tmp)) { + int ret; + ret = snprintf(obj->secmark.ctx, sizeof(obj->secmark.ctx), "%s", tmp); + if (ret < 0 || ret >= (int)sizeof(obj->secmark.ctx)) { + json_error(ctx, "Invalid secmark context '%s', max length is %zu.", + tmp, sizeof(obj->secmark.ctx)); + obj_free(obj); + return NULL; + } + } + break; case NFT_OBJECT_CT_HELPER: cmd_obj = CMD_OBJ_CT_HELPER; obj->type = NFT_OBJECT_CT_HELPER; @@ -2675,7 +2689,8 @@ static struct cmd *json_parse_cmd_add(struct json_ctx *ctx, { "counter", CMD_OBJ_COUNTER, json_parse_cmd_add_object }, { "quota", CMD_OBJ_QUOTA, json_parse_cmd_add_object }, { "ct helper", NFT_OBJECT_CT_HELPER, json_parse_cmd_add_object }, - { "limit", CMD_OBJ_LIMIT, json_parse_cmd_add_object } + { "limit", CMD_OBJ_LIMIT, json_parse_cmd_add_object }, + { "secmark", CMD_OBJ_SECMARK, json_parse_cmd_add_object } }; unsigned int i; json_t *tmp; @@ -2841,6 +2856,8 @@ static struct cmd *json_parse_cmd_list(struct json_ctx *ctx, { "meter", CMD_OBJ_METER, json_parse_cmd_add_set }, { "meters", CMD_OBJ_METERS, json_parse_cmd_list_multiple }, { "flowtables", CMD_OBJ_FLOWTABLES, json_parse_cmd_list_multiple }, + { "secmark", CMD_OBJ_SECMARK, json_parse_cmd_add_object }, + { "secmarks", CMD_OBJ_SECMARKS, json_parse_cmd_list_multiple }, }; unsigned int i; json_t *tmp; diff --git a/src/rule.c b/src/rule.c index 56b956a..0431cf2 100644 --- a/src/rule.c +++ b/src/rule.c @@ -1090,6 +1090,7 @@ void cmd_free(struct cmd *cmd) case CMD_OBJ_QUOTA: case CMD_OBJ_CT_HELPER: case CMD_OBJ_LIMIT: + case CMD_OBJ_SECMARK: obj_free(cmd->object); break; case CMD_OBJ_FLOWTABLE: @@ -1184,6 +1185,7 @@ static int do_command_add(struct netlink_ctx *ctx, struct cmd *cmd, bool excl) case CMD_OBJ_QUOTA: case CMD_OBJ_CT_HELPER: case CMD_OBJ_LIMIT: + case CMD_OBJ_SECMARK: return netlink_add_obj(ctx, cmd, flags); case CMD_OBJ_FLOWTABLE: return netlink_add_flowtable(ctx, cmd, flags); @@ -1270,6 +1272,8 @@ static int do_command_delete(struct netlink_ctx *ctx, struct cmd *cmd) return netlink_delete_obj(ctx, cmd, NFT_OBJECT_CT_HELPER); case CMD_OBJ_LIMIT: return netlink_delete_obj(ctx, cmd, NFT_OBJECT_LIMIT); + case CMD_OBJ_SECMARK: + return netlink_delete_obj(ctx, cmd, NFT_OBJECT_SECMARK); case CMD_OBJ_FLOWTABLE: return netlink_delete_flowtable(ctx, cmd); default: @@ -1454,6 +1458,13 @@ static void obj_print_data(const struct obj *obj, } } break; + case NFT_OBJECT_SECMARK: + nft_print(octx, " %s {", obj->handle.obj.name); + if (octx->handle > 0) + nft_print(octx, " # handle %" PRIu64, obj->handle.handle.id); + nft_print(octx, "%s%s%s", opts->nl, opts->tab, opts->tab); + nft_print(octx, "%s", obj->secmark.ctx); + break; case NFT_OBJECT_CT_HELPER: nft_print(octx, "ct helper %s {", obj->handle.obj.name); if (octx->handle > 0) @@ -1511,6 +1522,7 @@ static const char * const obj_type_name_array[] = { [NFT_OBJECT_QUOTA] = "quota", [NFT_OBJECT_CT_HELPER] = "", [NFT_OBJECT_LIMIT] = "limit", + [NFT_OBJECT_SECMARK] = "secmark", }; const char *obj_type_name(enum stmt_types type) @@ -1525,6 +1537,7 @@ static uint32_t obj_type_cmd_array[NFT_OBJECT_MAX + 1] = { [NFT_OBJECT_QUOTA] = CMD_OBJ_QUOTA, [NFT_OBJECT_CT_HELPER] = CMD_OBJ_CT_HELPER, [NFT_OBJECT_LIMIT] = CMD_OBJ_LIMIT, + [NFT_OBJECT_SECMARK] = CMD_OBJ_SECMARK, }; uint32_t obj_type_to_cmd(uint32_t type) @@ -1878,6 +1891,9 @@ static int do_command_list(struct netlink_ctx *ctx, struct cmd *cmd) case CMD_OBJ_LIMIT: case CMD_OBJ_LIMITS: return do_list_obj(ctx, cmd, NFT_OBJECT_LIMIT); + case CMD_OBJ_SECMARK: + case CMD_OBJ_SECMARKS: + return do_list_obj(ctx, cmd, NFT_OBJECT_SECMARK); case CMD_OBJ_FLOWTABLES: return do_list_flowtables(ctx, cmd); default: diff --git a/src/scanner.l b/src/scanner.l index 416bd27..9a5bd30 100644 --- a/src/scanner.l +++ b/src/scanner.l @@ -547,6 +547,9 @@ addrstring ({macaddr}|{ip4addr}|{ip6addr}) "exthdr" { return EXTHDR; } +"secmark" { return SECMARK; } +"secmarks" { return SECMARKS; } + {addrstring} { yylval->string = xstrdup(yytext); return STRING; diff --git a/src/statement.c b/src/statement.c index 58e86f2..ebdcb50 100644 --- a/src/statement.c +++ b/src/statement.c @@ -202,6 +202,7 @@ static const char *objref_type[NFT_OBJECT_MAX + 1] = { [NFT_OBJECT_QUOTA] = "quota", [NFT_OBJECT_CT_HELPER] = "ct helper", [NFT_OBJECT_LIMIT] = "limit", + [NFT_OBJECT_SECMARK] = "secmark", }; const char *objref_type_name(uint32_t type) -- 2.19.0