Home | History | Annotate | Line # | Download | only in libnpftest
      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.3 2025/07/01 20:19:30 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,  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 		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 %zu:\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 }
    274