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