URL: https://github.com/freeipa/freeipa/pull/6140
Author: abbra
 Title: #6140: ipa-kdb: refactor KDB driver to prepare for KDB version 9
Action: opened

PR body:
"""
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 <aboko...@redhat.com>
"""

To pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/6140/head:pr6140
git checkout pr6140
From 9d6439035d011066ba7d0ef203377c9fe2b889df Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <aboko...@redhat.com>
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 <aboko...@redhat.com>
---
 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 a64b434fe8b..f10211761d0 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 5775d408642..05e35f09eca 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 6e1e3e3513a..59dbe5411e7 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 1b972c167dd..64962cc9aff 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 @@ static krb5_error_code ipadb_get_pac(krb5_context kcontext,
     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 @@ static krb5_error_code ipadb_add_transited_service(krb5_context context,
     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 @@ static krb5_error_code ipadb_verify_pac(krb5_context context,
     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 @@ void get_authz_data_types(krb5_context context, krb5_db_entry *entry,
 
 }
 
-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 3696c3c6ce6..58b0ed8ff21 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 00000000000..fc6ca890ec9
--- /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 <sso...@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;
+}
+
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 00000000000..9ad6f964ee2
--- /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 <sso...@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"
+
+
diff --git a/server.m4 b/server.m4
index 5a14af06aaa..0139cbac4c1 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 ---------------------------------------------------------------------------
_______________________________________________
FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org
To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: 
https://docs.fedoraproject.org/en-US/project/code-of-conduct/
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedorahosted.org/archives/list/freeipa-devel@lists.fedorahosted.org
Do not reply to spam on the list, report it: 
https://pagure.io/fedora-infrastructure

Reply via email to