mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-12 17:21:55 -06:00
2dc752018c
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>
316 lines
11 KiB
C
316 lines
11 KiB
C
/*
|
|
* 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;
|
|
}
|
|
|