From 45580b2c90d7c19f1d8df57ce7b3e9f3e0acc244 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose@redhat.com>
Date: Wed, 27 Mar 2019 21:05:06 +0100
Subject: [PATCH 19/21] PAM: add initial prompting configuration
Add new section for sssd.conf to allow more flexible prompting during
authentication.
Related to https://pagure.io/SSSD/sssd/issue/3264
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
(cherry picked with fixes from commit a4d178593bec65a4c7534b841cedfbb74c56f49f)
---
Makefile.am | 7 +
src/confdb/confdb.h | 10 +
src/man/sssd.conf.5.xml | 66 ++++++
src/responder/pam/pam_prompting_config.c | 275 +++++++++++++++++++++++
src/responder/pam/pamsrv.c | 16 +-
src/responder/pam/pamsrv.h | 6 +
src/responder/pam/pamsrv_cmd.c | 8 +
7 files changed, 387 insertions(+), 1 deletion(-)
create mode 100644 src/responder/pam/pam_prompting_config.c
diff --git a/Makefile.am b/Makefile.am
index f7f55e96a..e22423071 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1397,8 +1397,13 @@ sssd_pam_SOURCES = \
src/responder/pam/pamsrv_cmd.c \
src/responder/pam/pamsrv_p11.c \
src/responder/pam/pamsrv_dp.c \
+ src/responder/pam/pam_prompting_config.c \
+ src/sss_client/pam_sss_prompt_config.c \
src/responder/pam/pam_helpers.c \
$(SSSD_RESPONDER_OBJ)
+sssd_pam_CFLAGS = \
+ $(AM_CFLAGS) \
+ $(NULL)
sssd_pam_LDADD = \
$(LIBADD_DL) \
$(TDB_LIBS) \
@@ -2446,6 +2451,8 @@ pam_srv_tests_SOURCES = \
src/responder/pam/pam_helpers.c \
src/responder/pam/pamsrv_dp.c \
src/responder/pam/pam_LOCAL_domain.c \
+ src/responder/pam/pam_prompting_config.c \
+ src/sss_client/pam_sss_prompt_config.c \
$(NULL)
pam_srv_tests_CFLAGS = \
-U SSSD_LIBEXEC_PATH -DSSSD_LIBEXEC_PATH=\"$(abs_builddir)\" \
diff --git a/src/confdb/confdb.h b/src/confdb/confdb.h
index e8091fcd9..0251ab606 100644
--- a/src/confdb/confdb.h
+++ b/src/confdb/confdb.h
@@ -266,6 +266,16 @@
#define CONFDB_KCM_SOCKET "socket_path"
#define CONFDB_KCM_DB "ccache_storage" /* Undocumented on purpose */
+/* Prompting */
+#define CONFDB_PC_CONF_ENTRY "config/prompting"
+#define CONFDB_PC_TYPE_PASSWORD "password"
+#define CONFDB_PC_PASSWORD_PROMPT "password_prompt"
+#define CONFDB_PC_TYPE_2FA "2fa"
+#define CONFDB_PC_2FA_SINGLE_PROMPT "single_prompt"
+#define CONFDB_PC_2FA_1ST_PROMPT "first_prompt"
+#define CONFDB_PC_2FA_2ND_PROMPT "second_prompt"
+#define CONFDB_PC_TYPE_CERT_AUTH "cert_auth"
+
struct confdb_ctx;
struct config_file_ctx;
diff --git a/src/man/sssd.conf.5.xml b/src/man/sssd.conf.5.xml
index 3d017f638..274809e24 100644
--- a/src/man/sssd.conf.5.xml
+++ b/src/man/sssd.conf.5.xml
@@ -3364,6 +3364,72 @@ ldap_user_extra_attrs = phone:telephoneNumber
</para>
</refsect1>
+ <refsect1 id='prompting_configuration'>
+ <title>PROMPTING CONFIGURATION SECTION</title>
+ <para>
+ If a special file
+ (<filename>/var/lib/sss/pubconf/pam_preauth_available</filename>)
+ exists SSSD's PAM module pam_sss will ask SSSD to figure out which
+ authentication methods are available for the user trying to log in.
+ Based on the results pam_sss will prompt the user for appropriate
+ credentials.
+ </para>
+ <para>
+ With the growing number of authentication methods and the
+ possibility that there are multiple ones for a single user the
+ heuristic used by pam_sss to select the prompting might not be
+ suitable for all use cases. To following options should provide a
+ better flexibility here.
+ </para>
+ <para>
+ Each supported authentication method has it's own configuration
+ sub-section under <quote>[prompting/...]</quote>. Currently there
+ are:
+ <variablelist>
+ <varlistentry>
+ <term>[prompting/password]</term>
+ <listitem>
+ <para>to configure password prompting, allowed options are:
+ <variablelist><varlistentry><term>password_prompt</term>
+ <listitem><para>to change the string of the password
+ prompt</para></listitem></varlistentry></variablelist>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ <variablelist>
+ <varlistentry>
+ <term>[prompting/2fa]</term>
+ <listitem>
+ <para>to configure two-factor authentication prompting,
+ allowed options are:
+ <variablelist><varlistentry><term>first_prompt</term>
+ <listitem><para>to change the string of the prompt for
+ the first factor </para></listitem>
+ </varlistentry>
+ <varlistentry><term>second_prompt</term>
+ <listitem><para>to change the string of the prompt for
+ the second factor </para></listitem>
+ </varlistentry>
+ <varlistentry><term>single_prompt</term>
+ <listitem><para>boolean value, if True there will be
+ only a single prompt using the value of first_prompt
+ where it is expected that both factor are entered as a
+ single string</para></listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ <para>
+ It is possible to add a sub-section for specific PAM services like
+ e.g. <quote>[prompting/password/sshd]</quote> to individual change
+ the prompting for this service.
+ </para>
+ </refsect1>
+
<refsect1 id='example'>
<title>EXAMPLES</title>
<para>
diff --git a/src/responder/pam/pam_prompting_config.c b/src/responder/pam/pam_prompting_config.c
new file mode 100644
index 000000000..c3ee41d4b
--- /dev/null
+++ b/src/responder/pam/pam_prompting_config.c
@@ -0,0 +1,275 @@
+/*
+ SSSD
+
+ PAM Responder - helpers for PAM prompting configuration
+
+ Copyright (C) Sumit Bose <sbose@redhat.com> 2019
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "providers/data_provider.h"
+#include "confdb/confdb.h"
+#include "sss_client/sss_cli.h"
+#include "responder/pam/pamsrv.h"
+
+typedef errno_t (pam_set_prompting_fn_t)(TALLOC_CTX *, struct confdb_ctx *,
+ const char *,
+ struct prompt_config ***);
+
+
+static errno_t pam_set_password_prompting_options(TALLOC_CTX *tmp_ctx,
+ struct confdb_ctx *cdb,
+ const char *section,
+ struct prompt_config ***pc_list)
+{
+ int ret;
+ char *value = NULL;
+
+ ret = confdb_get_string(cdb, tmp_ctx, section, CONFDB_PC_PASSWORD_PROMPT,
+ NULL, &value);
+ if (ret == EOK && value != NULL) {
+ ret = pc_list_add_password(pc_list, value);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "pc_list_add_password failed.\n");
+ }
+ return ret;
+ }
+
+ return ENOENT;
+}
+
+static errno_t pam_set_2fa_prompting_options(TALLOC_CTX *tmp_ctx,
+ struct confdb_ctx *cdb,
+ const char *section,
+ struct prompt_config ***pc_list)
+{
+ bool single_2fa_prompt = false;
+ char *first_prompt = NULL;
+ char *second_prompt = NULL;
+ int ret;
+
+
+ ret = confdb_get_bool(cdb, section, CONFDB_PC_2FA_SINGLE_PROMPT, false,
+ &single_2fa_prompt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "confdb_get_bool failed, using defaults");
+ }
+ ret = confdb_get_string(cdb, tmp_ctx, section, CONFDB_PC_2FA_1ST_PROMPT,
+ NULL, &first_prompt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "confdb_get_string failed, using defaults");
+ }
+
+ if (single_2fa_prompt) {
+ ret = pc_list_add_2fa_single(pc_list, first_prompt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "pc_list_add_2fa_single failed.\n");
+ }
+ return ret;
+ } else {
+ ret = confdb_get_string(cdb, tmp_ctx, section, CONFDB_PC_2FA_2ND_PROMPT,
+ NULL, &second_prompt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "confdb_get_string failed, using defaults");
+ }
+
+ ret = pc_list_add_2fa(pc_list, first_prompt, second_prompt);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "pc_list_add_2fa failed.\n");
+ }
+ return ret;
+ }
+
+ return ENOENT;
+}
+
+static errno_t pam_set_prompting_options(struct confdb_ctx *cdb,
+ const char *service_name,
+ char **sections,
+ int num_sections,
+ const char *section_path,
+ pam_set_prompting_fn_t *setter,
+ struct prompt_config ***pc_list)
+{
+ char *dummy;
+ size_t c;
+ bool global = false;
+ bool specific = false;
+ char *section = NULL;
+ int ret;
+ char *last;
+ TALLOC_CTX *tmp_ctx = NULL;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+
+ dummy = talloc_asprintf(tmp_ctx, "%s/%s", section_path,
+ service_name);
+ for (c = 0; c < num_sections; c++) {
+ if (strcmp(sections[c], CONFDB_PC_TYPE_PASSWORD) == 0) {
+ global = true;
+ }
+ if (dummy != NULL && strcmp(sections[c], dummy) == 0) {
+ specific = true;
+ }
+ }
+
+ section = talloc_asprintf(tmp_ctx, "%s/%s", CONFDB_PC_CONF_ENTRY, dummy);
+ if (section == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = ENOENT;
+ if (specific) {
+ ret = setter(tmp_ctx, cdb, section, pc_list);
+ }
+ if (global && ret == ENOENT) {
+ last = strrchr(section, '/');
+ if (last != NULL) {
+ *last = '\0';
+ ret = setter(tmp_ctx, cdb, section, pc_list);
+ }
+ }
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "setter failed.\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ talloc_free(tmp_ctx);
+ return ret;
+}
+
+errno_t pam_eval_prompting_config(struct pam_ctx *pctx, struct pam_data *pd)
+{
+ int ret;
+ struct response_data *resp;
+ bool password_auth = false;
+ bool otp_auth = false;
+ bool cert_auth = false;
+ struct prompt_config **pc_list = NULL;
+ int resp_len;
+ uint8_t *resp_data = NULL;
+
+ if (pctx->num_prompting_config_sections == 0) {
+ DEBUG(SSSDBG_TRACE_ALL, "No prompting configuration found.\n");
+ return EOK;
+ }
+
+ resp = pd->resp_list;
+ while (resp != NULL) {
+ switch (resp->type) {
+ case SSS_PAM_OTP_INFO:
+ otp_auth = true;
+ break;
+ case SSS_PAM_CERT_INFO:
+ cert_auth = true;
+ break;
+ case SSS_PASSWORD_PROMPTING:
+ password_auth = true;
+ break;
+ case SSS_CERT_AUTH_PROMPTING:
+ /* currently not used */
+ break;
+ default:
+ break;
+ }
+ resp = resp->next;
+ }
+
+ if (!password_auth && !otp_auth && !cert_auth) {
+ /* If the backend cannot determine which authentication types are
+ * available the default would be to prompt for a password. */
+ password_auth = true;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Authentication types for user [%s] and service "
+ "[%s]:%s%s%s\n", pd->user, pd->service,
+ password_auth ? " password": "",
+ otp_auth ? " two-factor" : "",
+ cert_auth ? " smartcard" : "");
+
+ if (cert_auth) {
+ /* If certificate based authentication is possilbe, i.e. a Smartcard
+ * or similar with the mapped certificate is available we currently
+ * prefer this authentication type unconditionally. If other types
+ * should be used the Smartcard can be removed during authentication.
+ * Since there currently are no specific options for cert_auth we are
+ * done. */
+ ret = EOK;
+ goto done;
+ }
+
+ /* If OTP and password auth are possible we currently prefer OTP. */
+ if (otp_auth) {
+ ret = pam_set_prompting_options(pctx->rctx->cdb, pd->service,
+ pctx->prompting_config_sections,
+ pctx->num_prompting_config_sections,
+ CONFDB_PC_TYPE_2FA,
+ pam_set_2fa_prompting_options,
+ &pc_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "pam_set_prompting_options failed.\n");
+ goto done;
+ }
+ }
+
+ if (password_auth) {
+ ret = pam_set_prompting_options(pctx->rctx->cdb, pd->service,
+ pctx->prompting_config_sections,
+ pctx->num_prompting_config_sections,
+ CONFDB_PC_TYPE_PASSWORD,
+ pam_set_password_prompting_options,
+ &pc_list);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "pam_set_prompting_options failed.\n");
+ goto done;
+ }
+ }
+
+ if (pc_list != NULL) {
+ ret = pam_get_response_prompt_config(pc_list, &resp_len, &resp_data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "pam_get_response_prompt_config failed.\n");
+ goto done;
+ }
+
+ ret = pam_add_response(pd, SSS_PAM_PROMPT_CONFIG, resp_len, resp_data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "pam_add_response failed.\n");
+ goto done;
+ }
+ }
+
+ ret = EOK;
+done:
+ free(resp_data);
+ pc_list_free(pc_list);
+
+ return ret;
+}
diff --git a/src/responder/pam/pamsrv.c b/src/responder/pam/pamsrv.c
index 4ddd1d0b3..fb799d28b 100644
--- a/src/responder/pam/pamsrv.c
+++ b/src/responder/pam/pamsrv.c
@@ -315,6 +315,16 @@ static int pam_process_init(TALLOC_CTX *mem_ctx,
goto done;
}
+ /* Check if there is a prompting configuration */
+ pctx->prompting_config_sections = NULL;
+ pctx->num_prompting_config_sections = 0;
+ ret = confdb_get_sub_sections(pctx, pctx->rctx->cdb, CONFDB_PC_CONF_ENTRY,
+ &pctx->prompting_config_sections,
+ &pctx->num_prompting_config_sections);
+ if (ret != EOK && ret != ENOENT) {
+ DEBUG(SSSDBG_OP_FAILURE, "confdb_get_sub_sections failed, not fatal.\n");
+ }
+
/* Check if certificate based authentication is enabled */
ret = confdb_get_bool(pctx->rctx->cdb,
CONFDB_PAM_CONF_ENTRY,
@@ -346,11 +356,15 @@ static int pam_process_init(TALLOC_CTX *mem_ctx,
goto done;
}
+ }
+
+ if (pctx->cert_auth || pctx->num_prompting_config_sections != 0) {
ret = create_preauth_indicator();
if (ret != EOK) {
DEBUG(SSSDBG_OP_FAILURE,
"Failed to create pre-authentication indicator file, "
- "Smartcard authentication might not work as expected.\n");
+ "Smartcard authentication or configured prompting might "
+ "not work as expected.\n");
}
}
diff --git a/src/responder/pam/pamsrv.h b/src/responder/pam/pamsrv.h
index 3325d9b9f..319362a95 100644
--- a/src/responder/pam/pamsrv.h
+++ b/src/responder/pam/pamsrv.h
@@ -52,6 +52,9 @@ struct pam_ctx {
char *nss_db;
struct sss_certmap_ctx *sss_certmap_ctx;
char **smartcard_services;
+
+ char **prompting_config_sections;
+ int num_prompting_config_sections;
};
struct pam_auth_dp_req {
@@ -130,4 +133,7 @@ pam_set_last_online_auth_with_curr_token(struct sss_domain_info *domain,
errno_t filter_responses(struct confdb_ctx *cdb,
struct response_data *resp_list,
struct pam_data *pd);
+
+errno_t pam_eval_prompting_config(struct pam_ctx *pctx, struct pam_data *pd);
+
#endif /* __PAMSRV_H__ */
diff --git a/src/responder/pam/pamsrv_cmd.c b/src/responder/pam/pamsrv_cmd.c
index 6f3a7e56b..6b2dc5bdc 100644
--- a/src/responder/pam/pamsrv_cmd.c
+++ b/src/responder/pam/pamsrv_cmd.c
@@ -1003,6 +1003,14 @@ static void pam_reply(struct pam_auth_req *preq)
}
}
+ if (pd->cmd == SSS_PAM_PREAUTH) {
+ ret = pam_eval_prompting_config(pctx, pd);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Failed to add prompting information, "
+ "using defaults.\n");
+ }
+ }
+
/*
* Export non-overridden shell to tlog-rec-session when opening the session
*/
--
2.19.1