summaryrefslogtreecommitdiffstats
path: root/src/providers/krb5/krb5_child.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-20 15:22:36 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-20 15:22:36 +0000
commit80959e30ce7c57b5865f5358c42a7bcffe450d4d (patch)
treeb740c16852fea65d00f8acfc751765ae7aafbbb4 /src/providers/krb5/krb5_child.c
parentReleasing progress-linux version 2.9.4-2~progress7.99u2. (diff)
downloadsssd-80959e30ce7c57b5865f5358c42a7bcffe450d4d.tar.xz
sssd-80959e30ce7c57b5865f5358c42a7bcffe450d4d.zip
Merging upstream version 2.9.5.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/providers/krb5/krb5_child.c')
-rw-r--r--src/providers/krb5/krb5_child.c260
1 files changed, 179 insertions, 81 deletions
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 704f650..494711d 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -123,7 +123,18 @@ struct krb5_req {
};
static krb5_context krb5_error_ctx;
-#define KRB5_CHILD_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error)
+
+#define KRB5_CHILD_DEBUG_INT(level, errctx, krb5_error) do { \
+ const char *__krb5_error_msg; \
+ __krb5_error_msg = sss_krb5_get_error_message(errctx, krb5_error); \
+ DEBUG(level, "%d: [%d][%s]\n", __LINE__, krb5_error, __krb5_error_msg); \
+ if (level & (SSSDBG_CRIT_FAILURE | SSSDBG_FATAL_FAILURE)) { \
+ sss_log(SSS_LOG_ERR, "%s", __krb5_error_msg); \
+ } \
+ sss_krb5_free_error_message(errctx, __krb5_error_msg); \
+} while(0)
+
+#define KRB5_CHILD_DEBUG(level, error) KRB5_CHILD_DEBUG_INT(level, krb5_error_ctx, error)
static errno_t k5c_attach_otp_info_msg(struct krb5_req *kr);
static errno_t k5c_attach_oauth2_info_msg(struct krb5_req *kr, struct sss_idp_oauth2 *data);
@@ -734,50 +745,60 @@ static krb5_error_code answer_pkinit(krb5_context ctx,
DEBUG(SSSDBG_TRACE_ALL, "Setting pkinit_prompting.\n");
kr->pkinit_prompting = true;
- if (kr->pd->cmd == SSS_PAM_AUTHENTICATE
- && (sss_authtok_get_type(kr->pd->authtok)
+ if (kr->pd->cmd == SSS_PAM_AUTHENTICATE) {
+ if ((sss_authtok_get_type(kr->pd->authtok)
== SSS_AUTHTOK_TYPE_SC_PIN
|| sss_authtok_get_type(kr->pd->authtok)
== SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
- kerr = sss_authtok_get_sc(kr->pd->authtok, &pin, NULL,
- &token_name, NULL,
- &module_name, NULL,
- NULL, NULL, NULL, NULL);
- if (kerr != EOK) {
- DEBUG(SSSDBG_OP_FAILURE,
- "sss_authtok_get_sc failed.\n");
- goto done;
- }
+ kerr = sss_authtok_get_sc(kr->pd->authtok, &pin, NULL,
+ &token_name, NULL,
+ &module_name, NULL,
+ NULL, NULL, NULL, NULL);
+ if (kerr != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "sss_authtok_get_sc failed.\n");
+ goto done;
+ }
- for (c = 0; chl->identities[c] != NULL; c++) {
- if (chl->identities[c]->identity != NULL
- && pkinit_identity_matches(chl->identities[c]->identity,
- token_name, module_name)) {
- break;
+ for (c = 0; chl->identities[c] != NULL; c++) {
+ if (chl->identities[c]->identity != NULL
+ && pkinit_identity_matches(chl->identities[c]->identity,
+ token_name, module_name)) {
+ break;
+ }
}
- }
- if (chl->identities[c] == NULL) {
- DEBUG(SSSDBG_CRIT_FAILURE,
- "No matching identity for [%s][%s] found in pkinit challenge.\n",
- token_name, module_name);
- kerr = EINVAL;
- goto done;
- }
+ if (chl->identities[c] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "No matching identity for [%s][%s] found in pkinit "
+ "challenge.\n", token_name, module_name);
+ kerr = EINVAL;
+ goto done;
+ }
- kerr = krb5_responder_pkinit_set_answer(ctx, rctx,
- chl->identities[c]->identity,
- pin);
- if (kerr != 0) {
- DEBUG(SSSDBG_OP_FAILURE,
- "krb5_responder_set_answer failed.\n");
- }
+ kerr = krb5_responder_pkinit_set_answer(ctx, rctx,
+ chl->identities[c]->identity,
+ pin);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "krb5_responder_set_answer failed.\n");
+ }
- goto done;
+ goto done;
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Unexpected authentication token type [%s]\n",
+ sss_authtok_type_to_str(sss_authtok_get_type(kr->pd->authtok)));
+ kerr = EAGAIN;
+ goto done;
+ }
+ } else {
+ /* We only expect SSS_PAM_PREAUTH here, but also for all other
+ * commands the graceful solution would be to let the caller
+ * check other authentication methods as well. */
+ kerr = EAGAIN;
}
- kerr = EOK;
-
done:
krb5_responder_pkinit_challenge_free(ctx, rctx, chl);
@@ -903,9 +924,9 @@ static krb5_error_code answer_idp_oauth2(krb5_context kctx,
type = sss_authtok_get_type(kr->pd->authtok);
if (type != SSS_AUTHTOK_TYPE_OAUTH2) {
- DEBUG(SSSDBG_OP_FAILURE, "Unexpected authentication token type [%s]\n",
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unexpected authentication token type [%s]\n",
sss_authtok_type_to_str(type));
- kerr = EINVAL;
+ kerr = EAGAIN;
goto done;
}
@@ -1130,9 +1151,9 @@ static krb5_error_code answer_passkey(krb5_context kctx,
type = sss_authtok_get_type(kr->pd->authtok);
if (type != SSS_AUTHTOK_TYPE_PASSKEY_REPLY) {
- DEBUG(SSSDBG_OP_FAILURE, "Unexpected authentication token type [%s]\n",
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unexpected authentication token type [%s]\n",
sss_authtok_type_to_str(type));
- kerr = EINVAL;
+ kerr = EAGAIN;
goto done;
}
@@ -1186,6 +1207,44 @@ done:
#endif /* BUILD_PASSKEY */
}
+static krb5_error_code answer_password(krb5_context kctx,
+ struct krb5_req *kr,
+ krb5_responder_context rctx)
+{
+ krb5_error_code kerr;
+ int ret;
+ const char *pwd;
+
+ 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(kctx, rctx,
+ KRB5_RESPONDER_QUESTION_PASSWORD,
+ pwd);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "krb5_responder_set_answer failed.\n");
+ }
+
+ return kerr;
+ }
+
+ /* For SSS_PAM_PREAUTH and the other remaining commands the caller should
+ * continue to iterate over the available authentication methods. */
+ return EAGAIN;
+}
+
static krb5_error_code sss_krb5_responder(krb5_context ctx,
void *data,
krb5_responder_context rctx)
@@ -1193,9 +1252,7 @@ static krb5_error_code sss_krb5_responder(krb5_context ctx,
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;
+ krb5_error_code kerr = EINVAL;
if (kr == NULL) {
return EINVAL;
@@ -1207,48 +1264,76 @@ static krb5_error_code sss_krb5_responder(krb5_context ctx,
for (c = 0; question_list[c] != NULL; c++) {
DEBUG(SSSDBG_TRACE_ALL, "Got question [%s].\n", question_list[c]);
+ /* It is expected that the answer_*() functions only return EOK
+ * (success) if the authentication was successful, i.e. during
+ * SSS_PAM_AUTHENTICATE. In all other cases, e.g. during
+ * SSS_PAM_PREAUTH either EAGAIN should be returned to indicate
+ * that the other available authentication methods should be
+ * checked as well. Or some other error code to indicate a fatal
+ * error where no other methods should be tried.
+ * Especially if setting the answer failed neither EOK nor EAGAIN
+ * should be returned. */
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;
- }
+ kerr = answer_password(ctx, kr, rctx);
} else if (strcmp(question_list[c],
KRB5_RESPONDER_QUESTION_PKINIT) == 0
&& (sss_authtok_get_type(kr->pd->authtok)
== SSS_AUTHTOK_TYPE_SC_PIN
|| sss_authtok_get_type(kr->pd->authtok)
== SSS_AUTHTOK_TYPE_SC_KEYPAD)) {
- return answer_pkinit(ctx, kr, rctx);
+ kerr = answer_pkinit(ctx, kr, rctx);
} else if (strcmp(question_list[c], SSSD_IDP_OAUTH2_QUESTION) == 0) {
- return answer_idp_oauth2(ctx, kr, rctx);
+ kerr = answer_idp_oauth2(ctx, kr, rctx);
} else if (strcmp(question_list[c], SSSD_PASSKEY_QUESTION) == 0) {
- return answer_passkey(ctx, kr, rctx);
+ /* Skip answer_passkey for expired password changes, e.g. user with auth types
+ * passkey AND password set */
+ if (kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM || kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
+ continue;
+ }
+ kerr = answer_passkey(ctx, kr, rctx);
+ } else if (strcmp(question_list[c], KRB5_RESPONDER_QUESTION_OTP) == 0) {
+ kerr = answer_otp(ctx, kr, rctx);
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Unknown question type [%s]\n", question_list[c]);
+ kerr = EINVAL;
+ }
+
+ /* Continue to the next question when the given authtype cannot be
+ * handled by the answer_* function. This allows fallback between auth
+ * types, such as passkey -> password. */
+ if (kerr == EAGAIN) {
+ /* During pre-auth iterating over all authentication methods
+ * is expected and no message will be displayed. */
+ if (kr->pd->cmd == SSS_PAM_AUTHENTICATE) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Auth type [%s] could not be handled by answer "
+ "function, continuing to next question.\n",
+ question_list[c]);
+ }
+ continue;
+ } else {
+ return kerr;
}
}
+ } else {
+ kerr = answer_password(ctx, kr, rctx);
}
- return answer_otp(ctx, kr, rctx);
+ /* During SSS_PAM_PREAUTH 'EAGAIN' is expected because we will run
+ * through all offered authentication methods and all are expect to return
+ * 'EAGAIN' in the positive case to indicate that the other methods should
+ * be checked as well. If all methods are checked we are done and should
+ * return success.
+ * In the other steps, especially SSS_PAM_AUTHENTICATE, having 'EAGAIN' at
+ * this stage would mean that no method feels responsible for the provided
+ * credentials i.e. authentication failed and we should return an error.
+ */
+ if (kr->pd->cmd == SSS_PAM_PREAUTH) {
+ return kerr == EAGAIN ? 0 : kerr;
+ } else {
+ return kerr;
+ }
}
#endif /* HAVE_KRB5_GET_INIT_CREDS_OPT_SET_RESPONDER */
@@ -1270,13 +1355,14 @@ static krb5_error_code sss_krb5_prompter(krb5_context context, void *data,
int ret;
size_t c;
struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
+ const char *err_msg;
if (kr == NULL) {
return EINVAL;
}
DEBUG(SSSDBG_TRACE_ALL,
- "sss_krb5_prompter name [%s] banner [%s] num_prompts [%d] EINVAL.\n",
+ "sss_krb5_prompter name [%s] banner [%s] num_prompts [%d].\n",
name, banner, num_prompts);
if (num_prompts != 0) {
@@ -1285,7 +1371,12 @@ static krb5_error_code sss_krb5_prompter(krb5_context context, void *data,
prompts[c].prompt);
}
- DEBUG(SSSDBG_FUNC_DATA, "Prompter interface isn't used for password prompts by SSSD.\n");
+ err_msg = krb5_get_error_message(context, KRB5_LIBOS_CANTREADPWD);
+ DEBUG(SSSDBG_FUNC_DATA,
+ "Prompter interface isn't used for prompting by SSSD."
+ "Returning the expected error [%ld/%s].\n",
+ KRB5_LIBOS_CANTREADPWD, err_msg);
+ krb5_free_error_message(context, err_msg);
return KRB5_LIBOS_CANTREADPWD;
}
@@ -1314,7 +1405,7 @@ static krb5_error_code create_empty_cred(krb5_context ctx, krb5_principal princ,
krb5_creds *cred = NULL;
krb5_data *krb5_realm;
- cred = calloc(sizeof(krb5_creds), 1);
+ cred = calloc(1, sizeof(krb5_creds));
if (cred == NULL) {
DEBUG(SSSDBG_CRIT_FAILURE, "calloc failed.\n");
return ENOMEM;
@@ -2158,19 +2249,25 @@ sss_krb5_get_init_creds_password(krb5_context context, krb5_creds *creds,
{
krb5_error_code kerr;
krb5_init_creds_context init_cred_ctx = NULL;
+ int log_level = SSSDBG_MINOR_FAILURE;
+ struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
+
+ if (kr->pd->cmd != SSS_PAM_PREAUTH) {
+ log_level = SSSDBG_OP_FAILURE;
+ }
kerr = krb5_init_creds_init(context, client, prompter, data,
start_time, k5_gic_options,
&init_cred_ctx);
if (kerr != 0) {
- KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
+ KRB5_CHILD_DEBUG(log_level, kerr);
goto done;
}
if (password != NULL) {
kerr = krb5_init_creds_set_password(context, init_cred_ctx, password);
if (kerr != 0) {
- KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
+ KRB5_CHILD_DEBUG(log_level, kerr);
goto done;
}
}
@@ -2179,7 +2276,7 @@ sss_krb5_get_init_creds_password(krb5_context context, krb5_creds *creds,
kerr = krb5_init_creds_set_service(context, init_cred_ctx,
in_tkt_service);
if (kerr != 0) {
- KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
+ KRB5_CHILD_DEBUG(log_level, kerr);
goto done;
}
}
@@ -2190,7 +2287,7 @@ sss_krb5_get_init_creds_password(krb5_context context, krb5_creds *creds,
}
if (kerr != 0) {
- KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
+ KRB5_CHILD_DEBUG(log_level, kerr);
goto done;
}
@@ -2391,12 +2488,12 @@ static errno_t map_krb5_error(krb5_error_code kerr)
{
/* just pass SSSD's internal error codes */
if (kerr > 0 && IS_SSSD_ERROR(kerr)) {
- DEBUG(SSSDBG_CRIT_FAILURE, "[%d][%s].\n", kerr, sss_strerror(kerr));
+ DEBUG(SSSDBG_OP_FAILURE, "[%d][%s].\n", kerr, sss_strerror(kerr));
return kerr;
}
if (kerr != 0) {
- KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
+ KRB5_CHILD_DEBUG(SSSDBG_OP_FAILURE, kerr);
}
switch (kerr) {
@@ -2748,8 +2845,9 @@ static errno_t tgt_req_child(struct krb5_req *kr)
* should now know which authentication methods are available to
* update the password. */
DEBUG(SSSDBG_TRACE_FUNC,
- "krb5_get_init_creds_password returned [%d] during pre-auth, "
- "ignored.\n", kerr);
+ "krb5_get_init_creds_password returned [%d] while collecting "
+ "available authentication types, errors are expected "
+ "and ignored.\n", kerr);
ret = pam_add_prompting(kr);
if (ret != EOK) {
DEBUG(SSSDBG_CRIT_FAILURE, "pam_add_prompting failed.\n");