]> git.ipfire.org Git - thirdparty/krb5.git/commitdiff
Add PAC APIs which can include a client realm
authorIsaac Boukris <iboukris@gmail.com>
Sat, 29 Sep 2018 04:21:56 +0000 (07:21 +0300)
committerGreg Hudson <ghudson@mit.edu>
Thu, 11 Oct 2018 15:02:03 +0000 (11:02 -0400)
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@mit.edu: added documentation; changed names and parameter
order; edited commit message]

ticket: 8749 (new)

doc/appdev/refs/api/index.rst
src/include/krb5/krb5.hin
src/lib/krb5/krb/authdata.h
src/lib/krb5/krb/pac.c
src/lib/krb5/krb/pac_sign.c
src/lib/krb5/krb/t_pac.c
src/lib/krb5/libkrb5.exports
src/lib/krb5_32.def

index 66aff5951eac7ac7487e371b27ae4ba37a8a0726..f8a5aa5e67977e2d4a464620763c794826a69f82 100644 (file)
@@ -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
index 21fabb48c0b88409e13867ffd3ffa08d290019b1..c40a6cca86fa53c680d21622108ae232f808125f 100644 (file)
@@ -8312,6 +8312,30 @@ krb5_pac_verify(krb5_context context, const krb5_pac pac,
                 krb5_timestamp authtime, krb5_const_principal principal,
                 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.
  *
@@ -8335,6 +8359,32 @@ 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);
 
+/**
+ * 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.
  *
index 1e5c08426b8c488c6e36a88cb3e3de614214df31..74d663c0c56b083da919988c7009ec8571ccf7a3 100644 (file)
@@ -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,
index c9b5de30a29813cf30405286812b807cb2925407..cc74f378b0b2a4169c5061b7c1fe245b7fffc39f 100644 (file)
@@ -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;
 
@@ -622,6 +629,19 @@ krb5_pac_verify(krb5_context context,
                 krb5_const_principal principal,
                 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;
 
@@ -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;
     }
index c94899c96a79442074ff9e062a2602599d934d34..12f0259b4f0af813c31b0cf7a09bb350158cc032 100644 (file)
@@ -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;
 
@@ -182,6 +190,17 @@ krb5_error_code KRB5_CALLCONV
 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;
@@ -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;
     }
index 61fb51a98a7f6fb7f3d3d2b0a5e8b0b1f467e7cd..8f9579ba60f419bdc9bfb2b1a68299d369bc0ec4 100644 (file)
@@ -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);
index 542209d9d174ca309959b7bb32de3ed03f80490d..dfdb72dafcf25093e290bfb4d9272d7d09e7549d 100644 (file)
@@ -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
index 3bb2a80f8d3c465fd6414ef981524b55a50da7f7..c350229317e8d7fa3fb6f786b9e32b40e59e8252 100644 (file)
@@ -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