Resolves: RHEL-173741 - SSSD Rebase for RHEL 9.9 Resolves: RHEL-173740 - CVE-2026-6245 sssd: out-of-bounds read in the sssd Resolves: RHEL-150439 - Poor performance of `BE_REQ_INITGROUPS` handling by 'sssd_be' (LDAP RFC2307, no nested groups) Resolves: RHEL-143416 - Performance impact with enumerate in SSSD > 2.7.3 Resolves: RHEL-152067 - [RFE] SSSD PAM: Support Microsoft AD PKINIT authentication indicator (ms-pkca) for pam_gssapi_indicators_map Resolves: RHEL-173742 - Man page update: man sssd_krb5_localauth_plugin – Missing disable = an2ln Resolves: RHEL-173743 - Concurrent child processes trigger unnecessarily backtrace Resolves: RHEL-173744 - Detect foreign security principals in AD group members and silently ignore them Resolves: RHEL-156519 - GDM Support for IdM IdP feature and MFA [SSSD] Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
9566 lines
349 KiB
Diff
9566 lines
349 KiB
Diff
From 7d47dd3bd38d068d848d24fa36886173ce9568bf Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Thu, 18 Jan 2024 15:46:33 +0100
|
|
Subject: [PATCH 01/36] util: implement pam_get_response_data()
|
|
|
|
This API gets the selected response type data from the response_data
|
|
linked list. Includes unit tests.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Signed-off-by: Ray Strode <halfline@redhat.com>
|
|
---
|
|
Makefile.am | 18 +++
|
|
src/tests/cmocka/test_sss_pam_data.c | 171 +++++++++++++++++++++++++++
|
|
src/util/sss_pam_data.c | 34 ++++++
|
|
src/util/sss_pam_data.h | 17 +++
|
|
4 files changed, 240 insertions(+)
|
|
create mode 100644 src/tests/cmocka/test_sss_pam_data.c
|
|
|
|
diff --git a/Makefile.am b/Makefile.am
|
|
index 60bec863d..ab8a3fe9d 100644
|
|
--- a/Makefile.am
|
|
+++ b/Makefile.am
|
|
@@ -298,6 +298,7 @@ if HAVE_CMOCKA
|
|
test_sssd_krb5_locator_plugin \
|
|
test_confdb \
|
|
test_krb5_idp_plugin \
|
|
+ test_sss_pam_data \
|
|
$(NULL)
|
|
|
|
|
|
@@ -2694,6 +2695,23 @@ if BUILD_PASSKEY
|
|
pam_srv_tests_SOURCES += src/responder/pam/pamsrv_passkey.c
|
|
endif # BUILD_PASSKEY
|
|
|
|
+test_sss_pam_data_SOURCES = \
|
|
+ src/util/sss_pam_data.c \
|
|
+ src/tests/cmocka/test_sss_pam_data.c \
|
|
+ $(NULL)
|
|
+test_sss_pam_data_CFLAGS = \
|
|
+ $(AM_CFLAGS) \
|
|
+ $(NULL)
|
|
+test_sss_pam_data_LDFLAGS = \
|
|
+ $(NULL)
|
|
+test_sss_pam_data_LDADD = \
|
|
+ $(CMOCKA_LIBS) \
|
|
+ $(SSSD_LIBS) \
|
|
+ $(SSSD_INTERNAL_LTLIBS) \
|
|
+ $(TALLOC_LIBS) \
|
|
+ libsss_test_common.la \
|
|
+ $(NULL)
|
|
+
|
|
EXTRA_ssh_srv_tests_DEPENDENCIES = \
|
|
$(ldblib_LTLIBRARIES) \
|
|
$(NULL)
|
|
diff --git a/src/tests/cmocka/test_sss_pam_data.c b/src/tests/cmocka/test_sss_pam_data.c
|
|
new file mode 100644
|
|
index 000000000..442b37372
|
|
--- /dev/null
|
|
+++ b/src/tests/cmocka/test_sss_pam_data.c
|
|
@@ -0,0 +1,171 @@
|
|
+/*
|
|
+ SSSD
|
|
+
|
|
+ Unit test for sss_pam_data
|
|
+
|
|
+ Authors:
|
|
+ Iker Pedrosa <ipedrosa@redhat.com>
|
|
+
|
|
+ Copyright (C) 2024 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 "tests/cmocka/common_mock.h"
|
|
+
|
|
+#include "util/sss_pam_data.h"
|
|
+
|
|
+#define PASSKEY_PIN "1234"
|
|
+#define OAUTH2_URI "short.url.com/tmp\0"
|
|
+#define OAUTH2_CODE "1234-5678"
|
|
+#define OAUTH2_STR OAUTH2_URI OAUTH2_CODE
|
|
+#define CCACHE_NAME "KRB5CCNAME=KCM:"
|
|
+
|
|
+
|
|
+/***********************
|
|
+ * TEST
|
|
+ **********************/
|
|
+void test_pam_get_response_data_not_found(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct pam_data *pd = NULL;
|
|
+ uint8_t *buf = NULL;
|
|
+ int32_t len;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ pd = talloc(test_ctx, struct pam_data);
|
|
+ assert_non_null(pd);
|
|
+ pd->resp_list = NULL;
|
|
+ pam_add_response(pd, SSS_PAM_PASSKEY_INFO, 5, discard_const(PASSKEY_PIN));
|
|
+
|
|
+ ret = pam_get_response_data(test_ctx, pd, SSS_PAM_OAUTH2_INFO, &buf, &len);
|
|
+ assert_int_equal(ret, ENOENT);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
+void test_pam_get_response_data_one_element(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct pam_data *pd = NULL;
|
|
+ uint8_t *buf = NULL;
|
|
+ int32_t len;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ pd = talloc(test_ctx, struct pam_data);
|
|
+ assert_non_null(pd);
|
|
+ pd->resp_list = NULL;
|
|
+ pam_add_response(pd, SSS_PAM_PASSKEY_INFO, 5, discard_const(PASSKEY_PIN));
|
|
+
|
|
+ ret = pam_get_response_data(test_ctx, pd, SSS_PAM_PASSKEY_INFO, &buf, &len);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_int_equal(len, strlen(PASSKEY_PIN) + 1);
|
|
+ assert_string_equal((const char*) buf, PASSKEY_PIN);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
+void test_pam_get_response_data_three_elements(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct pam_data *pd = NULL;
|
|
+ uint8_t *buf = NULL;
|
|
+ int32_t len;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ pd = talloc(test_ctx, struct pam_data);
|
|
+ assert_non_null(pd);
|
|
+ pd->resp_list = NULL;
|
|
+ pam_add_response(pd, SSS_PAM_PASSKEY_INFO, 5, discard_const(PASSKEY_PIN));
|
|
+ len = strlen(OAUTH2_URI)+1+strlen(OAUTH2_CODE)+1;
|
|
+ pam_add_response(pd, SSS_PAM_OAUTH2_INFO, len, discard_const(OAUTH2_STR));
|
|
+ len = strlen(CCACHE_NAME) + 1;
|
|
+ pam_add_response(pd, SSS_PAM_ENV_ITEM, len, discard_const(CCACHE_NAME));
|
|
+
|
|
+ ret = pam_get_response_data(test_ctx, pd, SSS_PAM_ENV_ITEM, &buf, &len);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_int_equal(len, strlen(CCACHE_NAME) + 1);
|
|
+ assert_string_equal((const char*) buf, CCACHE_NAME);
|
|
+
|
|
+ ret = pam_get_response_data(test_ctx, pd, SSS_PAM_OAUTH2_INFO, &buf, &len);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_int_equal(len, strlen(OAUTH2_URI)+1+strlen(OAUTH2_CODE)+1);
|
|
+ assert_string_equal((const char*) buf, OAUTH2_URI);
|
|
+ assert_string_equal((const char*) buf+strlen(OAUTH2_URI)+1, OAUTH2_CODE);
|
|
+
|
|
+ ret = pam_get_response_data(test_ctx, pd, SSS_PAM_PASSKEY_INFO, &buf, &len);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_int_equal(len, strlen(PASSKEY_PIN) + 1);
|
|
+ assert_string_equal((const char*) buf, PASSKEY_PIN);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
+static void test_parse_supp_valgrind_args(void)
|
|
+{
|
|
+ /*
|
|
+ * The objective of this function is to filter the unit-test functions
|
|
+ * that trigger a valgrind memory leak and suppress them to avoid false
|
|
+ * positives.
|
|
+ */
|
|
+ DEBUG_CLI_INIT(debug_level);
|
|
+}
|
|
+
|
|
+int main(int argc, const char *argv[])
|
|
+{
|
|
+ poptContext pc;
|
|
+ int opt;
|
|
+ struct poptOption long_options[] = {
|
|
+ POPT_AUTOHELP
|
|
+ SSSD_DEBUG_OPTS
|
|
+ POPT_TABLEEND
|
|
+ };
|
|
+
|
|
+ const struct CMUnitTest tests[] = {
|
|
+ cmocka_unit_test(test_pam_get_response_data_not_found),
|
|
+ cmocka_unit_test(test_pam_get_response_data_one_element),
|
|
+ cmocka_unit_test(test_pam_get_response_data_three_elements),
|
|
+ };
|
|
+
|
|
+ /* 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) {
|
|
+ default:
|
|
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
|
|
+ poptBadOption(pc, 0), poptStrerror(opt));
|
|
+ poptPrintUsage(pc, stderr, 0);
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ poptFreeContext(pc);
|
|
+
|
|
+ test_parse_supp_valgrind_args();
|
|
+
|
|
+ /* Even though normally the tests should clean up after themselves
|
|
+ * they might not after a failed run. Remove the old DB to be sure */
|
|
+ tests_set_cwd();
|
|
+
|
|
+ return cmocka_run_group_tests(tests, NULL, NULL);
|
|
+}
|
|
diff --git a/src/util/sss_pam_data.c b/src/util/sss_pam_data.c
|
|
index f09b9c5eb..75421d8e0 100644
|
|
--- a/src/util/sss_pam_data.c
|
|
+++ b/src/util/sss_pam_data.c
|
|
@@ -203,3 +203,37 @@ int pam_add_response(struct pam_data *pd, enum response_type type,
|
|
|
|
return EOK;
|
|
}
|
|
+
|
|
+errno_t
|
|
+pam_get_response_data(TALLOC_CTX *mem_ctx, struct pam_data *pd, int32_t type,
|
|
+ uint8_t **_buf, int32_t *_len)
|
|
+{
|
|
+ struct response_data *data = pd->resp_list;
|
|
+ struct response_data *match = NULL;
|
|
+ uint8_t *buf = NULL;
|
|
+ int ret;
|
|
+
|
|
+ while (data != NULL) {
|
|
+ if (data->type == type) match = data;
|
|
+
|
|
+ data = data->next;
|
|
+ }
|
|
+
|
|
+ if (match != NULL) {
|
|
+ buf = talloc_memdup(mem_ctx, match->data, match->len);
|
|
+ if (buf == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ *_buf = buf;
|
|
+ *_len = match->len;
|
|
+ ret = EOK;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = ENOENT;
|
|
+
|
|
+done:
|
|
+ return ret;
|
|
+}
|
|
diff --git a/src/util/sss_pam_data.h b/src/util/sss_pam_data.h
|
|
index e9b90a8a4..a7efba791 100644
|
|
--- a/src/util/sss_pam_data.h
|
|
+++ b/src/util/sss_pam_data.h
|
|
@@ -96,4 +96,21 @@ int pam_add_response(struct pam_data *pd,
|
|
enum response_type type,
|
|
int len, const uint8_t *data);
|
|
|
|
+/**
|
|
+ * @brief Get the selected response type data from the response_data linked
|
|
+ * list
|
|
+ *
|
|
+ * @param[in] mem_ctx Memory context
|
|
+ * @param[in] pd Data structure containing the response_data linked list
|
|
+ * @param[in] type Response type
|
|
+ * @param[out] _buf Data wrapped inside response_data structure
|
|
+ * @param[out] _len Data length
|
|
+ *
|
|
+ * @return 0 if the data was obtained properly,
|
|
+ * error code otherwise.
|
|
+ */
|
|
+errno_t
|
|
+pam_get_response_data(TALLOC_CTX *mem_ctx, struct pam_data *pd, int32_t type,
|
|
+ uint8_t **_buf, int32_t *_len);
|
|
+
|
|
#endif /* _SSS_PAM_DATA_H_ */
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From ecd8ebbccd44d149010476199bc7904d5b3fa084 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Wed, 8 May 2024 10:46:47 +0200
|
|
Subject: [PATCH 02/36] sss_client: add EIdP to prompt_config structure
|
|
|
|
Integration with GDM requests two prompts for EIdP so adding them to
|
|
prompt_config structure. In addition, implement all the functions needed
|
|
to manipulate the structure for these new prompts. Finally, add
|
|
unit-tests for the new functions.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/sss_client/pam_sss_prompt_config.c | 146 +++++++++++++++++++++++++
|
|
src/sss_client/sss_cli.h | 5 +
|
|
src/tests/cmocka/test_prompt_config.c | 39 ++++++-
|
|
3 files changed, 185 insertions(+), 5 deletions(-)
|
|
|
|
diff --git a/src/sss_client/pam_sss_prompt_config.c b/src/sss_client/pam_sss_prompt_config.c
|
|
index f3360544b..891fcd60c 100644
|
|
--- a/src/sss_client/pam_sss_prompt_config.c
|
|
+++ b/src/sss_client/pam_sss_prompt_config.c
|
|
@@ -49,6 +49,11 @@ struct prompt_config_sc_pin {
|
|
char *prompt; /* Currently not used */
|
|
};
|
|
|
|
+struct prompt_config_eidp {
|
|
+ char *prompt_init;
|
|
+ char *prompt_link;
|
|
+};
|
|
+
|
|
struct prompt_config {
|
|
enum prompt_config_type type;
|
|
union {
|
|
@@ -57,6 +62,7 @@ struct prompt_config {
|
|
struct prompt_config_2fa_single two_fa_single;
|
|
struct prompt_config_passkey passkey;
|
|
struct prompt_config_sc_pin sc_pin;
|
|
+ struct prompt_config_eidp eidp;
|
|
} data;
|
|
};
|
|
|
|
@@ -116,6 +122,22 @@ const char *pc_get_passkey_inter_prompt(struct prompt_config *pc)
|
|
return NULL;
|
|
}
|
|
|
|
+const char *pc_get_eidp_init_prompt(struct prompt_config *pc)
|
|
+{
|
|
+ if (pc != NULL && (pc_get_type(pc) == PC_TYPE_EIDP)) {
|
|
+ return pc->data.eidp.prompt_init;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+const char *pc_get_eidp_link_prompt(struct prompt_config *pc)
|
|
+{
|
|
+ if (pc != NULL && (pc_get_type(pc) == PC_TYPE_EIDP)) {
|
|
+ return pc->data.eidp.prompt_link;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
static void pc_free_passkey(struct prompt_config *pc)
|
|
{
|
|
if (pc != NULL && pc_get_type(pc) == PC_TYPE_PASSKEY) {
|
|
@@ -165,6 +187,17 @@ static void pc_free_sc_pin(struct prompt_config *pc)
|
|
return;
|
|
}
|
|
|
|
+static void pc_free_eidp(struct prompt_config *pc)
|
|
+{
|
|
+ if (pc != NULL && pc_get_type(pc) == PC_TYPE_EIDP) {
|
|
+ free(pc->data.eidp.prompt_init);
|
|
+ pc->data.eidp.prompt_init = NULL;
|
|
+ free(pc->data.eidp.prompt_link);
|
|
+ pc->data.eidp.prompt_link = NULL;
|
|
+ }
|
|
+ return;
|
|
+}
|
|
+
|
|
|
|
void pc_list_free(struct prompt_config **pc_list)
|
|
{
|
|
@@ -191,6 +224,9 @@ void pc_list_free(struct prompt_config **pc_list)
|
|
case PC_TYPE_PASSKEY:
|
|
pc_free_passkey(pc_list[c]);
|
|
break;
|
|
+ case PC_TYPE_EIDP:
|
|
+ pc_free_eidp(pc_list[c]);
|
|
+ break;
|
|
default:
|
|
return;
|
|
}
|
|
@@ -396,6 +432,53 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+errno_t pc_list_add_eidp(struct prompt_config ***pc_list,
|
|
+ const char *prompt_init, const char *prompt_link)
|
|
+{
|
|
+ struct prompt_config *pc;
|
|
+ int ret;
|
|
+
|
|
+ if (pc_list == NULL) {
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ pc = calloc(1, sizeof(struct prompt_config));
|
|
+ if (pc == NULL) {
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ pc->type = PC_TYPE_EIDP;
|
|
+
|
|
+ pc->data.eidp.prompt_init = strdup(prompt_init != NULL ? prompt_init
|
|
+ : "");
|
|
+ if (pc->data.eidp.prompt_init == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ pc->data.eidp.prompt_link = strdup(prompt_link != NULL ? prompt_link
|
|
+ : "");
|
|
+ if (pc->data.eidp.prompt_link == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = pc_list_add_pc(pc_list, pc);
|
|
+ if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ if (ret != EOK) {
|
|
+ free(pc->data.eidp.prompt_init);
|
|
+ free(pc->data.eidp.prompt_link);
|
|
+ free(pc);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
errno_t pam_get_response_prompt_config(struct prompt_config **pc_list, int *len,
|
|
uint8_t **data)
|
|
{
|
|
@@ -435,6 +518,12 @@ errno_t pam_get_response_prompt_config(struct prompt_config **pc_list, int *len,
|
|
break;
|
|
case PC_TYPE_SC_PIN:
|
|
break;
|
|
+ case PC_TYPE_EIDP:
|
|
+ l += sizeof(uint32_t);
|
|
+ l += strlen(pc_list[c]->data.eidp.prompt_init);
|
|
+ l += sizeof(uint32_t);
|
|
+ l += strlen(pc_list[c]->data.eidp.prompt_link);
|
|
+ break;
|
|
default:
|
|
return EINVAL;
|
|
}
|
|
@@ -494,6 +583,18 @@ errno_t pam_get_response_prompt_config(struct prompt_config **pc_list, int *len,
|
|
break;
|
|
case PC_TYPE_SC_PIN:
|
|
break;
|
|
+ case PC_TYPE_EIDP:
|
|
+ SAFEALIGN_SET_UINT32(&d[rp],
|
|
+ strlen(pc_list[c]->data.eidp.prompt_init),
|
|
+ &rp);
|
|
+ safealign_memcpy(&d[rp], pc_list[c]->data.eidp.prompt_init,
|
|
+ strlen(pc_list[c]->data.eidp.prompt_init), &rp);
|
|
+ SAFEALIGN_SET_UINT32(&d[rp],
|
|
+ strlen(pc_list[c]->data.eidp.prompt_link),
|
|
+ &rp);
|
|
+ safealign_memcpy(&d[rp], pc_list[c]->data.eidp.prompt_link,
|
|
+ strlen(pc_list[c]->data.eidp.prompt_link), &rp);
|
|
+ break;
|
|
default:
|
|
free(d);
|
|
return EINVAL;
|
|
@@ -681,6 +782,51 @@ errno_t pc_list_from_response(int size, uint8_t *buf,
|
|
break;
|
|
case PC_TYPE_SC_PIN:
|
|
break;
|
|
+ case PC_TYPE_EIDP:
|
|
+ if (rp > size - sizeof(uint32_t)) {
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp);
|
|
+
|
|
+ if (l > size || rp > size - l) {
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ str = strndup((char *) buf + rp, l);
|
|
+ if (str == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ rp += l;
|
|
+
|
|
+ if (rp > size - sizeof(uint32_t)) {
|
|
+ free(str);
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp);
|
|
+
|
|
+ if (l > size || rp > size - l) {
|
|
+ free(str);
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ str2 = strndup((char *) buf + rp, l);
|
|
+ if (str2 == NULL) {
|
|
+ free(str);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ rp += l;
|
|
+
|
|
+ ret = pc_list_add_eidp(&pl, str, str2);
|
|
+ free(str);
|
|
+ free(str2);
|
|
+ if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+ break;
|
|
default:
|
|
ret = EINVAL;
|
|
goto done;
|
|
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
|
|
index 29b496e1d..792c34cb9 100644
|
|
--- a/src/sss_client/sss_cli.h
|
|
+++ b/src/sss_client/sss_cli.h
|
|
@@ -664,6 +664,7 @@ enum prompt_config_type {
|
|
PC_TYPE_2FA_SINGLE,
|
|
PC_TYPE_PASSKEY,
|
|
PC_TYPE_SC_PIN,
|
|
+ PC_TYPE_EIDP,
|
|
PC_TYPE_LAST
|
|
};
|
|
|
|
@@ -676,6 +677,8 @@ const char *pc_get_2fa_2nd_prompt(struct prompt_config *pc);
|
|
const char *pc_get_2fa_single_prompt(struct prompt_config *pc);
|
|
const char *pc_get_passkey_inter_prompt(struct prompt_config *pc);
|
|
const char *pc_get_passkey_touch_prompt(struct prompt_config *pc);
|
|
+const char *pc_get_eidp_init_prompt(struct prompt_config *pc);
|
|
+const char *pc_get_eidp_link_prompt(struct prompt_config *pc);
|
|
errno_t pc_list_add_passkey(struct prompt_config ***pc_list,
|
|
const char *inter_prompt,
|
|
const char *touch_prompt);
|
|
@@ -686,6 +689,8 @@ errno_t pc_list_add_2fa(struct prompt_config ***pc_list,
|
|
const char *prompt_1st, const char *prompt_2nd);
|
|
errno_t pc_list_add_2fa_single(struct prompt_config ***pc_list,
|
|
const char *prompt);
|
|
+errno_t pc_list_add_eidp(struct prompt_config ***pc_list,
|
|
+ const char *prompt_init, const char *prompt_link);
|
|
errno_t pam_get_response_prompt_config(struct prompt_config **pc_list, int *len,
|
|
uint8_t **data);
|
|
errno_t pc_list_from_response(int size, uint8_t *buf,
|
|
diff --git a/src/tests/cmocka/test_prompt_config.c b/src/tests/cmocka/test_prompt_config.c
|
|
index 0b761ae4c..70b27875a 100644
|
|
--- a/src/tests/cmocka/test_prompt_config.c
|
|
+++ b/src/tests/cmocka/test_prompt_config.c
|
|
@@ -100,6 +100,23 @@ void test_pc_list_add_2fa(void **state)
|
|
pc_list_free(pc_list);
|
|
}
|
|
|
|
+void test_pc_list_add_eidp(void **state)
|
|
+{
|
|
+ int ret;
|
|
+ struct prompt_config **pc_list = NULL;
|
|
+
|
|
+ ret = pc_list_add_eidp(&pc_list, "init", "link");
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_non_null(pc_list);
|
|
+ assert_non_null(pc_list[0]);
|
|
+ assert_int_equal(PC_TYPE_EIDP, pc_get_type(pc_list[0]));
|
|
+ assert_string_equal("init", pc_get_eidp_init_prompt(pc_list[0]));
|
|
+ assert_string_equal("link", pc_get_eidp_link_prompt(pc_list[0]));
|
|
+ assert_null(pc_list[1]);
|
|
+
|
|
+ pc_list_free(pc_list);
|
|
+}
|
|
+
|
|
void test_pam_get_response_prompt_config(void **state)
|
|
{
|
|
int ret;
|
|
@@ -116,15 +133,18 @@ void test_pam_get_response_prompt_config(void **state)
|
|
ret = pc_list_add_2fa_single(&pc_list, "single");
|
|
assert_int_equal(ret, EOK);
|
|
|
|
+ ret = pc_list_add_eidp(&pc_list, "init", "link");
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
ret = pam_get_response_prompt_config(pc_list, &len, &data);
|
|
pc_list_free(pc_list);
|
|
assert_int_equal(ret, EOK);
|
|
- assert_int_equal(len, 57);
|
|
+ assert_int_equal(len, 77);
|
|
|
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
- assert_memory_equal(data, "\3\0\0\0\1\0\0\0\10\0\0\0" "password\2\0\0\0\5\0\0\0" "first\6\0\0\0" "second\3\0\0\0\6\0\0\0" "single", len);
|
|
+ assert_memory_equal(data, "\4\0\0\0\1\0\0\0\10\0\0\0" "password\2\0\0\0\5\0\0\0" "first\6\0\0\0" "second\3\0\0\0\6\0\0\0" "single\6\0\0\0\4\0\0\0" "init\4\0\0\0" "link", len);
|
|
#else
|
|
- assert_memory_equal(data, "\0\0\0\3\0\0\0\1\0\0\0\10" "password\0\0\0\2\0\0\0\5" "first\0\0\0\6" "second\0\0\0\3\0\0\0\6" "single", len);
|
|
+ assert_memory_equal(data, "\0\0\0\4\0\0\0\1\0\0\0\10" "password\0\0\0\2\0\0\0\5" "first\0\0\0\6" "second\0\0\0\3\0\0\0\6" "single\0\0\0\6\0\0\0\4" "init\0\0\0\4" "link", len);
|
|
#endif
|
|
|
|
free(data);
|
|
@@ -146,10 +166,13 @@ void test_pc_list_from_response(void **state)
|
|
ret = pc_list_add_2fa_single(&pc_list, "single");
|
|
assert_int_equal(ret, EOK);
|
|
|
|
+ ret = pc_list_add_eidp(&pc_list, "init", "link");
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
ret = pam_get_response_prompt_config(pc_list, &len, &data);
|
|
pc_list_free(pc_list);
|
|
assert_int_equal(ret, EOK);
|
|
- assert_int_equal(len, 57);
|
|
+ assert_int_equal(len, 77);
|
|
|
|
pc_list = NULL;
|
|
|
|
@@ -171,7 +194,12 @@ void test_pc_list_from_response(void **state)
|
|
assert_int_equal(PC_TYPE_2FA_SINGLE, pc_get_type(pc_list[2]));
|
|
assert_string_equal("single", pc_get_2fa_single_prompt(pc_list[2]));
|
|
|
|
- assert_null(pc_list[3]);
|
|
+ assert_non_null(pc_list[3]);
|
|
+ assert_int_equal(PC_TYPE_EIDP, pc_get_type(pc_list[3]));
|
|
+ assert_string_equal("init", pc_get_eidp_init_prompt(pc_list[3]));
|
|
+ assert_string_equal("link", pc_get_eidp_link_prompt(pc_list[3]));
|
|
+
|
|
+ assert_null(pc_list[4]);
|
|
|
|
pc_list_free(pc_list);
|
|
}
|
|
@@ -190,6 +218,7 @@ int main(int argc, const char *argv[])
|
|
cmocka_unit_test(test_pc_list_add_password),
|
|
cmocka_unit_test(test_pc_list_add_2fa_single),
|
|
cmocka_unit_test(test_pc_list_add_2fa),
|
|
+ cmocka_unit_test(test_pc_list_add_eidp),
|
|
cmocka_unit_test(test_pam_get_response_prompt_config),
|
|
cmocka_unit_test(test_pc_list_from_response),
|
|
};
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 88c7149497557970e22468ab323a208caa1c1833 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Wed, 8 May 2024 10:30:54 +0200
|
|
Subject: [PATCH 03/36] Responder: tune prompts in the GUI
|
|
|
|
Return `prompt_config` structure in `pam_eval_prompting_config` to tune
|
|
the prompts from the SSSD config in the GUI.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pam_prompting_config.c | 8 ++++++--
|
|
src/responder/pam/pamsrv.h | 6 +++++-
|
|
src/responder/pam/pamsrv_cmd.c | 3 ++-
|
|
3 files changed, 13 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/src/responder/pam/pam_prompting_config.c b/src/responder/pam/pam_prompting_config.c
|
|
index 7d0362fbb..27f794b92 100644
|
|
--- a/src/responder/pam/pam_prompting_config.c
|
|
+++ b/src/responder/pam/pam_prompting_config.c
|
|
@@ -212,7 +212,8 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
-errno_t pam_eval_prompting_config(struct pam_ctx *pctx, struct pam_data *pd)
|
|
+errno_t pam_eval_prompting_config(struct pam_ctx *pctx, struct pam_data *pd,
|
|
+ struct prompt_config ***_pc_list)
|
|
{
|
|
int ret;
|
|
struct prompt_config **pc_list = NULL;
|
|
@@ -300,10 +301,13 @@ errno_t pam_eval_prompting_config(struct pam_ctx *pctx, struct pam_data *pd)
|
|
}
|
|
}
|
|
|
|
+ *_pc_list = pc_list;
|
|
ret = EOK;
|
|
done:
|
|
free(resp_data);
|
|
- pc_list_free(pc_list);
|
|
+ if (ret != EOK) {
|
|
+ pc_list_free(pc_list);
|
|
+ }
|
|
|
|
return ret;
|
|
}
|
|
diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
|
|
index 0246faa14..a2567b068 100644
|
|
--- a/src/responder/pam/pamsrv.h
|
|
+++ b/src/responder/pam/pamsrv.h
|
|
@@ -28,6 +28,9 @@
|
|
#include "responder/common/cache_req/cache_req.h"
|
|
#include "lib/certmap/sss_certmap.h"
|
|
|
|
+#define PROMPT_CONFIG_FIRST 1
|
|
+#define PROMPT_CONFIG_SECOND 2
|
|
+
|
|
struct pam_auth_req;
|
|
|
|
typedef void (pam_dp_callback_t)(struct pam_auth_req *preq);
|
|
@@ -174,7 +177,8 @@ errno_t filter_responses(struct pam_ctx *pctx,
|
|
|
|
errno_t pam_get_auth_types(struct pam_data *pd,
|
|
struct pam_resp_auth_type *_auth_types);
|
|
-errno_t pam_eval_prompting_config(struct pam_ctx *pctx, struct pam_data *pd);
|
|
+errno_t pam_eval_prompting_config(struct pam_ctx *pctx, struct pam_data *pd,
|
|
+ struct prompt_config ***_pc_list);
|
|
|
|
enum pam_initgroups_scheme pam_initgroups_string_to_enum(const char *str);
|
|
const char *pam_initgroup_enum_to_string(enum pam_initgroups_scheme scheme);
|
|
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
|
|
index 62587f053..3bd19f243 100644
|
|
--- a/src/responder/pam/pamsrv_cmd.c
|
|
+++ b/src/responder/pam/pamsrv_cmd.c
|
|
@@ -1231,6 +1231,7 @@ void pam_reply(struct pam_auth_req *preq)
|
|
int pam_verbosity;
|
|
bool local_sc_auth_allow = false;
|
|
bool local_passkey_auth_allow = false;
|
|
+ struct prompt_config **pc_list = NULL;
|
|
#ifdef BUILD_PASSKEY
|
|
bool pk_preauth_done = false;
|
|
bool pk_kerberos = false;
|
|
@@ -1510,7 +1511,7 @@ void pam_reply(struct pam_auth_req *preq)
|
|
}
|
|
|
|
if (pd->cmd == SSS_PAM_PREAUTH) {
|
|
- ret = pam_eval_prompting_config(pctx, pd);
|
|
+ ret = pam_eval_prompting_config(pctx, pd, &pc_list);
|
|
if (ret != EOK) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "Failed to add prompting information, "
|
|
"using defaults.\n");
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From b3a9cb2ac244efc5a20ad62dbd570bb10667043a Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Fri, 19 Jan 2024 09:27:24 +0100
|
|
Subject: [PATCH 04/36] Responder: generate JSON message for GUI
|
|
|
|
Implement a set of functions to check the available authentication
|
|
mechanisms and their associated data, and generate a JSON message with
|
|
it. This JSON formatted message will be consumed by apps that provide
|
|
GUI login (i.e. GDM). Currently, the implementation only takes into
|
|
account password and OAUTH2 mechanisms.
|
|
|
|
Include unit tests to check the implemented functions.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
Makefile.am | 40 +++
|
|
src/responder/pam/pamsrv_json.c | 407 ++++++++++++++++++++++++++++
|
|
src/responder/pam/pamsrv_json.h | 112 ++++++++
|
|
src/sss_client/sss_cli.h | 5 +
|
|
src/tests/cmocka/test_pamsrv_json.c | 288 ++++++++++++++++++++
|
|
5 files changed, 852 insertions(+)
|
|
create mode 100644 src/responder/pam/pamsrv_json.c
|
|
create mode 100644 src/responder/pam/pamsrv_json.h
|
|
create mode 100644 src/tests/cmocka/test_pamsrv_json.c
|
|
|
|
diff --git a/Makefile.am b/Makefile.am
|
|
index ab8a3fe9d..290a94890 100644
|
|
--- a/Makefile.am
|
|
+++ b/Makefile.am
|
|
@@ -299,6 +299,7 @@ if HAVE_CMOCKA
|
|
test_confdb \
|
|
test_krb5_idp_plugin \
|
|
test_sss_pam_data \
|
|
+ test_pamsrv_json \
|
|
$(NULL)
|
|
|
|
|
|
@@ -750,6 +751,7 @@ dist_noinst_HEADERS = \
|
|
src/responder/common/cache_req/cache_req_private.h \
|
|
src/responder/pam/pamsrv.h \
|
|
src/responder/pam/pam_helpers.h \
|
|
+ src/responder/pam/pamsrv_json.h \
|
|
src/responder/pam/pamsrv_passkey.h \
|
|
src/responder/nss/nss_private.h \
|
|
src/responder/nss/nss_protocol.h \
|
|
@@ -2695,6 +2697,44 @@ if BUILD_PASSKEY
|
|
pam_srv_tests_SOURCES += src/responder/pam/pamsrv_passkey.c
|
|
endif # BUILD_PASSKEY
|
|
|
|
+test_pamsrv_json_SOURCES = \
|
|
+ $(TEST_MOCK_RESP_OBJ) \
|
|
+ src/responder/pam/pamsrv_cmd.c \
|
|
+ src/responder/pam/pamsrv_json.c \
|
|
+ src/responder/pam/pamsrv_p11.c \
|
|
+ src/responder/pam/pamsrv_gssapi.c \
|
|
+ src/responder/pam/pam_helpers.c \
|
|
+ src/responder/pam/pamsrv_dp.c \
|
|
+ src/responder/pam/pam_prompting_config.c \
|
|
+ src/sss_client/pam_sss_prompt_config.c \
|
|
+ src/tests/cmocka/test_pamsrv_json.c \
|
|
+ $(NULL)
|
|
+if BUILD_PASSKEY
|
|
+ test_pamsrv_json_SOURCES += src/responder/pam/pamsrv_passkey.c
|
|
+endif # BUILD_PASSKEY
|
|
+test_pamsrv_json_CFLAGS = \
|
|
+ $(AM_CFLAGS) \
|
|
+ $(CMOCKA_CFLAGS) \
|
|
+ $(NULL)
|
|
+test_pamsrv_json_LDFLAGS = \
|
|
+ -Wl,-wrap,json_array_append_new \
|
|
+ $(NULL)
|
|
+test_pamsrv_json_LDADD = \
|
|
+ $(LIBADD_DL) \
|
|
+ $(CMOCKA_LIBS) \
|
|
+ $(PAM_LIBS) \
|
|
+ $(SSSD_LIBS) \
|
|
+ $(SSSD_INTERNAL_LTLIBS) \
|
|
+ $(JANSSON_LIBS) \
|
|
+ $(GSSAPI_KRB5_LIBS) \
|
|
+ $(TALLOC_LIBS) \
|
|
+ libsss_test_common.la \
|
|
+ libsss_idmap.la \
|
|
+ libsss_certmap.la \
|
|
+ libsss_iface.la \
|
|
+ libsss_sbus.la \
|
|
+ $(NULL)
|
|
+
|
|
test_sss_pam_data_SOURCES = \
|
|
src/util/sss_pam_data.c \
|
|
src/tests/cmocka/test_sss_pam_data.c \
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
new file mode 100644
|
|
index 000000000..c95247420
|
|
--- /dev/null
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -0,0 +1,407 @@
|
|
+/*
|
|
+ SSSD
|
|
+
|
|
+ pamsrv_json authentication selection helper for GDM
|
|
+
|
|
+ Authors:
|
|
+ Iker Pedrosa <ipedrosa@redhat.com>
|
|
+
|
|
+ Copyright (C) 2024 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 <errno.h>
|
|
+#include <stdbool.h>
|
|
+#include <string.h>
|
|
+
|
|
+#include "responder/pam/pamsrv.h"
|
|
+#include "util/debug.h"
|
|
+
|
|
+#include "pamsrv_json.h"
|
|
+
|
|
+
|
|
+static errno_t
|
|
+obtain_oauth2_data(TALLOC_CTX *mem_ctx, struct pam_data *pd, char **_uri,
|
|
+ char **_code)
|
|
+{
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ uint8_t *oauth2 = NULL;
|
|
+ char *uri = NULL;
|
|
+ char *uri_complete = NULL;
|
|
+ char *code = NULL;
|
|
+ int32_t len;
|
|
+ int32_t offset;
|
|
+ int32_t str_len;
|
|
+ int ret;
|
|
+
|
|
+ tmp_ctx = talloc_new(NULL);
|
|
+ if (tmp_ctx == NULL) {
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ ret = pam_get_response_data(tmp_ctx, pd, SSS_PAM_OAUTH2_INFO, &oauth2, &len);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "Unable to get SSS_PAM_OAUTH2_INFO, ret %d.\n",
|
|
+ ret);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ str_len = strnlen((const char *)oauth2, len);
|
|
+ if (str_len >= len) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "uri string is not null-terminated within buffer bounds.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ uri = talloc_strndup(tmp_ctx, (const char *)oauth2, str_len);
|
|
+ if (uri == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ offset = str_len + 1;
|
|
+
|
|
+ if (offset >= len) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "Trying to access data outside of the boundaries.\n");
|
|
+ ret = EPERM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ str_len = strnlen((const char *)oauth2 + offset, len - offset);
|
|
+ if (str_len >= (len - offset)) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "uri_complete string is not null-terminated within buffer bounds.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ uri_complete = talloc_strndup(tmp_ctx, (const char *)oauth2 + offset, str_len);
|
|
+ if (uri_complete == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ offset += str_len + 1;
|
|
+
|
|
+ if (offset >= len) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "Trying to access data outside of the boundaries.\n");
|
|
+ ret = EPERM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ str_len = strnlen((const char *)oauth2 + offset, len - offset);
|
|
+ if (str_len >= (len - offset)) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "code string is not null-terminated within buffer bounds.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ code = talloc_strndup(tmp_ctx, (const char *)oauth2 + offset, str_len);
|
|
+ if (code == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ *_uri = talloc_steal(mem_ctx, uri);
|
|
+ *_code = talloc_steal(mem_ctx, code);
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ talloc_free(tmp_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static errno_t
|
|
+obtain_prompts(struct confdb_ctx *cdb, TALLOC_CTX *mem_ctx,
|
|
+ struct prompt_config **pc_list, const char **_password_prompt,
|
|
+ const char **_oauth2_init_prompt, const char **_oauth2_link_prompt)
|
|
+{
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ char *password_prompt = NULL;
|
|
+ char *oauth2_init_prompt = NULL;
|
|
+ char *oauth2_link_prompt = NULL;
|
|
+ errno_t ret;
|
|
+
|
|
+ tmp_ctx = talloc_new(NULL);
|
|
+ if (tmp_ctx == NULL) {
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ password_prompt = talloc_strdup(tmp_ctx, PASSWORD_PROMPT);
|
|
+ if (password_prompt == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ oauth2_init_prompt = talloc_strdup(tmp_ctx, OAUTH2_INIT_PROMPT);
|
|
+ if (oauth2_init_prompt == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ oauth2_link_prompt = talloc_strdup(tmp_ctx, OAUTH2_LINK_PROMPT);
|
|
+ if (oauth2_link_prompt == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ *_password_prompt = talloc_steal(mem_ctx, password_prompt);
|
|
+ *_oauth2_init_prompt = talloc_steal(mem_ctx, oauth2_init_prompt);
|
|
+ *_oauth2_link_prompt = talloc_steal(mem_ctx, oauth2_link_prompt);
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ talloc_free(tmp_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+errno_t
|
|
+json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
+ bool oauth2_auth, const char *uri, const char *code,
|
|
+ const char *oauth2_init_prompt,
|
|
+ const char *oauth2_link_prompt,
|
|
+ json_t **_list_mech)
|
|
+{
|
|
+ json_t *root = NULL;
|
|
+ json_t *json_pass = NULL;
|
|
+ json_t *json_oauth2 = NULL;
|
|
+ int ret;
|
|
+
|
|
+ root = json_object();
|
|
+ if (root == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_array failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (password_auth) {
|
|
+ json_pass = json_pack("{s:s,s:s,s:s}",
|
|
+ "name", "Password",
|
|
+ "role", "password",
|
|
+ "prompt", password_prompt);
|
|
+ if (json_pass == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_pack failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = json_object_set_new(root, "password", json_pass);
|
|
+ if (ret == -1) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_array_append failed.\n");
|
|
+ json_decref(json_pass);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (oauth2_auth) {
|
|
+ json_oauth2 = json_pack("{s:s,s:s,s:s,s:s,s:s,s:s,s:i}",
|
|
+ "name", "Web Login",
|
|
+ "role", "eidp",
|
|
+ "init_prompt", oauth2_init_prompt,
|
|
+ "link_prompt", oauth2_link_prompt,
|
|
+ "uri", uri,
|
|
+ "code", code,
|
|
+ "timeout", 300);
|
|
+ if (json_oauth2 == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_pack failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = json_object_set_new(root, "eidp", json_oauth2);
|
|
+ if (ret == -1) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_array_append failed.\n");
|
|
+ json_decref(json_oauth2);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ *_list_mech = root;
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ if (ret != EOK) {
|
|
+ json_decref(root);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+errno_t
|
|
+json_format_priority(bool password_auth, bool oauth2_auth, json_t **_priority)
|
|
+{
|
|
+ json_t *root = NULL;
|
|
+ json_t *json_priority = NULL;
|
|
+ int ret;
|
|
+
|
|
+ root = json_array();
|
|
+ if (root == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_array failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (oauth2_auth) {
|
|
+ json_priority = json_string("eidp");
|
|
+ ret = json_array_append_new(root, json_priority);
|
|
+ if (ret == -1) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_array_append failed.\n");
|
|
+ json_decref(json_priority);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (password_auth) {
|
|
+ json_priority = json_string("password");
|
|
+ ret = json_array_append_new(root, json_priority);
|
|
+ if (ret == -1) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_array_append failed.\n");
|
|
+ json_decref(json_priority);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ ret = EOK;
|
|
+ *_priority = root;
|
|
+
|
|
+done:
|
|
+ if (ret != EOK) {
|
|
+ json_decref(root);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+errno_t
|
|
+json_format_auth_selection(TALLOC_CTX *mem_ctx,
|
|
+ bool password_auth, const char *password_prompt,
|
|
+ bool oauth2_auth, const char *uri, const char *code,
|
|
+ const char *oauth2_init_prompt,
|
|
+ const char *oauth2_link_prompt,
|
|
+ char **_result)
|
|
+{
|
|
+ json_t *root = NULL;
|
|
+ json_t *json_mech = NULL;
|
|
+ json_t *json_priority = NULL;
|
|
+ char *string = NULL;
|
|
+ int ret;
|
|
+
|
|
+ ret = json_format_mechanisms(password_auth, password_prompt,
|
|
+ oauth2_auth, uri, code, oauth2_init_prompt,
|
|
+ oauth2_link_prompt, &json_mech);
|
|
+ if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = json_format_priority(password_auth, oauth2_auth, &json_priority);
|
|
+ if (ret != EOK) {
|
|
+ json_decref(json_mech);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ root = json_pack("{s:{s:o,s:o}}",
|
|
+ "auth-selection",
|
|
+ "mechanisms", json_mech,
|
|
+ "priority", json_priority);
|
|
+ if (root == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_pack failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ json_decref(json_mech);
|
|
+ json_decref(json_priority);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ string = json_dumps(root, 0);
|
|
+ if (string == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_dumps failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ *_result = talloc_strdup(mem_ctx, string);
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ free(string);
|
|
+ json_decref(root);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+errno_t
|
|
+generate_json_auth_message(struct confdb_ctx *cdb,
|
|
+ struct prompt_config **pc_list,
|
|
+ struct pam_data *_pd)
|
|
+{
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ const char *password_prompt = NULL;
|
|
+ const char *oauth2_init_prompt = NULL;
|
|
+ const char *oauth2_link_prompt = NULL;
|
|
+ char *oauth2_uri = NULL;
|
|
+ char *oauth2_code = NULL;
|
|
+ char *result = NULL;
|
|
+ bool oauth2_auth = true;
|
|
+ int ret;
|
|
+
|
|
+ tmp_ctx = talloc_new(NULL);
|
|
+ if (tmp_ctx == NULL) {
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ ret = obtain_prompts(cdb, tmp_ctx, pc_list, &password_prompt,
|
|
+ &oauth2_init_prompt, &oauth2_link_prompt);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failure to obtain the prompts.\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = obtain_oauth2_data(tmp_ctx, _pd, &oauth2_uri, &oauth2_code);
|
|
+ if (ret == ENOENT) {
|
|
+ oauth2_auth = false;
|
|
+ } else if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = json_format_auth_selection(tmp_ctx, true, password_prompt,
|
|
+ oauth2_auth, oauth2_uri, oauth2_code,
|
|
+ oauth2_init_prompt, oauth2_link_prompt,
|
|
+ &result);
|
|
+ if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = pam_add_response(_pd, SSS_PAM_JSON_AUTH_INFO, strlen(result)+1,
|
|
+ (const uint8_t *)result);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
|
|
+ goto done;
|
|
+ }
|
|
+ DEBUG(SSSDBG_TRACE_FUNC, "Generated JSON message: %s.\n", result);
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ talloc_free(tmp_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
diff --git a/src/responder/pam/pamsrv_json.h b/src/responder/pam/pamsrv_json.h
|
|
new file mode 100644
|
|
index 000000000..57fce53e2
|
|
--- /dev/null
|
|
+++ b/src/responder/pam/pamsrv_json.h
|
|
@@ -0,0 +1,112 @@
|
|
+/*
|
|
+ SSSD
|
|
+
|
|
+ pamsrv_json authentication selection helper for GDM
|
|
+
|
|
+ Authors:
|
|
+ Iker Pedrosa <ipedrosa@redhat.com>
|
|
+
|
|
+ Copyright (C) 2024 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 __PAMSRV_JSON__H__
|
|
+#define __PAMSRV_JSON__H__
|
|
+
|
|
+#include <jansson.h>
|
|
+#include <talloc.h>
|
|
+
|
|
+#include "util/sss_pam_data.h"
|
|
+
|
|
+#define PASSWORD_PROMPT "Password"
|
|
+#define OAUTH2_INIT_PROMPT "Log In"
|
|
+#define OAUTH2_LINK_PROMPT "Log in online with another device"
|
|
+
|
|
+
|
|
+/**
|
|
+ * @brief Format authentication mechanisms to JSON
|
|
+ *
|
|
+ * @param[in] password_auth Whether password authentication is allowed
|
|
+ * @param[in] password_prompt Password prompt
|
|
+ * @param[in] oath2_auth Whether OAUTH2 authentication is allowed
|
|
+ * @param[in] uri OAUTH2 uri
|
|
+ * @param[in] code OAUTH2 code
|
|
+ * @param[in] oauth2_init_prompt OAUTH2 initial prompt
|
|
+ * @param[in] oauth2_link_prompt OAUTH2 link prompt
|
|
+ * @param[out] _list_mech authentication mechanisms JSON object
|
|
+ *
|
|
+ * @return 0 if the authentication mechanisms were formatted properly,
|
|
+ * error code otherwise.
|
|
+ */
|
|
+errno_t
|
|
+json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
+ bool oauth2_auth, const char *uri, const char *code,
|
|
+ const char *oauth2_init_prompt,
|
|
+ const char *oauth2_link_prompt,
|
|
+ json_t **_list_mech);
|
|
+
|
|
+/**
|
|
+ * @brief Format priority to JSON
|
|
+ *
|
|
+ * @param[in] password_auth Whether password authentication is allowed
|
|
+ * @param[in] oath2_auth Whether OAUTH2 authentication is allowed
|
|
+ * @param[out] _priority priority JSON object
|
|
+ *
|
|
+ * @return 0 if the priority was formatted properly,
|
|
+ * error code otherwise.
|
|
+ */
|
|
+errno_t
|
|
+json_format_priority(bool password_auth, bool oauth2_auth, json_t **_priority);
|
|
+
|
|
+/**
|
|
+ * @brief Format data to JSON
|
|
+ *
|
|
+ * @param[in] mem_ctx Memory context
|
|
+ * @param[in] password_auth Whether password authentication is allowed
|
|
+ * @param[in] password_prompt Password prompt
|
|
+ * @param[in] oath2_auth Whether OAUTH2 authentication is allowed
|
|
+ * @param[in] uri OAUTH2 uri
|
|
+ * @param[in] code OAUTH2 code
|
|
+ * @param[in] oauth2_init_prompt OAUTH2 initial prompt
|
|
+ * @param[in] oauth2_link_prompt OAUTH2 link prompt
|
|
+ * @param[out] _result JSON message
|
|
+ *
|
|
+ * @return 0 if the JSON message was formatted properly,
|
|
+ * error code otherwise.
|
|
+ */
|
|
+errno_t
|
|
+json_format_auth_selection(TALLOC_CTX *mem_ctx,
|
|
+ bool password_auth, const char *password_prompt,
|
|
+ bool oath2_auth, const char *uri, const char *code,
|
|
+ const char *oauth2_init_prompt,
|
|
+ const char *oauth2_link_prompt,
|
|
+ char **_result);
|
|
+
|
|
+/**
|
|
+ * @brief Check the internal data and generate the JSON message
|
|
+ *
|
|
+ * @param[in] cdb The connection object to the confdb
|
|
+ * @param[in] pc_list List that contains all authentication mechanisms prompts
|
|
+ * @param[out] pd Data structure containing the response_data linked list
|
|
+ *
|
|
+ * @return 0 if the data was extracted correctly and JSON message was formatted
|
|
+ * properly, error code otherwise.
|
|
+ */
|
|
+errno_t
|
|
+generate_json_auth_message(struct confdb_ctx *cdb,
|
|
+ struct prompt_config **pc_list,
|
|
+ struct pam_data *_pd);
|
|
+
|
|
+#endif /* __PAMSRV_JSON__H__ */
|
|
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
|
|
index 792c34cb9..b99c145c4 100644
|
|
--- a/src/sss_client/sss_cli.h
|
|
+++ b/src/sss_client/sss_cli.h
|
|
@@ -556,6 +556,11 @@ enum response_type {
|
|
* - user verification (string)
|
|
* - key (string)
|
|
*/
|
|
+ SSS_PAM_JSON_AUTH_INFO, /**< A JSON formatted message containing the available
|
|
+ * authentication mechanisms and their associated data.
|
|
+ * @param
|
|
+ * - json_auth_msg
|
|
+ */
|
|
};
|
|
|
|
/**
|
|
diff --git a/src/tests/cmocka/test_pamsrv_json.c b/src/tests/cmocka/test_pamsrv_json.c
|
|
new file mode 100644
|
|
index 000000000..7f391732d
|
|
--- /dev/null
|
|
+++ b/src/tests/cmocka/test_pamsrv_json.c
|
|
@@ -0,0 +1,288 @@
|
|
+/*
|
|
+ SSSD
|
|
+
|
|
+ Unit test for pamsrv_json
|
|
+
|
|
+ Authors:
|
|
+ Iker Pedrosa <ipedrosa@redhat.com>
|
|
+
|
|
+ Copyright (C) 2024 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 <popt.h>
|
|
+
|
|
+#include "tests/cmocka/common_mock.h"
|
|
+
|
|
+#include "src/responder/pam/pamsrv_json.h"
|
|
+
|
|
+#define OAUTH2_URI "short.url.com/tmp\0"
|
|
+#define OAUTH2_URI_COMP "\0"
|
|
+#define OAUTH2_CODE "1234-5678"
|
|
+#define OAUTH2_STR OAUTH2_URI OAUTH2_URI_COMP OAUTH2_CODE
|
|
+
|
|
+#define BASIC_PASSWORD "\"password\": {" \
|
|
+ "\"name\": \"Password\", \"role\": \"password\", " \
|
|
+ "\"prompt\": \"Password\"}"
|
|
+#define BASIC_OAUTH2 "\"eidp\": {" \
|
|
+ "\"name\": \"Web Login\", \"role\": \"eidp\", " \
|
|
+ "\"init_prompt\": \"" OAUTH2_INIT_PROMPT "\", " \
|
|
+ "\"link_prompt\": \"" OAUTH2_LINK_PROMPT "\", " \
|
|
+ "\"uri\": \"short.url.com/tmp\", \"code\": \"1234-5678\", " \
|
|
+ "\"timeout\": 300}"
|
|
+#define MECHANISMS_PASSWORD "{" BASIC_PASSWORD "}"
|
|
+#define MECHANISMS_OAUTH2 "{" BASIC_OAUTH2 "}"
|
|
+#define PRIORITY_ALL "[\"eidp\", \"password\"]"
|
|
+#define AUTH_SELECTION_PASSWORD "{\"auth-selection\": {\"mechanisms\": " \
|
|
+ MECHANISMS_PASSWORD ", " \
|
|
+ "\"priority\": [\"password\"]}}"
|
|
+#define AUTH_SELECTION_OAUTH2 "{\"auth-selection\": {\"mechanisms\": " \
|
|
+ MECHANISMS_OAUTH2 ", " \
|
|
+ "\"priority\": [\"eidp\"]}}"
|
|
+#define AUTH_SELECTION_ALL "{\"auth-selection\": {\"mechanisms\": {" \
|
|
+ BASIC_PASSWORD ", " \
|
|
+ BASIC_OAUTH2 "}, " \
|
|
+ "\"priority\": " PRIORITY_ALL "}}"
|
|
+
|
|
+
|
|
+/***********************
|
|
+ * WRAPPERS
|
|
+ **********************/
|
|
+int __real_json_array_append_new(json_t *array, json_t *value);
|
|
+
|
|
+int
|
|
+__wrap_json_array_append_new(json_t *array, json_t *value)
|
|
+{
|
|
+ int fail;
|
|
+ int ret;
|
|
+
|
|
+ fail = mock();
|
|
+
|
|
+ if(fail) {
|
|
+ ret = -1;
|
|
+ } else {
|
|
+ ret = __real_json_array_append_new(array, value);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+/***********************
|
|
+ * TEST
|
|
+ **********************/
|
|
+void test_json_format_mechanisms_password(void **state)
|
|
+{
|
|
+ json_t *mechs = NULL;
|
|
+ char *string;
|
|
+ int ret;
|
|
+
|
|
+ ret = json_format_mechanisms(true, PASSWORD_PROMPT, false, NULL, NULL,
|
|
+ NULL, NULL, &mechs);
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
+ string = json_dumps(mechs, 0);
|
|
+ assert_string_equal(string, MECHANISMS_PASSWORD);
|
|
+ json_decref(mechs);
|
|
+ free(string);
|
|
+}
|
|
+
|
|
+void test_json_format_mechanisms_oauth2(void **state)
|
|
+{
|
|
+ json_t *mechs = NULL;
|
|
+ char *string;
|
|
+ int ret;
|
|
+
|
|
+ ret = json_format_mechanisms(false, NULL, true, OAUTH2_URI, OAUTH2_CODE,
|
|
+ OAUTH2_INIT_PROMPT, OAUTH2_LINK_PROMPT,
|
|
+ &mechs);
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
+ string = json_dumps(mechs, 0);
|
|
+ assert_string_equal(string, MECHANISMS_OAUTH2);
|
|
+ json_decref(mechs);
|
|
+ free(string);
|
|
+}
|
|
+
|
|
+void test_json_format_priority_all(void **state)
|
|
+{
|
|
+ json_t *priority = NULL;
|
|
+ char *string;
|
|
+ int ret;
|
|
+
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ ret = json_format_priority(true, true, &priority);
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
+ string = json_dumps(priority, 0);
|
|
+ assert_string_equal(string, PRIORITY_ALL);
|
|
+ json_decref(priority);
|
|
+ free(string);
|
|
+}
|
|
+
|
|
+void test_json_format_auth_selection_password(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx;
|
|
+ char *json_msg = NULL;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ ret = json_format_auth_selection(test_ctx, true, PASSWORD_PROMPT,
|
|
+ false, NULL, NULL, NULL, NULL, &json_msg);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_string_equal(json_msg, AUTH_SELECTION_PASSWORD);
|
|
+}
|
|
+
|
|
+void test_json_format_auth_selection_oauth2(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx;
|
|
+ char *json_msg = NULL;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ ret = json_format_auth_selection(test_ctx, false, NULL,
|
|
+ true, OAUTH2_URI, OAUTH2_CODE,
|
|
+ OAUTH2_INIT_PROMPT, OAUTH2_LINK_PROMPT,
|
|
+ &json_msg);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_string_equal(json_msg, AUTH_SELECTION_OAUTH2);
|
|
+}
|
|
+
|
|
+void test_json_format_auth_selection_all(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx;
|
|
+ char *json_msg = NULL;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ ret = json_format_auth_selection(test_ctx, true, PASSWORD_PROMPT,
|
|
+ true, OAUTH2_URI, OAUTH2_CODE,
|
|
+ OAUTH2_INIT_PROMPT, OAUTH2_LINK_PROMPT,
|
|
+ &json_msg);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_string_equal(json_msg, AUTH_SELECTION_ALL);
|
|
+}
|
|
+
|
|
+void test_json_format_auth_selection_failure(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx;
|
|
+ char *json_msg = NULL;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+
|
|
+ will_return(__wrap_json_array_append_new, true);
|
|
+ ret = json_format_auth_selection(test_ctx, true, PASSWORD_PROMPT,
|
|
+ true, OAUTH2_URI, OAUTH2_CODE,
|
|
+ OAUTH2_INIT_PROMPT, OAUTH2_LINK_PROMPT,
|
|
+ &json_msg);
|
|
+ assert_int_equal(ret, ENOMEM);
|
|
+ assert_null(json_msg);
|
|
+}
|
|
+
|
|
+void test_generate_json_message_integration(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx;
|
|
+ struct pam_data *pd = NULL;
|
|
+ struct prompt_config **pc_list = NULL;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ pd = talloc_zero(test_ctx, struct pam_data);
|
|
+ assert_non_null(pd);
|
|
+
|
|
+ pd->resp_list = talloc(pd, struct response_data);
|
|
+ pd->resp_list->type = SSS_PAM_OAUTH2_INFO;
|
|
+ pd->resp_list->len = strlen(OAUTH2_URI)+1+strlen(OAUTH2_URI_COMP)+1+strlen(OAUTH2_CODE)+1;
|
|
+ pd->resp_list->data = discard_const(OAUTH2_STR);
|
|
+ pd->resp_list->next = NULL;
|
|
+
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ ret = generate_json_auth_message(NULL, pc_list, pd);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_string_equal((char*) pd->resp_list->data, AUTH_SELECTION_ALL);
|
|
+
|
|
+ pc_list_free(pc_list);
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
+static void test_parse_supp_valgrind_args(void)
|
|
+{
|
|
+ /*
|
|
+ * The objective of this function is to filter the unit-test functions
|
|
+ * that trigger a valgrind memory leak and suppress them to avoid false
|
|
+ * positives.
|
|
+ */
|
|
+ DEBUG_CLI_INIT(debug_level);
|
|
+}
|
|
+
|
|
+int main(int argc, const char *argv[])
|
|
+{
|
|
+ poptContext pc;
|
|
+ int opt;
|
|
+ struct poptOption long_options[] = {
|
|
+ POPT_AUTOHELP
|
|
+ SSSD_DEBUG_OPTS
|
|
+ POPT_TABLEEND
|
|
+ };
|
|
+
|
|
+ const struct CMUnitTest tests[] = {
|
|
+ cmocka_unit_test(test_json_format_mechanisms_password),
|
|
+ cmocka_unit_test(test_json_format_mechanisms_oauth2),
|
|
+ cmocka_unit_test(test_json_format_priority_all),
|
|
+ cmocka_unit_test(test_json_format_auth_selection_password),
|
|
+ cmocka_unit_test(test_json_format_auth_selection_oauth2),
|
|
+ cmocka_unit_test(test_json_format_auth_selection_all),
|
|
+ cmocka_unit_test(test_json_format_auth_selection_failure),
|
|
+ cmocka_unit_test(test_generate_json_message_integration),
|
|
+ };
|
|
+
|
|
+ /* 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) {
|
|
+ default:
|
|
+ fprintf(stderr, "\nInvalid option %s: %s\n\n",
|
|
+ poptBadOption(pc, 0), poptStrerror(opt));
|
|
+ poptPrintUsage(pc, stderr, 0);
|
|
+ return 1;
|
|
+ }
|
|
+ }
|
|
+ poptFreeContext(pc);
|
|
+
|
|
+ test_parse_supp_valgrind_args();
|
|
+
|
|
+ /* Even though normally the tests should clean up after themselves
|
|
+ * they might not after a failed run. Remove the old DB to be sure */
|
|
+ tests_set_cwd();
|
|
+
|
|
+ return cmocka_run_group_tests(tests, NULL, NULL);
|
|
+}
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From a05cb30e05fcfc2766b2cb76180956dbaf378139 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Tue, 30 Jan 2024 11:21:59 +0100
|
|
Subject: [PATCH 05/36] Responder: unpack JSON reply from GUI
|
|
|
|
Implement a set of functions to unpack the JSON reply from the GUI.
|
|
Include unit tests to check the implemented functions.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_json.c | 170 ++++++++++++++++++++++++++++
|
|
src/responder/pam/pamsrv_json.h | 38 +++++++
|
|
src/tests/cmocka/test_pamsrv_json.c | 116 +++++++++++++++++++
|
|
src/util/sss_pam_data.h | 2 +
|
|
4 files changed, 326 insertions(+)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
index c95247420..f0ed24f7a 100644
|
|
--- a/src/responder/pam/pamsrv_json.c
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -405,3 +405,173 @@ done:
|
|
|
|
return ret;
|
|
}
|
|
+
|
|
+errno_t
|
|
+json_unpack_password(json_t *jroot, char **_password)
|
|
+{
|
|
+ char *password = NULL;
|
|
+ int ret = EOK;
|
|
+
|
|
+ ret = json_unpack(jroot, "{s:s}",
|
|
+ "password", &password);
|
|
+ if (ret != 0) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "json_unpack for password failed.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ *_password = password;
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+errno_t
|
|
+json_unpack_oauth2_code(TALLOC_CTX *mem_ctx, char *json_auth_msg,
|
|
+ char **_oauth2_code)
|
|
+{
|
|
+ json_t *jroot = NULL;
|
|
+ json_t *json_mechs = NULL;
|
|
+ json_t *json_priority = NULL;
|
|
+ json_t *json_mech = NULL;
|
|
+ json_t *jobj = NULL;
|
|
+ const char *key = NULL;
|
|
+ const char *oauth2_code = NULL;
|
|
+ json_error_t jret;
|
|
+ int ret = EOK;
|
|
+
|
|
+ jroot = json_loads(json_auth_msg, 0, &jret);
|
|
+ if (jroot == NULL) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "json_loads failed.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = json_unpack(jroot, "{s:{s:o,s:o}}",
|
|
+ "auth-selection",
|
|
+ "mechanisms", &json_mechs,
|
|
+ "priority", &json_priority);
|
|
+ if (ret != 0) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "json_unpack failed.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ json_object_foreach(json_mechs, key, json_mech){
|
|
+ if (strcmp(key, "eidp") == 0) {
|
|
+ json_object_foreach(json_mech, key, jobj){
|
|
+ if (strcmp(key, "code") == 0) {
|
|
+ oauth2_code = json_string_value(jobj);
|
|
+ ret = EOK;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "OAUTH2 code not found in JSON message.\n");
|
|
+ ret = ENOENT;
|
|
+
|
|
+done:
|
|
+ if (ret == EOK) {
|
|
+ *_oauth2_code = talloc_strdup(mem_ctx, oauth2_code);
|
|
+ }
|
|
+ if (jroot != NULL) {
|
|
+ json_decref(jroot);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+errno_t
|
|
+json_unpack_auth_reply(struct pam_data *pd)
|
|
+{
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ json_t *jroot = NULL;
|
|
+ json_t *jauth_selection = NULL;
|
|
+ json_t *jobj = NULL;
|
|
+ json_error_t jret;
|
|
+ const char *key = NULL;
|
|
+ const char *status = NULL;
|
|
+ char *password = NULL;
|
|
+ char *oauth2_code = NULL;
|
|
+ int ret = EOK;
|
|
+
|
|
+ DEBUG(SSSDBG_TRACE_FUNC, "Received JSON message: %s.\n",
|
|
+ pd->json_auth_selected);
|
|
+
|
|
+ tmp_ctx = talloc_new(NULL);
|
|
+ if (tmp_ctx == NULL) {
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ jroot = json_loads(pd->json_auth_selected, 0, &jret);
|
|
+ if (jroot == NULL) {
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = json_unpack(jroot, "{s:o}", "auth-selection", &jauth_selection);
|
|
+ if (ret != 0) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "json_unpack for auth-selection failed.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ json_object_foreach(jauth_selection, key, jobj){
|
|
+ if (strcmp(key, "status") == 0) {
|
|
+ status = json_string_value(jobj);
|
|
+ if (status == NULL) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "NULL status returned.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ } else if (strcmp(status, "Ok") != 0) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "Incorrect status returned: %s.\n", status);
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (strcmp(key, "password") == 0) {
|
|
+ ret = json_unpack_password(jobj, &password);
|
|
+ if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = sss_authtok_set_password(pd->authtok, password, strlen(password));
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "sss_authtok_set_password failed: %d.\n", ret);
|
|
+ }
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (strcmp(key, "eidp") == 0) {
|
|
+ ret = json_unpack_oauth2_code(tmp_ctx, pd->json_auth_msg, &oauth2_code);
|
|
+ if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = sss_authtok_set_oauth2(pd->authtok, oauth2_code,
|
|
+ strlen(oauth2_code));
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "sss_authtok_set_oauth2 failed: %d.\n", ret);
|
|
+ }
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unknown authentication mechanism\n");
|
|
+ ret = EINVAL;
|
|
+
|
|
+done:
|
|
+ if (jroot != NULL) {
|
|
+ json_decref(jroot);
|
|
+ }
|
|
+ talloc_free(tmp_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
diff --git a/src/responder/pam/pamsrv_json.h b/src/responder/pam/pamsrv_json.h
|
|
index 57fce53e2..d861120df 100644
|
|
--- a/src/responder/pam/pamsrv_json.h
|
|
+++ b/src/responder/pam/pamsrv_json.h
|
|
@@ -109,4 +109,42 @@ generate_json_auth_message(struct confdb_ctx *cdb,
|
|
struct prompt_config **pc_list,
|
|
struct pam_data *_pd);
|
|
|
|
+
|
|
+/**
|
|
+ * @brief Unpack password specific data reply
|
|
+ *
|
|
+ * @param[in] jroot jansson structure containing the password specific data
|
|
+ * @param[out] _password user password
|
|
+ *
|
|
+ * @return 0 if the reply was unpacked and the result is ok,
|
|
+ * error code otherwise.
|
|
+ */
|
|
+errno_t
|
|
+json_unpack_password(json_t *jroot, char **_password);
|
|
+
|
|
+/**
|
|
+ * @brief Unpack OAUTH2 code
|
|
+ *
|
|
+ * @param[in] mem_ctx Memory context
|
|
+ * @param[in] json_auth_msg JSON authentication mechanisms message
|
|
+ * @param[out] _oauth2_code OAUTH2 code
|
|
+ *
|
|
+ * @return 0 if the reply was unpacked and the result is ok,
|
|
+ * error code otherwise.
|
|
+ */
|
|
+errno_t
|
|
+json_unpack_oauth2_code(TALLOC_CTX *mem_ctx, char *json_auth_msg,
|
|
+ char **_oauth2_code);
|
|
+
|
|
+/**
|
|
+ * @brief Unpack GDM reply and check its value
|
|
+ *
|
|
+ * @param[in] pd pam_data containing the GDM reply in JSON format
|
|
+ *
|
|
+ * @return 0 if the reply was unpacked and the result is ok,
|
|
+ * error code otherwise.
|
|
+ */
|
|
+errno_t
|
|
+json_unpack_auth_reply(struct pam_data *pd);
|
|
+
|
|
#endif /* __PAMSRV_JSON__H__ */
|
|
diff --git a/src/tests/cmocka/test_pamsrv_json.c b/src/tests/cmocka/test_pamsrv_json.c
|
|
index 7f391732d..6d00d0bd0 100644
|
|
--- a/src/tests/cmocka/test_pamsrv_json.c
|
|
+++ b/src/tests/cmocka/test_pamsrv_json.c
|
|
@@ -57,6 +57,15 @@
|
|
BASIC_OAUTH2 "}, " \
|
|
"\"priority\": " PRIORITY_ALL "}}"
|
|
|
|
+#define PASSWORD_CONTENT "{\"password\": \"ThePassword\"}"
|
|
+#define AUTH_MECH_REPLY_PASSWORD "{\"auth-selection\": {" \
|
|
+ "\"status\": \"Ok\", \"password\": " \
|
|
+ PASSWORD_CONTENT "}}"
|
|
+#define AUTH_MECH_REPLY_OAUTH2 "{\"auth-selection\": {" \
|
|
+ "\"status\": \"Ok\", \"eidp\": {}}}"
|
|
+#define AUTH_MECH_ERRONEOUS "{\"auth-selection\": {" \
|
|
+ "\"status\": \"Ok\", \"lololo\": {}}}"
|
|
+
|
|
|
|
/***********************
|
|
* WRAPPERS
|
|
@@ -232,6 +241,108 @@ void test_generate_json_message_integration(void **state)
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
+void test_json_unpack_password_ok(void **state)
|
|
+{
|
|
+ json_t *jroot = NULL;
|
|
+ char *password = NULL;
|
|
+ json_error_t jret;
|
|
+ int ret;
|
|
+
|
|
+ jroot = json_loads(PASSWORD_CONTENT, 0, &jret);
|
|
+ assert_non_null(jroot);
|
|
+
|
|
+ ret = json_unpack_password(jroot, &password);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_string_equal(password, "ThePassword");
|
|
+ json_decref(jroot);
|
|
+}
|
|
+
|
|
+void test_json_unpack_auth_reply_password(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct pam_data *pd = NULL;
|
|
+ const char *password = NULL;
|
|
+ size_t len;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ pd = talloc_zero(test_ctx, struct pam_data);
|
|
+ assert_non_null(pd);
|
|
+ pd->authtok = sss_authtok_new(pd);
|
|
+ assert_non_null(pd->authtok);
|
|
+ pd->json_auth_selected = discard_const(AUTH_MECH_REPLY_PASSWORD);
|
|
+
|
|
+ ret = json_unpack_auth_reply(pd);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_int_equal(sss_authtok_get_type(pd->authtok), SSS_AUTHTOK_TYPE_PASSWORD);
|
|
+ sss_authtok_get_password(pd->authtok, &password, &len);
|
|
+ assert_string_equal(password, "ThePassword");
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
+void test_json_unpack_auth_reply_oauth2(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct pam_data *pd = NULL;
|
|
+ const char *code = NULL;
|
|
+ size_t len;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ pd = talloc_zero(test_ctx, struct pam_data);
|
|
+ assert_non_null(pd);
|
|
+ pd->authtok = sss_authtok_new(pd);
|
|
+ assert_non_null(pd->authtok);
|
|
+ pd->json_auth_msg = discard_const(AUTH_SELECTION_OAUTH2);
|
|
+ pd->json_auth_selected = discard_const(AUTH_MECH_REPLY_OAUTH2);
|
|
+
|
|
+ ret = json_unpack_auth_reply(pd);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_int_equal(sss_authtok_get_type(pd->authtok), SSS_AUTHTOK_TYPE_OAUTH2);
|
|
+ sss_authtok_get_oauth2(pd->authtok, &code, &len);
|
|
+ assert_string_equal(code, OAUTH2_CODE);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
+void test_json_unpack_auth_reply_failure(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct pam_data *pd = NULL;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ pd = talloc_zero(test_ctx, struct pam_data);
|
|
+ assert_non_null(pd);
|
|
+ pd->json_auth_selected = discard_const(AUTH_MECH_ERRONEOUS);
|
|
+
|
|
+ ret = json_unpack_auth_reply(pd);
|
|
+ assert_int_equal(ret, EINVAL);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
+void test_json_unpack_oauth2_code(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ char *oauth2_code = NULL;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+
|
|
+ ret = json_unpack_oauth2_code(test_ctx, discard_const(AUTH_SELECTION_ALL),
|
|
+ &oauth2_code);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_string_equal(oauth2_code, OAUTH2_CODE);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
static void test_parse_supp_valgrind_args(void)
|
|
{
|
|
/*
|
|
@@ -261,6 +372,11 @@ int main(int argc, const char *argv[])
|
|
cmocka_unit_test(test_json_format_auth_selection_all),
|
|
cmocka_unit_test(test_json_format_auth_selection_failure),
|
|
cmocka_unit_test(test_generate_json_message_integration),
|
|
+ cmocka_unit_test(test_json_unpack_password_ok),
|
|
+ cmocka_unit_test(test_json_unpack_auth_reply_password),
|
|
+ cmocka_unit_test(test_json_unpack_auth_reply_oauth2),
|
|
+ cmocka_unit_test(test_json_unpack_auth_reply_failure),
|
|
+ cmocka_unit_test(test_json_unpack_oauth2_code),
|
|
};
|
|
|
|
/* Set debug level to invalid value so we can decide if -d 0 was used. */
|
|
diff --git a/src/util/sss_pam_data.h b/src/util/sss_pam_data.h
|
|
index a7efba791..441720e97 100644
|
|
--- a/src/util/sss_pam_data.h
|
|
+++ b/src/util/sss_pam_data.h
|
|
@@ -75,6 +75,8 @@ struct pam_data {
|
|
key_serial_t key_serial;
|
|
#endif
|
|
bool passkey_local_done;
|
|
+ char *json_auth_msg;
|
|
+ char *json_auth_selected;
|
|
};
|
|
|
|
/**
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 470a3d1eecc2ac36c67ffba072bc5fffe7222afa Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Wed, 6 Mar 2024 12:25:55 +0100
|
|
Subject: [PATCH 06/36] Responder: check PAM service file for JSON protocol
|
|
|
|
Implement a function to check whether the PAM service file in use is
|
|
enabled for the JSON procotol. This helps us filter which applications
|
|
are compatible with this protocol.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_json.c | 17 +++++++++++++++
|
|
src/responder/pam/pamsrv_json.h | 13 +++++++++++
|
|
src/tests/cmocka/test_pamsrv_json.c | 34 +++++++++++++++++++++++++++++
|
|
3 files changed, 64 insertions(+)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
index f0ed24f7a..e59f218b2 100644
|
|
--- a/src/responder/pam/pamsrv_json.c
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -575,3 +575,20 @@ done:
|
|
|
|
return ret;
|
|
}
|
|
+
|
|
+bool is_pam_json_enabled(char **json_services,
|
|
+ char *service)
|
|
+{
|
|
+ if (json_services == NULL) {
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ if (strcmp(json_services[0], "-") == 0) {
|
|
+ /* Dash is used to disable the JSON protocol */
|
|
+ DEBUG(SSSDBG_TRACE_FUNC, "Dash - was used as a PAM service name. "
|
|
+ "JSON protocol is disabled.\n");
|
|
+ return false;
|
|
+ }
|
|
+
|
|
+ return string_in_list(service, json_services, true);
|
|
+}
|
|
diff --git a/src/responder/pam/pamsrv_json.h b/src/responder/pam/pamsrv_json.h
|
|
index d861120df..4c8c3aef3 100644
|
|
--- a/src/responder/pam/pamsrv_json.h
|
|
+++ b/src/responder/pam/pamsrv_json.h
|
|
@@ -147,4 +147,17 @@ json_unpack_oauth2_code(TALLOC_CTX *mem_ctx, char *json_auth_msg,
|
|
errno_t
|
|
json_unpack_auth_reply(struct pam_data *pd);
|
|
|
|
+/**
|
|
+ * @brief Check whether the PAM service file in use is enabled for the JSON
|
|
+ * protocol
|
|
+ *
|
|
+ * @param[in] json_services Enabled PAM services for JSON protocol
|
|
+ * @param[in] service PAM service file in use
|
|
+ *
|
|
+ * @return true if the JSON protocol is enabled for the PAM service file,
|
|
+ * false otherwise.
|
|
+ */
|
|
+bool is_pam_json_enabled(char **json_services,
|
|
+ char *service);
|
|
+
|
|
#endif /* __PAMSRV_JSON__H__ */
|
|
diff --git a/src/tests/cmocka/test_pamsrv_json.c b/src/tests/cmocka/test_pamsrv_json.c
|
|
index 6d00d0bd0..3e1b8b0d1 100644
|
|
--- a/src/tests/cmocka/test_pamsrv_json.c
|
|
+++ b/src/tests/cmocka/test_pamsrv_json.c
|
|
@@ -343,6 +343,37 @@ void test_json_unpack_oauth2_code(void **state)
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
+void test_is_pam_json_enabled_service_in_list(void **state)
|
|
+{
|
|
+ char *json_services[] = {discard_const("sshd"), discard_const("su"),
|
|
+ discard_const("gdm-switchable-auth"), NULL};
|
|
+ bool result;
|
|
+
|
|
+ result = is_pam_json_enabled(json_services,
|
|
+ discard_const("gdm-switchable-auth"));
|
|
+ assert_int_equal(result, true);
|
|
+}
|
|
+
|
|
+void test_is_pam_json_enabled_service_not_in_list(void **state)
|
|
+{
|
|
+ char *json_services[] = {discard_const("sshd"), discard_const("su"),
|
|
+ discard_const("gdm-switchable-auth"), NULL};
|
|
+ bool result;
|
|
+
|
|
+ result = is_pam_json_enabled(json_services,
|
|
+ discard_const("sudo"));
|
|
+ assert_int_equal(result, false);
|
|
+}
|
|
+
|
|
+void test_is_pam_json_enabled_null_list(void **state)
|
|
+{
|
|
+ bool result;
|
|
+
|
|
+ result = is_pam_json_enabled(NULL,
|
|
+ discard_const("sudo"));
|
|
+ assert_int_equal(result, false);
|
|
+}
|
|
+
|
|
static void test_parse_supp_valgrind_args(void)
|
|
{
|
|
/*
|
|
@@ -377,6 +408,9 @@ int main(int argc, const char *argv[])
|
|
cmocka_unit_test(test_json_unpack_auth_reply_oauth2),
|
|
cmocka_unit_test(test_json_unpack_auth_reply_failure),
|
|
cmocka_unit_test(test_json_unpack_oauth2_code),
|
|
+ cmocka_unit_test(test_is_pam_json_enabled_service_in_list),
|
|
+ cmocka_unit_test(test_is_pam_json_enabled_service_not_in_list),
|
|
+ cmocka_unit_test(test_is_pam_json_enabled_null_list),
|
|
};
|
|
|
|
/* Set debug level to invalid value so we can decide if -d 0 was used. */
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 9d7998b874eae4e2f543cd4e2d82ee316d19a57a Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Tue, 20 Feb 2024 12:19:24 +0100
|
|
Subject: [PATCH 07/36] Responder: new option `pam_json_services`
|
|
|
|
This new option is used to enable the JSON protocol in the PAM responder
|
|
based on the PAM service file in use.
|
|
|
|
:config: A new option `pam_json_services` is now available to enable
|
|
JSON protocol to communicate the available authentication
|
|
mechanisms.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/confdb/confdb.h | 1 +
|
|
src/config/SSSDConfig/sssdoptions.py | 1 +
|
|
src/config/cfg_rules.ini | 1 +
|
|
src/config/etc/sssd.api.conf | 1 +
|
|
src/man/Makefile.am | 3 +++
|
|
src/man/sssd.conf.5.xml | 23 +++++++++++++++++++++++
|
|
src/responder/pam/pamsrv.c | 24 ++++++++++++++++++++++++
|
|
src/responder/pam/pamsrv.h | 1 +
|
|
8 files changed, 55 insertions(+)
|
|
|
|
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
|
|
index b21b3e0cd..e7772c190 100644
|
|
--- a/src/confdb/confdb.h
|
|
+++ b/src/confdb/confdb.h
|
|
@@ -161,6 +161,7 @@
|
|
#define CONFDB_PAM_PASSKEY_AUTH "pam_passkey_auth"
|
|
#define CONFDB_PAM_PASSKEY_CHILD_TIMEOUT "passkey_child_timeout"
|
|
#define CONFDB_PAM_PASSKEY_DEBUG_LIBFIDO2 "passkey_debug_libfido2"
|
|
+#define CONFDB_PAM_JSON_SERVICES "pam_json_services"
|
|
|
|
/* SUDO */
|
|
#define CONFDB_SUDO_CONF_ENTRY "config/sudo"
|
|
diff --git a/src/config/SSSDConfig/sssdoptions.py b/src/config/SSSDConfig/sssdoptions.py
|
|
index 3a88c1d2f..cf191dc28 100644
|
|
--- a/src/config/SSSDConfig/sssdoptions.py
|
|
+++ b/src/config/SSSDConfig/sssdoptions.py
|
|
@@ -120,6 +120,7 @@ class SSSDOptions(object):
|
|
'pam_passkey_auth': _('Allow passkey device authentication.'),
|
|
'passkey_child_timeout': _('How many seconds will pam_sss wait for passkey_child to finish'),
|
|
'passkey_debug_libfido2': _('Enable debugging in the libfido2 library'),
|
|
+ 'pam_json_services': _('Enable JSON protocol for authentication methods selection.'),
|
|
|
|
# [sudo]
|
|
'sudo_timed': _('Whether to evaluate the time-based attributes in sudo rules'),
|
|
diff --git a/src/config/cfg_rules.ini b/src/config/cfg_rules.ini
|
|
index 92f553bf0..c1167875b 100644
|
|
--- a/src/config/cfg_rules.ini
|
|
+++ b/src/config/cfg_rules.ini
|
|
@@ -148,6 +148,7 @@ option = pam_gssapi_indicators_apply
|
|
option = pam_passkey_auth
|
|
option = passkey_child_timeout
|
|
option = passkey_debug_libfido2
|
|
+option = pam_json_services
|
|
|
|
[rule/allowed_sudo_options]
|
|
validator = ini_allowed_options
|
|
diff --git a/src/config/etc/sssd.api.conf b/src/config/etc/sssd.api.conf
|
|
index b537d72c0..b8960edba 100644
|
|
--- a/src/config/etc/sssd.api.conf
|
|
+++ b/src/config/etc/sssd.api.conf
|
|
@@ -90,6 +90,7 @@ pam_gssapi_indicators_apply = str, None, false
|
|
pam_passkey_auth = bool, None, false
|
|
passkey_child_timeout = int, None, false
|
|
passkey_debug_libfido2 = bool, None, false
|
|
+pam_json_services = str, None, false
|
|
|
|
[sudo]
|
|
# sudo service
|
|
diff --git a/src/man/Makefile.am b/src/man/Makefile.am
|
|
index 475bfbbca..876561289 100644
|
|
--- a/src/man/Makefile.am
|
|
+++ b/src/man/Makefile.am
|
|
@@ -67,6 +67,9 @@ endif
|
|
if BUILD_SAMBA
|
|
SAMBA_CONDS = ;with_samba
|
|
endif
|
|
+if HAVE_GDM_CUSTOM_JSON_PAM_EXTENSION
|
|
+JSON_PAM_CONDS = ;build_json_pam
|
|
+endif
|
|
|
|
|
|
CONDS = with_false$(SUDO_CONDS)$(AUTOFS_CONDS)$(SSH_CONDS)$(PAC_RESPONDER_CONDS)$(IFP_CONDS)$(GPO_CONDS)$(SYSTEMD_CONDS)$(KCM_CONDS)$(STAP_CONDS)$(KCM_RENEWAL_CONDS)$(LOCKFREE_CLIENT_CONDS)$(HAVE_INOTIFY_CONDS)$(SUBID_CONDS)$(PASSKEY_CONDS)$(FILES_PROVIDER_CONDS)$(SSSD_NON_ROOT_USER_CONDS)$(LIBNL_CONDS)$(SAMBA_CONDS)
|
|
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
|
|
index 6a4fea68e..a4829366a 100644
|
|
--- a/src/man/sssd.conf.5.xml
|
|
+++ b/src/man/sssd.conf.5.xml
|
|
@@ -2105,6 +2105,29 @@ pam_gssapi_indicators_apply = SID:S-1-5-12345-23456-34567-4321:pkinit
|
|
</para>
|
|
</listitem>
|
|
</varlistentry>
|
|
+ <varlistentry condition="build_json_pam">
|
|
+ <term>pam_json_services (string)</term>
|
|
+ <listitem>
|
|
+ <para>
|
|
+ Comma separated list of PAM services which can
|
|
+ handle the JSON protocol for selecting
|
|
+ authentication mechanisms
|
|
+ </para>
|
|
+ <para>
|
|
+ To disable JSON protocol, set this option
|
|
+ to <quote>-</quote> (dash).
|
|
+ </para>
|
|
+ <para>
|
|
+ Example:
|
|
+ <programlisting>
|
|
+pam_json_services = gdm-switchable-auth
|
|
+ </programlisting>
|
|
+ </para>
|
|
+ <para>
|
|
+ Default: - (JSON protocol is disabled)
|
|
+ </para>
|
|
+ </listitem>
|
|
+ </varlistentry>
|
|
</variablelist>
|
|
</refsect2>
|
|
|
|
diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c
|
|
index 95536fee7..291bcdf23 100644
|
|
--- a/src/responder/pam/pamsrv.c
|
|
+++ b/src/responder/pam/pamsrv.c
|
|
@@ -435,6 +435,30 @@ static int pam_process_init(TALLOC_CTX *mem_ctx,
|
|
#endif
|
|
}
|
|
|
|
+ /* Check if JSON authentication selection method is enabled for any PAM
|
|
+ * services
|
|
+ */
|
|
+ ret = confdb_get_string(pctx->rctx->cdb, pctx, CONFDB_PAM_CONF_ENTRY,
|
|
+ CONFDB_PAM_JSON_SERVICES, "-", &tmpstr);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_FATAL_FAILURE,
|
|
+ "Failed to determine json services.\n");
|
|
+ goto done;
|
|
+ }
|
|
+ DEBUG(SSSDBG_TRACE_INTERNAL, "Found value [%s] for option [%s].\n", tmpstr,
|
|
+ CONFDB_PAM_JSON_SERVICES);
|
|
+
|
|
+ if (tmpstr != NULL) {
|
|
+ ret = split_on_separator(pctx, tmpstr, ',', true, true,
|
|
+ &pctx->json_services, NULL);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_MINOR_FAILURE,
|
|
+ "split_on_separator() failed [%d]: [%s].\n", ret,
|
|
+ sss_strerror(ret));
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
/* The responder is initialized. Now tell it to the monitor. */
|
|
ret = sss_monitor_service_init(rctx, rctx->ev, SSS_BUS_PAM,
|
|
SSS_PAM_SBUS_SERVICE_NAME,
|
|
diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
|
|
index a2567b068..627b6c147 100644
|
|
--- a/src/responder/pam/pamsrv.h
|
|
+++ b/src/responder/pam/pamsrv.h
|
|
@@ -77,6 +77,7 @@ struct pam_ctx {
|
|
bool gssapi_check_upn;
|
|
bool passkey_auth;
|
|
struct pam_passkey_table_data *pk_table_data;
|
|
+ char **json_services;
|
|
};
|
|
|
|
struct pam_auth_req {
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From a1777b281f96be886c02a1b08948fba350ebaae7 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Mon, 22 Jan 2024 10:30:16 +0100
|
|
Subject: [PATCH 08/36] Responder: call JSON message generation
|
|
|
|
Call JSON message generation function and fill the data structure
|
|
containing the response_data linked list.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
Makefile.am | 4 ++++
|
|
src/responder/pam/pamsrv_cmd.c | 14 ++++++++++++++
|
|
2 files changed, 18 insertions(+)
|
|
|
|
diff --git a/Makefile.am b/Makefile.am
|
|
index 290a94890..e052c3079 100644
|
|
--- a/Makefile.am
|
|
+++ b/Makefile.am
|
|
@@ -1566,6 +1566,7 @@ endif
|
|
sssd_pam_SOURCES = \
|
|
src/responder/pam/pamsrv.c \
|
|
src/responder/pam/pamsrv_cmd.c \
|
|
+ src/responder/pam/pamsrv_json.c \
|
|
src/responder/pam/pamsrv_p11.c \
|
|
src/responder/pam/pamsrv_dp.c \
|
|
src/responder/pam/pamsrv_gssapi.c \
|
|
@@ -1594,6 +1595,7 @@ sssd_pam_LDADD = \
|
|
$(PAM_LIBS) \
|
|
$(SYSTEMD_DAEMON_LIBS) \
|
|
$(GSSAPI_KRB5_LIBS) \
|
|
+ $(JANSSON_LIBS) \
|
|
libsss_certmap.la \
|
|
$(SSSD_INTERNAL_LTLIBS) \
|
|
libsss_iface.la \
|
|
@@ -2649,6 +2651,7 @@ pam_srv_tests_SOURCES = \
|
|
src/tests/cmocka/common_utils.c \
|
|
src/sss_client/pam_message.c \
|
|
src/responder/pam/pamsrv_cmd.c \
|
|
+ src/responder/pam/pamsrv_json.c \
|
|
src/responder/pam/pamsrv_p11.c \
|
|
src/responder/pam/pamsrv_gssapi.c \
|
|
src/responder/pam/pam_helpers.c \
|
|
@@ -2684,6 +2687,7 @@ pam_srv_tests_LDADD = \
|
|
$(SSSD_INTERNAL_LTLIBS) \
|
|
$(SYSTEMD_DAEMON_LIBS) \
|
|
$(GSSAPI_KRB5_LIBS) \
|
|
+ $(JANSSON_LIBS) \
|
|
libsss_test_common.la \
|
|
libsss_idmap.la \
|
|
libsss_certmap.la \
|
|
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
|
|
index 3bd19f243..0daf4d584 100644
|
|
--- a/src/responder/pam/pamsrv_cmd.c
|
|
+++ b/src/responder/pam/pamsrv_cmd.c
|
|
@@ -37,6 +37,7 @@
|
|
#include "responder/common/negcache.h"
|
|
#include "providers/data_provider.h"
|
|
#include "responder/pam/pamsrv.h"
|
|
+#include "responder/pam/pamsrv_json.h"
|
|
#include "responder/pam/pamsrv_passkey.h"
|
|
#include "responder/pam/pam_helpers.h"
|
|
#include "responder/common/cache_req/cache_req.h"
|
|
@@ -1535,6 +1536,19 @@ void pam_reply(struct pam_auth_req *preq)
|
|
return;
|
|
}
|
|
#endif /* BUILD_PASSKEY */
|
|
+
|
|
+#ifdef HAVE_GDM_CUSTOM_JSON_PAM_EXTENSION
|
|
+ if (is_pam_json_enabled(pctx->json_services,
|
|
+ pd->service)) {
|
|
+ ret = generate_json_auth_message(pctx->rctx->cdb, pc_list, pd);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "failed to generate JSON message.\n");
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+#endif /* HAVE_GDM_CUSTOM_JSON_PAM_EXTENSION */
|
|
+ pc_list_free(pc_list);
|
|
}
|
|
|
|
/*
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From c85859645da2374789704b8c32474ef73d4c27d4 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Mon, 22 Jan 2024 10:32:48 +0100
|
|
Subject: [PATCH 09/36] SSS_CLIENT: forward available auth JSON message
|
|
|
|
Forward the available authentication mechanisms and their associated
|
|
data message to the GUI login using a PAM conversation. Then, obtain the
|
|
reply and forward it to the responder, so that it can parse it.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Signed-off-by: Ray Strode <halfline@redhat.com>
|
|
---
|
|
src/external/pam.m4 | 7 +++
|
|
src/sss_client/pam_message.c | 8 ++++
|
|
src/sss_client/pam_message.h | 4 ++
|
|
src/sss_client/pam_sss.c | 93 ++++++++++++++++++++++++++++++++++++
|
|
src/sss_client/sss_cli.h | 2 +
|
|
5 files changed, 114 insertions(+)
|
|
|
|
diff --git a/src/external/pam.m4 b/src/external/pam.m4
|
|
index 0dc7f19d0..844a0a711 100644
|
|
--- a/src/external/pam.m4
|
|
+++ b/src/external/pam.m4
|
|
@@ -39,3 +39,10 @@ AC_SUBST(GDM_PAM_EXTENSIONS_CFLAGS)
|
|
AS_IF([test x"$found_gdm_pam_extensions" = xyes],
|
|
[AC_DEFINE_UNQUOTED(HAVE_GDM_PAM_EXTENSIONS, 1,
|
|
[Build with gdm-pam-extensions support])])
|
|
+
|
|
+AS_IF([test x"$found_gdm_pam_extensions" = xyes],
|
|
+ [AC_CHECK_HEADER([gdm/gdm-custom-json-pam-extension.h],
|
|
+ [AC_DEFINE_UNQUOTED(HAVE_GDM_CUSTOM_JSON_PAM_EXTENSION, 1,
|
|
+ [Build with gdm-custom-json-pam-extension support])])])
|
|
+AM_CONDITIONAL([HAVE_GDM_CUSTOM_JSON_PAM_EXTENSION],
|
|
+ [test x"$found_gdm_pam_extensions" = xyes])
|
|
diff --git a/src/sss_client/pam_message.c b/src/sss_client/pam_message.c
|
|
index e3a09f501..e98192c11 100644
|
|
--- a/src/sss_client/pam_message.c
|
|
+++ b/src/sss_client/pam_message.c
|
|
@@ -128,6 +128,10 @@ int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer)
|
|
len += *pi->requested_domains != '\0' ?
|
|
2*sizeof(uint32_t) + pi->requested_domains_size : 0;
|
|
len += 3*sizeof(uint32_t); /* flags */
|
|
+ len += *pi->json_auth_msg != '\0' ?
|
|
+ 2*sizeof(uint32_t) + pi->json_auth_msg_size : 0;
|
|
+ len += *pi->json_auth_selected != '\0' ?
|
|
+ 2*sizeof(uint32_t) + pi->json_auth_selected_size : 0;
|
|
|
|
/* optional child_pid */
|
|
if(pi->child_pid > 0) {
|
|
@@ -178,6 +182,10 @@ int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer)
|
|
|
|
rp += add_uint32_t_item(SSS_PAM_ITEM_FLAGS, (uint32_t) pi->flags,
|
|
&buf[rp]);
|
|
+ rp += add_string_item(SSS_PAM_ITEM_JSON_AUTH_INFO, pi->json_auth_msg,
|
|
+ pi->json_auth_msg_size, &buf[rp]);
|
|
+ rp += add_string_item(SSS_PAM_ITEM_JSON_AUTH_SELECTED, pi->json_auth_selected,
|
|
+ pi->json_auth_selected_size, &buf[rp]);
|
|
|
|
SAFEALIGN_SETMEM_UINT32(buf + rp, SSS_END_OF_PAM_REQUEST, &rp);
|
|
|
|
diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h
|
|
index d6fb254f2..c145b8a51 100644
|
|
--- a/src/sss_client/pam_message.h
|
|
+++ b/src/sss_client/pam_message.h
|
|
@@ -66,6 +66,10 @@ struct pam_items {
|
|
char *first_factor;
|
|
char *passkey_key;
|
|
char *passkey_prompt_pin;
|
|
+ char *json_auth_msg;
|
|
+ size_t json_auth_msg_size;
|
|
+ const char *json_auth_selected;
|
|
+ size_t json_auth_selected_size;
|
|
bool password_prompting;
|
|
|
|
bool user_name_hint;
|
|
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
|
|
index 969ff366d..ca400b03b 100644
|
|
--- a/src/sss_client/pam_sss.c
|
|
+++ b/src/sss_client/pam_sss.c
|
|
@@ -41,6 +41,10 @@
|
|
#include <gdm/gdm-pam-extensions.h>
|
|
#endif
|
|
|
|
+#ifdef HAVE_GDM_CUSTOM_JSON_PAM_EXTENSION
|
|
+#include <gdm/gdm-custom-json-pam-extension.h>
|
|
+#endif
|
|
+
|
|
#include "sss_pam_compat.h"
|
|
#include "sss_pam_macros.h"
|
|
|
|
@@ -1350,6 +1354,19 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
|
|
break;
|
|
}
|
|
break;
|
|
+ case SSS_PAM_JSON_AUTH_INFO:
|
|
+ if (buf[p + (len - 1)] != '\0') {
|
|
+ D(("json auth info does not end with \\0."));
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ free(pi->json_auth_msg);
|
|
+ pi->json_auth_msg = strdup((char *) &buf[p]);
|
|
+ if (pi->json_auth_msg == NULL) {
|
|
+ D(("strdup failed"));
|
|
+ break;
|
|
+ }
|
|
+ break;
|
|
default:
|
|
D(("Unknown response type [%d]", type));
|
|
}
|
|
@@ -1464,6 +1481,10 @@ static int get_pam_items(pam_handle_t *pamh, uint32_t flags,
|
|
pi->pc = NULL;
|
|
|
|
pi->flags = flags;
|
|
+ if (pi->json_auth_msg == NULL) pi->json_auth_msg = strdup("");
|
|
+ pi->json_auth_msg_size = strlen(pi->json_auth_msg) + 1;
|
|
+ if (pi->json_auth_selected == NULL) pi->json_auth_selected = "";
|
|
+ pi->json_auth_selected_size = strlen(pi->json_auth_selected) + 1;
|
|
|
|
return PAM_SUCCESS;
|
|
}
|
|
@@ -2008,6 +2029,65 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+static int auth_selection_conversation_gdm(pam_handle_t *pamh,
|
|
+ struct pam_items *pi)
|
|
+{
|
|
+#ifdef HAVE_GDM_CUSTOM_JSON_PAM_EXTENSION
|
|
+ const struct pam_conv *conv;
|
|
+ GdmPamExtensionJSONProtocol *request = NULL;
|
|
+ GdmPamExtensionJSONProtocol *response = NULL;
|
|
+ struct pam_message prompt_message;
|
|
+ const struct pam_message *prompt_messages[1];
|
|
+ struct pam_response *reply = NULL;
|
|
+ int ret;
|
|
+
|
|
+ ret = pam_get_item(pamh, PAM_CONV, (const void **)&conv);
|
|
+ if (ret != PAM_SUCCESS) {
|
|
+ ret = EIO;
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ request = calloc(1, GDM_PAM_EXTENSION_CUSTOM_JSON_SIZE);
|
|
+ if (request == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ GDM_PAM_EXTENSION_CUSTOM_JSON_REQUEST_INIT(request, "auth-mechanisms", 1,
|
|
+ pi->json_auth_msg);
|
|
+ GDM_PAM_EXTENSION_MESSAGE_TO_BINARY_PROMPT_MESSAGE(request,
|
|
+ &prompt_message);
|
|
+ prompt_messages[0] = &prompt_message;
|
|
+
|
|
+ ret = conv->conv(1, prompt_messages, &reply, conv->appdata_ptr);
|
|
+ if (ret != PAM_SUCCESS) {
|
|
+ ret = EIO;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ response = GDM_PAM_EXTENSION_REPLY_TO_CUSTOM_JSON_RESPONSE(reply);
|
|
+ if (response->json == NULL) {
|
|
+ ret = EIO;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ pi->json_auth_msg_size = strlen(pi->json_auth_msg)+1;
|
|
+ pi->json_auth_selected = strdup(response->json);
|
|
+ pi->json_auth_selected_size = strlen(response->json)+1;
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ if (request != NULL) {
|
|
+ free(request);
|
|
+ }
|
|
+ free(response);
|
|
+
|
|
+ return ret;
|
|
+#else
|
|
+ return ENOTSUP;
|
|
+#endif /* HAVE_GDM_CUSTOM_JSON_PAM_EXTENSION */
|
|
+}
|
|
+
|
|
#define SC_PROMPT_FMT "PIN for %s: "
|
|
|
|
#ifndef discard_const
|
|
@@ -3014,6 +3094,19 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
|
|
* errors can be ignored here.
|
|
*/
|
|
}
|
|
+
|
|
+ if (pi.json_auth_msg != NULL
|
|
+ && strcmp(pi.json_auth_msg, "") != 0) {
|
|
+ ret = auth_selection_conversation_gdm(pamh, &pi);
|
|
+ if (ret == EOK) {
|
|
+ break;
|
|
+ } else if (ret == ENOTSUP) {
|
|
+ D(("gdm-custom-json-pam-extensions not supported."));
|
|
+ } else {
|
|
+ D(("auth_selection_conversation_gdm failed."));
|
|
+ return ret;
|
|
+ }
|
|
+ }
|
|
}
|
|
|
|
if (flags & PAM_CLI_FLAGS_TRY_CERT_AUTH
|
|
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
|
|
index b99c145c4..0b00c4452 100644
|
|
--- a/src/sss_client/sss_cli.h
|
|
+++ b/src/sss_client/sss_cli.h
|
|
@@ -415,6 +415,8 @@ enum pam_item_type {
|
|
SSS_PAM_ITEM_CHILD_PID,
|
|
SSS_PAM_ITEM_REQUESTED_DOMAINS,
|
|
SSS_PAM_ITEM_FLAGS,
|
|
+ SSS_PAM_ITEM_JSON_AUTH_INFO,
|
|
+ SSS_PAM_ITEM_JSON_AUTH_SELECTED,
|
|
};
|
|
|
|
#define PAM_CLI_FLAGS_USE_FIRST_PASS (1 << 0)
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From dbfb5185be2fb8750a8642bb9ae546ddce95ccd7 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Tue, 30 Jan 2024 11:32:25 +0100
|
|
Subject: [PATCH 10/36] Responder: parse GUI reply
|
|
|
|
Parse GUI reply and set the appropriate data in `sss_auth_token`
|
|
structure.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_cmd.c | 39 ++++++++++++++++++++++++++++++++++
|
|
1 file changed, 39 insertions(+)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
|
|
index 0daf4d584..6b45c47c7 100644
|
|
--- a/src/responder/pam/pamsrv_cmd.c
|
|
+++ b/src/responder/pam/pamsrv_cmd.c
|
|
@@ -289,6 +289,8 @@ static int pam_parse_in_data_v2(struct pam_data *pd,
|
|
uint32_t start;
|
|
uint32_t terminator;
|
|
char *requested_domains;
|
|
+ bool authtok_set = false;
|
|
+ bool json_auth_set = false;
|
|
|
|
if (blen < 4*sizeof(uint32_t)+2) {
|
|
DEBUG(SSSDBG_CRIT_FAILURE, "Received data is invalid.\n");
|
|
@@ -366,6 +368,14 @@ static int pam_parse_in_data_v2(struct pam_data *pd,
|
|
if (ret != EOK) return ret;
|
|
break;
|
|
case SSS_PAM_ITEM_AUTHTOK:
|
|
+ if (json_auth_set) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "Failing because SSS_PAM_ITEM_AUTHTOK and " \
|
|
+ "SSS_PAM_ITEM_JSON_AUTH_SELECTED are mutually " \
|
|
+ "exclusive.\n");
|
|
+ return EPERM;
|
|
+ }
|
|
+ authtok_set = true;
|
|
ret = extract_authtok_v2(pd->authtok,
|
|
size, body, blen, &c);
|
|
if (ret != EOK) return ret;
|
|
@@ -380,6 +390,24 @@ static int pam_parse_in_data_v2(struct pam_data *pd,
|
|
body, blen, &c);
|
|
if (ret != EOK) return ret;
|
|
break;
|
|
+ case SSS_PAM_ITEM_JSON_AUTH_INFO:
|
|
+ ret = extract_string(&pd->json_auth_msg, size, body,
|
|
+ blen, &c);
|
|
+ if (ret != EOK) return ret;
|
|
+ break;
|
|
+ case SSS_PAM_ITEM_JSON_AUTH_SELECTED:
|
|
+ if (authtok_set) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "Failing because SSS_PAM_ITEM_AUTHTOK and " \
|
|
+ "SSS_PAM_ITEM_JSON_AUTH_SELECTED are mutually " \
|
|
+ "exclusive.\n");
|
|
+ return EPERM;
|
|
+ }
|
|
+ json_auth_set = true;
|
|
+ ret = extract_string(&pd->json_auth_selected, size, body,
|
|
+ blen, &c);
|
|
+ if (ret != EOK) return ret;
|
|
+ break;
|
|
default:
|
|
DEBUG(SSSDBG_CRIT_FAILURE,
|
|
"Ignoring unknown data type [%d].\n", type);
|
|
@@ -1737,6 +1765,17 @@ static errno_t pam_forwarder_parse_data(struct cli_ctx *cctx, struct pam_data *p
|
|
goto done;
|
|
}
|
|
|
|
+#ifdef HAVE_GDM_CUSTOM_JSON_PAM_EXTENSION
|
|
+ if (pd->cmd == SSS_PAM_AUTHENTICATE
|
|
+ && pd->json_auth_selected != NULL) {
|
|
+ ret = json_unpack_auth_reply(pd);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_unpack_auth_reply failed.\n");
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+#endif /* HAVE_GDM_CUSTOM_JSON_PAM_EXTENSION */
|
|
+
|
|
if (pd->logon_name != NULL) {
|
|
ret = sss_parse_name_for_domains(pd, cctx->rctx->domains,
|
|
cctx->rctx->default_domain,
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 970e1df5c8099aeb00f7f2bfa5238024a554d692 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Tue, 5 Mar 2024 15:45:40 +0100
|
|
Subject: [PATCH 11/36] Test: adapt test_pam_srv to JSON message
|
|
|
|
Include JSON message where applies.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_json.c | 6 ++++--
|
|
src/tests/cmocka/test_pam_srv.c | 6 ++++++
|
|
src/tests/cmocka/test_pamsrv_json.c | 3 ++-
|
|
3 files changed, 12 insertions(+), 3 deletions(-)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
index e59f218b2..98bc3cbaa 100644
|
|
--- a/src/responder/pam/pamsrv_json.c
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -192,9 +192,10 @@ json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
}
|
|
|
|
if (password_auth) {
|
|
- json_pass = json_pack("{s:s,s:s,s:s}",
|
|
+ json_pass = json_pack("{s:s,s:s,s:b,s:s}",
|
|
"name", "Password",
|
|
"role", "password",
|
|
+ "selectable", true,
|
|
"prompt", password_prompt);
|
|
if (json_pass == NULL) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "json_pack failed.\n");
|
|
@@ -212,9 +213,10 @@ json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
}
|
|
|
|
if (oauth2_auth) {
|
|
- json_oauth2 = json_pack("{s:s,s:s,s:s,s:s,s:s,s:s,s:i}",
|
|
+ json_oauth2 = json_pack("{s:s,s:s,s:b,s:s,s:s,s:s,s:s,s:i}",
|
|
"name", "Web Login",
|
|
"role", "eidp",
|
|
+ "selectable", true,
|
|
"init_prompt", oauth2_init_prompt,
|
|
"link_prompt", oauth2_link_prompt,
|
|
"uri", uri,
|
|
diff --git a/src/tests/cmocka/test_pam_srv.c b/src/tests/cmocka/test_pam_srv.c
|
|
index 35ebfbafa..12ae8bea7 100644
|
|
--- a/src/tests/cmocka/test_pam_srv.c
|
|
+++ b/src/tests/cmocka/test_pam_srv.c
|
|
@@ -641,6 +641,8 @@ static void mock_input_pam_passkey(TALLOC_CTX *mem_ctx,
|
|
pi.pam_rhost_size = strlen(pi.pam_rhost) + 1;
|
|
pi.requested_domains = "";
|
|
pi.cli_pid = 12345;
|
|
+ pi.json_auth_msg = discard_const("");
|
|
+ pi.json_auth_selected = "";
|
|
|
|
ret = pack_message_v3(&pi, &buf_size, &m_buf);
|
|
assert_int_equal(ret, 0);
|
|
@@ -736,6 +738,8 @@ static void mock_input_pam_ex(TALLOC_CTX *mem_ctx,
|
|
pi.pam_rhost_size = strlen(pi.pam_rhost) + 1;
|
|
pi.requested_domains = "";
|
|
pi.cli_pid = 12345;
|
|
+ pi.json_auth_msg = discard_const("");
|
|
+ pi.json_auth_selected = "";
|
|
|
|
ret = pack_message_v3(&pi, &buf_size, &m_buf);
|
|
assert_int_equal(ret, 0);
|
|
@@ -817,6 +821,8 @@ static void mock_input_pam_cert(TALLOC_CTX *mem_ctx, const char *name,
|
|
pi.pam_rhost_size = strlen(pi.pam_rhost) + 1;
|
|
pi.requested_domains = "";
|
|
pi.cli_pid = 12345;
|
|
+ pi.json_auth_msg = discard_const("");
|
|
+ pi.json_auth_selected = "";
|
|
|
|
ret = pack_message_v3(&pi, &buf_size, &m_buf);
|
|
free(pi.pam_authtok);
|
|
diff --git a/src/tests/cmocka/test_pamsrv_json.c b/src/tests/cmocka/test_pamsrv_json.c
|
|
index 3e1b8b0d1..7322c533d 100644
|
|
--- a/src/tests/cmocka/test_pamsrv_json.c
|
|
+++ b/src/tests/cmocka/test_pamsrv_json.c
|
|
@@ -36,9 +36,10 @@
|
|
|
|
#define BASIC_PASSWORD "\"password\": {" \
|
|
"\"name\": \"Password\", \"role\": \"password\", " \
|
|
- "\"prompt\": \"Password\"}"
|
|
+ "\"selectable\": true, \"prompt\": \"Password\"}"
|
|
#define BASIC_OAUTH2 "\"eidp\": {" \
|
|
"\"name\": \"Web Login\", \"role\": \"eidp\", " \
|
|
+ "\"selectable\": true, " \
|
|
"\"init_prompt\": \"" OAUTH2_INIT_PROMPT "\", " \
|
|
"\"link_prompt\": \"" OAUTH2_LINK_PROMPT "\", " \
|
|
"\"uri\": \"short.url.com/tmp\", \"code\": \"1234-5678\", " \
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 6540db69463791cdc54ed5e89ec98701c51f25ae Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Thu, 13 Jun 2024 14:18:46 +0200
|
|
Subject: [PATCH 12/36] Responder: check return value for json_string()
|
|
|
|
It returns NULL on error, but this wasn't checked.
|
|
|
|
Fixes: ceeffa9e1 ("Responder: generate JSON message for GUI")
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_json.c | 10 ++++++++++
|
|
1 file changed, 10 insertions(+)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
index 98bc3cbaa..898cd4cf5 100644
|
|
--- a/src/responder/pam/pamsrv_json.c
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -264,6 +264,11 @@ json_format_priority(bool password_auth, bool oauth2_auth, json_t **_priority)
|
|
|
|
if (oauth2_auth) {
|
|
json_priority = json_string("eidp");
|
|
+ if (json_priority == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_string failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
ret = json_array_append_new(root, json_priority);
|
|
if (ret == -1) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "json_array_append failed.\n");
|
|
@@ -275,6 +280,11 @@ json_format_priority(bool password_auth, bool oauth2_auth, json_t **_priority)
|
|
|
|
if (password_auth) {
|
|
json_priority = json_string("password");
|
|
+ if (json_priority == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_string failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
ret = json_array_append_new(root, json_priority);
|
|
if (ret == -1) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "json_array_append failed.\n");
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From b1307326efaa950de011c7fd48775245a2264647 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Mon, 22 Sep 2025 10:19:51 +0200
|
|
Subject: [PATCH 13/36] Responder: update JSON message format
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_json.c | 18 ++++++++----------
|
|
src/tests/cmocka/test_pamsrv_json.c | 19 +++++++++----------
|
|
2 files changed, 17 insertions(+), 20 deletions(-)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
index 898cd4cf5..a434e660a 100644
|
|
--- a/src/responder/pam/pamsrv_json.c
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -192,10 +192,9 @@ json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
}
|
|
|
|
if (password_auth) {
|
|
- json_pass = json_pack("{s:s,s:s,s:b,s:s}",
|
|
+ json_pass = json_pack("{s:s,s:s,s:s}",
|
|
"name", "Password",
|
|
"role", "password",
|
|
- "selectable", true,
|
|
"prompt", password_prompt);
|
|
if (json_pass == NULL) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "json_pack failed.\n");
|
|
@@ -213,12 +212,11 @@ json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
}
|
|
|
|
if (oauth2_auth) {
|
|
- json_oauth2 = json_pack("{s:s,s:s,s:b,s:s,s:s,s:s,s:s,s:i}",
|
|
+ json_oauth2 = json_pack("{s:s,s:s,s:s,s:s,s:s,s:s,s:i}",
|
|
"name", "Web Login",
|
|
"role", "eidp",
|
|
- "selectable", true,
|
|
- "init_prompt", oauth2_init_prompt,
|
|
- "link_prompt", oauth2_link_prompt,
|
|
+ "initPrompt", oauth2_init_prompt,
|
|
+ "linkPrompt", oauth2_link_prompt,
|
|
"uri", uri,
|
|
"code", code,
|
|
"timeout", 300);
|
|
@@ -333,7 +331,7 @@ json_format_auth_selection(TALLOC_CTX *mem_ctx,
|
|
}
|
|
|
|
root = json_pack("{s:{s:o,s:o}}",
|
|
- "auth-selection",
|
|
+ "authSelection",
|
|
"mechanisms", json_mech,
|
|
"priority", json_priority);
|
|
if (root == NULL) {
|
|
@@ -461,7 +459,7 @@ json_unpack_oauth2_code(TALLOC_CTX *mem_ctx, char *json_auth_msg,
|
|
}
|
|
|
|
ret = json_unpack(jroot, "{s:{s:o,s:o}}",
|
|
- "auth-selection",
|
|
+ "authSelection",
|
|
"mechanisms", &json_mechs,
|
|
"priority", &json_priority);
|
|
if (ret != 0) {
|
|
@@ -524,9 +522,9 @@ json_unpack_auth_reply(struct pam_data *pd)
|
|
goto done;
|
|
}
|
|
|
|
- ret = json_unpack(jroot, "{s:o}", "auth-selection", &jauth_selection);
|
|
+ ret = json_unpack(jroot, "{s:o}", "authSelection", &jauth_selection);
|
|
if (ret != 0) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE, "json_unpack for auth-selection failed.\n");
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "json_unpack for authSelection failed.\n");
|
|
ret = EINVAL;
|
|
goto done;
|
|
}
|
|
diff --git a/src/tests/cmocka/test_pamsrv_json.c b/src/tests/cmocka/test_pamsrv_json.c
|
|
index 7322c533d..fcc7928f6 100644
|
|
--- a/src/tests/cmocka/test_pamsrv_json.c
|
|
+++ b/src/tests/cmocka/test_pamsrv_json.c
|
|
@@ -36,35 +36,34 @@
|
|
|
|
#define BASIC_PASSWORD "\"password\": {" \
|
|
"\"name\": \"Password\", \"role\": \"password\", " \
|
|
- "\"selectable\": true, \"prompt\": \"Password\"}"
|
|
+ "\"prompt\": \"Password\"}"
|
|
#define BASIC_OAUTH2 "\"eidp\": {" \
|
|
"\"name\": \"Web Login\", \"role\": \"eidp\", " \
|
|
- "\"selectable\": true, " \
|
|
- "\"init_prompt\": \"" OAUTH2_INIT_PROMPT "\", " \
|
|
- "\"link_prompt\": \"" OAUTH2_LINK_PROMPT "\", " \
|
|
+ "\"initPrompt\": \"" OAUTH2_INIT_PROMPT "\", " \
|
|
+ "\"linkPrompt\": \"" OAUTH2_LINK_PROMPT "\", " \
|
|
"\"uri\": \"short.url.com/tmp\", \"code\": \"1234-5678\", " \
|
|
"\"timeout\": 300}"
|
|
#define MECHANISMS_PASSWORD "{" BASIC_PASSWORD "}"
|
|
#define MECHANISMS_OAUTH2 "{" BASIC_OAUTH2 "}"
|
|
#define PRIORITY_ALL "[\"eidp\", \"password\"]"
|
|
-#define AUTH_SELECTION_PASSWORD "{\"auth-selection\": {\"mechanisms\": " \
|
|
+#define AUTH_SELECTION_PASSWORD "{\"authSelection\": {\"mechanisms\": " \
|
|
MECHANISMS_PASSWORD ", " \
|
|
"\"priority\": [\"password\"]}}"
|
|
-#define AUTH_SELECTION_OAUTH2 "{\"auth-selection\": {\"mechanisms\": " \
|
|
+#define AUTH_SELECTION_OAUTH2 "{\"authSelection\": {\"mechanisms\": " \
|
|
MECHANISMS_OAUTH2 ", " \
|
|
"\"priority\": [\"eidp\"]}}"
|
|
-#define AUTH_SELECTION_ALL "{\"auth-selection\": {\"mechanisms\": {" \
|
|
+#define AUTH_SELECTION_ALL "{\"authSelection\": {\"mechanisms\": {" \
|
|
BASIC_PASSWORD ", " \
|
|
BASIC_OAUTH2 "}, " \
|
|
"\"priority\": " PRIORITY_ALL "}}"
|
|
|
|
#define PASSWORD_CONTENT "{\"password\": \"ThePassword\"}"
|
|
-#define AUTH_MECH_REPLY_PASSWORD "{\"auth-selection\": {" \
|
|
+#define AUTH_MECH_REPLY_PASSWORD "{\"authSelection\": {" \
|
|
"\"status\": \"Ok\", \"password\": " \
|
|
PASSWORD_CONTENT "}}"
|
|
-#define AUTH_MECH_REPLY_OAUTH2 "{\"auth-selection\": {" \
|
|
+#define AUTH_MECH_REPLY_OAUTH2 "{\"authSelection\": {" \
|
|
"\"status\": \"Ok\", \"eidp\": {}}}"
|
|
-#define AUTH_MECH_ERRONEOUS "{\"auth-selection\": {" \
|
|
+#define AUTH_MECH_ERRONEOUS "{\"authSelection\": {" \
|
|
"\"status\": \"Ok\", \"lololo\": {}}}"
|
|
|
|
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From c1d9b9d10e266e4046c6cfc828a446bcd2f7c3b4 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Tue, 4 Jun 2024 10:51:36 +0200
|
|
Subject: [PATCH 14/36] sss_client: modify smartcard in prompt_config structure
|
|
|
|
Integration with GDM requests two prompts for smartcard so modifying the
|
|
prompt_config structure. In addition, implement all the functions needed
|
|
to manipulate the structure for these new prompts. Finally, add
|
|
unit-tests for the new functions.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/sss_client/pam_sss.c | 2 +-
|
|
src/sss_client/pam_sss_prompt_config.c | 147 +++++++++++++++++++++++--
|
|
src/sss_client/sss_cli.h | 6 +-
|
|
src/tests/cmocka/test_prompt_config.c | 45 +++++++-
|
|
4 files changed, 181 insertions(+), 19 deletions(-)
|
|
|
|
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
|
|
index ca400b03b..ba25959d5 100644
|
|
--- a/src/sss_client/pam_sss.c
|
|
+++ b/src/sss_client/pam_sss.c
|
|
@@ -2604,7 +2604,7 @@ static int prompt_by_config(pam_handle_t *pamh, struct pam_items *pi)
|
|
pc_get_passkey_inter_prompt(pi->pc[c]),
|
|
pc_get_passkey_touch_prompt(pi->pc[c]));
|
|
break;
|
|
- case PC_TYPE_SC_PIN:
|
|
+ case PC_TYPE_SMARTCARD:
|
|
ret = prompt_sc_pin(pamh, pi);
|
|
/* Todo: add extra string option */
|
|
break;
|
|
diff --git a/src/sss_client/pam_sss_prompt_config.c b/src/sss_client/pam_sss_prompt_config.c
|
|
index 891fcd60c..caf308c82 100644
|
|
--- a/src/sss_client/pam_sss_prompt_config.c
|
|
+++ b/src/sss_client/pam_sss_prompt_config.c
|
|
@@ -45,8 +45,9 @@ struct prompt_config_passkey {
|
|
char *prompt_touch;
|
|
};
|
|
|
|
-struct prompt_config_sc_pin {
|
|
- char *prompt; /* Currently not used */
|
|
+struct prompt_config_smartcard {
|
|
+ char *prompt_init;
|
|
+ char *prompt_pin;
|
|
};
|
|
|
|
struct prompt_config_eidp {
|
|
@@ -61,7 +62,7 @@ struct prompt_config {
|
|
struct prompt_config_2fa two_fa;
|
|
struct prompt_config_2fa_single two_fa_single;
|
|
struct prompt_config_passkey passkey;
|
|
- struct prompt_config_sc_pin sc_pin;
|
|
+ struct prompt_config_smartcard smartcard;
|
|
struct prompt_config_eidp eidp;
|
|
} data;
|
|
};
|
|
@@ -138,6 +139,22 @@ const char *pc_get_eidp_link_prompt(struct prompt_config *pc)
|
|
return NULL;
|
|
}
|
|
|
|
+const char *pc_get_smartcard_init_prompt(struct prompt_config *pc)
|
|
+{
|
|
+ if (pc != NULL && (pc_get_type(pc) == PC_TYPE_SMARTCARD)) {
|
|
+ return pc->data.smartcard.prompt_init;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+const char *pc_get_smartcard_pin_prompt(struct prompt_config *pc)
|
|
+{
|
|
+ if (pc != NULL && (pc_get_type(pc) == PC_TYPE_SMARTCARD)) {
|
|
+ return pc->data.smartcard.prompt_pin;
|
|
+ }
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
static void pc_free_passkey(struct prompt_config *pc)
|
|
{
|
|
if (pc != NULL && pc_get_type(pc) == PC_TYPE_PASSKEY) {
|
|
@@ -178,11 +195,13 @@ static void pc_free_2fa_single(struct prompt_config *pc)
|
|
return;
|
|
}
|
|
|
|
-static void pc_free_sc_pin(struct prompt_config *pc)
|
|
+static void pc_free_smartcard(struct prompt_config *pc)
|
|
{
|
|
- if (pc != NULL && pc_get_type(pc) == PC_TYPE_SC_PIN) {
|
|
- free(pc->data.sc_pin.prompt);
|
|
- pc->data.sc_pin.prompt = NULL;
|
|
+ if (pc != NULL && pc_get_type(pc) == PC_TYPE_SMARTCARD) {
|
|
+ free(pc->data.smartcard.prompt_init);
|
|
+ pc->data.smartcard.prompt_init = NULL;
|
|
+ free(pc->data.smartcard.prompt_pin);
|
|
+ pc->data.smartcard.prompt_pin = NULL;
|
|
}
|
|
return;
|
|
}
|
|
@@ -218,8 +237,8 @@ void pc_list_free(struct prompt_config **pc_list)
|
|
case PC_TYPE_2FA_SINGLE:
|
|
pc_free_2fa_single(pc_list[c]);
|
|
break;
|
|
- case PC_TYPE_SC_PIN:
|
|
- pc_free_sc_pin(pc_list[c]);
|
|
+ case PC_TYPE_SMARTCARD:
|
|
+ pc_free_smartcard(pc_list[c]);
|
|
break;
|
|
case PC_TYPE_PASSKEY:
|
|
pc_free_passkey(pc_list[c]);
|
|
@@ -479,6 +498,53 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+errno_t pc_list_add_smartcard(struct prompt_config ***pc_list,
|
|
+ const char *prompt_init, const char *prompt_pin)
|
|
+{
|
|
+ struct prompt_config *pc;
|
|
+ int ret;
|
|
+
|
|
+ if (pc_list == NULL) {
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ pc = calloc(1, sizeof(struct prompt_config));
|
|
+ if (pc == NULL) {
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ pc->type = PC_TYPE_SMARTCARD;
|
|
+
|
|
+ pc->data.smartcard.prompt_init = strdup(prompt_init != NULL ? prompt_init
|
|
+ : "");
|
|
+ if (pc->data.smartcard.prompt_init == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ pc->data.smartcard.prompt_pin = strdup(prompt_pin != NULL ? prompt_pin
|
|
+ : "");
|
|
+ if (pc->data.smartcard.prompt_pin == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = pc_list_add_pc(pc_list, pc);
|
|
+ if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ if (ret != EOK) {
|
|
+ free(pc->data.smartcard.prompt_init);
|
|
+ free(pc->data.smartcard.prompt_pin);
|
|
+ free(pc);
|
|
+ }
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
errno_t pam_get_response_prompt_config(struct prompt_config **pc_list, int *len,
|
|
uint8_t **data)
|
|
{
|
|
@@ -516,7 +582,11 @@ errno_t pam_get_response_prompt_config(struct prompt_config **pc_list, int *len,
|
|
l += sizeof(uint32_t);
|
|
l += strlen(pc_list[c]->data.passkey.prompt_touch);
|
|
break;
|
|
- case PC_TYPE_SC_PIN:
|
|
+ case PC_TYPE_SMARTCARD:
|
|
+ l += sizeof(uint32_t);
|
|
+ l += strlen(pc_list[c]->data.smartcard.prompt_init);
|
|
+ l += sizeof(uint32_t);
|
|
+ l += strlen(pc_list[c]->data.smartcard.prompt_pin);
|
|
break;
|
|
case PC_TYPE_EIDP:
|
|
l += sizeof(uint32_t);
|
|
@@ -581,7 +651,17 @@ errno_t pam_get_response_prompt_config(struct prompt_config **pc_list, int *len,
|
|
safealign_memcpy(&d[rp], pc_list[c]->data.passkey.prompt_touch,
|
|
strlen(pc_list[c]->data.passkey.prompt_touch), &rp);
|
|
break;
|
|
- case PC_TYPE_SC_PIN:
|
|
+ case PC_TYPE_SMARTCARD:
|
|
+ SAFEALIGN_SET_UINT32(&d[rp],
|
|
+ strlen(pc_list[c]->data.smartcard.prompt_init),
|
|
+ &rp);
|
|
+ safealign_memcpy(&d[rp], pc_list[c]->data.smartcard.prompt_init,
|
|
+ strlen(pc_list[c]->data.smartcard.prompt_init), &rp);
|
|
+ SAFEALIGN_SET_UINT32(&d[rp],
|
|
+ strlen(pc_list[c]->data.smartcard.prompt_pin),
|
|
+ &rp);
|
|
+ safealign_memcpy(&d[rp], pc_list[c]->data.smartcard.prompt_pin,
|
|
+ strlen(pc_list[c]->data.smartcard.prompt_pin), &rp);
|
|
break;
|
|
case PC_TYPE_EIDP:
|
|
SAFEALIGN_SET_UINT32(&d[rp],
|
|
@@ -780,7 +860,50 @@ errno_t pc_list_from_response(int size, uint8_t *buf,
|
|
goto done;
|
|
}
|
|
break;
|
|
- case PC_TYPE_SC_PIN:
|
|
+ case PC_TYPE_SMARTCARD:
|
|
+ if (rp > size - sizeof(uint32_t)) {
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp);
|
|
+
|
|
+ if (l > size || rp > size - l) {
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ str = strndup((char *) buf + rp, l);
|
|
+ if (str == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ rp += l;
|
|
+
|
|
+ if (rp > size - sizeof(uint32_t)) {
|
|
+ free(str);
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ SAFEALIGN_COPY_UINT32(&l, buf + rp, &rp);
|
|
+
|
|
+ if (l > size || rp > size - l) {
|
|
+ free(str);
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ str2 = strndup((char *) buf + rp, l);
|
|
+ if (str2 == NULL) {
|
|
+ free(str);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ rp += l;
|
|
+
|
|
+ ret = pc_list_add_smartcard(&pl, str, str2);
|
|
+ free(str);
|
|
+ free(str2);
|
|
+ if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
break;
|
|
case PC_TYPE_EIDP:
|
|
if (rp > size - sizeof(uint32_t)) {
|
|
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
|
|
index 0b00c4452..4e3c58c31 100644
|
|
--- a/src/sss_client/sss_cli.h
|
|
+++ b/src/sss_client/sss_cli.h
|
|
@@ -670,7 +670,7 @@ enum prompt_config_type {
|
|
PC_TYPE_2FA,
|
|
PC_TYPE_2FA_SINGLE,
|
|
PC_TYPE_PASSKEY,
|
|
- PC_TYPE_SC_PIN,
|
|
+ PC_TYPE_SMARTCARD,
|
|
PC_TYPE_EIDP,
|
|
PC_TYPE_LAST
|
|
};
|
|
@@ -686,6 +686,8 @@ const char *pc_get_passkey_inter_prompt(struct prompt_config *pc);
|
|
const char *pc_get_passkey_touch_prompt(struct prompt_config *pc);
|
|
const char *pc_get_eidp_init_prompt(struct prompt_config *pc);
|
|
const char *pc_get_eidp_link_prompt(struct prompt_config *pc);
|
|
+const char *pc_get_smartcard_init_prompt(struct prompt_config *pc);
|
|
+const char *pc_get_smartcard_pin_prompt(struct prompt_config *pc);
|
|
errno_t pc_list_add_passkey(struct prompt_config ***pc_list,
|
|
const char *inter_prompt,
|
|
const char *touch_prompt);
|
|
@@ -698,6 +700,8 @@ errno_t pc_list_add_2fa_single(struct prompt_config ***pc_list,
|
|
const char *prompt);
|
|
errno_t pc_list_add_eidp(struct prompt_config ***pc_list,
|
|
const char *prompt_init, const char *prompt_link);
|
|
+errno_t pc_list_add_smartcard(struct prompt_config ***pc_list,
|
|
+ const char *prompt_init, const char *prompt_pin);
|
|
errno_t pam_get_response_prompt_config(struct prompt_config **pc_list, int *len,
|
|
uint8_t **data);
|
|
errno_t pc_list_from_response(int size, uint8_t *buf,
|
|
diff --git a/src/tests/cmocka/test_prompt_config.c b/src/tests/cmocka/test_prompt_config.c
|
|
index 70b27875a..99f2ebc4b 100644
|
|
--- a/src/tests/cmocka/test_prompt_config.c
|
|
+++ b/src/tests/cmocka/test_prompt_config.c
|
|
@@ -117,6 +117,23 @@ void test_pc_list_add_eidp(void **state)
|
|
pc_list_free(pc_list);
|
|
}
|
|
|
|
+void test_pc_list_add_smartcard(void **state)
|
|
+{
|
|
+ int ret;
|
|
+ struct prompt_config **pc_list = NULL;
|
|
+
|
|
+ ret = pc_list_add_smartcard(&pc_list, "init", "PIN");
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_non_null(pc_list);
|
|
+ assert_non_null(pc_list[0]);
|
|
+ assert_int_equal(PC_TYPE_SMARTCARD, pc_get_type(pc_list[0]));
|
|
+ assert_string_equal("init", pc_get_smartcard_init_prompt(pc_list[0]));
|
|
+ assert_string_equal("PIN", pc_get_smartcard_pin_prompt(pc_list[0]));
|
|
+ assert_null(pc_list[1]);
|
|
+
|
|
+ pc_list_free(pc_list);
|
|
+}
|
|
+
|
|
void test_pam_get_response_prompt_config(void **state)
|
|
{
|
|
int ret;
|
|
@@ -136,15 +153,24 @@ void test_pam_get_response_prompt_config(void **state)
|
|
ret = pc_list_add_eidp(&pc_list, "init", "link");
|
|
assert_int_equal(ret, EOK);
|
|
|
|
+ ret = pc_list_add_smartcard(&pc_list, "init", "PIN");
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
ret = pam_get_response_prompt_config(pc_list, &len, &data);
|
|
pc_list_free(pc_list);
|
|
assert_int_equal(ret, EOK);
|
|
- assert_int_equal(len, 77);
|
|
+ assert_int_equal(len, 96);
|
|
|
|
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
|
- assert_memory_equal(data, "\4\0\0\0\1\0\0\0\10\0\0\0" "password\2\0\0\0\5\0\0\0" "first\6\0\0\0" "second\3\0\0\0\6\0\0\0" "single\6\0\0\0\4\0\0\0" "init\4\0\0\0" "link", len);
|
|
+ assert_memory_equal(data, "\5\0\0\0\1\0\0\0\10\0\0\0" "password\2\0\0\0\5\0\0\0"
|
|
+ "first\6\0\0\0" "second\3\0\0\0\6\0\0\0" "single\6\0\0\0\4\0\0\0"
|
|
+ "init\4\0\0\0" "link\5\0\0\0\4\0\0\0"
|
|
+ "init\3\0\0\0" "PIN", len);
|
|
#else
|
|
- assert_memory_equal(data, "\0\0\0\4\0\0\0\1\0\0\0\10" "password\0\0\0\2\0\0\0\5" "first\0\0\0\6" "second\0\0\0\3\0\0\0\6" "single\0\0\0\6\0\0\0\4" "init\0\0\0\4" "link", len);
|
|
+ assert_memory_equal(data, "\0\0\0\5\0\0\0\1\0\0\0\10" "password\0\0\0\2\0\0\0\5"
|
|
+ "first\0\0\0\6" "second\0\0\0\3\0\0\0\6" "single\0\0\0\6\0\0\0\4"
|
|
+ "init\0\0\0\4" "link\0\0\0\5\0\0\0\4"
|
|
+ "init\0\0\0\3" "PIN", len);
|
|
#endif
|
|
|
|
free(data);
|
|
@@ -169,10 +195,13 @@ void test_pc_list_from_response(void **state)
|
|
ret = pc_list_add_eidp(&pc_list, "init", "link");
|
|
assert_int_equal(ret, EOK);
|
|
|
|
+ ret = pc_list_add_smartcard(&pc_list, "init", "PIN");
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
ret = pam_get_response_prompt_config(pc_list, &len, &data);
|
|
pc_list_free(pc_list);
|
|
assert_int_equal(ret, EOK);
|
|
- assert_int_equal(len, 77);
|
|
+ assert_int_equal(len, 96);
|
|
|
|
pc_list = NULL;
|
|
|
|
@@ -199,7 +228,12 @@ void test_pc_list_from_response(void **state)
|
|
assert_string_equal("init", pc_get_eidp_init_prompt(pc_list[3]));
|
|
assert_string_equal("link", pc_get_eidp_link_prompt(pc_list[3]));
|
|
|
|
- assert_null(pc_list[4]);
|
|
+ assert_non_null(pc_list[4]);
|
|
+ assert_int_equal(PC_TYPE_SMARTCARD, pc_get_type(pc_list[4]));
|
|
+ assert_string_equal("init", pc_get_smartcard_init_prompt(pc_list[4]));
|
|
+ assert_string_equal("PIN", pc_get_smartcard_pin_prompt(pc_list[4]));
|
|
+
|
|
+ assert_null(pc_list[5]);
|
|
|
|
pc_list_free(pc_list);
|
|
}
|
|
@@ -219,6 +253,7 @@ int main(int argc, const char *argv[])
|
|
cmocka_unit_test(test_pc_list_add_2fa_single),
|
|
cmocka_unit_test(test_pc_list_add_2fa),
|
|
cmocka_unit_test(test_pc_list_add_eidp),
|
|
+ cmocka_unit_test(test_pc_list_add_smartcard),
|
|
cmocka_unit_test(test_pam_get_response_prompt_config),
|
|
cmocka_unit_test(test_pc_list_from_response),
|
|
};
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 7fd02b5ae107f102f05dc2125d3ddd31cd48275b Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Wed, 12 Jun 2024 15:37:31 +0200
|
|
Subject: [PATCH 15/36] util: implement pam_get_response_data_all_same_type()
|
|
|
|
This API gets all the elements with the selected response type data from
|
|
the response_data linked list. Includes unit tests.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/tests/cmocka/test_sss_pam_data.c | 73 ++++++++++++++++++++++++++++
|
|
src/util/sss_pam_data.c | 66 +++++++++++++++++++++++++
|
|
src/util/sss_pam_data.h | 19 ++++++++
|
|
3 files changed, 158 insertions(+)
|
|
|
|
diff --git a/src/tests/cmocka/test_sss_pam_data.c b/src/tests/cmocka/test_sss_pam_data.c
|
|
index 442b37372..ce4d328c7 100644
|
|
--- a/src/tests/cmocka/test_sss_pam_data.c
|
|
+++ b/src/tests/cmocka/test_sss_pam_data.c
|
|
@@ -34,6 +34,34 @@
|
|
#define OAUTH2_STR OAUTH2_URI OAUTH2_CODE
|
|
#define CCACHE_NAME "KRB5CCNAME=KCM:"
|
|
|
|
+#define SC1_CERT_USER "cert_user1\0"
|
|
+#define SC1_TOKEN_NAME "token_name1\0"
|
|
+#define SC1_MODULE_NAME "module_name1\0"
|
|
+#define SC1_KEY_ID "key_id1\0"
|
|
+#define SC1_LABEL "label1\0"
|
|
+#define SC1_PROMPT_STR "prompt1\0"
|
|
+#define SC1_PAM_CERT_USER "pam_cert_user1"
|
|
+#define SC1_STR SC1_CERT_USER SC1_TOKEN_NAME SC1_MODULE_NAME SC1_KEY_ID \
|
|
+ SC1_LABEL SC1_PROMPT_STR SC1_PAM_CERT_USER
|
|
+#define SC2_CERT_USER "cert_user2\0"
|
|
+#define SC2_TOKEN_NAME "token_name2\0"
|
|
+#define SC2_MODULE_NAME "module_name2\0"
|
|
+#define SC2_KEY_ID "key_id2\0"
|
|
+#define SC2_LABEL "label2\0"
|
|
+#define SC2_PROMPT_STR "prompt2\0"
|
|
+#define SC2_PAM_CERT_USER "pam_cert_user2"
|
|
+#define SC2_STR SC2_CERT_USER SC2_TOKEN_NAME SC2_MODULE_NAME SC2_KEY_ID \
|
|
+ SC2_LABEL SC2_PROMPT_STR SC2_PAM_CERT_USER
|
|
+#define SC3_CERT_USER "cert_user3\0"
|
|
+#define SC3_TOKEN_NAME "token_name3\0"
|
|
+#define SC3_MODULE_NAME "module_name3\0"
|
|
+#define SC3_KEY_ID "key_id3\0"
|
|
+#define SC3_LABEL "label3\0"
|
|
+#define SC3_PROMPT_STR "prompt3\0"
|
|
+#define SC3_PAM_CERT_USER "pam_cert_user3"
|
|
+#define SC3_STR SC3_CERT_USER SC3_TOKEN_NAME SC3_MODULE_NAME SC3_KEY_ID \
|
|
+ SC3_LABEL SC3_PROMPT_STR SC3_PAM_CERT_USER
|
|
+
|
|
|
|
/***********************
|
|
* TEST
|
|
@@ -120,6 +148,50 @@ void test_pam_get_response_data_three_elements(void **state)
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
+void test_pam_get_response_data_three_same_elements(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct pam_data *pd = NULL;
|
|
+ uint8_t **buf = NULL;
|
|
+ int32_t *expected_len = NULL;
|
|
+ int32_t *result_len = NULL;
|
|
+ int num;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ pd = talloc(test_ctx, struct pam_data);
|
|
+ assert_non_null(pd);
|
|
+ expected_len = talloc_array(test_ctx, int32_t, 3);
|
|
+ assert_non_null(expected_len);
|
|
+ pd->resp_list = NULL;
|
|
+ expected_len[0] = strlen(SC1_CERT_USER)+1+strlen(SC1_TOKEN_NAME)+1+
|
|
+ strlen(SC1_MODULE_NAME)+1+strlen(SC1_KEY_ID)+1+strlen(SC1_LABEL)+1+
|
|
+ strlen(SC1_PROMPT_STR)+1+strlen(SC1_PAM_CERT_USER)+1;
|
|
+ pam_add_response(pd, SSS_PAM_CERT_INFO, expected_len[0], discard_const(SC1_STR));
|
|
+ expected_len[1] = strlen(SC2_CERT_USER)+1+strlen(SC2_TOKEN_NAME)+1+
|
|
+ strlen(SC2_MODULE_NAME)+1+strlen(SC2_KEY_ID)+1+strlen(SC2_LABEL)+1+
|
|
+ strlen(SC2_PROMPT_STR)+1+strlen(SC2_PAM_CERT_USER)+1;
|
|
+ pam_add_response(pd, SSS_PAM_CERT_INFO, expected_len[1], discard_const(SC2_STR));
|
|
+ expected_len[2] = strlen(SC3_CERT_USER)+1+strlen(SC3_TOKEN_NAME)+1+
|
|
+ strlen(SC3_MODULE_NAME)+1+strlen(SC3_KEY_ID)+1+strlen(SC3_LABEL)+1+
|
|
+ strlen(SC3_PROMPT_STR)+1+strlen(SC3_PAM_CERT_USER)+1;
|
|
+ pam_add_response(pd, SSS_PAM_CERT_INFO, expected_len[2], discard_const(SC3_STR));
|
|
+
|
|
+ ret = pam_get_response_data_all_same_type(test_ctx, pd, SSS_PAM_CERT_INFO,
|
|
+ &buf, &result_len, &num);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_int_equal(num, 3);
|
|
+ assert_int_equal(result_len[0], expected_len[0]);
|
|
+ assert_string_equal((const char*) buf[0], SC3_STR);
|
|
+ assert_int_equal(result_len[1], expected_len[1]);
|
|
+ assert_string_equal((const char*) buf[1], SC2_STR);
|
|
+ assert_int_equal(result_len[2], expected_len[2]);
|
|
+ assert_string_equal((const char*) buf[2], SC1_STR);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
static void test_parse_supp_valgrind_args(void)
|
|
{
|
|
/*
|
|
@@ -144,6 +216,7 @@ int main(int argc, const char *argv[])
|
|
cmocka_unit_test(test_pam_get_response_data_not_found),
|
|
cmocka_unit_test(test_pam_get_response_data_one_element),
|
|
cmocka_unit_test(test_pam_get_response_data_three_elements),
|
|
+ cmocka_unit_test(test_pam_get_response_data_three_same_elements),
|
|
};
|
|
|
|
/* Set debug level to invalid value so we can decide if -d 0 was used. */
|
|
diff --git a/src/util/sss_pam_data.c b/src/util/sss_pam_data.c
|
|
index 75421d8e0..da7a9f19f 100644
|
|
--- a/src/util/sss_pam_data.c
|
|
+++ b/src/util/sss_pam_data.c
|
|
@@ -237,3 +237,69 @@ pam_get_response_data(TALLOC_CTX *mem_ctx, struct pam_data *pd, int32_t type,
|
|
done:
|
|
return ret;
|
|
}
|
|
+
|
|
+errno_t
|
|
+pam_get_response_data_all_same_type(TALLOC_CTX *mem_ctx, struct pam_data *pd,
|
|
+ int32_t type, uint8_t ***_buf,
|
|
+ int32_t **_len, int *_num)
|
|
+{
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ struct response_data *rdata = pd->resp_list;
|
|
+ uint8_t **buf = NULL;
|
|
+ int32_t *len = NULL;
|
|
+ int count = 0;
|
|
+ int ret;
|
|
+
|
|
+ tmp_ctx = talloc_new(NULL);
|
|
+ if (tmp_ctx == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ while (rdata != NULL) {
|
|
+ if (rdata->type == type) {
|
|
+ count++;
|
|
+ }
|
|
+ rdata = rdata->next;
|
|
+ }
|
|
+
|
|
+ if (count == 0) {
|
|
+ ret = ENOENT;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ buf = talloc_array(tmp_ctx, uint8_t*, count);
|
|
+ len = talloc_array(tmp_ctx, int32_t, count);
|
|
+ if (buf == NULL || len == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero_array failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ count = 0;
|
|
+ rdata = pd->resp_list;
|
|
+ while (rdata != NULL) {
|
|
+ if (rdata->type == type) {
|
|
+ buf[count] = talloc_memdup(buf, rdata->data, rdata->len);
|
|
+ if (buf[count] == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_memdup failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ len[count] = rdata->len;
|
|
+ count++;
|
|
+ }
|
|
+
|
|
+ rdata = rdata->next;
|
|
+ }
|
|
+
|
|
+ *_buf = talloc_steal(mem_ctx, buf);
|
|
+ *_len = talloc_steal(mem_ctx, len);
|
|
+ *_num = count;
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ talloc_free(tmp_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
diff --git a/src/util/sss_pam_data.h b/src/util/sss_pam_data.h
|
|
index 441720e97..fa83c4a1c 100644
|
|
--- a/src/util/sss_pam_data.h
|
|
+++ b/src/util/sss_pam_data.h
|
|
@@ -115,4 +115,23 @@ errno_t
|
|
pam_get_response_data(TALLOC_CTX *mem_ctx, struct pam_data *pd, int32_t type,
|
|
uint8_t **_buf, int32_t *_len);
|
|
|
|
+/**
|
|
+ * @brief Get the all the elements with the selected response type data from
|
|
+ * the response_data linked list
|
|
+ *
|
|
+ * @param[in] mem_ctx Memory context
|
|
+ * @param[in] pd Data structure containing the response_data linked list
|
|
+ * @param[in] type Response type
|
|
+ * @param[out] _buf Data wrapped inside response_data structure
|
|
+ * @param[out] _len Data length
|
|
+ * @param[out] _num Number of elements with the selected type
|
|
+ *
|
|
+ * @return 0 if the data was obtained properly,
|
|
+ * error code otherwise.
|
|
+ */
|
|
+errno_t
|
|
+pam_get_response_data_all_same_type(TALLOC_CTX *mem_ctx, struct pam_data *pd,
|
|
+ int32_t type, uint8_t ***_buf,
|
|
+ int32_t **_len, int *_num);
|
|
+
|
|
#endif /* _SSS_PAM_DATA_H_ */
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From faeec86c1c9e7c005ff57763c931e4c1323a1492 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Tue, 9 Apr 2024 12:05:56 +0200
|
|
Subject: [PATCH 16/36] Responder: generate JSON message for smartcard
|
|
|
|
Implement a set of functions to retrieve the smartcard data and generate
|
|
the JSON message with it.
|
|
|
|
Implement new unit test and adapt the existing ones to take into account
|
|
the new data.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_json.c | 387 +++++++++++++++++++++++++++-
|
|
src/responder/pam/pamsrv_json.h | 49 +++-
|
|
src/tests/cmocka/test_pamsrv_json.c | 307 +++++++++++++++++++++-
|
|
3 files changed, 725 insertions(+), 18 deletions(-)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
index a434e660a..7c778c42a 100644
|
|
--- a/src/responder/pam/pamsrv_json.c
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -22,8 +22,13 @@
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
+#ifndef _GNU_SOURCE
|
|
+#define _GNU_SOURCE
|
|
+#endif
|
|
+
|
|
#include <errno.h>
|
|
#include <stdbool.h>
|
|
+#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "responder/pam/pamsrv.h"
|
|
@@ -31,6 +36,20 @@
|
|
|
|
#include "pamsrv_json.h"
|
|
|
|
+struct cert_auth_info {
|
|
+ char *cert_user;
|
|
+ char *cert;
|
|
+ char *token_name;
|
|
+ char *module_name;
|
|
+ char *key_id;
|
|
+ char *label;
|
|
+ char *prompt_str;
|
|
+ char *pam_cert_user;
|
|
+ char *choice_list_id;
|
|
+ struct cert_auth_info *prev;
|
|
+ struct cert_auth_info *next;
|
|
+};
|
|
+
|
|
|
|
static errno_t
|
|
obtain_oauth2_data(TALLOC_CTX *mem_ctx, struct pam_data *pd, char **_uri,
|
|
@@ -130,12 +149,15 @@ done:
|
|
static errno_t
|
|
obtain_prompts(struct confdb_ctx *cdb, TALLOC_CTX *mem_ctx,
|
|
struct prompt_config **pc_list, const char **_password_prompt,
|
|
- const char **_oauth2_init_prompt, const char **_oauth2_link_prompt)
|
|
+ const char **_oauth2_init_prompt, const char **_oauth2_link_prompt,
|
|
+ const char **_sc_init_prompt, const char **_sc_pin_prompt)
|
|
{
|
|
TALLOC_CTX *tmp_ctx = NULL;
|
|
char *password_prompt = NULL;
|
|
char *oauth2_init_prompt = NULL;
|
|
char *oauth2_link_prompt = NULL;
|
|
+ char *sc_init_prompt = NULL;
|
|
+ char *sc_pin_prompt = NULL;
|
|
errno_t ret;
|
|
|
|
tmp_ctx = talloc_new(NULL);
|
|
@@ -161,9 +183,271 @@ obtain_prompts(struct confdb_ctx *cdb, TALLOC_CTX *mem_ctx,
|
|
goto done;
|
|
}
|
|
|
|
+ sc_init_prompt = talloc_strdup(tmp_ctx, SC_INIT_PROMPT);
|
|
+ if (sc_init_prompt == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ sc_pin_prompt = talloc_strdup(tmp_ctx, SC_PIN_PROMPT);
|
|
+ if (sc_pin_prompt == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
*_password_prompt = talloc_steal(mem_ctx, password_prompt);
|
|
*_oauth2_init_prompt = talloc_steal(mem_ctx, oauth2_init_prompt);
|
|
*_oauth2_link_prompt = talloc_steal(mem_ctx, oauth2_link_prompt);
|
|
+ *_sc_init_prompt = talloc_steal(mem_ctx, sc_init_prompt);
|
|
+ *_sc_pin_prompt = talloc_steal(mem_ctx, sc_pin_prompt);
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ talloc_free(tmp_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+errno_t
|
|
+get_cert_list(TALLOC_CTX *mem_ctx, struct pam_data *pd,
|
|
+ struct cert_auth_info **_cert_list)
|
|
+{
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ struct cert_auth_info *cert_list = NULL;
|
|
+ struct cert_auth_info *cai = NULL;
|
|
+ uint8_t **sc = NULL;
|
|
+ int32_t *len = NULL;
|
|
+ int32_t offset;
|
|
+ int32_t str_len;
|
|
+ int num;
|
|
+ int ret;
|
|
+
|
|
+ tmp_ctx = talloc_new(NULL);
|
|
+ if (tmp_ctx == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = pam_get_response_data_all_same_type(tmp_ctx, pd, SSS_PAM_CERT_INFO,
|
|
+ &sc, &len, &num);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "Unable to get SSS_PAM_CERT_INFO, ret %d.\n",
|
|
+ ret);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ for (int i = 0; i < num; i++) {
|
|
+ cai = talloc_zero(tmp_ctx, struct cert_auth_info);
|
|
+ if (cai == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ str_len = strnlen((const char *)sc[i], len[i]);
|
|
+ if (str_len >= len[i]) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "cert_user string is not null-terminated within buffer bounds.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ cai->cert_user = talloc_strndup(cai, (const char *)sc[i], str_len);
|
|
+ if (cai->cert_user == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ offset = str_len + 1;
|
|
+
|
|
+ if (offset >= len[i]) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "Trying to access data outside of the boundaries.\n");
|
|
+ ret = EPERM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ str_len = strnlen((const char *)sc[i] + offset, len[i] - offset);
|
|
+ if (str_len >= (len[i] - offset)) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "token_name string is not null-terminated within buffer bounds.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ cai->token_name = talloc_strndup(cai, (const char *)sc[i] + offset, str_len);
|
|
+ if (cai->token_name == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ offset += str_len + 1;
|
|
+
|
|
+ if (offset >= len[i]) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "Trying to access data outside of the boundaries.\n");
|
|
+ ret = EPERM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ str_len = strnlen((const char *)sc[i] + offset, len[i] - offset);
|
|
+ if (str_len >= (len[i] - offset)) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "module_name string is not null-terminated within buffer bounds.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ cai->module_name = talloc_strndup(cai, (const char *)sc[i] + offset, str_len);
|
|
+ if (cai->module_name == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ offset += str_len + 1;
|
|
+
|
|
+ if (offset >= len[i]) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "Trying to access data outside of the boundaries.\n");
|
|
+ ret = EPERM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ str_len = strnlen((const char *)sc[i] + offset, len[i] - offset);
|
|
+ if (str_len >= (len[i] - offset)) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "key_id string is not null-terminated within buffer bounds.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ cai->key_id = talloc_strndup(cai, (const char *)sc[i] + offset, str_len);
|
|
+ if (cai->key_id == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ offset += str_len + 1;
|
|
+
|
|
+ if (offset >= len[i]) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "Trying to access data outside of the boundaries.\n");
|
|
+ ret = EPERM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ str_len = strnlen((const char *)sc[i] + offset, len[i] - offset);
|
|
+ if (str_len >= (len[i] - offset)) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "label string is not null-terminated within buffer bounds.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ cai->label = talloc_strndup(cai, (const char *)sc[i] + offset, str_len);
|
|
+ if (cai->label == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ offset += str_len + 1;
|
|
+
|
|
+ if (offset >= len[i]) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "Trying to access data outside of the boundaries.\n");
|
|
+ ret = EPERM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ str_len = strnlen((const char *)sc[i] + offset, len[i] - offset);
|
|
+ if (str_len >= (len[i] - offset)) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "prompt_str string is not null-terminated within buffer bounds.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ cai->prompt_str = talloc_strndup(cai, (const char *)sc[i] + offset, str_len);
|
|
+ if (cai->prompt_str == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ offset += str_len + 1;
|
|
+
|
|
+ if (offset >= len[i]) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "Trying to access data outside of the boundaries.\n");
|
|
+ ret = EPERM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ str_len = strnlen((const char *)sc[i] + offset, len[i] - offset);
|
|
+ if (str_len >= (len[i] - offset)) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "pam_cert_user string is not null-terminated within buffer bounds.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+ cai->pam_cert_user = talloc_strndup(cai, (const char *)sc[i] + offset, str_len);
|
|
+ if (cai->pam_cert_user == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ offset += str_len + 1;
|
|
+
|
|
+ DEBUG(SSSDBG_FUNC_DATA,
|
|
+ "cert_user %s, token_name %s, module_name %s, key_id %s,"
|
|
+ "label %s, prompt_str %s, pam_cert_user %s.\n",
|
|
+ cai->cert_user, cai->token_name, cai->module_name, cai->key_id,
|
|
+ cai->label, cai->prompt_str, cai->pam_cert_user);
|
|
+
|
|
+ DLIST_ADD(cert_list, cai);
|
|
+ }
|
|
+
|
|
+ DLIST_FOR_EACH(cai, cert_list) {
|
|
+ talloc_steal(mem_ctx, cai);
|
|
+ }
|
|
+ *_cert_list = cert_list;
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ talloc_free(tmp_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+errno_t
|
|
+get_cert_names(TALLOC_CTX *mem_ctx, struct cert_auth_info *cert_list,
|
|
+ char ***_names)
|
|
+{
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ struct cert_auth_info *item = NULL;
|
|
+ char **names = NULL;
|
|
+ int i = 0;
|
|
+ int ret;
|
|
+
|
|
+ tmp_ctx = talloc_new(NULL);
|
|
+ if (tmp_ctx == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_new failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ DLIST_FOR_EACH(item, cert_list) {
|
|
+ i++;
|
|
+ }
|
|
+
|
|
+ names = talloc_array(tmp_ctx, char *, i+1);
|
|
+ if (names == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ i = 0;
|
|
+ DLIST_FOR_EACH(item, cert_list) {
|
|
+ names[i] = talloc_strdup(names, item->prompt_str);
|
|
+ if (names[i] == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ i++;
|
|
+ }
|
|
+ names[i] = NULL;
|
|
+
|
|
+ *_names = talloc_steal(mem_ctx, names);
|
|
ret = EOK;
|
|
|
|
done:
|
|
@@ -177,11 +461,16 @@ json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
bool oauth2_auth, const char *uri, const char *code,
|
|
const char *oauth2_init_prompt,
|
|
const char *oauth2_link_prompt,
|
|
+ bool sc_auth, char **sc_names,
|
|
+ const char *sc_init_prompt,
|
|
+ const char *sc_pin_prompt,
|
|
json_t **_list_mech)
|
|
{
|
|
json_t *root = NULL;
|
|
json_t *json_pass = NULL;
|
|
json_t *json_oauth2 = NULL;
|
|
+ json_t *json_sc = NULL;
|
|
+ char *key = NULL;
|
|
int ret;
|
|
|
|
root = json_object();
|
|
@@ -235,6 +524,38 @@ json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
}
|
|
}
|
|
|
|
+ if (sc_auth) {
|
|
+ for (int i = 0; sc_names[i] != NULL; i++) {
|
|
+ json_sc = json_pack("{s:s,s:s,s:b,s:s,s:s}",
|
|
+ "name", sc_names[i],
|
|
+ "role", "smartcard",
|
|
+ "selectable", true,
|
|
+ "init_instruction", sc_init_prompt,
|
|
+ "pin_prompt", sc_pin_prompt);
|
|
+ if (json_sc == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_pack failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = asprintf(&key, "smartcard:%d", i+1);
|
|
+ if (ret == -1) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "asprintf failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = json_object_set_new(root, key, json_sc);
|
|
+ if (ret == -1) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_array_append failed.\n");
|
|
+ json_decref(json_pass);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ free(key);
|
|
+ }
|
|
+ }
|
|
+
|
|
*_list_mech = root;
|
|
ret = EOK;
|
|
|
|
@@ -247,10 +568,12 @@ done:
|
|
}
|
|
|
|
errno_t
|
|
-json_format_priority(bool password_auth, bool oauth2_auth, json_t **_priority)
|
|
+json_format_priority(bool password_auth, bool oauth2_auth, bool sc_auth,
|
|
+ char **sc_names, json_t **_priority)
|
|
{
|
|
json_t *root = NULL;
|
|
json_t *json_priority = NULL;
|
|
+ char *key = NULL;
|
|
int ret;
|
|
|
|
root = json_array();
|
|
@@ -276,6 +599,31 @@ json_format_priority(bool password_auth, bool oauth2_auth, json_t **_priority)
|
|
}
|
|
}
|
|
|
|
+ if (sc_auth) {
|
|
+ for (int i = 0; sc_names[i] != NULL; i++) {
|
|
+ ret = asprintf(&key, "smartcard:%d", i+1);
|
|
+ if (ret == -1) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "asprintf failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ json_priority = json_string(key);
|
|
+ free(key);
|
|
+ if (json_priority == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_string failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ ret = json_array_append_new(root, json_priority);
|
|
+ if (ret == -1) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_array_append failed.\n");
|
|
+ json_decref(json_priority);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+
|
|
if (password_auth) {
|
|
json_priority = json_string("password");
|
|
if (json_priority == NULL) {
|
|
@@ -309,6 +657,9 @@ json_format_auth_selection(TALLOC_CTX *mem_ctx,
|
|
bool oauth2_auth, const char *uri, const char *code,
|
|
const char *oauth2_init_prompt,
|
|
const char *oauth2_link_prompt,
|
|
+ bool sc_auth, char **sc_names,
|
|
+ const char *sc_init_prompt,
|
|
+ const char *sc_pin_prompt,
|
|
char **_result)
|
|
{
|
|
json_t *root = NULL;
|
|
@@ -318,13 +669,17 @@ json_format_auth_selection(TALLOC_CTX *mem_ctx,
|
|
int ret;
|
|
|
|
ret = json_format_mechanisms(password_auth, password_prompt,
|
|
- oauth2_auth, uri, code, oauth2_init_prompt,
|
|
- oauth2_link_prompt, &json_mech);
|
|
+ oauth2_auth, uri, code,
|
|
+ oauth2_init_prompt, oauth2_link_prompt,
|
|
+ sc_auth, sc_names,
|
|
+ sc_init_prompt, sc_pin_prompt,
|
|
+ &json_mech);
|
|
if (ret != EOK) {
|
|
goto done;
|
|
}
|
|
|
|
- ret = json_format_priority(password_auth, oauth2_auth, &json_priority);
|
|
+ ret = json_format_priority(password_auth, oauth2_auth, sc_auth, sc_names,
|
|
+ &json_priority);
|
|
if (ret != EOK) {
|
|
json_decref(json_mech);
|
|
goto done;
|
|
@@ -365,13 +720,18 @@ generate_json_auth_message(struct confdb_ctx *cdb,
|
|
struct pam_data *_pd)
|
|
{
|
|
TALLOC_CTX *tmp_ctx = NULL;
|
|
+ struct cert_auth_info *cert_list = NULL;
|
|
const char *password_prompt = NULL;
|
|
const char *oauth2_init_prompt = NULL;
|
|
const char *oauth2_link_prompt = NULL;
|
|
+ const char *sc_init_prompt = NULL;
|
|
+ const char *sc_pin_prompt = NULL;
|
|
char *oauth2_uri = NULL;
|
|
char *oauth2_code = NULL;
|
|
+ char **sc_names = NULL;
|
|
char *result = NULL;
|
|
bool oauth2_auth = true;
|
|
+ bool sc_auth = true;
|
|
int ret;
|
|
|
|
tmp_ctx = talloc_new(NULL);
|
|
@@ -380,7 +740,8 @@ generate_json_auth_message(struct confdb_ctx *cdb,
|
|
}
|
|
|
|
ret = obtain_prompts(cdb, tmp_ctx, pc_list, &password_prompt,
|
|
- &oauth2_init_prompt, &oauth2_link_prompt);
|
|
+ &oauth2_init_prompt, &oauth2_link_prompt,
|
|
+ &sc_init_prompt, &sc_pin_prompt);
|
|
if (ret != EOK) {
|
|
DEBUG(SSSDBG_CRIT_FAILURE, "Failure to obtain the prompts.\n");
|
|
goto done;
|
|
@@ -393,9 +754,23 @@ generate_json_auth_message(struct confdb_ctx *cdb,
|
|
goto done;
|
|
}
|
|
|
|
+ ret = get_cert_list(tmp_ctx, _pd, &cert_list);
|
|
+ if (ret == ENOENT) {
|
|
+ sc_auth = false;
|
|
+ } else if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = get_cert_names(tmp_ctx, cert_list, &sc_names);
|
|
+ if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
ret = json_format_auth_selection(tmp_ctx, true, password_prompt,
|
|
oauth2_auth, oauth2_uri, oauth2_code,
|
|
oauth2_init_prompt, oauth2_link_prompt,
|
|
+ sc_auth, sc_names,
|
|
+ sc_init_prompt, sc_pin_prompt,
|
|
&result);
|
|
if (ret != EOK) {
|
|
goto done;
|
|
diff --git a/src/responder/pam/pamsrv_json.h b/src/responder/pam/pamsrv_json.h
|
|
index 4c8c3aef3..380bd65f7 100644
|
|
--- a/src/responder/pam/pamsrv_json.h
|
|
+++ b/src/responder/pam/pamsrv_json.h
|
|
@@ -33,8 +33,38 @@
|
|
#define PASSWORD_PROMPT "Password"
|
|
#define OAUTH2_INIT_PROMPT "Log In"
|
|
#define OAUTH2_LINK_PROMPT "Log in online with another device"
|
|
+#define SC_INIT_PROMPT "Insert smartcard"
|
|
+#define SC_PIN_PROMPT "PIN"
|
|
|
|
|
|
+/**
|
|
+ * @brief Extract smartcard certificate list from pam_data structure
|
|
+ *
|
|
+ * @param[in] mem_ctx Memory context
|
|
+ * @param[in] pd pam_data containing the certificates
|
|
+ * @param[out] _cert_list Certificate list
|
|
+ *
|
|
+ * @return 0 if the data was extracted successfully,
|
|
+ * error code otherwise.
|
|
+ */
|
|
+errno_t
|
|
+get_cert_list(TALLOC_CTX *mem_ctx, struct pam_data *pd,
|
|
+ struct cert_auth_info **_cert_list);
|
|
+
|
|
+/**
|
|
+ * @brief Extract smartcard certificate name list from the certificate list
|
|
+ *
|
|
+ * @param[in] mem_ctx Memory context
|
|
+ * @param[in] cert_list Certificate list
|
|
+ * @param[out] _names Certificate names list
|
|
+ *
|
|
+ * @return 0 if the data was extracted successfully,
|
|
+ * error code otherwise.
|
|
+ */
|
|
+errno_t
|
|
+get_cert_names(TALLOC_CTX *mem_ctx, struct cert_auth_info *cert_list,
|
|
+ char ***_names);
|
|
+
|
|
/**
|
|
* @brief Format authentication mechanisms to JSON
|
|
*
|
|
@@ -45,6 +75,10 @@
|
|
* @param[in] code OAUTH2 code
|
|
* @param[in] oauth2_init_prompt OAUTH2 initial prompt
|
|
* @param[in] oauth2_link_prompt OAUTH2 link prompt
|
|
+ * @param[in] sc_auth Whether smartcard authentication is allowed
|
|
+ * @param[in] sc_names smartcard names
|
|
+ * @param[in] sc_init_prompt smartcard initial prompt
|
|
+ * @param[in] sc_pin_prompt smartcard PIN prompt
|
|
* @param[out] _list_mech authentication mechanisms JSON object
|
|
*
|
|
* @return 0 if the authentication mechanisms were formatted properly,
|
|
@@ -55,6 +89,9 @@ json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
bool oauth2_auth, const char *uri, const char *code,
|
|
const char *oauth2_init_prompt,
|
|
const char *oauth2_link_prompt,
|
|
+ bool sc_auth, char **sc_names,
|
|
+ const char *sc_init_prompt,
|
|
+ const char *sc_pin_prompt,
|
|
json_t **_list_mech);
|
|
|
|
/**
|
|
@@ -62,13 +99,16 @@ json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
*
|
|
* @param[in] password_auth Whether password authentication is allowed
|
|
* @param[in] oath2_auth Whether OAUTH2 authentication is allowed
|
|
+ * @param[in] sc_auth Whether smartcard authentication is allowed
|
|
+ * @param[in] sc_names Smartcard certificate names
|
|
* @param[out] _priority priority JSON object
|
|
*
|
|
* @return 0 if the priority was formatted properly,
|
|
* error code otherwise.
|
|
*/
|
|
errno_t
|
|
-json_format_priority(bool password_auth, bool oauth2_auth, json_t **_priority);
|
|
+json_format_priority(bool password_auth, bool oauth2_auth, bool sc_auth,
|
|
+ char **sc_names, json_t **_priority);
|
|
|
|
/**
|
|
* @brief Format data to JSON
|
|
@@ -81,6 +121,10 @@ json_format_priority(bool password_auth, bool oauth2_auth, json_t **_priority);
|
|
* @param[in] code OAUTH2 code
|
|
* @param[in] oauth2_init_prompt OAUTH2 initial prompt
|
|
* @param[in] oauth2_link_prompt OAUTH2 link prompt
|
|
+ * @param[in] sc_auth Whether smartcard authentication is allowed
|
|
+ * @param[in] sc_names smartcard names
|
|
+ * @param[in] sc_init_prompt smartcard initial prompt
|
|
+ * @param[in] sc_pin_prompt smartcard PIN prompt
|
|
* @param[out] _result JSON message
|
|
*
|
|
* @return 0 if the JSON message was formatted properly,
|
|
@@ -92,6 +136,9 @@ json_format_auth_selection(TALLOC_CTX *mem_ctx,
|
|
bool oath2_auth, const char *uri, const char *code,
|
|
const char *oauth2_init_prompt,
|
|
const char *oauth2_link_prompt,
|
|
+ bool sc_auth, char **sc_names,
|
|
+ const char *sc_init_prompt,
|
|
+ const char *sc_pin_prompt,
|
|
char **_result);
|
|
|
|
/**
|
|
diff --git a/src/tests/cmocka/test_pamsrv_json.c b/src/tests/cmocka/test_pamsrv_json.c
|
|
index fcc7928f6..0abefa324 100644
|
|
--- a/src/tests/cmocka/test_pamsrv_json.c
|
|
+++ b/src/tests/cmocka/test_pamsrv_json.c
|
|
@@ -27,6 +27,7 @@
|
|
|
|
#include "tests/cmocka/common_mock.h"
|
|
|
|
+#include "src/responder/pam/pamsrv.h"
|
|
#include "src/responder/pam/pamsrv_json.h"
|
|
|
|
#define OAUTH2_URI "short.url.com/tmp\0"
|
|
@@ -34,6 +35,25 @@
|
|
#define OAUTH2_CODE "1234-5678"
|
|
#define OAUTH2_STR OAUTH2_URI OAUTH2_URI_COMP OAUTH2_CODE
|
|
|
|
+#define SC1_CERT_USER "cert_user1\0"
|
|
+#define SC1_TOKEN_NAME "token_name1\0"
|
|
+#define SC1_MODULE_NAME "module_name1\0"
|
|
+#define SC1_KEY_ID "key_id1\0"
|
|
+#define SC1_LABEL "label1\0"
|
|
+#define SC1_PROMPT_STR "prompt1\0"
|
|
+#define SC1_PAM_CERT_USER "pam_cert_user1"
|
|
+#define SC1_STR SC1_CERT_USER SC1_TOKEN_NAME SC1_MODULE_NAME SC1_KEY_ID \
|
|
+ SC1_LABEL SC1_PROMPT_STR SC1_PAM_CERT_USER
|
|
+#define SC2_CERT_USER "cert_user2\0"
|
|
+#define SC2_TOKEN_NAME "token_name2\0"
|
|
+#define SC2_MODULE_NAME "module_name2\0"
|
|
+#define SC2_KEY_ID "key_id2\0"
|
|
+#define SC2_LABEL "label2\0"
|
|
+#define SC2_PROMPT_STR "prompt2\0"
|
|
+#define SC2_PAM_CERT_USER "pam_cert_user2"
|
|
+#define SC2_STR SC2_CERT_USER SC2_TOKEN_NAME SC2_MODULE_NAME SC2_KEY_ID \
|
|
+ SC2_LABEL SC2_PROMPT_STR SC2_PAM_CERT_USER
|
|
+
|
|
#define BASIC_PASSWORD "\"password\": {" \
|
|
"\"name\": \"Password\", \"role\": \"password\", " \
|
|
"\"prompt\": \"Password\"}"
|
|
@@ -43,18 +63,39 @@
|
|
"\"linkPrompt\": \"" OAUTH2_LINK_PROMPT "\", " \
|
|
"\"uri\": \"short.url.com/tmp\", \"code\": \"1234-5678\", " \
|
|
"\"timeout\": 300}"
|
|
+#define BASIC_SC "\"smartcard:1\": {" \
|
|
+ "\"name\": \"prompt1\", \"role\": \"smartcard\", " \
|
|
+ "\"selectable\": true, " \
|
|
+ "\"init_instruction\": \"Insert smartcard\", " \
|
|
+ "\"pin_prompt\": \"PIN\"}"
|
|
+#define MULTIPLE_SC "\"smartcard:1\": {" \
|
|
+ "\"name\": \"prompt1\", \"role\": \"smartcard\", " \
|
|
+ "\"selectable\": true, " \
|
|
+ "\"init_instruction\": \"Insert smartcard\", " \
|
|
+ "\"pin_prompt\": \"PIN\"}, " \
|
|
+ "\"smartcard:2\": {" \
|
|
+ "\"name\": \"prompt2\", \"role\": \"smartcard\", " \
|
|
+ "\"selectable\": true, " \
|
|
+ "\"init_instruction\": \"Insert smartcard\", " \
|
|
+ "\"pin_prompt\": \"PIN\"}"
|
|
#define MECHANISMS_PASSWORD "{" BASIC_PASSWORD "}"
|
|
#define MECHANISMS_OAUTH2 "{" BASIC_OAUTH2 "}"
|
|
-#define PRIORITY_ALL "[\"eidp\", \"password\"]"
|
|
+#define MECHANISMS_SC1 "{" BASIC_SC "}"
|
|
+#define MECHANISMS_SC2 "{" MULTIPLE_SC "}"
|
|
+#define PRIORITY_ALL "[\"eidp\", \"smartcard:1\", \"smartcard:2\", \"password\"]"
|
|
#define AUTH_SELECTION_PASSWORD "{\"authSelection\": {\"mechanisms\": " \
|
|
MECHANISMS_PASSWORD ", " \
|
|
"\"priority\": [\"password\"]}}"
|
|
#define AUTH_SELECTION_OAUTH2 "{\"authSelection\": {\"mechanisms\": " \
|
|
MECHANISMS_OAUTH2 ", " \
|
|
"\"priority\": [\"eidp\"]}}"
|
|
+#define AUTH_SELECTION_SC "{\"authSelection\": {\"mechanisms\": " \
|
|
+ MECHANISMS_SC2 ", " \
|
|
+ "\"priority\": [\"smartcard:1\", \"smartcard:2\"]}}"
|
|
#define AUTH_SELECTION_ALL "{\"authSelection\": {\"mechanisms\": {" \
|
|
BASIC_PASSWORD ", " \
|
|
- BASIC_OAUTH2 "}, " \
|
|
+ BASIC_OAUTH2 ", " \
|
|
+ MULTIPLE_SC "}, " \
|
|
"\"priority\": " PRIORITY_ALL "}}"
|
|
|
|
#define PASSWORD_CONTENT "{\"password\": \"ThePassword\"}"
|
|
@@ -66,6 +107,19 @@
|
|
#define AUTH_MECH_ERRONEOUS "{\"authSelection\": {" \
|
|
"\"status\": \"Ok\", \"lololo\": {}}}"
|
|
|
|
+struct cert_auth_info {
|
|
+ char *cert_user;
|
|
+ char *cert;
|
|
+ char *token_name;
|
|
+ char *module_name;
|
|
+ char *key_id;
|
|
+ char *label;
|
|
+ char *prompt_str;
|
|
+ char *pam_cert_user;
|
|
+ char *choice_list_id;
|
|
+ struct cert_auth_info *prev;
|
|
+ struct cert_auth_info *next;
|
|
+};
|
|
|
|
/***********************
|
|
* WRAPPERS
|
|
@@ -92,14 +146,86 @@ __wrap_json_array_append_new(json_t *array, json_t *value)
|
|
/***********************
|
|
* TEST
|
|
**********************/
|
|
+void test_get_cert_list(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct cert_auth_info *cert_list = NULL;
|
|
+ struct cert_auth_info *item = NULL;
|
|
+ struct pam_data *pd = NULL;
|
|
+ const char *expected_token_name[] = {SC1_TOKEN_NAME, SC2_TOKEN_NAME};
|
|
+ const char *expected_module_name[] = {SC1_MODULE_NAME, SC2_MODULE_NAME};
|
|
+ const char *expected_key_id[] = {SC1_KEY_ID, SC2_KEY_ID};
|
|
+ const char *expected_label[] = {SC1_LABEL, SC2_LABEL};
|
|
+ const char *expected_prompt_str[] = {SC1_PROMPT_STR, SC2_PROMPT_STR};
|
|
+ int i = 0;
|
|
+ int len;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ pd = talloc_zero(test_ctx, struct pam_data);
|
|
+ assert_non_null(pd);
|
|
+
|
|
+ len = strlen(SC1_CERT_USER)+1+strlen(SC1_TOKEN_NAME)+1+
|
|
+ strlen(SC1_MODULE_NAME)+1+strlen(SC1_KEY_ID)+1+strlen(SC1_LABEL)+1+
|
|
+ strlen(SC1_PROMPT_STR)+1+strlen(SC1_PAM_CERT_USER)+1;
|
|
+ ret = pam_add_response(pd, SSS_PAM_CERT_INFO, len, discard_const(SC1_STR));
|
|
+ assert_int_equal(ret, EOK);
|
|
+ len = strlen(SC2_CERT_USER)+1+strlen(SC2_TOKEN_NAME)+1+
|
|
+ strlen(SC2_MODULE_NAME)+1+strlen(SC2_KEY_ID)+1+strlen(SC2_LABEL)+1+
|
|
+ strlen(SC2_PROMPT_STR)+1+strlen(SC2_PAM_CERT_USER)+1;
|
|
+ ret = pam_add_response(pd, SSS_PAM_CERT_INFO, len, discard_const(SC2_STR));
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
+ ret = get_cert_list(test_ctx, pd, &cert_list);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ DLIST_FOR_EACH(item, cert_list) {
|
|
+ assert_string_equal(expected_token_name[i], item->token_name);
|
|
+ assert_string_equal(expected_module_name[i], item->module_name);
|
|
+ assert_string_equal(expected_key_id[i], item->key_id);
|
|
+ assert_string_equal(expected_label[i], item->label);
|
|
+ assert_string_equal(expected_prompt_str[i], item->prompt_str);
|
|
+ i++;
|
|
+ }
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
+void test_get_cert_names(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct cert_auth_info *cert_list = NULL;
|
|
+ struct cert_auth_info *cai = NULL;
|
|
+ char **names = NULL;
|
|
+ int ret;
|
|
+
|
|
+ cai = talloc_zero(test_ctx, struct cert_auth_info);
|
|
+ assert_non_null(cai);
|
|
+ cai->prompt_str = discard_const(SC1_PROMPT_STR);
|
|
+ DLIST_ADD(cert_list, cai);
|
|
+ cai = talloc_zero(test_ctx, struct cert_auth_info);
|
|
+ assert_non_null(cai);
|
|
+ cai->prompt_str = discard_const(SC2_PROMPT_STR);
|
|
+ DLIST_ADD(cert_list, cai);
|
|
+
|
|
+ ret = get_cert_names(test_ctx, cert_list, &names);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_string_equal(names[0], SC2_PROMPT_STR);
|
|
+ assert_string_equal(names[1], SC1_PROMPT_STR);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
void test_json_format_mechanisms_password(void **state)
|
|
{
|
|
json_t *mechs = NULL;
|
|
char *string;
|
|
int ret;
|
|
|
|
- ret = json_format_mechanisms(true, PASSWORD_PROMPT, false, NULL, NULL,
|
|
- NULL, NULL, &mechs);
|
|
+ ret = json_format_mechanisms(true, PASSWORD_PROMPT,
|
|
+ false, NULL, NULL, NULL, NULL,
|
|
+ false, NULL, NULL, NULL,
|
|
+ &mechs);
|
|
assert_int_equal(ret, EOK);
|
|
|
|
string = json_dumps(mechs, 0);
|
|
@@ -116,6 +242,7 @@ void test_json_format_mechanisms_oauth2(void **state)
|
|
|
|
ret = json_format_mechanisms(false, NULL, true, OAUTH2_URI, OAUTH2_CODE,
|
|
OAUTH2_INIT_PROMPT, OAUTH2_LINK_PROMPT,
|
|
+ false, NULL, NULL, NULL,
|
|
&mechs);
|
|
assert_int_equal(ret, EOK);
|
|
|
|
@@ -125,21 +252,99 @@ void test_json_format_mechanisms_oauth2(void **state)
|
|
free(string);
|
|
}
|
|
|
|
+void test_json_format_mechanisms_sc1(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ json_t *mechs = NULL;
|
|
+ char **sc_names = NULL;
|
|
+ char *string;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ sc_names = talloc_array(test_ctx, char *, 2);
|
|
+ assert_non_null(sc_names);
|
|
+ sc_names[0] = talloc_strdup(sc_names, SC1_PROMPT_STR);
|
|
+ assert_non_null(sc_names[0]);
|
|
+ sc_names[1] = NULL;
|
|
+
|
|
+ ret = json_format_mechanisms(false, NULL,
|
|
+ false, NULL, NULL, NULL, NULL,
|
|
+ true, sc_names, SC_INIT_PROMPT, SC_PIN_PROMPT,
|
|
+ &mechs);
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
+ string = json_dumps(mechs, 0);
|
|
+ assert_string_equal(string, MECHANISMS_SC1);
|
|
+
|
|
+ json_decref(mechs);
|
|
+ free(string);
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
+void test_json_format_mechanisms_sc2(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ json_t *mechs = NULL;
|
|
+ char **sc_names = NULL;
|
|
+ char *string;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ sc_names = talloc_array(test_ctx, char *, 3);
|
|
+ assert_non_null(sc_names);
|
|
+ sc_names[0] = talloc_strdup(sc_names, SC1_PROMPT_STR);
|
|
+ assert_non_null(sc_names[0]);
|
|
+ sc_names[1] = talloc_strdup(sc_names, SC2_PROMPT_STR);
|
|
+ assert_non_null(sc_names[1]);
|
|
+ sc_names[2] = NULL;
|
|
+
|
|
+ ret = json_format_mechanisms(false, NULL,
|
|
+ false, NULL, NULL, NULL, NULL,
|
|
+ true, sc_names, SC_INIT_PROMPT, SC_PIN_PROMPT,
|
|
+ &mechs);
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
+ string = json_dumps(mechs, 0);
|
|
+ assert_string_equal(string, MECHANISMS_SC2);
|
|
+
|
|
+ json_decref(mechs);
|
|
+ free(string);
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
void test_json_format_priority_all(void **state)
|
|
{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
json_t *priority = NULL;
|
|
+ char **sc_names = NULL;
|
|
char *string;
|
|
int ret;
|
|
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ sc_names = talloc_array(test_ctx, char *, 3);
|
|
+ assert_non_null(sc_names);
|
|
+ sc_names[0] = talloc_strdup(sc_names, SC1_LABEL);
|
|
+ assert_non_null(sc_names[0]);
|
|
+ sc_names[1] = talloc_strdup(sc_names, SC2_LABEL);
|
|
+ assert_non_null(sc_names[1]);
|
|
+ sc_names[2] = NULL;
|
|
+
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
- ret = json_format_priority(true, true, &priority);
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ ret = json_format_priority(true, true, true, sc_names, &priority);
|
|
assert_int_equal(ret, EOK);
|
|
|
|
string = json_dumps(priority, 0);
|
|
assert_string_equal(string, PRIORITY_ALL);
|
|
+
|
|
json_decref(priority);
|
|
free(string);
|
|
+ talloc_free(test_ctx);
|
|
}
|
|
|
|
void test_json_format_auth_selection_password(void **state)
|
|
@@ -153,9 +358,13 @@ void test_json_format_auth_selection_password(void **state)
|
|
|
|
will_return(__wrap_json_array_append_new, false);
|
|
ret = json_format_auth_selection(test_ctx, true, PASSWORD_PROMPT,
|
|
- false, NULL, NULL, NULL, NULL, &json_msg);
|
|
+ false, NULL, NULL, NULL, NULL,
|
|
+ false, NULL, NULL, NULL,
|
|
+ &json_msg);
|
|
assert_int_equal(ret, EOK);
|
|
assert_string_equal(json_msg, AUTH_SELECTION_PASSWORD);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
}
|
|
|
|
void test_json_format_auth_selection_oauth2(void **state)
|
|
@@ -171,46 +380,105 @@ void test_json_format_auth_selection_oauth2(void **state)
|
|
ret = json_format_auth_selection(test_ctx, false, NULL,
|
|
true, OAUTH2_URI, OAUTH2_CODE,
|
|
OAUTH2_INIT_PROMPT, OAUTH2_LINK_PROMPT,
|
|
+ false, NULL, NULL, NULL,
|
|
&json_msg);
|
|
assert_int_equal(ret, EOK);
|
|
assert_string_equal(json_msg, AUTH_SELECTION_OAUTH2);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
+void test_json_format_auth_selection_sc(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx;
|
|
+ char **sc_names = NULL;
|
|
+ char *json_msg = NULL;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ sc_names = talloc_array(test_ctx, char *, 3);
|
|
+ assert_non_null(sc_names);
|
|
+ sc_names[0] = talloc_strdup(sc_names, SC1_PROMPT_STR);
|
|
+ assert_non_null(sc_names[0]);
|
|
+ sc_names[1] = talloc_strdup(sc_names, SC2_PROMPT_STR);
|
|
+ assert_non_null(sc_names[1]);
|
|
+ sc_names[2] = NULL;
|
|
+
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ ret = json_format_auth_selection(test_ctx, false, NULL,
|
|
+ false, NULL, NULL, NULL, NULL,
|
|
+ true, sc_names,
|
|
+ SC_INIT_PROMPT, SC_PIN_PROMPT,
|
|
+ &json_msg);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_string_equal(json_msg, AUTH_SELECTION_SC);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
}
|
|
|
|
void test_json_format_auth_selection_all(void **state)
|
|
{
|
|
TALLOC_CTX *test_ctx;
|
|
+ char **sc_names = NULL;
|
|
char *json_msg = NULL;
|
|
int ret;
|
|
|
|
test_ctx = talloc_new(NULL);
|
|
assert_non_null(test_ctx);
|
|
+ sc_names = talloc_array(test_ctx, char *, 3);
|
|
+ assert_non_null(sc_names);
|
|
+ sc_names[0] = talloc_strdup(sc_names, SC1_PROMPT_STR);
|
|
+ assert_non_null(sc_names[0]);
|
|
+ sc_names[1] = talloc_strdup(sc_names, SC2_PROMPT_STR);
|
|
+ assert_non_null(sc_names[1]);
|
|
+ sc_names[2] = NULL;
|
|
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
ret = json_format_auth_selection(test_ctx, true, PASSWORD_PROMPT,
|
|
true, OAUTH2_URI, OAUTH2_CODE,
|
|
OAUTH2_INIT_PROMPT, OAUTH2_LINK_PROMPT,
|
|
+ true, sc_names,
|
|
+ SC_INIT_PROMPT, SC_PIN_PROMPT,
|
|
&json_msg);
|
|
assert_int_equal(ret, EOK);
|
|
assert_string_equal(json_msg, AUTH_SELECTION_ALL);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
}
|
|
|
|
void test_json_format_auth_selection_failure(void **state)
|
|
{
|
|
TALLOC_CTX *test_ctx;
|
|
+ char **sc_names = NULL;
|
|
char *json_msg = NULL;
|
|
int ret;
|
|
|
|
test_ctx = talloc_new(NULL);
|
|
assert_non_null(test_ctx);
|
|
+ sc_names = talloc_array(test_ctx, char *, 3);
|
|
+ assert_non_null(sc_names);
|
|
+ sc_names[0] = talloc_strdup(sc_names, SC1_LABEL);
|
|
+ assert_non_null(sc_names[0]);
|
|
+ sc_names[1] = talloc_strdup(sc_names, SC2_LABEL);
|
|
+ assert_non_null(sc_names[1]);
|
|
+ sc_names[2] = NULL;
|
|
|
|
will_return(__wrap_json_array_append_new, true);
|
|
ret = json_format_auth_selection(test_ctx, true, PASSWORD_PROMPT,
|
|
true, OAUTH2_URI, OAUTH2_CODE,
|
|
OAUTH2_INIT_PROMPT, OAUTH2_LINK_PROMPT,
|
|
+ true, sc_names,
|
|
+ SC_INIT_PROMPT, SC_PIN_PROMPT,
|
|
&json_msg);
|
|
assert_int_equal(ret, ENOMEM);
|
|
assert_null(json_msg);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
}
|
|
|
|
void test_generate_json_message_integration(void **state)
|
|
@@ -218,6 +486,7 @@ void test_generate_json_message_integration(void **state)
|
|
TALLOC_CTX *test_ctx;
|
|
struct pam_data *pd = NULL;
|
|
struct prompt_config **pc_list = NULL;
|
|
+ int len;
|
|
int ret;
|
|
|
|
test_ctx = talloc_new(NULL);
|
|
@@ -225,12 +494,23 @@ void test_generate_json_message_integration(void **state)
|
|
pd = talloc_zero(test_ctx, struct pam_data);
|
|
assert_non_null(pd);
|
|
|
|
- pd->resp_list = talloc(pd, struct response_data);
|
|
- pd->resp_list->type = SSS_PAM_OAUTH2_INFO;
|
|
- pd->resp_list->len = strlen(OAUTH2_URI)+1+strlen(OAUTH2_URI_COMP)+1+strlen(OAUTH2_CODE)+1;
|
|
- pd->resp_list->data = discard_const(OAUTH2_STR);
|
|
- pd->resp_list->next = NULL;
|
|
+ len = strlen(OAUTH2_URI)+1+strlen(OAUTH2_URI_COMP)+1+strlen(OAUTH2_CODE)+1;
|
|
+ ret = pam_add_response(pd, SSS_PAM_OAUTH2_INFO, len,
|
|
+ discard_const(OAUTH2_STR));
|
|
+ assert_int_equal(ret, EOK);
|
|
+ len = strlen(SC1_CERT_USER)+1+strlen(SC1_TOKEN_NAME)+1+
|
|
+ strlen(SC1_MODULE_NAME)+1+strlen(SC1_KEY_ID)+1+strlen(SC1_LABEL)+1+
|
|
+ strlen(SC1_PROMPT_STR)+1+strlen(SC1_PAM_CERT_USER)+1;
|
|
+ ret = pam_add_response(pd, SSS_PAM_CERT_INFO, len, discard_const(SC1_STR));
|
|
+ assert_int_equal(ret, EOK);
|
|
+ len = strlen(SC2_CERT_USER)+1+strlen(SC2_TOKEN_NAME)+1+
|
|
+ strlen(SC2_MODULE_NAME)+1+strlen(SC2_KEY_ID)+1+strlen(SC2_LABEL)+1+
|
|
+ strlen(SC2_PROMPT_STR)+1+strlen(SC2_PAM_CERT_USER)+1;
|
|
+ ret = pam_add_response(pd, SSS_PAM_CERT_INFO, len, discard_const(SC2_STR));
|
|
+ assert_int_equal(ret, EOK);
|
|
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
ret = generate_json_auth_message(NULL, pc_list, pd);
|
|
@@ -395,11 +675,16 @@ int main(int argc, const char *argv[])
|
|
};
|
|
|
|
const struct CMUnitTest tests[] = {
|
|
+ cmocka_unit_test(test_get_cert_list),
|
|
+ cmocka_unit_test(test_get_cert_names),
|
|
cmocka_unit_test(test_json_format_mechanisms_password),
|
|
cmocka_unit_test(test_json_format_mechanisms_oauth2),
|
|
+ cmocka_unit_test(test_json_format_mechanisms_sc1),
|
|
+ cmocka_unit_test(test_json_format_mechanisms_sc2),
|
|
cmocka_unit_test(test_json_format_priority_all),
|
|
cmocka_unit_test(test_json_format_auth_selection_password),
|
|
cmocka_unit_test(test_json_format_auth_selection_oauth2),
|
|
+ cmocka_unit_test(test_json_format_auth_selection_sc),
|
|
cmocka_unit_test(test_json_format_auth_selection_all),
|
|
cmocka_unit_test(test_json_format_auth_selection_failure),
|
|
cmocka_unit_test(test_generate_json_message_integration),
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From fc69ec8928a44d6dbb04e376d31109608dc0f843 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Wed, 10 Apr 2024 17:35:22 +0200
|
|
Subject: [PATCH 17/36] Responder: parse reply for smartcard
|
|
|
|
Parse GUI reply for smartcard and set the appropriate data in
|
|
`sss_auth_token` structure.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_json.c | 86 +++++++++++++++++++++
|
|
src/responder/pam/pamsrv_json.h | 9 +++
|
|
src/tests/cmocka/test_pamsrv_json.c | 114 ++++++++++++++++++++++++++++
|
|
3 files changed, 209 insertions(+)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
index 7c778c42a..eba7707e5 100644
|
|
--- a/src/responder/pam/pamsrv_json.c
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -146,6 +146,27 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+static errno_t
|
|
+find_certificate_in_list(struct cert_auth_info *cert_list, int cert_num,
|
|
+ struct cert_auth_info **_cai)
|
|
+{
|
|
+ struct cert_auth_info *cai = NULL;
|
|
+ struct cert_auth_info *cai_next = NULL;
|
|
+ int i = 0;
|
|
+
|
|
+ DLIST_FOR_EACH_SAFE(cai, cai_next, cert_list) {
|
|
+ if (i == cert_num) {
|
|
+ goto done;
|
|
+ }
|
|
+ i++;
|
|
+ }
|
|
+
|
|
+done:
|
|
+ *_cai = cai;
|
|
+
|
|
+ return EOK;
|
|
+}
|
|
+
|
|
static errno_t
|
|
obtain_prompts(struct confdb_ctx *cdb, TALLOC_CTX *mem_ctx,
|
|
struct prompt_config **pc_list, const char **_password_prompt,
|
|
@@ -869,10 +890,33 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+errno_t
|
|
+json_unpack_smartcard(json_t *jroot, const char **_pin)
|
|
+{
|
|
+ char *pin = NULL;
|
|
+ int ret = EOK;
|
|
+
|
|
+ ret = json_unpack(jroot, "{s:s}",
|
|
+ "pin", &pin);
|
|
+ if (ret != 0) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "json_unpack for pin failed.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ *_pin = pin;
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
errno_t
|
|
json_unpack_auth_reply(struct pam_data *pd)
|
|
{
|
|
TALLOC_CTX *tmp_ctx = NULL;
|
|
+ struct cert_auth_info *cert_list = NULL;
|
|
+ struct cert_auth_info *cai = NULL;
|
|
json_t *jroot = NULL;
|
|
json_t *jauth_selection = NULL;
|
|
json_t *jobj = NULL;
|
|
@@ -881,6 +925,9 @@ json_unpack_auth_reply(struct pam_data *pd)
|
|
const char *status = NULL;
|
|
char *password = NULL;
|
|
char *oauth2_code = NULL;
|
|
+ const char *pin = NULL;
|
|
+ char *index = NULL;
|
|
+ int cert_num;
|
|
int ret = EOK;
|
|
|
|
DEBUG(SSSDBG_TRACE_FUNC, "Received JSON message: %s.\n",
|
|
@@ -947,6 +994,45 @@ json_unpack_auth_reply(struct pam_data *pd)
|
|
}
|
|
goto done;
|
|
}
|
|
+
|
|
+ if (strncmp(key, "smartcard", strlen("smartcard")) == 0) {
|
|
+ ret = json_unpack_smartcard(jobj, &pin);
|
|
+ if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = get_cert_list(tmp_ctx, pd, &cert_list);
|
|
+ if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ index = talloc_strdup(tmp_ctx, key + 10);
|
|
+ if (index == NULL) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "talloc_strdup failed: %d.\n", ret);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ cert_num = atoi(index);
|
|
+ cert_num--;
|
|
+ ret = find_certificate_in_list(cert_list, cert_num, &cai);
|
|
+ if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = sss_authtok_set_sc(pd->authtok, SSS_AUTHTOK_TYPE_SC_PIN,
|
|
+ pin, strlen(pin),
|
|
+ cai->token_name, strlen(cai->token_name),
|
|
+ cai->module_name, strlen(cai->module_name),
|
|
+ cai->key_id, strlen(cai->key_id),
|
|
+ cai->label, strlen(cai->label));
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "sss_authtok_set_sc failed: %d.\n", ret);
|
|
+ }
|
|
+ goto done;
|
|
+ }
|
|
}
|
|
|
|
DEBUG(SSSDBG_CRIT_FAILURE, "Unknown authentication mechanism\n");
|
|
diff --git a/src/responder/pam/pamsrv_json.h b/src/responder/pam/pamsrv_json.h
|
|
index 380bd65f7..2441510d0 100644
|
|
--- a/src/responder/pam/pamsrv_json.h
|
|
+++ b/src/responder/pam/pamsrv_json.h
|
|
@@ -183,6 +183,15 @@ errno_t
|
|
json_unpack_oauth2_code(TALLOC_CTX *mem_ctx, char *json_auth_msg,
|
|
char **_oauth2_code);
|
|
|
|
+/**
|
|
+ * @brief Unpack smartcard specific data reply
|
|
+ *
|
|
+ * @param[in] jroot jansson structure containing the smartcard specific data
|
|
+ * @param[out] _pin user PIN
|
|
+ */
|
|
+errno_t
|
|
+json_unpack_smartcard(json_t *jroot, const char **_pin);
|
|
+
|
|
/**
|
|
* @brief Unpack GDM reply and check its value
|
|
*
|
|
diff --git a/src/tests/cmocka/test_pamsrv_json.c b/src/tests/cmocka/test_pamsrv_json.c
|
|
index 0abefa324..2d4d6e4e1 100644
|
|
--- a/src/tests/cmocka/test_pamsrv_json.c
|
|
+++ b/src/tests/cmocka/test_pamsrv_json.c
|
|
@@ -99,11 +99,15 @@
|
|
"\"priority\": " PRIORITY_ALL "}}"
|
|
|
|
#define PASSWORD_CONTENT "{\"password\": \"ThePassword\"}"
|
|
+#define SMARTCARD_CONTENT "{\"pin\": \"ThePIN\"}"
|
|
#define AUTH_MECH_REPLY_PASSWORD "{\"authSelection\": {" \
|
|
"\"status\": \"Ok\", \"password\": " \
|
|
PASSWORD_CONTENT "}}"
|
|
#define AUTH_MECH_REPLY_OAUTH2 "{\"authSelection\": {" \
|
|
"\"status\": \"Ok\", \"eidp\": {}}}"
|
|
+#define AUTH_MECH_REPLY_SMARTCARD "{\"auth-selection\": {" \
|
|
+ "\"status\": \"Ok\", \"smartcard:1\": " \
|
|
+ SMARTCARD_CONTENT "}}"
|
|
#define AUTH_MECH_ERRONEOUS "{\"authSelection\": {" \
|
|
"\"status\": \"Ok\", \"lololo\": {}}}"
|
|
|
|
@@ -537,6 +541,22 @@ void test_json_unpack_password_ok(void **state)
|
|
json_decref(jroot);
|
|
}
|
|
|
|
+void test_json_unpack_sc_ok(void **state)
|
|
+{
|
|
+ json_t *jroot = NULL;
|
|
+ const char *pin = NULL;
|
|
+ json_error_t jret;
|
|
+ int ret;
|
|
+
|
|
+ jroot = json_loads(SMARTCARD_CONTENT, 0, &jret);
|
|
+ assert_non_null(jroot);
|
|
+
|
|
+ ret = json_unpack_smartcard(jroot, &pin);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_string_equal(pin, "ThePIN");
|
|
+ json_decref(jroot);
|
|
+}
|
|
+
|
|
void test_json_unpack_auth_reply_password(void **state)
|
|
{
|
|
TALLOC_CTX *test_ctx = NULL;
|
|
@@ -588,6 +608,97 @@ void test_json_unpack_auth_reply_oauth2(void **state)
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
+void test_json_unpack_auth_reply_sc1(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct pam_data *pd = NULL;
|
|
+ const char *pin = NULL;
|
|
+ const char *token_name = NULL;
|
|
+ const char *module_name = NULL;
|
|
+ const char *key_id = NULL;
|
|
+ const char *label = NULL;
|
|
+ size_t pin_len, token_name_len, module_name_len, key_id_len, label_len;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ pd = talloc_zero(test_ctx, struct pam_data);
|
|
+ assert_non_null(pd);
|
|
+ pd->authtok = sss_authtok_new(pd);
|
|
+ assert_non_null(pd->authtok);
|
|
+ pd->json_auth_selected = discard_const(AUTH_MECH_REPLY_SMARTCARD);
|
|
+ len = strlen(SC1_CERT_USER)+1+strlen(SC1_TOKEN_NAME)+1+
|
|
+ strlen(SC1_MODULE_NAME)+1+strlen(SC1_KEY_ID)+1+strlen(SC1_LABEL)+1+
|
|
+ strlen(SC1_PROMPT_STR)+1+strlen(SC1_PAM_CERT_USER)+1;
|
|
+ ret = pam_add_response(pd, SSS_PAM_CERT_INFO, len, discard_const(SC1_STR));
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
+ ret = json_unpack_auth_reply(pd);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_int_equal(sss_authtok_get_type(pd->authtok), SSS_AUTHTOK_TYPE_SC_PIN);
|
|
+ ret = sss_authtok_get_sc(pd->authtok, &pin, &pin_len,
|
|
+ &token_name, &token_name_len,
|
|
+ &module_name, &module_name_len,
|
|
+ &key_id, &key_id_len,
|
|
+ &label, &label_len);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_string_equal(pin, "ThePIN");
|
|
+ assert_string_equal(token_name, SC1_TOKEN_NAME);
|
|
+ assert_string_equal(module_name, SC1_MODULE_NAME);
|
|
+ assert_string_equal(key_id, SC1_KEY_ID);
|
|
+ assert_string_equal(label, SC1_LABEL);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
+void test_json_unpack_auth_reply_sc2(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct pam_data *pd = NULL;
|
|
+ const char *pin = NULL;
|
|
+ const char *token_name = NULL;
|
|
+ const char *module_name = NULL;
|
|
+ const char *key_id = NULL;
|
|
+ const char *label = NULL;
|
|
+ size_t pin_len, token_name_len, module_name_len, key_id_len, label_len;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ pd = talloc_zero(test_ctx, struct pam_data);
|
|
+ assert_non_null(pd);
|
|
+ pd->authtok = sss_authtok_new(pd);
|
|
+ assert_non_null(pd->authtok);
|
|
+ pd->json_auth_selected = discard_const(AUTH_MECH_REPLY_SMARTCARD);
|
|
+ len = strlen(SC1_CERT_USER)+1+strlen(SC1_TOKEN_NAME)+1+
|
|
+ strlen(SC1_MODULE_NAME)+1+strlen(SC1_KEY_ID)+1+strlen(SC1_LABEL)+1+
|
|
+ strlen(SC1_PROMPT_STR)+1+strlen(SC1_PAM_CERT_USER)+1;
|
|
+ ret = pam_add_response(pd, SSS_PAM_CERT_INFO, len, discard_const(SC1_STR));
|
|
+ assert_int_equal(ret, EOK);
|
|
+ len = strlen(SC2_CERT_USER)+1+strlen(SC2_TOKEN_NAME)+1+
|
|
+ strlen(SC2_MODULE_NAME)+1+strlen(SC2_KEY_ID)+1+strlen(SC2_LABEL)+1+
|
|
+ strlen(SC2_PROMPT_STR)+1+strlen(SC2_PAM_CERT_USER)+1;
|
|
+ ret = pam_add_response(pd, SSS_PAM_CERT_INFO, len, discard_const(SC2_STR));
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
+ ret = json_unpack_auth_reply(pd);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_int_equal(sss_authtok_get_type(pd->authtok), SSS_AUTHTOK_TYPE_SC_PIN);
|
|
+ ret = sss_authtok_get_sc(pd->authtok, &pin, &pin_len,
|
|
+ &token_name, &token_name_len,
|
|
+ &module_name, &module_name_len,
|
|
+ &key_id, &key_id_len,
|
|
+ &label, &label_len);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_string_equal(pin, "ThePIN");
|
|
+ assert_string_equal(token_name, SC1_TOKEN_NAME);
|
|
+ assert_string_equal(module_name, SC1_MODULE_NAME);
|
|
+ assert_string_equal(key_id, SC1_KEY_ID);
|
|
+ assert_string_equal(label, SC1_LABEL);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
void test_json_unpack_auth_reply_failure(void **state)
|
|
{
|
|
TALLOC_CTX *test_ctx = NULL;
|
|
@@ -689,8 +800,11 @@ int main(int argc, const char *argv[])
|
|
cmocka_unit_test(test_json_format_auth_selection_failure),
|
|
cmocka_unit_test(test_generate_json_message_integration),
|
|
cmocka_unit_test(test_json_unpack_password_ok),
|
|
+ cmocka_unit_test(test_json_unpack_sc_ok),
|
|
cmocka_unit_test(test_json_unpack_auth_reply_password),
|
|
cmocka_unit_test(test_json_unpack_auth_reply_oauth2),
|
|
+ cmocka_unit_test(test_json_unpack_auth_reply_sc1),
|
|
+ cmocka_unit_test(test_json_unpack_auth_reply_sc2),
|
|
cmocka_unit_test(test_json_unpack_auth_reply_failure),
|
|
cmocka_unit_test(test_json_unpack_oauth2_code),
|
|
cmocka_unit_test(test_is_pam_json_enabled_service_in_list),
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From a93959dfd0ee637cf7f4ad0a5879a58bea00bb6b Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Fri, 6 Sep 2024 11:23:14 +0200
|
|
Subject: [PATCH 18/36] Responder: refactor JSON functions to reduce args
|
|
|
|
Several of the functions in `pamsrv_json` had lots of arguments and I'm
|
|
about to add more for the passkey authentication mechanism. Reduce these
|
|
arguments by creating a structure that will contain all these data.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_json.c | 208 ++++++++++---------
|
|
src/responder/pam/pamsrv_json.h | 85 ++++----
|
|
src/tests/cmocka/test_pamsrv_json.c | 301 ++++++++++++++++------------
|
|
3 files changed, 328 insertions(+), 266 deletions(-)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
index eba7707e5..286a3fd50 100644
|
|
--- a/src/responder/pam/pamsrv_json.c
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -52,8 +52,8 @@ struct cert_auth_info {
|
|
|
|
|
|
static errno_t
|
|
-obtain_oauth2_data(TALLOC_CTX *mem_ctx, struct pam_data *pd, char **_uri,
|
|
- char **_code)
|
|
+obtain_oauth2_data(TALLOC_CTX *mem_ctx, struct pam_data *pd,
|
|
+ struct auth_data *_auth_data)
|
|
{
|
|
TALLOC_CTX *tmp_ctx = NULL;
|
|
uint8_t *oauth2 = NULL;
|
|
@@ -136,8 +136,8 @@ obtain_oauth2_data(TALLOC_CTX *mem_ctx, struct pam_data *pd, char **_uri,
|
|
goto done;
|
|
}
|
|
|
|
- *_uri = talloc_steal(mem_ctx, uri);
|
|
- *_code = talloc_steal(mem_ctx, code);
|
|
+ _auth_data->oauth2->uri = talloc_steal(mem_ctx, uri);
|
|
+ _auth_data->oauth2->code = talloc_steal(mem_ctx, code);
|
|
ret = EOK;
|
|
|
|
done:
|
|
@@ -169,9 +169,7 @@ done:
|
|
|
|
static errno_t
|
|
obtain_prompts(struct confdb_ctx *cdb, TALLOC_CTX *mem_ctx,
|
|
- struct prompt_config **pc_list, const char **_password_prompt,
|
|
- const char **_oauth2_init_prompt, const char **_oauth2_link_prompt,
|
|
- const char **_sc_init_prompt, const char **_sc_pin_prompt)
|
|
+ struct prompt_config **pc_list, struct auth_data *_auth_data)
|
|
{
|
|
TALLOC_CTX *tmp_ctx = NULL;
|
|
char *password_prompt = NULL;
|
|
@@ -216,11 +214,11 @@ obtain_prompts(struct confdb_ctx *cdb, TALLOC_CTX *mem_ctx,
|
|
goto done;
|
|
}
|
|
|
|
- *_password_prompt = talloc_steal(mem_ctx, password_prompt);
|
|
- *_oauth2_init_prompt = talloc_steal(mem_ctx, oauth2_init_prompt);
|
|
- *_oauth2_link_prompt = talloc_steal(mem_ctx, oauth2_link_prompt);
|
|
- *_sc_init_prompt = talloc_steal(mem_ctx, sc_init_prompt);
|
|
- *_sc_pin_prompt = talloc_steal(mem_ctx, sc_pin_prompt);
|
|
+ _auth_data->pswd->prompt = talloc_steal(mem_ctx, password_prompt);
|
|
+ _auth_data->oauth2->init_prompt = talloc_steal(mem_ctx, oauth2_init_prompt);
|
|
+ _auth_data->oauth2->link_prompt = talloc_steal(mem_ctx, oauth2_link_prompt);
|
|
+ _auth_data->sc->init_prompt = talloc_steal(mem_ctx, sc_init_prompt);
|
|
+ _auth_data->sc->pin_prompt = talloc_steal(mem_ctx, sc_pin_prompt);
|
|
ret = EOK;
|
|
|
|
done:
|
|
@@ -428,9 +426,81 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+static errno_t
|
|
+init_auth_data(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
|
|
+ struct prompt_config **pc_list, struct pam_data *pd,
|
|
+ struct auth_data **_auth_data)
|
|
+{
|
|
+ struct cert_auth_info *cert_list = NULL;
|
|
+ errno_t ret = EOK;
|
|
+
|
|
+ *_auth_data = talloc_zero(mem_ctx, struct auth_data);
|
|
+ if (*_auth_data == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ (*_auth_data)->pswd = talloc_zero(mem_ctx, struct password_data);
|
|
+ if ((*_auth_data)->pswd == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ (*_auth_data)->pswd->enabled = true;
|
|
+
|
|
+ (*_auth_data)->oauth2 = talloc_zero(mem_ctx, struct oauth2_data);
|
|
+ if ((*_auth_data)->oauth2 == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ (*_auth_data)->oauth2->enabled = true;
|
|
+
|
|
+ (*_auth_data)->sc = talloc_zero(mem_ctx, struct sc_data);
|
|
+ if ((*_auth_data)->sc == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ (*_auth_data)->sc->enabled = true;
|
|
+
|
|
+ ret = obtain_prompts(cdb, mem_ctx, pc_list, *_auth_data);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failure to obtain the prompts.\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = obtain_oauth2_data(mem_ctx, pd, *_auth_data);
|
|
+ if (ret == ENOENT) {
|
|
+ (*_auth_data)->oauth2->enabled = false;
|
|
+ } else if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failure to obtain OAUTH2 data.\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = get_cert_list(mem_ctx, pd, &cert_list);
|
|
+ if (ret == ENOENT) {
|
|
+ (*_auth_data)->sc->enabled = false;
|
|
+ } else if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "Failure to obtain smartcard certificate list.\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = get_cert_names(mem_ctx, cert_list, *_auth_data);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failure to obtain smartcard labels.\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+done:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
errno_t
|
|
get_cert_names(TALLOC_CTX *mem_ctx, struct cert_auth_info *cert_list,
|
|
- char ***_names)
|
|
+ struct auth_data *_auth_data)
|
|
{
|
|
TALLOC_CTX *tmp_ctx = NULL;
|
|
struct cert_auth_info *item = NULL;
|
|
@@ -468,7 +538,7 @@ get_cert_names(TALLOC_CTX *mem_ctx, struct cert_auth_info *cert_list,
|
|
}
|
|
names[i] = NULL;
|
|
|
|
- *_names = talloc_steal(mem_ctx, names);
|
|
+ _auth_data->sc->names = talloc_steal(mem_ctx, names);
|
|
ret = EOK;
|
|
|
|
done:
|
|
@@ -478,14 +548,7 @@ done:
|
|
}
|
|
|
|
errno_t
|
|
-json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
- bool oauth2_auth, const char *uri, const char *code,
|
|
- const char *oauth2_init_prompt,
|
|
- const char *oauth2_link_prompt,
|
|
- bool sc_auth, char **sc_names,
|
|
- const char *sc_init_prompt,
|
|
- const char *sc_pin_prompt,
|
|
- json_t **_list_mech)
|
|
+json_format_mechanisms(struct auth_data *auth_data, json_t **_list_mech)
|
|
{
|
|
json_t *root = NULL;
|
|
json_t *json_pass = NULL;
|
|
@@ -501,11 +564,11 @@ json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
goto done;
|
|
}
|
|
|
|
- if (password_auth) {
|
|
+ if (auth_data->pswd->enabled) {
|
|
json_pass = json_pack("{s:s,s:s,s:s}",
|
|
"name", "Password",
|
|
"role", "password",
|
|
- "prompt", password_prompt);
|
|
+ "prompt", auth_data->pswd->prompt);
|
|
if (json_pass == NULL) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "json_pack failed.\n");
|
|
ret = ENOMEM;
|
|
@@ -521,14 +584,14 @@ json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
}
|
|
}
|
|
|
|
- if (oauth2_auth) {
|
|
+ if (auth_data->oauth2->enabled) {
|
|
json_oauth2 = json_pack("{s:s,s:s,s:s,s:s,s:s,s:s,s:i}",
|
|
"name", "Web Login",
|
|
"role", "eidp",
|
|
- "initPrompt", oauth2_init_prompt,
|
|
- "linkPrompt", oauth2_link_prompt,
|
|
- "uri", uri,
|
|
- "code", code,
|
|
+ "initPrompt", auth_data->oauth2->init_prompt,
|
|
+ "linkPrompt", auth_data->oauth2->link_prompt,
|
|
+ "uri", auth_data->oauth2->uri,
|
|
+ "code", auth_data->oauth2->code,
|
|
"timeout", 300);
|
|
if (json_oauth2 == NULL) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "json_pack failed.\n");
|
|
@@ -545,14 +608,14 @@ json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
}
|
|
}
|
|
|
|
- if (sc_auth) {
|
|
- for (int i = 0; sc_names[i] != NULL; i++) {
|
|
+ if (auth_data->sc->enabled) {
|
|
+ for (int i = 0; auth_data->sc->names[i] != NULL; i++) {
|
|
json_sc = json_pack("{s:s,s:s,s:b,s:s,s:s}",
|
|
- "name", sc_names[i],
|
|
+ "name", auth_data->sc->names[i],
|
|
"role", "smartcard",
|
|
"selectable", true,
|
|
- "init_instruction", sc_init_prompt,
|
|
- "pin_prompt", sc_pin_prompt);
|
|
+ "init_instruction", auth_data->sc->init_prompt,
|
|
+ "pin_prompt", auth_data->sc->pin_prompt);
|
|
if (json_sc == NULL) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "json_pack failed.\n");
|
|
ret = ENOMEM;
|
|
@@ -589,8 +652,7 @@ done:
|
|
}
|
|
|
|
errno_t
|
|
-json_format_priority(bool password_auth, bool oauth2_auth, bool sc_auth,
|
|
- char **sc_names, json_t **_priority)
|
|
+json_format_priority(struct auth_data *auth_data, json_t **_priority)
|
|
{
|
|
json_t *root = NULL;
|
|
json_t *json_priority = NULL;
|
|
@@ -604,7 +666,7 @@ json_format_priority(bool password_auth, bool oauth2_auth, bool sc_auth,
|
|
goto done;
|
|
}
|
|
|
|
- if (oauth2_auth) {
|
|
+ if (auth_data->oauth2->enabled) {
|
|
json_priority = json_string("eidp");
|
|
if (json_priority == NULL) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "json_string failed.\n");
|
|
@@ -620,8 +682,8 @@ json_format_priority(bool password_auth, bool oauth2_auth, bool sc_auth,
|
|
}
|
|
}
|
|
|
|
- if (sc_auth) {
|
|
- for (int i = 0; sc_names[i] != NULL; i++) {
|
|
+ if (auth_data->sc->enabled) {
|
|
+ for (int i = 0; auth_data->sc->names[i] != NULL; i++) {
|
|
ret = asprintf(&key, "smartcard:%d", i+1);
|
|
if (ret == -1) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "asprintf failed.\n");
|
|
@@ -645,7 +707,7 @@ json_format_priority(bool password_auth, bool oauth2_auth, bool sc_auth,
|
|
}
|
|
}
|
|
|
|
- if (password_auth) {
|
|
+ if (auth_data->pswd->enabled) {
|
|
json_priority = json_string("password");
|
|
if (json_priority == NULL) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "json_string failed.\n");
|
|
@@ -673,14 +735,7 @@ done:
|
|
}
|
|
|
|
errno_t
|
|
-json_format_auth_selection(TALLOC_CTX *mem_ctx,
|
|
- bool password_auth, const char *password_prompt,
|
|
- bool oauth2_auth, const char *uri, const char *code,
|
|
- const char *oauth2_init_prompt,
|
|
- const char *oauth2_link_prompt,
|
|
- bool sc_auth, char **sc_names,
|
|
- const char *sc_init_prompt,
|
|
- const char *sc_pin_prompt,
|
|
+json_format_auth_selection(TALLOC_CTX *mem_ctx, struct auth_data *auth_data,
|
|
char **_result)
|
|
{
|
|
json_t *root = NULL;
|
|
@@ -689,18 +744,12 @@ json_format_auth_selection(TALLOC_CTX *mem_ctx,
|
|
char *string = NULL;
|
|
int ret;
|
|
|
|
- ret = json_format_mechanisms(password_auth, password_prompt,
|
|
- oauth2_auth, uri, code,
|
|
- oauth2_init_prompt, oauth2_link_prompt,
|
|
- sc_auth, sc_names,
|
|
- sc_init_prompt, sc_pin_prompt,
|
|
- &json_mech);
|
|
+ ret = json_format_mechanisms(auth_data, &json_mech);
|
|
if (ret != EOK) {
|
|
goto done;
|
|
}
|
|
|
|
- ret = json_format_priority(password_auth, oauth2_auth, sc_auth, sc_names,
|
|
- &json_priority);
|
|
+ ret = json_format_priority(auth_data, &json_priority);
|
|
if (ret != EOK) {
|
|
json_decref(json_mech);
|
|
goto done;
|
|
@@ -741,18 +790,8 @@ generate_json_auth_message(struct confdb_ctx *cdb,
|
|
struct pam_data *_pd)
|
|
{
|
|
TALLOC_CTX *tmp_ctx = NULL;
|
|
- struct cert_auth_info *cert_list = NULL;
|
|
- const char *password_prompt = NULL;
|
|
- const char *oauth2_init_prompt = NULL;
|
|
- const char *oauth2_link_prompt = NULL;
|
|
- const char *sc_init_prompt = NULL;
|
|
- const char *sc_pin_prompt = NULL;
|
|
- char *oauth2_uri = NULL;
|
|
- char *oauth2_code = NULL;
|
|
- char **sc_names = NULL;
|
|
+ struct auth_data *auth_data = NULL;
|
|
char *result = NULL;
|
|
- bool oauth2_auth = true;
|
|
- bool sc_auth = true;
|
|
int ret;
|
|
|
|
tmp_ctx = talloc_new(NULL);
|
|
@@ -760,40 +799,17 @@ generate_json_auth_message(struct confdb_ctx *cdb,
|
|
return ENOMEM;
|
|
}
|
|
|
|
- ret = obtain_prompts(cdb, tmp_ctx, pc_list, &password_prompt,
|
|
- &oauth2_init_prompt, &oauth2_link_prompt,
|
|
- &sc_init_prompt, &sc_pin_prompt);
|
|
- if (ret != EOK) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE, "Failure to obtain the prompts.\n");
|
|
- goto done;
|
|
- }
|
|
-
|
|
- ret = obtain_oauth2_data(tmp_ctx, _pd, &oauth2_uri, &oauth2_code);
|
|
- if (ret == ENOENT) {
|
|
- oauth2_auth = false;
|
|
- } else if (ret != EOK) {
|
|
- goto done;
|
|
- }
|
|
-
|
|
- ret = get_cert_list(tmp_ctx, _pd, &cert_list);
|
|
- if (ret == ENOENT) {
|
|
- sc_auth = false;
|
|
- } else if (ret != EOK) {
|
|
- goto done;
|
|
- }
|
|
-
|
|
- ret = get_cert_names(tmp_ctx, cert_list, &sc_names);
|
|
+ ret = init_auth_data(tmp_ctx, cdb, pc_list, _pd, &auth_data);
|
|
if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "Failed to initialize authentication data.\n");
|
|
goto done;
|
|
}
|
|
|
|
- ret = json_format_auth_selection(tmp_ctx, true, password_prompt,
|
|
- oauth2_auth, oauth2_uri, oauth2_code,
|
|
- oauth2_init_prompt, oauth2_link_prompt,
|
|
- sc_auth, sc_names,
|
|
- sc_init_prompt, sc_pin_prompt,
|
|
- &result);
|
|
+ ret = json_format_auth_selection(tmp_ctx, auth_data, &result);
|
|
if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "Failed to format JSON message.\n");
|
|
goto done;
|
|
}
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.h b/src/responder/pam/pamsrv_json.h
|
|
index 2441510d0..2c8949261 100644
|
|
--- a/src/responder/pam/pamsrv_json.h
|
|
+++ b/src/responder/pam/pamsrv_json.h
|
|
@@ -37,6 +37,33 @@
|
|
#define SC_PIN_PROMPT "PIN"
|
|
|
|
|
|
+struct auth_data {
|
|
+ struct password_data *pswd;
|
|
+ struct oauth2_data *oauth2;
|
|
+ struct sc_data *sc;
|
|
+};
|
|
+
|
|
+struct password_data {
|
|
+ bool enabled;
|
|
+ char *prompt;
|
|
+};
|
|
+
|
|
+struct oauth2_data {
|
|
+ bool enabled;
|
|
+ char *uri;
|
|
+ char *code;
|
|
+ char *init_prompt;
|
|
+ char *link_prompt;
|
|
+};
|
|
+
|
|
+struct sc_data {
|
|
+ bool enabled;
|
|
+ char **names;
|
|
+ char *init_prompt;
|
|
+ char *pin_prompt;
|
|
+};
|
|
+
|
|
+
|
|
/**
|
|
* @brief Extract smartcard certificate list from pam_data structure
|
|
*
|
|
@@ -56,89 +83,55 @@ get_cert_list(TALLOC_CTX *mem_ctx, struct pam_data *pd,
|
|
*
|
|
* @param[in] mem_ctx Memory context
|
|
* @param[in] cert_list Certificate list
|
|
- * @param[out] _names Certificate names list
|
|
+ * @param[out] _auth_data Structure containing the data from all available
|
|
+ * authentication mechanisms
|
|
*
|
|
* @return 0 if the data was extracted successfully,
|
|
* error code otherwise.
|
|
*/
|
|
errno_t
|
|
get_cert_names(TALLOC_CTX *mem_ctx, struct cert_auth_info *cert_list,
|
|
- char ***_names);
|
|
+ struct auth_data *_auth_data);
|
|
|
|
/**
|
|
* @brief Format authentication mechanisms to JSON
|
|
*
|
|
- * @param[in] password_auth Whether password authentication is allowed
|
|
- * @param[in] password_prompt Password prompt
|
|
- * @param[in] oath2_auth Whether OAUTH2 authentication is allowed
|
|
- * @param[in] uri OAUTH2 uri
|
|
- * @param[in] code OAUTH2 code
|
|
- * @param[in] oauth2_init_prompt OAUTH2 initial prompt
|
|
- * @param[in] oauth2_link_prompt OAUTH2 link prompt
|
|
- * @param[in] sc_auth Whether smartcard authentication is allowed
|
|
- * @param[in] sc_names smartcard names
|
|
- * @param[in] sc_init_prompt smartcard initial prompt
|
|
- * @param[in] sc_pin_prompt smartcard PIN prompt
|
|
+ * @param[in] auth_data Structure containing the data from all available
|
|
+ * authentication mechanisms
|
|
* @param[out] _list_mech authentication mechanisms JSON object
|
|
*
|
|
* @return 0 if the authentication mechanisms were formatted properly,
|
|
* error code otherwise.
|
|
*/
|
|
errno_t
|
|
-json_format_mechanisms(bool password_auth, const char *password_prompt,
|
|
- bool oauth2_auth, const char *uri, const char *code,
|
|
- const char *oauth2_init_prompt,
|
|
- const char *oauth2_link_prompt,
|
|
- bool sc_auth, char **sc_names,
|
|
- const char *sc_init_prompt,
|
|
- const char *sc_pin_prompt,
|
|
- json_t **_list_mech);
|
|
+json_format_mechanisms(struct auth_data *auth_data, json_t **_list_mech);
|
|
|
|
/**
|
|
* @brief Format priority to JSON
|
|
*
|
|
- * @param[in] password_auth Whether password authentication is allowed
|
|
- * @param[in] oath2_auth Whether OAUTH2 authentication is allowed
|
|
- * @param[in] sc_auth Whether smartcard authentication is allowed
|
|
- * @param[in] sc_names Smartcard certificate names
|
|
+ * @param[in] auth_data Structure containing the data from all available
|
|
+ * authentication mechanisms
|
|
* @param[out] _priority priority JSON object
|
|
*
|
|
* @return 0 if the priority was formatted properly,
|
|
* error code otherwise.
|
|
*/
|
|
errno_t
|
|
-json_format_priority(bool password_auth, bool oauth2_auth, bool sc_auth,
|
|
- char **sc_names, json_t **_priority);
|
|
+json_format_priority(struct auth_data *auth_data, json_t **_priority);
|
|
|
|
/**
|
|
* @brief Format data to JSON
|
|
*
|
|
* @param[in] mem_ctx Memory context
|
|
- * @param[in] password_auth Whether password authentication is allowed
|
|
- * @param[in] password_prompt Password prompt
|
|
- * @param[in] oath2_auth Whether OAUTH2 authentication is allowed
|
|
- * @param[in] uri OAUTH2 uri
|
|
- * @param[in] code OAUTH2 code
|
|
- * @param[in] oauth2_init_prompt OAUTH2 initial prompt
|
|
- * @param[in] oauth2_link_prompt OAUTH2 link prompt
|
|
- * @param[in] sc_auth Whether smartcard authentication is allowed
|
|
- * @param[in] sc_names smartcard names
|
|
- * @param[in] sc_init_prompt smartcard initial prompt
|
|
- * @param[in] sc_pin_prompt smartcard PIN prompt
|
|
+ * @param[in] auth_data Structure containing the data from all available
|
|
+ * authentication mechanisms
|
|
* @param[out] _result JSON message
|
|
*
|
|
* @return 0 if the JSON message was formatted properly,
|
|
* error code otherwise.
|
|
*/
|
|
errno_t
|
|
-json_format_auth_selection(TALLOC_CTX *mem_ctx,
|
|
- bool password_auth, const char *password_prompt,
|
|
- bool oath2_auth, const char *uri, const char *code,
|
|
- const char *oauth2_init_prompt,
|
|
- const char *oauth2_link_prompt,
|
|
- bool sc_auth, char **sc_names,
|
|
- const char *sc_init_prompt,
|
|
- const char *sc_pin_prompt,
|
|
+json_format_auth_selection(TALLOC_CTX *mem_ctx, struct auth_data *auth_data,
|
|
char **_result);
|
|
|
|
/**
|
|
diff --git a/src/tests/cmocka/test_pamsrv_json.c b/src/tests/cmocka/test_pamsrv_json.c
|
|
index 2d4d6e4e1..6dc85e384 100644
|
|
--- a/src/tests/cmocka/test_pamsrv_json.c
|
|
+++ b/src/tests/cmocka/test_pamsrv_json.c
|
|
@@ -125,6 +125,47 @@ struct cert_auth_info {
|
|
struct cert_auth_info *next;
|
|
};
|
|
|
|
+/***********************
|
|
+ * SETUP AND TEARDOWN
|
|
+ **********************/
|
|
+static int setup(void **state)
|
|
+{
|
|
+ struct auth_data *auth_data = NULL;
|
|
+
|
|
+ assert_true(leak_check_setup());
|
|
+
|
|
+ auth_data = talloc_zero(global_talloc_context, struct auth_data);
|
|
+ assert_non_null(auth_data);
|
|
+ auth_data->pswd = talloc_zero(auth_data, struct password_data);
|
|
+ assert_non_null(auth_data->pswd);
|
|
+ auth_data->oauth2 = talloc_zero(auth_data, struct oauth2_data);
|
|
+ assert_non_null(auth_data->oauth2);
|
|
+ auth_data->sc = talloc_zero(auth_data, struct sc_data);
|
|
+ assert_non_null(auth_data->sc);
|
|
+ auth_data->sc->names = talloc_array(auth_data->sc, char *, 3);
|
|
+ assert_non_null(auth_data->sc->names);
|
|
+
|
|
+ auth_data->pswd->enabled = false;
|
|
+ auth_data->oauth2->enabled = false;
|
|
+ auth_data->sc->enabled = false;
|
|
+
|
|
+ check_leaks_push(auth_data);
|
|
+ *state = (void *)auth_data;
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static int teardown(void **state)
|
|
+{
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
+
|
|
+ assert_non_null(auth_data);
|
|
+ assert_true(check_leaks_pop(auth_data));
|
|
+ talloc_free(auth_data);
|
|
+ assert_true(leak_check_teardown());
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
/***********************
|
|
* WRAPPERS
|
|
**********************/
|
|
@@ -198,11 +239,13 @@ void test_get_cert_list(void **state)
|
|
void test_get_cert_names(void **state)
|
|
{
|
|
TALLOC_CTX *test_ctx = NULL;
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
struct cert_auth_info *cert_list = NULL;
|
|
struct cert_auth_info *cai = NULL;
|
|
- char **names = NULL;
|
|
int ret;
|
|
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
cai = talloc_zero(test_ctx, struct cert_auth_info);
|
|
assert_non_null(cai);
|
|
cai->prompt_str = discard_const(SC1_PROMPT_STR);
|
|
@@ -212,70 +255,72 @@ void test_get_cert_names(void **state)
|
|
cai->prompt_str = discard_const(SC2_PROMPT_STR);
|
|
DLIST_ADD(cert_list, cai);
|
|
|
|
- ret = get_cert_names(test_ctx, cert_list, &names);
|
|
+ ret = get_cert_names(test_ctx, cert_list, auth_data);
|
|
assert_int_equal(ret, EOK);
|
|
- assert_string_equal(names[0], SC2_PROMPT_STR);
|
|
- assert_string_equal(names[1], SC1_PROMPT_STR);
|
|
+ assert_string_equal(auth_data->sc->names[0], SC2_PROMPT_STR);
|
|
+ assert_string_equal(auth_data->sc->names[1], SC1_PROMPT_STR);
|
|
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
void test_json_format_mechanisms_password(void **state)
|
|
{
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
json_t *mechs = NULL;
|
|
char *string;
|
|
int ret;
|
|
|
|
- ret = json_format_mechanisms(true, PASSWORD_PROMPT,
|
|
- false, NULL, NULL, NULL, NULL,
|
|
- false, NULL, NULL, NULL,
|
|
- &mechs);
|
|
+ auth_data->pswd->enabled = true;
|
|
+ auth_data->pswd->prompt = discard_const(PASSWORD_PROMPT);
|
|
+
|
|
+ ret = json_format_mechanisms(auth_data, &mechs);
|
|
assert_int_equal(ret, EOK);
|
|
|
|
string = json_dumps(mechs, 0);
|
|
assert_string_equal(string, MECHANISMS_PASSWORD);
|
|
+
|
|
json_decref(mechs);
|
|
free(string);
|
|
}
|
|
|
|
void test_json_format_mechanisms_oauth2(void **state)
|
|
{
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
json_t *mechs = NULL;
|
|
char *string;
|
|
int ret;
|
|
|
|
- ret = json_format_mechanisms(false, NULL, true, OAUTH2_URI, OAUTH2_CODE,
|
|
- OAUTH2_INIT_PROMPT, OAUTH2_LINK_PROMPT,
|
|
- false, NULL, NULL, NULL,
|
|
- &mechs);
|
|
+ auth_data->oauth2->enabled = true;
|
|
+ auth_data->oauth2->uri = discard_const(OAUTH2_URI);
|
|
+ auth_data->oauth2->code = discard_const(OAUTH2_CODE);
|
|
+ auth_data->oauth2->init_prompt = discard_const(OAUTH2_INIT_PROMPT);
|
|
+ auth_data->oauth2->link_prompt = discard_const(OAUTH2_LINK_PROMPT);
|
|
+
|
|
+ ret = json_format_mechanisms(auth_data, &mechs);
|
|
assert_int_equal(ret, EOK);
|
|
|
|
string = json_dumps(mechs, 0);
|
|
assert_string_equal(string, MECHANISMS_OAUTH2);
|
|
+
|
|
json_decref(mechs);
|
|
free(string);
|
|
}
|
|
|
|
void test_json_format_mechanisms_sc1(void **state)
|
|
{
|
|
- TALLOC_CTX *test_ctx = NULL;
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
json_t *mechs = NULL;
|
|
- char **sc_names = NULL;
|
|
char *string;
|
|
int ret;
|
|
|
|
- test_ctx = talloc_new(NULL);
|
|
- assert_non_null(test_ctx);
|
|
- sc_names = talloc_array(test_ctx, char *, 2);
|
|
- assert_non_null(sc_names);
|
|
- sc_names[0] = talloc_strdup(sc_names, SC1_PROMPT_STR);
|
|
- assert_non_null(sc_names[0]);
|
|
- sc_names[1] = NULL;
|
|
-
|
|
- ret = json_format_mechanisms(false, NULL,
|
|
- false, NULL, NULL, NULL, NULL,
|
|
- true, sc_names, SC_INIT_PROMPT, SC_PIN_PROMPT,
|
|
- &mechs);
|
|
+ auth_data->sc->enabled = true;
|
|
+ auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->names[0]);
|
|
+ auth_data->sc->names[1] = NULL;
|
|
+ auth_data->sc->init_prompt = discard_const(SC_INIT_PROMPT);
|
|
+ auth_data->sc->pin_prompt = discard_const(SC_PIN_PROMPT);
|
|
+
|
|
+ ret = json_format_mechanisms(auth_data, &mechs);
|
|
assert_int_equal(ret, EOK);
|
|
|
|
string = json_dumps(mechs, 0);
|
|
@@ -283,31 +328,26 @@ void test_json_format_mechanisms_sc1(void **state)
|
|
|
|
json_decref(mechs);
|
|
free(string);
|
|
- talloc_free(test_ctx);
|
|
+ talloc_free(auth_data->sc->names[0]);
|
|
}
|
|
|
|
void test_json_format_mechanisms_sc2(void **state)
|
|
{
|
|
- TALLOC_CTX *test_ctx = NULL;
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
json_t *mechs = NULL;
|
|
- char **sc_names = NULL;
|
|
char *string;
|
|
int ret;
|
|
|
|
- test_ctx = talloc_new(NULL);
|
|
- assert_non_null(test_ctx);
|
|
- sc_names = talloc_array(test_ctx, char *, 3);
|
|
- assert_non_null(sc_names);
|
|
- sc_names[0] = talloc_strdup(sc_names, SC1_PROMPT_STR);
|
|
- assert_non_null(sc_names[0]);
|
|
- sc_names[1] = talloc_strdup(sc_names, SC2_PROMPT_STR);
|
|
- assert_non_null(sc_names[1]);
|
|
- sc_names[2] = NULL;
|
|
-
|
|
- ret = json_format_mechanisms(false, NULL,
|
|
- false, NULL, NULL, NULL, NULL,
|
|
- true, sc_names, SC_INIT_PROMPT, SC_PIN_PROMPT,
|
|
- &mechs);
|
|
+ auth_data->sc->enabled = true;
|
|
+ auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->names[0]);
|
|
+ auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->names[1]);
|
|
+ auth_data->sc->names[2] = NULL;
|
|
+ auth_data->sc->init_prompt = discard_const(SC_INIT_PROMPT);
|
|
+ auth_data->sc->pin_prompt = discard_const(SC_PIN_PROMPT);
|
|
+
|
|
+ ret = json_format_mechanisms(auth_data, &mechs);
|
|
assert_int_equal(ret, EOK);
|
|
|
|
string = json_dumps(mechs, 0);
|
|
@@ -315,32 +355,31 @@ void test_json_format_mechanisms_sc2(void **state)
|
|
|
|
json_decref(mechs);
|
|
free(string);
|
|
- talloc_free(test_ctx);
|
|
+ talloc_free(auth_data->sc->names[0]);
|
|
+ talloc_free(auth_data->sc->names[1]);
|
|
}
|
|
|
|
void test_json_format_priority_all(void **state)
|
|
{
|
|
- TALLOC_CTX *test_ctx = NULL;
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
json_t *priority = NULL;
|
|
- char **sc_names = NULL;
|
|
char *string;
|
|
int ret;
|
|
|
|
- test_ctx = talloc_new(NULL);
|
|
- assert_non_null(test_ctx);
|
|
- sc_names = talloc_array(test_ctx, char *, 3);
|
|
- assert_non_null(sc_names);
|
|
- sc_names[0] = talloc_strdup(sc_names, SC1_LABEL);
|
|
- assert_non_null(sc_names[0]);
|
|
- sc_names[1] = talloc_strdup(sc_names, SC2_LABEL);
|
|
- assert_non_null(sc_names[1]);
|
|
- sc_names[2] = NULL;
|
|
+ auth_data->pswd->enabled = true;
|
|
+ auth_data->oauth2->enabled = true;
|
|
+ auth_data->sc->enabled = true;
|
|
+ auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_LABEL);
|
|
+ assert_non_null(auth_data->sc->names[0]);
|
|
+ auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_LABEL);
|
|
+ assert_non_null(auth_data->sc->names[1]);
|
|
+ auth_data->sc->names[2] = NULL;
|
|
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
- ret = json_format_priority(true, true, true, sc_names, &priority);
|
|
+ ret = json_format_priority(auth_data, &priority);
|
|
assert_int_equal(ret, EOK);
|
|
|
|
string = json_dumps(priority, 0);
|
|
@@ -348,23 +387,24 @@ void test_json_format_priority_all(void **state)
|
|
|
|
json_decref(priority);
|
|
free(string);
|
|
- talloc_free(test_ctx);
|
|
+ talloc_free(auth_data->sc->names[0]);
|
|
+ talloc_free(auth_data->sc->names[1]);
|
|
}
|
|
|
|
void test_json_format_auth_selection_password(void **state)
|
|
{
|
|
- TALLOC_CTX *test_ctx;
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
char *json_msg = NULL;
|
|
int ret;
|
|
|
|
test_ctx = talloc_new(NULL);
|
|
assert_non_null(test_ctx);
|
|
+ auth_data->pswd->enabled = true;
|
|
+ auth_data->pswd->prompt = discard_const(PASSWORD_PROMPT);
|
|
|
|
will_return(__wrap_json_array_append_new, false);
|
|
- ret = json_format_auth_selection(test_ctx, true, PASSWORD_PROMPT,
|
|
- false, NULL, NULL, NULL, NULL,
|
|
- false, NULL, NULL, NULL,
|
|
- &json_msg);
|
|
+ ret = json_format_auth_selection(test_ctx, auth_data, &json_msg);
|
|
assert_int_equal(ret, EOK);
|
|
assert_string_equal(json_msg, AUTH_SELECTION_PASSWORD);
|
|
|
|
@@ -373,19 +413,21 @@ void test_json_format_auth_selection_password(void **state)
|
|
|
|
void test_json_format_auth_selection_oauth2(void **state)
|
|
{
|
|
- TALLOC_CTX *test_ctx;
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
char *json_msg = NULL;
|
|
int ret;
|
|
|
|
test_ctx = talloc_new(NULL);
|
|
assert_non_null(test_ctx);
|
|
+ auth_data->oauth2->enabled = true;
|
|
+ auth_data->oauth2->uri = discard_const(OAUTH2_URI);
|
|
+ auth_data->oauth2->code = discard_const(OAUTH2_CODE);
|
|
+ auth_data->oauth2->init_prompt = discard_const(OAUTH2_INIT_PROMPT);
|
|
+ auth_data->oauth2->link_prompt = discard_const(OAUTH2_LINK_PROMPT);
|
|
|
|
will_return(__wrap_json_array_append_new, false);
|
|
- ret = json_format_auth_selection(test_ctx, false, NULL,
|
|
- true, OAUTH2_URI, OAUTH2_CODE,
|
|
- OAUTH2_INIT_PROMPT, OAUTH2_LINK_PROMPT,
|
|
- false, NULL, NULL, NULL,
|
|
- &json_msg);
|
|
+ ret = json_format_auth_selection(test_ctx, auth_data, &json_msg);
|
|
assert_int_equal(ret, EOK);
|
|
assert_string_equal(json_msg, AUTH_SELECTION_OAUTH2);
|
|
|
|
@@ -394,100 +436,111 @@ void test_json_format_auth_selection_oauth2(void **state)
|
|
|
|
void test_json_format_auth_selection_sc(void **state)
|
|
{
|
|
- TALLOC_CTX *test_ctx;
|
|
- char **sc_names = NULL;
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
char *json_msg = NULL;
|
|
int ret;
|
|
|
|
test_ctx = talloc_new(NULL);
|
|
assert_non_null(test_ctx);
|
|
- sc_names = talloc_array(test_ctx, char *, 3);
|
|
- assert_non_null(sc_names);
|
|
- sc_names[0] = talloc_strdup(sc_names, SC1_PROMPT_STR);
|
|
- assert_non_null(sc_names[0]);
|
|
- sc_names[1] = talloc_strdup(sc_names, SC2_PROMPT_STR);
|
|
- assert_non_null(sc_names[1]);
|
|
- sc_names[2] = NULL;
|
|
+ auth_data->sc->enabled = true;
|
|
+ auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->names[0]);
|
|
+ auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->names[1]);
|
|
+ auth_data->sc->names[2] = NULL;
|
|
+ auth_data->sc->init_prompt = discard_const(SC_INIT_PROMPT);
|
|
+ auth_data->sc->pin_prompt = discard_const(SC_PIN_PROMPT);
|
|
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
- ret = json_format_auth_selection(test_ctx, false, NULL,
|
|
- false, NULL, NULL, NULL, NULL,
|
|
- true, sc_names,
|
|
- SC_INIT_PROMPT, SC_PIN_PROMPT,
|
|
- &json_msg);
|
|
+ ret = json_format_auth_selection(test_ctx, auth_data, &json_msg);
|
|
assert_int_equal(ret, EOK);
|
|
assert_string_equal(json_msg, AUTH_SELECTION_SC);
|
|
|
|
+ talloc_free(auth_data->sc->names[0]);
|
|
+ talloc_free(auth_data->sc->names[1]);
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
void test_json_format_auth_selection_all(void **state)
|
|
{
|
|
- TALLOC_CTX *test_ctx;
|
|
- char **sc_names = NULL;
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
char *json_msg = NULL;
|
|
int ret;
|
|
|
|
test_ctx = talloc_new(NULL);
|
|
assert_non_null(test_ctx);
|
|
- sc_names = talloc_array(test_ctx, char *, 3);
|
|
- assert_non_null(sc_names);
|
|
- sc_names[0] = talloc_strdup(sc_names, SC1_PROMPT_STR);
|
|
- assert_non_null(sc_names[0]);
|
|
- sc_names[1] = talloc_strdup(sc_names, SC2_PROMPT_STR);
|
|
- assert_non_null(sc_names[1]);
|
|
- sc_names[2] = NULL;
|
|
+ auth_data->pswd->enabled = true;
|
|
+ auth_data->pswd->prompt = discard_const(PASSWORD_PROMPT);
|
|
+ auth_data->oauth2->enabled = true;
|
|
+ auth_data->oauth2->uri = discard_const(OAUTH2_URI);
|
|
+ auth_data->oauth2->code = discard_const(OAUTH2_CODE);
|
|
+ auth_data->oauth2->init_prompt = discard_const(OAUTH2_INIT_PROMPT);
|
|
+ auth_data->oauth2->link_prompt = discard_const(OAUTH2_LINK_PROMPT);
|
|
+ auth_data->sc->enabled = true;
|
|
+ auth_data->sc->names = talloc_array(test_ctx, char *, 3);
|
|
+ assert_non_null(auth_data->sc->names);
|
|
+ auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->names[0]);
|
|
+ auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->names[1]);
|
|
+ auth_data->sc->names[2] = NULL;
|
|
+ auth_data->sc->init_prompt = discard_const(SC_INIT_PROMPT);
|
|
+ auth_data->sc->pin_prompt = discard_const(SC_PIN_PROMPT);
|
|
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
- ret = json_format_auth_selection(test_ctx, true, PASSWORD_PROMPT,
|
|
- true, OAUTH2_URI, OAUTH2_CODE,
|
|
- OAUTH2_INIT_PROMPT, OAUTH2_LINK_PROMPT,
|
|
- true, sc_names,
|
|
- SC_INIT_PROMPT, SC_PIN_PROMPT,
|
|
- &json_msg);
|
|
+ ret = json_format_auth_selection(test_ctx, auth_data, &json_msg);
|
|
assert_int_equal(ret, EOK);
|
|
assert_string_equal(json_msg, AUTH_SELECTION_ALL);
|
|
|
|
+ talloc_free(auth_data->sc->names[0]);
|
|
+ talloc_free(auth_data->sc->names[1]);
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
void test_json_format_auth_selection_failure(void **state)
|
|
{
|
|
- TALLOC_CTX *test_ctx;
|
|
- char **sc_names = NULL;
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
char *json_msg = NULL;
|
|
int ret;
|
|
|
|
test_ctx = talloc_new(NULL);
|
|
assert_non_null(test_ctx);
|
|
- sc_names = talloc_array(test_ctx, char *, 3);
|
|
- assert_non_null(sc_names);
|
|
- sc_names[0] = talloc_strdup(sc_names, SC1_LABEL);
|
|
- assert_non_null(sc_names[0]);
|
|
- sc_names[1] = talloc_strdup(sc_names, SC2_LABEL);
|
|
- assert_non_null(sc_names[1]);
|
|
- sc_names[2] = NULL;
|
|
+ auth_data->pswd->enabled = true;
|
|
+ auth_data->pswd->prompt = discard_const(PASSWORD_PROMPT);
|
|
+ auth_data->oauth2->enabled = true;
|
|
+ auth_data->oauth2->uri = discard_const(OAUTH2_URI);
|
|
+ auth_data->oauth2->code = discard_const(OAUTH2_CODE);
|
|
+ auth_data->oauth2->init_prompt = discard_const(OAUTH2_INIT_PROMPT);
|
|
+ auth_data->oauth2->link_prompt = discard_const(OAUTH2_LINK_PROMPT);
|
|
+ auth_data->sc->enabled = true;
|
|
+ auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_LABEL);
|
|
+ assert_non_null(auth_data->sc->names[0]);
|
|
+ auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_LABEL);
|
|
+ assert_non_null(auth_data->sc->names[1]);
|
|
+ auth_data->sc->names[2] = NULL;
|
|
+ auth_data->sc->init_prompt = discard_const(SC_INIT_PROMPT);
|
|
+ auth_data->sc->pin_prompt = discard_const(SC_PIN_PROMPT);
|
|
|
|
will_return(__wrap_json_array_append_new, true);
|
|
- ret = json_format_auth_selection(test_ctx, true, PASSWORD_PROMPT,
|
|
- true, OAUTH2_URI, OAUTH2_CODE,
|
|
- OAUTH2_INIT_PROMPT, OAUTH2_LINK_PROMPT,
|
|
- true, sc_names,
|
|
- SC_INIT_PROMPT, SC_PIN_PROMPT,
|
|
- &json_msg);
|
|
+ ret = json_format_auth_selection(test_ctx, auth_data, &json_msg);
|
|
assert_int_equal(ret, ENOMEM);
|
|
assert_null(json_msg);
|
|
|
|
+ talloc_free(auth_data->sc->names[0]);
|
|
+ talloc_free(auth_data->sc->names[1]);
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
void test_generate_json_message_integration(void **state)
|
|
{
|
|
- TALLOC_CTX *test_ctx;
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
struct pam_data *pd = NULL;
|
|
struct prompt_config **pc_list = NULL;
|
|
int len;
|
|
@@ -787,17 +840,17 @@ int main(int argc, const char *argv[])
|
|
|
|
const struct CMUnitTest tests[] = {
|
|
cmocka_unit_test(test_get_cert_list),
|
|
- cmocka_unit_test(test_get_cert_names),
|
|
- cmocka_unit_test(test_json_format_mechanisms_password),
|
|
- cmocka_unit_test(test_json_format_mechanisms_oauth2),
|
|
- cmocka_unit_test(test_json_format_mechanisms_sc1),
|
|
- cmocka_unit_test(test_json_format_mechanisms_sc2),
|
|
- cmocka_unit_test(test_json_format_priority_all),
|
|
- cmocka_unit_test(test_json_format_auth_selection_password),
|
|
- cmocka_unit_test(test_json_format_auth_selection_oauth2),
|
|
- cmocka_unit_test(test_json_format_auth_selection_sc),
|
|
- cmocka_unit_test(test_json_format_auth_selection_all),
|
|
- cmocka_unit_test(test_json_format_auth_selection_failure),
|
|
+ cmocka_unit_test_setup_teardown(test_get_cert_names, setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_json_format_mechanisms_password, setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_json_format_mechanisms_oauth2, setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_json_format_mechanisms_sc1, setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_json_format_mechanisms_sc2, setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_json_format_priority_all, setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_json_format_auth_selection_password, setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_json_format_auth_selection_oauth2, setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_json_format_auth_selection_sc, setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_json_format_auth_selection_all, setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_json_format_auth_selection_failure, setup, teardown),
|
|
cmocka_unit_test(test_generate_json_message_integration),
|
|
cmocka_unit_test(test_json_unpack_password_ok),
|
|
cmocka_unit_test(test_json_unpack_sc_ok),
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 3b56743dab76b7afa6a6e0e997aac402b688afac Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Wed, 29 Jan 2025 10:42:40 +0100
|
|
Subject: [PATCH 19/36] Responder: extend smartcard JSON request message
|
|
|
|
Include the certificate data in the JSON messages to set it in the
|
|
authtok structure more easily.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_json.c | 172 ++++++++++++++++------
|
|
src/responder/pam/pamsrv_json.h | 8 +-
|
|
src/tests/cmocka/test_pamsrv_json.c | 219 +++++++++++++++++++++++-----
|
|
3 files changed, 312 insertions(+), 87 deletions(-)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
index 286a3fd50..e28051060 100644
|
|
--- a/src/responder/pam/pamsrv_json.c
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -488,7 +488,7 @@ init_auth_data(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
|
|
goto done;
|
|
}
|
|
|
|
- ret = get_cert_names(mem_ctx, cert_list, *_auth_data);
|
|
+ ret = get_cert_data(mem_ctx, cert_list, *_auth_data);
|
|
if (ret != EOK) {
|
|
DEBUG(SSSDBG_CRIT_FAILURE, "Failure to obtain smartcard labels.\n");
|
|
goto done;
|
|
@@ -499,12 +499,16 @@ done:
|
|
}
|
|
|
|
errno_t
|
|
-get_cert_names(TALLOC_CTX *mem_ctx, struct cert_auth_info *cert_list,
|
|
+get_cert_data(TALLOC_CTX *mem_ctx, struct cert_auth_info *cert_list,
|
|
struct auth_data *_auth_data)
|
|
{
|
|
TALLOC_CTX *tmp_ctx = NULL;
|
|
struct cert_auth_info *item = NULL;
|
|
char **names = NULL;
|
|
+ char **cert_instructions = NULL;
|
|
+ char **module_names = NULL;
|
|
+ char **key_ids = NULL;
|
|
+ char **labels = NULL;
|
|
int i = 0;
|
|
int ret;
|
|
|
|
@@ -526,19 +530,83 @@ get_cert_names(TALLOC_CTX *mem_ctx, struct cert_auth_info *cert_list,
|
|
goto done;
|
|
}
|
|
|
|
+ cert_instructions = talloc_array(tmp_ctx, char *, i+1);
|
|
+ if (cert_instructions == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ module_names = talloc_array(tmp_ctx, char *, i+1);
|
|
+ if (module_names == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ key_ids = talloc_array(tmp_ctx, char *, i+1);
|
|
+ if (key_ids == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ labels = talloc_array(tmp_ctx, char *, i+1);
|
|
+ if (labels == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
i = 0;
|
|
DLIST_FOR_EACH(item, cert_list) {
|
|
- names[i] = talloc_strdup(names, item->prompt_str);
|
|
+ names[i] = talloc_strdup(names, item->token_name);
|
|
if (names[i] == NULL) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
|
|
ret = ENOMEM;
|
|
goto done;
|
|
}
|
|
+
|
|
+ cert_instructions[i] = talloc_strdup(names, item->prompt_str);
|
|
+ if (cert_instructions[i] == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ module_names[i] = talloc_strdup(names, item->module_name);
|
|
+ if (module_names[i] == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ key_ids[i] = talloc_strdup(names, item->key_id);
|
|
+ if (key_ids[i] == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ labels[i] = talloc_strdup(names, item->label);
|
|
+ if (labels[i] == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
i++;
|
|
}
|
|
names[i] = NULL;
|
|
+ cert_instructions[i] = NULL;
|
|
+ module_names[i] = NULL;
|
|
+ key_ids[i] = NULL;
|
|
+ labels[i] = NULL;
|
|
|
|
_auth_data->sc->names = talloc_steal(mem_ctx, names);
|
|
+ _auth_data->sc->cert_instructions = talloc_steal(mem_ctx, cert_instructions);
|
|
+ _auth_data->sc->module_names = talloc_steal(mem_ctx, module_names);
|
|
+ _auth_data->sc->key_ids = talloc_steal(mem_ctx, key_ids);
|
|
+ _auth_data->sc->labels = talloc_steal(mem_ctx, labels);
|
|
ret = EOK;
|
|
|
|
done:
|
|
@@ -553,8 +621,9 @@ json_format_mechanisms(struct auth_data *auth_data, json_t **_list_mech)
|
|
json_t *root = NULL;
|
|
json_t *json_pass = NULL;
|
|
json_t *json_oauth2 = NULL;
|
|
+ json_t *json_cert = NULL;
|
|
+ json_t *json_cert_array = NULL;
|
|
json_t *json_sc = NULL;
|
|
- char *key = NULL;
|
|
int ret;
|
|
|
|
root = json_object();
|
|
@@ -609,34 +678,52 @@ json_format_mechanisms(struct auth_data *auth_data, json_t **_list_mech)
|
|
}
|
|
|
|
if (auth_data->sc->enabled) {
|
|
+ json_cert_array = json_array();
|
|
+ if (json_cert_array == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_array failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
for (int i = 0; auth_data->sc->names[i] != NULL; i++) {
|
|
- json_sc = json_pack("{s:s,s:s,s:b,s:s,s:s}",
|
|
- "name", auth_data->sc->names[i],
|
|
- "role", "smartcard",
|
|
- "selectable", true,
|
|
- "init_instruction", auth_data->sc->init_prompt,
|
|
- "pin_prompt", auth_data->sc->pin_prompt);
|
|
- if (json_sc == NULL) {
|
|
+ json_cert = json_pack("{s:s,s:s,s:s,s:s,s:s,s:s}",
|
|
+ "tokenName", auth_data->sc->names[i],
|
|
+ "certInstruction", auth_data->sc->cert_instructions[i],
|
|
+ "pinPrompt", auth_data->sc->pin_prompt,
|
|
+ "moduleName", auth_data->sc->module_names[i],
|
|
+ "keyId", auth_data->sc->key_ids[i],
|
|
+ "label", auth_data->sc->labels[i]);
|
|
+ if (json_cert == NULL) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "json_pack failed.\n");
|
|
ret = ENOMEM;
|
|
goto done;
|
|
}
|
|
|
|
- ret = asprintf(&key, "smartcard:%d", i+1);
|
|
+ ret = json_array_append_new(json_cert_array, json_cert);
|
|
if (ret == -1) {
|
|
- DEBUG(SSSDBG_OP_FAILURE, "asprintf failed.\n");
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_array_append_new failed.\n");
|
|
+ json_decref(json_cert);
|
|
ret = ENOMEM;
|
|
goto done;
|
|
}
|
|
+ }
|
|
|
|
- ret = json_object_set_new(root, key, json_sc);
|
|
- if (ret == -1) {
|
|
- DEBUG(SSSDBG_OP_FAILURE, "json_array_append failed.\n");
|
|
- json_decref(json_pass);
|
|
- ret = ENOMEM;
|
|
- goto done;
|
|
- }
|
|
- free(key);
|
|
+ json_sc = json_pack("{s:s,s:s,s:o}",
|
|
+ "name", "Smartcard",
|
|
+ "role", "smartcard",
|
|
+ "certificates", json_cert_array);
|
|
+ if (json_sc == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_pack failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = json_object_set_new(root, "smartcard", json_sc);
|
|
+ if (ret == -1) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_object_set_new failed.\n");
|
|
+ json_decref(json_sc);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
}
|
|
}
|
|
|
|
@@ -646,6 +733,9 @@ json_format_mechanisms(struct auth_data *auth_data, json_t **_list_mech)
|
|
done:
|
|
if (ret != EOK) {
|
|
json_decref(root);
|
|
+ if (json_cert_array != NULL) {
|
|
+ json_decref(json_cert_array);
|
|
+ }
|
|
}
|
|
|
|
return ret;
|
|
@@ -656,7 +746,6 @@ json_format_priority(struct auth_data *auth_data, json_t **_priority)
|
|
{
|
|
json_t *root = NULL;
|
|
json_t *json_priority = NULL;
|
|
- char *key = NULL;
|
|
int ret;
|
|
|
|
root = json_array();
|
|
@@ -666,8 +755,8 @@ json_format_priority(struct auth_data *auth_data, json_t **_priority)
|
|
goto done;
|
|
}
|
|
|
|
- if (auth_data->oauth2->enabled) {
|
|
- json_priority = json_string("eidp");
|
|
+ if (auth_data->sc->enabled) {
|
|
+ json_priority = json_string("smartcard");
|
|
if (json_priority == NULL) {
|
|
DEBUG(SSSDBG_OP_FAILURE, "json_string failed.\n");
|
|
ret = ENOMEM;
|
|
@@ -682,28 +771,19 @@ json_format_priority(struct auth_data *auth_data, json_t **_priority)
|
|
}
|
|
}
|
|
|
|
- if (auth_data->sc->enabled) {
|
|
- for (int i = 0; auth_data->sc->names[i] != NULL; i++) {
|
|
- ret = asprintf(&key, "smartcard:%d", i+1);
|
|
- if (ret == -1) {
|
|
- DEBUG(SSSDBG_OP_FAILURE, "asprintf failed.\n");
|
|
- ret = ENOMEM;
|
|
- goto done;
|
|
- }
|
|
- json_priority = json_string(key);
|
|
- free(key);
|
|
- if (json_priority == NULL) {
|
|
- DEBUG(SSSDBG_OP_FAILURE, "json_string failed.\n");
|
|
- ret = ENOMEM;
|
|
- goto done;
|
|
- }
|
|
- ret = json_array_append_new(root, json_priority);
|
|
- if (ret == -1) {
|
|
- DEBUG(SSSDBG_OP_FAILURE, "json_array_append failed.\n");
|
|
- json_decref(json_priority);
|
|
- ret = ENOMEM;
|
|
- goto done;
|
|
- }
|
|
+ if (auth_data->oauth2->enabled) {
|
|
+ json_priority = json_string("eidp");
|
|
+ if (json_priority == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_string failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ ret = json_array_append_new(root, json_priority);
|
|
+ if (ret == -1) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_array_append failed.\n");
|
|
+ json_decref(json_priority);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
}
|
|
}
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.h b/src/responder/pam/pamsrv_json.h
|
|
index 2c8949261..ddc1c442a 100644
|
|
--- a/src/responder/pam/pamsrv_json.h
|
|
+++ b/src/responder/pam/pamsrv_json.h
|
|
@@ -60,7 +60,11 @@ struct sc_data {
|
|
bool enabled;
|
|
char **names;
|
|
char *init_prompt;
|
|
+ char **cert_instructions;
|
|
char *pin_prompt;
|
|
+ char **module_names;
|
|
+ char **key_ids;
|
|
+ char **labels;
|
|
};
|
|
|
|
|
|
@@ -79,7 +83,7 @@ get_cert_list(TALLOC_CTX *mem_ctx, struct pam_data *pd,
|
|
struct cert_auth_info **_cert_list);
|
|
|
|
/**
|
|
- * @brief Extract smartcard certificate name list from the certificate list
|
|
+ * @brief Extract smartcard certificate data from the certificate list
|
|
*
|
|
* @param[in] mem_ctx Memory context
|
|
* @param[in] cert_list Certificate list
|
|
@@ -90,7 +94,7 @@ get_cert_list(TALLOC_CTX *mem_ctx, struct pam_data *pd,
|
|
* error code otherwise.
|
|
*/
|
|
errno_t
|
|
-get_cert_names(TALLOC_CTX *mem_ctx, struct cert_auth_info *cert_list,
|
|
+get_cert_data(TALLOC_CTX *mem_ctx, struct cert_auth_info *cert_list,
|
|
struct auth_data *_auth_data);
|
|
|
|
/**
|
|
diff --git a/src/tests/cmocka/test_pamsrv_json.c b/src/tests/cmocka/test_pamsrv_json.c
|
|
index 6dc85e384..bb4d9c071 100644
|
|
--- a/src/tests/cmocka/test_pamsrv_json.c
|
|
+++ b/src/tests/cmocka/test_pamsrv_json.c
|
|
@@ -63,26 +63,35 @@
|
|
"\"linkPrompt\": \"" OAUTH2_LINK_PROMPT "\", " \
|
|
"\"uri\": \"short.url.com/tmp\", \"code\": \"1234-5678\", " \
|
|
"\"timeout\": 300}"
|
|
-#define BASIC_SC "\"smartcard:1\": {" \
|
|
- "\"name\": \"prompt1\", \"role\": \"smartcard\", " \
|
|
- "\"selectable\": true, " \
|
|
- "\"init_instruction\": \"Insert smartcard\", " \
|
|
- "\"pin_prompt\": \"PIN\"}"
|
|
-#define MULTIPLE_SC "\"smartcard:1\": {" \
|
|
- "\"name\": \"prompt1\", \"role\": \"smartcard\", " \
|
|
- "\"selectable\": true, " \
|
|
- "\"init_instruction\": \"Insert smartcard\", " \
|
|
- "\"pin_prompt\": \"PIN\"}, " \
|
|
- "\"smartcard:2\": {" \
|
|
- "\"name\": \"prompt2\", \"role\": \"smartcard\", " \
|
|
- "\"selectable\": true, " \
|
|
- "\"init_instruction\": \"Insert smartcard\", " \
|
|
- "\"pin_prompt\": \"PIN\"}"
|
|
+#define BASIC_SC "\"smartcard\": {" \
|
|
+ "\"name\": \"Smartcard\", \"role\": \"smartcard\", " \
|
|
+ "\"certificates\": [{" \
|
|
+ "\"tokenName\": \"token_name1\", " \
|
|
+ "\"certInstruction\": \"prompt1\", " \
|
|
+ "\"pinPrompt\": \"" SC_PIN_PROMPT "\", " \
|
|
+ "\"moduleName\": \"module_name1\", " \
|
|
+ "\"keyId\": \"key_id1\", " \
|
|
+ "\"label\": \"label1\"}]}"
|
|
+#define MULTIPLE_SC "\"smartcard\": {" \
|
|
+ "\"name\": \"Smartcard\", \"role\": \"smartcard\", " \
|
|
+ "\"certificates\": [{" \
|
|
+ "\"tokenName\": \"token_name1\", " \
|
|
+ "\"certInstruction\": \"prompt1\", " \
|
|
+ "\"pinPrompt\": \"" SC_PIN_PROMPT "\", " \
|
|
+ "\"moduleName\": \"module_name1\", " \
|
|
+ "\"keyId\": \"key_id1\", " \
|
|
+ "\"label\": \"label1\"}, {" \
|
|
+ "\"tokenName\": \"token_name2\", " \
|
|
+ "\"certInstruction\": \"prompt2\", " \
|
|
+ "\"pinPrompt\": \"" SC_PIN_PROMPT "\", " \
|
|
+ "\"moduleName\": \"module_name2\", " \
|
|
+ "\"keyId\": \"key_id2\", " \
|
|
+ "\"label\": \"label2\"}]}"
|
|
#define MECHANISMS_PASSWORD "{" BASIC_PASSWORD "}"
|
|
#define MECHANISMS_OAUTH2 "{" BASIC_OAUTH2 "}"
|
|
#define MECHANISMS_SC1 "{" BASIC_SC "}"
|
|
#define MECHANISMS_SC2 "{" MULTIPLE_SC "}"
|
|
-#define PRIORITY_ALL "[\"eidp\", \"smartcard:1\", \"smartcard:2\", \"password\"]"
|
|
+#define PRIORITY_ALL "[\"smartcard\", \"eidp\", \"password\"]"
|
|
#define AUTH_SELECTION_PASSWORD "{\"authSelection\": {\"mechanisms\": " \
|
|
MECHANISMS_PASSWORD ", " \
|
|
"\"priority\": [\"password\"]}}"
|
|
@@ -91,7 +100,7 @@
|
|
"\"priority\": [\"eidp\"]}}"
|
|
#define AUTH_SELECTION_SC "{\"authSelection\": {\"mechanisms\": " \
|
|
MECHANISMS_SC2 ", " \
|
|
- "\"priority\": [\"smartcard:1\", \"smartcard:2\"]}}"
|
|
+ "\"priority\": [\"smartcard\"]}}"
|
|
#define AUTH_SELECTION_ALL "{\"authSelection\": {\"mechanisms\": {" \
|
|
BASIC_PASSWORD ", " \
|
|
BASIC_OAUTH2 ", " \
|
|
@@ -144,6 +153,14 @@ static int setup(void **state)
|
|
assert_non_null(auth_data->sc);
|
|
auth_data->sc->names = talloc_array(auth_data->sc, char *, 3);
|
|
assert_non_null(auth_data->sc->names);
|
|
+ auth_data->sc->cert_instructions = talloc_array(auth_data->sc, char *, 3);
|
|
+ assert_non_null(auth_data->sc->cert_instructions);
|
|
+ auth_data->sc->module_names = talloc_array(auth_data->sc, char *, 3);
|
|
+ assert_non_null(auth_data->sc->module_names);
|
|
+ auth_data->sc->key_ids = talloc_array(auth_data->sc, char *, 3);
|
|
+ assert_non_null(auth_data->sc->key_ids);
|
|
+ auth_data->sc->labels = talloc_array(auth_data->sc, char *, 3);
|
|
+ assert_non_null(auth_data->sc->labels);
|
|
|
|
auth_data->pswd->enabled = false;
|
|
auth_data->oauth2->enabled = false;
|
|
@@ -236,7 +253,7 @@ void test_get_cert_list(void **state)
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
-void test_get_cert_names(void **state)
|
|
+void test_get_cert_data(void **state)
|
|
{
|
|
TALLOC_CTX *test_ctx = NULL;
|
|
struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
@@ -248,17 +265,33 @@ void test_get_cert_names(void **state)
|
|
assert_non_null(test_ctx);
|
|
cai = talloc_zero(test_ctx, struct cert_auth_info);
|
|
assert_non_null(cai);
|
|
+ cai->token_name = discard_const(SC1_TOKEN_NAME);
|
|
+ cai->module_name = discard_const(SC1_MODULE_NAME);
|
|
+ cai->key_id = discard_const(SC1_KEY_ID);
|
|
+ cai->label = discard_const(SC1_LABEL);
|
|
cai->prompt_str = discard_const(SC1_PROMPT_STR);
|
|
DLIST_ADD(cert_list, cai);
|
|
cai = talloc_zero(test_ctx, struct cert_auth_info);
|
|
assert_non_null(cai);
|
|
+ cai->token_name = discard_const(SC2_TOKEN_NAME);
|
|
+ cai->module_name = discard_const(SC2_MODULE_NAME);
|
|
+ cai->key_id = discard_const(SC2_KEY_ID);
|
|
+ cai->label = discard_const(SC2_LABEL);
|
|
cai->prompt_str = discard_const(SC2_PROMPT_STR);
|
|
DLIST_ADD(cert_list, cai);
|
|
|
|
- ret = get_cert_names(test_ctx, cert_list, auth_data);
|
|
+ ret = get_cert_data(test_ctx, cert_list, auth_data);
|
|
assert_int_equal(ret, EOK);
|
|
- assert_string_equal(auth_data->sc->names[0], SC2_PROMPT_STR);
|
|
- assert_string_equal(auth_data->sc->names[1], SC1_PROMPT_STR);
|
|
+ assert_string_equal(auth_data->sc->names[0], SC2_TOKEN_NAME);
|
|
+ assert_string_equal(auth_data->sc->module_names[0], SC2_MODULE_NAME);
|
|
+ assert_string_equal(auth_data->sc->key_ids[0], SC2_KEY_ID);
|
|
+ assert_string_equal(auth_data->sc->labels[0], SC2_LABEL);
|
|
+ assert_string_equal(auth_data->sc->cert_instructions[0], SC2_PROMPT_STR);
|
|
+ assert_string_equal(auth_data->sc->names[1], SC1_TOKEN_NAME);
|
|
+ assert_string_equal(auth_data->sc->module_names[1], SC1_MODULE_NAME);
|
|
+ assert_string_equal(auth_data->sc->key_ids[1], SC1_KEY_ID);
|
|
+ assert_string_equal(auth_data->sc->labels[1], SC1_LABEL);
|
|
+ assert_string_equal(auth_data->sc->cert_instructions[1], SC1_PROMPT_STR);
|
|
|
|
talloc_free(test_ctx);
|
|
}
|
|
@@ -314,12 +347,21 @@ void test_json_format_mechanisms_sc1(void **state)
|
|
int ret;
|
|
|
|
auth_data->sc->enabled = true;
|
|
- auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_PROMPT_STR);
|
|
+ auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_TOKEN_NAME);
|
|
assert_non_null(auth_data->sc->names[0]);
|
|
+ auth_data->sc->cert_instructions[0] = talloc_strdup(auth_data->sc->cert_instructions, SC1_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->cert_instructions[0]);
|
|
+ auth_data->sc->module_names[0] = talloc_strdup(auth_data->sc->module_names, SC1_MODULE_NAME);
|
|
+ assert_non_null(auth_data->sc->module_names[0]);
|
|
+ auth_data->sc->key_ids[0] = talloc_strdup(auth_data->sc->key_ids, SC1_KEY_ID);
|
|
+ assert_non_null(auth_data->sc->key_ids[0]);
|
|
+ auth_data->sc->labels[0] = talloc_strdup(auth_data->sc->labels, SC1_LABEL);
|
|
+ assert_non_null(auth_data->sc->labels[0]);
|
|
auth_data->sc->names[1] = NULL;
|
|
- auth_data->sc->init_prompt = discard_const(SC_INIT_PROMPT);
|
|
auth_data->sc->pin_prompt = discard_const(SC_PIN_PROMPT);
|
|
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+
|
|
ret = json_format_mechanisms(auth_data, &mechs);
|
|
assert_int_equal(ret, EOK);
|
|
|
|
@@ -329,6 +371,10 @@ void test_json_format_mechanisms_sc1(void **state)
|
|
json_decref(mechs);
|
|
free(string);
|
|
talloc_free(auth_data->sc->names[0]);
|
|
+ talloc_free(auth_data->sc->cert_instructions[0]);
|
|
+ talloc_free(auth_data->sc->module_names[0]);
|
|
+ talloc_free(auth_data->sc->key_ids[0]);
|
|
+ talloc_free(auth_data->sc->labels[0]);
|
|
}
|
|
|
|
void test_json_format_mechanisms_sc2(void **state)
|
|
@@ -339,14 +385,32 @@ void test_json_format_mechanisms_sc2(void **state)
|
|
int ret;
|
|
|
|
auth_data->sc->enabled = true;
|
|
- auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_PROMPT_STR);
|
|
+ auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_TOKEN_NAME);
|
|
assert_non_null(auth_data->sc->names[0]);
|
|
- auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_PROMPT_STR);
|
|
+ auth_data->sc->cert_instructions[0] = talloc_strdup(auth_data->sc->cert_instructions, SC1_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->cert_instructions[0]);
|
|
+ auth_data->sc->module_names[0] = talloc_strdup(auth_data->sc->module_names, SC1_MODULE_NAME);
|
|
+ assert_non_null(auth_data->sc->module_names[0]);
|
|
+ auth_data->sc->key_ids[0] = talloc_strdup(auth_data->sc->key_ids, SC1_KEY_ID);
|
|
+ assert_non_null(auth_data->sc->key_ids[0]);
|
|
+ auth_data->sc->labels[0] = talloc_strdup(auth_data->sc->labels, SC1_LABEL);
|
|
+ assert_non_null(auth_data->sc->labels[0]);
|
|
+ auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_TOKEN_NAME);
|
|
assert_non_null(auth_data->sc->names[1]);
|
|
+ auth_data->sc->cert_instructions[1] = talloc_strdup(auth_data->sc->cert_instructions, SC2_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->cert_instructions[1]);
|
|
+ auth_data->sc->module_names[1] = talloc_strdup(auth_data->sc->module_names, SC2_MODULE_NAME);
|
|
+ assert_non_null(auth_data->sc->module_names[1]);
|
|
+ auth_data->sc->key_ids[1] = talloc_strdup(auth_data->sc->key_ids, SC2_KEY_ID);
|
|
+ assert_non_null(auth_data->sc->key_ids[1]);
|
|
+ auth_data->sc->labels[1] = talloc_strdup(auth_data->sc->labels, SC2_LABEL);
|
|
+ assert_non_null(auth_data->sc->labels[1]);
|
|
auth_data->sc->names[2] = NULL;
|
|
- auth_data->sc->init_prompt = discard_const(SC_INIT_PROMPT);
|
|
auth_data->sc->pin_prompt = discard_const(SC_PIN_PROMPT);
|
|
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+
|
|
ret = json_format_mechanisms(auth_data, &mechs);
|
|
assert_int_equal(ret, EOK);
|
|
|
|
@@ -356,7 +420,15 @@ void test_json_format_mechanisms_sc2(void **state)
|
|
json_decref(mechs);
|
|
free(string);
|
|
talloc_free(auth_data->sc->names[0]);
|
|
+ talloc_free(auth_data->sc->cert_instructions[0]);
|
|
+ talloc_free(auth_data->sc->module_names[0]);
|
|
+ talloc_free(auth_data->sc->key_ids[0]);
|
|
+ talloc_free(auth_data->sc->labels[0]);
|
|
talloc_free(auth_data->sc->names[1]);
|
|
+ talloc_free(auth_data->sc->cert_instructions[1]);
|
|
+ talloc_free(auth_data->sc->module_names[1]);
|
|
+ talloc_free(auth_data->sc->key_ids[1]);
|
|
+ talloc_free(auth_data->sc->labels[1]);
|
|
}
|
|
|
|
void test_json_format_priority_all(void **state)
|
|
@@ -378,7 +450,6 @@ void test_json_format_priority_all(void **state)
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
ret = json_format_priority(auth_data, &priority);
|
|
assert_int_equal(ret, EOK);
|
|
|
|
@@ -444,14 +515,30 @@ void test_json_format_auth_selection_sc(void **state)
|
|
test_ctx = talloc_new(NULL);
|
|
assert_non_null(test_ctx);
|
|
auth_data->sc->enabled = true;
|
|
- auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_PROMPT_STR);
|
|
+ auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_TOKEN_NAME);
|
|
assert_non_null(auth_data->sc->names[0]);
|
|
- auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_PROMPT_STR);
|
|
+ auth_data->sc->cert_instructions[0] = talloc_strdup(auth_data->sc->cert_instructions, SC1_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->cert_instructions[0]);
|
|
+ auth_data->sc->module_names[0] = talloc_strdup(auth_data->sc->module_names, SC1_MODULE_NAME);
|
|
+ assert_non_null(auth_data->sc->module_names[0]);
|
|
+ auth_data->sc->key_ids[0] = talloc_strdup(auth_data->sc->key_ids, SC1_KEY_ID);
|
|
+ assert_non_null(auth_data->sc->key_ids[0]);
|
|
+ auth_data->sc->labels[0] = talloc_strdup(auth_data->sc->labels, SC1_LABEL);
|
|
+ assert_non_null(auth_data->sc->labels[0]);
|
|
+ auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_TOKEN_NAME);
|
|
assert_non_null(auth_data->sc->names[1]);
|
|
+ auth_data->sc->cert_instructions[1] = talloc_strdup(auth_data->sc->cert_instructions, SC2_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->cert_instructions[1]);
|
|
+ auth_data->sc->module_names[1] = talloc_strdup(auth_data->sc->module_names, SC2_MODULE_NAME);
|
|
+ assert_non_null(auth_data->sc->module_names[1]);
|
|
+ auth_data->sc->key_ids[1] = talloc_strdup(auth_data->sc->key_ids, SC2_KEY_ID);
|
|
+ assert_non_null(auth_data->sc->key_ids[1]);
|
|
+ auth_data->sc->labels[1] = talloc_strdup(auth_data->sc->labels, SC2_LABEL);
|
|
+ assert_non_null(auth_data->sc->labels[1]);
|
|
auth_data->sc->names[2] = NULL;
|
|
- auth_data->sc->init_prompt = discard_const(SC_INIT_PROMPT);
|
|
auth_data->sc->pin_prompt = discard_const(SC_PIN_PROMPT);
|
|
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
ret = json_format_auth_selection(test_ctx, auth_data, &json_msg);
|
|
@@ -459,7 +546,15 @@ void test_json_format_auth_selection_sc(void **state)
|
|
assert_string_equal(json_msg, AUTH_SELECTION_SC);
|
|
|
|
talloc_free(auth_data->sc->names[0]);
|
|
+ talloc_free(auth_data->sc->cert_instructions[0]);
|
|
+ talloc_free(auth_data->sc->module_names[0]);
|
|
+ talloc_free(auth_data->sc->key_ids[0]);
|
|
+ talloc_free(auth_data->sc->labels[0]);
|
|
talloc_free(auth_data->sc->names[1]);
|
|
+ talloc_free(auth_data->sc->cert_instructions[1]);
|
|
+ talloc_free(auth_data->sc->module_names[1]);
|
|
+ talloc_free(auth_data->sc->key_ids[1]);
|
|
+ talloc_free(auth_data->sc->labels[1]);
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
@@ -480,26 +575,48 @@ void test_json_format_auth_selection_all(void **state)
|
|
auth_data->oauth2->init_prompt = discard_const(OAUTH2_INIT_PROMPT);
|
|
auth_data->oauth2->link_prompt = discard_const(OAUTH2_LINK_PROMPT);
|
|
auth_data->sc->enabled = true;
|
|
- auth_data->sc->names = talloc_array(test_ctx, char *, 3);
|
|
- assert_non_null(auth_data->sc->names);
|
|
- auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_PROMPT_STR);
|
|
+ auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_TOKEN_NAME);
|
|
assert_non_null(auth_data->sc->names[0]);
|
|
- auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_PROMPT_STR);
|
|
+ auth_data->sc->cert_instructions[0] = talloc_strdup(auth_data->sc->cert_instructions, SC1_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->cert_instructions[0]);
|
|
+ auth_data->sc->module_names[0] = talloc_strdup(auth_data->sc->module_names, SC1_MODULE_NAME);
|
|
+ assert_non_null(auth_data->sc->module_names[0]);
|
|
+ auth_data->sc->key_ids[0] = talloc_strdup(auth_data->sc->key_ids, SC1_KEY_ID);
|
|
+ assert_non_null(auth_data->sc->key_ids[0]);
|
|
+ auth_data->sc->labels[0] = talloc_strdup(auth_data->sc->labels, SC1_LABEL);
|
|
+ assert_non_null(auth_data->sc->labels[0]);
|
|
+ auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_TOKEN_NAME);
|
|
assert_non_null(auth_data->sc->names[1]);
|
|
+ auth_data->sc->cert_instructions[1] = talloc_strdup(auth_data->sc->cert_instructions, SC2_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->cert_instructions[1]);
|
|
+ auth_data->sc->module_names[1] = talloc_strdup(auth_data->sc->module_names, SC2_MODULE_NAME);
|
|
+ assert_non_null(auth_data->sc->module_names[1]);
|
|
+ auth_data->sc->key_ids[1] = talloc_strdup(auth_data->sc->key_ids, SC2_KEY_ID);
|
|
+ assert_non_null(auth_data->sc->key_ids[1]);
|
|
+ auth_data->sc->labels[1] = talloc_strdup(auth_data->sc->labels, SC2_LABEL);
|
|
+ assert_non_null(auth_data->sc->labels[1]);
|
|
auth_data->sc->names[2] = NULL;
|
|
- auth_data->sc->init_prompt = discard_const(SC_INIT_PROMPT);
|
|
auth_data->sc->pin_prompt = discard_const(SC_PIN_PROMPT);
|
|
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
ret = json_format_auth_selection(test_ctx, auth_data, &json_msg);
|
|
assert_int_equal(ret, EOK);
|
|
assert_string_equal(json_msg, AUTH_SELECTION_ALL);
|
|
|
|
talloc_free(auth_data->sc->names[0]);
|
|
+ talloc_free(auth_data->sc->cert_instructions[0]);
|
|
+ talloc_free(auth_data->sc->module_names[0]);
|
|
+ talloc_free(auth_data->sc->key_ids[0]);
|
|
+ talloc_free(auth_data->sc->labels[0]);
|
|
talloc_free(auth_data->sc->names[1]);
|
|
+ talloc_free(auth_data->sc->cert_instructions[1]);
|
|
+ talloc_free(auth_data->sc->module_names[1]);
|
|
+ talloc_free(auth_data->sc->key_ids[1]);
|
|
+ talloc_free(auth_data->sc->labels[1]);
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
@@ -520,12 +637,27 @@ void test_json_format_auth_selection_failure(void **state)
|
|
auth_data->oauth2->init_prompt = discard_const(OAUTH2_INIT_PROMPT);
|
|
auth_data->oauth2->link_prompt = discard_const(OAUTH2_LINK_PROMPT);
|
|
auth_data->sc->enabled = true;
|
|
- auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_LABEL);
|
|
+ auth_data->sc->names[0] = talloc_strdup(auth_data->sc->names, SC1_TOKEN_NAME);
|
|
assert_non_null(auth_data->sc->names[0]);
|
|
- auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_LABEL);
|
|
+ auth_data->sc->cert_instructions[0] = talloc_strdup(auth_data->sc->cert_instructions, SC1_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->cert_instructions[0]);
|
|
+ auth_data->sc->module_names[0] = talloc_strdup(auth_data->sc->module_names, SC1_MODULE_NAME);
|
|
+ assert_non_null(auth_data->sc->module_names[0]);
|
|
+ auth_data->sc->key_ids[0] = talloc_strdup(auth_data->sc->key_ids, SC1_KEY_ID);
|
|
+ assert_non_null(auth_data->sc->key_ids[0]);
|
|
+ auth_data->sc->labels[0] = talloc_strdup(auth_data->sc->labels, SC1_LABEL);
|
|
+ assert_non_null(auth_data->sc->labels[0]);
|
|
+ auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_TOKEN_NAME);
|
|
assert_non_null(auth_data->sc->names[1]);
|
|
+ auth_data->sc->cert_instructions[1] = talloc_strdup(auth_data->sc->cert_instructions, SC2_PROMPT_STR);
|
|
+ assert_non_null(auth_data->sc->cert_instructions[1]);
|
|
+ auth_data->sc->module_names[1] = talloc_strdup(auth_data->sc->module_names, SC2_MODULE_NAME);
|
|
+ assert_non_null(auth_data->sc->module_names[1]);
|
|
+ auth_data->sc->key_ids[1] = talloc_strdup(auth_data->sc->key_ids, SC2_KEY_ID);
|
|
+ assert_non_null(auth_data->sc->key_ids[1]);
|
|
+ auth_data->sc->labels[1] = talloc_strdup(auth_data->sc->labels, SC2_LABEL);
|
|
+ assert_non_null(auth_data->sc->labels[1]);
|
|
auth_data->sc->names[2] = NULL;
|
|
- auth_data->sc->init_prompt = discard_const(SC_INIT_PROMPT);
|
|
auth_data->sc->pin_prompt = discard_const(SC_PIN_PROMPT);
|
|
|
|
will_return(__wrap_json_array_append_new, true);
|
|
@@ -534,7 +666,15 @@ void test_json_format_auth_selection_failure(void **state)
|
|
assert_null(json_msg);
|
|
|
|
talloc_free(auth_data->sc->names[0]);
|
|
+ talloc_free(auth_data->sc->cert_instructions[0]);
|
|
+ talloc_free(auth_data->sc->module_names[0]);
|
|
+ talloc_free(auth_data->sc->key_ids[0]);
|
|
+ talloc_free(auth_data->sc->labels[0]);
|
|
talloc_free(auth_data->sc->names[1]);
|
|
+ talloc_free(auth_data->sc->cert_instructions[1]);
|
|
+ talloc_free(auth_data->sc->module_names[1]);
|
|
+ talloc_free(auth_data->sc->key_ids[1]);
|
|
+ talloc_free(auth_data->sc->labels[1]);
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
@@ -570,6 +710,7 @@ void test_generate_json_message_integration(void **state)
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
will_return(__wrap_json_array_append_new, false);
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
ret = generate_json_auth_message(NULL, pc_list, pd);
|
|
assert_int_equal(ret, EOK);
|
|
assert_string_equal((char*) pd->resp_list->data, AUTH_SELECTION_ALL);
|
|
@@ -840,7 +981,7 @@ int main(int argc, const char *argv[])
|
|
|
|
const struct CMUnitTest tests[] = {
|
|
cmocka_unit_test(test_get_cert_list),
|
|
- cmocka_unit_test_setup_teardown(test_get_cert_names, setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_get_cert_data, setup, teardown),
|
|
cmocka_unit_test_setup_teardown(test_json_format_mechanisms_password, setup, teardown),
|
|
cmocka_unit_test_setup_teardown(test_json_format_mechanisms_oauth2, setup, teardown),
|
|
cmocka_unit_test_setup_teardown(test_json_format_mechanisms_sc1, setup, teardown),
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From bb37515adfac3bc9e3c1a95e96363d14f0fb7f86 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Wed, 29 Jan 2025 17:27:06 +0100
|
|
Subject: [PATCH 20/36] Responder: extend smartcard JSON reply message
|
|
|
|
Include the certificate data in the JSON message to set it in the
|
|
authtok structure more easily.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_json.c | 77 +++++++++++------------------
|
|
src/responder/pam/pamsrv_json.h | 4 +-
|
|
src/tests/cmocka/test_pamsrv_json.c | 37 +++++++-------
|
|
3 files changed, 48 insertions(+), 70 deletions(-)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
index e28051060..09b6c35d1 100644
|
|
--- a/src/responder/pam/pamsrv_json.c
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -146,27 +146,6 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
-static errno_t
|
|
-find_certificate_in_list(struct cert_auth_info *cert_list, int cert_num,
|
|
- struct cert_auth_info **_cai)
|
|
-{
|
|
- struct cert_auth_info *cai = NULL;
|
|
- struct cert_auth_info *cai_next = NULL;
|
|
- int i = 0;
|
|
-
|
|
- DLIST_FOR_EACH_SAFE(cai, cai_next, cert_list) {
|
|
- if (i == cert_num) {
|
|
- goto done;
|
|
- }
|
|
- i++;
|
|
- }
|
|
-
|
|
-done:
|
|
- *_cai = cai;
|
|
-
|
|
- return EOK;
|
|
-}
|
|
-
|
|
static errno_t
|
|
obtain_prompts(struct confdb_ctx *cdb, TALLOC_CTX *mem_ctx,
|
|
struct prompt_config **pc_list, struct auth_data *_auth_data)
|
|
@@ -987,23 +966,46 @@ done:
|
|
}
|
|
|
|
errno_t
|
|
-json_unpack_smartcard(json_t *jroot, const char **_pin)
|
|
+json_unpack_smartcard(TALLOC_CTX *mem_ctx, json_t *jroot,
|
|
+ const char **_pin, struct cert_auth_info **_cai)
|
|
{
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ struct cert_auth_info *cai = NULL;
|
|
char *pin = NULL;
|
|
int ret = EOK;
|
|
|
|
- ret = json_unpack(jroot, "{s:s}",
|
|
- "pin", &pin);
|
|
+ tmp_ctx = talloc_new(NULL);
|
|
+ if (tmp_ctx == NULL) {
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ cai = talloc_zero(tmp_ctx, struct cert_auth_info);
|
|
+ if (cai == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = json_unpack(jroot, "{s:s,s:s,s:s,s:s,s:s}",
|
|
+ "pin", &pin,
|
|
+ "tokenName", &cai->token_name,
|
|
+ "moduleName", &cai->module_name,
|
|
+ "keyId", &cai->key_id,
|
|
+ "label", &cai->label);
|
|
if (ret != 0) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE, "json_unpack for pin failed.\n");
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "json_unpack for smartcard failed.\n");
|
|
ret = EINVAL;
|
|
goto done;
|
|
}
|
|
|
|
*_pin = pin;
|
|
+ *_cai = talloc_steal(mem_ctx, cai);
|
|
+
|
|
ret = EOK;
|
|
|
|
done:
|
|
+ talloc_free(tmp_ctx);
|
|
+
|
|
return ret;
|
|
}
|
|
|
|
@@ -1011,7 +1013,6 @@ errno_t
|
|
json_unpack_auth_reply(struct pam_data *pd)
|
|
{
|
|
TALLOC_CTX *tmp_ctx = NULL;
|
|
- struct cert_auth_info *cert_list = NULL;
|
|
struct cert_auth_info *cai = NULL;
|
|
json_t *jroot = NULL;
|
|
json_t *jauth_selection = NULL;
|
|
@@ -1022,8 +1023,6 @@ json_unpack_auth_reply(struct pam_data *pd)
|
|
char *password = NULL;
|
|
char *oauth2_code = NULL;
|
|
const char *pin = NULL;
|
|
- char *index = NULL;
|
|
- int cert_num;
|
|
int ret = EOK;
|
|
|
|
DEBUG(SSSDBG_TRACE_FUNC, "Received JSON message: %s.\n",
|
|
@@ -1092,27 +1091,7 @@ json_unpack_auth_reply(struct pam_data *pd)
|
|
}
|
|
|
|
if (strncmp(key, "smartcard", strlen("smartcard")) == 0) {
|
|
- ret = json_unpack_smartcard(jobj, &pin);
|
|
- if (ret != EOK) {
|
|
- goto done;
|
|
- }
|
|
-
|
|
- ret = get_cert_list(tmp_ctx, pd, &cert_list);
|
|
- if (ret != EOK) {
|
|
- goto done;
|
|
- }
|
|
-
|
|
- index = talloc_strdup(tmp_ctx, key + 10);
|
|
- if (index == NULL) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE,
|
|
- "talloc_strdup failed: %d.\n", ret);
|
|
- ret = ENOMEM;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- cert_num = atoi(index);
|
|
- cert_num--;
|
|
- ret = find_certificate_in_list(cert_list, cert_num, &cai);
|
|
+ ret = json_unpack_smartcard(tmp_ctx, jobj, &pin, &cai);
|
|
if (ret != EOK) {
|
|
goto done;
|
|
}
|
|
diff --git a/src/responder/pam/pamsrv_json.h b/src/responder/pam/pamsrv_json.h
|
|
index ddc1c442a..6e2c90d70 100644
|
|
--- a/src/responder/pam/pamsrv_json.h
|
|
+++ b/src/responder/pam/pamsrv_json.h
|
|
@@ -185,9 +185,11 @@ json_unpack_oauth2_code(TALLOC_CTX *mem_ctx, char *json_auth_msg,
|
|
*
|
|
* @param[in] jroot jansson structure containing the smartcard specific data
|
|
* @param[out] _pin user PIN
|
|
+ * @param[out] _cai certificate data
|
|
*/
|
|
errno_t
|
|
-json_unpack_smartcard(json_t *jroot, const char **_pin);
|
|
+json_unpack_smartcard(TALLOC_CTX *mem_ctx, json_t *jroot,
|
|
+ const char **_pin, struct cert_auth_info **_cai);
|
|
|
|
/**
|
|
* @brief Unpack GDM reply and check its value
|
|
diff --git a/src/tests/cmocka/test_pamsrv_json.c b/src/tests/cmocka/test_pamsrv_json.c
|
|
index bb4d9c071..8e58222ed 100644
|
|
--- a/src/tests/cmocka/test_pamsrv_json.c
|
|
+++ b/src/tests/cmocka/test_pamsrv_json.c
|
|
@@ -108,13 +108,15 @@
|
|
"\"priority\": " PRIORITY_ALL "}}"
|
|
|
|
#define PASSWORD_CONTENT "{\"password\": \"ThePassword\"}"
|
|
-#define SMARTCARD_CONTENT "{\"pin\": \"ThePIN\"}"
|
|
+#define SMARTCARD_CONTENT "{\"pin\": \"ThePIN\", \"tokenName\": \"token_name1\", " \
|
|
+ "\"moduleName\": \"module_name1\", \"keyId\": \"key_id1\", " \
|
|
+ "\"label\": \"label1\"}"
|
|
#define AUTH_MECH_REPLY_PASSWORD "{\"authSelection\": {" \
|
|
"\"status\": \"Ok\", \"password\": " \
|
|
PASSWORD_CONTENT "}}"
|
|
#define AUTH_MECH_REPLY_OAUTH2 "{\"authSelection\": {" \
|
|
"\"status\": \"Ok\", \"eidp\": {}}}"
|
|
-#define AUTH_MECH_REPLY_SMARTCARD "{\"auth-selection\": {" \
|
|
+#define AUTH_MECH_REPLY_SMARTCARD "{\"authSelection\": {" \
|
|
"\"status\": \"Ok\", \"smartcard:1\": " \
|
|
SMARTCARD_CONTENT "}}"
|
|
#define AUTH_MECH_ERRONEOUS "{\"authSelection\": {" \
|
|
@@ -735,20 +737,30 @@ void test_json_unpack_password_ok(void **state)
|
|
json_decref(jroot);
|
|
}
|
|
|
|
-void test_json_unpack_sc_ok(void **state)
|
|
+void test_json_unpack_smartcard_ok(void **state)
|
|
{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
json_t *jroot = NULL;
|
|
const char *pin = NULL;
|
|
+ struct cert_auth_info *cai = NULL;
|
|
json_error_t jret;
|
|
int ret;
|
|
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
jroot = json_loads(SMARTCARD_CONTENT, 0, &jret);
|
|
assert_non_null(jroot);
|
|
|
|
- ret = json_unpack_smartcard(jroot, &pin);
|
|
+ ret = json_unpack_smartcard(test_ctx, jroot, &pin, &cai);
|
|
assert_int_equal(ret, EOK);
|
|
assert_string_equal(pin, "ThePIN");
|
|
+ assert_string_equal(cai->token_name, "token_name1");
|
|
+ assert_string_equal(cai->module_name, "module_name1");
|
|
+ assert_string_equal(cai->key_id, "key_id1");
|
|
+ assert_string_equal(cai->label, "label1");
|
|
json_decref(jroot);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
}
|
|
|
|
void test_json_unpack_auth_reply_password(void **state)
|
|
@@ -821,11 +833,6 @@ void test_json_unpack_auth_reply_sc1(void **state)
|
|
pd->authtok = sss_authtok_new(pd);
|
|
assert_non_null(pd->authtok);
|
|
pd->json_auth_selected = discard_const(AUTH_MECH_REPLY_SMARTCARD);
|
|
- len = strlen(SC1_CERT_USER)+1+strlen(SC1_TOKEN_NAME)+1+
|
|
- strlen(SC1_MODULE_NAME)+1+strlen(SC1_KEY_ID)+1+strlen(SC1_LABEL)+1+
|
|
- strlen(SC1_PROMPT_STR)+1+strlen(SC1_PAM_CERT_USER)+1;
|
|
- ret = pam_add_response(pd, SSS_PAM_CERT_INFO, len, discard_const(SC1_STR));
|
|
- assert_int_equal(ret, EOK);
|
|
|
|
ret = json_unpack_auth_reply(pd);
|
|
assert_int_equal(ret, EOK);
|
|
@@ -864,16 +871,6 @@ void test_json_unpack_auth_reply_sc2(void **state)
|
|
pd->authtok = sss_authtok_new(pd);
|
|
assert_non_null(pd->authtok);
|
|
pd->json_auth_selected = discard_const(AUTH_MECH_REPLY_SMARTCARD);
|
|
- len = strlen(SC1_CERT_USER)+1+strlen(SC1_TOKEN_NAME)+1+
|
|
- strlen(SC1_MODULE_NAME)+1+strlen(SC1_KEY_ID)+1+strlen(SC1_LABEL)+1+
|
|
- strlen(SC1_PROMPT_STR)+1+strlen(SC1_PAM_CERT_USER)+1;
|
|
- ret = pam_add_response(pd, SSS_PAM_CERT_INFO, len, discard_const(SC1_STR));
|
|
- assert_int_equal(ret, EOK);
|
|
- len = strlen(SC2_CERT_USER)+1+strlen(SC2_TOKEN_NAME)+1+
|
|
- strlen(SC2_MODULE_NAME)+1+strlen(SC2_KEY_ID)+1+strlen(SC2_LABEL)+1+
|
|
- strlen(SC2_PROMPT_STR)+1+strlen(SC2_PAM_CERT_USER)+1;
|
|
- ret = pam_add_response(pd, SSS_PAM_CERT_INFO, len, discard_const(SC2_STR));
|
|
- assert_int_equal(ret, EOK);
|
|
|
|
ret = json_unpack_auth_reply(pd);
|
|
assert_int_equal(ret, EOK);
|
|
@@ -994,7 +991,7 @@ int main(int argc, const char *argv[])
|
|
cmocka_unit_test_setup_teardown(test_json_format_auth_selection_failure, setup, teardown),
|
|
cmocka_unit_test(test_generate_json_message_integration),
|
|
cmocka_unit_test(test_json_unpack_password_ok),
|
|
- cmocka_unit_test(test_json_unpack_sc_ok),
|
|
+ cmocka_unit_test(test_json_unpack_smartcard_ok),
|
|
cmocka_unit_test(test_json_unpack_auth_reply_password),
|
|
cmocka_unit_test(test_json_unpack_auth_reply_oauth2),
|
|
cmocka_unit_test(test_json_unpack_auth_reply_sc1),
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From ce9b0e9c6150727250b8e39e8d5d02b053f36aef Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Wed, 12 Feb 2025 18:17:25 +0100
|
|
Subject: [PATCH 21/36] Responder: make `decode_pam_passkey_msg()` public
|
|
|
|
This is needed by `pamsrv_json.c`, so let's make it public.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_passkey.h | 4 ++++
|
|
1 file changed, 4 insertions(+)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_passkey.h b/src/responder/pam/pamsrv_passkey.h
|
|
index 8d35cbb26..a52579ec5 100644
|
|
--- a/src/responder/pam/pamsrv_passkey.h
|
|
+++ b/src/responder/pam/pamsrv_passkey.h
|
|
@@ -72,6 +72,10 @@ struct tevent_req *pam_passkey_auth_send(TALLOC_CTX *mem_ctx,
|
|
bool kerberos_pa);
|
|
errno_t pam_passkey_auth_recv(struct tevent_req *req,
|
|
int *child_status);
|
|
+errno_t decode_pam_passkey_msg(TALLOC_CTX *mem_ctx,
|
|
+ uint8_t *buf,
|
|
+ size_t len,
|
|
+ struct pk_child_user_data **_data);
|
|
errno_t pam_eval_passkey_response(struct pam_ctx *pctx,
|
|
struct pam_data *pd,
|
|
struct pam_auth_req *preq,
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From a0bc5692815ec97adeaff86bddb3b41d4e29341f Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Tue, 10 Sep 2024 09:22:28 +0200
|
|
Subject: [PATCH 22/36] Responder: generate JSON message for passkey
|
|
|
|
Implement a set of functions to retrieve the passkey data and generate
|
|
the JSON message with it.
|
|
|
|
Implement new unit test and adapt the existing ones to take into account
|
|
the new data.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_json.c | 162 ++++++++++++++++++++++++++++
|
|
src/responder/pam/pamsrv_json.h | 16 +++
|
|
src/tests/cmocka/test_pamsrv_json.c | 111 ++++++++++++++++---
|
|
3 files changed, 273 insertions(+), 16 deletions(-)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
index 09b6c35d1..e532f3df4 100644
|
|
--- a/src/responder/pam/pamsrv_json.c
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -32,6 +32,9 @@
|
|
#include <string.h>
|
|
|
|
#include "responder/pam/pamsrv.h"
|
|
+#ifdef BUILD_PASSKEY
|
|
+#include "responder/pam/pamsrv_passkey.h"
|
|
+#endif /* BUILD_PASSKEY */
|
|
#include "util/debug.h"
|
|
|
|
#include "pamsrv_json.h"
|
|
@@ -146,6 +149,79 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+#ifdef BUILD_PASSKEY
|
|
+static errno_t
|
|
+obtain_passkey_data(TALLOC_CTX *mem_ctx, struct pam_data *pd,
|
|
+ struct auth_data *_auth_data)
|
|
+{
|
|
+ TALLOC_CTX *tmp_ctx = NULL;
|
|
+ struct pk_child_user_data *pk_data = NULL;
|
|
+ const char *crypto_challenge = NULL;
|
|
+ bool passkey_enabled = false;
|
|
+ bool passkey_kerberos = false;
|
|
+ bool user_verification = true;
|
|
+ uint8_t *buf = NULL;
|
|
+ int32_t len;
|
|
+ int ret;
|
|
+
|
|
+ tmp_ctx = talloc_new(NULL);
|
|
+ if (tmp_ctx == NULL) {
|
|
+ return ENOMEM;
|
|
+ }
|
|
+
|
|
+ ret = pam_get_response_data(tmp_ctx, pd, SSS_PAM_PASSKEY_KRB_INFO, &buf, &len);
|
|
+ if (ret == EOK) {
|
|
+ passkey_enabled = true;
|
|
+ passkey_kerberos = true;
|
|
+ ret = decode_pam_passkey_msg(tmp_ctx, buf, len, &pk_data);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "Failure decoding PAM passkey msg, ret %d.\n",
|
|
+ ret);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (strcmp(pk_data->user_verification, "false") == 0) {
|
|
+ user_verification = false;
|
|
+ }
|
|
+ crypto_challenge = pk_data->crypto_challenge;
|
|
+ } else if (ret == ENOENT) {
|
|
+ DEBUG(SSSDBG_FUNC_DATA, "SSS_PAM_PASSKEY_KRB_INFO not found.\n");
|
|
+ ret = pam_get_response_data(tmp_ctx, pd, SSS_PAM_PASSKEY_INFO, &buf, &len);
|
|
+ if (ret == EOK) {
|
|
+ passkey_enabled = true;
|
|
+ crypto_challenge = talloc_strdup(tmp_ctx, "");
|
|
+ } else if (ret == ENOENT) {
|
|
+ DEBUG(SSSDBG_FUNC_DATA, "SSS_PAM_PASSKEY_INFO not found.\n");
|
|
+ } else {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "Unable to get SSS_PAM_PASSKEY_INFO, ret %d.\n",
|
|
+ ret);
|
|
+ goto done;
|
|
+ }
|
|
+ } else {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "Unable to get SSS_PAM_PASSKEY_KRB_INFO, ret %d.\n",
|
|
+ ret);
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ _auth_data->passkey->enabled = passkey_enabled;
|
|
+ _auth_data->passkey->kerberos = passkey_kerberos;
|
|
+ _auth_data->passkey->key_connected = true;
|
|
+ _auth_data->passkey->pin_request = user_verification;
|
|
+ _auth_data->passkey->crypto_challenge = talloc_steal(mem_ctx, crypto_challenge);
|
|
+ /* Hardcoding of the following values for the moment */
|
|
+ _auth_data->passkey->pin_attempts = 8;
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ talloc_free(tmp_ctx);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+#endif /* BUILD_PASSKEY */
|
|
+
|
|
static errno_t
|
|
obtain_prompts(struct confdb_ctx *cdb, TALLOC_CTX *mem_ctx,
|
|
struct prompt_config **pc_list, struct auth_data *_auth_data)
|
|
@@ -156,6 +232,9 @@ obtain_prompts(struct confdb_ctx *cdb, TALLOC_CTX *mem_ctx,
|
|
char *oauth2_link_prompt = NULL;
|
|
char *sc_init_prompt = NULL;
|
|
char *sc_pin_prompt = NULL;
|
|
+ char *passkey_init_prompt = NULL;
|
|
+ char *passkey_pin_prompt = NULL;
|
|
+ char *passkey_touch_prompt = NULL;
|
|
errno_t ret;
|
|
|
|
tmp_ctx = talloc_new(NULL);
|
|
@@ -193,11 +272,32 @@ obtain_prompts(struct confdb_ctx *cdb, TALLOC_CTX *mem_ctx,
|
|
goto done;
|
|
}
|
|
|
|
+ passkey_init_prompt = talloc_strdup(tmp_ctx, PASSKEY_INIT_PROMPT);
|
|
+ if (passkey_init_prompt == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ passkey_pin_prompt = talloc_strdup(tmp_ctx, PASSKEY_PIN_PROMPT);
|
|
+ if (passkey_pin_prompt == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ passkey_touch_prompt = talloc_strdup(tmp_ctx, PASSKEY_TOUCH_PROMPT);
|
|
+ if (passkey_touch_prompt == NULL) {
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
_auth_data->pswd->prompt = talloc_steal(mem_ctx, password_prompt);
|
|
_auth_data->oauth2->init_prompt = talloc_steal(mem_ctx, oauth2_init_prompt);
|
|
_auth_data->oauth2->link_prompt = talloc_steal(mem_ctx, oauth2_link_prompt);
|
|
_auth_data->sc->init_prompt = talloc_steal(mem_ctx, sc_init_prompt);
|
|
_auth_data->sc->pin_prompt = talloc_steal(mem_ctx, sc_pin_prompt);
|
|
+ _auth_data->passkey->init_prompt = talloc_steal(mem_ctx, passkey_init_prompt);
|
|
+ _auth_data->passkey->pin_prompt = talloc_steal(mem_ctx, passkey_pin_prompt);
|
|
+ _auth_data->passkey->touch_prompt = talloc_steal(mem_ctx, passkey_touch_prompt);
|
|
ret = EOK;
|
|
|
|
done:
|
|
@@ -444,6 +544,14 @@ init_auth_data(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
|
|
}
|
|
(*_auth_data)->sc->enabled = true;
|
|
|
|
+ (*_auth_data)->passkey = talloc_zero(mem_ctx, struct passkey_data);
|
|
+ if ((*_auth_data)->passkey == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_zero failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ (*_auth_data)->passkey->enabled = true;
|
|
+
|
|
ret = obtain_prompts(cdb, mem_ctx, pc_list, *_auth_data);
|
|
if (ret != EOK) {
|
|
DEBUG(SSSDBG_CRIT_FAILURE, "Failure to obtain the prompts.\n");
|
|
@@ -473,6 +581,16 @@ init_auth_data(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
|
|
goto done;
|
|
}
|
|
|
|
+#ifdef BUILD_PASSKEY
|
|
+ ret = obtain_passkey_data(mem_ctx, pd, *_auth_data);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failure to obtain passkey data.\n");
|
|
+ goto done;
|
|
+ }
|
|
+#else
|
|
+ (*_auth_data)->passkey->enabled = false;
|
|
+#endif /* BUILD_PASSKEY */
|
|
+
|
|
done:
|
|
return ret;
|
|
}
|
|
@@ -603,6 +721,7 @@ json_format_mechanisms(struct auth_data *auth_data, json_t **_list_mech)
|
|
json_t *json_cert = NULL;
|
|
json_t *json_cert_array = NULL;
|
|
json_t *json_sc = NULL;
|
|
+ json_t *json_passkey = NULL;
|
|
int ret;
|
|
|
|
root = json_object();
|
|
@@ -706,6 +825,33 @@ json_format_mechanisms(struct auth_data *auth_data, json_t **_list_mech)
|
|
}
|
|
}
|
|
|
|
+ if (auth_data->passkey->enabled) {
|
|
+ json_passkey = json_pack("{s:s,s:s,s:s,s:b,s:b,s:i,s:s,s:s,s:b,s:s}",
|
|
+ "name", "Passkey",
|
|
+ "role", "passkey",
|
|
+ "initInstruction", auth_data->passkey->init_prompt,
|
|
+ "keyConnected", auth_data->passkey->key_connected,
|
|
+ "pinRequest", auth_data->passkey->pin_request,
|
|
+ "pinAttempts", auth_data->passkey->pin_attempts,
|
|
+ "pinPrompt", auth_data->passkey->pin_prompt,
|
|
+ "touchInstruction", auth_data->passkey->touch_prompt,
|
|
+ "kerberos", auth_data->passkey->kerberos,
|
|
+ "cryptoChallenge", auth_data->passkey->crypto_challenge);
|
|
+ if (json_passkey == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_pack failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ ret = json_object_set_new(root, "passkey", json_passkey);
|
|
+ if (ret == -1) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_array_append failed.\n");
|
|
+ json_decref(json_passkey);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
*_list_mech = root;
|
|
ret = EOK;
|
|
|
|
@@ -750,6 +896,22 @@ json_format_priority(struct auth_data *auth_data, json_t **_priority)
|
|
}
|
|
}
|
|
|
|
+ if (auth_data->passkey->enabled) {
|
|
+ json_priority = json_string("passkey");
|
|
+ if (json_priority == NULL) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_string failed.\n");
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ ret = json_array_append_new(root, json_priority);
|
|
+ if (ret == -1) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "json_array_append failed.\n");
|
|
+ json_decref(json_priority);
|
|
+ ret = ENOMEM;
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
if (auth_data->oauth2->enabled) {
|
|
json_priority = json_string("eidp");
|
|
if (json_priority == NULL) {
|
|
diff --git a/src/responder/pam/pamsrv_json.h b/src/responder/pam/pamsrv_json.h
|
|
index 6e2c90d70..7b8b9a5f5 100644
|
|
--- a/src/responder/pam/pamsrv_json.h
|
|
+++ b/src/responder/pam/pamsrv_json.h
|
|
@@ -35,12 +35,16 @@
|
|
#define OAUTH2_LINK_PROMPT "Log in online with another device"
|
|
#define SC_INIT_PROMPT "Insert smartcard"
|
|
#define SC_PIN_PROMPT "PIN"
|
|
+#define PASSKEY_INIT_PROMPT "Insert key"
|
|
+#define PASSKEY_PIN_PROMPT "Security key PIN"
|
|
+#define PASSKEY_TOUCH_PROMPT "Touch security key"
|
|
|
|
|
|
struct auth_data {
|
|
struct password_data *pswd;
|
|
struct oauth2_data *oauth2;
|
|
struct sc_data *sc;
|
|
+ struct passkey_data *passkey;
|
|
};
|
|
|
|
struct password_data {
|
|
@@ -67,6 +71,18 @@ struct sc_data {
|
|
char **labels;
|
|
};
|
|
|
|
+struct passkey_data {
|
|
+ bool enabled;
|
|
+ char *init_prompt;
|
|
+ bool key_connected;
|
|
+ bool pin_request;
|
|
+ int pin_attempts;
|
|
+ char *pin_prompt;
|
|
+ char *touch_prompt;
|
|
+ bool kerberos;
|
|
+ const char *crypto_challenge;
|
|
+};
|
|
+
|
|
|
|
/**
|
|
* @brief Extract smartcard certificate list from pam_data structure
|
|
diff --git a/src/tests/cmocka/test_pamsrv_json.c b/src/tests/cmocka/test_pamsrv_json.c
|
|
index 8e58222ed..671af0f0c 100644
|
|
--- a/src/tests/cmocka/test_pamsrv_json.c
|
|
+++ b/src/tests/cmocka/test_pamsrv_json.c
|
|
@@ -87,11 +87,21 @@
|
|
"\"moduleName\": \"module_name2\", " \
|
|
"\"keyId\": \"key_id2\", " \
|
|
"\"label\": \"label2\"}]}"
|
|
+#define BASIC_PASSKEY "\"passkey\": {" \
|
|
+ "\"name\": \"Passkey\", \"role\": \"passkey\", " \
|
|
+ "\"initInstruction\": \"" PASSKEY_INIT_PROMPT "\", " \
|
|
+ "\"keyConnected\": true, " \
|
|
+ "\"pinRequest\": true, \"pinAttempts\": 8, " \
|
|
+ "\"pinPrompt\": \"" PASSKEY_PIN_PROMPT "\", " \
|
|
+ "\"touchInstruction\": \"" PASSKEY_TOUCH_PROMPT "\", " \
|
|
+ "\"kerberos\": false, " \
|
|
+ "\"cryptoChallenge\": \"\"}"
|
|
#define MECHANISMS_PASSWORD "{" BASIC_PASSWORD "}"
|
|
#define MECHANISMS_OAUTH2 "{" BASIC_OAUTH2 "}"
|
|
#define MECHANISMS_SC1 "{" BASIC_SC "}"
|
|
#define MECHANISMS_SC2 "{" MULTIPLE_SC "}"
|
|
-#define PRIORITY_ALL "[\"smartcard\", \"eidp\", \"password\"]"
|
|
+#define MECHANISMS_PASSKEY "{" BASIC_PASSKEY "}"
|
|
+#define PRIORITY_ALL "[\"smartcard\", \"passkey\", \"eidp\", \"password\"]"
|
|
#define AUTH_SELECTION_PASSWORD "{\"authSelection\": {\"mechanisms\": " \
|
|
MECHANISMS_PASSWORD ", " \
|
|
"\"priority\": [\"password\"]}}"
|
|
@@ -101,10 +111,14 @@
|
|
#define AUTH_SELECTION_SC "{\"authSelection\": {\"mechanisms\": " \
|
|
MECHANISMS_SC2 ", " \
|
|
"\"priority\": [\"smartcard\"]}}"
|
|
+#define AUTH_SELECTION_PASSKEY "{\"authSelection\": {\"mechanisms\": " \
|
|
+ MECHANISMS_PASSKEY ", " \
|
|
+ "\"priority\": [\"passkey\"]}}"
|
|
#define AUTH_SELECTION_ALL "{\"authSelection\": {\"mechanisms\": {" \
|
|
BASIC_PASSWORD ", " \
|
|
BASIC_OAUTH2 ", " \
|
|
- MULTIPLE_SC "}, " \
|
|
+ MULTIPLE_SC ", " \
|
|
+ BASIC_PASSKEY "}, " \
|
|
"\"priority\": " PRIORITY_ALL "}}"
|
|
|
|
#define PASSWORD_CONTENT "{\"password\": \"ThePassword\"}"
|
|
@@ -163,10 +177,13 @@ static int setup(void **state)
|
|
assert_non_null(auth_data->sc->key_ids);
|
|
auth_data->sc->labels = talloc_array(auth_data->sc, char *, 3);
|
|
assert_non_null(auth_data->sc->labels);
|
|
+ auth_data->passkey = talloc_zero(auth_data, struct passkey_data);
|
|
+ assert_non_null(auth_data->passkey);
|
|
|
|
auth_data->pswd->enabled = false;
|
|
auth_data->oauth2->enabled = false;
|
|
auth_data->sc->enabled = false;
|
|
+ auth_data->passkey->enabled = false;
|
|
|
|
check_leaks_push(auth_data);
|
|
*state = (void *)auth_data;
|
|
@@ -433,6 +450,33 @@ void test_json_format_mechanisms_sc2(void **state)
|
|
talloc_free(auth_data->sc->labels[1]);
|
|
}
|
|
|
|
+void test_json_format_mechanisms_passkey(void **state)
|
|
+{
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
+ json_t *mechs = NULL;
|
|
+ char *string;
|
|
+ int ret;
|
|
+
|
|
+ auth_data->passkey->enabled = true;
|
|
+ auth_data->passkey->init_prompt = discard_const(PASSKEY_INIT_PROMPT);
|
|
+ auth_data->passkey->key_connected = true;
|
|
+ auth_data->passkey->pin_request = true;
|
|
+ auth_data->passkey->pin_attempts = 8;
|
|
+ auth_data->passkey->pin_prompt = discard_const(PASSKEY_PIN_PROMPT);
|
|
+ auth_data->passkey->touch_prompt = discard_const(PASSKEY_TOUCH_PROMPT);
|
|
+ auth_data->passkey->kerberos = false;
|
|
+ auth_data->passkey->crypto_challenge = discard_const("");
|
|
+
|
|
+ ret = json_format_mechanisms(auth_data, &mechs);
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
+ string = json_dumps(mechs, 0);
|
|
+ assert_string_equal(string, MECHANISMS_PASSKEY);
|
|
+
|
|
+ json_decref(mechs);
|
|
+ free(string);
|
|
+}
|
|
+
|
|
void test_json_format_priority_all(void **state)
|
|
{
|
|
struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
@@ -448,10 +492,9 @@ void test_json_format_priority_all(void **state)
|
|
auth_data->sc->names[1] = talloc_strdup(auth_data->sc->names, SC2_LABEL);
|
|
assert_non_null(auth_data->sc->names[1]);
|
|
auth_data->sc->names[2] = NULL;
|
|
+ auth_data->passkey->enabled = true;
|
|
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
+ will_return_count(__wrap_json_array_append_new, false, 4);
|
|
ret = json_format_priority(auth_data, &priority);
|
|
assert_int_equal(ret, EOK);
|
|
|
|
@@ -560,6 +603,33 @@ void test_json_format_auth_selection_sc(void **state)
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
+void test_json_format_auth_selection_passkey(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct auth_data *auth_data = talloc_get_type_abort(*state, struct auth_data);
|
|
+ char *json_msg = NULL;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ auth_data->passkey->enabled = true;
|
|
+ auth_data->passkey->init_prompt = discard_const(PASSKEY_INIT_PROMPT);
|
|
+ auth_data->passkey->key_connected = true;
|
|
+ auth_data->passkey->pin_request = true;
|
|
+ auth_data->passkey->pin_attempts = 8;
|
|
+ auth_data->passkey->pin_prompt = discard_const(PASSKEY_PIN_PROMPT);
|
|
+ auth_data->passkey->touch_prompt = discard_const(PASSKEY_TOUCH_PROMPT);
|
|
+ auth_data->passkey->kerberos = false;
|
|
+ auth_data->passkey->crypto_challenge = discard_const("");
|
|
+
|
|
+ will_return(__wrap_json_array_append_new, false);
|
|
+ ret = json_format_auth_selection(test_ctx, auth_data, &json_msg);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_string_equal(json_msg, AUTH_SELECTION_PASSKEY);
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
void test_json_format_auth_selection_all(void **state)
|
|
{
|
|
TALLOC_CTX *test_ctx = NULL;
|
|
@@ -599,12 +669,17 @@ void test_json_format_auth_selection_all(void **state)
|
|
assert_non_null(auth_data->sc->labels[1]);
|
|
auth_data->sc->names[2] = NULL;
|
|
auth_data->sc->pin_prompt = discard_const(SC_PIN_PROMPT);
|
|
-
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
+ auth_data->passkey->enabled = true;
|
|
+ auth_data->passkey->init_prompt = discard_const(PASSKEY_INIT_PROMPT);
|
|
+ auth_data->passkey->key_connected = true;
|
|
+ auth_data->passkey->pin_request = true;
|
|
+ auth_data->passkey->pin_attempts = 8;
|
|
+ auth_data->passkey->pin_prompt = discard_const(PASSKEY_PIN_PROMPT);
|
|
+ auth_data->passkey->touch_prompt = discard_const(PASSKEY_TOUCH_PROMPT);
|
|
+ auth_data->passkey->kerberos = false;
|
|
+ auth_data->passkey->crypto_challenge = discard_const("");
|
|
+
|
|
+ will_return_count(__wrap_json_array_append_new, false, 6);
|
|
ret = json_format_auth_selection(test_ctx, auth_data, &json_msg);
|
|
assert_int_equal(ret, EOK);
|
|
assert_string_equal(json_msg, AUTH_SELECTION_ALL);
|
|
@@ -685,6 +760,7 @@ void test_generate_json_message_integration(void **state)
|
|
TALLOC_CTX *test_ctx = NULL;
|
|
struct pam_data *pd = NULL;
|
|
struct prompt_config **pc_list = NULL;
|
|
+ const char *prompt_pin = "true";
|
|
int len;
|
|
int ret;
|
|
|
|
@@ -707,12 +783,11 @@ void test_generate_json_message_integration(void **state)
|
|
strlen(SC2_PROMPT_STR)+1+strlen(SC2_PAM_CERT_USER)+1;
|
|
ret = pam_add_response(pd, SSS_PAM_CERT_INFO, len, discard_const(SC2_STR));
|
|
assert_int_equal(ret, EOK);
|
|
+ len = strlen(prompt_pin)+1;
|
|
+ ret = pam_add_response(pd, SSS_PAM_PASSKEY_INFO, len,
|
|
+ discard_const(prompt_pin));
|
|
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
- will_return(__wrap_json_array_append_new, false);
|
|
+ will_return_count(__wrap_json_array_append_new, false, 6);
|
|
ret = generate_json_auth_message(NULL, pc_list, pd);
|
|
assert_int_equal(ret, EOK);
|
|
assert_string_equal((char*) pd->resp_list->data, AUTH_SELECTION_ALL);
|
|
@@ -983,13 +1058,17 @@ int main(int argc, const char *argv[])
|
|
cmocka_unit_test_setup_teardown(test_json_format_mechanisms_oauth2, setup, teardown),
|
|
cmocka_unit_test_setup_teardown(test_json_format_mechanisms_sc1, setup, teardown),
|
|
cmocka_unit_test_setup_teardown(test_json_format_mechanisms_sc2, setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_json_format_mechanisms_passkey, setup, teardown),
|
|
cmocka_unit_test_setup_teardown(test_json_format_priority_all, setup, teardown),
|
|
cmocka_unit_test_setup_teardown(test_json_format_auth_selection_password, setup, teardown),
|
|
cmocka_unit_test_setup_teardown(test_json_format_auth_selection_oauth2, setup, teardown),
|
|
cmocka_unit_test_setup_teardown(test_json_format_auth_selection_sc, setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_json_format_auth_selection_passkey, setup, teardown),
|
|
cmocka_unit_test_setup_teardown(test_json_format_auth_selection_all, setup, teardown),
|
|
cmocka_unit_test_setup_teardown(test_json_format_auth_selection_failure, setup, teardown),
|
|
+#ifdef BUILD_PASSKEY
|
|
cmocka_unit_test(test_generate_json_message_integration),
|
|
+#endif
|
|
cmocka_unit_test(test_json_unpack_password_ok),
|
|
cmocka_unit_test(test_json_unpack_smartcard_ok),
|
|
cmocka_unit_test(test_json_unpack_auth_reply_password),
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From b49fb1440308f549b78ecf41d40d2e80998d78bf Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Tue, 17 Sep 2024 10:21:10 +0200
|
|
Subject: [PATCH 23/36] util: implement function to set passkey PIN
|
|
|
|
`sss_authtok_set_local_passkey_pin` provides a way to set the passkey
|
|
PIN in the authtok structure for local passkey authentication.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/tests/cmocka/test_authtok.c | 32 ++++++++++++++++++++++++++++++++
|
|
src/util/authtok.c | 16 ++++++++++++++++
|
|
src/util/authtok.h | 15 +++++++++++++++
|
|
3 files changed, 63 insertions(+)
|
|
|
|
diff --git a/src/tests/cmocka/test_authtok.c b/src/tests/cmocka/test_authtok.c
|
|
index c736fd5a3..53ad75d16 100644
|
|
--- a/src/tests/cmocka/test_authtok.c
|
|
+++ b/src/tests/cmocka/test_authtok.c
|
|
@@ -29,6 +29,8 @@
|
|
|
|
#include "util/authtok.h"
|
|
|
|
+#define PIN "ThePIN"
|
|
+
|
|
|
|
struct test_state {
|
|
struct sss_auth_token *authtoken;
|
|
@@ -751,6 +753,34 @@ static void test_sss_authtok_oauth2(void **state)
|
|
sss_authtok_set_empty(ts->authtoken);
|
|
}
|
|
|
|
+void test_sss_authtok_set_local_passkey_pin(void **state)
|
|
+{
|
|
+ struct test_state *ts = NULL;
|
|
+ enum sss_authtok_type type;
|
|
+ const char *pin = NULL;
|
|
+ char *data = NULL;
|
|
+ size_t len = 0;
|
|
+ int ret;
|
|
+
|
|
+ ts = talloc_get_type_abort(*state, struct test_state);
|
|
+ type = SSS_AUTHTOK_TYPE_PASSKEY;
|
|
+ data = talloc_strdup(ts, "passkey");
|
|
+ assert_non_null(data);
|
|
+ len = strlen(data) + 1;
|
|
+ ret = sss_authtok_set(ts->authtoken, type, (const uint8_t *)data, len);
|
|
+ assert_int_equal(ret, EOK);
|
|
+
|
|
+ ret = sss_authtok_set_local_passkey_pin(ts->authtoken, PIN);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ ret = sss_authtok_get_passkey_pin(ts->authtoken, &pin, &len);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_int_equal(len, strlen(PIN));
|
|
+ assert_string_equal(pin, PIN);
|
|
+
|
|
+ talloc_free(data);
|
|
+ sss_authtok_set_empty(ts->authtoken);
|
|
+}
|
|
+
|
|
|
|
int main(int argc, const char *argv[])
|
|
{
|
|
@@ -791,6 +821,8 @@ int main(int argc, const char *argv[])
|
|
setup, teardown),
|
|
cmocka_unit_test_setup_teardown(test_sss_authtok_oauth2,
|
|
setup, teardown),
|
|
+ cmocka_unit_test_setup_teardown(test_sss_authtok_set_local_passkey_pin,
|
|
+ setup, teardown),
|
|
};
|
|
|
|
/* Set debug level to invalid value so we can decide if -d 0 was used. */
|
|
diff --git a/src/util/authtok.c b/src/util/authtok.c
|
|
index df64ede57..4c2d7ca4d 100644
|
|
--- a/src/util/authtok.c
|
|
+++ b/src/util/authtok.c
|
|
@@ -818,6 +818,22 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+errno_t sss_authtok_set_local_passkey_pin(struct sss_auth_token *tok,
|
|
+ const char *pin)
|
|
+{
|
|
+ int ret;
|
|
+
|
|
+ if (!tok) {
|
|
+ return EINVAL;
|
|
+ }
|
|
+
|
|
+ sss_authtok_set_empty(tok);
|
|
+ ret = sss_authtok_set_string(tok, SSS_AUTHTOK_TYPE_PASSKEY,
|
|
+ "passkey", pin, strlen(pin));
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
errno_t sss_authtok_get_passkey(TALLOC_CTX *mem_ctx,
|
|
struct sss_auth_token *tok,
|
|
const char **_prompt,
|
|
diff --git a/src/util/authtok.h b/src/util/authtok.h
|
|
index acabb7078..b68101211 100644
|
|
--- a/src/util/authtok.h
|
|
+++ b/src/util/authtok.h
|
|
@@ -508,6 +508,21 @@ errno_t sss_authtok_get_passkey(TALLOC_CTX *mem_ctx,
|
|
errno_t sss_authtok_get_passkey_pin(struct sss_auth_token *tok,
|
|
const char **pin, size_t *len);
|
|
|
|
+/**
|
|
+ * @brief Set local passkey PIN in sss_auth_token structure
|
|
+ *
|
|
+ * @param tok A pointer to an sss_auth_token
|
|
+ * @param pin A pointer to a const char *, that will point to a null
|
|
+ * terminated string
|
|
+ *
|
|
+ * @return EOK on success
|
|
+ * EINVAL if there's no token
|
|
+ * ENOENT if the token is empty
|
|
+ * EACCESS if the token is not a passkey token
|
|
+ */
|
|
+errno_t sss_authtok_set_local_passkey_pin(struct sss_auth_token *tok,
|
|
+ const char *pin);
|
|
+
|
|
/**
|
|
* @brief Set passkey kerberos preauth credentials into an auth token,
|
|
* replacing any previous data.
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 9c67585687e985f78b07e95960f23e14d86a207a Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Tue, 17 Sep 2024 09:20:53 +0200
|
|
Subject: [PATCH 24/36] Responder: parse reply for passkey
|
|
|
|
Parse GUI reply for passkey and set the appropriate data in
|
|
`sss_auth_token` structure.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_json.c | 72 +++++++++++++++++++++++++++++
|
|
src/responder/pam/pamsrv_json.h | 12 +++++
|
|
src/tests/cmocka/test_pamsrv_json.c | 54 ++++++++++++++++++++++
|
|
3 files changed, 138 insertions(+)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
index e532f3df4..37d3c276a 100644
|
|
--- a/src/responder/pam/pamsrv_json.c
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -1171,6 +1171,45 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
+errno_t
|
|
+json_unpack_passkey(json_t *jroot, const char **_pin, bool *_kerberos,
|
|
+ char **_crypto_challenge)
|
|
+{
|
|
+ json_t *pin = NULL;
|
|
+ json_t *kerberos = NULL;
|
|
+ json_t *crypto = NULL;
|
|
+ int ret = EOK;
|
|
+
|
|
+ pin = json_object_get(jroot, "pin");
|
|
+ if (pin == NULL) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "json_object_get for pin failed.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ kerberos = json_object_get(jroot, "kerberos");
|
|
+ if (kerberos == NULL) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "json_object_get for kerberos failed.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ crypto = json_object_get(jroot, "cryptoChallenge");
|
|
+ if (crypto == NULL) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "json_object_get for crypto-challenge failed.\n");
|
|
+ ret = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ *_pin = discard_const(json_string_value(pin));
|
|
+ *_kerberos = json_boolean_value(kerberos);
|
|
+ *_crypto_challenge = discard_const(json_string_value(crypto));
|
|
+ ret = EOK;
|
|
+
|
|
+done:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
errno_t
|
|
json_unpack_auth_reply(struct pam_data *pd)
|
|
{
|
|
@@ -1182,9 +1221,12 @@ json_unpack_auth_reply(struct pam_data *pd)
|
|
json_error_t jret;
|
|
const char *key = NULL;
|
|
const char *status = NULL;
|
|
+ const char *user_verification = NULL;
|
|
char *password = NULL;
|
|
char *oauth2_code = NULL;
|
|
const char *pin = NULL;
|
|
+ char *crypto_challenge = NULL;
|
|
+ bool passkey_kerberos = false;
|
|
int ret = EOK;
|
|
|
|
DEBUG(SSSDBG_TRACE_FUNC, "Received JSON message: %s.\n",
|
|
@@ -1270,6 +1312,36 @@ json_unpack_auth_reply(struct pam_data *pd)
|
|
}
|
|
goto done;
|
|
}
|
|
+
|
|
+ if (strcmp(key, "passkey") == 0) {
|
|
+ ret = json_unpack_passkey(jobj, &pin, &passkey_kerberos, &crypto_challenge);
|
|
+ if (ret != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (passkey_kerberos) {
|
|
+ if (pin != NULL && pin[0] != '\0') {
|
|
+ user_verification = talloc_strdup(tmp_ctx, "true");
|
|
+ } else {
|
|
+ user_verification = talloc_strdup(tmp_ctx, "false");
|
|
+ }
|
|
+ ret = sss_authtok_set_passkey_krb(pd->authtok, user_verification, crypto_challenge, pin);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "sss_authtok_set_passkey_krb failed: %d.\n", ret);
|
|
+ goto done;
|
|
+ }
|
|
+ } else {
|
|
+ ret = sss_authtok_set_local_passkey_pin(pd->authtok, pin);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "sss_authtok_set_local_passkey_pin failed: %d.\n",
|
|
+ ret);
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+ goto done;
|
|
+ }
|
|
}
|
|
|
|
DEBUG(SSSDBG_CRIT_FAILURE, "Unknown authentication mechanism\n");
|
|
diff --git a/src/responder/pam/pamsrv_json.h b/src/responder/pam/pamsrv_json.h
|
|
index 7b8b9a5f5..2f6c5cf36 100644
|
|
--- a/src/responder/pam/pamsrv_json.h
|
|
+++ b/src/responder/pam/pamsrv_json.h
|
|
@@ -207,6 +207,18 @@ errno_t
|
|
json_unpack_smartcard(TALLOC_CTX *mem_ctx, json_t *jroot,
|
|
const char **_pin, struct cert_auth_info **_cai);
|
|
|
|
+/**
|
|
+ * @brief Unpack passkey specific data reply
|
|
+ *
|
|
+ * @param[in] jroot jansson structure containing the data
|
|
+ * @param[out] _pin user PIN
|
|
+ * @param[out] _kerberos whether passkey auth is kerberos
|
|
+ * @param[out] _crypto_challenge cryptographic challenge
|
|
+ */
|
|
+errno_t
|
|
+json_unpack_passkey(json_t *jroot, const char **_pin, bool *_kerberos,
|
|
+ char **_crypto_challenge);
|
|
+
|
|
/**
|
|
* @brief Unpack GDM reply and check its value
|
|
*
|
|
diff --git a/src/tests/cmocka/test_pamsrv_json.c b/src/tests/cmocka/test_pamsrv_json.c
|
|
index 671af0f0c..d432ea065 100644
|
|
--- a/src/tests/cmocka/test_pamsrv_json.c
|
|
+++ b/src/tests/cmocka/test_pamsrv_json.c
|
|
@@ -34,6 +34,7 @@
|
|
#define OAUTH2_URI_COMP "\0"
|
|
#define OAUTH2_CODE "1234-5678"
|
|
#define OAUTH2_STR OAUTH2_URI OAUTH2_URI_COMP OAUTH2_CODE
|
|
+#define PASSKEY_CRYPTO_CHAL "6uDMvRKj3W5xJV3HaQjZrtXMNmUUAjRGklFG2MIhN5s="
|
|
|
|
#define SC1_CERT_USER "cert_user1\0"
|
|
#define SC1_TOKEN_NAME "token_name1\0"
|
|
@@ -125,6 +126,8 @@
|
|
#define SMARTCARD_CONTENT "{\"pin\": \"ThePIN\", \"tokenName\": \"token_name1\", " \
|
|
"\"moduleName\": \"module_name1\", \"keyId\": \"key_id1\", " \
|
|
"\"label\": \"label1\"}"
|
|
+#define PASSKEY_CONTENT "{\"pin\": \"ThePIN\", \"kerberos\": true, " \
|
|
+ "\"cryptoChallenge\": \"" PASSKEY_CRYPTO_CHAL "\"}"
|
|
#define AUTH_MECH_REPLY_PASSWORD "{\"authSelection\": {" \
|
|
"\"status\": \"Ok\", \"password\": " \
|
|
PASSWORD_CONTENT "}}"
|
|
@@ -133,6 +136,9 @@
|
|
#define AUTH_MECH_REPLY_SMARTCARD "{\"authSelection\": {" \
|
|
"\"status\": \"Ok\", \"smartcard:1\": " \
|
|
SMARTCARD_CONTENT "}}"
|
|
+#define AUTH_MECH_REPLY_PASSKEY "{\"authSelection\": {" \
|
|
+ "\"status\": \"Ok\", \"passkey\": " \
|
|
+ PASSKEY_CONTENT "}}"
|
|
#define AUTH_MECH_ERRONEOUS "{\"authSelection\": {" \
|
|
"\"status\": \"Ok\", \"lololo\": {}}}"
|
|
|
|
@@ -838,6 +844,26 @@ void test_json_unpack_smartcard_ok(void **state)
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
+void test_json_unpack_passkey_ok(void **state)
|
|
+{
|
|
+ json_t *jroot = NULL;
|
|
+ const char *pin = NULL;
|
|
+ char *crypto_challenge = NULL;
|
|
+ bool kerberos = false;
|
|
+ json_error_t jret;
|
|
+ int ret;
|
|
+
|
|
+ jroot = json_loads(PASSKEY_CONTENT, 0, &jret);
|
|
+ assert_non_null(jroot);
|
|
+
|
|
+ ret = json_unpack_passkey(jroot, &pin, &kerberos, &crypto_challenge);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_string_equal(pin, "ThePIN");
|
|
+ assert_int_equal(kerberos, true);
|
|
+ assert_string_equal(crypto_challenge, PASSKEY_CRYPTO_CHAL);
|
|
+ json_decref(jroot);
|
|
+}
|
|
+
|
|
void test_json_unpack_auth_reply_password(void **state)
|
|
{
|
|
TALLOC_CTX *test_ctx = NULL;
|
|
@@ -965,6 +991,32 @@ void test_json_unpack_auth_reply_sc2(void **state)
|
|
talloc_free(test_ctx);
|
|
}
|
|
|
|
+void test_json_unpack_auth_reply_passkey(void **state)
|
|
+{
|
|
+ TALLOC_CTX *test_ctx = NULL;
|
|
+ struct pam_data *pd = NULL;
|
|
+ const char *pin = NULL;
|
|
+ size_t len = 0;
|
|
+ int ret;
|
|
+
|
|
+ test_ctx = talloc_new(NULL);
|
|
+ assert_non_null(test_ctx);
|
|
+ pd = talloc_zero(test_ctx, struct pam_data);
|
|
+ assert_non_null(pd);
|
|
+ pd->authtok = sss_authtok_new(pd);
|
|
+ assert_non_null(pd->authtok);
|
|
+ pd->json_auth_msg = discard_const(AUTH_SELECTION_PASSKEY);
|
|
+ pd->json_auth_selected = discard_const(AUTH_MECH_REPLY_PASSKEY);
|
|
+
|
|
+ ret = json_unpack_auth_reply(pd);
|
|
+ assert_int_equal(ret, EOK);
|
|
+ assert_int_equal(sss_authtok_get_type(pd->authtok), SSS_AUTHTOK_TYPE_PASSKEY_KRB);
|
|
+ sss_authtok_get_passkey_pin(pd->authtok, &pin, &len);
|
|
+ assert_string_equal(pin, "ThePIN");
|
|
+
|
|
+ talloc_free(test_ctx);
|
|
+}
|
|
+
|
|
void test_json_unpack_auth_reply_failure(void **state)
|
|
{
|
|
TALLOC_CTX *test_ctx = NULL;
|
|
@@ -1071,10 +1123,12 @@ int main(int argc, const char *argv[])
|
|
#endif
|
|
cmocka_unit_test(test_json_unpack_password_ok),
|
|
cmocka_unit_test(test_json_unpack_smartcard_ok),
|
|
+ cmocka_unit_test(test_json_unpack_passkey_ok),
|
|
cmocka_unit_test(test_json_unpack_auth_reply_password),
|
|
cmocka_unit_test(test_json_unpack_auth_reply_oauth2),
|
|
cmocka_unit_test(test_json_unpack_auth_reply_sc1),
|
|
cmocka_unit_test(test_json_unpack_auth_reply_sc2),
|
|
+ cmocka_unit_test(test_json_unpack_auth_reply_passkey),
|
|
cmocka_unit_test(test_json_unpack_auth_reply_failure),
|
|
cmocka_unit_test(test_json_unpack_oauth2_code),
|
|
cmocka_unit_test(test_is_pam_json_enabled_service_in_list),
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 590caf2a3a25eea897f65599f4186d9a61245101 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Thu, 27 Mar 2025 10:20:12 +0100
|
|
Subject: [PATCH 25/36] krb5_child: advertise authentication methods
|
|
|
|
During the `preauthentication` phase krb5_child checks for the available
|
|
authentication methods for the given user, advertises them and the
|
|
process is kept alive. Once the state is change to `authentication` the
|
|
same krb5_child process processes the credentials and proceeds with the
|
|
authentication itself.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/providers/krb5/krb5_child.c | 611 ++++++++++++++++++++------------
|
|
1 file changed, 375 insertions(+), 236 deletions(-)
|
|
|
|
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
|
|
index 0506a3527..15bef2d00 100644
|
|
--- a/src/providers/krb5/krb5_child.c
|
|
+++ b/src/providers/krb5/krb5_child.c
|
|
@@ -564,6 +564,62 @@ static krb5_error_code tokeninfo_matches(TALLOC_CTX *mem_ctx,
|
|
return ERR_CHECK_NEXT_AUTH_TYPE;
|
|
}
|
|
|
|
+static krb5_error_code request_otp(krb5_context ctx,
|
|
+ struct krb5_req *kr,
|
|
+ krb5_responder_context rctx)
|
|
+{
|
|
+ krb5_responder_otp_challenge *chl;
|
|
+ size_t i;
|
|
+ krb5_error_code kerr;
|
|
+
|
|
+ kerr = krb5_responder_otp_get_challenge(ctx, rctx, &chl);
|
|
+ if (kerr != EOK || chl == NULL) {
|
|
+ /* Either an error, or nothing to do. */
|
|
+ return kerr;
|
|
+ }
|
|
+
|
|
+ if (chl->tokeninfo == NULL || chl->tokeninfo[0] == NULL) {
|
|
+ /* No tokeninfos? Absurd! */
|
|
+ kerr = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ for (i = 0; chl->tokeninfo[i] != NULL; i++) {
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Vendor [%s].\n",
|
|
+ i, chl->tokeninfo[i]->vendor);
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Token-ID [%s].\n",
|
|
+ i, chl->tokeninfo[i]->token_id);
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Challenge [%s].\n",
|
|
+ i, chl->tokeninfo[i]->challenge);
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Flags [%d].\n",
|
|
+ i, chl->tokeninfo[i]->flags);
|
|
+ }
|
|
+
|
|
+ if (chl->tokeninfo[0]->vendor != NULL) {
|
|
+ kr->otp_vendor = talloc_strdup(kr, chl->tokeninfo[0]->vendor);
|
|
+ }
|
|
+ if (chl->tokeninfo[0]->token_id != NULL) {
|
|
+ kr->otp_token_id = talloc_strdup(kr, chl->tokeninfo[0]->token_id);
|
|
+ }
|
|
+ if (chl->tokeninfo[0]->challenge != NULL) {
|
|
+ kr->otp_challenge = talloc_strdup(kr, chl->tokeninfo[0]->challenge);
|
|
+ }
|
|
+ /* Allocation errors are ignored on purpose */
|
|
+
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "Setting otp prompting.\n");
|
|
+ if (kr->otp) {
|
|
+ kerr = k5c_attach_otp_info_msg(kr);
|
|
+ if (kerr != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "Failed to add otp prompting data.\n");
|
|
+ }
|
|
+ }
|
|
+
|
|
+done:
|
|
+ krb5_responder_otp_challenge_free(ctx, rctx, chl);
|
|
+ return kerr;
|
|
+}
|
|
+
|
|
static krb5_error_code answer_otp(krb5_context ctx,
|
|
struct krb5_req *kr,
|
|
krb5_responder_context rctx)
|
|
@@ -572,6 +628,15 @@ static krb5_error_code answer_otp(krb5_context ctx,
|
|
char *token = NULL, *pin = NULL;
|
|
krb5_error_code ret;
|
|
size_t i;
|
|
+ enum sss_authtok_type type;
|
|
+
|
|
+ type = sss_authtok_get_type(kr->pd->authtok);
|
|
+ if (type != SSS_AUTHTOK_TYPE_2FA_SINGLE
|
|
+ && type != SSS_AUTHTOK_TYPE_2FA) {
|
|
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unexpected authentication token type [%s]\n",
|
|
+ sss_authtok_type_to_str(type));
|
|
+ return ERR_CHECK_NEXT_AUTH_TYPE;
|
|
+ }
|
|
|
|
ret = krb5_responder_otp_get_challenge(ctx, rctx, &chl);
|
|
if (ret != EOK || chl == NULL) {
|
|
@@ -587,32 +652,27 @@ static krb5_error_code answer_otp(krb5_context ctx,
|
|
|
|
kr->otp = true;
|
|
|
|
- if (kr->pd->cmd == SSS_PAM_PREAUTH) {
|
|
- for (i = 0; chl->tokeninfo[i] != NULL; i++) {
|
|
- DEBUG(SSSDBG_TRACE_ALL, "[%zu] Vendor [%s].\n",
|
|
- i, chl->tokeninfo[i]->vendor);
|
|
- DEBUG(SSSDBG_TRACE_ALL, "[%zu] Token-ID [%s].\n",
|
|
- i, chl->tokeninfo[i]->token_id);
|
|
- DEBUG(SSSDBG_TRACE_ALL, "[%zu] Challenge [%s].\n",
|
|
- i, chl->tokeninfo[i]->challenge);
|
|
- DEBUG(SSSDBG_TRACE_ALL, "[%zu] Flags [%d].\n",
|
|
- i, chl->tokeninfo[i]->flags);
|
|
- }
|
|
-
|
|
- if (chl->tokeninfo[0]->vendor != NULL) {
|
|
- kr->otp_vendor = talloc_strdup(kr, chl->tokeninfo[0]->vendor);
|
|
- }
|
|
- if (chl->tokeninfo[0]->token_id != NULL) {
|
|
- kr->otp_token_id = talloc_strdup(kr, chl->tokeninfo[0]->token_id);
|
|
- }
|
|
- if (chl->tokeninfo[0]->challenge != NULL) {
|
|
- kr->otp_challenge = talloc_strdup(kr, chl->tokeninfo[0]->challenge);
|
|
- }
|
|
- /* Allocation errors are ignored on purpose */
|
|
+ for (i = 0; chl->tokeninfo[i] != NULL; i++) {
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Vendor [%s].\n",
|
|
+ i, chl->tokeninfo[i]->vendor);
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Token-ID [%s].\n",
|
|
+ i, chl->tokeninfo[i]->token_id);
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Challenge [%s].\n",
|
|
+ i, chl->tokeninfo[i]->challenge);
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Flags [%d].\n",
|
|
+ i, chl->tokeninfo[i]->flags);
|
|
+ }
|
|
|
|
- DEBUG(SSSDBG_TRACE_INTERNAL, "Exit answer_otp during pre-auth.\n");
|
|
- return ERR_CHECK_NEXT_AUTH_TYPE;
|
|
+ if (chl->tokeninfo[0]->vendor != NULL) {
|
|
+ kr->otp_vendor = talloc_strdup(kr, chl->tokeninfo[0]->vendor);
|
|
}
|
|
+ if (chl->tokeninfo[0]->token_id != NULL) {
|
|
+ kr->otp_token_id = talloc_strdup(kr, chl->tokeninfo[0]->token_id);
|
|
+ }
|
|
+ if (chl->tokeninfo[0]->challenge != NULL) {
|
|
+ kr->otp_challenge = talloc_strdup(kr, chl->tokeninfo[0]->challenge);
|
|
+ }
|
|
+ /* Allocation errors are ignored on purpose */
|
|
|
|
/* Find the first supported tokeninfo which matches our authtoken. */
|
|
for (i = 0; chl->tokeninfo[i] != NULL; i++) {
|
|
@@ -705,6 +765,19 @@ done:
|
|
return res;
|
|
}
|
|
|
|
+static krb5_error_code request_pkinit(struct krb5_req *kr)
|
|
+{
|
|
+ krb5_error_code kerr;
|
|
+
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "Setting pkinit prompting.\n");
|
|
+ kerr = pam_add_response(kr->pd, SSS_CERT_AUTH_PROMPTING, 0, NULL);
|
|
+ if (kerr != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to add pkinit prompting data.\n");
|
|
+ }
|
|
+
|
|
+ return kerr;
|
|
+}
|
|
+
|
|
static krb5_error_code answer_pkinit(krb5_context ctx,
|
|
struct krb5_req *kr,
|
|
krb5_responder_context rctx)
|
|
@@ -715,6 +788,15 @@ static krb5_error_code answer_pkinit(krb5_context ctx,
|
|
const char *module_name = NULL;
|
|
krb5_responder_pkinit_challenge *chl = NULL;
|
|
size_t c;
|
|
+ enum sss_authtok_type type;
|
|
+
|
|
+ type = sss_authtok_get_type(kr->pd->authtok);
|
|
+ if (type != SSS_AUTHTOK_TYPE_SC_PIN && type != SSS_AUTHTOK_TYPE_SC_KEYPAD) {
|
|
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unexpected authentication token type [%s]\n",
|
|
+ sss_authtok_type_to_str(type));
|
|
+ kerr = ERR_CHECK_NEXT_AUTH_TYPE;
|
|
+ goto done;
|
|
+ }
|
|
|
|
kerr = krb5_responder_pkinit_get_challenge(ctx, rctx, &chl);
|
|
if (kerr != EOK || chl == NULL) {
|
|
@@ -737,58 +819,38 @@ static krb5_error_code answer_pkinit(krb5_context ctx,
|
|
DEBUG(SSSDBG_TRACE_ALL, "Setting pkinit_prompting.\n");
|
|
kr->pkinit_prompting = true;
|
|
|
|
- if (kr->pd->cmd == SSS_PAM_AUTHENTICATE) {
|
|
- if ((sss_authtok_get_type(kr->pd->authtok)
|
|
- == SSS_AUTHTOK_TYPE_SC_PIN
|
|
- || sss_authtok_get_type(kr->pd->authtok)
|
|
- == SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
|
|
- kerr = sss_authtok_get_sc(kr->pd->authtok, &pin, NULL,
|
|
- &token_name, NULL,
|
|
- &module_name, NULL,
|
|
- NULL, NULL, NULL, NULL);
|
|
- if (kerr != EOK) {
|
|
- DEBUG(SSSDBG_OP_FAILURE,
|
|
- "sss_authtok_get_sc failed.\n");
|
|
- goto done;
|
|
- }
|
|
-
|
|
- for (c = 0; chl->identities[c] != NULL; c++) {
|
|
- if (chl->identities[c]->identity != NULL
|
|
- && pkinit_identity_matches(chl->identities[c]->identity,
|
|
- token_name, module_name)) {
|
|
- break;
|
|
- }
|
|
- }
|
|
+ kerr = sss_authtok_get_sc(kr->pd->authtok, &pin, NULL,
|
|
+ &token_name, NULL,
|
|
+ &module_name, NULL,
|
|
+ NULL, NULL, NULL, NULL);
|
|
+ if (kerr != EOK) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "sss_authtok_get_sc failed.\n");
|
|
+ goto done;
|
|
+ }
|
|
|
|
- if (chl->identities[c] == NULL) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE,
|
|
- "No matching identity for [%s][%s] found in pkinit "
|
|
- "challenge.\n", token_name, module_name);
|
|
- kerr = EINVAL;
|
|
- goto done;
|
|
- }
|
|
+ for (c = 0; chl->identities[c] != NULL; c++) {
|
|
+ if (chl->identities[c]->identity != NULL
|
|
+ && pkinit_identity_matches(chl->identities[c]->identity,
|
|
+ token_name, module_name)) {
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
|
|
- kerr = krb5_responder_pkinit_set_answer(ctx, rctx,
|
|
- chl->identities[c]->identity,
|
|
- pin);
|
|
- if (kerr != 0) {
|
|
- DEBUG(SSSDBG_OP_FAILURE,
|
|
- "krb5_responder_set_answer failed.\n");
|
|
- }
|
|
+ if (chl->identities[c] == NULL) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "No matching identity for [%s][%s] found in pkinit "
|
|
+ "challenge.\n", token_name, module_name);
|
|
+ kerr = EINVAL;
|
|
+ goto done;
|
|
+ }
|
|
|
|
- goto done;
|
|
- } else {
|
|
- DEBUG(SSSDBG_MINOR_FAILURE,
|
|
- "Unexpected authentication token type [%s]\n",
|
|
- sss_authtok_type_to_str(sss_authtok_get_type(kr->pd->authtok)));
|
|
- kerr = ERR_CHECK_NEXT_AUTH_TYPE;
|
|
- goto done;
|
|
- }
|
|
- } else {
|
|
- /* We only expect SSS_PAM_PREAUTH here, but also for all other
|
|
- * commands the graceful solution would be to let the caller
|
|
- * check other authentication methods as well. */
|
|
- kerr = ERR_CHECK_NEXT_AUTH_TYPE;
|
|
+ kerr = krb5_responder_pkinit_set_answer(ctx, rctx,
|
|
+ chl->identities[c]->identity,
|
|
+ pin);
|
|
+ if (kerr != 0) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE,
|
|
+ "krb5_responder_set_answer failed.\n");
|
|
}
|
|
|
|
done:
|
|
@@ -820,11 +882,9 @@ static errno_t krb5_req_update(struct krb5_req *dest, struct krb5_req *src)
|
|
return EOK;
|
|
}
|
|
|
|
-static krb5_error_code idp_oauth2_preauth(struct krb5_req *kr,
|
|
- struct sss_idp_oauth2 *oauth2)
|
|
+static krb5_error_code idp_oauth2_method_req(struct krb5_req *kr,
|
|
+ struct sss_idp_oauth2 *oauth2)
|
|
{
|
|
- struct krb5_req *tmpkr = NULL;
|
|
- uint32_t offline;
|
|
errno_t ret;
|
|
|
|
if (oauth2->verification_uri == NULL || oauth2->user_code == NULL) {
|
|
@@ -842,6 +902,19 @@ static krb5_error_code idp_oauth2_preauth(struct krb5_req *kr,
|
|
return ret;
|
|
}
|
|
|
|
+done:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static krb5_error_code k5c_send_and_recv(struct krb5_req *kr)
|
|
+{
|
|
+ struct krb5_req *tmpkr = NULL;
|
|
+ uint32_t offline;
|
|
+ errno_t ret;
|
|
+
|
|
+ /* Challenge was presented. We need to continue the authentication
|
|
+ * with this exact child process in order to maintain internal Kerberos
|
|
+ * state so we are able to respond to this particular challenge. */
|
|
ret = k5c_attach_keep_alive_msg(kr);
|
|
if (ret != EOK) {
|
|
DEBUG(SSSDBG_CRIT_FAILURE, "k5c_attach_keep_alive_msg failed.\n");
|
|
@@ -878,15 +951,13 @@ done:
|
|
return ret;
|
|
}
|
|
|
|
-static krb5_error_code answer_idp_oauth2(krb5_context kctx,
|
|
- struct krb5_req *kr,
|
|
- krb5_responder_context rctx)
|
|
+static krb5_error_code request_idp_oauth2(krb5_context kctx,
|
|
+ struct krb5_req *kr,
|
|
+ krb5_responder_context rctx,
|
|
+ struct sss_idp_oauth2 **_data)
|
|
{
|
|
- enum sss_authtok_type type;
|
|
struct sss_idp_oauth2 *data;
|
|
const char *challenge;
|
|
- const char *token;
|
|
- size_t token_len;
|
|
krb5_error_code kerr;
|
|
|
|
challenge = krb5_responder_get_challenge(kctx, rctx,
|
|
@@ -902,18 +973,33 @@ static krb5_error_code answer_idp_oauth2(krb5_context kctx,
|
|
}
|
|
|
|
if (kr->pd->cmd == SSS_PAM_PREAUTH) {
|
|
- kerr = idp_oauth2_preauth(kr, data);
|
|
+ kerr = idp_oauth2_method_req(kr, data);
|
|
if (kerr != EOK) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
- if (kr->pd->cmd != SSS_PAM_AUTHENTICATE) {
|
|
- DEBUG(SSSDBG_OP_FAILURE, "Unexpected command [%d]\n", kr->pd->cmd);
|
|
- kerr = EINVAL;
|
|
- goto done;
|
|
+ *_data = data;
|
|
+ kerr = EOK;
|
|
+
|
|
+done:
|
|
+ if (kerr != EOK) {
|
|
+ sss_idp_oauth2_free(data);
|
|
}
|
|
|
|
+ return kerr;
|
|
+}
|
|
+
|
|
+static krb5_error_code answer_idp_oauth2(krb5_context kctx,
|
|
+ struct krb5_req *kr,
|
|
+ krb5_responder_context rctx,
|
|
+ struct sss_idp_oauth2 *data)
|
|
+{
|
|
+ enum sss_authtok_type type;
|
|
+ const char *token;
|
|
+ size_t token_len;
|
|
+ krb5_error_code kerr;
|
|
+
|
|
type = sss_authtok_get_type(kr->pd->authtok);
|
|
if (type != SSS_AUTHTOK_TYPE_OAUTH2) {
|
|
DEBUG(SSSDBG_MINOR_FAILURE, "Unexpected authentication token type [%s]\n",
|
|
@@ -952,8 +1038,6 @@ static krb5_error_code answer_idp_oauth2(krb5_context kctx,
|
|
kerr = EOK;
|
|
|
|
done:
|
|
- sss_idp_oauth2_free(data);
|
|
-
|
|
return kerr;
|
|
}
|
|
|
|
@@ -1038,11 +1122,9 @@ static errno_t k5c_attach_passkey_msg(struct krb5_req *kr,
|
|
return ret;
|
|
}
|
|
|
|
-static krb5_error_code passkey_preauth(struct krb5_req *kr,
|
|
- struct sss_passkey_challenge *passkey)
|
|
+static krb5_error_code passkey_method_req(struct krb5_req *kr,
|
|
+ struct sss_passkey_challenge *passkey)
|
|
{
|
|
- struct krb5_req *tmpkr = NULL;
|
|
- uint32_t offline;
|
|
errno_t ret;
|
|
|
|
if (passkey->domain == NULL || passkey->credential_id_list == NULL
|
|
@@ -1057,64 +1139,22 @@ static krb5_error_code passkey_preauth(struct krb5_req *kr,
|
|
return ret;
|
|
}
|
|
|
|
- /* Challenge was presented. We need to continue the authentication
|
|
- * with this exact child process in order to maintain internal Kerberos
|
|
- * state so we are able to respond to this particular challenge. */
|
|
- ret = k5c_attach_keep_alive_msg(kr);
|
|
- if (ret != EOK) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE, "k5c_attach_keep_alive_msg failed.\n");
|
|
- return ret;
|
|
- }
|
|
-
|
|
- tmpkr = talloc_zero(NULL, struct krb5_req);
|
|
- if (tmpkr == NULL) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
|
|
- ret = ENOMEM;
|
|
- goto done;
|
|
- }
|
|
-
|
|
- /* Send reply and wait for next step. */
|
|
- ret = k5c_send_data(kr, STDOUT_FILENO, ret);
|
|
- if (ret != EOK) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE, "Failed to send reply\n");
|
|
- }
|
|
-
|
|
- ret = k5c_recv_data(tmpkr, STDIN_FILENO, &offline);
|
|
- if (ret != EOK) {
|
|
- goto done;
|
|
- }
|
|
-
|
|
- ret = krb5_req_update(kr, tmpkr);
|
|
- if (ret != EOK) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE, "Unable to update krb request [%d]: %s\n",
|
|
- ret, sss_strerror(ret));
|
|
- goto done;
|
|
- }
|
|
-
|
|
done:
|
|
- talloc_free(tmpkr);
|
|
return ret;
|
|
}
|
|
#endif /* BUILD_PASSKEY */
|
|
|
|
-static krb5_error_code answer_passkey(krb5_context kctx,
|
|
- struct krb5_req *kr,
|
|
- krb5_responder_context rctx)
|
|
+static krb5_error_code request_passkey(krb5_context kctx,
|
|
+ struct krb5_req *kr,
|
|
+ krb5_responder_context rctx)
|
|
{
|
|
#ifndef BUILD_PASSKEY
|
|
DEBUG(SSSDBG_TRACE_FUNC, "Passkey auth not possible, SSSD built without passkey support!\n");
|
|
return EINVAL;
|
|
#else
|
|
- enum sss_authtok_type type;
|
|
struct sss_passkey_message *msg;
|
|
- struct sss_passkey_message *reply_msg = NULL;
|
|
const char *challenge;
|
|
- const char *reply;
|
|
- char *reply_str = NULL;
|
|
- enum sss_passkey_phase phase;
|
|
- const char *state;
|
|
- size_t reply_len;
|
|
- krb5_error_code kerr;
|
|
+ krb5_error_code kerr = EINVAL;
|
|
|
|
challenge = krb5_responder_get_challenge(kctx, rctx,
|
|
SSSD_PASSKEY_QUESTION);
|
|
@@ -1129,17 +1169,33 @@ static krb5_error_code answer_passkey(krb5_context kctx,
|
|
}
|
|
|
|
if (kr->pd->cmd == SSS_PAM_PREAUTH) {
|
|
- kerr = passkey_preauth(kr, msg->data.challenge);
|
|
+ kerr = passkey_method_req(kr, msg->data.challenge);
|
|
if (kerr != EOK) {
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
- if (kr->pd->cmd != SSS_PAM_AUTHENTICATE) {
|
|
- DEBUG(SSSDBG_OP_FAILURE, "Unexpected command [%d]\n", kr->pd->cmd);
|
|
- kerr = EINVAL;
|
|
- goto done;
|
|
- }
|
|
+done:
|
|
+ return kerr;
|
|
+#endif /* BUILD_PASSKEY */
|
|
+}
|
|
+
|
|
+static krb5_error_code answer_passkey(krb5_context kctx,
|
|
+ struct krb5_req *kr,
|
|
+ krb5_responder_context rctx)
|
|
+{
|
|
+#ifndef BUILD_PASSKEY
|
|
+ DEBUG(SSSDBG_TRACE_FUNC, "Passkey auth not possible, SSSD built without passkey support!\n");
|
|
+ return EINVAL;
|
|
+#else
|
|
+ enum sss_authtok_type type;
|
|
+ struct sss_passkey_message *reply_msg = NULL;
|
|
+ const char *reply;
|
|
+ char *reply_str = NULL;
|
|
+ enum sss_passkey_phase phase;
|
|
+ const char *state;
|
|
+ size_t reply_len;
|
|
+ krb5_error_code kerr;
|
|
|
|
type = sss_authtok_get_type(kr->pd->authtok);
|
|
if (type != SSS_AUTHTOK_TYPE_PASSKEY_REPLY) {
|
|
@@ -1199,44 +1255,185 @@ done:
|
|
#endif /* BUILD_PASSKEY */
|
|
}
|
|
|
|
+static krb5_error_code request_password(struct krb5_req *kr)
|
|
+{
|
|
+ krb5_error_code kerr;
|
|
+
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "Setting password prompting.\n");
|
|
+ kerr = pam_add_response(kr->pd, SSS_PASSWORD_PROMPTING, 0, NULL);
|
|
+ if (kerr != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to add password prompting data.\n");
|
|
+ }
|
|
+
|
|
+ return kerr;
|
|
+}
|
|
+
|
|
static krb5_error_code answer_password(krb5_context kctx,
|
|
struct krb5_req *kr,
|
|
krb5_responder_context rctx)
|
|
{
|
|
- krb5_error_code kerr;
|
|
- int ret;
|
|
+ krb5_error_code kerr = EINVAL;
|
|
const char *pwd;
|
|
+ enum sss_authtok_type type;
|
|
|
|
kr->password_prompting = true;
|
|
|
|
- if ((kr->pd->cmd == SSS_PAM_AUTHENTICATE
|
|
- || kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM
|
|
- || kr->pd->cmd == SSS_PAM_CHAUTHTOK)
|
|
- && (sss_authtok_get_type(kr->pd->authtok)
|
|
- == SSS_AUTHTOK_TYPE_PASSWORD
|
|
- || sss_authtok_get_type(kr->pd->authtok)
|
|
- == SSS_AUTHTOK_TYPE_PAM_STACKED)) {
|
|
- ret = sss_authtok_get_password(kr->pd->authtok, &pwd, NULL);
|
|
- if (ret != EOK) {
|
|
+ type = sss_authtok_get_type(kr->pd->authtok);
|
|
+ if (type != SSS_AUTHTOK_TYPE_PASSWORD
|
|
+ && type != SSS_AUTHTOK_TYPE_PAM_STACKED) {
|
|
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unexpected authentication token type [%s]\n",
|
|
+ sss_authtok_type_to_str(type));
|
|
+ kerr = ERR_CHECK_NEXT_AUTH_TYPE;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ if (kr->pd->cmd == SSS_PAM_AUTHENTICATE
|
|
+ || kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM
|
|
+ || kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
|
|
+ kerr = sss_authtok_get_password(kr->pd->authtok, &pwd, NULL);
|
|
+ if (kerr != EOK) {
|
|
DEBUG(SSSDBG_OP_FAILURE,
|
|
- "sss_authtok_get_password failed.\n");
|
|
- return ret;
|
|
+ "sss_authtok_get_password failed.\n");
|
|
+ goto done;
|
|
}
|
|
|
|
kerr = krb5_responder_set_answer(kctx, rctx,
|
|
- KRB5_RESPONDER_QUESTION_PASSWORD,
|
|
- pwd);
|
|
+ KRB5_RESPONDER_QUESTION_PASSWORD,
|
|
+ pwd);
|
|
if (kerr != 0) {
|
|
DEBUG(SSSDBG_OP_FAILURE,
|
|
- "krb5_responder_set_answer failed.\n");
|
|
+ "krb5_responder_set_answer failed.\n");
|
|
}
|
|
+ }
|
|
|
|
- return kerr;
|
|
+done:
|
|
+ return kerr;
|
|
+}
|
|
+
|
|
+static krb5_error_code sss_krb5_auth_methods_request(krb5_context ctx,
|
|
+ struct krb5_req *kr,
|
|
+ krb5_responder_context rctx,
|
|
+ const char * const *question_list,
|
|
+ struct sss_idp_oauth2 **_oath2_data)
|
|
+{
|
|
+ size_t c;
|
|
+ int count = 0;
|
|
+ krb5_error_code kerr = EINVAL;
|
|
+
|
|
+ if (kr->pd->cmd != SSS_PAM_PREAUTH) {
|
|
+ DEBUG(SSSDBG_TRACE_ALL,
|
|
+ "Unexpected state [%d], skipping methods request\n",
|
|
+ kr->pd->cmd);
|
|
+ kerr = EOK;
|
|
+ goto done;
|
|
}
|
|
|
|
- /* For SSS_PAM_PREAUTH and the other remaining commands the caller should
|
|
- * continue to iterate over the available authentication methods. */
|
|
- return ERR_CHECK_NEXT_AUTH_TYPE;
|
|
+ for (c = 0; question_list[c] != NULL; c++) {
|
|
+ kerr = EINVAL;
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "Got request [%s].\n", question_list[c]);
|
|
+
|
|
+ if (strcmp(question_list[c], KRB5_RESPONDER_QUESTION_PASSWORD) == 0) {
|
|
+ kerr = request_password(kr);
|
|
+ } else if (strcmp(question_list[c], KRB5_RESPONDER_QUESTION_PKINIT) == 0) {
|
|
+ kerr = request_pkinit(kr);
|
|
+ } else if (strcmp(question_list[c], SSSD_IDP_OAUTH2_QUESTION) == 0) {
|
|
+ kerr = request_idp_oauth2(ctx, kr, rctx, _oath2_data);
|
|
+ } else if (strcmp(question_list[c], SSSD_PASSKEY_QUESTION) == 0) {
|
|
+ kerr = request_passkey(ctx, kr, rctx);
|
|
+ } else if (strcmp(question_list[c], KRB5_RESPONDER_QUESTION_OTP) == 0) {
|
|
+ kerr = request_otp(ctx, kr, rctx);
|
|
+ } else {
|
|
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unknown question type [%s]\n", question_list[c]);
|
|
+ kerr = EINVAL;
|
|
+ }
|
|
+
|
|
+ if (kerr == EOK) {
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "Request found for %s\n", question_list[c]);
|
|
+ count++;
|
|
+ } else if (kerr == ENOENT) {
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "Request not found for %s\n", question_list[c]);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (count == 0 && (kerr != EOK && kerr != ENOENT)) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "Authentication method request error\n");
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ kerr = k5c_send_and_recv(kr);
|
|
+ if (kerr != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+done:
|
|
+ return kerr;
|
|
+}
|
|
+
|
|
+static krb5_error_code sss_krb5_auth_methods_answer(krb5_context ctx,
|
|
+ struct krb5_req *kr,
|
|
+ krb5_responder_context rctx,
|
|
+ const char * const *question_list,
|
|
+ struct sss_idp_oauth2 *oath2_data)
|
|
+{
|
|
+ size_t c;
|
|
+ krb5_error_code kerr = EINVAL;
|
|
+
|
|
+ if (kr->pd->cmd != SSS_PAM_AUTHENTICATE
|
|
+ && kr->pd->cmd != SSS_PAM_CHAUTHTOK_PRELIM
|
|
+ && kr->pd->cmd != SSS_PAM_CHAUTHTOK) {
|
|
+ DEBUG(SSSDBG_TRACE_ALL,
|
|
+ "Unexpected state [%d], skipping methods answer\n",
|
|
+ kr->pd->cmd);
|
|
+ kerr = EOK;
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ for (c = 0; question_list[c] != NULL; c++) {
|
|
+ DEBUG(SSSDBG_TRACE_ALL, "Got question [%s].\n", question_list[c]);
|
|
+
|
|
+ if (strcmp(question_list[c],
|
|
+ KRB5_RESPONDER_QUESTION_PASSWORD) == 0) {
|
|
+ kerr = answer_password(ctx, kr, rctx);
|
|
+ } else if (strcmp(question_list[c],
|
|
+ KRB5_RESPONDER_QUESTION_PKINIT) == 0) {
|
|
+ /* Skip answer_pkinit for expired password changes, e.g. user with auth types
|
|
+ * passkey AND password set */
|
|
+ if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM || kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
|
|
+ continue;
|
|
+ }
|
|
+ kerr = answer_pkinit(ctx, kr, rctx);
|
|
+ } else if (strcmp(question_list[c], SSSD_IDP_OAUTH2_QUESTION) == 0) {
|
|
+ kerr = answer_idp_oauth2(ctx, kr, rctx, oath2_data);
|
|
+ } else if (strcmp(question_list[c], SSSD_PASSKEY_QUESTION) == 0) {
|
|
+ /* Skip answer_passkey for expired password changes, e.g. user with auth types
|
|
+ * passkey AND password set */
|
|
+ if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM || kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
|
|
+ continue;
|
|
+ }
|
|
+ kerr = answer_passkey(ctx, kr, rctx);
|
|
+ } else if (strcmp(question_list[c], KRB5_RESPONDER_QUESTION_OTP) == 0) {
|
|
+ kerr = answer_otp(ctx, kr, rctx);
|
|
+ } else {
|
|
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unknown question type [%s]\n", question_list[c]);
|
|
+ kerr = EINVAL;
|
|
+ }
|
|
+
|
|
+ /* Continue to the next question when the given authtype cannot be
|
|
+ * handled by the answer_* function. This allows fallback between auth
|
|
+ * types, such as passkey -> password. */
|
|
+ if (kerr == ERR_CHECK_NEXT_AUTH_TYPE) {
|
|
+ DEBUG(SSSDBG_TRACE_ALL,
|
|
+ "Auth type [%s] could not be handled by answer "
|
|
+ "function, continuing to next question.\n",
|
|
+ question_list[c]);
|
|
+ continue;
|
|
+ } else {
|
|
+ goto done;
|
|
+ }
|
|
+ }
|
|
+
|
|
+done:
|
|
+ return kerr;
|
|
}
|
|
|
|
static krb5_error_code sss_krb5_responder(krb5_context ctx,
|
|
@@ -1244,8 +1441,8 @@ static krb5_error_code sss_krb5_responder(krb5_context ctx,
|
|
krb5_responder_context rctx)
|
|
{
|
|
struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
|
|
+ struct sss_idp_oauth2 *oath2_data = NULL;
|
|
const char * const *question_list;
|
|
- size_t c;
|
|
krb5_error_code kerr = EINVAL;
|
|
|
|
if (kr == NULL) {
|
|
@@ -1255,80 +1452,22 @@ static krb5_error_code sss_krb5_responder(krb5_context ctx,
|
|
question_list = krb5_responder_list_questions(ctx, rctx);
|
|
|
|
if (question_list != NULL) {
|
|
- for (c = 0; question_list[c] != NULL; c++) {
|
|
- DEBUG(SSSDBG_TRACE_ALL, "Got question [%s].\n", question_list[c]);
|
|
-
|
|
- /* It is expected that the answer_*() functions only return EOK
|
|
- * (success) if the authentication was successful, i.e. during
|
|
- * SSS_PAM_AUTHENTICATE. In all other cases, e.g. during
|
|
- * SSS_PAM_PREAUTH either ERR_CHECK_NEXT_AUTH_TYPE should be
|
|
- * returned to indicate that the other available authentication
|
|
- * methods should be checked as well. Or some other error code to
|
|
- * indicate a fatal error where no other methods should be tried.
|
|
- * Especially if setting the answer failed neither EOK nor
|
|
- * ERR_CHECK_NEXT_AUTH_TYPE should be returned. */
|
|
- if (strcmp(question_list[c],
|
|
- KRB5_RESPONDER_QUESTION_PASSWORD) == 0) {
|
|
- kerr = answer_password(ctx, kr, rctx);
|
|
- } else if (strcmp(question_list[c],
|
|
- KRB5_RESPONDER_QUESTION_PKINIT) == 0
|
|
- && (sss_authtok_get_type(kr->pd->authtok)
|
|
- == SSS_AUTHTOK_TYPE_SC_PIN
|
|
- || sss_authtok_get_type(kr->pd->authtok)
|
|
- == SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
|
|
- kerr = answer_pkinit(ctx, kr, rctx);
|
|
- } else if (strcmp(question_list[c], SSSD_IDP_OAUTH2_QUESTION) == 0) {
|
|
- kerr = answer_idp_oauth2(ctx, kr, rctx);
|
|
- } else if (strcmp(question_list[c], SSSD_PASSKEY_QUESTION) == 0) {
|
|
- /* Skip answer_passkey for expired password changes, e.g. user with auth types
|
|
- * passkey AND password set */
|
|
- if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM || kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
|
|
- continue;
|
|
- }
|
|
- kerr = answer_passkey(ctx, kr, rctx);
|
|
- } else if (strcmp(question_list[c], KRB5_RESPONDER_QUESTION_OTP) == 0) {
|
|
- kerr = answer_otp(ctx, kr, rctx);
|
|
- } else {
|
|
- DEBUG(SSSDBG_MINOR_FAILURE, "Unknown question type [%s]\n", question_list[c]);
|
|
- kerr = EINVAL;
|
|
- }
|
|
-
|
|
- /* Continue to the next question when the given authtype cannot be
|
|
- * handled by the answer_* function. This allows fallback between auth
|
|
- * types, such as passkey -> password. */
|
|
- if (kerr == ERR_CHECK_NEXT_AUTH_TYPE) {
|
|
- /* During pre-auth iterating over all authentication methods
|
|
- * is expected and no message will be displayed. */
|
|
- if (kr->pd->cmd == SSS_PAM_AUTHENTICATE) {
|
|
- DEBUG(SSSDBG_TRACE_ALL,
|
|
- "Auth type [%s] could not be handled by answer "
|
|
- "function, continuing to next question.\n",
|
|
- question_list[c]);
|
|
- }
|
|
- continue;
|
|
- } else {
|
|
- return kerr;
|
|
- }
|
|
+ kerr = sss_krb5_auth_methods_request(ctx, kr, rctx, question_list, &oath2_data);
|
|
+ if (kerr != EOK) {
|
|
+ goto done;
|
|
+ }
|
|
+ kerr = sss_krb5_auth_methods_answer(ctx, kr, rctx, question_list, oath2_data);
|
|
+ if (kerr != EOK) {
|
|
+ goto done;
|
|
}
|
|
} else {
|
|
kerr = answer_password(ctx, kr, rctx);
|
|
}
|
|
|
|
- /* During SSS_PAM_PREAUTH 'ERR_CHECK_NEXT_AUTH_TYPE' is expected because we
|
|
- * will run through all offered authentication methods and all are expect to
|
|
- * return 'ERR_CHECK_NEXT_AUTH_TYPE' in the positive case to indicate that
|
|
- * the other methods should be checked as well. If all methods are checked
|
|
- * we are done and should return success.
|
|
- * In the other steps, especially SSS_PAM_AUTHENTICATE, having
|
|
- * 'ERR_CHECK_NEXT_AUTH_TYPE' at this stage would mean that no method feels
|
|
- * responsible for the provided credentials i.e. authentication failed and
|
|
- * we should return an error.
|
|
- */
|
|
- if (kr->pd->cmd == SSS_PAM_PREAUTH) {
|
|
- return kerr == ERR_CHECK_NEXT_AUTH_TYPE ? 0 : kerr;
|
|
- } else {
|
|
- return kerr;
|
|
- }
|
|
+done:
|
|
+ sss_idp_oauth2_free(oath2_data);
|
|
+
|
|
+ return kerr;
|
|
}
|
|
#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_RESPONDER */
|
|
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From e6956137b7ba64f3da41adb504b9c70c7a93994c Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Fri, 7 Nov 2025 09:38:29 +0100
|
|
Subject: [PATCH 26/36] Responder: add `gdm-switchable-auth` to
|
|
`pam_p11_allowed_services` defaults
|
|
|
|
The `pam_p11_allowed_services` option now includes `gdm-switchable-auth`
|
|
as one of the default allowed PAM services for smartcard authentication.
|
|
The service was added alongside the other GDM-related services
|
|
(gdm-smartcard and gdm-password) for logical grouping.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/man/sssd.conf.5.xml | 5 +++++
|
|
src/responder/pam/pamsrv_p11.c | 2 +-
|
|
2 files changed, 6 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
|
|
index a4829366a..70c99da30 100644
|
|
--- a/src/man/sssd.conf.5.xml
|
|
+++ b/src/man/sssd.conf.5.xml
|
|
@@ -1849,6 +1849,11 @@ pam_p11_allowed_services = +my_pam_service, -login
|
|
gdm-password
|
|
</para>
|
|
</listitem>
|
|
+ <listitem>
|
|
+ <para>
|
|
+ gdm-switchable-auth
|
|
+ </para>
|
|
+ </listitem>
|
|
<listitem>
|
|
<para>
|
|
kdm
|
|
diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
|
|
index 1490ca28d..cd41bf3e9 100644
|
|
--- a/src/responder/pam/pamsrv_p11.c
|
|
+++ b/src/responder/pam/pamsrv_p11.c
|
|
@@ -276,7 +276,7 @@ static errno_t get_sc_services(TALLOC_CTX *mem_ctx, struct pam_ctx *pctx,
|
|
|
|
const char *default_sc_services[] = {
|
|
"login", "su", "su-l", "gdm-smartcard", "gdm-password", "kdm", "sudo",
|
|
- "sudo-i", "gnome-screensaver", "polkit-1", NULL,
|
|
+ "sudo-i", "gnome-screensaver", "gdm-switchable-auth", "polkit-1", NULL,
|
|
};
|
|
const int default_sc_services_size =
|
|
sizeof(default_sc_services) / sizeof(default_sc_services[0]);
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 03ebcb71fa69e15d1724d8f4a6ac8fbbe0f36a9d Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Wed, 12 Nov 2025 14:26:27 +0100
|
|
Subject: [PATCH 27/36] sss_client: prevent JSON auth during password change
|
|
preauth
|
|
|
|
When a user's password expires after successful JSON authentication, the
|
|
fallback to traditional password change fails. Add
|
|
PAM_CLI_FLAGS_CHAUTHTOK_PREAUTH flag to distinguish password change
|
|
preauth from normal authentication preauth. When this flag is set, the
|
|
PAM responder skips JSON message generation and returns traditional
|
|
preauth data instead.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_cmd.c | 4 ++--
|
|
src/sss_client/pam_sss.c | 2 ++
|
|
src/sss_client/sss_cli.h | 2 ++
|
|
3 files changed, 6 insertions(+), 2 deletions(-)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
|
|
index 6b45c47c7..c09c9b987 100644
|
|
--- a/src/responder/pam/pamsrv_cmd.c
|
|
+++ b/src/responder/pam/pamsrv_cmd.c
|
|
@@ -1566,8 +1566,8 @@ void pam_reply(struct pam_auth_req *preq)
|
|
#endif /* BUILD_PASSKEY */
|
|
|
|
#ifdef HAVE_GDM_CUSTOM_JSON_PAM_EXTENSION
|
|
- if (is_pam_json_enabled(pctx->json_services,
|
|
- pd->service)) {
|
|
+ if (is_pam_json_enabled(pctx->json_services, pd->service) &&
|
|
+ !(pd->cli_flags & PAM_CLI_FLAGS_CHAUTHTOK_PREAUTH)) {
|
|
ret = generate_json_auth_message(pctx->rctx->cdb, pc_list, pd);
|
|
if (ret != EOK) {
|
|
DEBUG(SSSDBG_CRIT_FAILURE,
|
|
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
|
|
index ba25959d5..8d104f4fd 100644
|
|
--- a/src/sss_client/pam_sss.c
|
|
+++ b/src/sss_client/pam_sss.c
|
|
@@ -3156,6 +3156,8 @@ static int pam_sss(enum sss_cli_command task, pam_handle_t *pamh,
|
|
&& (pi.pam_authtok == NULL
|
|
|| (flags & PAM_CLI_FLAGS_PROMPT_ALWAYS))
|
|
&& access(PAM_PREAUTH_INDICATOR, F_OK) == 0) {
|
|
+ /* Set flag to indicate this preauth is for password change */
|
|
+ pi.flags |= PAM_CLI_FLAGS_CHAUTHTOK_PREAUTH;
|
|
pam_status = send_and_receive(pamh, &pi, SSS_PAM_PREAUTH,
|
|
quiet_mode);
|
|
if (pam_status != PAM_SUCCESS) {
|
|
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
|
|
index 4e3c58c31..cadf9be07 100644
|
|
--- a/src/sss_client/sss_cli.h
|
|
+++ b/src/sss_client/sss_cli.h
|
|
@@ -429,6 +429,8 @@ enum pam_item_type {
|
|
#define PAM_CLI_FLAGS_PROMPT_ALWAYS (1 << 7)
|
|
#define PAM_CLI_FLAGS_TRY_CERT_AUTH (1 << 8)
|
|
#define PAM_CLI_FLAGS_REQUIRE_CERT_AUTH (1 << 9)
|
|
+#define PAM_CLI_FLAGS_ALLOW_CHAUTHTOK_BY_ROOT (1 << 10)
|
|
+#define PAM_CLI_FLAGS_CHAUTHTOK_PREAUTH (1 << 11)
|
|
|
|
#define SSS_NSS_MAX_ENTRIES 256
|
|
#define SSS_NSS_HEADER_SIZE (sizeof(uint32_t) * 4)
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From d87b3fd6eedf2e142e8ced3fdd1b101a4b397543 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Thu, 13 Nov 2025 16:17:05 +0100
|
|
Subject: [PATCH 28/36] Responder: change authentication mechanism detection
|
|
|
|
Use `pam_get_auth_types()` to detect the available mechanisms for a
|
|
user.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/responder/pam/pamsrv_json.c | 49 +++++++++++++++++------------
|
|
src/tests/cmocka/test_pamsrv_json.c | 4 +++
|
|
2 files changed, 33 insertions(+), 20 deletions(-)
|
|
|
|
diff --git a/src/responder/pam/pamsrv_json.c b/src/responder/pam/pamsrv_json.c
|
|
index 37d3c276a..7f7e03bf3 100644
|
|
--- a/src/responder/pam/pamsrv_json.c
|
|
+++ b/src/responder/pam/pamsrv_json.c
|
|
@@ -511,6 +511,7 @@ init_auth_data(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
|
|
struct auth_data **_auth_data)
|
|
{
|
|
struct cert_auth_info *cert_list = NULL;
|
|
+ struct pam_resp_auth_type types;
|
|
errno_t ret = EOK;
|
|
|
|
*_auth_data = talloc_zero(mem_ctx, struct auth_data);
|
|
@@ -526,7 +527,6 @@ init_auth_data(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
|
|
ret = ENOMEM;
|
|
goto done;
|
|
}
|
|
- (*_auth_data)->pswd->enabled = true;
|
|
|
|
(*_auth_data)->oauth2 = talloc_zero(mem_ctx, struct oauth2_data);
|
|
if ((*_auth_data)->oauth2 == NULL) {
|
|
@@ -534,7 +534,6 @@ init_auth_data(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
|
|
ret = ENOMEM;
|
|
goto done;
|
|
}
|
|
- (*_auth_data)->oauth2->enabled = true;
|
|
|
|
(*_auth_data)->sc = talloc_zero(mem_ctx, struct sc_data);
|
|
if ((*_auth_data)->sc == NULL) {
|
|
@@ -542,7 +541,6 @@ init_auth_data(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
|
|
ret = ENOMEM;
|
|
goto done;
|
|
}
|
|
- (*_auth_data)->sc->enabled = true;
|
|
|
|
(*_auth_data)->passkey = talloc_zero(mem_ctx, struct passkey_data);
|
|
if ((*_auth_data)->passkey == NULL) {
|
|
@@ -550,7 +548,16 @@ init_auth_data(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
|
|
ret = ENOMEM;
|
|
goto done;
|
|
}
|
|
- (*_auth_data)->passkey->enabled = true;
|
|
+
|
|
+ ret = pam_get_auth_types(pd, &types);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to get authentication types\n");
|
|
+ goto done;
|
|
+ }
|
|
+ (*_auth_data)->pswd->enabled = types.password_auth;
|
|
+ (*_auth_data)->oauth2->enabled = true;
|
|
+ (*_auth_data)->sc->enabled = types.cert_auth;
|
|
+ (*_auth_data)->passkey->enabled = types.passkey_auth;
|
|
|
|
ret = obtain_prompts(cdb, mem_ctx, pc_list, *_auth_data);
|
|
if (ret != EOK) {
|
|
@@ -566,26 +573,28 @@ init_auth_data(TALLOC_CTX *mem_ctx, struct confdb_ctx *cdb,
|
|
goto done;
|
|
}
|
|
|
|
- ret = get_cert_list(mem_ctx, pd, &cert_list);
|
|
- if (ret == ENOENT) {
|
|
- (*_auth_data)->sc->enabled = false;
|
|
- } else if (ret != EOK) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE,
|
|
- "Failure to obtain smartcard certificate list.\n");
|
|
- goto done;
|
|
- }
|
|
+ if ((*_auth_data)->sc->enabled) {
|
|
+ ret = get_cert_list(mem_ctx, pd, &cert_list);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE,
|
|
+ "Failure to obtain smartcard certificate list.\n");
|
|
+ goto done;
|
|
+ }
|
|
|
|
- ret = get_cert_data(mem_ctx, cert_list, *_auth_data);
|
|
- if (ret != EOK) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE, "Failure to obtain smartcard labels.\n");
|
|
- goto done;
|
|
+ ret = get_cert_data(mem_ctx, cert_list, *_auth_data);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failure to obtain smartcard labels.\n");
|
|
+ goto done;
|
|
+ }
|
|
}
|
|
|
|
#ifdef BUILD_PASSKEY
|
|
- ret = obtain_passkey_data(mem_ctx, pd, *_auth_data);
|
|
- if (ret != EOK) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE, "Failure to obtain passkey data.\n");
|
|
- goto done;
|
|
+ if ((*_auth_data)->passkey->enabled) {
|
|
+ ret = obtain_passkey_data(mem_ctx, pd, *_auth_data);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failure to obtain passkey data.\n");
|
|
+ goto done;
|
|
+ }
|
|
}
|
|
#else
|
|
(*_auth_data)->passkey->enabled = false;
|
|
diff --git a/src/tests/cmocka/test_pamsrv_json.c b/src/tests/cmocka/test_pamsrv_json.c
|
|
index d432ea065..0430d2651 100644
|
|
--- a/src/tests/cmocka/test_pamsrv_json.c
|
|
+++ b/src/tests/cmocka/test_pamsrv_json.c
|
|
@@ -775,6 +775,8 @@ void test_generate_json_message_integration(void **state)
|
|
pd = talloc_zero(test_ctx, struct pam_data);
|
|
assert_non_null(pd);
|
|
|
|
+ ret = pam_add_response(pd, SSS_PASSWORD_PROMPTING, 0, NULL);
|
|
+ assert_int_equal(ret, EOK);
|
|
len = strlen(OAUTH2_URI)+1+strlen(OAUTH2_URI_COMP)+1+strlen(OAUTH2_CODE)+1;
|
|
ret = pam_add_response(pd, SSS_PAM_OAUTH2_INFO, len,
|
|
discard_const(OAUTH2_STR));
|
|
@@ -789,6 +791,8 @@ void test_generate_json_message_integration(void **state)
|
|
strlen(SC2_PROMPT_STR)+1+strlen(SC2_PAM_CERT_USER)+1;
|
|
ret = pam_add_response(pd, SSS_PAM_CERT_INFO, len, discard_const(SC2_STR));
|
|
assert_int_equal(ret, EOK);
|
|
+ ret = pam_add_response(pd, SSS_CERT_AUTH_PROMPTING, 0, NULL);
|
|
+ assert_int_equal(ret, EOK);
|
|
len = strlen(prompt_pin)+1;
|
|
ret = pam_add_response(pd, SSS_PAM_PASSKEY_INFO, len,
|
|
discard_const(prompt_pin));
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 8395c756ebfabc631fb21836d269b748cd1723d4 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Tue, 18 Nov 2025 16:08:15 +0100
|
|
Subject: [PATCH 29/36] man: clarify and fix `pam_json_services` compilation
|
|
|
|
Add a note to clarify that 2FA isn't supported in JSON protocol and fix
|
|
man page compilation for `pam_json_services` option.
|
|
|
|
:feature: Unified passwordless login in the GUI. SSSD now supports a
|
|
rich authentication selection interface. Users can login with
|
|
smartcards, passkey, External IdPs and passwords directly
|
|
within the graphical user interface.
|
|
:packaging: SSSD now supports authentication mechanism selection through
|
|
PAM using a JSON-based protocol. This feature enables
|
|
passwordless authentication mechanisms in GUI login
|
|
environments that support the protocol.
|
|
Feature will be supported by GNOME Display Manager (GDM)
|
|
starting with GNOME 50. While currently optimized for GNOME,
|
|
the JSON protocol design allows for future support in other
|
|
display managers.
|
|
authselect is the recommended approach and will handle the
|
|
necessary PAM stack modifications automatically starting
|
|
with version 1.7 through the new option `with-switch-auth`
|
|
which provides a new PAM service called `switchable-auth`.
|
|
Manual PAM configuration is also possible.
|
|
For more technical details and implementation specifications,
|
|
see the design documentation:
|
|
https://github.com/SSSD/sssd.io/pull/79
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/man/Makefile.am | 2 +-
|
|
src/man/sssd.conf.5.xml | 5 +++++
|
|
2 files changed, 6 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/src/man/Makefile.am b/src/man/Makefile.am
|
|
index 876561289..012194180 100644
|
|
--- a/src/man/Makefile.am
|
|
+++ b/src/man/Makefile.am
|
|
@@ -72,7 +72,7 @@ JSON_PAM_CONDS = ;build_json_pam
|
|
endif
|
|
|
|
|
|
-CONDS = with_false$(SUDO_CONDS)$(AUTOFS_CONDS)$(SSH_CONDS)$(PAC_RESPONDER_CONDS)$(IFP_CONDS)$(GPO_CONDS)$(SYSTEMD_CONDS)$(KCM_CONDS)$(STAP_CONDS)$(KCM_RENEWAL_CONDS)$(LOCKFREE_CLIENT_CONDS)$(HAVE_INOTIFY_CONDS)$(SUBID_CONDS)$(PASSKEY_CONDS)$(FILES_PROVIDER_CONDS)$(SSSD_NON_ROOT_USER_CONDS)$(LIBNL_CONDS)$(SAMBA_CONDS)
|
|
+CONDS = with_false$(SUDO_CONDS)$(AUTOFS_CONDS)$(SSH_CONDS)$(PAC_RESPONDER_CONDS)$(IFP_CONDS)$(GPO_CONDS)$(SYSTEMD_CONDS)$(KCM_CONDS)$(STAP_CONDS)$(KCM_RENEWAL_CONDS)$(LOCKFREE_CLIENT_CONDS)$(HAVE_INOTIFY_CONDS)$(SUBID_CONDS)$(PASSKEY_CONDS)$(FILES_PROVIDER_CONDS)$(SSSD_NON_ROOT_USER_CONDS)$(LIBNL_CONDS)$(SAMBA_CONDS)$(JSON_PAM_CONDS)
|
|
|
|
|
|
#Special Rules:
|
|
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
|
|
index 70c99da30..d817a6b11 100644
|
|
--- a/src/man/sssd.conf.5.xml
|
|
+++ b/src/man/sssd.conf.5.xml
|
|
@@ -2131,6 +2131,11 @@ pam_json_services = gdm-switchable-auth
|
|
<para>
|
|
Default: - (JSON protocol is disabled)
|
|
</para>
|
|
+ <para>
|
|
+ Note: 2-Factor Authentication (2FA) is not
|
|
+ supported. If 2FA is required, do not
|
|
+ activate the JSON protocol.
|
|
+ </para>
|
|
</listitem>
|
|
</varlistentry>
|
|
</variablelist>
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From c354593c4ad5d2032215639f7bd7903c5e4602bd Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Thu, 20 Nov 2025 16:33:41 +0100
|
|
Subject: [PATCH 30/36] krb5: port pre-authentication retry logic
|
|
|
|
Port the pre-authentication retry logic from the IPA provider to the
|
|
krb5 provider, making it available to all krb5-based authentication
|
|
flows.
|
|
|
|
Relates: 6c1272edf1 ("krb5: Add fallback password change support")
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
---
|
|
src/providers/krb5/krb5_auth.c | 50 ++++++++++++++++++++++++++++++++++
|
|
1 file changed, 50 insertions(+)
|
|
|
|
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
|
|
index fb2f58869..0eacb5523 100644
|
|
--- a/src/providers/krb5/krb5_auth.c
|
|
+++ b/src/providers/krb5/krb5_auth.c
|
|
@@ -1282,10 +1282,14 @@ int krb5_auth_recv(struct tevent_req *req, int *pam_status, int *dp_err)
|
|
}
|
|
|
|
struct krb5_pam_handler_state {
|
|
+ struct tevent_context *ev;
|
|
+ struct be_ctx *be_ctx;
|
|
struct pam_data *pd;
|
|
+ struct krb5_ctx *krb5_ctx;
|
|
};
|
|
|
|
static void krb5_pam_handler_auth_done(struct tevent_req *subreq);
|
|
+static void krb5_pam_handler_auth_retry_done(struct tevent_req *subreq);
|
|
static void krb5_pam_handler_access_done(struct tevent_req *subreq);
|
|
|
|
struct tevent_req *
|
|
@@ -1305,7 +1309,10 @@ krb5_pam_handler_send(TALLOC_CTX *mem_ctx,
|
|
return NULL;
|
|
}
|
|
|
|
+ state->ev = params->ev;
|
|
+ state->be_ctx = params->be_ctx;
|
|
state->pd = pd;
|
|
+ state->krb5_ctx = krb5_ctx;
|
|
|
|
switch (pd->cmd) {
|
|
case SSS_PAM_AUTHENTICATE:
|
|
@@ -1372,6 +1379,49 @@ static void krb5_pam_handler_auth_done(struct tevent_req *subreq)
|
|
state->pd->pam_status = PAM_SYSTEM_ERR;
|
|
}
|
|
|
|
+ if (state->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM
|
|
+ && state->pd->pam_status == PAM_TRY_AGAIN) {
|
|
+ /* Reset this to fork a new krb5_child in handle_child_send() */
|
|
+ state->pd->child_pid = 0;
|
|
+ subreq = krb5_auth_queue_send(state, state->ev, state->be_ctx, state->pd,
|
|
+ state->krb5_ctx);
|
|
+ if (subreq == NULL) {
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
+ tevent_req_set_callback(subreq, krb5_pam_handler_auth_retry_done, req);
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ /* PAM_CRED_ERR is used to indicate to the IPA provider that trying
|
|
+ * password migration would make sense. From this point on it isn't
|
|
+ * necessary to keep this status, so it can be translated to PAM_AUTH_ERR.
|
|
+ */
|
|
+ if (state->pd->pam_status == PAM_CRED_ERR) {
|
|
+ state->pd->pam_status = PAM_AUTH_ERR;
|
|
+ }
|
|
+
|
|
+done:
|
|
+ /* TODO For backward compatibility we always return EOK to DP now. */
|
|
+ tevent_req_done(req);
|
|
+}
|
|
+
|
|
+static void krb5_pam_handler_auth_retry_done(struct tevent_req *subreq)
|
|
+{
|
|
+ struct krb5_pam_handler_state *state;
|
|
+ struct tevent_req *req;
|
|
+ errno_t ret;
|
|
+
|
|
+ req = tevent_req_callback_data(subreq, struct tevent_req);
|
|
+ state = tevent_req_data(req, struct krb5_pam_handler_state);
|
|
+
|
|
+ ret = krb5_auth_queue_recv(subreq, &state->pd->pam_status, NULL);
|
|
+ talloc_free(subreq);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_OP_FAILURE, "krb5_auth_recv request failed.\n");
|
|
+ state->pd->pam_status = PAM_SYSTEM_ERR;
|
|
+ }
|
|
+
|
|
/* PAM_CRED_ERR is used to indicate to the IPA provider that trying
|
|
* password migration would make sense. From this point on it isn't
|
|
* necessary to keep this status, so it can be translated to PAM_AUTH_ERR.
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 314233a2ebfe75f12f34e7a953a526949cca6f3f Mon Sep 17 00:00:00 2001
|
|
From: Sumit Bose <sbose@redhat.com>
|
|
Date: Thu, 11 Dec 2025 11:03:17 +0100
|
|
Subject: [PATCH 31/36] krb5: fix OTP authentication
|
|
|
|
Resolves: https://github.com/SSSD/sssd/issues/8292
|
|
Reviewed-by: Justin Stephenson <jstephen@redhat.com>
|
|
(cherry picked from commit 60ba493e9664caecd9496901e8b9639c874b04ee)
|
|
---
|
|
src/providers/krb5/krb5_child.c | 11 +++++------
|
|
1 file changed, 5 insertions(+), 6 deletions(-)
|
|
|
|
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
|
|
index 15bef2d00..ddb8b6f46 100644
|
|
--- a/src/providers/krb5/krb5_child.c
|
|
+++ b/src/providers/krb5/krb5_child.c
|
|
@@ -584,6 +584,8 @@ static krb5_error_code request_otp(krb5_context ctx,
|
|
goto done;
|
|
}
|
|
|
|
+ kr->otp = true;
|
|
+
|
|
for (i = 0; chl->tokeninfo[i] != NULL; i++) {
|
|
DEBUG(SSSDBG_TRACE_ALL, "[%zu] Vendor [%s].\n",
|
|
i, chl->tokeninfo[i]->vendor);
|
|
@@ -607,12 +609,9 @@ static krb5_error_code request_otp(krb5_context ctx,
|
|
/* Allocation errors are ignored on purpose */
|
|
|
|
DEBUG(SSSDBG_TRACE_ALL, "Setting otp prompting.\n");
|
|
- if (kr->otp) {
|
|
- kerr = k5c_attach_otp_info_msg(kr);
|
|
- if (kerr != EOK) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE,
|
|
- "Failed to add otp prompting data.\n");
|
|
- }
|
|
+ kerr = k5c_attach_otp_info_msg(kr);
|
|
+ if (kerr != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to add otp prompting data.\n");
|
|
}
|
|
|
|
done:
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 08b512d85af642a7735741bb660f0902d4d21dd5 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Thu, 11 Dec 2025 12:25:00 +0100
|
|
Subject: [PATCH 32/36] krb5_child: fix OTP authentication for PAM stacked
|
|
tokens
|
|
|
|
The `tokeninfo_matches()` function already handles PAM stacked tokens
|
|
correctly by processing them through the 2FA single path, so the
|
|
`answer_otp()` function should allow this token type to proceed.
|
|
|
|
Add SSS_AUTHTOK_TYPE_PAM_STACKED to the allowed authentication token
|
|
types in `answer_otp()` to restore previous functionality.
|
|
|
|
Fixes: 4cb99a248 ("krb5_child: advertise authentication methods").
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Reviewed-by: Justin Stephenson <jstephen@redhat.com>
|
|
(cherry picked from commit df15165db43eeb380c2c2af0eb4492c647c266d5)
|
|
---
|
|
src/providers/krb5/krb5_child.c | 3 ++-
|
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
|
|
index ddb8b6f46..d74ea0745 100644
|
|
--- a/src/providers/krb5/krb5_child.c
|
|
+++ b/src/providers/krb5/krb5_child.c
|
|
@@ -631,7 +631,8 @@ static krb5_error_code answer_otp(krb5_context ctx,
|
|
|
|
type = sss_authtok_get_type(kr->pd->authtok);
|
|
if (type != SSS_AUTHTOK_TYPE_2FA_SINGLE
|
|
- && type != SSS_AUTHTOK_TYPE_2FA) {
|
|
+ && type != SSS_AUTHTOK_TYPE_2FA
|
|
+ && type != SSS_AUTHTOK_TYPE_PAM_STACKED) {
|
|
DEBUG(SSSDBG_MINOR_FAILURE, "Unexpected authentication token type [%s]\n",
|
|
sss_authtok_type_to_str(type));
|
|
return ERR_CHECK_NEXT_AUTH_TYPE;
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From fb2a7d0479457ecc173e58c187c28169e1df2d27 Mon Sep 17 00:00:00 2001
|
|
From: Alexey Tikhonov <atikhono@redhat.com>
|
|
Date: Fri, 9 Jan 2026 13:33:42 +0100
|
|
Subject: [PATCH 33/36] KRB5_CHILD: allow `k5c_ccache_setup()` during
|
|
SSS_PAM_PREAUTH
|
|
|
|
This should cover a case when a single execution of 'krb5_child'
|
|
handles both PREAUTH and AUTH
|
|
|
|
Resolves: https://github.com/SSSD/sssd/issues/8331
|
|
Reviewed-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Reviewed-by: Sumit Bose <sbose@redhat.com>
|
|
(cherry picked from commit e2273e09a8e539fdb156bd1aba56c2d4210a7ad3)
|
|
---
|
|
src/providers/krb5/krb5_child.c | 17 +++++++++--------
|
|
1 file changed, 9 insertions(+), 8 deletions(-)
|
|
|
|
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
|
|
index d74ea0745..23808a397 100644
|
|
--- a/src/providers/krb5/krb5_child.c
|
|
+++ b/src/providers/krb5/krb5_child.c
|
|
@@ -864,7 +864,10 @@ static errno_t krb5_req_update(struct krb5_req *dest, struct krb5_req *src)
|
|
/* Check request validity. This should never happen, but it is better to
|
|
* be little paranoid. */
|
|
if (strcmp(dest->ccname, src->ccname) != 0) {
|
|
- return EINVAL;
|
|
+ /* Let's check if 'old_ccname' was reused during PREAUTH */
|
|
+ if (!src->old_ccname || (strcmp(dest->ccname, src->old_ccname) != 0)) {
|
|
+ return EINVAL;
|
|
+ }
|
|
}
|
|
|
|
if (strcmp(dest->upn, src->upn) != 0) {
|
|
@@ -4205,13 +4208,11 @@ static krb5_error_code privileged_krb5_setup(struct krb5_req *kr,
|
|
}
|
|
|
|
/* For ccache types FILE: and DIR: we might need to create some directory
|
|
- * components as root. Cache files are not needed during preauth. */
|
|
- if (kr->pd->cmd != SSS_PAM_PREAUTH) {
|
|
- ret = k5c_ccache_setup(kr, offline);
|
|
- if (ret != EOK) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE, "k5c_ccache_setup failed.\n");
|
|
- return ret;
|
|
- }
|
|
+ * components as root. */
|
|
+ ret = k5c_ccache_setup(kr, offline);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "k5c_ccache_setup() failed.\n");
|
|
+ return ret;
|
|
}
|
|
|
|
if (!(offline ||
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From abbd851d2a5e776bcc4d6978731079ecfb7d5c68 Mon Sep 17 00:00:00 2001
|
|
From: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Date: Mon, 12 Jan 2026 11:47:17 +0100
|
|
Subject: [PATCH 34/36] krb5_child: fix enterprise principal parsing in
|
|
keep-alive sessions
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
When keep-alive sessions transition between command types (e.g., from
|
|
SSS_PAM_PREAUTH to SSS_PAM_AUTHENTICATE), enterprise principal settings
|
|
were not being updated, causing parsing inconsistencies in complex AD
|
|
environments.
|
|
|
|
This change ensures that when the backend sends updated enterprise
|
|
principal settings for different command types, the principals are
|
|
correctly re-parsed with the appropriate flags, fixing UPN handling in
|
|
multi-domain AD environments.
|
|
|
|
Signed-off-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Reviewed-by: Alejandro López <allopez@redhat.com>
|
|
Reviewed-by: Sumit Bose <sbose@redhat.com>
|
|
(cherry picked from commit dd3cd958d5b06e2429921ae46feeddfe137a1f7e)
|
|
---
|
|
src/providers/krb5/krb5_child.c | 51 +++++++++++++++++++++++++++------
|
|
1 file changed, 42 insertions(+), 9 deletions(-)
|
|
|
|
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
|
|
index 23808a397..d485f263b 100644
|
|
--- a/src/providers/krb5/krb5_child.c
|
|
+++ b/src/providers/krb5/krb5_child.c
|
|
@@ -144,6 +144,7 @@ static errno_t k5c_attach_passkey_msg(struct krb5_req *kr, struct sss_passkey_ch
|
|
static errno_t k5c_attach_keep_alive_msg(struct krb5_req *kr);
|
|
static errno_t k5c_recv_data(struct krb5_req *kr, int fd, uint32_t *offline);
|
|
static errno_t k5c_send_data(struct krb5_req *kr, int fd, errno_t error);
|
|
+static int k5c_setup(struct krb5_req *kr, uint32_t offline);
|
|
|
|
static errno_t k5c_become_user(uid_t uid, gid_t gid, bool is_posix)
|
|
{
|
|
@@ -882,6 +883,12 @@ static errno_t krb5_req_update(struct krb5_req *dest, struct krb5_req *src)
|
|
talloc_free(dest->pd);
|
|
dest->pd = talloc_steal(dest, src->pd);
|
|
|
|
+ /* Update settings that may change between commands */
|
|
+ dest->use_enterprise_princ = src->use_enterprise_princ;
|
|
+ dest->validate = src->validate;
|
|
+ dest->posix_domain = src->posix_domain;
|
|
+ dest->send_pac = src->send_pac;
|
|
+
|
|
return EOK;
|
|
}
|
|
|
|
@@ -949,6 +956,13 @@ static krb5_error_code k5c_send_and_recv(struct krb5_req *kr)
|
|
goto done;
|
|
}
|
|
|
|
+ ret = k5c_setup(kr, offline);
|
|
+ if (ret != EOK) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "k5c_setup failed during keep-alive [%d]: %s\n",
|
|
+ ret, sss_strerror(ret));
|
|
+ goto done;
|
|
+ }
|
|
+
|
|
done:
|
|
talloc_free(tmpkr);
|
|
return ret;
|
|
@@ -4037,6 +4051,7 @@ static int k5c_ccache_setup(struct krb5_req *kr, uint32_t offline)
|
|
|
|
static int k5c_setup(struct krb5_req *kr, uint32_t offline)
|
|
{
|
|
+ krb5_principal princ;
|
|
krb5_error_code kerr;
|
|
int parse_flags;
|
|
|
|
@@ -4064,28 +4079,46 @@ static int k5c_setup(struct krb5_req *kr, uint32_t offline)
|
|
}
|
|
|
|
parse_flags = kr->use_enterprise_princ ? KRB5_PRINCIPAL_PARSE_ENTERPRISE : 0;
|
|
- kerr = sss_krb5_parse_name_flags(kr->ctx, kr->upn, parse_flags, &kr->princ);
|
|
+ kerr = sss_krb5_parse_name_flags(kr->ctx, kr->upn, parse_flags, &princ);
|
|
if (kerr != 0) {
|
|
KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
|
|
return kerr;
|
|
}
|
|
+ if (kr->princ == NULL || !krb5_principal_compare(kr->ctx, kr->princ, princ)) {
|
|
+ DEBUG(SSSDBG_TRACE_FUNC, "Updating principal\n");
|
|
+ if (kr->princ != NULL) {
|
|
+ krb5_free_principal(kr->ctx, kr->princ);
|
|
+ }
|
|
+ kr->princ = princ;
|
|
+ } else {
|
|
+ DEBUG(SSSDBG_TRACE_FUNC, "Principal unchanged, keeping existing\n");
|
|
+ krb5_free_principal(kr->ctx, princ);
|
|
+ }
|
|
|
|
- kerr = krb5_parse_name(kr->ctx, kr->upn, &kr->princ_orig);
|
|
- if (kerr != 0) {
|
|
- KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
|
|
- return kerr;
|
|
+ if (kr->princ_orig == NULL) {
|
|
+ kerr = krb5_parse_name(kr->ctx, kr->upn, &kr->princ_orig);
|
|
+ if (kerr != 0) {
|
|
+ KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
|
|
+ return kerr;
|
|
+ }
|
|
}
|
|
|
|
+ sss_krb5_free_unparsed_name(kr->ctx, kr->name);
|
|
+ kr->name = NULL;
|
|
kerr = krb5_unparse_name(kr->ctx, kr->princ, &kr->name);
|
|
if (kerr != 0) {
|
|
KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
|
|
return kerr;
|
|
}
|
|
|
|
- kr->creds = calloc(1, sizeof(krb5_creds));
|
|
- if (kr->creds == NULL) {
|
|
- DEBUG(SSSDBG_CRIT_FAILURE, "calloc failed.\n");
|
|
- return ENOMEM;
|
|
+ if (kr->creds != NULL) {
|
|
+ krb5_free_cred_contents(kr->ctx, kr->creds);
|
|
+ } else {
|
|
+ kr->creds = calloc(1, sizeof(krb5_creds));
|
|
+ if (kr->creds == NULL) {
|
|
+ DEBUG(SSSDBG_CRIT_FAILURE, "calloc failed.\n");
|
|
+ return ENOMEM;
|
|
+ }
|
|
}
|
|
|
|
#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_RESPONDER
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From f7ed80de34e389c8587d8d8a24c19a13ae38f692 Mon Sep 17 00:00:00 2001
|
|
From: Sumit Bose <sbose@redhat.com>
|
|
Date: Wed, 22 Apr 2026 12:33:17 +0200
|
|
Subject: [PATCH 35/36] krb5: restart krb5_child for Smartcard authentication
|
|
|
|
In contrast to other authentication methods for PKINIT some information
|
|
about the used Smartcard and certificate are already needed for the
|
|
pre-authentication step to trigger the MIT Kerberos PKINIT module to get
|
|
back the information if PKINIT is possible or not and if the Smartcard
|
|
can be used for authentication. If krb5_child is kept running between
|
|
the pre-authentication and the authentication step the information given
|
|
during pre-authentication is used if Smartcard authentication was
|
|
selected.
|
|
|
|
As long as only a single certificate is available there is no issue. But
|
|
if there are multiple certificates which all apply to the given mapping
|
|
and matching rules for the user trying to log in and the user can choose
|
|
a certificate for authentication the authentication might fail if the
|
|
certificate use during pre-authentication and the one selected by the
|
|
user differ. Before the change to keep krb5_child running for all
|
|
authentication methods this was not an issue since the fresh instance
|
|
started during the authentication step was using the certificate
|
|
selected by the user.
|
|
|
|
With this patch krb5_child is restart during the authentication step is
|
|
Smartcard authentication was selected.
|
|
|
|
Reviewed-by: Iker Pedrosa <ipedrosa@redhat.com>
|
|
Reviewed-by: Justin Stephenson <jstephen@redhat.com>
|
|
(cherry picked from commit f3a36bec2a6c9fe11076c8f4673775a0d4221ad1)
|
|
---
|
|
src/providers/krb5/krb5_auth.c | 8 ++++++++
|
|
1 file changed, 8 insertions(+)
|
|
|
|
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
|
|
index 0eacb5523..ac02dfbd7 100644
|
|
--- a/src/providers/krb5/krb5_auth.c
|
|
+++ b/src/providers/krb5/krb5_auth.c
|
|
@@ -866,6 +866,14 @@ static void krb5_auth_resolve_done(struct tevent_req *subreq)
|
|
kr->is_offline = false;
|
|
}
|
|
|
|
+ /* Restart krb5_child for Smartcard authentication in case a different
|
|
+ * certificate was selected by the user */
|
|
+ if (kr->pd->cmd == SSS_PAM_AUTHENTICATE && IS_SC_AUTHTOK(kr->pd->authtok)
|
|
+ && kr->pd->child_pid != 0) {
|
|
+ soft_terminate_krb5_child(state, kr->pd, kr->krb5_ctx);
|
|
+ kr->pd->child_pid = 0;
|
|
+ }
|
|
+
|
|
subreq = handle_child_send(state, state->ev, kr);
|
|
if (subreq == NULL) {
|
|
DEBUG(SSSDBG_CRIT_FAILURE, "handle_child_send failed.\n");
|
|
--
|
|
2.54.0
|
|
|
|
|
|
From 8b960460475ad4dbe10ab6306ed1c81d4a9441db Mon Sep 17 00:00:00 2001
|
|
From: Paul Adelsbach <paul.adelsbach@wolfssl.com>
|
|
Date: Tue, 21 Apr 2026 09:30:51 -0700
|
|
Subject: [PATCH 36/36] pam: gate PAC indicator code on BUILD_SAMBA
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Commit 1f680edad023c8c57343447b156f6b34696e8221 added ad_pac_common.c and
|
|
$(NDR_KRB5PAC_LIBS) to sssd_pam unconditionally. So when building --without-samba, sssd_pam fails to link with undefined references to ndr_pull_init_blob and ndr_pull_PAC_DATA.
|
|
|
|
This change qualifies those additions with `BUILD_SAMBA` so the PAC
|
|
indicator feature is compiled in only when samba support is enabled.
|
|
|
|
Reviewed-by: Sumit Bose <sbose@redhat.com>
|
|
Reviewed-by: Tomáš Halman <thalman@redhat.com>
|
|
(cherry picked from commit d0beceaa17b94fa07a2ee7af8165392b90e83d94)
|
|
---
|
|
Makefile.am | 9 +++++++++
|
|
1 file changed, 9 insertions(+)
|
|
|
|
diff --git a/Makefile.am b/Makefile.am
|
|
index e052c3079..962ccca49 100644
|
|
--- a/Makefile.am
|
|
+++ b/Makefile.am
|
|
@@ -2716,10 +2716,16 @@ test_pamsrv_json_SOURCES = \
|
|
if BUILD_PASSKEY
|
|
test_pamsrv_json_SOURCES += src/responder/pam/pamsrv_passkey.c
|
|
endif # BUILD_PASSKEY
|
|
+if BUILD_SAMBA
|
|
+ test_pamsrv_json_SOURCES += src/providers/ad/ad_pac_common.c
|
|
+endif
|
|
test_pamsrv_json_CFLAGS = \
|
|
$(AM_CFLAGS) \
|
|
$(CMOCKA_CFLAGS) \
|
|
$(NULL)
|
|
+if BUILD_SAMBA
|
|
+test_pamsrv_json_CFLAGS += $(NDR_KRB5PAC_CFLAGS)
|
|
+endif
|
|
test_pamsrv_json_LDFLAGS = \
|
|
-Wl,-wrap,json_array_append_new \
|
|
$(NULL)
|
|
@@ -2738,6 +2744,9 @@ test_pamsrv_json_LDADD = \
|
|
libsss_iface.la \
|
|
libsss_sbus.la \
|
|
$(NULL)
|
|
+if BUILD_SAMBA
|
|
+test_pamsrv_json_LDADD += $(NDR_KRB5PAC_LIBS)
|
|
+endif
|
|
|
|
test_sss_pam_data_SOURCES = \
|
|
src/util/sss_pam_data.c \
|
|
--
|
|
2.54.0
|
|
|