diff options
Diffstat (limited to 'src/providers/krb5')
-rw-r--r-- | src/providers/krb5/krb5_child.c | 140 |
1 files changed, 139 insertions, 1 deletions
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index 6e27df0d..4bfbf4bf 100644 --- a/src/providers/krb5/krb5_child.c +++ b/src/providers/krb5/krb5_child.c @@ -36,6 +36,7 @@ #include "providers/dp_backend.h" #include "providers/krb5/krb5_auth.h" #include "providers/krb5/krb5_utils.h" +#include "sss_cli.h" #define SSSD_KRB5_CHANGEPW_PRINCIPAL "kadmin/changepw" @@ -100,6 +101,124 @@ struct krb5_req { static krb5_context krb5_error_ctx; #define KRB5_CHILD_DEBUG(level, error) KRB5_DEBUG(level, krb5_error_ctx, error) +static krb5_error_code extract_and_send_pac(struct krb5_req *kr, + krb5_ccache ccache, + krb5_principal server_principal, + krb5_principal client_principal, + krb5_keytab keytab) +{ + krb5_error_code kerr; + krb5_creds mcred; + krb5_creds cred; + krb5_authdata **pac_authdata = NULL; + krb5_pac pac = NULL; + struct sss_cli_req_data sss_data; + int ret; + int errnop; + krb5_ticket *ticket = NULL; + krb5_keytab_entry entry; + + memset(&entry, 0, sizeof(entry)); + memset(&mcred, 0, sizeof(mcred)); + memset(&cred, 0, sizeof(mcred)); + + mcred.server = server_principal; + mcred.client = client_principal; + + kerr = krb5_cc_retrieve_cred(kr->ctx, ccache, 0, &mcred, &cred); + if (kerr != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("krb5_cc_retrieve_cred failed.\n")); + goto done; + } + + kerr = krb5_decode_ticket(&cred.ticket, &ticket); + if (kerr != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("krb5_decode_ticket failed.\n")); + goto done; + } + + kerr = krb5_server_decrypt_ticket_keytab(kr->ctx, keytab, ticket); + if (kerr != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("krb5_server_decrypt_ticket_keytab failed.\n")); + goto done; + } + + kerr = sss_krb5_find_authdata(kr->ctx, + ticket->enc_part2->authorization_data, NULL, + KRB5_AUTHDATA_WIN2K_PAC, &pac_authdata); + if (kerr != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("krb5_find_authdata failed.\n")); + goto done; + } + + if (pac_authdata == NULL || pac_authdata[0] == NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("No PAC authdata available.\n")); + kerr = ENOENT; + goto done; + } + + if (pac_authdata[1] != NULL) { + DEBUG(SSSDBG_OP_FAILURE, ("More than one PAC autdata found.\n")); + kerr = EINVAL; + goto done; + } + + kerr = krb5_pac_parse(kr->ctx, pac_authdata[0]->contents, + pac_authdata[0]->length, &pac); + if (kerr != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("krb5_pac_parse failed.\n")); + goto done; + } + + kerr = krb5_kt_get_entry(kr->ctx, keytab, ticket->server, + ticket->enc_part.kvno, ticket->enc_part.enctype, + &entry); + if (kerr != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("krb5_kt_get_entry failed.\n")); + goto done; + } + + kerr = krb5_pac_verify(kr->ctx, pac, 0, NULL, &entry.key, NULL); + if (kerr != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("krb5_pac_verify failed.\n")); + goto done; + } + + ret = unsetenv("_SSS_LOOPS"); + if (ret != EOK) { + DEBUG(1, ("Failed to unset _SSS_LOOPS, " + "sss_pac_make_request will most certainly fail.\n")); + } + + sss_data.len = pac_authdata[0]->length; + sss_data.data = pac_authdata[0]->contents; + + ret = sss_pac_make_request(SSS_PAC_ADD_PAC_USER, &sss_data, + NULL, NULL, &errnop); + if (ret != NSS_STATUS_SUCCESS || errnop != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("sss_pac_make_request failed [%d][%d].\n", + ret, errnop)); + kerr = EIO; + goto done; + } + + kerr = 0; + +done: + if (entry.magic != 0) { + krb5_free_keytab_entry_contents(kr->ctx, &entry); + } + krb5_pac_free(kr->ctx, pac); + krb5_free_authdata(kr->ctx, pac_authdata); + if (ticket != NULL) { + krb5_free_ticket(kr->ctx, ticket); + } + + krb5_free_cred_contents(kr->ctx, &cred); + + return kerr; +} + static void sss_krb5_expire_callback_func(krb5_context context, void *data, krb5_timestamp password_expiration, krb5_timestamp account_expiration, @@ -696,6 +815,7 @@ static krb5_error_code validate_tgt(struct krb5_req *kr) krb5_verify_init_creds_opt opt; krb5_principal validation_princ = NULL; bool realm_entry_found = false; + krb5_ccache validation_ccache = NULL; memset(&keytab, 0, sizeof(keytab)); kerr = krb5_kt_resolve(kr->ctx, kr->keytab, &keytab); @@ -778,7 +898,7 @@ static krb5_error_code validate_tgt(struct krb5_req *kr) krb5_verify_init_creds_opt_init(&opt); kerr = krb5_verify_init_creds(kr->ctx, kr->creds, validation_princ, keytab, - NULL, &opt); + &validation_ccache, &opt); if (kerr == 0) { DEBUG(SSSDBG_TRACE_FUNC, ("TGT verified using key for [%s].\n", @@ -786,9 +906,27 @@ static krb5_error_code validate_tgt(struct krb5_req *kr) } else { DEBUG(SSSDBG_CRIT_FAILURE ,("TGT failed verification using key " \ "for [%s].\n", principal)); + goto done; + } + + /* Try to find and send the PAC to the PAC responder for principals which + * do not belong to our realm. Failures are not critical. */ + if (kr->upn_from_different_realm) { + kerr = extract_and_send_pac(kr, validation_ccache, validation_princ, + kr->creds->client, keytab); + if (kerr != 0) { + DEBUG(SSSDBG_OP_FAILURE, ("extract_and_send_pac failed, group " \ + "membership for user with principal [%s] " \ + "might not be correct.\n", kr->name)); + kerr = 0; + } } done: + if (validation_ccache != NULL) { + krb5_cc_destroy(kr->ctx, validation_ccache); + } + if (krb5_kt_close(kr->ctx, keytab) != 0) { DEBUG(SSSDBG_MINOR_FAILURE, ("krb5_kt_close failed")); } |