From a4d178593bec65a4c7534b841cedfbb74c56f49f Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Apr 01 2019 20:40:41 +0000 Subject: 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 --- diff --git a/Makefile.am b/Makefile.am index 72866cd..17d6446 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1589,8 +1589,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) \ @@ -2721,6 +2726,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 338656b..7c4ee2d 100644 --- a/src/confdb/confdb.h +++ b/src/confdb/confdb.h @@ -275,6 +275,15 @@ #define CONFDB_CERTMAP_DOMAINS "domains" #define CONFDB_CERTMAP_PRIORITY "priority" +/* 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 699c5ff..c12fef7 100644 --- a/src/man/sssd.conf.5.xml +++ b/src/man/sssd.conf.5.xml @@ -3579,6 +3579,72 @@ ldap_user_extra_attrs = phone:telephoneNumber + + PROMPTING CONFIGURATION SECTION + + If a special file + (/var/lib/sss/pubconf/pam_preauth_available) + 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. + + + 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. + + + Each supported authentication method has it's own configuration + sub-section under [prompting/...]. Currently there + are: + + + [prompting/password] + + to configure password prompting, allowed options are: + password_prompt + to change the string of the password + prompt + + + + + + + [prompting/2fa] + + to configure two-factor authentication prompting, + allowed options are: + first_prompt + to change the string of the prompt for + the first factor + + second_prompt + to change the string of the prompt for + the second factor + + single_prompt + 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 + + + + + + + + + It is possible to add a sub-section for specific PAM services like + e.g. [prompting/password/sshd] to individual change + the prompting for this service. + + + EXAMPLES diff --git a/src/responder/pam/pam_prompting_config.c b/src/responder/pam/pam_prompting_config.c new file mode 100644 index 0000000..dc10e50 --- /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 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 . +*/ + +#include "util/util.h" +#include "util/sss_pam_data.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 6cfc511..38db6fc 100644 --- a/src/responder/pam/pamsrv.c +++ b/src/responder/pam/pamsrv.c @@ -254,6 +254,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, @@ -285,11 +295,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 3a927bb..99c6e10 100644 --- a/src/responder/pam/pamsrv.h +++ b/src/responder/pam/pamsrv.h @@ -51,6 +51,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_req { @@ -126,4 +129,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 6c04cb1..aabb58f 100644 --- a/src/responder/pam/pamsrv_cmd.c +++ b/src/responder/pam/pamsrv_cmd.c @@ -1008,6 +1008,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 */