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.4 2026/03/08 16:59:24 christos 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, NPF_RULE_LAYER_3); 120 121 slock = npf_config_read_enter(npf); 122 rl = npf_ruleset_inspect(npc, npf_config_ruleset(npf), t->di, NPF_RULE_LAYER_3); 123 if (rl) { 124 npf_match_info_t mi; 125 int id_match; 126 127 mutex_enter(softnet_lock); 128 id_match = npf_rule_match_rid(rl, npc, t->di); 129 mutex_exit(softnet_lock); 130 error = npf_rule_conclude(rl, &mi); 131 if (verbose) 132 printf("id match is ...%d\n", id_match); 133 if (id_match != -1 && !id_match) { 134 error = npf_rule_reverse(npc, &mi, error); 135 } 136 137 } else { 138 error = ENOENT; 139 } 140 npf_config_read_exit(npf, slock); 141 142 put_cached_pkt(npc); 143 return error; 144 } 145 146 static int 147 run_handler_testcase(unsigned i) 148 { 149 const struct test_case *t = &test_cases[i]; 150 ifnet_t *ifp = npf_test_getif(t->ifname); 151 npf_t *npf = npf_getkernctx(); 152 struct mbuf *m; 153 int error; 154 155 m = mbuf_get_pkt(t->af, IPPROTO_UDP, t->src, t->dst, t->sport, t->dport); 156 mutex_enter(softnet_lock); 157 error = npfk_packet_handler(npf, &m, ifp, t->di); 158 mutex_exit(softnet_lock); 159 if (m) { 160 m_freem(m); 161 } 162 return error; 163 } 164 165 /* 166 * we create our specific server socket here which listens on 167 * loopback address and port 65000. easier to test pcb lookup here since 168 * it will be loaded into the protocol table. 169 */ 170 static struct socket * 171 test_socket(int dir, uid_t uid, gid_t gid) 172 { 173 struct sockaddr_in server; 174 struct lwp *cur = curlwp; 175 void *p, *rp; 176 177 memset(&server, 0, sizeof(server)); 178 179 server.sin_len = sizeof(server); 180 server.sin_family = AF_INET; 181 p = &server.sin_addr.s_addr; 182 npf_inet_pton(AF_INET, LOCAL_IP, p); /* we bind to 127.0.0.1 */ 183 server.sin_port = htons(LOCAL_PORT); 184 185 struct socket *so; 186 int error = socreate(AF_INET, &so, SOCK_DGRAM, 0, cur, NULL); 187 if (error) { 188 printf("socket creation failed: error is %d\n", error); 189 return NULL; 190 } 191 192 solock(so); 193 194 kauth_cred_t cred = kauth_cred_alloc(); 195 kauth_cred_seteuid(cred, uid); 196 kauth_cred_setegid(cred, gid); 197 198 kauth_cred_t old = so->so_cred; 199 so->so_cred = kauth_cred_dup(cred); 200 kauth_cred_free(old); 201 202 sounlock(so); 203 204 if ((error = sobind(so, (struct sockaddr *)&server, cur)) != 0) { 205 printf("bind failed %d\n", error); 206 return NULL; 207 } 208 209 if (dir == PFIL_OUT) { 210 /* connect to an additional remote address to set the 4 tuple addr-port state */ 211 struct sockaddr_in remote; 212 memset(&remote, 0, sizeof(remote)); 213 214 remote.sin_len = sizeof(remote); 215 remote.sin_family = AF_INET; 216 rp = &remote.sin_addr.s_addr; 217 npf_inet_pton(AF_INET, REMOTE_IP, rp); /* we connect to 127.0.0.1 */ 218 remote.sin_port = htons(REMOTE_PORT); 219 220 solock(so); 221 if ((error = soconnect(so, (struct sockaddr *)&remote, cur)) != 0) { 222 printf("connect failed :%d\n", error); 223 return NULL; 224 } 225 sounlock(so); 226 } 227 228 return so; 229 } 230 231 static bool 232 test_static(bool verbose) 233 { 234 for (size_t i = 0; i < __arraycount(test_cases); i++) { 235 const struct test_case *t = &test_cases[i]; 236 int error, serror; 237 struct socket *so; 238 239 so = test_socket(t->di, t->uid, t->gid); 240 if (so == NULL) { 241 printf("socket:\n"); 242 return false; 243 } 244 245 if (npf_test_getif(t->ifname) == NULL) { 246 printf("Interface %s is not configured.\n", t->ifname); 247 return false; 248 } 249 250 error = run_raw_testcase(i, verbose); 251 serror = run_handler_testcase(i); 252 253 if (verbose) { 254 printf("rule test %zu:\texpected %d (stateful) and %d\n" 255 "\t\t-> returned %d and %d\n", 256 i + 1, t->stateful_ret, t->ret, serror, error); 257 } 258 CHECK_TRUE(error == t->ret); 259 CHECK_TRUE(serror == t->stateful_ret) 260 261 soclose(so); 262 } 263 return true; 264 } 265 266 bool 267 npf_guid_test(bool verbose) 268 { 269 soinit1(); 270 271 bool ok; 272 273 ok = test_static(verbose); 274 CHECK_TRUE(ok); 275 276 return true; 277 } 278