From 2dc752018cc452b57bd6018504166131cd33c6c5 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Thu, 13 Jan 2022 14:34:55 +0200 Subject: [PATCH] ipa-kdb: refactor KDB driver to prepare for KDB version 9 MIT Kerberos 1.20 changes DAL interface around PAC record issuance: sign_authdata callback is removed and replaced with issue_pac one. The signatures are different and logic changed as well. Prepare for KDB version 9 by moving PAC implementation into separate source files. ipa_kdb_mspac.c is left with most of the common code. FreeIPA supports sign_authdata callback since KDB version 6, move current implementation to ipa_kdb_mspac_v6.c. KDB version 8 actually changed sign_authdata interface and we accounted to that in ipa_kdb.c with a stub that re-uses v6 version. Keep it as it is right now. Finally, add KDB version 9 stub files. Compiling against MIT Kerberos 1.20 does not work yet, thus explicit #error message in ipa_kdb.c. This will be worked on later. Related: https://pagure.io/freeipa/issue/9083 Signed-off-by: Alexander Bokovoy Reviewed-By: Rob Crittenden --- configure.ac | 3 + daemons/ipa-kdb/Makefile.am | 8 +- daemons/ipa-kdb/ipa_kdb.c | 41 ++- daemons/ipa-kdb/ipa_kdb_mspac.c | 315 ++---------------------- daemons/ipa-kdb/ipa_kdb_mspac_private.h | 20 +- daemons/ipa-kdb/ipa_kdb_mspac_v6.c | 315 ++++++++++++++++++++++++ daemons/ipa-kdb/ipa_kdb_mspac_v9.c | 34 +++ server.m4 | 9 + 8 files changed, 445 insertions(+), 300 deletions(-) create mode 100644 daemons/ipa-kdb/ipa_kdb_mspac_v6.c create mode 100644 daemons/ipa-kdb/ipa_kdb_mspac_v9.c diff --git a/configure.ac b/configure.ac index a64b434fe..f10211761 100644 --- a/configure.ac +++ b/configure.ac @@ -293,6 +293,9 @@ AM_COND_IF([BUILD_IPA_CERTAUTH_PLUGIN], [ AM_CONDITIONAL([BUILD_IPA_KDCPOLICY_PLUGIN], [test x$have_kdcpolicy_plugin = xyes]) +AM_CONDITIONAL([BUILD_IPA_ISSUE_PAC], + [test x$have_kdb_issue_pac = xyes]) + dnl --------------------------------------------------------------------------- dnl - Check for program paths dnl --------------------------------------------------------------------------- diff --git a/daemons/ipa-kdb/Makefile.am b/daemons/ipa-kdb/Makefile.am index 5775d4086..05e35f09e 100644 --- a/daemons/ipa-kdb/Makefile.am +++ b/daemons/ipa-kdb/Makefile.am @@ -33,7 +33,7 @@ ipadb_la_SOURCES = \ ipa_kdb_passwords.c \ ipa_kdb_principals.c \ ipa_kdb_pwdpolicy.c \ - ipa_kdb_mspac.c \ + ipa_kdb_mspac.c \ ipa_kdb_mspac_private.h \ ipa_kdb_delegation.c \ ipa_kdb_audit_as.c \ @@ -41,6 +41,12 @@ ipadb_la_SOURCES = \ dist_noinst_DATA = ipa_kdb.exports +if BUILD_IPA_ISSUE_PAC +ipadb_la_SOURCES += ipa_kdb_mspac_v9.c +else +ipadb_la_SOURCES += ipa_kdb_mspac_v6.c +endif + if BUILD_IPA_CERTAUTH_PLUGIN ipadb_la_SOURCES += ipa_kdb_certauth.c endif diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c index 6e1e3e351..59dbe5411 100644 --- a/daemons/ipa-kdb/ipa_kdb.c +++ b/daemons/ipa-kdb/ipa_kdb.c @@ -792,8 +792,47 @@ kdb_vftabl kdb_function_table = { }; #endif +#if (KRB5_KDB_DAL_MAJOR_VERSION == 9) +#error DAL version 9 is not supported yet +/* Version 9 removes sign_authdata and adds issue_pac method. It is a complete + * revamp of how PAC is issued, so we need to implement it differently to previous + * versions. */ + +kdb_vftabl kdb_function_table = { + .maj_ver = KRB5_KDB_DAL_MAJOR_VERSION, + .min_ver = 0, + .init_library = ipadb_init_library, + .fini_library = ipadb_fini_library, + .init_module = ipadb_init_module, + .fini_module = ipadb_fini_module, + .create = ipadb_create, + .get_age = ipadb_get_age, + .get_principal = ipadb_get_principal, + .put_principal = ipadb_put_principal, + .delete_principal = ipadb_delete_principal, + .iterate = ipadb_iterate, + .create_policy = ipadb_create_pwd_policy, + .get_policy = ipadb_get_pwd_policy, + .put_policy = ipadb_put_pwd_policy, + .iter_policy = ipadb_iterate_pwd_policy, + .delete_policy = ipadb_delete_pwd_policy, + .fetch_master_key = ipadb_fetch_master_key, + .store_master_key_list = ipadb_store_master_key_list, + .change_pwd = ipadb_change_pwd, + .check_transited_realms = ipadb_check_transited_realms, + .check_policy_as = ipadb_check_policy_as, + .audit_as_req = ipadb_audit_as_req, + .check_allowed_to_delegate = ipadb_check_allowed_to_delegate, + .free_principal_e_data = ipadb_free_principal_e_data, + .get_s4u_x509_principal = NULL, + .allowed_to_delegate_from = NULL, + .issue_pac = NULL, +}; +#endif + #if (KRB5_KDB_DAL_MAJOR_VERSION != 6) && \ (KRB5_KDB_DAL_MAJOR_VERSION != 7) && \ - (KRB5_KDB_DAL_MAJOR_VERSION != 8) + (KRB5_KDB_DAL_MAJOR_VERSION != 8) && \ + (KRB5_KDB_DAL_MAJOR_VERSION != 9) #error unsupported DAL major version #endif diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c index 1b972c167..64962cc9a 100644 --- a/daemons/ipa-kdb/ipa_kdb_mspac.c +++ b/daemons/ipa-kdb/ipa_kdb_mspac.c @@ -982,11 +982,11 @@ static krb5_error_code ipadb_get_pac_attrs_blob(TALLOC_CTX *mem_ctx, #endif -static krb5_error_code ipadb_get_pac(krb5_context kcontext, - krb5_db_entry *client, - unsigned int flags, - krb5_timestamp authtime, - krb5_pac *pac) +krb5_error_code ipadb_get_pac(krb5_context kcontext, + krb5_db_entry *client, + unsigned int flags, + krb5_timestamp authtime, + krb5_pac *pac) { TALLOC_CTX *tmpctx; struct ipadb_e_data *ied; @@ -1181,7 +1181,7 @@ done: return kerr; } -static bool is_cross_realm_krbtgt(krb5_const_principal princ) +bool ipadb_is_cross_realm_krbtgt(krb5_const_principal princ) { if ((princ->length != 2) || (princ->data[0].length != 6) || @@ -2224,17 +2224,17 @@ done: return kerr; } -static krb5_error_code ipadb_verify_pac(krb5_context context, - unsigned int flags, - krb5_const_principal client_princ, - krb5_db_entry *proxy, - krb5_db_entry *server, - krb5_db_entry *krbtgt, - krb5_keyblock *server_key, - krb5_keyblock *krbtgt_key, - krb5_timestamp authtime, - krb5_authdata **authdata, - krb5_pac *pac) +krb5_error_code ipadb_verify_pac(krb5_context context, + unsigned int flags, + krb5_const_principal client_princ, + krb5_db_entry *proxy, + krb5_db_entry *server, + krb5_db_entry *krbtgt, + krb5_keyblock *server_key, + krb5_keyblock *krbtgt_key, + krb5_timestamp authtime, + krb5_authdata **authdata, + krb5_pac *pac) { krb5_keyblock *srv_key = NULL; krb5_keyblock *priv_key = NULL; @@ -2264,7 +2264,7 @@ static krb5_error_code ipadb_verify_pac(krb5_context context, * But when a trusted realm passes us a PAC the kdc checksum is * generated with that realm krbtgt key, so we need to use the cross * realm krbtgt to check the 'server' checksum instead. */ - if (is_cross_realm_krbtgt(krbtgt->princ)) { + if (ipadb_is_cross_realm_krbtgt(krbtgt->princ)) { /* krbtgt from a trusted realm */ is_cross_realm = true; @@ -2425,104 +2425,6 @@ done: return kerr; } -static krb5_error_code ipadb_sign_pac(krb5_context context, - unsigned int flags, - krb5_const_principal client_princ, - krb5_db_entry *server, - krb5_db_entry *krbtgt, - krb5_keyblock *server_key, - krb5_keyblock *krbtgt_key, - krb5_timestamp authtime, - krb5_pac pac, - krb5_data *pac_data) -{ - krb5_keyblock *right_krbtgt_signing_key = NULL; - krb5_key_data *right_krbtgt_key; - krb5_db_entry *right_krbtgt = NULL; - krb5_principal krbtgt_princ = NULL; - krb5_error_code kerr; - char *princ = NULL; - bool is_issuing_referral = false; - int ret; - - /* for cross realm trusts cases we need to sign with the right key. - * we need to fetch the right key on our own until the DAL is fixed - * to pass us separate check tgt keys and sign tgt keys */ - - /* We can only ever create the kdc checksum with our realm tgt key. - * So, if we get a cross realm tgt we have to fetch our realm tgt - * instead. */ - if (is_cross_realm_krbtgt(krbtgt->princ)) { - - ret = asprintf(&princ, "krbtgt/%.*s@%.*s", - server->princ->realm.length, - server->princ->realm.data, - server->princ->realm.length, - server->princ->realm.data); - if (ret == -1) { - princ = NULL; - kerr = ENOMEM; - goto done; - } - - kerr = krb5_parse_name(context, princ, &krbtgt_princ); - if (kerr) { - goto done; - } - - kerr = ipadb_get_principal(context, krbtgt_princ, 0, &right_krbtgt); - if (kerr) { - goto done; - } - - kerr = krb5_dbe_find_enctype(context, right_krbtgt, - -1, -1, 0, &right_krbtgt_key); - if (kerr) { - goto done; - } - if (!right_krbtgt_key) { - kerr = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; - goto done; - } - - right_krbtgt_signing_key = malloc(sizeof(krb5_keyblock)); - if (!right_krbtgt_signing_key) { - kerr = ENOMEM; - goto done; - } - - kerr = krb5_dbe_decrypt_key_data(context, NULL, right_krbtgt_key, - right_krbtgt_signing_key, NULL); - if (kerr) { - goto done; - } - - } else { - right_krbtgt_signing_key = krbtgt_key; - } - -#ifdef KRB5_KDB_FLAG_ISSUING_REFERRAL - is_issuing_referral = (flags & KRB5_KDB_FLAG_ISSUING_REFERRAL) != 0; -#endif - - /* only pass with_realm TRUE when it is cross-realm ticket and S4U2Self - * was requested */ - kerr = krb5_pac_sign_ext(context, pac, authtime, client_princ, server_key, - right_krbtgt_signing_key, - (is_issuing_referral && - (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION)), - pac_data); - -done: - free(princ); - krb5_free_principal(context, krbtgt_princ); - ipadb_free_principal(context, right_krbtgt); - if (right_krbtgt_signing_key != krbtgt_key) { - krb5_free_keyblock(context, right_krbtgt_signing_key); - } - return kerr; -} - void get_authz_data_types(krb5_context context, krb5_db_entry *entry, bool *_with_pac, bool *_with_pad) { @@ -2652,187 +2554,6 @@ done: } -krb5_error_code ipadb_sign_authdata(krb5_context context, - unsigned int flags, - krb5_const_principal client_princ, - krb5_db_entry *client, - krb5_db_entry *server, - krb5_db_entry *krbtgt, - krb5_keyblock *client_key, - krb5_keyblock *server_key, - krb5_keyblock *krbtgt_key, - krb5_keyblock *session_key, - krb5_timestamp authtime, - krb5_authdata **tgt_auth_data, - krb5_authdata ***signed_auth_data) -{ - krb5_const_principal ks_client_princ; - krb5_authdata **pac_auth_data = NULL; - krb5_authdata *authdata[2] = { NULL, NULL }; - krb5_authdata ad; - krb5_boolean is_as_req; - krb5_error_code kerr; - krb5_pac pac = NULL; - krb5_data pac_data; - struct ipadb_context *ipactx; - bool with_pac; - bool with_pad; - bool make_ad = false; - int result; - krb5_db_entry *client_entry = NULL; - krb5_boolean is_equal; - bool force_reinit_mspac = false; - - - is_as_req = ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0); - - /* When using s4u2proxy client_princ actually refers to the proxied user - * while client->princ to the proxy service asking for the TGS on behalf - * of the proxied user. So always use client_princ in preference */ - if (client_princ != NULL) { - ks_client_princ = client_princ; - if (!is_as_req) { - is_equal = false; - if ((client != NULL) && (client->princ != NULL)) { - is_equal = krb5_principal_compare(context, client_princ, client->princ); - } - if (!is_equal) { - kerr = ipadb_get_principal(context, client_princ, flags, &client_entry); - /* If we didn't find client_princ in our database, it might be: - * - a principal from another realm, handle it down in ipadb_get/verify_pac() - */ - if (kerr != 0) { - client_entry = NULL; - } - } - } - } else { - if (client == NULL) { - *signed_auth_data = NULL; - return 0; - } - ks_client_princ = client->princ; - } - - if (client_entry == NULL) client_entry = client; - - if (is_as_req) { - get_authz_data_types(context, client_entry, &with_pac, &with_pad); - } else { - get_authz_data_types(context, server, &with_pac, &with_pad); - } - - if (with_pad) { - krb5_klog_syslog(LOG_ERR, "PAD authorization data is requested but " \ - "currently not supported."); - } - - /* we need to create a PAC if we are requested one and this is an AS REQ, - * or we are doing protocol transition (S4USelf) but not over cross-realm - */ - if ((is_as_req && (flags & KRB5_KDB_FLAG_INCLUDE_PAC)) || - ((flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION) && (client != NULL))) { - make_ad = true; - } - - if (with_pac && make_ad) { - - ipactx = ipadb_get_context(context); - if (!ipactx) { - kerr = ENOMEM; - goto done; - } - - /* Be aggressive here: special case for discovering range type - * immediately after establishing the trust by IPA framework. For all - * other cases call ipadb_reinit_mspac() with force_reinit_mspac set - * to 'false' to make sure the information about trusted domains is - * updated on a regular basis for all worker processes. */ - if ((krb5_princ_size(context, ks_client_princ) == 2) && - (strncmp(krb5_princ_component(context, ks_client_princ, 0)->data, "HTTP", - krb5_princ_component(context, ks_client_princ, 0)->length) == 0) && - (ulc_casecmp(krb5_princ_component(context, ks_client_princ, 1)->data, - krb5_princ_component(context, ks_client_princ, 1)->length, - ipactx->kdc_hostname, strlen(ipactx->kdc_hostname), - NULL, NULL, &result) == 0)) { - force_reinit_mspac = true; - } - - (void)ipadb_reinit_mspac(ipactx, force_reinit_mspac); - - kerr = ipadb_get_pac(context, client, flags, authtime, &pac); - if (kerr != 0 && kerr != ENOENT) { - goto done; - } - } else if (with_pac && !is_as_req) { - /* find the existing PAC, if present */ - kerr = krb5_find_authdata(context, tgt_auth_data, NULL, - KRB5_AUTHDATA_WIN2K_PAC, &pac_auth_data); - if (kerr != 0) { - goto done; - } - /* check or generate pac data */ - if ((pac_auth_data == NULL) || (pac_auth_data[0] == NULL)) { - if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) { - kerr = ipadb_get_pac(context, client_entry, flags, authtime, &pac); - if (kerr != 0 && kerr != ENOENT) { - goto done; - } - } - } else { - if (pac_auth_data[1] != NULL) { - kerr = KRB5KDC_ERR_BADOPTION; /* FIXME: right error ? */ - goto done; - } - - kerr = ipadb_verify_pac(context, flags, ks_client_princ, client, - server, krbtgt, server_key, krbtgt_key, - authtime, pac_auth_data, &pac); - if (kerr != 0) { - goto done; - } - } - } - - if (pac == NULL) { - /* No PAC to deal with, proceed */ - *signed_auth_data = NULL; - kerr = 0; - goto done; - } - - kerr = ipadb_sign_pac(context, flags, ks_client_princ, server, krbtgt, - server_key, krbtgt_key, authtime, pac, &pac_data); - if (kerr != 0) { - goto done; - } - - /* put in signed data */ - ad.magic = KV5M_AUTHDATA; - ad.ad_type = KRB5_AUTHDATA_WIN2K_PAC; - ad.contents = (krb5_octet *)pac_data.data; - ad.length = pac_data.length; - - authdata[0] = &ad; - - kerr = krb5_encode_authdata_container(context, - KRB5_AUTHDATA_IF_RELEVANT, - authdata, - signed_auth_data); - krb5_free_data_contents(context, &pac_data); - if (kerr != 0) { - goto done; - } - - kerr = 0; - -done: - if (client_entry != NULL && client_entry != client) { - ipadb_free_principal(context, client_entry); - } - krb5_pac_free(context, pac); - return kerr; -} static char *get_server_netbios_name(struct ipadb_context *ipactx) { diff --git a/daemons/ipa-kdb/ipa_kdb_mspac_private.h b/daemons/ipa-kdb/ipa_kdb_mspac_private.h index 3696c3c6c..58b0ed8ff 100644 --- a/daemons/ipa-kdb/ipa_kdb_mspac_private.h +++ b/daemons/ipa-kdb/ipa_kdb_mspac_private.h @@ -55,4 +55,22 @@ char *dom_sid_string(TALLOC_CTX *memctx, const struct dom_sid *dom_sid); krb5_error_code filter_logon_info(krb5_context context, TALLOC_CTX *memctx, krb5_data realm, struct PAC_LOGON_INFO_CTR *info); void get_authz_data_types(krb5_context context, krb5_db_entry *entry, - bool *_with_pac, bool *_with_pad); \ No newline at end of file + bool *_with_pac, bool *_with_pad); + +bool ipadb_is_cross_realm_krbtgt(krb5_const_principal princ); +krb5_error_code ipadb_get_pac(krb5_context kcontext, + krb5_db_entry *client, + unsigned int flags, + krb5_timestamp authtime, + krb5_pac *pac); +krb5_error_code ipadb_verify_pac(krb5_context context, + unsigned int flags, + krb5_const_principal client_princ, + krb5_db_entry *proxy, + krb5_db_entry *server, + krb5_db_entry *krbtgt, + krb5_keyblock *server_key, + krb5_keyblock *krbtgt_key, + krb5_timestamp authtime, + krb5_authdata **authdata, + krb5_pac *pac); diff --git a/daemons/ipa-kdb/ipa_kdb_mspac_v6.c b/daemons/ipa-kdb/ipa_kdb_mspac_v6.c new file mode 100644 index 000000000..fc6ca890e --- /dev/null +++ b/daemons/ipa-kdb/ipa_kdb_mspac_v6.c @@ -0,0 +1,315 @@ +/* + * MIT Kerberos KDC database backend for FreeIPA + * + * Authors: Simo Sorce + * + * Copyright (C) 2011 Simo Sorce, Red Hat + * see file 'COPYING' for use and warranty information + * + * This program is free software you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include "ipa_hostname.h" +#include "ipa_kdb.h" +#include +#include +#include "util/time.h" +#include "gen_ndr/ndr_krb5pac.h" + +#include "ipa_kdb_mspac_private.h" + + +static krb5_error_code ipadb_sign_pac(krb5_context context, + unsigned int flags, + krb5_const_principal client_princ, + krb5_db_entry *server, + krb5_db_entry *krbtgt, + krb5_keyblock *server_key, + krb5_keyblock *krbtgt_key, + krb5_timestamp authtime, + krb5_pac pac, + krb5_data *pac_data) +{ + krb5_keyblock *right_krbtgt_signing_key = NULL; + krb5_key_data *right_krbtgt_key; + krb5_db_entry *right_krbtgt = NULL; + krb5_principal krbtgt_princ = NULL; + krb5_error_code kerr; + char *princ = NULL; + bool is_issuing_referral = false; + int ret; + + /* for cross realm trusts cases we need to sign with the right key. + * we need to fetch the right key on our own until the DAL is fixed + * to pass us separate check tgt keys and sign tgt keys */ + + /* We can only ever create the kdc checksum with our realm tgt key. + * So, if we get a cross realm tgt we have to fetch our realm tgt + * instead. */ + if (ipadb_is_cross_realm_krbtgt(krbtgt->princ)) { + + ret = asprintf(&princ, "krbtgt/%.*s@%.*s", + server->princ->realm.length, + server->princ->realm.data, + server->princ->realm.length, + server->princ->realm.data); + if (ret == -1) { + princ = NULL; + kerr = ENOMEM; + goto done; + } + + kerr = krb5_parse_name(context, princ, &krbtgt_princ); + if (kerr) { + goto done; + } + + kerr = ipadb_get_principal(context, krbtgt_princ, 0, &right_krbtgt); + if (kerr) { + goto done; + } + + kerr = krb5_dbe_find_enctype(context, right_krbtgt, + -1, -1, 0, &right_krbtgt_key); + if (kerr) { + goto done; + } + if (!right_krbtgt_key) { + kerr = KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN; + goto done; + } + + right_krbtgt_signing_key = malloc(sizeof(krb5_keyblock)); + if (!right_krbtgt_signing_key) { + kerr = ENOMEM; + goto done; + } + + kerr = krb5_dbe_decrypt_key_data(context, NULL, right_krbtgt_key, + right_krbtgt_signing_key, NULL); + if (kerr) { + goto done; + } + + } else { + right_krbtgt_signing_key = krbtgt_key; + } + +#ifdef KRB5_KDB_FLAG_ISSUING_REFERRAL + is_issuing_referral = (flags & KRB5_KDB_FLAG_ISSUING_REFERRAL) != 0; +#endif + + /* only pass with_realm TRUE when it is cross-realm ticket and S4U2Self + * was requested */ + kerr = krb5_pac_sign_ext(context, pac, authtime, client_princ, server_key, + right_krbtgt_signing_key, + (is_issuing_referral && + (flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION)), + pac_data); + +done: + free(princ); + krb5_free_principal(context, krbtgt_princ); + ipadb_free_principal(context, right_krbtgt); + if (right_krbtgt_signing_key != krbtgt_key) { + krb5_free_keyblock(context, right_krbtgt_signing_key); + } + return kerr; +} + + +krb5_error_code ipadb_sign_authdata(krb5_context context, + unsigned int flags, + krb5_const_principal client_princ, + krb5_db_entry *client, + krb5_db_entry *server, + krb5_db_entry *krbtgt, + krb5_keyblock *client_key, + krb5_keyblock *server_key, + krb5_keyblock *krbtgt_key, + krb5_keyblock *session_key, + krb5_timestamp authtime, + krb5_authdata **tgt_auth_data, + krb5_authdata ***signed_auth_data) +{ + krb5_const_principal ks_client_princ; + krb5_authdata **pac_auth_data = NULL; + krb5_authdata *authdata[2] = { NULL, NULL }; + krb5_authdata ad; + krb5_boolean is_as_req; + krb5_error_code kerr; + krb5_pac pac = NULL; + krb5_data pac_data; + struct ipadb_context *ipactx; + bool with_pac; + bool with_pad; + bool make_ad = false; + int result; + krb5_db_entry *client_entry = NULL; + krb5_boolean is_equal; + bool force_reinit_mspac = false; + + + is_as_req = ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0); + + /* When using s4u2proxy client_princ actually refers to the proxied user + * while client->princ to the proxy service asking for the TGS on behalf + * of the proxied user. So always use client_princ in preference */ + if (client_princ != NULL) { + ks_client_princ = client_princ; + if (!is_as_req) { + is_equal = false; + if ((client != NULL) && (client->princ != NULL)) { + is_equal = krb5_principal_compare(context, client_princ, client->princ); + } + if (!is_equal) { + kerr = ipadb_get_principal(context, client_princ, flags, &client_entry); + /* If we didn't find client_princ in our database, it might be: + * - a principal from another realm, handle it down in ipadb_get/verify_pac() + */ + if (kerr != 0) { + client_entry = NULL; + } + } + } + } else { + if (client == NULL) { + *signed_auth_data = NULL; + return 0; + } + ks_client_princ = client->princ; + } + + if (client_entry == NULL) client_entry = client; + + if (is_as_req) { + get_authz_data_types(context, client_entry, &with_pac, &with_pad); + } else { + get_authz_data_types(context, server, &with_pac, &with_pad); + } + + if (with_pad) { + krb5_klog_syslog(LOG_ERR, "PAD authorization data is requested but " \ + "currently not supported."); + } + + /* we need to create a PAC if we are requested one and this is an AS REQ, + * or we are doing protocol transition (S4USelf) but not over cross-realm + */ + if ((is_as_req && (flags & KRB5_KDB_FLAG_INCLUDE_PAC)) || + ((flags & KRB5_KDB_FLAG_PROTOCOL_TRANSITION) && (client != NULL))) { + make_ad = true; + } + + if (with_pac && make_ad) { + + ipactx = ipadb_get_context(context); + if (!ipactx) { + kerr = ENOMEM; + goto done; + } + + /* Be aggressive here: special case for discovering range type + * immediately after establishing the trust by IPA framework. For all + * other cases call ipadb_reinit_mspac() with force_reinit_mspac set + * to 'false' to make sure the information about trusted domains is + * updated on a regular basis for all worker processes. */ + if ((krb5_princ_size(context, ks_client_princ) == 2) && + (strncmp(krb5_princ_component(context, ks_client_princ, 0)->data, "HTTP", + krb5_princ_component(context, ks_client_princ, 0)->length) == 0) && + (ulc_casecmp(krb5_princ_component(context, ks_client_princ, 1)->data, + krb5_princ_component(context, ks_client_princ, 1)->length, + ipactx->kdc_hostname, strlen(ipactx->kdc_hostname), + NULL, NULL, &result) == 0)) { + force_reinit_mspac = true; + } + + (void)ipadb_reinit_mspac(ipactx, force_reinit_mspac); + + kerr = ipadb_get_pac(context, client, flags, authtime, &pac); + if (kerr != 0 && kerr != ENOENT) { + goto done; + } + } else if (with_pac && !is_as_req) { + /* find the existing PAC, if present */ + kerr = krb5_find_authdata(context, tgt_auth_data, NULL, + KRB5_AUTHDATA_WIN2K_PAC, &pac_auth_data); + if (kerr != 0) { + goto done; + } + /* check or generate pac data */ + if ((pac_auth_data == NULL) || (pac_auth_data[0] == NULL)) { + if (flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) { + kerr = ipadb_get_pac(context, client_entry, flags, authtime, &pac); + if (kerr != 0 && kerr != ENOENT) { + goto done; + } + } + } else { + if (pac_auth_data[1] != NULL) { + kerr = KRB5KDC_ERR_BADOPTION; /* FIXME: right error ? */ + goto done; + } + + kerr = ipadb_verify_pac(context, flags, ks_client_princ, client, + server, krbtgt, server_key, krbtgt_key, + authtime, pac_auth_data, &pac); + if (kerr != 0) { + goto done; + } + } + } + + if (pac == NULL) { + /* No PAC to deal with, proceed */ + *signed_auth_data = NULL; + kerr = 0; + goto done; + } + + kerr = ipadb_sign_pac(context, flags, ks_client_princ, server, krbtgt, + server_key, krbtgt_key, authtime, pac, &pac_data); + if (kerr != 0) { + goto done; + } + + /* put in signed data */ + ad.magic = KV5M_AUTHDATA; + ad.ad_type = KRB5_AUTHDATA_WIN2K_PAC; + ad.contents = (krb5_octet *)pac_data.data; + ad.length = pac_data.length; + + authdata[0] = &ad; + + kerr = krb5_encode_authdata_container(context, + KRB5_AUTHDATA_IF_RELEVANT, + authdata, + signed_auth_data); + krb5_free_data_contents(context, &pac_data); + if (kerr != 0) { + goto done; + } + + kerr = 0; + +done: + if (client_entry != NULL && client_entry != client) { + ipadb_free_principal(context, client_entry); + } + krb5_pac_free(context, pac); + return kerr; +} + diff --git a/daemons/ipa-kdb/ipa_kdb_mspac_v9.c b/daemons/ipa-kdb/ipa_kdb_mspac_v9.c new file mode 100644 index 000000000..9ad6f964e --- /dev/null +++ b/daemons/ipa-kdb/ipa_kdb_mspac_v9.c @@ -0,0 +1,34 @@ +/* + * MIT Kerberos KDC database backend for FreeIPA + * + * Authors: Simo Sorce + * + * Copyright (C) 2011 Simo Sorce, Red Hat + * see file 'COPYING' for use and warranty information + * + * This program is free software you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "config.h" + +#include "ipa_hostname.h" +#include "ipa_kdb.h" +#include +#include +#include "util/time.h" +#include "gen_ndr/ndr_krb5pac.h" + +#include "ipa_kdb_mspac_private.h" + + diff --git a/server.m4 b/server.m4 index 5a14af06a..0139cbac4 100644 --- a/server.m4 +++ b/server.m4 @@ -74,6 +74,15 @@ krb5rundir="${runstatedir}/krb5kdc" AC_SUBST(KRAD_LIBS) AC_SUBST(krb5rundir) +dnl --------------------------------------------------------------------------- +dnl - Check for KRB5 KDB API issue_pac support +dnl --------------------------------------------------------------------------- + +AC_CHECK_HEADER(kdb.h, [], [AC_MSG_ERROR([kdb.h not found])]) +AC_CHECK_MEMBER([kdb_vftabl.issue_pac], + [have_kdb_issue_pac=yes], + [have_kdb_issue_pac=no]) + dnl --------------------------------------------------------------------------- dnl - Check for UUID library dnl ---------------------------------------------------------------------------