summaryrefslogtreecommitdiffstats
path: root/src/passkey_child
diff options
context:
space:
mode:
Diffstat (limited to 'src/passkey_child')
-rw-r--r--src/passkey_child/passkey_child.c106
-rw-r--r--src/passkey_child/passkey_child.h557
-rw-r--r--src/passkey_child/passkey_child_assert.c448
-rw-r--r--src/passkey_child/passkey_child_common.c885
-rw-r--r--src/passkey_child/passkey_child_credentials.c681
-rw-r--r--src/passkey_child/passkey_child_devices.c246
6 files changed, 2923 insertions, 0 deletions
diff --git a/src/passkey_child/passkey_child.c b/src/passkey_child/passkey_child.c
new file mode 100644
index 0000000..3209966
--- /dev/null
+++ b/src/passkey_child/passkey_child.c
@@ -0,0 +1,106 @@
+/*
+ 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 <fido.h>
+#include <fido/param.h>
+
+#include "util/debug.h"
+#include "util/util.h"
+
+#include "passkey_child.h"
+
+int main(int argc, const char *argv[])
+{
+ TALLOC_CTX *main_ctx = NULL;
+ struct passkey_data data;
+ int init_flags = 0;
+ errno_t ret = EOK;
+
+ main_ctx = talloc_new(NULL);
+ if (main_ctx == NULL) {
+ ERROR("talloc_new() failed.\n");
+ talloc_free(discard_const(debug_prg_name));
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = parse_arguments(main_ctx, argc, argv, &data);
+ if (ret != EOK) {
+ ERROR("Error parsing argument(s).\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "passkey_child started.\n");
+ talloc_steal(main_ctx, debug_prg_name);
+
+ ret = check_arguments(&data);
+ if (ret != EOK) {
+ ERROR("Invalid argument(s).\n");
+ goto done;
+ }
+
+ init_flags = (int)data.debug_libfido2 | FIDO_DISABLE_U2F_FALLBACK;
+ fido_init(init_flags);
+
+ if (data.action == ACTION_REGISTER) {
+ ret = register_key(&data);
+ if (ret != EOK) {
+ ERROR("Error registering key.\n");
+ goto done;
+ }
+ } else if (data.action == ACTION_AUTHENTICATE) {
+ ret = authenticate(&data);
+ if (ret == EOK) {
+ PRINT("Authentication success.\n");
+ goto done;
+ } else {
+ ERROR("Authentication error.\n");
+ goto done;
+ }
+ } else if (data.action == ACTION_GET_ASSERT) {
+ ret = get_assert_data(&data);
+ if (ret != EOK) {
+ ERROR("Error getting assertion data.\n");
+ goto done;
+ }
+ } else if (data.action == ACTION_VERIFY_ASSERT) {
+ ret = verify_assert_data(&data);
+ if (ret == EOK) {
+ PRINT("Verification success.\n");
+ goto done;
+ } else {
+ ERROR("Verification error.\n");
+ goto done;
+ }
+ }
+
+done:
+ talloc_free(main_ctx);
+
+ if (ret != EOK) {
+ return EXIT_FAILURE;
+ } else {
+ return EXIT_SUCCESS;
+ }
+}
diff --git a/src/passkey_child/passkey_child.h b/src/passkey_child/passkey_child.h
new file mode 100644
index 0000000..185e7e6
--- /dev/null
+++ b/src/passkey_child/passkey_child.h
@@ -0,0 +1,557 @@
+/*
+ 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/>.
+*/
+
+#ifndef __PASSKEY_CHILD_H__
+#define __PASSKEY_CHILD_H__
+
+#include <fido.h>
+
+#define DEFAULT_PROMPT "Insert your passkey device, then press ENTER."
+#define DEFAULT_CUE "Please touch the device."
+
+#define DEVLIST_SIZE 64
+#define USER_ID_SIZE 32
+#define TIMEOUT 15
+#define FREQUENCY 1
+
+enum action_opt {
+ ACTION_NONE,
+ ACTION_REGISTER,
+ ACTION_AUTHENTICATE,
+ ACTION_GET_ASSERT,
+ ACTION_VERIFY_ASSERT
+};
+
+enum credential_type {
+ CRED_SERVER_SIDE,
+ CRED_DISCOVERABLE
+};
+
+struct passkey_data {
+ enum action_opt action;
+ const char *shortname;
+ const char *domain;
+ char **key_handle_list;
+ int key_handle_size;
+ char **public_key_list;
+ int public_key_size;
+ const char *crypto_challenge;
+ const char *auth_data;
+ const char *signature;
+ int type;
+ fido_opt_t user_verification;
+ enum credential_type cred_type;
+ unsigned char *user_id;
+ char *mapping_file;
+ bool quiet;
+ bool debug_libfido2;
+};
+
+struct pk_data_t {
+ void *public_key;
+ int type;
+};
+
+/**
+ * @brief Parse arguments
+ *
+ * @param[in] mem_ctx Memory context
+ * @param[in] argc Number of arguments
+ * @param[in] argv Argument list
+ * @param[out] data passkey data
+ *
+ * @return 0 if the arguments were parsed properly,
+ * another value on error.
+ */
+errno_t
+parse_arguments(TALLOC_CTX *mem_ctx, int argc, const char *argv[],
+ struct passkey_data *data);
+
+/**
+ * @brief Check that all the arguments have been set
+ *
+ * @param[in] data passkey data
+ *
+ * @return 0 if the arguments were set properly,
+ * another value on error.
+ */
+errno_t
+check_arguments(const struct passkey_data *data);
+
+/**
+ * @brief Register a key for a user
+ *
+ * @param[in] data passkey data
+ *
+ * @return 0 if the key was registered properly,
+ * another value on error.
+ */
+errno_t
+register_key(struct passkey_data *data);
+
+/**
+ * @brief Translate COSE type from string to int
+ *
+ * @param[in] type string COSE type
+ * @param[out] out int COSE type
+ *
+ * @return 0 if the COSE type has been translated,
+ * another value if the COSE type doesn't exist.
+ */
+errno_t
+cose_str_to_int(const char *type, int *out);
+
+/**
+ * @brief Prepare user credentials
+ *
+ * @param[in] data passkey data
+ * @param[in] dev Device information
+ * @param[out] cred Credentials
+ *
+ * @return 0 if the credentials were prepared properly,
+ * another value on error.
+ */
+errno_t
+prepare_credentials(struct passkey_data *data, fido_dev_t *dev,
+ fido_cred_t *cred);
+
+/**
+ * @brief List connected passkey devices
+ *
+ * @param[out] dev_list passkey device list
+ * @param[out] dev_number Number of passkey devices
+ *
+ * @return 0 if the list was retrieved properly, another value on error.
+ */
+errno_t
+list_devices(fido_dev_info_t *dev_list, size_t *dev_number);
+
+/**
+ * @brief Select passkey device
+ *
+ * @param[in] action Action to perform with the key
+ * @param[in] dev_list passkey device list
+ * @param[in] dev_index passkey device index
+ * @param[in] assert Assert
+ * @param[out] dev Device information
+ *
+ * @return 0 if the device was opened properly, another value on error.
+ */
+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);
+
+/**
+ * @brief Get authenticator data from assert
+ *
+ * @param[in] dev_list passkey device list
+ * @param[in] dev_list_len passkey device list length
+ * @param[in] assert Assert
+ * @param[out] dev Authenticator data
+ *
+ * @return 0 if the authenticator data was retrieved properly,
+ * another value on error.
+ */
+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);
+
+/**
+ * @brief Receive PIN via stdin
+ *
+ * @param[in] mem_ctx Memory context
+ * @param[in] fd File descriptor
+ * @param[out] pin Pin
+ *
+ * @return 0 if the authenticator data was received properly,
+ * error code otherwise.
+ */
+errno_t
+passkey_recv_pin(TALLOC_CTX *mem_ctx, int fd, char **_pin);
+
+/**
+ * @brief Disable echoing and read PIN from stdin
+ *
+ * @param[out] line_ptr PIN
+ *
+ * @return Number of bytes read, or -1 on error.
+ */
+ssize_t
+read_pin(char **line_ptr);
+
+/**
+ * @brief Generate passkey credentials
+ *
+ * @param[in] data passkey data
+ * @param[in] dev Device information
+ * @param[out] cred Credentials
+ *
+ * @return 0 if the credentials were generated properly,
+ * another value on error.
+ */
+errno_t
+generate_credentials(struct passkey_data *data, fido_dev_t *dev,
+ fido_cred_t *cred);
+
+/**
+ * @brief Verify passkey credentials
+ *
+ * @param[in] cred Credentials
+ *
+ * @return 0 if the credentials were verified properly,
+ * another value on error.
+ */
+errno_t
+verify_credentials(const fido_cred_t *const cred);
+
+/**
+ * @brief Print passkey credentials
+ *
+ * @param[in] data passkey data
+ * @param[out] cred Credentials
+ *
+ * @return 0 if the credentials were printed properly,
+ * another value on error.
+ */
+errno_t
+print_credentials(const struct passkey_data *data,
+ const fido_cred_t *const cred);
+
+/**
+ * @brief Print passkey credentials
+ *
+ * @param[in] data passkey data
+ * @param[in] b64_cred_id Credential ID in b64
+ * @param[in] pem_key Public key in PEM format
+ *
+ * @return 0 if the credentials were printed properly,
+ * another value on error.
+ */
+errno_t
+print_credentials_to_file(const struct passkey_data *data,
+ const char *b64_cred_id,
+ const char *pem_key);
+
+/**
+ * @brief Format libfido2's es256 data structure to EVP_PKEY
+ *
+ * @param[in] mem_ctx Memory context
+ * @param[in] es256_key Public key pointer
+ * @param[in] es256_key_len Public key length
+ * @param[out] _evp_pkey Pointer to public key structure
+ *
+ * @return 0 if the key was formatted properly, error code otherwise.
+ */
+int
+es256_pubkey_to_evp_pkey(TALLOC_CTX *mem_ctx, const void *es256_key,
+ size_t es256_key_len, EVP_PKEY **_evp_pkey);
+
+/**
+ * @brief Format libfido2's rs256 data structure to EVP_PKEY
+ *
+ * @param[in] mem_ctx Memory context
+ * @param[in] rs256_key Public key pointer
+ * @param[in] rs256_key_len Public key length
+ * @param[out] _evp_pkey Pointer to public key structure
+ *
+ * @return 0 if the key was formatted properly, error code otherwise.
+ */
+int
+rs256_pubkey_to_evp_pkey(TALLOC_CTX *mem_ctx, const void *rs256_key,
+ size_t rs256_key_len, EVP_PKEY **_evp_pkey);
+
+/**
+ * @brief Format libfido2's eddsa data structure to EVP_PKEY
+ *
+ * @param[in] mem_ctx Memory context
+ * @param[in] eddsa_key Public key pointer
+ * @param[in] eddsa_key_len Public key length
+ * @param[out] _evp_pkey Pointer to public key structure
+ *
+ * @return 0 if the key was formatted properly, error code otherwise.
+ */
+int
+eddsa_pubkey_to_evp_pkey(TALLOC_CTX *mem_ctx, const void *eddsa_key,
+ size_t eddsa_key_len, EVP_PKEY **_evp_pkey);
+
+/**
+ * @brief Format the public key to base64
+ *
+ * @param[in] mem_ctx Memory context
+ * @param[in] data passkey data
+ * @param[in] public_key Public key
+ * @param[in] pk_len Public key length
+ * @param[out] _pem_key Public key in PEM format
+ *
+ * @return 0 if the key was formatted properly, error code otherwise.
+ */
+errno_t
+public_key_to_base64(TALLOC_CTX *mem_ctx, const struct passkey_data *data,
+ const unsigned char *public_key, size_t pk_len,
+ char **_pem_key);
+
+/*
+ * @brief Authenticate a user
+ *
+ * Prepare the assertion request data, select the device to use, get the device
+ * options and compare them with the organization policy, decode the public
+ * key, request the assert and verify it.
+ *
+ * @param[in] data passkey data
+ *
+ * @return 0 if the user was authenticated properly,
+ * error code otherwise.
+ */
+errno_t
+authenticate(struct passkey_data *data);
+
+/*
+ * @brief Select authenticator for verification
+ *
+ *
+ * @param[in] data passkey data
+ * @param[out] _dev Device information
+ * @param[out] _assert Assert
+ * @param[out] _index Index for key handle list
+ *
+ * @return 0 if the authenticator was selected properly,
+ * error code otherwise.
+ */
+errno_t
+select_authenticator(struct passkey_data *data, fido_dev_t **_dev,
+ fido_assert_t **_assert, int *_index);
+
+/**
+ * @brief Set client data hash in the assert
+ *
+ * @param[in] data passkey data
+ * @param[in,out] _assert Assert
+ *
+ * @return 0 if the data was set properly,
+ * error code otherwise.
+ */
+errno_t
+set_assert_client_data_hash(const struct passkey_data *data,
+ fido_assert_t *_assert);
+
+/**
+ * @brief Set authenticator data and signature in the assert
+ *
+ * @param[in] data passkey data
+ * @param[in,out] _assert Assert
+ *
+ * @return 0 if the data was set properly,
+ * error code otherwise.
+ */
+errno_t
+set_assert_auth_data_signature(const struct passkey_data *data,
+ fido_assert_t *_assert);
+
+/**
+ * @brief Set options in the assert
+ *
+ * @param[in] up User presence check
+ * @param[in] uv User verification check
+ * @param[out] assert Assert
+ *
+ * @return 0 if the data was set properly,
+ * error code otherwise.
+ */
+errno_t
+set_assert_options(fido_opt_t up, fido_opt_t uv, fido_assert_t *_assert);
+
+/**
+ * @brief Get authentication data and signature from assert
+ *
+ * @param[in] mem_ctx Memory context
+ * @param[in] assert Assert
+ * @param[out] _auth_data Authentication data
+ * @param[out] _signature Signature
+ *
+ * @return 0 if the data was get properly,
+ * error code otherwise.
+ */
+errno_t
+get_assert_auth_data_signature(TALLOC_CTX *mem_ctx, fido_assert_t *assert,
+ const char **_auth_data,
+ const char **_signature);
+
+/**
+ * @brief Prepare assert
+ *
+ * @param[in] data passkey data
+ * @param[in] index Index for key handle list
+ * @param[in,out] _assert Assert
+ *
+ * @return 0 if the assert was prepared properly,
+ * error code otherwise.
+ */
+errno_t
+prepare_assert(const struct passkey_data *data, int index,
+ fido_assert_t *_assert);
+
+/**
+ * @brief Reset and free public key
+ *
+ * @param[out] _pk_data Public key data
+ *
+ * @return 0 if the public key was reset properly,
+ * error code otherwise.
+ */
+errno_t
+reset_public_key(struct pk_data_t *_pk_data);
+
+/**
+ * @brief Format EVP_PKEY to libfido2's es256 data structure
+ *
+ * @param[in] evp_pkey EVP_PKEY public key
+ * @param[out] _pk_data Public key data
+ *
+ * @return 0 if the public key was formatted properly,
+ * error code otherwise.
+ */
+errno_t
+evp_pkey_to_es256_pubkey(const EVP_PKEY *evp_pkey, struct pk_data_t *_pk_data);
+
+/**
+ * @brief Format EVP_PKEY to libfido2's rs256 data structure
+ *
+ * @param[in] evp_pkey EVP_PKEY public key
+ * @param[out] _pk_data Public key data
+ *
+ * @return 0 if the public key was formatted properly,
+ * error code otherwise.
+ */
+errno_t
+evp_pkey_to_rs256_pubkey(const EVP_PKEY *evp_pkey, struct pk_data_t *_pk_data);
+
+/**
+ * @brief Format EVP_PKEY to libfido2's eddsa data structure
+ *
+ * @param[in] evp_pkey EVP_PKEY public key
+ * @param[out] _pk_data Public key data
+ *
+ * @return 0 if the public key was formatted properly,
+ * error code otherwise.
+ */
+errno_t
+evp_pkey_to_eddsa_pubkey(const EVP_PKEY *evp_pkey, struct pk_data_t *_pk_data);
+
+/**
+ * @brief Format the public key to the libfido2 data structure
+ *
+ * @param[in] pem_public_key PEM formatter public key
+ * @param[out] _pk_data Public key data
+ *
+ * @return 0 if the public key was formatted properly,
+ * error code otherwise.
+ */
+errno_t
+public_key_to_libfido2(const char *pem_public_key, struct pk_data_t *_pk_data);
+
+/**
+ * @brief Get device options and compare with the policy options expectations
+ *
+ * @param[in] dev Device information
+ * @param[out] data passkey data
+ *
+ * @return 0 if the device data was retrieved and the options match properly,
+ * error code otherwise.
+ */
+errno_t
+get_device_options(fido_dev_t *dev, struct passkey_data *_data);
+
+/**
+ * @brief Get assertion data
+ *
+ * @param[in] data passkey data
+ * @param[in] dev Device information
+ * @param[out] assert Assert
+ *
+ * @return 0 if the assertion was verified properly,
+ * error code otherwise.
+ */
+errno_t
+request_assert(struct passkey_data *data, fido_dev_t *dev,
+ fido_assert_t *_assert);
+
+/**
+ * @brief Verify assertion
+ *
+ * @param[in] pk_data Public key data
+ * @param[in] assert Assert
+ *
+ * @return 0 if the assertion was verified properly,
+ * error code otherwise.
+ */
+errno_t
+verify_assert(struct pk_data_t *data, fido_assert_t *assert);
+
+/**
+ * @brief Print assert request data in JSON format
+ *
+ * @param[in] key_handle Key handle
+ * @param[in] crypto_challenge Cryptographic challenge
+ * @param[in] auth_data Authenticator data
+ * @param[in] signature Assertion signature
+ *
+ */
+void
+print_assert_data(const char *key_handle, const char *crypto_challenge,
+ const char *auth_data, const char *signature);
+
+/**
+ * @brief Obtain assertion data
+ *
+ * Prepare the assertion request data, select the device to use, select the
+ * authenticator, get the device options and compare them with the organization
+ * policy, request the assert, get the authenticator data, get the signature
+ * and print this all information.
+ *
+ * @param[in] data passkey data
+ *
+ * @return 0 if the assertion was obtained properly,
+ * error code otherwise.
+ */
+errno_t
+get_assert_data(struct passkey_data *data);
+
+/**
+ * @brief Verify assertion data
+ *
+ * Prepare the assertion data, including the authenticator data and the
+ * signature; decode the public key and verify the assertion.
+ *
+ * @param[in] data passkey data
+ *
+ * @return 0 if the assertion was obtained properly,
+ * error code otherwise.
+ */
+errno_t
+verify_assert_data(struct passkey_data *data);
+
+#endif /* __PASSKEY_CHILD_H__ */
diff --git a/src/passkey_child/passkey_child_assert.c b/src/passkey_child/passkey_child_assert.c
new file mode 100644
index 0000000..5139dc8
--- /dev/null
+++ b/src/passkey_child/passkey_child_assert.c
@@ -0,0 +1,448 @@
+/*
+ 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 <jansson.h>
+#include <termios.h>
+#include <stdio.h>
+#include <fido/es256.h>
+#include <fido/rs256.h>
+#include <fido/eddsa.h>
+
+#include "util/crypto/sss_crypto.h"
+#include "util/debug.h"
+#include "util/util.h"
+
+#include "passkey_child.h"
+
+errno_t
+set_assert_client_data_hash(const struct passkey_data *data,
+ fido_assert_t *_assert)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ unsigned char cdh[32];
+ unsigned char *crypto_challenge = NULL;
+ size_t crypto_challenge_len;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new() failed.\n");
+ return ENOMEM;
+ }
+
+ if (data->action == ACTION_AUTHENTICATE) {
+ ret = sss_generate_csprng_buffer(cdh, sizeof(cdh));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_generate_csprng_buffer failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ ret = fido_assert_set_clientdata_hash(_assert, cdh, sizeof(cdh));
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_set_clientdata_hash failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+ } else {
+ crypto_challenge = sss_base64_decode(tmp_ctx, data->crypto_challenge,
+ &crypto_challenge_len);
+ if (crypto_challenge == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "failed to decode client data hash.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (crypto_challenge_len != 32) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "cryptographic-challenge length [%ld] must be 32.\n",
+ crypto_challenge_len);
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = fido_assert_set_clientdata_hash(_assert, crypto_challenge,
+ crypto_challenge_len);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_set_clientdata_hash failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+ }
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+set_assert_options(fido_opt_t up, fido_opt_t uv, fido_assert_t *_assert)
+{
+ errno_t ret;
+
+ ret = fido_assert_set_up(_assert, up);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_set_up failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ ret = fido_assert_set_uv(_assert, uv);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_set_uv failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+errno_t
+get_assert_auth_data_signature(TALLOC_CTX *mem_ctx, fido_assert_t *assert,
+ const char **_auth_data,
+ const char **_signature)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ const unsigned char *auth_data;
+ const unsigned char *signature;
+ const char *b64_auth_data;
+ const char *b64_signature;
+ size_t auth_data_len;
+ size_t signature_len;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new() failed.\n");
+ return ENOMEM;
+ }
+
+ auth_data = fido_assert_authdata_ptr(assert, 0);
+ if (auth_data == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_authdata_ptr failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ auth_data_len = fido_assert_authdata_len(assert, 0);
+ if (auth_data_len == 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_authdata_len failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ b64_auth_data = sss_base64_encode(tmp_ctx, auth_data, auth_data_len);
+ if (b64_auth_data == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "failed to encode authenticator data.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ signature = fido_assert_sig_ptr(assert, 0);
+ if (signature == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_sig_ptr failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ signature_len = fido_assert_sig_len(assert, 0);
+ if (signature_len == 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_sig_len failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ b64_signature = sss_base64_encode(tmp_ctx, signature, signature_len);
+ if (b64_signature == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "failed to encode signature.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_auth_data = talloc_steal(mem_ctx, b64_auth_data);
+ *_signature = talloc_steal(mem_ctx, b64_signature);
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+set_assert_auth_data_signature(const struct passkey_data *data,
+ fido_assert_t *_assert)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ const unsigned char *auth_data = NULL;
+ const unsigned char *signature = NULL;
+ size_t auth_data_len;
+ size_t signature_len;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = fido_assert_set_count(_assert, 1);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_set_count failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ auth_data = sss_base64_decode(tmp_ctx, data->auth_data, &auth_data_len);
+ if (auth_data == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "failed to decode authenticator data.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = fido_assert_set_authdata(_assert, 0, auth_data, auth_data_len);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_set_authdata failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ signature = sss_base64_decode(tmp_ctx, data->signature, &signature_len);
+ if (signature == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "failed to decode signature.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = fido_assert_set_sig(_assert, 0, signature, signature_len);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_set_sig failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+prepare_assert(const struct passkey_data *data, int index,
+ fido_assert_t *_assert)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ unsigned char *key_handle;
+ size_t key_handle_len;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = fido_assert_set_rp(_assert, data->domain);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_set_rp failed [%d]: %s.\n", ret, fido_strerr(ret));
+ goto done;
+ }
+
+ key_handle = sss_base64_decode(tmp_ctx, data->key_handle_list[index],
+ &key_handle_len);
+ if (key_handle == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to decode key handle.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = fido_assert_allow_cred(_assert, key_handle, key_handle_len);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_allow_cred failed [%d]: %s.\n", ret, fido_strerr(ret));
+ goto done;
+ }
+
+ ret = set_assert_options(FIDO_OPT_FALSE, FIDO_OPT_OMIT, _assert);
+ if (ret != FIDO_OK) {
+ goto done;
+ }
+
+ ret = set_assert_client_data_hash(data, _assert);
+ if (ret != EOK) {
+ goto done;
+ }
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+reset_public_key(struct pk_data_t *_pk_data)
+{
+ if (_pk_data->type == COSE_ES256) {
+ es256_pk_free((es256_pk_t **) &_pk_data->public_key);
+ } else if (_pk_data->type == COSE_RS256) {
+ rs256_pk_free((rs256_pk_t **) &_pk_data->public_key);
+ } else if (_pk_data->type == COSE_EDDSA) {
+ eddsa_pk_free((eddsa_pk_t **) &_pk_data->public_key);
+ }
+
+ return EOK;
+}
+
+errno_t
+request_assert(struct passkey_data *data, fido_dev_t *dev,
+ fido_assert_t *_assert)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *pin = NULL;
+ bool has_pin;
+ bool has_uv;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new() failed.\n");
+ return ENOMEM;
+ }
+
+ has_pin = fido_dev_has_pin(dev);
+ has_uv = fido_dev_has_uv(dev);
+ if (has_uv == true && data->user_verification != FIDO_OPT_FALSE) {
+ ret = fido_dev_get_assert(dev, _assert, NULL);
+ if (ret != FIDO_OK && has_pin == true) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_dev_get_assert failed [%d]: %s. "
+ "Falling back to PIN authentication.\n",
+ ret, fido_strerr(ret));
+ } else if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_dev_get_assert failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC, "fido_dev_get_assert succeeded.\n");
+ goto done;
+ }
+ }
+
+ if (has_pin == true && data->user_verification != FIDO_OPT_FALSE) {
+ ret = passkey_recv_pin(tmp_ctx, STDIN_FILENO, &pin);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ ret = fido_dev_get_assert(dev, _assert, pin);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_dev_get_assert failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ ret = fido_assert_set_uv(_assert, data->user_verification);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_assert_set_uv failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+done:
+ if (pin != NULL) {
+ sss_erase_mem_securely(pin, strlen(pin));
+ }
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+verify_assert(struct pk_data_t *pk_data, fido_assert_t *assert)
+{
+ errno_t ret;
+
+ ret = fido_assert_verify(assert, 0, pk_data->type, pk_data->public_key);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_assert_verify failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+void
+print_assert_data(const char *key_handle, const char *crypto_challenge,
+ const char *auth_data, const char *signature)
+{
+ json_t *passkey = NULL;
+ char* string = NULL;
+
+ /* Kerberos expects the user_id field, thus it cannot be removed and there
+ * is nothing to set so it's an empty string.
+ */
+ passkey = json_pack("{s:s*, s:s*, s:s*, s:s*, s:s*}",
+ "credential_id", key_handle,
+ "cryptographic_challenge", crypto_challenge,
+ "authenticator_data", auth_data,
+ "assertion_signature", signature,
+ "user_id", "");
+ if (passkey == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to create passkey object.\n");
+ goto done;
+ }
+
+ string = json_dumps(passkey, 0);
+ if (string == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "json_dumps() failed.\n");
+ goto done;
+ }
+
+ puts(string);
+ free(string);
+
+done:
+ json_decref(passkey);
+
+ return;
+}
diff --git a/src/passkey_child/passkey_child_common.c b/src/passkey_child/passkey_child_common.c
new file mode 100644
index 0000000..000a7ee
--- /dev/null
+++ b/src/passkey_child/passkey_child_common.c
@@ -0,0 +1,885 @@
+/*
+ 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 <popt.h>
+#include <sys/prctl.h>
+#include <fido/param.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+#include "util/crypto/sss_crypto.h"
+#include "util/debug.h"
+#include "util/util.h"
+#include "util/crypto/sss_crypto.h"
+
+#include "passkey_child.h"
+
+#if OPENSSL_VERSION_NUMBER >= 0x30000000
+#define get_id(x) EVP_PKEY_get_base_id((x))
+#else
+#define get_id(x) EVP_PKEY_base_id((x))
+#endif /* OPENSSL_VERSION_NUMBER */
+
+errno_t
+cose_str_to_int(const char *type, int *out)
+{
+ if (strcasecmp(type, "es256") == 0) {
+ *out = COSE_ES256;
+ } else if (strcasecmp(type, "rs256") == 0) {
+ *out = COSE_RS256;
+ } else if (strcasecmp(type, "eddsa") == 0) {
+ *out = COSE_EDDSA;
+ } else {
+ *out = 0;
+ return ERR_INVALID_CRED_TYPE;
+ }
+
+ return EOK;
+}
+
+static errno_t
+cred_type_str_to_enum(const char *type, enum credential_type *out)
+{
+ if (strcasecmp(type, "server-side") == 0) {
+ *out = CRED_SERVER_SIDE;
+ } else if (strcasecmp(type, "discoverable") == 0) {
+ *out = CRED_DISCOVERABLE;
+ } else {
+ *out = 0;
+ return ERR_INVALID_CRED_TYPE;
+ }
+
+ return EOK;
+}
+
+static errno_t
+parse_public_keys_and_handlers(TALLOC_CTX *mem_ctx,
+ const char *public_keys,
+ const char *key_handles,
+ struct passkey_data *_data)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ char **pk_list = NULL;
+ char **kh_list = NULL;
+ int pk_num = 0;
+ int kh_num = 0;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ERROR("talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = split_on_separator(tmp_ctx, public_keys, ',', true, true, &pk_list, &pk_num);
+ if (ret != EOK && _data->action == ACTION_AUTHENTICATE) {
+ ERROR("Incorrectly formatted public keys.\n");
+ goto done;
+ }
+
+ ret = split_on_separator(tmp_ctx, key_handles, ',', true, true, &kh_list, &kh_num);
+ if (ret != EOK) {
+ ERROR("Incorrectly formatted public keys.\n");
+ goto done;
+ }
+
+ if (_data->action == ACTION_AUTHENTICATE && pk_num != kh_num) {
+ ERROR("The number of public keys and key handles don't match.\n");
+ goto done;
+ }
+
+ _data->public_key_list = talloc_steal(mem_ctx, pk_list);
+ _data->key_handle_list = talloc_steal(mem_ctx, kh_list);
+ _data->public_key_size = pk_num;
+ _data->key_handle_size = kh_num;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+parse_arguments(TALLOC_CTX *mem_ctx, int argc, const char *argv[],
+ struct passkey_data *data)
+{
+ int opt;
+ int dumpable = 1;
+ int debug_fd = -1;
+ char *user_verification = NULL;
+ char *public_keys = NULL;
+ char *key_handles = NULL;
+ const char *opt_logger = NULL;
+ const char *type = NULL;
+ const char *cred_type = NULL;
+ poptContext pc;
+ errno_t ret;
+
+ /* Set defaults */
+ data->action = ACTION_NONE;
+ data->shortname = NULL;
+ data->domain = NULL;
+ data->public_key_list = NULL;
+ data->key_handle_list = NULL;
+ data->public_key_size = 0;
+ data->key_handle_size = 0;
+ data->crypto_challenge = NULL;
+ data->auth_data = NULL;
+ data->signature = NULL;
+ data->type = COSE_ES256;
+ data->user_verification = FIDO_OPT_OMIT;
+ data->cred_type = CRED_SERVER_SIDE;
+ data->user_id = NULL;
+ data->mapping_file = NULL;
+ data->quiet = false;
+ data->debug_libfido2 = false;
+
+ struct poptOption long_options[] = {
+ POPT_AUTOHELP
+ SSSD_DEBUG_OPTS
+ {"dumpable", 0, POPT_ARG_INT, &dumpable, 0,
+ _("Allow core dumps"), NULL },
+ {"debug-fd", 0, POPT_ARG_INT, &debug_fd, 0,
+ _("An open file descriptor for the debug logs"), NULL},
+ SSSD_LOGGER_OPTS
+ {"register", 0, POPT_ARG_NONE, NULL, 'r',
+ _("Register a passkey for a user"), NULL },
+ {"authenticate", 0, POPT_ARG_NONE, NULL, 'a',
+ _("Authenticate a user with a passkey"), NULL },
+ {"get-assert", 0, POPT_ARG_NONE, NULL, 'g',
+ _("Obtain assertion data"), NULL },
+ {"verify-assert", 0, POPT_ARG_NONE, NULL, 'v',
+ _("Verify assertion data"), NULL },
+ {"username", 0, POPT_ARG_STRING, &data->shortname, 0,
+ _("Shortname"), NULL },
+ {"domain", 0, POPT_ARG_STRING, &data->domain, 0,
+ _("Domain"), NULL},
+ {"public-key", 0, POPT_ARG_STRING, &public_keys, 0,
+ _("Public key"), NULL },
+ {"key-handle", 0, POPT_ARG_STRING, &key_handles, 0,
+ _("Key handle"), NULL},
+ {"cryptographic-challenge", 0, POPT_ARG_STRING,
+ &data->crypto_challenge, 0,
+ _("Cryptographic challenge"), NULL},
+ {"auth-data", 0, POPT_ARG_STRING, &data->auth_data, 0,
+ _("Authenticator data"), NULL},
+ {"signature", 0, POPT_ARG_STRING, &data->signature, 0,
+ _("Signature"), NULL},
+ {"type", 0, POPT_ARG_STRING, &type, 0,
+ _("COSE type to use"), "es256|rs256|eddsa"},
+ {"user-verification", 0, POPT_ARG_STRING, &user_verification, 0,
+ _("Require user-verification"), "true|false"},
+ {"cred-type", 0, POPT_ARG_STRING, &cred_type, 0,
+ _("Credential type"), "server-side|discoverable"},
+ {"output-file", 0, POPT_ARG_STRING, &data->mapping_file, 0,
+ _("Write key mapping data to file"), NULL},
+ {"quiet", 0, POPT_ARG_NONE, NULL, 'q',
+ _("Supress prompts"), NULL},
+ {"debug-libfido2", 0, POPT_ARG_NONE, NULL, 'd',
+ _("Enable debug in libfido2 library"), NULL},
+ SSSD_LOGGER_OPTS
+ POPT_TABLEEND
+ };
+
+ /* Set debug level to invalid value so we can decide if -d 0 was used. */
+ debug_level = SSSDBG_INVALID;
+
+ pc = poptGetContext(argv[0], argc, argv, long_options, 0);
+
+ while((opt = poptGetNextOpt(pc)) != -1) {
+ switch(opt) {
+ case 'r':
+ if (data->action != ACTION_NONE
+ && data->action != ACTION_REGISTER) {
+ fprintf(stderr, "\nActions are mutually exclusive and should" \
+ " be used only once.\n\n");
+ poptPrintUsage(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+ data->action = ACTION_REGISTER;
+ break;
+ case 'a':
+ if (data->action != ACTION_NONE
+ && data->action != ACTION_AUTHENTICATE) {
+ fprintf(stderr, "\nActions are mutually exclusive and should" \
+ " be used only once.\n\n");
+ poptPrintUsage(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+ data->action = ACTION_AUTHENTICATE;
+ break;
+ case 'g':
+ if (data->action != ACTION_NONE
+ && data->action != ACTION_GET_ASSERT) {
+ fprintf(stderr, "\nActions are mutually exclusive and should" \
+ " be used only once.\n\n");
+ poptPrintUsage(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+ data->action = ACTION_GET_ASSERT;
+ break;
+ case 'v':
+ if (data->action != ACTION_NONE
+ && data->action != ACTION_VERIFY_ASSERT) {
+ fprintf(stderr, "\nActions are mutually exclusive and should" \
+ " be used only once.\n\n");
+ poptPrintUsage(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+ data->action = ACTION_VERIFY_ASSERT;
+ break;
+ case 'q':
+ data->quiet = true;
+ break;
+ case 'd':
+ data->debug_libfido2 = true;
+ break;
+ default:
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ poptFreeContext(pc);
+
+ prctl(PR_SET_DUMPABLE, (dumpable == 0) ? 0 : 1);
+
+ if (user_verification != NULL) {
+ if (strcmp(user_verification, "true") == 0) {
+ data->user_verification = FIDO_OPT_TRUE;
+ } else if (strcmp(user_verification, "false") == 0) {
+ data->user_verification = FIDO_OPT_FALSE;
+ } else if (user_verification != NULL) {
+ ERROR("[%s] is not a valid user-verification value.\n",
+ user_verification);
+ ret = EINVAL;
+ goto done;
+ }
+ }
+
+ if (type != NULL) {
+ ret = cose_str_to_int(type, &data->type);
+ if (ret != EOK) {
+ ERROR("[%s] is not a valid COSE type (es256, rs256 or eddsa).\n",
+ type);
+ goto done;
+ }
+ }
+
+ if (public_keys != NULL || key_handles != NULL) {
+ ret = parse_public_keys_and_handlers(mem_ctx, public_keys, key_handles,
+ data);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ if (cred_type != NULL) {
+ ret = cred_type_str_to_enum(cred_type, &data->cred_type);
+ if (ret != EOK) {
+ ERROR("[%s] is not a valid credential type (server-side or"
+ " discoverable).\n",
+ cred_type);
+ goto done;
+ }
+ }
+
+ debug_prg_name = talloc_asprintf(NULL, "passkey_child[%d]", getpid());
+ if (debug_prg_name == NULL) {
+ ERROR("talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (debug_fd != -1) {
+ opt_logger = sss_logger_str[FILES_LOGGER];
+ ret = set_debug_file_from_fd(debug_fd);
+ if (ret != EOK) {
+ opt_logger = sss_logger_str[STDERR_LOGGER];
+ ERROR("set_debug_file_from_fd failed.\n");
+ }
+ }
+
+ DEBUG_INIT(debug_level, opt_logger);
+
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+errno_t
+check_arguments(const struct passkey_data *data)
+{
+ errno_t ret = EOK;
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Argument values after parsing\n");
+ DEBUG(SSSDBG_TRACE_FUNC, "action: %d\n", data->action);
+ DEBUG(SSSDBG_TRACE_FUNC, "shortname: %s, domain: %s\n",
+ data->shortname, data->domain);
+ DEBUG(SSSDBG_TRACE_FUNC, "Number of key handles %d\n",
+ data->key_handle_size);
+ for (int i = 0; i < data->key_handle_size; i++) {
+ DEBUG(SSSDBG_TRACE_FUNC, "key %d, key_handle: %s\n",
+ i + 1, data->key_handle_list[i]);
+ }
+ DEBUG(SSSDBG_TRACE_FUNC, "Number of public keys %d\n",
+ data->public_key_size);
+ for (int i = 0; i < data->public_key_size; i++) {
+ DEBUG(SSSDBG_TRACE_FUNC, "key %d, public_key: %s\n",
+ i + 1, data->public_key_list[i]);
+ }
+ DEBUG(SSSDBG_TRACE_FUNC, "cryptographic-challenge: %s\n",
+ data->crypto_challenge);
+ DEBUG(SSSDBG_TRACE_FUNC, "auth-data: %s\n",
+ data->auth_data);
+ DEBUG(SSSDBG_TRACE_FUNC, "signature: %s\n",
+ data->signature);
+ DEBUG(SSSDBG_TRACE_FUNC, "type: %d\n", data->type);
+ DEBUG(SSSDBG_TRACE_FUNC, "user_verification: %d\n",
+ data->user_verification);
+ DEBUG(SSSDBG_TRACE_FUNC, "cred_type: %d\n",
+ data->cred_type);
+ DEBUG(SSSDBG_TRACE_FUNC, "Mapping file: %s\n", data->mapping_file);
+ DEBUG(SSSDBG_TRACE_FUNC, "debug_libfido2: %d\n", data->debug_libfido2);
+
+ if (data->action == ACTION_NONE) {
+ DEBUG(SSSDBG_OP_FAILURE, "No action set.\n");
+ ret = ERR_INPUT_PARSE;
+ goto done;
+ }
+
+ if (data->action == ACTION_REGISTER
+ && (data->shortname == NULL || data->domain == NULL)) {
+ DEBUG(SSSDBG_OP_FAILURE, "Too few arguments for register action.\n");
+ ret = ERR_INPUT_PARSE;
+ goto done;
+ }
+
+ if (data->action == ACTION_AUTHENTICATE
+ && (data->domain == NULL || data->public_key_list == NULL
+ || data->key_handle_list == NULL)) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Too few arguments for authenticate action.\n");
+ ret = ERR_INPUT_PARSE;
+ goto done;
+ }
+
+ if (data->action == ACTION_GET_ASSERT
+ && (data->domain == NULL || data->key_handle_list == NULL
+ || data->crypto_challenge == NULL)) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Too few arguments for get-assert action.\n");
+ ret = ERR_INPUT_PARSE;
+ goto done;
+ }
+
+ if (data->action == ACTION_VERIFY_ASSERT
+ && (data->domain == NULL || data->public_key_list == NULL
+ || data->key_handle_list == NULL || data->crypto_challenge == NULL
+ || data->auth_data == NULL || data->signature == NULL)) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Too few arguments for verify-assert action.\n");
+ ret = ERR_INPUT_PARSE;
+ goto done;
+ }
+
+done:
+ return ret;
+}
+
+errno_t
+register_key(struct passkey_data *data)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ fido_cred_t *cred = NULL;
+ fido_dev_t *dev = NULL;
+ fido_dev_info_t *dev_list = NULL;
+ size_t dev_number = 0;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new() failed.\n");
+ return ENOMEM;
+ }
+
+ data->user_id = talloc_array(tmp_ctx, unsigned char, USER_ID_SIZE);
+ if (data->user_id == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array() failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cred = fido_cred_new();
+ if (cred == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_cred_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ dev_list = fido_dev_info_new(DEVLIST_SIZE);
+ if (dev_list == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_dev_info_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = list_devices(dev_list, &dev_number);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = select_device(data->action, dev_list, dev_number, NULL, &dev);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = prepare_credentials(data, dev, cred);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = generate_credentials(data, dev, cred);
+ if (ret != EOK) {
+ ERROR("A problem occurred while generating the credentials.\n");
+ goto done;
+ }
+
+ ret = verify_credentials(cred);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = print_credentials(data, cred);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ fido_cred_free(&cred);
+ fido_dev_info_free(&dev_list, dev_number);
+ if (dev != NULL) {
+ fido_dev_close(dev);
+ }
+ fido_dev_free(&dev);
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+public_key_to_base64(TALLOC_CTX *mem_ctx, const struct passkey_data *data,
+ const unsigned char *public_key, size_t pk_len,
+ char **_pem_key)
+{
+ EVP_PKEY *evp_pkey = NULL;
+ unsigned char *pub = NULL;
+ char *pem_key = NULL;
+ unsigned long err;
+ errno_t ret;
+
+ if (_pem_key == NULL) {
+ ret = EINVAL;
+ goto done;
+ }
+
+ switch (data->type) {
+ case COSE_ES256:
+ ret = es256_pubkey_to_evp_pkey(mem_ctx, public_key, pk_len, &evp_pkey);
+ break;
+ case COSE_RS256:
+ ret = rs256_pubkey_to_evp_pkey(mem_ctx, public_key, pk_len, &evp_pkey);
+ break;
+ case COSE_EDDSA:
+ ret = eddsa_pubkey_to_evp_pkey(mem_ctx, public_key, pk_len, &evp_pkey);
+ break;
+ default:
+ DEBUG(SSSDBG_OP_FAILURE, "Invalid key type.\n");
+ ret = EINVAL;
+ break;
+ }
+
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = i2d_PUBKEY(evp_pkey, &pub);
+ if (ret < 1) {
+ err = ERR_get_error();
+ DEBUG(SSSDBG_OP_FAILURE, "i2d_PUBKEY failed [%lu][%s].\n",
+ err, ERR_error_string(err, NULL));
+ ret = EIO;
+ goto done;
+ }
+
+ pem_key = sss_base64_encode(mem_ctx, pub, ret);
+ if (pem_key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_base64_encode failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_pem_key = pem_key;
+ ret = EOK;
+
+done:
+ free(pub);
+
+ if (evp_pkey != NULL) {
+ EVP_PKEY_free(evp_pkey);
+ }
+
+ return ret;
+}
+
+errno_t
+select_authenticator(struct passkey_data *data, fido_dev_t **_dev,
+ fido_assert_t **_assert, int *_index)
+{
+ fido_dev_info_t *dev_list = NULL;
+ fido_dev_t *dev = NULL;
+ size_t dev_list_len = 0;
+ fido_assert_t *assert = NULL;
+ int index = 0;
+ errno_t ret;
+
+ dev_list = fido_dev_info_new(DEVLIST_SIZE);
+ if (dev_list == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_dev_info_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Checking for devices.\n");
+ ret = list_devices(dev_list, &dev_list_len);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "%d key handles provided.\n",
+ data->key_handle_size);
+
+ while (index < data->key_handle_size) {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Preparing assert request data with key handle %d.\n", index + 1);
+
+ assert = fido_assert_new();
+ if (assert == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_assert_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Preparing assert request data.\n");
+ ret = prepare_assert(data, index, assert);
+ if (ret != FIDO_OK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Selecting device.\n");
+ ret = select_device(data->action, dev_list, dev_list_len, assert, &dev);
+ if (ret == EOK) {
+ /* Key handle found in device */
+ break;
+ }
+
+ if (dev != NULL) {
+ fido_dev_close(dev);
+ }
+ fido_dev_free(&dev);
+ fido_assert_free(&assert);
+ index++;
+ }
+
+ *_dev = dev;
+ *_assert = assert;
+ *_index = index;
+
+done:
+ if (ret != EOK) {
+ fido_assert_free(&assert);
+ }
+ fido_dev_info_free(&dev_list, dev_list_len);
+
+ return ret;
+}
+
+errno_t
+public_key_to_libfido2(const char *pem_public_key, struct pk_data_t *_pk_data)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ const unsigned char *public_key = NULL;
+ size_t pk_len;
+ const EVP_PKEY *evp_pkey = NULL;
+ int base_id;
+ unsigned long err;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ public_key = sss_base64_decode(tmp_ctx, pem_public_key, &pk_len);
+ if (public_key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to decode public key.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ evp_pkey = d2i_PUBKEY(NULL, &public_key, pk_len);
+ if (evp_pkey == NULL) {
+ err = ERR_get_error();
+ DEBUG(SSSDBG_OP_FAILURE, "d2i_pubkey failed [%lu][%s].\n",
+ err, ERR_error_string(err, NULL));
+ ret = EIO;
+ goto done;
+ }
+
+ base_id = get_id(evp_pkey);
+ if (base_id == EVP_PKEY_EC) {
+ _pk_data->type = COSE_ES256;
+ ret = evp_pkey_to_es256_pubkey(evp_pkey, _pk_data);
+ } else if (base_id == EVP_PKEY_RSA) {
+ _pk_data->type = COSE_RS256;
+ ret = evp_pkey_to_rs256_pubkey(evp_pkey, _pk_data);
+ } else if (base_id == EVP_PKEY_ED25519) {
+ _pk_data->type = COSE_EDDSA;
+ ret = evp_pkey_to_eddsa_pubkey(evp_pkey, _pk_data);
+ } else {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unrecognized key type.\n");
+ ret = EINVAL;
+ }
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (evp_pkey != NULL) {
+ EVP_PKEY_free(discard_const(evp_pkey));
+ }
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+authenticate(struct passkey_data *data)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ fido_assert_t *assert = NULL;
+ fido_dev_t *dev = NULL;
+ struct pk_data_t pk_data = { 0 };
+ int index;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ERROR("talloc_new() failed\n");
+ return ENOMEM;
+ }
+
+ ret = select_authenticator(data, &dev, &assert, &index);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Comparing the device and policy options.\n");
+ ret = get_device_options(dev, data);
+ if (ret != FIDO_OK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Resetting assert options.\n");
+ ret = set_assert_options(FIDO_OPT_TRUE, data->user_verification, assert);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to reset assert options.\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Resetting assert client data.\n");
+ ret = set_assert_client_data_hash(data, assert);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to reset client data hash.\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Decoding public key.\n");
+ ret = public_key_to_libfido2(data->public_key_list[index], &pk_data);
+ if (ret != FIDO_OK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Getting assert.\n");
+ ret = request_assert(data, dev, assert);
+ if (ret != FIDO_OK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Verifying assert.\n");
+ ret = verify_assert(&pk_data, assert);
+ if (ret != FIDO_OK) {
+ goto done;
+ }
+
+ ret = FIDO_OK;
+
+done:
+ reset_public_key(&pk_data);
+ if (dev != NULL) {
+ fido_dev_close(dev);
+ }
+ fido_dev_free(&dev);
+ fido_assert_free(&assert);
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+get_assert_data(struct passkey_data *data)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ fido_dev_t *dev = NULL;
+ fido_assert_t *assert = NULL;
+ const char *auth_data = NULL;
+ const char *signature = NULL;
+ int index;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new() failed.\n");
+ return ENOMEM;
+ }
+
+ ret = select_authenticator(data, &dev, &assert, &index);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Comparing the device and policy options.\n");
+ ret = get_device_options(dev, data);
+ if (ret != FIDO_OK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Resetting assert options.\n");
+ ret = set_assert_options(FIDO_OPT_TRUE, data->user_verification, assert);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to reset assert options.\n");
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Getting assert.\n");
+ ret = request_assert(data, dev, assert);
+ if (ret != FIDO_OK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Getting authentication data and signature.\n");
+ ret = get_assert_auth_data_signature(tmp_ctx, assert, &auth_data,
+ &signature);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ print_assert_data(data->key_handle_list[index], data->crypto_challenge,
+ auth_data, signature);
+
+done:
+ if (dev != NULL) {
+ fido_dev_close(dev);
+ }
+ fido_dev_free(&dev);
+ fido_assert_free(&assert);
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+verify_assert_data(struct passkey_data *data)
+{
+ fido_assert_t *assert = NULL;
+ struct pk_data_t pk_data = { 0 };
+ errno_t ret;
+
+ assert = fido_assert_new();
+ if (assert == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_assert_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Preparing assert data.\n");
+ ret = prepare_assert(data, 0, assert);
+ if (ret != FIDO_OK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Preparing assert authenticator data and signature.\n");
+ ret = set_assert_auth_data_signature(data, assert);
+ if (ret != FIDO_OK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Decoding public key.\n");
+ ret = public_key_to_libfido2(data->public_key_list[0], &pk_data);
+ if (ret != FIDO_OK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Verifying assert.\n");
+ ret = verify_assert(&pk_data, assert);
+ if (ret != FIDO_OK) {
+ goto done;
+ }
+
+ ret = FIDO_OK;
+
+done:
+ reset_public_key(&pk_data);
+ fido_assert_free(&assert);
+
+ return ret;
+}
diff --git a/src/passkey_child/passkey_child_credentials.c b/src/passkey_child/passkey_child_credentials.c
new file mode 100644
index 0000000..e27afb4
--- /dev/null
+++ b/src/passkey_child/passkey_child_credentials.c
@@ -0,0 +1,681 @@
+/*
+ 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 <fcntl.h>
+#include <termios.h>
+#include <stdio.h>
+
+#include <fido/es256.h>
+#include <fido/rs256.h>
+#include <fido/eddsa.h>
+
+#include "util/crypto/sss_crypto.h"
+#include "util/debug.h"
+#include "util/util.h"
+
+#include "passkey_child.h"
+
+#define IN_BUF_SIZE 1024
+
+errno_t
+prepare_credentials(struct passkey_data *data, fido_dev_t *dev,
+ fido_cred_t *cred)
+{
+ unsigned char cdh[32];
+ fido_opt_t rk = FIDO_OPT_OMIT;
+ bool has_pin;
+ bool has_uv;
+ errno_t ret = EOK;
+
+ ret = fido_cred_set_type(cred, data->type);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_cred_set_type failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ ret = sss_generate_csprng_buffer(cdh, sizeof(cdh));
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_generate_csprng_buffer failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ ret = fido_cred_set_clientdata_hash(cred, cdh, sizeof(cdh));
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_cred_set_clientdata_hash failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Setting Relying Party ID and name to %s.\n",
+ data->domain);
+
+ ret = fido_cred_set_rp(cred, data->domain, data->domain);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_cred_set_rp failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ if (data->user_id == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "user_id must be allocated before using it.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sss_generate_csprng_buffer(data->user_id, USER_ID_SIZE);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_generate_csprng_buffer failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "Setting user: %s.\n", data->shortname);
+
+ ret = fido_cred_set_user(cred, data->user_id, USER_ID_SIZE,
+ data->shortname, NULL, NULL);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_cred_set_user failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ if (data->cred_type == CRED_DISCOVERABLE) {
+ rk = FIDO_OPT_TRUE;
+ }
+
+ /* Set to FIDO_OPT_OMIT instead of FIDO_OPT_FALSE for compatibility reasons
+ */
+ ret = fido_cred_set_rk(cred, rk);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_cred_set_rk failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ has_uv = fido_dev_has_uv(dev);
+ has_pin = fido_dev_has_pin(dev);
+ if (data->user_verification == FIDO_OPT_TRUE && has_uv == false
+ && has_pin == false) {
+ 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_FALSE
+ && (has_uv == true || has_pin == true)) {
+ DEBUG(SSSDBG_CONF_SETTINGS,
+ "Policy disabled user-verification but the key settings are "
+ "enforcing it. Thus, enabling user-verification.\n");
+ data->user_verification = FIDO_OPT_TRUE;
+ }
+
+ if (has_uv == true) {
+ ret = fido_cred_set_uv(cred, FIDO_OPT_TRUE);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_cred_set_uv failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+ }
+
+done:
+ return ret;
+}
+
+errno_t
+passkey_recv_pin(TALLOC_CTX *mem_ctx, int fd, char **_pin)
+{
+ uint8_t buf[IN_BUF_SIZE];
+ ssize_t len;
+ errno_t ret;
+ char *str;
+
+ errno = 0;
+ len = sss_atomic_read_s(fd, buf, IN_BUF_SIZE);
+ if (len == -1) {
+ ret = errno;
+ ret = (ret == 0) ? EINVAL: ret;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "read failed [%d][%s].\n", ret, strerror(ret));
+ return ret;
+ }
+
+ if (len == 0 || *buf == '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing PIN.\n");
+ return EINVAL;
+ }
+
+ str = talloc_strndup(mem_ctx, (char *) buf, len);
+ if (str == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strndup failed.\n");
+ return ENOMEM;
+ }
+
+ if (strlen(str) != len) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Input contains additional data, only PIN expected.\n");
+ talloc_free(str);
+ return EINVAL;
+ }
+
+ *_pin = str;
+
+ return EOK;
+}
+
+ssize_t
+read_pin(char **pin)
+{
+ char *line_ptr = NULL;
+ struct termios old, new;
+ size_t line_len = 0;
+ ssize_t bytes_read;
+ ssize_t ret;
+
+ ret = tcgetattr(STDIN_FILENO, &old);
+ if (ret != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to get the parameters associated with stdin [%d]: %s.\n",
+ errno, sss_strerror(errno));
+ goto done;
+ }
+ new = old;
+ new.c_lflag &= ~ECHO;
+ ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, &new);
+ if (ret != 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to turn echoing off [%d]: %s.\n",
+ errno, sss_strerror(errno));
+ goto done;
+ }
+
+ PRINT("Enter PIN:\n");
+ fflush(stdout);
+ bytes_read = getline(&line_ptr, &line_len, stdin);
+ if (bytes_read == -1) {
+ DEBUG(SSSDBG_OP_FAILURE, "getline failed [%d]: %s.\n",
+ errno, sss_strerror(errno));
+ } else {
+ /* Remove the end of line '\n' character */
+ line_ptr[--bytes_read] = '\0';
+ }
+ PRINT("\n");
+
+ ret = tcsetattr(STDIN_FILENO, TCSAFLUSH, &old);
+ if (ret != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to restore parameters associated with stdin [%d]: %s.\n",
+ errno, sss_strerror(errno));
+ goto done;
+ }
+
+ ret = bytes_read;
+ *pin = line_ptr;
+
+done:
+ return ret;
+}
+
+errno_t
+generate_credentials(struct passkey_data *data, fido_dev_t *dev,
+ fido_cred_t *cred)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *pin = NULL;
+ char *tmp_pin = NULL;
+ bool has_pin;
+ ssize_t pin_len = 0;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new() failed.\n");
+ return ENOMEM;
+ }
+
+ has_pin = fido_dev_has_pin(dev);
+ if (has_pin == true) {
+ if (data->quiet == true) {
+ ret = passkey_recv_pin(tmp_ctx, STDIN_FILENO, &pin);
+ if (ret != EOK) {
+ goto done;
+ }
+ } else {
+ pin_len = read_pin(&tmp_pin);
+ if (pin_len == -1) {
+ ret = ERR_INPUT_PARSE;
+ goto done;
+ }
+ pin = talloc_strdup(tmp_ctx, tmp_pin);
+ sss_erase_mem_securely(tmp_pin, pin_len);
+ free(tmp_pin);
+ }
+ }
+
+ if (data->quiet == false) {
+ PRINT("Please touch the device.\n");
+ fflush(stdout);
+ }
+ ret = fido_dev_make_cred(dev, cred, pin);
+ sss_erase_mem_securely(pin, pin_len);
+
+ if (ret != FIDO_OK) {
+ if (ret == FIDO_ERR_PIN_INVALID) {
+ ERROR("Invalid PIN.\n");
+ }
+ DEBUG(SSSDBG_OP_FAILURE, "fido_dev_make_cred failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ if (has_pin == true) {
+ ret = fido_cred_set_uv(cred, FIDO_OPT_TRUE);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_cred_set_uv failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+ }
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+verify_credentials(const fido_cred_t *const cred)
+{
+ errno_t ret;
+
+ if (fido_cred_x5c_ptr(cred) != NULL) {
+ ret = fido_cred_verify(cred);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_cred_verify failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+ } else {
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "Attestation certificate missing. "
+ "Falling back to self attestation.\n");
+ ret = fido_cred_verify_self(cred);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "fido_cred_verify_self failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+ }
+
+done:
+ return ret;
+}
+
+errno_t
+print_credentials(const struct passkey_data *data,
+ const fido_cred_t *const cred)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ const unsigned char *cred_id = NULL;
+ const unsigned char *public_key = NULL;
+ const char *b64_cred_id = NULL;
+ char *pem_key = NULL;
+ size_t cred_id_len;
+ size_t user_key_len;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ cred_id = fido_cred_id_ptr(cred);
+ if (cred_id == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_cred_id_ptr failed.\n");
+ ret = ERR_CREDS_INVALID;
+ goto done;
+ }
+
+ cred_id_len = fido_cred_id_len(cred);
+ if (cred_id_len == 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_cred_id_len returned 0.\n");
+ ret = ERR_CREDS_INVALID;
+ goto done;
+ }
+
+ b64_cred_id = sss_base64_encode(tmp_ctx, cred_id, cred_id_len);
+ if (b64_cred_id == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "failed to encode key handle.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ public_key = fido_cred_pubkey_ptr(cred);
+ if (public_key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_cred_pubkey_ptr failed.\n");
+ ret = ERR_CREDS_INVALID;
+ goto done;
+ }
+
+ user_key_len = fido_cred_pubkey_len(cred);
+ if (user_key_len == 0) {
+ DEBUG(SSSDBG_OP_FAILURE, "fido_cred_pubkey_len returned 0.\n");
+ ret = ERR_CREDS_INVALID;
+ goto done;
+ }
+
+ ret = public_key_to_base64(tmp_ctx, data, public_key, user_key_len,
+ &pem_key);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "failed to format public key to b64 [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ PRINT("passkey:%s,%s\n", b64_cred_id, pem_key);
+ if (data->mapping_file != NULL) {
+ print_credentials_to_file(data, b64_cred_id, pem_key);
+ }
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+errno_t
+print_credentials_to_file(const struct passkey_data *data,
+ const char *b64_cred_id,
+ const char *pem_key)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *mapping_data = NULL;
+ int mapping_data_len = 0;
+ int fd = -1;
+ ssize_t written = 0;
+ errno_t ret;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ mapping_data = talloc_asprintf(tmp_ctx, "passkey:%s,%s",
+ b64_cred_id, pem_key);
+ if (mapping_data == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+ mapping_data_len = strlen(mapping_data);
+
+ fd = open(data->mapping_file, O_WRONLY|O_CREAT, 0640);
+ if (fd == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE,
+ "open() failed [%d][%s]\n", ret, strerror(ret));
+ ret = EIO;
+ goto done;
+ }
+
+ errno = 0;
+ written = sss_atomic_write_s(fd, mapping_data, mapping_data_len);
+ if (written == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Write failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+
+ if (written != mapping_data_len) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Write error, wrote [%zd] bytes, expected [%d]\n",
+ written, mapping_data_len);
+ ret = EIO;
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (fd != -1) {
+ if (close(fd) == -1) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Close failed [%s].\n", strerror(errno));
+ }
+ }
+ talloc_free(tmp_ctx);
+
+ return ret;
+}
+
+int
+es256_pubkey_to_evp_pkey(TALLOC_CTX *mem_ctx, const void *es256_key,
+ size_t es256_key_len, EVP_PKEY **_evp_pkey)
+{
+ EVP_PKEY *evp_pkey = NULL;
+ es256_pk_t *public_key = NULL;
+ errno_t ret;
+
+ public_key = es256_pk_new();
+ if (public_key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "es256_pk_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = es256_pk_from_ptr(public_key, es256_key, es256_key_len);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "es256_pk_from_ptr failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ evp_pkey = es256_pk_to_EVP_PKEY(public_key);
+ if (evp_pkey == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "es256_pk_to_EVP_PKEY failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_evp_pkey = evp_pkey;
+ ret = EOK;
+
+done:
+ es256_pk_free(&public_key);
+
+ return ret;
+}
+
+int
+rs256_pubkey_to_evp_pkey(TALLOC_CTX *mem_ctx, const void *rs256_key,
+ size_t rs256_key_len, EVP_PKEY **_evp_pkey)
+{
+ EVP_PKEY *evp_pkey = NULL;
+ rs256_pk_t *public_key = NULL;
+ errno_t ret;
+
+ public_key = rs256_pk_new();
+ if (public_key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "rs256_pk_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = rs256_pk_from_ptr(public_key, rs256_key, rs256_key_len);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "rs256_pk_from_ptr failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ evp_pkey = rs256_pk_to_EVP_PKEY(public_key);
+ if (evp_pkey == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "rs256_pk_to_EVP_PKEY failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_evp_pkey = evp_pkey;
+ ret = EOK;
+
+done:
+ rs256_pk_free(&public_key);
+
+ return ret;
+}
+
+int
+eddsa_pubkey_to_evp_pkey(TALLOC_CTX *mem_ctx, const void *eddsa_key,
+ size_t eddsa_key_len, EVP_PKEY **_evp_pkey)
+{
+ EVP_PKEY *evp_pkey = NULL;
+ eddsa_pk_t *public_key = NULL;
+ errno_t ret;
+
+ public_key = eddsa_pk_new();
+ if (public_key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "eddsa_pk_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = eddsa_pk_from_ptr(public_key, eddsa_key, eddsa_key_len);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE, "eddsa_pk_from_ptr failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ evp_pkey = eddsa_pk_to_EVP_PKEY(public_key);
+ if (evp_pkey == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "eddsa_pk_to_EVP_PKEY failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ *_evp_pkey = evp_pkey;
+ ret = EOK;
+
+done:
+ eddsa_pk_free(&public_key);
+
+ return ret;
+}
+
+errno_t
+evp_pkey_to_es256_pubkey(const EVP_PKEY *evp_pkey, struct pk_data_t *_pk_data)
+{
+ void *public_key = NULL;
+ errno_t ret;
+
+ public_key = es256_pk_new();
+ if (public_key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "es256_pk_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = es256_pk_from_EVP_PKEY(public_key, evp_pkey);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "es256_pk_from_EVP_PKEY failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ _pk_data->public_key = public_key;
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+errno_t
+evp_pkey_to_rs256_pubkey(const EVP_PKEY *evp_pkey, struct pk_data_t *_pk_data)
+{
+ void *public_key = NULL;
+ errno_t ret;
+
+ public_key = rs256_pk_new();
+ if (public_key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "rs256_pk_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = rs256_pk_from_EVP_PKEY(public_key, evp_pkey);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "rs256_pk_from_EVP_PKEY failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ _pk_data->public_key = public_key;
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+errno_t
+evp_pkey_to_eddsa_pubkey(const EVP_PKEY *evp_pkey, struct pk_data_t *_pk_data)
+{
+ void *public_key = NULL;
+ errno_t ret;
+
+ public_key = eddsa_pk_new();
+ if (public_key == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "eddsa_pk_new failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = eddsa_pk_from_EVP_PKEY(public_key, evp_pkey);
+ if (ret != FIDO_OK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "eddsa_pk_from_EVP_PKEY failed [%d]: %s.\n",
+ ret, fido_strerr(ret));
+ goto done;
+ }
+
+ _pk_data->public_key = public_key;
+ ret = EOK;
+
+done:
+ return ret;
+}
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;
+}