/* SSSD Helper child to commmunicate with passkey devices Authors: Iker Pedrosa Copyright (C) 2022 Red Hat This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "util/debug.h" #include "util/util.h" #include "passkey_child.h" errno_t list_devices(fido_dev_info_t *dev_list, size_t *dev_number) { errno_t ret; for (int i = 0; i < TIMEOUT; i += FREQUENCY) { ret = fido_dev_info_manifest(dev_list, DEVLIST_SIZE, dev_number); if (ret != FIDO_OK) { DEBUG(SSSDBG_OP_FAILURE, "Unable to discover device(s) [%d]: %s.\n", ret, fido_strerr(ret)); } if ((*dev_number) != 0) { DEBUG(SSSDBG_TRACE_FUNC, "Device found.\n"); break; } if (i < (TIMEOUT - 1)) { DEBUG(SSSDBG_TRACE_FUNC, "No device available, retrying.\n"); sleep(FREQUENCY); } } return ret; } errno_t select_device(enum action_opt action, fido_dev_info_t *dev_list, size_t dev_list_len, fido_assert_t *assert, fido_dev_t **_dev) { fido_dev_t *dev = NULL; const char *path; const fido_dev_info_t *di = NULL; errno_t ret; if (dev_list_len == 0) { DEBUG(SSSDBG_OP_FAILURE, "No device found. Aborting.\n"); ret = ENOENT; goto done; } else if (action == ACTION_REGISTER && dev_list_len == 1) { dev = fido_dev_new(); if (dev == NULL) { DEBUG(SSSDBG_OP_FAILURE, "fido_dev_new failed.\n"); ret = ENOMEM; goto done; } di = fido_dev_info_ptr(dev_list, 0); if (di == NULL) { DEBUG(SSSDBG_OP_FAILURE, "fido_dev_info_ptr failed.\n"); ret = ENOMEM; goto done; } path = fido_dev_info_path(di); if (path == NULL) { DEBUG(SSSDBG_OP_FAILURE, "fido_dev_info_path failed.\n"); ret = ENOMEM; goto done; } ret = fido_dev_open(dev, path); if (ret != FIDO_OK) { DEBUG(SSSDBG_OP_FAILURE, "fido_dev_open failed [%d]: %s.\n", ret, fido_strerr(ret)); goto done; } *_dev = dev; } else if (action == ACTION_REGISTER && dev_list_len > 1) { DEBUG(SSSDBG_OP_FAILURE, "Only one device is supported at a time. Aborting.\n"); fprintf(stderr, "Only one device is supported at a time. Aborting.\n"); ret = EPERM; goto done; } else { if (assert == NULL) { DEBUG(SSSDBG_OP_FAILURE, "assert cannot be NULL.\n"); ret = EINVAL; goto done; } ret = select_from_multiple_devices(dev_list, dev_list_len, assert, _dev); if (ret != FIDO_OK) { goto done; } } ret = EOK; done: if (ret != EOK) { if (dev != NULL) { fido_dev_close(dev); } fido_dev_free(&dev); } return ret; } errno_t select_from_multiple_devices(fido_dev_info_t *dev_list, size_t dev_list_len, fido_assert_t *assert, fido_dev_t **_dev) { fido_dev_t *dev = NULL; const fido_dev_info_t *di = NULL; const char *path; bool is_fido2; errno_t ret; DEBUG(SSSDBG_TRACE_FUNC, "Working with %ld authenticator(s).\n", dev_list_len); for (size_t i = 0; i < dev_list_len; i++) { dev = fido_dev_new(); if (dev == NULL) { DEBUG(SSSDBG_OP_FAILURE, "fido_dev_new failed.\n"); ret = ENOMEM; goto done; } di = fido_dev_info_ptr(dev_list, i); if (di == NULL) { DEBUG(SSSDBG_OP_FAILURE, "fido_dev_info_ptr failed.\n"); ret = ENOMEM; goto done; } path = fido_dev_info_path(di); if (path == NULL) { DEBUG(SSSDBG_OP_FAILURE, "fido_dev_info_path failed.\n"); ret = ENOMEM; goto done; } ret = fido_dev_open(dev, path); if (ret != FIDO_OK) { DEBUG(SSSDBG_OP_FAILURE, "fido_dev_open failed [%d]: %s.\n", ret, fido_strerr(ret)); } is_fido2 = fido_dev_is_fido2(dev); ret = fido_dev_get_assert(dev, assert, NULL); if ((is_fido2 == false && ret == FIDO_ERR_USER_PRESENCE_REQUIRED) || (is_fido2 == true && ret == FIDO_OK)) { *_dev = dev; DEBUG(SSSDBG_FUNC_DATA, "Assertion found in passkey %ld.\n", i); ret = EOK; goto done; } DEBUG(SSSDBG_FUNC_DATA, "Assertion not found in passkey %ld.\n", i); fido_dev_close(dev); fido_dev_free(&dev); } ret = FIDO_ERR_NOTFOUND; DEBUG(SSSDBG_OP_FAILURE, "Assertion not found.\n"); done: return ret; } errno_t get_device_options(fido_dev_t *dev, struct passkey_data *_data) { bool has_pin; bool has_uv; bool supports_uv; errno_t ret; has_uv = fido_dev_has_uv(dev); has_pin = fido_dev_has_pin(dev); supports_uv = fido_dev_supports_uv(dev); if (_data->user_verification == FIDO_OPT_TRUE && has_pin != true && has_uv != true) { DEBUG(SSSDBG_OP_FAILURE, "Policy enabled user-verification but there isn't any " "verification method set.\n"); ret = EINVAL; goto done; } if (_data->user_verification == FIDO_OPT_OMIT && (has_uv == true || has_pin == true)) { DEBUG(SSSDBG_CONF_SETTINGS, "Policy didn't indicate any preference for user-verification " "but the key settings are enforcing it. Thus, enforcing the " "user-verification.\n"); _data->user_verification = FIDO_OPT_TRUE; ret = EOK; goto done; } if (_data->user_verification == FIDO_OPT_FALSE && supports_uv == false) { DEBUG(SSSDBG_CONF_SETTINGS, "Policy disabled user-verification but the key doesn't support " "it. Thus, omitting the user-verification.\n"); _data->user_verification = FIDO_OPT_OMIT; ret = EOK; goto done; } ret = EOK; done: return ret; }