mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
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 <abokovoy@redhat.com> Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
parent
2278de732b
commit
2dc752018c
@ -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 ---------------------------------------------------------------------------
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
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);
|
||||
|
315
daemons/ipa-kdb/ipa_kdb_mspac_v6.c
Normal file
315
daemons/ipa-kdb/ipa_kdb_mspac_v6.c
Normal file
@ -0,0 +1,315 @@
|
||||
/*
|
||||
* MIT Kerberos KDC database backend for FreeIPA
|
||||
*
|
||||
* Authors: Simo Sorce <ssorce@redhat.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ipa_hostname.h"
|
||||
#include "ipa_kdb.h"
|
||||
#include <talloc.h>
|
||||
#include <unicase.h>
|
||||
#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;
|
||||
}
|
||||
|
34
daemons/ipa-kdb/ipa_kdb_mspac_v9.c
Normal file
34
daemons/ipa-kdb/ipa_kdb_mspac_v9.c
Normal file
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* MIT Kerberos KDC database backend for FreeIPA
|
||||
*
|
||||
* Authors: Simo Sorce <ssorce@redhat.com>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "ipa_hostname.h"
|
||||
#include "ipa_kdb.h"
|
||||
#include <talloc.h>
|
||||
#include <unicase.h>
|
||||
#include "util/time.h"
|
||||
#include "gen_ndr/ndr_krb5pac.h"
|
||||
|
||||
#include "ipa_kdb_mspac_private.h"
|
||||
|
||||
|
@ -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 ---------------------------------------------------------------------------
|
||||
|
Loading…
Reference in New Issue
Block a user