summaryrefslogtreecommitdiffstats
path: root/src/passkey_child/passkey_child_credentials.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
commit74aa0bc6779af38018a03fd2cf4419fe85917904 (patch)
tree9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/passkey_child/passkey_child_credentials.c
parentInitial commit. (diff)
downloadsssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz
sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/passkey_child/passkey_child_credentials.c')
-rw-r--r--src/passkey_child/passkey_child_credentials.c681
1 files changed, 681 insertions, 0 deletions
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;
+}