Coverage Report

Created: 2026-04-08 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/libfido2/src/netlink.c
Line
Count
Source
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
2.27M
# define READ   fuzz_read
24
2.27M
# 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
4.71k
#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
4.53M
{
87
4.53M
        return (&m->u.nlmsg);
88
4.53M
}
89
90
static size_t
91
nlmsg_len(const nlmsgbuf_t *m)
92
6.80M
{
93
6.80M
        return (m->u.nlmsg.nlmsg_len);
94
6.80M
}
95
96
static uint16_t
97
nlmsg_type(const nlmsgbuf_t *m)
98
94.0k
{
99
94.0k
        return (m->u.nlmsg.nlmsg_type);
100
94.0k
}
101
102
static nlmsgbuf_t *
103
nlmsg_new(uint16_t type, uint16_t flags, size_t len)
104
2.34M
{
105
2.34M
        nlmsgbuf_t *m;
106
2.34M
        size_t siz;
107
108
2.34M
        if (len > SIZE_MAX - sizeof(*m) ||
109
2.34M
            (siz = sizeof(*m) + len) > UINT16_MAX ||
110
2.34M
            (m = calloc(1, siz)) == NULL)
111
6.53k
                return (NULL);
112
113
2.33M
        m->siz = siz;
114
2.33M
        m->len = len;
115
2.33M
        m->ptr = m->payload;
116
2.33M
        m->u.nlmsg.nlmsg_type = type;
117
2.33M
        m->u.nlmsg.nlmsg_flags = NLM_F_REQUEST | flags;
118
2.33M
        m->u.nlmsg.nlmsg_len = NLMSG_HDRLEN;
119
120
2.33M
        return (m);
121
2.34M
}
122
123
static nlamsgbuf_t *
124
nla_from_buf(const unsigned char **ptr, size_t *len)
125
208k
{
126
208k
        nlamsgbuf_t h, *a;
127
208k
        size_t nlalen, skip;
128
129
208k
        if (*len < sizeof(h.u))
130
24.7k
                return (NULL);
131
132
183k
        memset(&h, 0, sizeof(h));
133
183k
        memcpy(&h.u, *ptr, sizeof(h.u));
134
135
183k
        if ((nlalen = h.u.nla.nla_len) < sizeof(h.u) || nlalen > *len ||
136
183k
            nlalen - sizeof(h.u) > UINT16_MAX ||
137
183k
            nlalen > SIZE_MAX - sizeof(*a) ||
138
183k
            (skip = NLMSG_ALIGN(nlalen)) > *len ||
139
183k
            (a = calloc(1, sizeof(*a) + nlalen - sizeof(h.u))) == NULL)
140
21.3k
                return (NULL);
141
142
162k
        memcpy(&a->u, *ptr, nlalen);
143
162k
        a->siz = sizeof(*a) + nlalen - sizeof(h.u);
144
162k
        a->ptr = a->payload;
145
162k
        a->len = nlalen - sizeof(h.u);
146
162k
        *ptr += skip;
147
162k
        *len -= skip;
148
149
162k
        return (a);
150
183k
}
151
152
static nlamsgbuf_t *
153
nla_getattr(nlamsgbuf_t *a)
154
76.0k
{
155
76.0k
        return (nla_from_buf((void *)&a->ptr, &a->len));
156
76.0k
}
157
158
static uint16_t
159
nla_type(const nlamsgbuf_t *a)
160
232k
{
161
232k
        return (a->u.nla.nla_type);
162
232k
}
163
164
static nlamsgbuf_t *
165
nlmsg_getattr(nlmsgbuf_t *m)
166
132k
{
167
132k
        return (nla_from_buf((void *)&m->ptr, &m->len));
168
132k
}
169
170
static int
171
nla_read(nlamsgbuf_t *a, void *buf, size_t cnt)
172
27.9k
{
173
27.9k
        if (cnt > a->u.nla.nla_len ||
174
27.9k
            fido_buf_read((void *)&a->ptr, &a->len, buf, cnt) < 0)
175
183
                return (-1);
176
177
27.7k
        a->u.nla.nla_len = (uint16_t)(a->u.nla.nla_len - cnt);
178
179
27.7k
        return (0);
180
27.9k
}
181
182
static nlmsgbuf_t *
183
nlmsg_from_buf(const unsigned char **ptr, size_t *len)
184
60.2k
{
185
60.2k
        nlmsgbuf_t h, *m;
186
60.2k
        size_t msglen, skip;
187
188
60.2k
        if (*len < sizeof(h.u))
189
3.30k
                return (NULL);
190
191
56.9k
        memset(&h, 0, sizeof(h));
192
56.9k
        memcpy(&h.u, *ptr, sizeof(h.u));
193
194
56.9k
        if ((msglen = h.u.nlmsg.nlmsg_len) < sizeof(h.u) || msglen > *len ||
195
56.9k
            msglen - sizeof(h.u) > UINT16_MAX ||
196
56.9k
            (skip = NLMSG_ALIGN(msglen)) > *len ||
197
56.9k
            (m = nlmsg_new(0, 0, msglen - sizeof(h.u))) == NULL)
198
6.37k
                return (NULL);
199
200
50.6k
        memcpy(&m->u, *ptr, msglen);
201
50.6k
        *ptr += skip;
202
50.6k
        *len -= skip;
203
204
50.6k
        return (m);
205
56.9k
}
206
207
static int
208
nlmsg_read(nlmsgbuf_t *m, void *buf, size_t cnt)
209
33.2k
{
210
33.2k
        if (cnt > m->u.nlmsg.nlmsg_len ||
211
33.2k
            fido_buf_read((void *)&m->ptr, &m->len, buf, cnt) < 0)
212
2.22k
                return (-1);
213
214
31.0k
        m->u.nlmsg.nlmsg_len = (uint32_t)(m->u.nlmsg.nlmsg_len - cnt);
215
216
31.0k
        return (0);
217
33.2k
}
218
219
static int
220
nlmsg_write(nlmsgbuf_t *m, const void *buf, size_t cnt)
221
15.9M
{
222
15.9M
        if (cnt > UINT32_MAX - m->u.nlmsg.nlmsg_len ||
223
15.9M
            fido_buf_write(&m->ptr, &m->len, buf, cnt) < 0)
224
0
                return (-1);
225
226
15.9M
        m->u.nlmsg.nlmsg_len = (uint32_t)(m->u.nlmsg.nlmsg_len + cnt);
227
228
15.9M
        return (0);
229
15.9M
}
230
231
static int
232
nlmsg_set_genl(nlmsgbuf_t *m, uint8_t cmd)
233
2.28M
{
234
2.28M
        genlmsgbuf_t g;
235
236
2.28M
        memset(&g, 0, sizeof(g));
237
2.28M
        g.u.genl.cmd = cmd;
238
2.28M
        g.u.genl.version = NFC_GENL_VERSION;
239
240
2.28M
        return (nlmsg_write(m, &g, sizeof(g)));
241
2.28M
}
242
243
static int
244
nlmsg_get_genl(nlmsgbuf_t *m, uint8_t cmd)
245
26.0k
{
246
26.0k
        genlmsgbuf_t g;
247
248
26.0k
        memset(&g, 0, sizeof(g));
249
250
26.0k
        if (nlmsg_read(m, &g, sizeof(g)) < 0 || g.u.genl.cmd != cmd)
251
6.81k
                return (-1);
252
253
19.2k
        return (0);
254
26.0k
}
255
256
static int
257
nlmsg_get_status(nlmsgbuf_t *m)
258
7.17k
{
259
7.17k
        int status;
260
261
7.17k
        if (nlmsg_read(m, &status, sizeof(status)) < 0 || status == INT_MIN)
262
201
                return (-1);
263
6.97k
        if (status < 0)
264
882
                status = -status;
265
266
6.97k
        return (status);
267
7.17k
}
268
269
static int
270
nlmsg_setattr(nlmsgbuf_t *m, uint16_t type, const void *ptr, size_t len)
271
4.55M
{
272
4.55M
        int r;
273
4.55M
        char *padding;
274
4.55M
        size_t skip;
275
4.55M
        nlamsgbuf_t a;
276
277
4.55M
        if ((skip = NLMSG_ALIGN(len)) > UINT16_MAX - sizeof(a.u) ||
278
4.55M
            skip < len || (padding = calloc(1, skip - len)) == NULL)
279
11.7k
                return (-1);
280
281
4.54M
        memset(&a, 0, sizeof(a));
282
4.54M
        a.u.nla.nla_type = type;
283
4.54M
        a.u.nla.nla_len = (uint16_t)(len + sizeof(a.u));
284
4.54M
        r = nlmsg_write(m, &a.u, sizeof(a.u)) < 0 ||
285
4.54M
            nlmsg_write(m, ptr, len) < 0 ||
286
4.54M
            nlmsg_write(m, padding, skip - len) < 0 ? -1 : 0;
287
288
4.54M
        free(padding);
289
290
4.54M
        return (r);
291
4.55M
}
292
293
static int
294
nlmsg_set_u16(nlmsgbuf_t *m, uint16_t type, uint16_t val)
295
2.27M
{
296
2.27M
        return (nlmsg_setattr(m, type, &val, sizeof(val)));
297
2.27M
}
298
299
static int
300
nlmsg_set_u32(nlmsgbuf_t *m, uint16_t type, uint32_t val)
301
18.5k
{
302
18.5k
        return (nlmsg_setattr(m, type, &val, sizeof(val)));
303
18.5k
}
304
305
static int
306
nlmsg_set_str(nlmsgbuf_t *m, uint16_t type, const char *val)
307
2.26M
{
308
2.26M
        return (nlmsg_setattr(m, type, val, strlen(val) + 1));
309
2.26M
}
310
311
static int
312
nla_get_u16(nlamsgbuf_t *a, uint16_t *v)
313
9.63k
{
314
9.63k
        return (nla_read(a, v, sizeof(*v)));
315
9.63k
}
316
317
static int
318
nla_get_u32(nlamsgbuf_t *a, uint32_t *v)
319
12.0k
{
320
12.0k
        return (nla_read(a, v, sizeof(*v)));
321
12.0k
}
322
323
static char *
324
nla_get_str(nlamsgbuf_t *a)
325
7.01k
{
326
7.01k
        size_t n;
327
7.01k
        char *s = NULL;
328
329
7.01k
        if ((n = a->len) < 1 || a->ptr[n - 1] != '\0' ||
330
7.01k
            (s = calloc(1, n)) == NULL || nla_read(a, s, n) < 0) {
331
716
                free(s);
332
716
                return (NULL);
333
716
        }
334
6.30k
        s[n - 1] = '\0';
335
336
6.30k
        return (s);
337
7.01k
}
338
339
static int
340
nlmsg_tx(int fd, const nlmsgbuf_t *m)
341
2.27M
{
342
2.27M
        ssize_t r;
343
344
2.27M
        if ((r = WRITE(fd, nlmsg_ptr(m), nlmsg_len(m))) == -1) {
345
6.02k
                fido_log_error(errno, "%s: write", __func__);
346
6.02k
                return (-1);
347
6.02k
        }
348
2.26M
        if (r < 0 || (size_t)r != nlmsg_len(m)) {
349
0
                fido_log_debug("%s: %zd != %zu", __func__, r, nlmsg_len(m));
350
0
                return (-1);
351
0
        }
352
2.26M
        fido_log_xxd(nlmsg_ptr(m), nlmsg_len(m), "%s", __func__);
353
354
2.26M
        return (0);
355
2.26M
}
356
357
static ssize_t
358
nlmsg_rx(int fd, unsigned char *ptr, size_t len, int ms)
359
2.27M
{
360
2.27M
        ssize_t r;
361
362
2.27M
        if (len > SSIZE_MAX) {
363
0
                fido_log_debug("%s: len", __func__);
364
0
                return (-1);
365
0
        }
366
2.27M
        if (fido_hid_unix_wait(fd, ms, NULL) < 0) {
367
0
                fido_log_debug("%s: fido_hid_unix_wait", __func__);
368
0
                return (-1);
369
0
        }
370
2.27M
        if ((r = READ(fd, ptr, len)) == -1) {
371
6.09k
                fido_log_error(errno, "%s: read %zd", __func__, r);
372
6.09k
                return (-1);
373
6.09k
        }
374
2.26M
        fido_log_xxd(ptr, (size_t)r, "%s", __func__);
375
376
2.26M
        return (r);
377
2.27M
}
378
379
static int
380
nlmsg_iter(nlmsgbuf_t *m, void *arg, int (*parser)(nlamsgbuf_t *, void *))
381
18.8k
{
382
18.8k
        nlamsgbuf_t *a;
383
18.8k
        int r;
384
385
132k
        while ((a = nlmsg_getattr(m)) != NULL) {
386
117k
                r = parser(a, arg);
387
117k
                free(a);
388
117k
                if (r < 0) {
389
4.56k
                        fido_log_debug("%s: parser", __func__);
390
4.56k
                        return (-1);
391
4.56k
                }
392
117k
        }
393
394
14.3k
        return (0);
395
18.8k
}
396
397
static int
398
nla_iter(nlamsgbuf_t *g, void *arg, int (*parser)(nlamsgbuf_t *, void *))
399
40.7k
{
400
40.7k
        nlamsgbuf_t *a;
401
40.7k
        int r;
402
403
76.0k
        while ((a = nla_getattr(g)) != NULL) {
404
44.1k
                r = parser(a, arg);
405
44.1k
                free(a);
406
44.1k
                if (r < 0) {
407
8.88k
                        fido_log_debug("%s: parser", __func__);
408
8.88k
                        return (-1);
409
8.88k
                }
410
44.1k
        }
411
412
31.8k
        return (0);
413
40.7k
}
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
2.26M
{
419
2.26M
        nlmsgbuf_t *m;
420
2.26M
        int r;
421
422
2.30M
        while (blob_len) {
423
60.2k
                if ((m = nlmsg_from_buf(&blob, &blob_len)) == NULL) {
424
9.68k
                        fido_log_debug("%s: nlmsg", __func__);
425
9.68k
                        return (-1);
426
9.68k
                }
427
50.6k
                if (nlmsg_type(m) == NLMSG_ERROR) {
428
7.17k
                        r = nlmsg_get_status(m);
429
7.17k
                        free(m);
430
7.17k
                        return (r);
431
7.17k
                }
432
43.4k
                if (nlmsg_type(m) != msg_type ||
433
43.4k
                    nlmsg_get_genl(m, genl_cmd) < 0) {
434
24.2k
                        fido_log_debug("%s: skipping", __func__);
435
24.2k
                        free(m);
436
24.2k
                        continue;
437
24.2k
                }
438
19.2k
                if (parser != NULL && nlmsg_iter(m, arg, parser) < 0) {
439
4.56k
                        fido_log_debug("%s: nlmsg_iter", __func__);
440
4.56k
                        free(m);
441
4.56k
                        return (-1);
442
4.56k
                }
443
14.6k
                free(m);
444
14.6k
        }
445
446
2.24M
        return (0);
447
2.26M
}
448
449
static int
450
parse_mcastgrp(nlamsgbuf_t *a, void *arg)
451
22.1k
{
452
22.1k
        nl_family_t *family = arg;
453
22.1k
        char *name;
454
455
22.1k
        switch (nla_type(a)) {
456
7.01k
        case CTRL_ATTR_MCAST_GRP_NAME:
457
7.01k
                if ((name = nla_get_str(a)) == NULL ||
458
7.01k
                    strcmp(name, NFC_GENL_MCAST_EVENT_NAME) != 0) {
459
4.37k
                        free(name);
460
4.37k
                        return (-1); /* XXX skip? */
461
4.37k
                }
462
2.63k
                free(name);
463
2.63k
                return (0);
464
12.2k
        case CTRL_ATTR_MCAST_GRP_ID:
465
12.2k
                if (family->mcastgrp)
466
3.68k
                        break;
467
8.51k
                if (nla_get_u32(a, &family->mcastgrp) < 0) {
468
63
                        fido_log_debug("%s: group", __func__);
469
63
                        return (-1);
470
63
                }
471
8.45k
                return (0);
472
22.1k
        }
473
474
6.64k
        fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
475
476
6.64k
        return (0);
477
22.1k
}
478
479
static int
480
parse_mcastgrps(nlamsgbuf_t *a, void *arg)
481
21.9k
{
482
21.9k
        return (nla_iter(a, arg, parse_mcastgrp));
483
21.9k
}
484
485
static int
486
parse_family(nlamsgbuf_t *a, void *arg)
487
111k
{
488
111k
        nl_family_t *family = arg;
489
490
111k
        switch (nla_type(a)) {
491
16.4k
        case CTRL_ATTR_FAMILY_ID:
492
16.4k
                if (family->id)
493
6.79k
                        break;
494
9.63k
                if (nla_get_u16(a, &family->id) < 0) {
495
90
                        fido_log_debug("%s: id", __func__);
496
90
                        return (-1);
497
90
                }
498
9.54k
                return (0);
499
18.7k
        case CTRL_ATTR_MCAST_GROUPS:
500
18.7k
                return (nla_iter(a, family, parse_mcastgrps));
501
111k
        }
502
503
83.2k
        fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
504
505
83.2k
        return (0);
506
111k
}
507
508
static int
509
nl_get_nfc_family(int fd, uint16_t *type, uint32_t *mcastgrp)
510
2.27M
{
511
2.27M
        nlmsgbuf_t *m;
512
2.27M
        uint8_t reply[512];
513
2.27M
        nl_family_t family;
514
2.27M
        ssize_t r;
515
2.27M
        int ok;
516
517
2.27M
        if ((m = nlmsg_new(GENL_ID_CTRL, 0, 64)) == NULL ||
518
2.27M
            nlmsg_set_genl(m, CTRL_CMD_GETFAMILY) < 0 ||
519
2.27M
            nlmsg_set_u16(m, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL) < 0 ||
520
2.27M
            nlmsg_set_str(m, CTRL_ATTR_FAMILY_NAME, NFC_GENL_NAME) < 0 ||
521
2.27M
            nlmsg_tx(fd, m) < 0) {
522
22.4k
                free(m);
523
22.4k
                return (-1);
524
22.4k
        }
525
2.25M
        free(m);
526
2.25M
        memset(&family, 0, sizeof(family));
527
2.25M
        if ((r = nlmsg_rx(fd, reply, sizeof(reply), -1)) < 0) {
528
5.64k
                fido_log_debug("%s: nlmsg_rx", __func__);
529
5.64k
                return (-1);
530
5.64k
        }
531
2.24M
        if ((ok = nl_parse_reply(reply, (size_t)r, GENL_ID_CTRL,
532
2.24M
            CTRL_CMD_NEWFAMILY, &family, parse_family)) != 0) {
533
12.1k
                fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
534
12.1k
                return (-1);
535
12.1k
        }
536
2.23M
        if (family.id == 0 || family.mcastgrp == 0) {
537
2.22M
                fido_log_debug("%s: missing attr", __func__);
538
2.22M
                return (-1);
539
2.22M
        }
540
6.46k
        *type = family.id;
541
6.46k
        *mcastgrp = family.mcastgrp;
542
543
6.46k
        return (0);
544
2.23M
}
545
546
static int
547
parse_target(nlamsgbuf_t *a, void *arg)
548
1.22k
{
549
1.22k
        nl_target_t *t = arg;
550
551
1.22k
        if (t->found || nla_type(a) != NFC_ATTR_TARGET_INDEX) {
552
1.12k
                fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
553
1.12k
                return (0);
554
1.12k
        }
555
98
        if (nla_get_u32(a, t->value) < 0) {
556
15
                fido_log_debug("%s: target", __func__);
557
15
                return (-1);
558
15
        }
559
83
        t->found = 1;
560
561
83
        return (0);
562
98
}
563
564
int
565
fido_nl_power_nfc(fido_nl_t *nl, uint32_t dev)
566
6.46k
{
567
6.46k
        nlmsgbuf_t *m;
568
6.46k
        uint8_t reply[512];
569
6.46k
        ssize_t r;
570
6.46k
        int ok;
571
572
6.46k
        if ((m = nlmsg_new(nl->nfc_type, NLM_F_ACK, 64)) == NULL ||
573
6.46k
            nlmsg_set_genl(m, NFC_CMD_DEV_UP) < 0 ||
574
6.46k
            nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 ||
575
6.46k
            nlmsg_tx(nl->fd, m) < 0) {
576
1.53k
                free(m);
577
1.53k
                return (-1);
578
1.53k
        }
579
4.92k
        free(m);
580
4.92k
        if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1)) < 0) {
581
393
                fido_log_debug("%s: nlmsg_rx", __func__);
582
393
                return (-1);
583
393
        }
584
4.53k
        if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
585
4.53k
            NFC_CMD_DEV_UP, NULL, NULL)) != 0 && ok != EALREADY) {
586
2.16k
                fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
587
2.16k
                return (-1);
588
2.16k
        }
589
590
2.37k
        return (0);
591
4.53k
}
592
593
static int
594
nl_nfc_poll(fido_nl_t *nl, uint32_t dev)
595
6.18k
{
596
6.18k
        nlmsgbuf_t *m;
597
6.18k
        uint8_t reply[512];
598
6.18k
        ssize_t r;
599
6.18k
        int ok;
600
601
6.18k
        if ((m = nlmsg_new(nl->nfc_type, NLM_F_ACK, 64)) == NULL ||
602
6.18k
            nlmsg_set_genl(m, NFC_CMD_START_POLL) < 0 ||
603
6.18k
            nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 ||
604
6.18k
            nlmsg_set_u32(m, NFC_ATTR_PROTOCOLS, NFC_PROTO_ISO14443_MASK) < 0 ||
605
6.18k
            nlmsg_tx(nl->fd, m) < 0) {
606
141
                free(m);
607
141
                return (-1);
608
141
        }
609
6.04k
        free(m);
610
6.04k
        if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1)) < 0) {
611
30
                fido_log_debug("%s: nlmsg_rx", __func__);
612
30
                return (-1);
613
30
        }
614
6.01k
        if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
615
6.01k
            NFC_CMD_START_POLL, NULL, NULL)) != 0) {
616
1.29k
                fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
617
1.29k
                return (-1);
618
1.29k
        }
619
620
4.71k
        return (0);
621
6.01k
}
622
623
static int
624
nl_dump_nfc_target(fido_nl_t *nl, uint32_t dev, uint32_t *target, int ms)
625
636
{
626
636
        nlmsgbuf_t *m;
627
636
        nl_target_t t;
628
636
        uint8_t reply[512];
629
636
        ssize_t r;
630
636
        int ok;
631
632
636
        if ((m = nlmsg_new(nl->nfc_type, NLM_F_DUMP, 64)) == NULL ||
633
636
            nlmsg_set_genl(m, NFC_CMD_GET_TARGET) < 0 ||
634
636
            nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 ||
635
636
            nlmsg_tx(nl->fd, m) < 0) {
636
24
                free(m);
637
24
                return (-1);
638
24
        }
639
612
        free(m);
640
612
        if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), ms)) < 0) {
641
9
                fido_log_debug("%s: nlmsg_rx", __func__);
642
9
                return (-1);
643
9
        }
644
603
        memset(&t, 0, sizeof(t));
645
603
        t.value = target;
646
603
        if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
647
603
            NFC_CMD_GET_TARGET, &t, parse_target)) != 0) {
648
398
                fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
649
398
                return (-1);
650
398
        }
651
205
        if (!t.found) {
652
137
                fido_log_debug("%s: target not found", __func__);
653
137
                return (-1);
654
137
        }
655
656
68
        return (0);
657
205
}
658
659
static int
660
parse_nfc_event(nlamsgbuf_t *a, void *arg)
661
5.03k
{
662
5.03k
        nl_poll_t *ctx = arg;
663
5.03k
        uint32_t dev;
664
665
5.03k
        if (nla_type(a) != NFC_ATTR_DEVICE_INDEX) {
666
1.62k
                fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
667
1.62k
                return (0);
668
1.62k
        }
669
3.41k
        if (nla_get_u32(a, &dev) < 0) {
670
15
                fido_log_debug("%s: dev", __func__);
671
15
                return (-1);
672
15
        }
673
3.39k
        if (dev == ctx->dev)
674
1.00k
                ctx->eventcnt++;
675
2.39k
        else
676
2.39k
                fido_log_debug("%s: ignoring dev 0x%x", __func__, dev);
677
678
3.39k
        return (0);
679
3.41k
}
680
681
int
682
fido_nl_get_nfc_target(fido_nl_t *nl, uint32_t dev, uint32_t *target)
683
6.18k
{
684
6.18k
        uint8_t reply[512];
685
6.18k
        nl_poll_t ctx;
686
6.18k
        ssize_t r;
687
6.18k
        int ok;
688
689
6.18k
        if (nl_nfc_poll(nl, dev) < 0) {
690
1.46k
                fido_log_debug("%s: nl_nfc_poll", __func__);
691
1.46k
                return (-1);
692
1.46k
        }
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
4.71k
        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
4.71k
        if (r < 0) {
709
22
                fido_log_debug("%s: nlmsg_rx", __func__);
710
22
                return (-1);
711
22
        }
712
4.69k
        memset(&ctx, 0, sizeof(ctx));
713
4.69k
        ctx.dev = dev;
714
4.69k
        if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
715
4.69k
            NFC_EVENT_TARGETS_FOUND, &ctx, parse_nfc_event)) != 0) {
716
1.06k
                fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
717
1.06k
                return (-1);
718
1.06k
        }
719
3.63k
        if (ctx.eventcnt == 0) {
720
3.00k
                fido_log_debug("%s: dev 0x%x not observed", __func__, dev);
721
3.00k
                return (-1);
722
3.00k
        }
723
636
        if (nl_dump_nfc_target(nl, dev, target, -1) < 0) {
724
568
                fido_log_debug("%s: nl_dump_nfc_target", __func__);
725
568
                return (-1);
726
568
        }
727
728
68
        return (0);
729
636
}
730
731
void
732
fido_nl_free(fido_nl_t **nlp)
733
2.28M
{
734
2.28M
        fido_nl_t *nl;
735
736
2.28M
        if (nlp == NULL || (nl = *nlp) == NULL)
737
0
                return;
738
2.28M
        if (nl->fd != -1 && close(nl->fd) == -1)
739
0
                fido_log_error(errno, "%s: close", __func__);
740
741
2.28M
        free(nl);
742
2.28M
        *nlp = NULL;
743
2.28M
}
744
745
fido_nl_t *
746
fido_nl_new(void)
747
2.28M
{
748
2.28M
        fido_nl_t *nl;
749
2.28M
        int ok = -1;
750
751
2.28M
        if ((nl = calloc(1, sizeof(*nl))) == NULL)
752
5.69k
                return (NULL);
753
2.28M
        if ((nl->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC,
754
2.28M
            NETLINK_GENERIC)) == -1) {
755
0
                fido_log_error(errno, "%s: socket", __func__);
756
0
                goto fail;
757
0
        }
758
2.28M
        nl->saddr.nl_family = AF_NETLINK;
759
2.28M
        if (bind(nl->fd, (struct sockaddr *)&nl->saddr,
760
2.28M
            sizeof(nl->saddr)) == -1) {
761
5.61k
                fido_log_error(errno, "%s: bind", __func__);
762
5.61k
                goto fail;
763
5.61k
        }
764
2.27M
        if (nl_get_nfc_family(nl->fd, &nl->nfc_type, &nl->nfc_mcastgrp) < 0) {
765
2.27M
                fido_log_debug("%s: nl_get_nfc_family", __func__);
766
2.27M
                goto fail;
767
2.27M
        }
768
769
6.46k
        ok = 0;
770
2.28M
fail:
771
2.28M
        if (ok < 0)
772
2.27M
                fido_nl_free(&nl);
773
774
2.28M
        return (nl);
775
6.46k
}
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
21.8k
{
782
21.8k
        fuzz_read = read_f;
783
21.8k
        fuzz_write = write_f;
784
21.8k
}
785
#endif