summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2016-05-26 13:20:59 +0200
committerJakub Hrozek <jhrozek@redhat.com>2016-07-07 12:39:36 +0200
commit78027feeb56d6fe216f699be86a4716aaef3f628 (patch)
tree22018812b3382aff3db98e4f1063c6c16e3476a7
parentc88b63b2dd82f7111abc00d93fa8db2707487572 (diff)
downloadsssd-78027feeb56d6fe216f699be86a4716aaef3f628.zip
sssd-78027feeb56d6fe216f699be86a4716aaef3f628.tar.gz
sssd-78027feeb56d6fe216f699be86a4716aaef3f628.tar.xz
PAM/KRB5: optional otp and password prompting
Depending on the available Kerberos pre-authentication methods pam_sss will prompt the user for a password, 2 authentication factors or both. Resolves https://fedorahosted.org/sssd/ticket/2988 Reviewed-by: Nathaniel McCallum <npmccallum@redhat.com> Reviewed-by: Lukáš Slebodník <lslebodn@redhat.com>
-rw-r--r--src/providers/krb5/krb5_child.c85
-rw-r--r--src/sss_client/pam_message.h2
-rw-r--r--src/sss_client/pam_sss.c14
-rw-r--r--src/sss_client/sss_cli.h5
4 files changed, 101 insertions, 5 deletions
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 3b3ebd9..a0a0f74 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -54,6 +54,7 @@ struct krb5_req {
char* name;
krb5_creds *creds;
bool otp;
+ bool password_prompting;
char *otp_vendor;
char *otp_token_id;
char *otp_challenge;
@@ -586,24 +587,87 @@ 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);
+ const char * const *question_list;
+ size_t c;
+ const char *pwd;
+ int ret;
+ krb5_error_code kerr;
if (kr == NULL) {
return EINVAL;
}
+ 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]);
+
+ if (strcmp(question_list[c],
+ KRB5_RESPONDER_QUESTION_PASSWORD) == 0) {
+ 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) {
+ ret = sss_authtok_get_password(kr->pd->authtok, &pwd, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_authtok_get_password failed.\n");
+ return ret;
+ }
+
+ kerr = krb5_responder_set_answer(ctx, rctx,
+ KRB5_RESPONDER_QUESTION_PASSWORD,
+ pwd);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "krb5_responder_set_answer failed.\n");
+ }
+
+ return kerr;
+ }
+ }
+ }
+ }
+
return answer_otp(ctx, kr, rctx);
}
+#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_RESPONDER */
+
+static char *password_or_responder(const char *password)
+{
+#ifdef HAVE_KRB5_GET_INIT_CREDS_OPT_SET_RESPONDER
+ /* If the new responder interface is available we will handle even simple
+ * passwords in the responder. */
+ return NULL;
+#else
+ return discard_const(password);
#endif
+}
static krb5_error_code sss_krb5_prompter(krb5_context context, void *data,
const char *name, const char *banner,
int num_prompts, krb5_prompt prompts[])
{
int ret;
+ size_t c;
struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
+ DEBUG(SSSDBG_TRACE_ALL,
+ "sss_krb5_prompter name [%s] banner [%s] num_prompts [%d] EINVAL.\n",
+ name, banner, num_prompts);
+
if (num_prompts != 0) {
DEBUG(SSSDBG_CRIT_FAILURE, "Cannot handle password prompts.\n");
+ if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) {
+ for (c = 0; c < num_prompts; c++) {
+ DEBUG(SSSDBG_TRACE_ALL, "Prompt [%zu][%s].\n", c,
+ prompts[c].prompt);
+ }
+ }
return KRB5_LIBOS_CANTREADPWD;
}
@@ -1217,7 +1281,7 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
DEBUG(SSSDBG_TRACE_FUNC,
"Attempting kinit for realm [%s]\n",realm_name);
kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
- discard_const(password),
+ password_or_responder(password),
sss_krb5_prompter, kr, 0,
NULL, kr->options);
if (kr->pd->cmd == SSS_PAM_PREAUTH) {
@@ -1396,7 +1460,7 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim)
DEBUG(SSSDBG_TRACE_FUNC,
"Attempting kinit for realm [%s]\n",realm_name);
kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
- discard_const(password),
+ password_or_responder(password),
prompter, kr, 0,
SSSD_KRB5_CHANGEPW_PRINCIPAL,
kr->options);
@@ -1518,8 +1582,15 @@ static errno_t changepw_child(struct krb5_req *kr, bool prelim)
* to change them back to get a fresh TGT. */
revert_changepw_options(kr->options);
+ ret = sss_authtok_set_password(kr->pd->authtok, newpassword, 0);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set password for fresh TGT.\n");
+ return ret;
+ }
+
kerr = get_and_save_tgt(kr, newpassword);
+ sss_authtok_set_empty(kr->pd->authtok);
sss_authtok_set_empty(kr->pd->newauthtok);
if (kerr == 0) {
@@ -1564,6 +1635,14 @@ static errno_t tgt_req_child(struct krb5_req *kr)
if (kr->otp) {
kerr = k5c_attach_otp_info_msg(kr);
}
+
+ if (kr->password_prompting) {
+ ret = pam_add_response(kr->pd, SSS_PASSWORD_PROMPTING, 0, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_response failed.\n");
+ goto done;
+ }
+ }
} else {
if (kerr == 0) {
kerr = k5c_attach_ccname_msg(kr);
@@ -1589,7 +1668,7 @@ static errno_t tgt_req_child(struct krb5_req *kr)
set_changepw_options(kr->options);
kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
- discard_const(password),
+ password_or_responder(password),
sss_krb5_prompter, kr, 0,
SSSD_KRB5_CHANGEPW_PRINCIPAL,
kr->options);
diff --git a/src/sss_client/pam_message.h b/src/sss_client/pam_message.h
index f0a7a07..34889e0 100644
--- a/src/sss_client/pam_message.h
+++ b/src/sss_client/pam_message.h
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <stdint.h>
+#include <stdbool.h>
#include "sss_client/sss_cli.h"
@@ -56,6 +57,7 @@ struct pam_items {
char *otp_token_id;
char *otp_challenge;
char *first_factor;
+ bool password_prompting;
char *cert_user;
char *token_name;
diff --git a/src/sss_client/pam_sss.c b/src/sss_client/pam_sss.c
index 1ba02ad..fdb9c90 100644
--- a/src/sss_client/pam_sss.c
+++ b/src/sss_client/pam_sss.c
@@ -1014,6 +1014,10 @@ static int eval_response(pam_handle_t *pamh, size_t buflen, uint8_t *buf,
D(("cert user: [%s] token name: [%s]", pi->cert_user,
pi->token_name));
break;
+ case SSS_PASSWORD_PROMPTING:
+ D(("Password prompting available."));
+ pi->password_prompting = true;
+ break;
default:
D(("Unknown response type [%d]", type));
}
@@ -1102,6 +1106,7 @@ static int get_pam_items(pam_handle_t *pamh, uint32_t flags,
pi->otp_vendor = NULL;
pi->otp_token_id = NULL;
pi->otp_challenge = NULL;
+ pi->password_prompting = false;
pi->cert_user = NULL;
pi->token_name = NULL;
@@ -1571,8 +1576,13 @@ static int get_authtok_for_authentication(pam_handle_t *pamh,
if (flags & FLAGS_USE_2FA
|| (pi->otp_vendor != NULL && pi->otp_token_id != NULL
&& pi->otp_challenge != NULL)) {
- ret = prompt_2fa(pamh, pi, _("First Factor: "),
- _("Second Factor: "));
+ 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_user != NULL) {
ret = prompt_sc_pin(pamh, pi);
} else {
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index 17d8e45..b6610bc 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -426,6 +426,11 @@ enum response_type {
SSS_OTP, /**< Indicates that the autotok was a OTP, so don't
* cache it. There is no message.
* @param None. */
+ SSS_PASSWORD_PROMPTING, /**< Indicates that password prompting is possible.
+ * This might be used together with
+ * SSS_PAM_OTP_INFO to determine the type of
+ * prompting. There is no message.
+ * @param None. */
};
/**