summaryrefslogtreecommitdiffstats
path: root/src/providers/krb5
diff options
context:
space:
mode:
Diffstat (limited to 'src/providers/krb5')
-rw-r--r--src/providers/krb5/krb5_child.c140
1 files changed, 139 insertions, 1 deletions
diff --git a/src/providers/krb5/krb5_child.c b/src/providers/krb5/krb5_child.c
index 6e27df0d4..4bfbf4bf3 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"));
}