Home | History | Annotate | Line # | Download | only in src
      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