From 6b7ce87976ebba7b3c1aea24dbf91486ec5de2ed Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Wed, 27 Mar 2019 09:48:42 +0100
Subject: [PATCH 18/21] pam_sss: use configured prompting
If the responds of SSSD's PAM responder contains a prompt_config
structure use the content to prompt the user for credentials.
Related to https://pagure.io/SSSD/sssd/issue/3264
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
(cherry picked with fixes from commit fc26b4a82d4a92b29cf321fba8dbec52c3bff8d6)
---
src/sss_client/pam_message.h | 2 +
src/sss_client/pam_sss.c | 136 +++++++++++++++++++++++++++++------
src/sss_client/sss_cli.h | 3 +
3 files changed, 119 insertions(+), 22 deletions(-)
diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h
index 11526a80a..c87162479 100644
--- a/src/sss_client/pam_message.h
+++ b/src/sss_client/pam_message.h
@@ -64,6 +64,8 @@ struct pam_items {
bool user_name_hint;
struct cert_auth_info *cert_list;
struct cert_auth_info *selected_cert;
+
+ struct prompt_config **pc;
};
int pack_message_v3(struct pam_items *pi, size_t *size, uint8_t **buffer);
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index 59081cc67..ab9b7478e 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -205,6 +205,9 @@ static void overwrite_and_free_pam_items(struct pam_items *pi)
free_cert_list(pi->cert_list);
pi->cert_list = NULL;
pi->selected_cert = NULL;
+
+ pc_list_free(pi->pc);
+ pi->pc = NULL;
}
static int null_strcmp(const char *s1, const char *s2) {
@@ -1163,6 +1166,16 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
D(("Password prompting available."));
pi->password_prompting = true;
break;
+ case SSS_PAM_PROMPT_CONFIG:
+ if (pi->pc == NULL) {
+ ret = pc_list_from_response(len, &buf[p], &pi->pc);
+ if (ret != EOK) {
+ D(("Failed to parse prompting data, using defaults"));
+ pc_list_free(pi->pc);
+ pi->pc = NULL;
+ }
+ }
+ break;
default:
D(("Unknown response type [%d]", type));
}
@@ -1256,6 +1269,8 @@ static int get_pam_items(pam_handle_t *pamh, uint32_t flags,
pi->cert_list = NULL;
pi->selected_cert = NULL;
+ pi->pc = NULL;
+
return PAM_SUCCESS;
}
@@ -1558,6 +1573,37 @@ done:
return ret;
}
+static int prompt_2fa_single(pam_handle_t *pamh, struct pam_items *pi,
+ const char *prompt)
+{
+ int ret;
+ char *answer = NULL;
+
+ ret = do_pam_conversation(pamh, PAM_PROMPT_ECHO_OFF, prompt, NULL, &answer);
+ if (ret != PAM_SUCCESS) {
+ D(("do_pam_conversation failed."));
+ return ret;
+ }
+
+ if (answer == NULL) {
+ pi->pam_authtok = NULL;
+ pi->pam_authtok_type = SSS_AUTHTOK_TYPE_EMPTY;
+ pi->pam_authtok_size=0;
+ } else {
+ pi->pam_authtok = strdup(answer);
+ _pam_overwrite((void *)answer);
+ free(answer);
+ answer=NULL;
+ if (pi->pam_authtok == NULL) {
+ return PAM_BUF_ERR;
+ }
+ pi->pam_authtok_type = SSS_AUTHTOK_TYPE_2FA_SINGLE;
+ pi->pam_authtok_size=strlen(pi->pam_authtok);
+ }
+
+ return PAM_SUCCESS;
+}
+
#define SC_PROMPT_FMT "PIN for %s"
#ifndef discard_const
@@ -2014,6 +2060,48 @@ static void eval_argv(pam_handle_t *pamh, int argc, const char **argv,
return;
}
+static int prompt_by_config(pam_handle_t *pamh, struct pam_items *pi)
+{
+ size_t c;
+ int ret;
+
+ if (pi->pc == NULL || *pi->pc == NULL) {
+ return EINVAL;
+ }
+
+ for (c = 0; pi->pc[c] != NULL; c++) {
+ switch (pc_get_type(pi->pc[c])) {
+ case PC_TYPE_PASSWORD:
+ ret = prompt_password(pamh, pi, pc_get_password_prompt(pi->pc[c]));
+ break;
+ case PC_TYPE_2FA:
+ ret = prompt_2fa(pamh, pi, pc_get_2fa_1st_prompt(pi->pc[c]),
+ pc_get_2fa_2nd_prompt(pi->pc[c]));
+ break;
+ case PC_TYPE_2FA_SINGLE:
+ ret = prompt_2fa_single(pamh, pi,
+ pc_get_2fa_single_prompt(pi->pc[c]));
+ break;
+ case PC_TYPE_SC_PIN:
+ ret = prompt_sc_pin(pamh, pi);
+ /* Todo: add extra string option */
+ break;
+ default:
+ ret = EINVAL;
+ }
+
+ /* If not credential where given try the next type otherwise we are
+ * done. */
+ if (ret == PAM_SUCCESS && pi->pam_authtok_size == 0) {
+ continue;
+ }
+
+ break;
+ }
+
+ return ret;
+}
+
static int get_authtok_for_authentication(pam_handle_t *pamh,
struct pam_items *pi,
uint32_t flags)
@@ -2032,30 +2120,34 @@ static int get_authtok_for_authentication(pam_handle_t *pamh,
}
pi->pam_authtok_size = strlen(pi->pam_authtok);
} else {
- if (flags & FLAGS_USE_2FA
- || (pi->otp_vendor != NULL && pi->otp_token_id != NULL
- && pi->otp_challenge != NULL)) {
- if (pi->password_prompting) {
- ret = prompt_2fa(pamh, pi, _("First Factor: "),
- _("Second Factor (optional): "));
- } else {
- ret = prompt_2fa(pamh, pi, _("First Factor: "),
- _("Second Factor: "));
- }
- } else if (pi->cert_list != NULL) {
- if (pi->cert_list->next == NULL) {
- /* Only one certificate */
- pi->selected_cert = pi->cert_list;
- } else {
- ret = prompt_multi_cert(pamh, pi);
- if (ret != 0) {
- D(("Failed to select certificate"));
- return PAM_AUTHTOK_ERR;
+ if (pi->pc != NULL) {
+ ret = prompt_by_config(pamh, pi);
+ } else {
+ if (flags & FLAGS_USE_2FA
+ || (pi->otp_vendor != NULL && pi->otp_token_id != NULL
+ && pi->otp_challenge != NULL)) {
+ if (pi->password_prompting) {
+ ret = prompt_2fa(pamh, pi, _("First Factor: "),
+ _("Second Factor (optional): "));
+ } else {
+ ret = prompt_2fa(pamh, pi, _("First Factor: "),
+ _("Second Factor: "));
}
+ } else if (pi->cert_list != NULL) {
+ if (pi->cert_list->next == NULL) {
+ /* Only one certificate */
+ pi->selected_cert = pi->cert_list;
+ } else {
+ ret = prompt_multi_cert(pamh, pi);
+ if (ret != 0) {
+ D(("Failed to select certificate"));
+ return PAM_AUTHTOK_ERR;
+ }
+ }
+ ret = prompt_sc_pin(pamh, pi);
+ } else {
+ ret = prompt_password(pamh, pi, _("Password: "));
}
- ret = prompt_sc_pin(pamh, pi);
- } else {
- ret = prompt_password(pamh, pi, _("Password: "));
}
if (ret != PAM_SUCCESS) {
D(("failed to get password from user"));
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index 23ef21608..24b24a91b 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -469,6 +469,9 @@ enum response_type {
SSS_PAM_CERT_INFO_WITH_HINT, /**< Same as SSS_PAM_CERT_INFO but user name
* might be missing and should be prompted
* for. */
+ SSS_PAM_PROMPT_CONFIG, /**< Contains data which controls which credentials
+ * are expected and how the user is prompted for
+ * them. */
};
/**
--
2.19.1