npf_rid_test.c revision 1.1 1 /*
2 * NPF socket User/group id tests.
3 *
4 * Public Domain.
5 */
6
7 #ifdef _KERNEL
8 #include <sys/types.h>
9 #include <sys/cdefs.h>
10 __KERNEL_RCSID(0, "$NetBSD: npf_rid_test.c,v 1.1 2025/06/01 01:07:26 joe Exp $");
11 #endif
12
13 #include "npf_impl.h"
14 #include "npf_test.h"
15
16 #include <netinet/in.h>
17 #include <sys/socket.h>
18 #include <sys/kauth.h>
19 #include <sys/socketvar.h>
20 #include <sys/lwp.h>
21 #include <sys/cpu.h>
22
23 #define RESULT_PASS 0
24 #define RESULT_BLOCK ENETUNREACH
25
26 /* this port number suitable for testing */
27 #define REMOTE_PORT 65500
28 #define LOCAL_PORT 65000
29 #define LOCAL_IP "127.0.0.1"
30 #define REMOTE_IP LOCAL_IP
31
32 static const struct test_case {
33 int af;
34 const char * src;
35 uint16_t sport;
36 const char * dst;
37 uint16_t dport;
38 uint32_t uid;
39 uint32_t gid;
40 const char * ifname;
41 int di;
42 int ret;
43 int stateful_ret;
44 } test_cases[] = {
45 {
46 /* pass in final from $local_ip4 user $Kojo = 1001 group $wheel = 20 */
47 .af = AF_INET,
48 .src = "10.1.1.4", .sport = 9000,
49 .dst = LOCAL_IP, .dport = LOCAL_PORT,
50 .ifname = IFNAME_EXT, .di = PFIL_IN,
51 .uid = 1001, .gid = 20, /* matches so pass it */
52 .ret = RESULT_PASS, .stateful_ret = RESULT_PASS
53 },
54 {
55 /* connect on different UID and block */
56 .af = AF_INET,
57 .src = "10.1.1.4", .sport = 9000,
58 .dst = LOCAL_IP, .dport = LOCAL_PORT,
59 .ifname = IFNAME_EXT, .di = PFIL_IN,
60 .uid = 1001, .gid = 10, /* mismatch gid so block it */
61 .ret = RESULT_BLOCK, .stateful_ret = RESULT_BLOCK
62 },
63 {
64 .af = AF_INET,
65 .src = "10.1.1.4", .sport = 9000,
66 .dst = LOCAL_IP, .dport = LOCAL_PORT,
67 .ifname = IFNAME_EXT, .di = PFIL_IN,
68 .uid = 100, .gid = 20, /* mismatch uid so block it */
69 .ret = RESULT_BLOCK, .stateful_ret = RESULT_BLOCK
70 },
71
72
73 /* block out final to 127.0.0.1 user > $Kojo( > 1001) group 1 >< $wheel( IRG 1 >< 20) */
74 {
75 .af = AF_INET,
76 .src = LOCAL_IP, .sport = LOCAL_PORT,
77 .dst = REMOTE_IP, .dport = REMOTE_PORT,
78 .ifname = IFNAME_EXT, .di = PFIL_OUT,
79 .uid = 1005, .gid = 14, /* matches so blocks it */
80 .ret = RESULT_BLOCK, .stateful_ret = RESULT_BLOCK
81 },
82 {
83 .af = AF_INET,
84 .src = LOCAL_IP, .sport = LOCAL_PORT,
85 .dst = REMOTE_IP, .dport = REMOTE_PORT,
86 .ifname = IFNAME_EXT, .di = PFIL_OUT,
87 .uid = 1005, .gid = 30, /* mismatch gid so pass it */
88 .ret = RESULT_PASS, .stateful_ret = RESULT_PASS
89 },
90 {
91 .af = AF_INET,
92 .src = LOCAL_IP, .sport = LOCAL_PORT,
93 .dst = REMOTE_IP, .dport = REMOTE_PORT,
94 .ifname = IFNAME_EXT, .di = PFIL_OUT,
95 .uid = 100, .gid = 15, /* mismatch uid so pass it */
96 .ret = RESULT_PASS, .stateful_ret = RESULT_PASS
97 },
98 {
99 .af = AF_INET,
100 .src = LOCAL_IP, .sport = LOCAL_PORT,
101 .dst = REMOTE_IP, .dport = REMOTE_PORT,
102 .ifname = IFNAME_EXT, .di = PFIL_OUT,
103 .uid = 1010, .gid = 11, /* matches so blocks it */
104 .ret = RESULT_BLOCK, .stateful_ret = RESULT_BLOCK
105 },
106 };
107
108 static int
109 run_raw_testcase(unsigned i, bool verbose)
110 {
111 const struct test_case *t = &test_cases[i];
112 npf_t *npf = npf_getkernctx();
113 npf_cache_t *npc;
114 struct mbuf *m;
115 npf_rule_t *rl;
116 int slock, error;
117
118 m = mbuf_get_pkt(t->af, IPPROTO_UDP, t->src, t->dst, t->sport, t->dport);
119 npc = get_cached_pkt(m, t->ifname);
120
121 slock = npf_config_read_enter(npf);
122 rl = npf_ruleset_inspect(npc, npf_config_ruleset(npf), t->di, NPF_LAYER_3);
123 if (rl) {
124 npf_match_info_t mi;
125 int id_match;
126
127 id_match = npf_rule_match_rid(rl, npc, t->di);
128 error = npf_rule_conclude(rl, &mi);
129 if (verbose)
130 printf("id match is ...%d\n", id_match);
131 if (id_match != -1 && !id_match) {
132 error = npf_rule_reverse(npc, &mi, error);
133 }
134
135 } else {
136 error = ENOENT;
137 }
138 npf_config_read_exit(npf, slock);
139
140 put_cached_pkt(npc);
141 return error;
142 }
143
144 static int
145 run_handler_testcase(unsigned i)
146 {
147 const struct test_case *t = &test_cases[i];
148 ifnet_t *ifp = npf_test_getif(t->ifname);
149 npf_t *npf = npf_getkernctx();
150 struct mbuf *m;
151 int error;
152
153 m = mbuf_get_pkt(t->af, IPPROTO_UDP, t->src, t->dst, t->sport, t->dport);
154 error = npfk_packet_handler(npf, &m, ifp, t->di);
155 if (m) {
156 m_freem(m);
157 }
158 return error;
159 }
160
161 /*
162 * we create our specific server socket here which listens on
163 * loopback address and port 65000. easier to test pcb lookup here since
164 * it will be loaded into the protocol table.
165 */
166 static struct socket *
167 test_socket(int dir, uid_t uid, gid_t gid)
168 {
169 struct sockaddr_in server;
170 struct lwp *cur = curlwp;
171 void *p, *rp;
172
173 memset(&Server, 0, sizeof(server));
174
175 server.sin_len = sizeof(server);
176 server.sin_family = AF_INET;
177 p = &server.sin_addr.s_addr;
178 npf_inet_pton(AF_INET, LOCAL_IP, p); /* we bind to 127.0.0.1 */
179 server.sin_port = htons(LOCAL_PORT);
180
181 struct socket *so;
182 int error = socreate(AF_INET, &so, SOCK_DGRAM, 0, cur, NULL);
183 if (error) {
184 printf("socket creation failed: error is %d\n", error);
185 return NULL;
186 }
187
188 solock(so);
189
190 kauth_cred_t cred = kauth_cred_alloc();
191 kauth_cred_seteuid(cred, uid);
192 kauth_cred_setegid(cred, gid);
193
194 kauth_cred_t old = so->so_cred;
195 so->so_cred = kauth_cred_dup(cred);
196 kauth_cred_free(old);
197
198 sounlock(so);
199
200 if ((error = sobind(so, (struct sockaddr *)&server, cur)) != 0) {
201 printf("bind failed %d\n", error);
202 return NULL;
203 }
204
205 if (dir == PFIL_OUT) {
206 /* connect to an additional remote address to set the 4 tuple addr-port state */
207 struct sockaddr_in remote;
208 memset(&Remote, 0, sizeof(remote));
209
210 remote.sin_len = sizeof(remote);
211 remote.sin_family = AF_INET;
212 rp = &remote.sin_addr.s_addr;
213 npf_inet_pton(AF_INET, REMOTE_IP, rp); /* we connect to 127.0.0.1 */
214 remote.sin_port = htons(REMOTE_PORT);
215
216 solock(so);
217 if ((error = soconnect(so, (struct sockaddr *)&remote, cur)) != 0) {
218 printf("connect failed :%d\n", error);
219 return NULL;
220 }
221 sounlock(so);
222 }
223
224 return so;
225 }
226
227 static bool
228 test_static(bool verbose)
229 {
230 for (size_t i = 0; i < __arraycount(test_cases); i++) {
231 const struct test_case *t = &test_cases[i];
232 int error, serror;
233 struct socket *so;
234
235 so = test_socket(t->di, t->uid, t->gid);
236 if (so == NULL) {
237 printf("socket:\n");
238 return false;
239 }
240
241 if (npf_test_getif(t->ifname) == NULL) {
242 printf("Interface %s is not configured.\n", t->ifname);
243 return false;
244 }
245
246 error = run_raw_testcase(i, verbose);
247 serror = run_handler_testcase(i);
248
249 if (verbose) {
250 printf("rule test %d:\texpected %d (stateful) and %d\n"
251 "\t\t-> returned %d and %d\n",
252 i + 1, t->stateful_ret, t->ret, serror, error);
253 }
254 CHECK_TRUE(error == t->ret);
255 CHECK_TRUE(serror == t->stateful_ret)
256
257 soclose(so);
258 }
259 return true;
260 }
261
262 bool
263 npf_guid_test(bool verbose)
264 {
265 soinit1();
266
267 bool ok;
268
269 ok = test_static(verbose);
270 CHECK_TRUE(ok);
271
272 return true;
273 }