xref: /aosp_15_r20/external/ltp/testcases/network/iptables/nft02.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) 2023 SUSE LLC
4  * Author: Marcos Paulo de Souza <[email protected]>
5  * LTP port: Martin Doucha <[email protected]>
6  */
7 
8 /*\
9  * [Description]
10  *
11  * CVE-2023-31248
12  *
13  * Test for use-after-free when adding a new rule to a chain deleted
14  * in the same netlink message batch.
15  *
16  * Kernel bug fixed in:
17  *
18  *  commit 515ad530795c118f012539ed76d02bacfd426d89
19  *  Author: Thadeu Lima de Souza Cascardo <[email protected]>
20  *  Date:   Wed Jul 5 09:12:55 2023 -0300
21  *
22  *  netfilter: nf_tables: do not ignore genmask when looking up chain by id
23  */
24 
25 #include <linux/netlink.h>
26 #include <linux/tcp.h>
27 #include <arpa/inet.h>
28 #include <linux/netfilter.h>
29 #include "lapi/nf_tables.h"
30 #include <linux/netfilter/nfnetlink.h>
31 #include "tst_test.h"
32 #include "tst_netlink.h"
33 
34 #define TABNAME "ltp_table1"
35 #define SRCCHAIN "ltp_chain_src"
36 #define DESTCHAIN "ltp_chain_dest"
37 #define DESTCHAIN_ID 77
38 
39 static uint32_t chain_id;
40 static uint32_t imm_dreg, imm_verdict;
41 static struct tst_netlink_context *ctx;
42 
43 /* Table creation config */
44 static const struct tst_netlink_attr_list table_config[] = {
45 	{NFTA_TABLE_NAME, TABNAME, strlen(TABNAME) + 1, NULL},
46 	{0, NULL, -1, NULL}
47 };
48 
49 /* Chain creation and deletion config */
50 static const struct tst_netlink_attr_list destchain_config[] = {
51 	{NFTA_TABLE_NAME, TABNAME, strlen(TABNAME) + 1, NULL},
52 	{NFTA_CHAIN_NAME, DESTCHAIN, strlen(DESTCHAIN) + 1, NULL},
53 	{NFTA_CHAIN_ID, &chain_id, sizeof(chain_id), NULL},
54 	{0, NULL, -1, NULL}
55 };
56 
57 static const struct tst_netlink_attr_list delchain_config[] = {
58 	{NFTA_TABLE_NAME, TABNAME, strlen(TABNAME) + 1, NULL},
59 	{NFTA_CHAIN_NAME, DESTCHAIN, strlen(DESTCHAIN) + 1, NULL},
60 	{0, NULL, -1, NULL}
61 };
62 
63 static const struct tst_netlink_attr_list srcchain_config[] = {
64 	{NFTA_TABLE_NAME, TABNAME, strlen(TABNAME) + 1, NULL},
65 	{NFTA_CHAIN_NAME, SRCCHAIN, strlen(SRCCHAIN) + 1, NULL},
66 	{0, NULL, -1, NULL}
67 };
68 
69 /* Rule creation config */
70 static const struct tst_netlink_attr_list rule_verdict_config[] = {
71 	{NFTA_VERDICT_CODE, &imm_verdict, sizeof(imm_verdict), NULL},
72 	{NFTA_VERDICT_CHAIN_ID, &chain_id, sizeof(chain_id), NULL},
73 	{0, NULL, -1, NULL}
74 };
75 
76 static const struct tst_netlink_attr_list rule_data_config[] = {
77 	{NFTA_IMMEDIATE_DREG, &imm_dreg, sizeof(imm_dreg), NULL},
78 	{NFTA_IMMEDIATE_DATA, NULL, 0, (const struct tst_netlink_attr_list[]) {
79 		{NFTA_DATA_VERDICT, NULL, 0, rule_verdict_config},
80 		{0, NULL, -1, NULL}
81 	}},
82 	{0, NULL, -1, NULL}
83 };
84 
85 static const struct tst_netlink_attr_list rule_expr_config[] = {
86 	{NFTA_LIST_ELEM, NULL, 0, (const struct tst_netlink_attr_list[]) {
87 		{NFTA_EXPR_NAME, "immediate", 10, NULL},
88 		{NFTA_EXPR_DATA, NULL, 0, rule_data_config},
89 		{0, NULL, -1, NULL}
90 	}},
91 	{0, NULL, -1, NULL}
92 };
93 
94 static const struct tst_netlink_attr_list rule_config[] = {
95 	{NFTA_RULE_EXPRESSIONS, NULL, 0, rule_expr_config},
96 	{NFTA_RULE_TABLE, TABNAME, strlen(TABNAME) + 1, NULL},
97 	{NFTA_RULE_CHAIN, SRCCHAIN, strlen(SRCCHAIN) + 1, NULL},
98 	{0, NULL, -1, NULL}
99 };
100 
setup(void)101 static void setup(void)
102 {
103 	tst_setup_netns();
104 
105 	chain_id = htonl(DESTCHAIN_ID);
106 	imm_dreg = htonl(NFT_REG_VERDICT);
107 	imm_verdict = htonl(NFT_GOTO);
108 }
109 
run(void)110 static void run(void)
111 {
112 	int ret;
113 	struct nlmsghdr header;
114 	struct nfgenmsg nfpayload;
115 
116 	memset(&header, 0, sizeof(header));
117 	memset(&nfpayload, 0, sizeof(nfpayload));
118 	nfpayload.version = NFNETLINK_V0;
119 
120 	ctx = NETLINK_CREATE_CONTEXT(NETLINK_NETFILTER);
121 
122 	/* Start netfilter batch */
123 	header.nlmsg_type = NFNL_MSG_BATCH_BEGIN;
124 	header.nlmsg_flags = NLM_F_REQUEST;
125 	nfpayload.nfgen_family = AF_UNSPEC;
126 	nfpayload.res_id = htons(NFNL_SUBSYS_NFTABLES);
127 	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
128 
129 	/* Add table */
130 	header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWTABLE;
131 	header.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
132 	nfpayload.nfgen_family = NFPROTO_IPV4;
133 	nfpayload.res_id = htons(0);
134 	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
135 	NETLINK_ADD_ATTR_LIST(ctx, table_config);
136 
137 	/* Add destination chain */
138 	header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWCHAIN;
139 	header.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
140 	nfpayload.nfgen_family = NFPROTO_IPV4;
141 	nfpayload.res_id = htons(0);
142 	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
143 	NETLINK_ADD_ATTR_LIST(ctx, destchain_config);
144 
145 	/* Delete destination chain */
146 	header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_DELCHAIN;
147 	header.nlmsg_flags = NLM_F_REQUEST;
148 	nfpayload.nfgen_family = NFPROTO_IPV4;
149 	nfpayload.res_id = htons(0);
150 	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
151 	NETLINK_ADD_ATTR_LIST(ctx, delchain_config);
152 
153 	/* Add source chain */
154 	header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWCHAIN;
155 	header.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE;
156 	nfpayload.nfgen_family = NFPROTO_IPV4;
157 	nfpayload.res_id = htons(0);
158 	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
159 	NETLINK_ADD_ATTR_LIST(ctx, srcchain_config);
160 
161 	/* Add rule to source chain. Require ACK and check for ENOENT error. */
162 	header.nlmsg_type = (NFNL_SUBSYS_NFTABLES << 8) | NFT_MSG_NEWRULE;
163 	header.nlmsg_flags = NLM_F_REQUEST | NLM_F_APPEND | NLM_F_CREATE |
164 		NLM_F_ACK;
165 	nfpayload.nfgen_family = NFPROTO_IPV4;
166 	nfpayload.res_id = htons(0);
167 	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
168 	NETLINK_ADD_ATTR_LIST(ctx, rule_config);
169 
170 	/* End batch */
171 	header.nlmsg_type = NFNL_MSG_BATCH_END;
172 	header.nlmsg_flags = NLM_F_REQUEST;
173 	nfpayload.nfgen_family = AF_UNSPEC;
174 	nfpayload.res_id = htons(NFNL_SUBSYS_NFTABLES);
175 	NETLINK_ADD_MESSAGE(ctx, &header, &nfpayload, sizeof(nfpayload));
176 
177 	ret = NETLINK_SEND_VALIDATE(ctx);
178 	TST_ERR = tst_netlink_errno;
179 	NETLINK_DESTROY_CONTEXT(ctx);
180 	ctx = NULL;
181 
182 	if (ret)
183 		tst_res(TFAIL, "Netfilter chain list is corrupted");
184 	else if (TST_ERR == ENOENT)
185 		tst_res(TPASS, "Deleted netfilter chain cannot be referenced");
186 	else if (TST_ERR == EOPNOTSUPP || TST_ERR == EINVAL)
187 		tst_brk(TCONF, "Test requires unavailable netfilter features");
188 	else
189 		tst_brk(TBROK | TTERRNO, "Unknown nfnetlink error");
190 }
191 
cleanup(void)192 static void cleanup(void)
193 {
194 	NETLINK_DESTROY_CONTEXT(ctx);
195 }
196 
197 static struct tst_test test = {
198 	.test_all = run,
199 	.setup = setup,
200 	.cleanup = cleanup,
201 	.taint_check = TST_TAINT_W | TST_TAINT_D,
202 	.needs_kconfigs = (const char *[]) {
203 		"CONFIG_USER_NS=y",
204 		"CONFIG_NF_TABLES",
205 		NULL
206 	},
207 	.save_restore = (const struct tst_path_val[]) {
208 		{"/proc/sys/user/max_user_namespaces", "1024", TST_SR_SKIP},
209 		{}
210 	},
211 	.tags = (const struct tst_tag[]) {
212 		{"linux-git", "515ad530795c"},
213 		{"CVE", "2023-31248"},
214 		{}
215 	}
216 };
217