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