xref: /netbsd-src/external/bsd/libfido2/dist/src/nfc_linux.c (revision 2d40c4512a84c0d064ec30a492c5e2a14d230bc3)
195dbdf32Schristos /*
2*2d40c451Schristos  * Copyright (c) 2020-2022 Yubico AB. All rights reserved.
395dbdf32Schristos  * Use of this source code is governed by a BSD-style
495dbdf32Schristos  * license that can be found in the LICENSE file.
5*2d40c451Schristos  * SPDX-License-Identifier: BSD-2-Clause
695dbdf32Schristos  */
795dbdf32Schristos 
895dbdf32Schristos #include <sys/types.h>
995dbdf32Schristos #include <sys/uio.h>
1095dbdf32Schristos #include <sys/socket.h>
1195dbdf32Schristos 
1295dbdf32Schristos #include <linux/nfc.h>
1395dbdf32Schristos 
1495dbdf32Schristos #include <errno.h>
1595dbdf32Schristos #include <libudev.h>
1695dbdf32Schristos #include <signal.h>
17*2d40c451Schristos #include <stdio.h>
18*2d40c451Schristos #include <string.h>
1995dbdf32Schristos #include <unistd.h>
2095dbdf32Schristos 
2195dbdf32Schristos #include "fido.h"
2295dbdf32Schristos #include "fido/param.h"
2395dbdf32Schristos #include "netlink.h"
2495dbdf32Schristos #include "iso7816.h"
2595dbdf32Schristos 
2695dbdf32Schristos struct nfc_linux {
2795dbdf32Schristos 	int             fd;
2895dbdf32Schristos 	uint32_t        dev;
2995dbdf32Schristos 	uint32_t        target;
3095dbdf32Schristos 	sigset_t	sigmask;
3195dbdf32Schristos 	const sigset_t *sigmaskp;
3295dbdf32Schristos 	struct fido_nl *nl;
3395dbdf32Schristos };
3495dbdf32Schristos 
3595dbdf32Schristos static char *
get_parent_attr(struct udev_device * dev,const char * subsystem,const char * devtype,const char * attr)3695dbdf32Schristos get_parent_attr(struct udev_device *dev, const char *subsystem,
3795dbdf32Schristos     const char *devtype, const char *attr)
3895dbdf32Schristos {
3995dbdf32Schristos 	struct udev_device *parent;
4095dbdf32Schristos 	const char *value;
4195dbdf32Schristos 
4295dbdf32Schristos 	if ((parent = udev_device_get_parent_with_subsystem_devtype(dev,
4395dbdf32Schristos 	    subsystem, devtype)) == NULL || (value =
4495dbdf32Schristos 	    udev_device_get_sysattr_value(parent, attr)) == NULL)
45*2d40c451Schristos 		return NULL;
4695dbdf32Schristos 
47*2d40c451Schristos 	return strdup(value);
4895dbdf32Schristos }
4995dbdf32Schristos 
5095dbdf32Schristos static char *
get_usb_attr(struct udev_device * dev,const char * attr)5195dbdf32Schristos get_usb_attr(struct udev_device *dev, const char *attr)
5295dbdf32Schristos {
53*2d40c451Schristos 	return get_parent_attr(dev, "usb", "usb_device", attr);
5495dbdf32Schristos }
5595dbdf32Schristos 
5695dbdf32Schristos static int
copy_info(fido_dev_info_t * di,struct udev * udev,struct udev_list_entry * udev_entry)5795dbdf32Schristos copy_info(fido_dev_info_t *di, struct udev *udev,
5895dbdf32Schristos     struct udev_list_entry *udev_entry)
5995dbdf32Schristos {
6095dbdf32Schristos 	const char *name;
6195dbdf32Schristos 	char *str;
6295dbdf32Schristos 	struct udev_device *dev = NULL;
63*2d40c451Schristos 	uint64_t id;
64*2d40c451Schristos 	int ok = -1;
6595dbdf32Schristos 
6695dbdf32Schristos 	memset(di, 0, sizeof(*di));
6795dbdf32Schristos 
6895dbdf32Schristos 	if ((name = udev_list_entry_get_name(udev_entry)) == NULL ||
6995dbdf32Schristos 	    (dev = udev_device_new_from_syspath(udev, name)) == NULL)
7095dbdf32Schristos 		goto fail;
71*2d40c451Schristos 	if (asprintf(&di->path, "%s/%s", FIDO_NFC_PREFIX, name) == -1) {
72*2d40c451Schristos 		di->path = NULL;
7395dbdf32Schristos 		goto fail;
74*2d40c451Schristos 	}
75*2d40c451Schristos 	if (nfc_is_fido(di->path) == false) {
76*2d40c451Schristos 		fido_log_debug("%s: nfc_is_fido: %s", __func__, di->path);
77*2d40c451Schristos 		goto fail;
78*2d40c451Schristos 	}
79*2d40c451Schristos 	if ((di->manufacturer = get_usb_attr(dev, "manufacturer")) == NULL)
80*2d40c451Schristos 		di->manufacturer = strdup("");
81*2d40c451Schristos 	if ((di->product = get_usb_attr(dev, "product")) == NULL)
82*2d40c451Schristos 		di->product = strdup("");
83*2d40c451Schristos 	if (di->manufacturer == NULL || di->product == NULL)
84*2d40c451Schristos 		goto fail;
8595dbdf32Schristos 	/* XXX assumes USB for vendor/product info */
8695dbdf32Schristos 	if ((str = get_usb_attr(dev, "idVendor")) != NULL &&
87*2d40c451Schristos 	    fido_to_uint64(str, 16, &id) == 0 && id <= UINT16_MAX)
8895dbdf32Schristos 		di->vendor_id = (int16_t)id;
8995dbdf32Schristos 	free(str);
9095dbdf32Schristos 	if ((str = get_usb_attr(dev, "idProduct")) != NULL &&
91*2d40c451Schristos 	    fido_to_uint64(str, 16, &id) == 0 && id <= UINT16_MAX)
9295dbdf32Schristos 		di->product_id = (int16_t)id;
9395dbdf32Schristos 	free(str);
9495dbdf32Schristos 
9595dbdf32Schristos 	ok = 0;
9695dbdf32Schristos fail:
9795dbdf32Schristos 	if (dev != NULL)
9895dbdf32Schristos 		udev_device_unref(dev);
9995dbdf32Schristos 
10095dbdf32Schristos 	if (ok < 0) {
10195dbdf32Schristos 		free(di->path);
10295dbdf32Schristos 		free(di->manufacturer);
10395dbdf32Schristos 		free(di->product);
10495dbdf32Schristos 		explicit_bzero(di, sizeof(*di));
10595dbdf32Schristos 	}
10695dbdf32Schristos 
107*2d40c451Schristos 	return ok;
10895dbdf32Schristos }
10995dbdf32Schristos 
11095dbdf32Schristos static int
sysnum_from_syspath(const char * path)11195dbdf32Schristos sysnum_from_syspath(const char *path)
11295dbdf32Schristos {
11395dbdf32Schristos 	struct udev *udev = NULL;
11495dbdf32Schristos 	struct udev_device *dev = NULL;
11595dbdf32Schristos 	const char *str;
116*2d40c451Schristos 	uint64_t idx64;
117*2d40c451Schristos 	int idx = -1;
11895dbdf32Schristos 
119*2d40c451Schristos 	if ((udev = udev_new()) != NULL &&
120*2d40c451Schristos 	    (dev = udev_device_new_from_syspath(udev, path)) != NULL &&
121*2d40c451Schristos 	    (str = udev_device_get_sysnum(dev)) != NULL &&
122*2d40c451Schristos 	    fido_to_uint64(str, 10, &idx64) == 0 && idx64 < INT_MAX)
123*2d40c451Schristos 		idx = (int)idx64;
12495dbdf32Schristos 
12595dbdf32Schristos 	if (dev != NULL)
12695dbdf32Schristos 		udev_device_unref(dev);
12795dbdf32Schristos 	if (udev != NULL)
12895dbdf32Schristos 		udev_unref(udev);
12995dbdf32Schristos 
130*2d40c451Schristos 	return idx;
13195dbdf32Schristos }
13295dbdf32Schristos 
13395dbdf32Schristos int
fido_nfc_manifest(fido_dev_info_t * devlist,size_t ilen,size_t * olen)13495dbdf32Schristos fido_nfc_manifest(fido_dev_info_t *devlist, size_t ilen, size_t *olen)
13595dbdf32Schristos {
13695dbdf32Schristos 	struct udev *udev = NULL;
13795dbdf32Schristos 	struct udev_enumerate *udev_enum = NULL;
13895dbdf32Schristos 	struct udev_list_entry *udev_list;
13995dbdf32Schristos 	struct udev_list_entry *udev_entry;
14095dbdf32Schristos 	int r = FIDO_ERR_INTERNAL;
14195dbdf32Schristos 
14295dbdf32Schristos 	*olen = 0;
14395dbdf32Schristos 
14495dbdf32Schristos 	if (ilen == 0)
145*2d40c451Schristos 		return FIDO_OK;
14695dbdf32Schristos 
14795dbdf32Schristos 	if (devlist == NULL)
148*2d40c451Schristos 		return FIDO_ERR_INVALID_ARGUMENT;
14995dbdf32Schristos 
15095dbdf32Schristos 	if ((udev = udev_new()) == NULL ||
15195dbdf32Schristos 	    (udev_enum = udev_enumerate_new(udev)) == NULL)
15295dbdf32Schristos 		goto fail;
15395dbdf32Schristos 
15495dbdf32Schristos 	if (udev_enumerate_add_match_subsystem(udev_enum, "nfc") < 0 ||
15595dbdf32Schristos 	    udev_enumerate_scan_devices(udev_enum) < 0)
15695dbdf32Schristos 		goto fail;
15795dbdf32Schristos 
15895dbdf32Schristos 	if ((udev_list = udev_enumerate_get_list_entry(udev_enum)) == NULL) {
15995dbdf32Schristos 		r = FIDO_OK; /* zero nfc devices */
16095dbdf32Schristos 		goto fail;
16195dbdf32Schristos 	}
16295dbdf32Schristos 
16395dbdf32Schristos 	udev_list_entry_foreach(udev_entry, udev_list) {
16495dbdf32Schristos 		if (copy_info(&devlist[*olen], udev, udev_entry) == 0) {
16595dbdf32Schristos 			devlist[*olen].io = (fido_dev_io_t) {
16695dbdf32Schristos 				fido_nfc_open,
16795dbdf32Schristos 				fido_nfc_close,
16895dbdf32Schristos 				fido_nfc_read,
16995dbdf32Schristos 				fido_nfc_write,
17095dbdf32Schristos 			};
17195dbdf32Schristos 			devlist[*olen].transport = (fido_dev_transport_t) {
17295dbdf32Schristos 				fido_nfc_rx,
17395dbdf32Schristos 				fido_nfc_tx,
17495dbdf32Schristos 			};
17595dbdf32Schristos 			if (++(*olen) == ilen)
17695dbdf32Schristos 				break;
17795dbdf32Schristos 		}
17895dbdf32Schristos 	}
17995dbdf32Schristos 
18095dbdf32Schristos 	r = FIDO_OK;
18195dbdf32Schristos fail:
18295dbdf32Schristos 	if (udev_enum != NULL)
18395dbdf32Schristos 		udev_enumerate_unref(udev_enum);
18495dbdf32Schristos 	if (udev != NULL)
18595dbdf32Schristos 		udev_unref(udev);
18695dbdf32Schristos 
187*2d40c451Schristos 	return r;
18895dbdf32Schristos }
18995dbdf32Schristos 
19095dbdf32Schristos static int
nfc_target_connect(struct nfc_linux * ctx)19195dbdf32Schristos nfc_target_connect(struct nfc_linux *ctx)
19295dbdf32Schristos {
19395dbdf32Schristos 	struct sockaddr_nfc sa;
19495dbdf32Schristos 
19595dbdf32Schristos 	memset(&sa, 0, sizeof(sa));
19695dbdf32Schristos 	sa.sa_family = AF_NFC;
19795dbdf32Schristos 	sa.dev_idx = ctx->dev;
19895dbdf32Schristos 	sa.target_idx = ctx->target;
19995dbdf32Schristos 	sa.nfc_protocol = NFC_PROTO_ISO14443;
20095dbdf32Schristos 
20195dbdf32Schristos 	if ((ctx->fd = socket(AF_NFC, SOCK_SEQPACKET | SOCK_CLOEXEC,
20295dbdf32Schristos 	    NFC_SOCKPROTO_RAW)) == -1) {
20395dbdf32Schristos 		fido_log_error(errno, "%s: socket", __func__);
204*2d40c451Schristos 		return -1;
20595dbdf32Schristos 	}
20695dbdf32Schristos 	if (connect(ctx->fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
20795dbdf32Schristos 		fido_log_error(errno, "%s: connect", __func__);
20895dbdf32Schristos 		if (close(ctx->fd) == -1)
20995dbdf32Schristos 			fido_log_error(errno, "%s: close", __func__);
21095dbdf32Schristos 		ctx->fd = -1;
211*2d40c451Schristos 		return -1;
21295dbdf32Schristos 	}
21395dbdf32Schristos 
214*2d40c451Schristos 	return 0;
21595dbdf32Schristos }
21695dbdf32Schristos 
21795dbdf32Schristos static void
nfc_free(struct nfc_linux ** ctx_p)21895dbdf32Schristos nfc_free(struct nfc_linux **ctx_p)
21995dbdf32Schristos {
22095dbdf32Schristos 	struct nfc_linux *ctx;
22195dbdf32Schristos 
22295dbdf32Schristos 	if (ctx_p == NULL || (ctx = *ctx_p) == NULL)
22395dbdf32Schristos 		return;
22495dbdf32Schristos 	if (ctx->fd != -1 && close(ctx->fd) == -1)
22595dbdf32Schristos 		fido_log_error(errno, "%s: close", __func__);
22695dbdf32Schristos 	if (ctx->nl != NULL)
22795dbdf32Schristos 		fido_nl_free(&ctx->nl);
22895dbdf32Schristos 
22995dbdf32Schristos 	free(ctx);
23095dbdf32Schristos 	*ctx_p = NULL;
23195dbdf32Schristos }
23295dbdf32Schristos 
23395dbdf32Schristos static struct nfc_linux *
nfc_new(uint32_t dev)23495dbdf32Schristos nfc_new(uint32_t dev)
23595dbdf32Schristos {
23695dbdf32Schristos 	struct nfc_linux *ctx;
23795dbdf32Schristos 
23895dbdf32Schristos 	if ((ctx = calloc(1, sizeof(*ctx))) == NULL ||
23995dbdf32Schristos 	    (ctx->nl = fido_nl_new()) == NULL) {
24095dbdf32Schristos 		nfc_free(&ctx);
241*2d40c451Schristos 		return NULL;
24295dbdf32Schristos 	}
24395dbdf32Schristos 
24495dbdf32Schristos 	ctx->fd = -1;
24595dbdf32Schristos 	ctx->dev = dev;
24695dbdf32Schristos 
247*2d40c451Schristos 	return ctx;
24895dbdf32Schristos }
24995dbdf32Schristos 
25095dbdf32Schristos void *
fido_nfc_open(const char * path)25195dbdf32Schristos fido_nfc_open(const char *path)
25295dbdf32Schristos {
25395dbdf32Schristos 	struct nfc_linux *ctx = NULL;
25495dbdf32Schristos 	int idx;
25595dbdf32Schristos 
256*2d40c451Schristos 	if (strncmp(path, FIDO_NFC_PREFIX, strlen(FIDO_NFC_PREFIX)) != 0) {
257*2d40c451Schristos 		fido_log_debug("%s: bad prefix", __func__);
258*2d40c451Schristos 		goto fail;
259*2d40c451Schristos 	}
260*2d40c451Schristos 	if ((idx = sysnum_from_syspath(path + strlen(FIDO_NFC_PREFIX))) < 0 ||
26195dbdf32Schristos 	    (ctx = nfc_new((uint32_t)idx)) == NULL) {
26295dbdf32Schristos 		fido_log_debug("%s: nfc_new", __func__);
26395dbdf32Schristos 		goto fail;
26495dbdf32Schristos 	}
26595dbdf32Schristos 	if (fido_nl_power_nfc(ctx->nl, ctx->dev) < 0 ||
26695dbdf32Schristos 	    fido_nl_get_nfc_target(ctx->nl, ctx->dev, &ctx->target) < 0 ||
26795dbdf32Schristos 	    nfc_target_connect(ctx) < 0) {
26895dbdf32Schristos 		fido_log_debug("%s: netlink", __func__);
26995dbdf32Schristos 		goto fail;
27095dbdf32Schristos 	}
27195dbdf32Schristos 
272*2d40c451Schristos 	return ctx;
27395dbdf32Schristos fail:
27495dbdf32Schristos 	nfc_free(&ctx);
275*2d40c451Schristos 	return NULL;
27695dbdf32Schristos }
27795dbdf32Schristos 
27895dbdf32Schristos void
fido_nfc_close(void * handle)27995dbdf32Schristos fido_nfc_close(void *handle)
28095dbdf32Schristos {
28195dbdf32Schristos 	struct nfc_linux *ctx = handle;
28295dbdf32Schristos 
28395dbdf32Schristos 	nfc_free(&ctx);
28495dbdf32Schristos }
28595dbdf32Schristos 
28695dbdf32Schristos int
fido_nfc_set_sigmask(void * handle,const fido_sigset_t * sigmask)28795dbdf32Schristos fido_nfc_set_sigmask(void *handle, const fido_sigset_t *sigmask)
28895dbdf32Schristos {
28995dbdf32Schristos 	struct nfc_linux *ctx = handle;
29095dbdf32Schristos 
29195dbdf32Schristos 	ctx->sigmask = *sigmask;
29295dbdf32Schristos 	ctx->sigmaskp = &ctx->sigmask;
29395dbdf32Schristos 
294*2d40c451Schristos 	return FIDO_OK;
29595dbdf32Schristos }
29695dbdf32Schristos 
29795dbdf32Schristos int
fido_nfc_read(void * handle,unsigned char * buf,size_t len,int ms)29895dbdf32Schristos fido_nfc_read(void *handle, unsigned char *buf, size_t len, int ms)
29995dbdf32Schristos {
30095dbdf32Schristos 	struct nfc_linux *ctx = handle;
30195dbdf32Schristos 	struct iovec iov[2];
30295dbdf32Schristos 	uint8_t preamble;
30395dbdf32Schristos 	ssize_t	r;
30495dbdf32Schristos 
30595dbdf32Schristos 	memset(&iov, 0, sizeof(iov));
30695dbdf32Schristos 	iov[0].iov_base = &preamble;
30795dbdf32Schristos 	iov[0].iov_len = sizeof(preamble);
30895dbdf32Schristos 	iov[1].iov_base = buf;
30995dbdf32Schristos 	iov[1].iov_len = len;
31095dbdf32Schristos 
31195dbdf32Schristos 	if (fido_hid_unix_wait(ctx->fd, ms, ctx->sigmaskp) < 0) {
31295dbdf32Schristos 		fido_log_debug("%s: fido_hid_unix_wait", __func__);
313*2d40c451Schristos 		return -1;
31495dbdf32Schristos 	}
31595dbdf32Schristos 	if ((r = readv(ctx->fd, iov, nitems(iov))) == -1) {
31695dbdf32Schristos 		fido_log_error(errno, "%s: read", __func__);
317*2d40c451Schristos 		return -1;
31895dbdf32Schristos 	}
31995dbdf32Schristos 	if (r < 1) {
32095dbdf32Schristos 		fido_log_debug("%s: %zd < 1", __func__, r);
321*2d40c451Schristos 		return -1;
32295dbdf32Schristos 	}
32395dbdf32Schristos 	if (preamble != 0x00) {
32495dbdf32Schristos 		fido_log_debug("%s: preamble", __func__);
325*2d40c451Schristos 		return -1;
32695dbdf32Schristos 	}
32795dbdf32Schristos 
32895dbdf32Schristos 	r--;
32995dbdf32Schristos 	fido_log_xxd(buf, (size_t)r, "%s", __func__);
33095dbdf32Schristos 
331*2d40c451Schristos 	return (int)r;
33295dbdf32Schristos }
33395dbdf32Schristos 
33495dbdf32Schristos int
fido_nfc_write(void * handle,const unsigned char * buf,size_t len)33595dbdf32Schristos fido_nfc_write(void *handle, const unsigned char *buf, size_t len)
33695dbdf32Schristos {
33795dbdf32Schristos 	struct nfc_linux *ctx = handle;
33895dbdf32Schristos 	ssize_t	r;
33995dbdf32Schristos 
34095dbdf32Schristos 	fido_log_xxd(buf, len, "%s", __func__);
34195dbdf32Schristos 
34295dbdf32Schristos 	if (len > INT_MAX) {
34395dbdf32Schristos 		fido_log_debug("%s: len", __func__);
344*2d40c451Schristos 		return -1;
34595dbdf32Schristos 	}
34695dbdf32Schristos 	if ((r = write(ctx->fd, buf, len)) == -1) {
34795dbdf32Schristos 		fido_log_error(errno, "%s: write", __func__);
348*2d40c451Schristos 		return -1;
34995dbdf32Schristos 	}
35095dbdf32Schristos 	if (r < 0 || (size_t)r != len) {
35195dbdf32Schristos 		fido_log_debug("%s: %zd != %zu", __func__, r, len);
352*2d40c451Schristos 		return -1;
35395dbdf32Schristos 	}
35495dbdf32Schristos 
355*2d40c451Schristos 	return (int)r;
35695dbdf32Schristos }
357