diff options
Diffstat (limited to 'src/passkey_child/passkey_child_devices.c')
-rw-r--r-- | src/passkey_child/passkey_child_devices.c | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/src/passkey_child/passkey_child_devices.c b/src/passkey_child/passkey_child_devices.c new file mode 100644 index 0000000..2011b12 --- /dev/null +++ b/src/passkey_child/passkey_child_devices.c @@ -0,0 +1,246 @@ +/* + SSSD + + Helper child to commmunicate with passkey devices + + Authors: + Iker Pedrosa <ipedrosa@redhat.com> + + 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 <http://www.gnu.org/licenses/>. +*/ + +#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; +} |