krb5 commit: Add PAC APIs which can include a client realm

Greg Hudson ghudson at mit.edu
Thu Oct 11 14:33:06 EDT 2018


https://github.com/krb5/krb5/commit/3b56f54e31ee8db2b15a059e0d53609c1f4c3b83
commit 3b56f54e31ee8db2b15a059e0d53609c1f4c3b83
Author: Isaac Boukris <iboukris at gmail.com>
Date:   Sat Sep 29 07:21:56 2018 +0300

    Add PAC APIs which can include a client realm
    
    These APIs are needed for KDC handling of cross-realm S4U2Self
    tickets; see [MS-SFU] 3.2.5.x.  Note that we currently do not allow
    re-signing a PAC to include the realm; the caller must create a new
    one.
    
    [ghudson at mit.edu: added documentation; changed names and parameter
    order; edited commit message]
    
    ticket: 8749 (new)

 doc/appdev/refs/api/index.rst |    2 +
 src/include/krb5/krb5.hin     |   50 +++++++++++++++
 src/lib/krb5/krb/authdata.h   |    3 +-
 src/lib/krb5/krb/pac.c        |   31 ++++++++--
 src/lib/krb5/krb/pac_sign.c   |   32 ++++++++--
 src/lib/krb5/krb/t_pac.c      |  132 +++++++++++++++++++++++++++++++++++++++++
 src/lib/krb5/libkrb5.exports  |    2 +
 src/lib/krb5_32.def           |    2 +
 8 files changed, 242 insertions(+), 12 deletions(-)

diff --git a/doc/appdev/refs/api/index.rst b/doc/appdev/refs/api/index.rst
index 66aff59..f8a5aa5 100644
--- a/doc/appdev/refs/api/index.rst
+++ b/doc/appdev/refs/api/index.rst
@@ -256,7 +256,9 @@ Rarely used public interfaces
    krb5_pac_init.rst
    krb5_pac_parse.rst
    krb5_pac_sign.rst
+   krb5_pac_sign_ext.rst
    krb5_pac_verify.rst
+   krb5_pac_verify_ext.rst
    krb5_prepend_error_message.rst
    krb5_principal2salt.rst
    krb5_rd_cred.rst
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 21fabb4..c40a6cc 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -8313,6 +8313,30 @@ krb5_pac_verify(krb5_context context, const krb5_pac pac,
                 const krb5_keyblock *server, const krb5_keyblock *privsvr);
 
 /**
+ * Verify a PAC, possibly from a specified realm.
+ *
+ * @param [in] context          Library context
+ * @param [in] pac              PAC handle
+ * @param [in] authtime         Expected timestamp
+ * @param [in] principal        Expected principal name (or NULL)
+ * @param [in] server           Key to validate server checksum (or NULL)
+ * @param [in] privsvr          Key to validate KDC checksum (or NULL)
+ * @param [in] with_realm       If true, expect the realm of @a principal
+ *
+ * This function is similar to krb5_pac_verify(), but adds a parameter
+ * @a with_realm.  If @a with_realm is true, the PAC_CLIENT_INFO field is
+ * expected to include the realm of @a principal as well as the name.  This
+ * flag is necessary to verify PACs in cross-realm S4U2Self referral TGTs.
+ *
+ * @version New in 1.17
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_pac_verify_ext(krb5_context context, const krb5_pac pac,
+                    krb5_timestamp authtime, krb5_const_principal principal,
+                    const krb5_keyblock *server, const krb5_keyblock *privsvr,
+                    krb5_boolean with_realm);
+
+/**
  * Sign a PAC.
  *
  * @param [in]  context         Library context
@@ -8336,6 +8360,32 @@ krb5_pac_sign(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
               const krb5_keyblock *privsvr_key, krb5_data *data);
 
 /**
+ * Sign a PAC, possibly with a specified realm.
+ *
+ * @param [in]  context         Library context
+ * @param [in]  pac             PAC handle
+ * @param [in]  authtime        Expected timestamp
+ * @param [in]  principal       Principal name (or NULL)
+ * @param [in]  server_key      Key for server checksum
+ * @param [in]  privsvr_key     Key for KDC checksum
+ * @param [in]  with_realm      If true, include the realm of @a principal
+ * @param [out] data            Signed PAC encoding
+ *
+ * This function is similar to krb5_pac_sign(), but adds a parameter
+ * @a with_realm.  If @a with_realm is true, the PAC_CLIENT_INFO field of the
+ * signed PAC will include the realm of @a principal as well as the name.  This
+ * flag is necessary to generate PACs for cross-realm S4U2Self referrals.
+ *
+ * @version New in 1.17
+ */
+krb5_error_code KRB5_CALLCONV
+krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
+                  krb5_const_principal principal,
+                  const krb5_keyblock *server_key,
+                  const krb5_keyblock *privsvr_key, krb5_boolean with_realm,
+                  krb5_data *data);
+
+/**
  * Allow the appplication to override the profile's allow_weak_crypto setting.
  *
  * @param [in] context          Library context
diff --git a/src/lib/krb5/krb/authdata.h b/src/lib/krb5/krb/authdata.h
index 1e5c084..74d663c 100644
--- a/src/lib/krb5/krb/authdata.h
+++ b/src/lib/krb5/krb/authdata.h
@@ -90,7 +90,8 @@ krb5_error_code
 k5_pac_validate_client(krb5_context context,
                        const krb5_pac pac,
                        krb5_timestamp authtime,
-                       krb5_const_principal principal);
+                       krb5_const_principal principal,
+                       krb5_boolean with_realm);
 
 krb5_error_code
 k5_pac_add_buffer(krb5_context context,
diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index c9b5de3..cc74f37 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -403,7 +403,8 @@ krb5_error_code
 k5_pac_validate_client(krb5_context context,
                        const krb5_pac pac,
                        krb5_timestamp authtime,
-                       krb5_const_principal principal)
+                       krb5_const_principal principal,
+                       krb5_boolean with_realm)
 {
     krb5_error_code ret;
     krb5_data client_info;
@@ -413,7 +414,7 @@ k5_pac_validate_client(krb5_context context,
     krb5_ui_2 pac_princname_length;
     int64_t pac_nt_authtime;
     krb5_principal pac_principal;
-    int flags;
+    int flags = 0;
 
     ret = k5_pac_locate_buffer(context, pac, KRB5_PAC_CLIENT_INFO,
                                &client_info);
@@ -442,10 +443,15 @@ k5_pac_validate_client(krb5_context context,
         return ret;
 
     /* Parse the UTF-8 name as an enterprise principal if we are matching
-     * against one; otherwise parse it as a regular principal with no realm. */
-    flags = KRB5_PRINCIPAL_PARSE_NO_REALM;
+     * against one; otherwise parse it as a regular principal. */
     if (principal->type == KRB5_NT_ENTERPRISE_PRINCIPAL)
         flags |= KRB5_PRINCIPAL_PARSE_ENTERPRISE;
+
+    if (with_realm)
+        flags |= KRB5_PRINCIPAL_PARSE_REQUIRE_REALM;
+    else
+        flags |= KRB5_PRINCIPAL_PARSE_NO_REALM;
+
     ret = krb5_parse_name_flags(context, pac_princname, flags, &pac_principal);
     if (ret != 0) {
         free(pac_princname);
@@ -458,6 +464,7 @@ k5_pac_validate_client(krb5_context context,
         !krb5_principal_compare_flags(context,
                                       pac_principal,
                                       principal,
+                                      with_realm ? 0 :
                                       KRB5_PRINCIPAL_COMPARE_IGNORE_REALM))
         ret = KRB5KRB_AP_WRONG_PRINC;
 
@@ -623,6 +630,19 @@ krb5_pac_verify(krb5_context context,
                 const krb5_keyblock *server,
                 const krb5_keyblock *privsvr)
 {
+    return krb5_pac_verify_ext(context, pac, authtime, principal, server,
+                               privsvr, FALSE);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_pac_verify_ext(krb5_context context,
+                    const krb5_pac pac,
+                    krb5_timestamp authtime,
+                    krb5_const_principal principal,
+                    const krb5_keyblock *server,
+                    const krb5_keyblock *privsvr,
+                    krb5_boolean with_realm)
+{
     krb5_error_code ret;
 
     if (server != NULL) {
@@ -638,7 +658,8 @@ krb5_pac_verify(krb5_context context,
     }
 
     if (principal != NULL) {
-        ret = k5_pac_validate_client(context, pac, authtime, principal);
+        ret = k5_pac_validate_client(context, pac, authtime,
+                                     principal, with_realm);
         if (ret != 0)
             return ret;
     }
diff --git a/src/lib/krb5/krb/pac_sign.c b/src/lib/krb5/krb/pac_sign.c
index c94899c..12f0259 100644
--- a/src/lib/krb5/krb/pac_sign.c
+++ b/src/lib/krb5/krb/pac_sign.c
@@ -33,7 +33,8 @@ static krb5_error_code
 k5_insert_client_info(krb5_context context,
                       krb5_pac pac,
                       krb5_timestamp authtime,
-                      krb5_const_principal principal)
+                      krb5_const_principal principal,
+                      krb5_boolean with_realm)
 {
     krb5_error_code ret;
     krb5_data client_info;
@@ -41,16 +42,23 @@ k5_insert_client_info(krb5_context context,
     unsigned char *princ_name_utf16 = NULL, *p;
     size_t princ_name_utf16_len = 0;
     uint64_t nt_authtime;
+    int flags = 0;
 
     /* If we already have a CLIENT_INFO buffer, then just validate it */
     if (k5_pac_locate_buffer(context, pac, KRB5_PAC_CLIENT_INFO,
                              &client_info) == 0) {
-        return k5_pac_validate_client(context, pac, authtime, principal);
+        return k5_pac_validate_client(context, pac, authtime, principal,
+                                      with_realm);
     }
 
-    ret = krb5_unparse_name_flags(context, principal,
-                                  KRB5_PRINCIPAL_UNPARSE_NO_REALM,
-                                  &princ_name_utf8);
+    if (!with_realm) {
+        flags |= KRB5_PRINCIPAL_UNPARSE_NO_REALM;
+    } else if (principal->type == KRB5_NT_ENTERPRISE_PRINCIPAL) {
+        /* Avoid quoting the first @ sign for enterprise name with realm. */
+        flags |= KRB5_PRINCIPAL_UNPARSE_DISPLAY;
+    }
+
+    ret = krb5_unparse_name_flags(context, principal, flags, &princ_name_utf8);
     if (ret != 0)
         goto cleanup;
 
@@ -183,6 +191,17 @@ krb5_pac_sign(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
               krb5_const_principal principal, const krb5_keyblock *server_key,
               const krb5_keyblock *privsvr_key, krb5_data *data)
 {
+    return krb5_pac_sign_ext(context, pac, authtime, principal, server_key,
+                             privsvr_key, FALSE, data);
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_pac_sign_ext(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
+                  krb5_const_principal principal,
+                  const krb5_keyblock *server_key,
+                  const krb5_keyblock *privsvr_key, krb5_boolean with_realm,
+                  krb5_data *data)
+{
     krb5_error_code ret;
     krb5_data server_cksum, privsvr_cksum;
     krb5_cksumtype server_cksumtype, privsvr_cksumtype;
@@ -192,7 +211,8 @@ krb5_pac_sign(krb5_context context, krb5_pac pac, krb5_timestamp authtime,
     data->data = NULL;
 
     if (principal != NULL) {
-        ret = k5_insert_client_info(context, pac, authtime, principal);
+        ret = k5_insert_client_info(context, pac, authtime, principal,
+                                    with_realm);
         if (ret != 0)
             return ret;
     }
diff --git a/src/lib/krb5/krb/t_pac.c b/src/lib/krb5/krb/t_pac.c
index 61fb51a..8f9579b 100644
--- a/src/lib/krb5/krb/t_pac.c
+++ b/src/lib/krb5/krb/t_pac.c
@@ -313,6 +313,138 @@ main(int argc, char **argv)
         free(list);
     }
 
+    {
+        krb5_principal ep;
+
+        ret = krb5_parse_name_flags(context, user,
+                                    KRB5_PRINCIPAL_PARSE_ENTERPRISE, &ep);
+        if (ret)
+            err(context, ret, "krb5_parse_name_flags");
+
+        /* Try to verify as enterprise. */
+        ret = krb5_pac_verify(context, pac, authtime, ep, &member_keyblock,
+                              &kdc_keyblock);
+        if (!ret)
+            err(context, ret, "krb5_pac_verify should have failed");
+
+        ret = krb5_pac_sign(context, pac, authtime, ep, &member_keyblock,
+                            &kdc_keyblock, &data);
+        if (!ret)
+            err(context, ret, "krb5_pac_sign should have failed");
+
+        /* Try to verify with realm. */
+        ret = krb5_pac_verify_ext(context, pac, authtime, p, &member_keyblock,
+                                  &kdc_keyblock, TRUE);
+        if (!ret)
+            err(context, ret, "krb5_pac_verify_ext with realm should fail");
+
+        /* Currently we can't re-sign the PAC with realm (although that could
+         * be useful), only sign a new one. */
+        ret = krb5_pac_sign_ext(context, pac, authtime, p, &member_keyblock,
+                                &kdc_keyblock, TRUE, &data);
+        if (!ret)
+            err(context, ret, "krb5_pac_sign_ext with realm should fail");
+
+        krb5_pac_free(context, pac);
+
+        /* Test enterprise. */
+        ret = krb5_pac_init(context, &pac);
+        if (ret)
+            err(context, ret, "krb5_pac_init");
+
+        ret = krb5_pac_sign(context, pac, authtime, ep, &member_keyblock,
+                            &kdc_keyblock, &data);
+        if (ret)
+            err(context, ret, "krb5_pac_sign enterprise failed");
+
+        krb5_pac_free(context, pac);
+
+        ret = krb5_pac_parse(context, data.data, data.length, &pac);
+        krb5_free_data_contents(context, &data);
+        if (ret)
+            err(context, ret, "krb5_pac_parse failed");
+
+        ret = krb5_pac_verify(context, pac, authtime, ep, &member_keyblock,
+                              &kdc_keyblock);
+        if (ret)
+            err(context, ret, "krb5_pac_verify enterprise failed");
+
+        ret = krb5_pac_verify(context, pac, authtime, p, &member_keyblock,
+                              &kdc_keyblock);
+        if (!ret)
+            err(context, ret, "krb5_pac_verify should have failed");
+
+        krb5_pac_free(context, pac);
+
+        /* Test with realm. */
+        ret = krb5_pac_init(context, &pac);
+        if (ret)
+            err(context, ret, "krb5_pac_init");
+
+        ret = krb5_pac_sign_ext(context, pac, authtime, p, &member_keyblock,
+                                &kdc_keyblock, TRUE, &data);
+        if (ret)
+            err(context, ret, "krb5_pac_sign_ext with realm failed");
+
+        krb5_pac_free(context, pac);
+
+        ret = krb5_pac_parse(context, data.data, data.length, &pac);
+        krb5_free_data_contents(context, &data);
+        if (ret)
+            err(context, ret, "krb5_pac_parse failed");
+
+        ret = krb5_pac_verify_ext(context, pac, authtime, p, &member_keyblock,
+                                  &kdc_keyblock, TRUE);
+        if (ret)
+            err(context, ret, "krb5_pac_verify_ext with realm failed");
+
+        ret = krb5_pac_verify(context, pac, authtime, p, &member_keyblock,
+                              &kdc_keyblock);
+        if (!ret)
+            err(context, ret, "krb5_pac_verify should have failed");
+
+        krb5_pac_free(context, pac);
+
+        /* Test enterprise with realm. */
+        ret = krb5_pac_init(context, &pac);
+        if (ret)
+            err(context, ret, "krb5_pac_init");
+
+        ret = krb5_pac_sign_ext(context, pac, authtime, ep, &member_keyblock,
+                                &kdc_keyblock, TRUE, &data);
+        if (ret)
+            err(context, ret, "krb5_pac_sign_ext ent with realm failed");
+
+        krb5_pac_free(context, pac);
+
+        ret = krb5_pac_parse(context, data.data, data.length, &pac);
+        krb5_free_data_contents(context, &data);
+        if (ret)
+            err(context, ret, "krb5_pac_parse failed");
+
+        ret = krb5_pac_verify_ext(context, pac, authtime, ep, &member_keyblock,
+                                  &kdc_keyblock, TRUE);
+        if (ret)
+            err(context, ret, "krb5_pac_verify_ext ent with realm failed");
+
+        ret = krb5_pac_verify(context, pac, authtime, p, &member_keyblock,
+                              &kdc_keyblock);
+        if (!ret)
+            err(context, ret, "krb5_pac_verify should have failed");
+
+        ret = krb5_pac_verify(context, pac, authtime, ep, &member_keyblock,
+                              &kdc_keyblock);
+        if (!ret)
+            err(context, ret, "krb5_pac_verify should have failed");
+
+        ret = krb5_pac_verify_ext(context, pac, authtime, p, &member_keyblock,
+                                  &kdc_keyblock, TRUE);
+        if (!ret)
+            err(context, ret, "krb5_pac_verify_ext should have failed");
+
+        krb5_free_principal(context, ep);
+    }
+
     krb5_pac_free(context, pac);
 
     krb5_free_principal(context, p);
diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports
index 542209d..dfdb72d 100644
--- a/src/lib/krb5/libkrb5.exports
+++ b/src/lib/krb5/libkrb5.exports
@@ -487,7 +487,9 @@ krb5_pac_get_types
 krb5_pac_init
 krb5_pac_parse
 krb5_pac_sign
+krb5_pac_sign_ext
 krb5_pac_verify
+krb5_pac_verify_ext
 krb5_parse_name
 krb5_parse_name_flags
 krb5_prepend_error_message
diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def
index 3bb2a80..c350229 100644
--- a/src/lib/krb5_32.def
+++ b/src/lib/krb5_32.def
@@ -476,6 +476,8 @@ EXPORTS
 
 ; new in 1.17
 	krb5_get_etype_info				@447
+	krb5_pac_sign_ext				@448
+	krb5_pac_verify_ext				@449
 ; private symbols used by SPAKE client module
 	profile_get_string				@439 ; PRIVATE
 	profile_release_string				@440 ; PRIVATE


More information about the cvs-krb5 mailing list