From 5ea21aee2028c37f8195f147d46548fdb84ba429 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Thu, 18 Oct 2012 16:34:35 +0200 Subject: [PATCH 12/17] krb5_child: send PAC to PAC responder If the authenticated user comes from a different realm the service ticket which was returned during the validation of the TGT is used to extract the PAC which is send to the pac responder for evaluation. --- Makefile.am | 6 +- src/providers/krb5/krb5_child.c | 140 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 143 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index ee7e198..07dcbd2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1443,7 +1443,8 @@ krb5_child_SOURCES = \ src/util/sss_krb5.c \ src/util/atomic_io.c \ src/util/util.c \ - src/util/signal.c + src/util/signal.c \ + src/sss_client/common.c krb5_child_CFLAGS = \ $(AM_CFLAGS) \ $(POPT_CFLAGS) \ @@ -1455,7 +1456,8 @@ krb5_child_LDADD = \ $(TEVENT_LIBS) \ $(POPT_LIBS) \ $(DHASH_LIBS) \ - $(KRB5_LIBS) + $(KRB5_LIBS) \ + $(CLIENT_LIBS) ldap_child_SOURCES = \ src/providers/ldap/ldap_child.c \ diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c index 6e27df0..4bfbf4b 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")); } -- 1.7.7.6