dpward / rpms / sssd

Forked from rpms/sssd 3 years ago
Clone
Blob Blame History Raw
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