xref: /freebsd/contrib/libfido2/src/netlink.c (revision db33c6f3ae9d1231087710068ee4ea5398aacca7)
1 /*
2  * Copyright (c) 2020 Yubico AB. All rights reserved.
3  * Use of this source code is governed by a BSD-style
4  * license that can be found in the LICENSE file.
5  * SPDX-License-Identifier: BSD-2-Clause
6  */
7 
8 #include <sys/socket.h>
9 
10 #include <linux/genetlink.h>
11 #include <linux/netlink.h>
12 #include <linux/nfc.h>
13 
14 #include <errno.h>
15 #include <limits.h>
16 
17 #include "fido.h"
18 #include "netlink.h"
19 
20 #ifdef FIDO_FUZZ
21 static ssize_t (*fuzz_read)(int, void *, size_t);
22 static ssize_t (*fuzz_write)(int, const void *, size_t);
23 # define READ	fuzz_read
24 # define WRITE	fuzz_write
25 #else
26 # define READ	read
27 # define WRITE	write
28 #endif
29 
30 #ifndef SOL_NETLINK
31 #define SOL_NETLINK	270
32 #endif
33 
34 #define NETLINK_POLL_MS	100
35 
36 /* XXX avoid signed NLA_ALIGNTO */
37 #undef NLA_HDRLEN
38 #define NLA_HDRLEN	NLMSG_ALIGN(sizeof(struct nlattr))
39 
40 typedef struct nlmsgbuf {
41 	size_t         siz; /* alloc size */
42 	size_t         len; /* of payload */
43 	unsigned char *ptr; /* in payload */
44 	union {
45 		struct nlmsghdr   nlmsg;
46 		char              buf[NLMSG_HDRLEN]; /* align */
47 	}              u;
48 	unsigned char  payload[];
49 } nlmsgbuf_t;
50 
51 typedef struct genlmsgbuf {
52 	union {
53 		struct genlmsghdr genl;
54 		char              buf[GENL_HDRLEN];  /* align */
55 	}              u;
56 } genlmsgbuf_t;
57 
58 typedef struct nlamsgbuf {
59 	size_t         siz; /* alloc size */
60 	size_t         len; /* of payload */
61 	unsigned char *ptr; /* in payload */
62 	union {
63 		struct nlattr     nla;
64 		char              buf[NLA_HDRLEN];   /* align */
65 	}              u;
66 	unsigned char  payload[];
67 } nlamsgbuf_t;
68 
69 typedef struct nl_family {
70 	uint16_t id;
71 	uint32_t mcastgrp;
72 } nl_family_t;
73 
74 typedef struct nl_poll {
75 	uint32_t     dev;
76 	unsigned int eventcnt;
77 } nl_poll_t;
78 
79 typedef struct nl_target {
80 	int       found;
81 	uint32_t *value;
82 } nl_target_t;
83 
84 static const void *
85 nlmsg_ptr(const nlmsgbuf_t *m)
86 {
87 	return (&m->u.nlmsg);
88 }
89 
90 static size_t
91 nlmsg_len(const nlmsgbuf_t *m)
92 {
93 	return (m->u.nlmsg.nlmsg_len);
94 }
95 
96 static uint16_t
97 nlmsg_type(const nlmsgbuf_t *m)
98 {
99 	return (m->u.nlmsg.nlmsg_type);
100 }
101 
102 static nlmsgbuf_t *
103 nlmsg_new(uint16_t type, uint16_t flags, size_t len)
104 {
105 	nlmsgbuf_t *m;
106 	size_t siz;
107 
108 	if (len > SIZE_MAX - sizeof(*m) ||
109 	    (siz = sizeof(*m) + len) > UINT16_MAX ||
110 	    (m = calloc(1, siz)) == NULL)
111 		return (NULL);
112 
113 	m->siz = siz;
114 	m->len = len;
115 	m->ptr = m->payload;
116 	m->u.nlmsg.nlmsg_type = type;
117 	m->u.nlmsg.nlmsg_flags = NLM_F_REQUEST | flags;
118 	m->u.nlmsg.nlmsg_len = NLMSG_HDRLEN;
119 
120 	return (m);
121 }
122 
123 static nlamsgbuf_t *
124 nla_from_buf(const unsigned char **ptr, size_t *len)
125 {
126 	nlamsgbuf_t h, *a;
127 	size_t nlalen, skip;
128 
129 	if (*len < sizeof(h.u))
130 		return (NULL);
131 
132 	memset(&h, 0, sizeof(h));
133 	memcpy(&h.u, *ptr, sizeof(h.u));
134 
135 	if ((nlalen = h.u.nla.nla_len) < sizeof(h.u) || nlalen > *len ||
136 	    nlalen - sizeof(h.u) > UINT16_MAX ||
137 	    nlalen > SIZE_MAX - sizeof(*a) ||
138 	    (skip = NLMSG_ALIGN(nlalen)) > *len ||
139 	    (a = calloc(1, sizeof(*a) + nlalen - sizeof(h.u))) == NULL)
140 		return (NULL);
141 
142 	memcpy(&a->u, *ptr, nlalen);
143 	a->siz = sizeof(*a) + nlalen - sizeof(h.u);
144 	a->ptr = a->payload;
145 	a->len = nlalen - sizeof(h.u);
146 	*ptr += skip;
147 	*len -= skip;
148 
149 	return (a);
150 }
151 
152 static nlamsgbuf_t *
153 nla_getattr(nlamsgbuf_t *a)
154 {
155 	return (nla_from_buf((void *)&a->ptr, &a->len));
156 }
157 
158 static uint16_t
159 nla_type(const nlamsgbuf_t *a)
160 {
161 	return (a->u.nla.nla_type);
162 }
163 
164 static nlamsgbuf_t *
165 nlmsg_getattr(nlmsgbuf_t *m)
166 {
167 	return (nla_from_buf((void *)&m->ptr, &m->len));
168 }
169 
170 static int
171 nla_read(nlamsgbuf_t *a, void *buf, size_t cnt)
172 {
173 	if (cnt > a->u.nla.nla_len ||
174 	    fido_buf_read((void *)&a->ptr, &a->len, buf, cnt) < 0)
175 		return (-1);
176 
177 	a->u.nla.nla_len = (uint16_t)(a->u.nla.nla_len - cnt);
178 
179 	return (0);
180 }
181 
182 static nlmsgbuf_t *
183 nlmsg_from_buf(const unsigned char **ptr, size_t *len)
184 {
185 	nlmsgbuf_t h, *m;
186 	size_t msglen, skip;
187 
188 	if (*len < sizeof(h.u))
189 		return (NULL);
190 
191 	memset(&h, 0, sizeof(h));
192 	memcpy(&h.u, *ptr, sizeof(h.u));
193 
194 	if ((msglen = h.u.nlmsg.nlmsg_len) < sizeof(h.u) || msglen > *len ||
195 	    msglen - sizeof(h.u) > UINT16_MAX ||
196 	    (skip = NLMSG_ALIGN(msglen)) > *len ||
197 	    (m = nlmsg_new(0, 0, msglen - sizeof(h.u))) == NULL)
198 		return (NULL);
199 
200 	memcpy(&m->u, *ptr, msglen);
201 	*ptr += skip;
202 	*len -= skip;
203 
204 	return (m);
205 }
206 
207 static int
208 nlmsg_read(nlmsgbuf_t *m, void *buf, size_t cnt)
209 {
210 	if (cnt > m->u.nlmsg.nlmsg_len ||
211 	    fido_buf_read((void *)&m->ptr, &m->len, buf, cnt) < 0)
212 		return (-1);
213 
214 	m->u.nlmsg.nlmsg_len = (uint32_t)(m->u.nlmsg.nlmsg_len - cnt);
215 
216 	return (0);
217 }
218 
219 static int
220 nlmsg_write(nlmsgbuf_t *m, const void *buf, size_t cnt)
221 {
222 	if (cnt > UINT32_MAX - m->u.nlmsg.nlmsg_len ||
223 	    fido_buf_write(&m->ptr, &m->len, buf, cnt) < 0)
224 		return (-1);
225 
226 	m->u.nlmsg.nlmsg_len = (uint32_t)(m->u.nlmsg.nlmsg_len + cnt);
227 
228 	return (0);
229 }
230 
231 static int
232 nlmsg_set_genl(nlmsgbuf_t *m, uint8_t cmd)
233 {
234 	genlmsgbuf_t g;
235 
236 	memset(&g, 0, sizeof(g));
237 	g.u.genl.cmd = cmd;
238 	g.u.genl.version = NFC_GENL_VERSION;
239 
240 	return (nlmsg_write(m, &g, sizeof(g)));
241 }
242 
243 static int
244 nlmsg_get_genl(nlmsgbuf_t *m, uint8_t cmd)
245 {
246 	genlmsgbuf_t g;
247 
248 	memset(&g, 0, sizeof(g));
249 
250 	if (nlmsg_read(m, &g, sizeof(g)) < 0 || g.u.genl.cmd != cmd)
251 		return (-1);
252 
253 	return (0);
254 }
255 
256 static int
257 nlmsg_get_status(nlmsgbuf_t *m)
258 {
259 	int status;
260 
261 	if (nlmsg_read(m, &status, sizeof(status)) < 0 || status == INT_MIN)
262 		return (-1);
263 	if (status < 0)
264 		status = -status;
265 
266 	return (status);
267 }
268 
269 static int
270 nlmsg_setattr(nlmsgbuf_t *m, uint16_t type, const void *ptr, size_t len)
271 {
272 	int r;
273 	char *padding;
274 	size_t skip;
275 	nlamsgbuf_t a;
276 
277 	if ((skip = NLMSG_ALIGN(len)) > UINT16_MAX - sizeof(a.u) ||
278 	    skip < len || (padding = calloc(1, skip - len)) == NULL)
279 		return (-1);
280 
281 	memset(&a, 0, sizeof(a));
282 	a.u.nla.nla_type = type;
283 	a.u.nla.nla_len = (uint16_t)(len + sizeof(a.u));
284 	r = nlmsg_write(m, &a.u, sizeof(a.u)) < 0 ||
285 	    nlmsg_write(m, ptr, len) < 0 ||
286 	    nlmsg_write(m, padding, skip - len) < 0 ? -1 : 0;
287 
288 	free(padding);
289 
290 	return (r);
291 }
292 
293 static int
294 nlmsg_set_u16(nlmsgbuf_t *m, uint16_t type, uint16_t val)
295 {
296 	return (nlmsg_setattr(m, type, &val, sizeof(val)));
297 }
298 
299 static int
300 nlmsg_set_u32(nlmsgbuf_t *m, uint16_t type, uint32_t val)
301 {
302 	return (nlmsg_setattr(m, type, &val, sizeof(val)));
303 }
304 
305 static int
306 nlmsg_set_str(nlmsgbuf_t *m, uint16_t type, const char *val)
307 {
308 	return (nlmsg_setattr(m, type, val, strlen(val) + 1));
309 }
310 
311 static int
312 nla_get_u16(nlamsgbuf_t *a, uint16_t *v)
313 {
314 	return (nla_read(a, v, sizeof(*v)));
315 }
316 
317 static int
318 nla_get_u32(nlamsgbuf_t *a, uint32_t *v)
319 {
320 	return (nla_read(a, v, sizeof(*v)));
321 }
322 
323 static char *
324 nla_get_str(nlamsgbuf_t *a)
325 {
326 	size_t n;
327 	char *s = NULL;
328 
329 	if ((n = a->len) < 1 || a->ptr[n - 1] != '\0' ||
330 	    (s = calloc(1, n)) == NULL || nla_read(a, s, n) < 0) {
331 		free(s);
332 		return (NULL);
333 	}
334 	s[n - 1] = '\0';
335 
336 	return (s);
337 }
338 
339 static int
340 nlmsg_tx(int fd, const nlmsgbuf_t *m)
341 {
342 	ssize_t r;
343 
344 	if ((r = WRITE(fd, nlmsg_ptr(m), nlmsg_len(m))) == -1) {
345 		fido_log_error(errno, "%s: write", __func__);
346 		return (-1);
347 	}
348 	if (r < 0 || (size_t)r != nlmsg_len(m)) {
349 		fido_log_debug("%s: %zd != %zu", __func__, r, nlmsg_len(m));
350 		return (-1);
351 	}
352 	fido_log_xxd(nlmsg_ptr(m), nlmsg_len(m), "%s", __func__);
353 
354 	return (0);
355 }
356 
357 static ssize_t
358 nlmsg_rx(int fd, unsigned char *ptr, size_t len, int ms)
359 {
360 	ssize_t r;
361 
362 	if (len > SSIZE_MAX) {
363 		fido_log_debug("%s: len", __func__);
364 		return (-1);
365 	}
366 	if (fido_hid_unix_wait(fd, ms, NULL) < 0) {
367 		fido_log_debug("%s: fido_hid_unix_wait", __func__);
368 		return (-1);
369 	}
370 	if ((r = READ(fd, ptr, len)) == -1) {
371 		fido_log_error(errno, "%s: read %zd", __func__, r);
372 		return (-1);
373 	}
374 	fido_log_xxd(ptr, (size_t)r, "%s", __func__);
375 
376 	return (r);
377 }
378 
379 static int
380 nlmsg_iter(nlmsgbuf_t *m, void *arg, int (*parser)(nlamsgbuf_t *, void *))
381 {
382 	nlamsgbuf_t *a;
383 	int r;
384 
385 	while ((a = nlmsg_getattr(m)) != NULL) {
386 		r = parser(a, arg);
387 		free(a);
388 		if (r < 0) {
389 			fido_log_debug("%s: parser", __func__);
390 			return (-1);
391 		}
392 	}
393 
394 	return (0);
395 }
396 
397 static int
398 nla_iter(nlamsgbuf_t *g, void *arg, int (*parser)(nlamsgbuf_t *, void *))
399 {
400 	nlamsgbuf_t *a;
401 	int r;
402 
403 	while ((a = nla_getattr(g)) != NULL) {
404 		r = parser(a, arg);
405 		free(a);
406 		if (r < 0) {
407 			fido_log_debug("%s: parser", __func__);
408 			return (-1);
409 		}
410 	}
411 
412 	return (0);
413 }
414 
415 static int
416 nl_parse_reply(const uint8_t *blob, size_t blob_len, uint16_t msg_type,
417     uint8_t genl_cmd, void *arg, int (*parser)(nlamsgbuf_t *, void *))
418 {
419 	nlmsgbuf_t *m;
420 	int r;
421 
422 	while (blob_len) {
423 		if ((m = nlmsg_from_buf(&blob, &blob_len)) == NULL) {
424 			fido_log_debug("%s: nlmsg", __func__);
425 			return (-1);
426 		}
427 		if (nlmsg_type(m) == NLMSG_ERROR) {
428 			r = nlmsg_get_status(m);
429 			free(m);
430 			return (r);
431 		}
432 		if (nlmsg_type(m) != msg_type ||
433 		    nlmsg_get_genl(m, genl_cmd) < 0) {
434 			fido_log_debug("%s: skipping", __func__);
435 			free(m);
436 			continue;
437 		}
438 		if (parser != NULL && nlmsg_iter(m, arg, parser) < 0) {
439 			fido_log_debug("%s: nlmsg_iter", __func__);
440 			free(m);
441 			return (-1);
442 		}
443 		free(m);
444 	}
445 
446 	return (0);
447 }
448 
449 static int
450 parse_mcastgrp(nlamsgbuf_t *a, void *arg)
451 {
452 	nl_family_t *family = arg;
453 	char *name;
454 
455 	switch (nla_type(a)) {
456 	case CTRL_ATTR_MCAST_GRP_NAME:
457 		if ((name = nla_get_str(a)) == NULL ||
458 		    strcmp(name, NFC_GENL_MCAST_EVENT_NAME) != 0) {
459 			free(name);
460 			return (-1); /* XXX skip? */
461 		}
462 		free(name);
463 		return (0);
464 	case CTRL_ATTR_MCAST_GRP_ID:
465 		if (family->mcastgrp)
466 			break;
467 		if (nla_get_u32(a, &family->mcastgrp) < 0) {
468 			fido_log_debug("%s: group", __func__);
469 			return (-1);
470 		}
471 		return (0);
472 	}
473 
474 	fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
475 
476 	return (0);
477 }
478 
479 static int
480 parse_mcastgrps(nlamsgbuf_t *a, void *arg)
481 {
482 	return (nla_iter(a, arg, parse_mcastgrp));
483 }
484 
485 static int
486 parse_family(nlamsgbuf_t *a, void *arg)
487 {
488 	nl_family_t *family = arg;
489 
490 	switch (nla_type(a)) {
491 	case CTRL_ATTR_FAMILY_ID:
492 		if (family->id)
493 			break;
494 		if (nla_get_u16(a, &family->id) < 0) {
495 			fido_log_debug("%s: id", __func__);
496 			return (-1);
497 		}
498 		return (0);
499 	case CTRL_ATTR_MCAST_GROUPS:
500 		return (nla_iter(a, family, parse_mcastgrps));
501 	}
502 
503 	fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
504 
505 	return (0);
506 }
507 
508 static int
509 nl_get_nfc_family(int fd, uint16_t *type, uint32_t *mcastgrp)
510 {
511 	nlmsgbuf_t *m;
512 	uint8_t reply[512];
513 	nl_family_t family;
514 	ssize_t r;
515 	int ok;
516 
517 	if ((m = nlmsg_new(GENL_ID_CTRL, 0, 64)) == NULL ||
518 	    nlmsg_set_genl(m, CTRL_CMD_GETFAMILY) < 0 ||
519 	    nlmsg_set_u16(m, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL) < 0 ||
520 	    nlmsg_set_str(m, CTRL_ATTR_FAMILY_NAME, NFC_GENL_NAME) < 0 ||
521 	    nlmsg_tx(fd, m) < 0) {
522 		free(m);
523 		return (-1);
524 	}
525 	free(m);
526 	memset(&family, 0, sizeof(family));
527 	if ((r = nlmsg_rx(fd, reply, sizeof(reply), -1)) < 0) {
528 		fido_log_debug("%s: nlmsg_rx", __func__);
529 		return (-1);
530 	}
531 	if ((ok = nl_parse_reply(reply, (size_t)r, GENL_ID_CTRL,
532 	    CTRL_CMD_NEWFAMILY, &family, parse_family)) != 0) {
533 		fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
534 		return (-1);
535 	}
536 	if (family.id == 0 || family.mcastgrp == 0) {
537 		fido_log_debug("%s: missing attr", __func__);
538 		return (-1);
539 	}
540 	*type = family.id;
541 	*mcastgrp = family.mcastgrp;
542 
543 	return (0);
544 }
545 
546 static int
547 parse_target(nlamsgbuf_t *a, void *arg)
548 {
549 	nl_target_t *t = arg;
550 
551 	if (t->found || nla_type(a) != NFC_ATTR_TARGET_INDEX) {
552 		fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
553 		return (0);
554 	}
555 	if (nla_get_u32(a, t->value) < 0) {
556 		fido_log_debug("%s: target", __func__);
557 		return (-1);
558 	}
559 	t->found = 1;
560 
561 	return (0);
562 }
563 
564 int
565 fido_nl_power_nfc(fido_nl_t *nl, uint32_t dev)
566 {
567 	nlmsgbuf_t *m;
568 	uint8_t reply[512];
569 	ssize_t r;
570 	int ok;
571 
572 	if ((m = nlmsg_new(nl->nfc_type, NLM_F_ACK, 64)) == NULL ||
573 	    nlmsg_set_genl(m, NFC_CMD_DEV_UP) < 0 ||
574 	    nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 ||
575 	    nlmsg_tx(nl->fd, m) < 0) {
576 		free(m);
577 		return (-1);
578 	}
579 	free(m);
580 	if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1)) < 0) {
581 		fido_log_debug("%s: nlmsg_rx", __func__);
582 		return (-1);
583 	}
584 	if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
585 	    NFC_CMD_DEV_UP, NULL, NULL)) != 0 && ok != EALREADY) {
586 		fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
587 		return (-1);
588 	}
589 
590 	return (0);
591 }
592 
593 static int
594 nl_nfc_poll(fido_nl_t *nl, uint32_t dev)
595 {
596 	nlmsgbuf_t *m;
597 	uint8_t reply[512];
598 	ssize_t r;
599 	int ok;
600 
601 	if ((m = nlmsg_new(nl->nfc_type, NLM_F_ACK, 64)) == NULL ||
602 	    nlmsg_set_genl(m, NFC_CMD_START_POLL) < 0 ||
603 	    nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 ||
604 	    nlmsg_set_u32(m, NFC_ATTR_PROTOCOLS, NFC_PROTO_ISO14443_MASK) < 0 ||
605 	    nlmsg_tx(nl->fd, m) < 0) {
606 		free(m);
607 		return (-1);
608 	}
609 	free(m);
610 	if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1)) < 0) {
611 		fido_log_debug("%s: nlmsg_rx", __func__);
612 		return (-1);
613 	}
614 	if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
615 	    NFC_CMD_START_POLL, NULL, NULL)) != 0) {
616 		fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
617 		return (-1);
618 	}
619 
620 	return (0);
621 }
622 
623 static int
624 nl_dump_nfc_target(fido_nl_t *nl, uint32_t dev, uint32_t *target, int ms)
625 {
626 	nlmsgbuf_t *m;
627 	nl_target_t t;
628 	uint8_t reply[512];
629 	ssize_t r;
630 	int ok;
631 
632 	if ((m = nlmsg_new(nl->nfc_type, NLM_F_DUMP, 64)) == NULL ||
633 	    nlmsg_set_genl(m, NFC_CMD_GET_TARGET) < 0 ||
634 	    nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 ||
635 	    nlmsg_tx(nl->fd, m) < 0) {
636 		free(m);
637 		return (-1);
638 	}
639 	free(m);
640 	if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), ms)) < 0) {
641 		fido_log_debug("%s: nlmsg_rx", __func__);
642 		return (-1);
643 	}
644 	memset(&t, 0, sizeof(t));
645 	t.value = target;
646 	if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
647 	    NFC_CMD_GET_TARGET, &t, parse_target)) != 0) {
648 		fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
649 		return (-1);
650 	}
651 	if (!t.found) {
652 		fido_log_debug("%s: target not found", __func__);
653 		return (-1);
654 	}
655 
656 	return (0);
657 }
658 
659 static int
660 parse_nfc_event(nlamsgbuf_t *a, void *arg)
661 {
662 	nl_poll_t *ctx = arg;
663 	uint32_t dev;
664 
665 	if (nla_type(a) != NFC_ATTR_DEVICE_INDEX) {
666 		fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
667 		return (0);
668 	}
669 	if (nla_get_u32(a, &dev) < 0) {
670 		fido_log_debug("%s: dev", __func__);
671 		return (-1);
672 	}
673 	if (dev == ctx->dev)
674 		ctx->eventcnt++;
675 	else
676 		fido_log_debug("%s: ignoring dev 0x%x", __func__, dev);
677 
678 	return (0);
679 }
680 
681 int
682 fido_nl_get_nfc_target(fido_nl_t *nl, uint32_t dev, uint32_t *target)
683 {
684 	uint8_t reply[512];
685 	nl_poll_t ctx;
686 	ssize_t r;
687 	int ok;
688 
689 	if (nl_nfc_poll(nl, dev) < 0) {
690 		fido_log_debug("%s: nl_nfc_poll", __func__);
691 		return (-1);
692 	}
693 #ifndef FIDO_FUZZ
694 	if (setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
695 	    &nl->nfc_mcastgrp, sizeof(nl->nfc_mcastgrp)) == -1) {
696 		fido_log_error(errno, "%s: setsockopt add", __func__);
697 		return (-1);
698 	}
699 #endif
700 	r = nlmsg_rx(nl->fd, reply, sizeof(reply), NETLINK_POLL_MS);
701 #ifndef FIDO_FUZZ
702 	if (setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
703 	    &nl->nfc_mcastgrp, sizeof(nl->nfc_mcastgrp)) == -1) {
704 		fido_log_error(errno, "%s: setsockopt drop", __func__);
705 		return (-1);
706 	}
707 #endif
708 	if (r < 0) {
709 		fido_log_debug("%s: nlmsg_rx", __func__);
710 		return (-1);
711 	}
712 	memset(&ctx, 0, sizeof(ctx));
713 	ctx.dev = dev;
714 	if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
715 	    NFC_EVENT_TARGETS_FOUND, &ctx, parse_nfc_event)) != 0) {
716 		fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
717 		return (-1);
718 	}
719 	if (ctx.eventcnt == 0) {
720 		fido_log_debug("%s: dev 0x%x not observed", __func__, dev);
721 		return (-1);
722 	}
723 	if (nl_dump_nfc_target(nl, dev, target, -1) < 0) {
724 		fido_log_debug("%s: nl_dump_nfc_target", __func__);
725 		return (-1);
726 	}
727 
728 	return (0);
729 }
730 
731 void
732 fido_nl_free(fido_nl_t **nlp)
733 {
734 	fido_nl_t *nl;
735 
736 	if (nlp == NULL || (nl = *nlp) == NULL)
737 		return;
738 	if (nl->fd != -1 && close(nl->fd) == -1)
739 		fido_log_error(errno, "%s: close", __func__);
740 
741 	free(nl);
742 	*nlp = NULL;
743 }
744 
745 fido_nl_t *
746 fido_nl_new(void)
747 {
748 	fido_nl_t *nl;
749 	int ok = -1;
750 
751 	if ((nl = calloc(1, sizeof(*nl))) == NULL)
752 		return (NULL);
753 	if ((nl->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC,
754 	    NETLINK_GENERIC)) == -1) {
755 		fido_log_error(errno, "%s: socket", __func__);
756 		goto fail;
757 	}
758 	nl->saddr.nl_family = AF_NETLINK;
759 	if (bind(nl->fd, (struct sockaddr *)&nl->saddr,
760 	    sizeof(nl->saddr)) == -1) {
761 		fido_log_error(errno, "%s: bind", __func__);
762 		goto fail;
763 	}
764 	if (nl_get_nfc_family(nl->fd, &nl->nfc_type, &nl->nfc_mcastgrp) < 0) {
765 		fido_log_debug("%s: nl_get_nfc_family", __func__);
766 		goto fail;
767 	}
768 
769 	ok = 0;
770 fail:
771 	if (ok < 0)
772 		fido_nl_free(&nl);
773 
774 	return (nl);
775 }
776 
777 #ifdef FIDO_FUZZ
778 void
779 set_netlink_io_functions(ssize_t (*read_f)(int, void *, size_t),
780     ssize_t (*write_f)(int, const void *, size_t))
781 {
782 	fuzz_read = read_f;
783 	fuzz_write = write_f;
784 }
785 #endif
786