summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSumit Bose <sbose@redhat.com>2015-10-12 11:52:56 +0200
committerJakub Hrozek <jhrozek@redhat.com>2017-02-23 10:15:16 +0100
commit2d527aab0bab0c5323b7ea09c9a8c3820f4f8736 (patch)
tree4fe47afd3ff19934fabae7764edfb2b539e72e13
parent52f45837ded98564968da42229b37db6a36ad627 (diff)
downloadsssd-2d527aab0bab0c5323b7ea09c9a8c3820f4f8736.tar.gz
sssd-2d527aab0bab0c5323b7ea09c9a8c3820f4f8736.tar.xz
sssd-2d527aab0bab0c5323b7ea09c9a8c3820f4f8736.zip
KRB5: allow pkinit pre-authentication
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
-rw-r--r--src/providers/krb5/krb5_auth.c18
-rw-r--r--src/providers/krb5/krb5_child.c286
-rw-r--r--src/providers/krb5/krb5_child_handler.c6
-rw-r--r--src/sss_client/sss_cli.h6
4 files changed, 303 insertions, 13 deletions
diff --git a/src/providers/krb5/krb5_auth.c b/src/providers/krb5/krb5_auth.c
index 0e685618e..c2d6d7eea 100644
--- a/src/providers/krb5/krb5_auth.c
+++ b/src/providers/krb5/krb5_auth.c
@@ -344,8 +344,13 @@ static void krb5_auth_store_creds(struct sss_domain_info *domain,
domain->cache_credentials_min_ff_length);
ret = EINVAL;
}
- } else {
+ } else if (sss_authtok_get_type(pd->authtok) ==
+ SSS_AUTHTOK_TYPE_PASSWORD) {
ret = sss_authtok_get_password(pd->authtok, &password, NULL);
+ } else {
+ DEBUG(SSSDBG_MINOR_FAILURE, "Cannot cache authtok type [%d].\n",
+ sss_authtok_get_type(pd->authtok));
+ ret = EINVAL;
}
break;
case SSS_PAM_CHAUTHTOK:
@@ -466,7 +471,9 @@ struct tevent_req *krb5_auth_send(TALLOC_CTX *mem_ctx,
case SSS_PAM_AUTHENTICATE:
case SSS_PAM_CHAUTHTOK:
if (authtok_type != SSS_AUTHTOK_TYPE_PASSWORD
- && authtok_type != SSS_AUTHTOK_TYPE_2FA) {
+ && authtok_type != SSS_AUTHTOK_TYPE_2FA
+ && authtok_type != SSS_AUTHTOK_TYPE_SC_PIN
+ && authtok_type != SSS_AUTHTOK_TYPE_SC_KEYPAD) {
/* handle empty password gracefully */
if (authtok_type == SSS_AUTHTOK_TYPE_EMPTY) {
DEBUG(SSSDBG_CRIT_FAILURE,
@@ -1023,6 +1030,12 @@ static void krb5_auth_done(struct tevent_req *subreq)
ret = EOK;
goto done;
+ case ERR_NO_AUTH_METHOD_AVAILABLE:
+ state->pam_status = PAM_NO_MODULE_DATA;
+ state->dp_err = DP_ERR_OK;
+ ret = EOK;
+ goto done;
+
default:
DEBUG(SSSDBG_IMPORTANT_INFO,
"The krb5_child process returned an error. Please inspect the "
@@ -1185,6 +1198,7 @@ krb5_pam_handler_send(TALLOC_CTX *mem_ctx,
switch (pd->cmd) {
case SSS_PAM_AUTHENTICATE:
+ case SSS_PAM_PREAUTH:
case SSS_CMD_RENEW:
case SSS_PAM_CHAUTHTOK_PRELIM:
case SSS_PAM_CHAUTHTOK:
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 031847089..777a25f2a 100644
--- a/src/providers/krb5/krb5_child.c
+++ b/src/providers/krb5/krb5_child.c
@@ -64,6 +64,7 @@ struct krb5_req {
krb5_creds *creds;
bool otp;
bool password_prompting;
+ bool pkinit_prompting;
char *otp_vendor;
char *otp_token_id;
char *otp_challenge;
@@ -587,6 +588,138 @@ done:
return ret;
}
+static bool pkinit_identity_matches(const char *identity,
+ const char *token_name,
+ const char *module_name)
+{
+ TALLOC_CTX *tmp_ctx = NULL;
+ char *str;
+ bool res = false;
+
+ tmp_ctx = talloc_new(NULL);
+ if (tmp_ctx == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
+ return false;
+ }
+
+ str = talloc_asprintf(tmp_ctx, "module_name=%s", module_name);
+ if (str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ goto done;
+ }
+
+ if (strstr(identity, str) == NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "Identity [%s] does not contain [%s].\n",
+ identity, str);
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Found [%s] in identity [%s].\n", str, identity);
+
+ str = talloc_asprintf(tmp_ctx, "token=%s", token_name);
+ if (str == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_asprintf failed.\n");
+ goto done;
+ }
+
+ if (strstr(identity, str) == NULL) {
+ DEBUG(SSSDBG_TRACE_ALL, "Identity [%s] does not contain [%s].\n",
+ identity, str);
+ goto done;
+ }
+ DEBUG(SSSDBG_TRACE_ALL, "Found [%s] in identity [%s].\n", str, identity);
+
+ res = true;
+
+done:
+ talloc_free(tmp_ctx);
+
+ return res;
+}
+
+static krb5_error_code answer_pkinit(krb5_context ctx,
+ struct krb5_req *kr,
+ krb5_responder_context rctx)
+{
+ krb5_error_code kerr;
+ const char *pin = NULL;
+ const char *token_name = NULL;
+ const char *module_name = NULL;
+ krb5_responder_pkinit_challenge *chl = NULL;
+ size_t c;
+
+ kerr = krb5_responder_pkinit_get_challenge(ctx, rctx, &chl);
+ if (kerr != EOK || chl == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "krb5_responder_pkinit_get_challenge failed.\n");
+ return kerr;
+ }
+ if (chl->identities == NULL || chl->identities[0] == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "No identities for pkinit!\n");
+ kerr = EINVAL;
+ goto done;
+ }
+
+ if (DEBUG_IS_SET(SSSDBG_TRACE_ALL)) {
+ for (c = 0; chl->identities[c] != NULL; c++) {
+ DEBUG(SSSDBG_TRACE_ALL, "[%zu] Identity [%s] flags [%"PRId32"].\n",
+ c, chl->identities[c]->identity,
+ chl->identities[c]->token_flags);
+ }
+ }
+
+ 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)
+ == 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);
+ 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;
+ }
+ }
+
+ 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");
+ }
+
+ goto done;
+ }
+
+ kerr = EOK;
+
+done:
+ krb5_responder_pkinit_challenge_free(ctx, rctx, chl);
+
+ return kerr;
+}
+
static krb5_error_code sss_krb5_responder(krb5_context ctx,
void *data,
krb5_responder_context rctx)
@@ -634,6 +767,9 @@ static krb5_error_code sss_krb5_responder(krb5_context ctx,
return kerr;
}
+ } else if (strcmp(question_list[c],
+ KRB5_RESPONDER_QUESTION_PKINIT) == 0) {
+ return answer_pkinit(ctx, kr, rctx);
}
}
}
@@ -661,18 +797,23 @@ static krb5_error_code sss_krb5_prompter(krb5_context context, void *data,
size_t c;
struct krb5_req *kr = talloc_get_type(data, struct krb5_req);
+ if (kr == NULL) {
+ return EINVAL;
+ }
+
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);
}
}
+
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot handle password prompts.\n");
return KRB5_LIBOS_CANTREADPWD;
}
@@ -1036,6 +1177,63 @@ static errno_t k5c_send_data(struct krb5_req *kr, int fd, errno_t error)
return EOK;
}
+static errno_t get_pkinit_identity(TALLOC_CTX *mem_ctx,
+ struct sss_auth_token *authtok,
+ char **_identity)
+{
+ int ret;
+ char *identity;
+ const char *token_name;
+ const char *module_name;
+ const char *key_id;
+
+ ret = sss_authtok_get_sc(authtok, NULL, NULL,
+ &token_name, NULL,
+ &module_name, NULL,
+ &key_id, NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "sss_authtok_get_sc failed.\n");
+ return ret;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Got [%s][%s].\n", token_name, module_name);
+
+ if (module_name == NULL || *module_name == '\0') {
+ module_name = "p11-kit-proxy.so";
+ }
+
+ identity = talloc_asprintf(mem_ctx, "PKCS11:module_name=%s", module_name);
+ if (identity == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ return ENOMEM;
+ }
+
+ if (token_name != NULL && *token_name != '\0') {
+ identity = talloc_asprintf_append(identity, ":token=%s",
+ token_name);
+ if (identity == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "talloc_asprintf_append failed.\n");
+ return ENOMEM;
+ }
+ }
+
+ if (key_id != NULL && *key_id != '\0') {
+ identity = talloc_asprintf_append(identity, ":certid=%s", key_id);
+ if (identity == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "talloc_asprintf_append failed.\n");
+ return ENOMEM;
+ }
+ }
+
+ *_identity = identity;
+
+ DEBUG(SSSDBG_TRACE_ALL, "Using pkinit identity [%s].\n", identity);
+
+ return EOK;
+}
+
static errno_t add_ticket_times_and_upn_to_response(struct krb5_req *kr)
{
int ret;
@@ -1268,6 +1466,8 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
int realm_length;
krb5_error_code kerr;
char *cc_name;
+ int ret;
+ char *identity = NULL;
kerr = sss_krb5_get_init_creds_opt_set_expire_callback(kr->ctx, kr->options,
sss_krb5_expire_callback_func,
@@ -1284,6 +1484,30 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
return KRB5KRB_ERR_GENERIC;
}
+ 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) {
+ DEBUG(SSSDBG_TRACE_ALL,
+ "Found Smartcard credentials, trying pkinit.\n");
+
+ ret = get_pkinit_identity(kr, kr->pd->authtok, &identity);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_pkinit_identity failed.\n");
+ return ret;
+ }
+
+ kerr = krb5_get_init_creds_opt_set_pa(kr->ctx, kr->options,
+ "X509_user_identity", identity);
+ talloc_free(identity);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "krb5_get_init_creds_opt_set_pa failed.\n");
+ return kerr;
+ }
+
+ /* TODO: Maybe X509_anchors should be added here as well */
+ }
+
DEBUG(SSSDBG_TRACE_FUNC,
"Attempting kinit for realm [%s]\n",realm_name);
kerr = krb5_get_init_creds_password(kr->ctx, kr->creds, kr->princ,
@@ -1294,12 +1518,25 @@ static krb5_error_code get_and_save_tgt(struct krb5_req *kr,
/* Any errors are ignored during pre-auth, only data is collected to
* be send back to the client.*/
DEBUG(SSSDBG_TRACE_FUNC,
- "krb5_get_init_creds_password returned [%d} during pre-auth.\n",
+ "krb5_get_init_creds_password returned [%d] during pre-auth.\n",
kerr);
return 0;
} else {
if (kerr != 0) {
KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
+
+ /* If during authentication either the MIT Kerberos pkinit
+ * pre-auth module is missing or no Smartcard is inserted and only
+ * pkinit is available KRB5_PREAUTH_FAILED is returned.
+ * ERR_NO_AUTH_METHOD_AVAILABLE is used to indicate to the
+ * frontend that local authentication might be tried. */
+ if (kr->pd->cmd == SSS_PAM_AUTHENTICATE
+ && kerr == KRB5_PREAUTH_FAILED
+ && kr->password_prompting == false
+ && kr->otp == false
+ && kr->pkinit_prompting == false) {
+ return ERR_NO_AUTH_METHOD_AVAILABLE;
+ }
return kerr;
}
}
@@ -1367,6 +1604,12 @@ done:
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));
+ return kerr;
+ }
+
if (kerr != 0) {
KRB5_CHILD_DEBUG(SSSDBG_CRIT_FAILURE, kerr);
}
@@ -1615,9 +1858,12 @@ static errno_t tgt_req_child(struct krb5_req *kr)
DEBUG(SSSDBG_TRACE_LIBS, "Attempting to get a TGT\n");
- /* No password is needed for pre-auth, or if we have 2FA */
+ /* No password is needed for pre-auth or if we have 2FA or SC */
if (kr->pd->cmd != SSS_PAM_PREAUTH
- && sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_2FA) {
+ && sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_2FA
+ && sss_authtok_get_type(kr->pd->authtok) != SSS_AUTHTOK_TYPE_SC_PIN
+ && sss_authtok_get_type(kr->pd->authtok)
+ != SSS_AUTHTOK_TYPE_SC_KEYPAD) {
ret = sss_authtok_get_password(kr->pd->authtok, &password, NULL);
switch (ret) {
case EOK:
@@ -1641,7 +1887,12 @@ static errno_t tgt_req_child(struct krb5_req *kr)
if (kr->pd->cmd == SSS_PAM_PREAUTH) {
/* add OTP tokeninfo messge if available */
if (kr->otp) {
- kerr = k5c_attach_otp_info_msg(kr);
+ ret = k5c_attach_otp_info_msg(kr);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "k5c_attach_otp_info_msg failed.\n");
+ goto done;
+ }
}
if (kr->password_prompting) {
@@ -1651,6 +1902,15 @@ static errno_t tgt_req_child(struct krb5_req *kr)
goto done;
}
}
+
+ if (kr->pkinit_prompting) {
+ ret = pam_add_response(kr->pd, SSS_CERT_AUTH_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);
@@ -1918,6 +2178,7 @@ static errno_t unpack_buffer(uint8_t *buf, size_t size,
*offline ? "true" : "false", kr->upn ? kr->upn : "none");
if (pd->cmd == SSS_PAM_AUTHENTICATE ||
+ pd->cmd == SSS_PAM_PREAUTH ||
pd->cmd == SSS_CMD_RENEW ||
pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM || pd->cmd == SSS_PAM_CHAUTHTOK) {
SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
@@ -2801,11 +3062,16 @@ int main(int argc, const char *argv[])
goto done;
}
- kerr = become_user(kr->uid, kr->gid);
- if (kerr != 0) {
- DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n");
- ret = EFAULT;
- goto done;
+ /* pkinit need access to pcscd */
+ 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 = become_user(kr->uid, kr->gid);
+ if (kerr != 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "become_user failed.\n");
+ ret = EFAULT;
+ goto done;
+ }
}
DEBUG(SSSDBG_TRACE_INTERNAL,
diff --git a/src/providers/krb5/krb5_child_handler.c b/src/providers/krb5/krb5_child_handler.c
index 6a3dc9d73..680e67b08 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -90,7 +90,9 @@ static errno_t pack_authtok(struct io_buffer *buf, size_t *rp,
if (ret == EOK) {
SAFEALIGN_COPY_UINT32(&buf->data[*rp], &auth_token_type, rp);
SAFEALIGN_COPY_UINT32(&buf->data[*rp], &auth_token_length, rp);
- safealign_memcpy(&buf->data[*rp], data, auth_token_length, rp);
+ if (data != NULL) {
+ safealign_memcpy(&buf->data[*rp], data, auth_token_length, rp);
+ }
}
return ret;
@@ -145,6 +147,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
buf->size = 8*sizeof(uint32_t) + strlen(kr->upn);
if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ||
+ kr->pd->cmd == SSS_PAM_PREAUTH ||
kr->pd->cmd == SSS_CMD_RENEW ||
kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
@@ -187,6 +190,7 @@ static errno_t create_send_buffer(struct krb5child_req *kr,
safealign_memcpy(&buf->data[rp], kr->upn, strlen(kr->upn), &rp);
if (kr->pd->cmd == SSS_PAM_AUTHENTICATE ||
+ kr->pd->cmd == SSS_PAM_PREAUTH ||
kr->pd->cmd == SSS_CMD_RENEW ||
kr->pd->cmd == SSS_PAM_CHAUTHTOK_PRELIM ||
kr->pd->cmd == SSS_PAM_CHAUTHTOK) {
diff --git a/src/sss_client/sss_cli.h b/src/sss_client/sss_cli.h
index b6610bc6d..8091e1151 100644
--- a/src/sss_client/sss_cli.h
+++ b/src/sss_client/sss_cli.h
@@ -431,6 +431,12 @@ enum response_type {
* SSS_PAM_OTP_INFO to determine the type of
* prompting. There is no message.
* @param None. */
+ SSS_CERT_AUTH_PROMPTING, /**< Indicates that on the server side
+ * Smartcard/certificate based authentication is
+ * available for the selected account. This might
+ * be used together with other prompting options
+ * to determine the type of prompting.
+ * @param None. */
};
/**