From c0395578c50fbc4f1946e2f5a065d94f67212eb0 Mon Sep 17 00:00:00 2001
From: Joseph Sutton <josephsutton@catalyst.net.nz>
Date: Wed, 15 Jun 2022 19:37:39 +1200
Subject: [PATCH 55/99] CVE-2022-2031 s4:kdc: Add MIT support for
 ATTRIBUTES_INFO and REQUESTER_SID PAC buffers

So that we do not confuse TGTs and kpasswd tickets, it is critical to
check that the REQUESTER_SID buffer exists in TGTs, and to ensure that
it is not propagated to service tickets.

BUG: https://bugzilla.samba.org/show_bug.cgi?id=15047

Signed-off-by: Joseph Sutton <josephsutton@catalyst.net.nz>

[jsutton@samba.org Brought in changes to add ATTRIBUTES_INFO and
 REQUESTER_SID buffers to new PACs, and updated knownfails]

[jsutton@samba.org Adjusted MIT knownfails]

Conflict: remove selftest/knownfail_mit_kdc

---
 source4/kdc/mit-kdb/kdb_samba_policies.c |  5 +-
 source4/kdc/mit_samba.c                  | 93 +++++++++++++++++++++++-
 source4/kdc/mit_samba.h                  |  1 +
 4 files changed, 94 insertions(+), 22 deletions(-)

diff --git a/source4/kdc/mit-kdb/kdb_samba_policies.c b/source4/kdc/mit-kdb/kdb_samba_policies.c
index 7bc9a7b3347..3b25fff410b 100644
--- a/source4/kdc/mit-kdb/kdb_samba_policies.c
+++ b/source4/kdc/mit-kdb/kdb_samba_policies.c
@@ -159,6 +159,7 @@ done:
 
 static krb5_error_code ks_get_pac(krb5_context context,
 				  krb5_db_entry *client,
+				  krb5_db_entry *server,
 				  krb5_keyblock *client_key,
 				  krb5_pac *pac)
 {
@@ -173,6 +174,7 @@ static krb5_error_code ks_get_pac(krb5_context context,
 	code = mit_samba_get_pac(mit_ctx,
 				 context,
 				 client,
+				 server,
 				 client_key,
 				 pac);
 	if (code != 0) {
@@ -439,7 +441,7 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
 	 */
 	if (with_pac && generate_pac) {
 		DBG_DEBUG("Generate PAC for AS-REQ [%s]\n", client_name);
-		code = ks_get_pac(context, client_entry, client_key, &pac);
+		code = ks_get_pac(context, client_entry, server, client_key, &pac);
 		if (code != 0) {
 			goto done;
 		}
@@ -490,6 +492,7 @@ krb5_error_code kdb_samba_db_sign_auth_data(krb5_context context,
 
 				code = ks_get_pac(context,
 						  client_entry,
+						  server,
 						  client_key,
 						  &pac);
 				if (code != 0 && code != ENOENT) {
diff --git a/source4/kdc/mit_samba.c b/source4/kdc/mit_samba.c
index c2a604045d9..df2ba0a906f 100644
--- a/source4/kdc/mit_samba.c
+++ b/source4/kdc/mit_samba.c
@@ -407,6 +407,7 @@ int mit_samba_get_nextkey(struct mit_samba_context *ctx,
 int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
 		      krb5_context context,
 		      krb5_db_entry *client,
+		      krb5_db_entry *server,
 		      krb5_keyblock *client_key,
 		      krb5_pac *pac)
 {
@@ -417,9 +418,12 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
 	DATA_BLOB **cred_ndr_ptr = NULL;
 	DATA_BLOB cred_blob = data_blob_null;
 	DATA_BLOB *pcred_blob = NULL;
+	DATA_BLOB *pac_attrs_blob = NULL;
+	DATA_BLOB *requester_sid_blob = NULL;
 	NTSTATUS nt_status;
 	krb5_error_code code;
 	struct samba_kdc_entry *skdc_entry;
+	bool is_krbtgt;
 
 	skdc_entry = talloc_get_type_abort(client->e_data,
 					   struct samba_kdc_entry);
@@ -438,12 +442,16 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
 	}
 #endif
 
+	is_krbtgt = ks_is_tgs_principal(smb_ctx, server->princ);
+
 	nt_status = samba_kdc_get_pac_blobs(tmp_ctx,
 					    skdc_entry,
 					    &logon_info_blob,
 					    cred_ndr_ptr,
 					    &upn_dns_info_blob,
-					    NULL, NULL, NULL,
+					    is_krbtgt ? &pac_attrs_blob : NULL,
+					    NULL,
+					    is_krbtgt ? &requester_sid_blob : NULL,
 					    NULL);
 	if (!NT_STATUS_IS_OK(nt_status)) {
 		talloc_free(tmp_ctx);
@@ -471,8 +479,8 @@ int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
 				   logon_info_blob,
 				   pcred_blob,
 				   upn_dns_info_blob,
-				   NULL,
-				   NULL,
+				   pac_attrs_blob,
+				   requester_sid_blob,
 				   NULL,
 				   pac);
 
@@ -496,6 +504,7 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
 	DATA_BLOB *pac_blob = NULL;
 	DATA_BLOB *upn_blob = NULL;
 	DATA_BLOB *deleg_blob = NULL;
+	DATA_BLOB *requester_sid_blob = NULL;
 	struct samba_kdc_entry *client_skdc_entry = NULL;
 	struct samba_kdc_entry *krbtgt_skdc_entry = NULL;
 	struct samba_kdc_entry *server_skdc_entry = NULL;
@@ -511,8 +520,12 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
 	ssize_t upn_dns_info_idx = -1;
 	ssize_t srv_checksum_idx = -1;
 	ssize_t kdc_checksum_idx = -1;
+	ssize_t tkt_checksum_idx = -1;
+	ssize_t attrs_info_idx = -1;
+	ssize_t requester_sid_idx = -1;
 	krb5_pac new_pac = NULL;
 	bool ok;
+	bool is_krbtgt;
 
 	/* Create a memory context early so code can use talloc_stackframe() */
 	tmp_ctx = talloc_named(ctx, 0, "mit_samba_reget_pac context");
@@ -520,6 +533,8 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
 		return ENOMEM;
 	}
 
+	is_krbtgt = ks_is_tgs_principal(ctx, server->princ);
+
 	if (client != NULL) {
 		client_skdc_entry =
 			talloc_get_type_abort(client->e_data,
@@ -578,7 +593,7 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
 						    NULL,
 						    &upn_blob,
 						    NULL, NULL,
-						    NULL,
+						    &requester_sid_blob,
 						    NULL);
 		if (!NT_STATUS_IS_OK(nt_status)) {
 			code = EINVAL;
@@ -737,6 +752,45 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
 			}
 			kdc_checksum_idx = i;
 			break;
+		case PAC_TYPE_TICKET_CHECKSUM:
+			if (tkt_checksum_idx != -1) {
+				DBG_WARNING("ticket checksum type[%u] twice "
+					    "[%zd] and [%zu]: \n",
+					    types[i],
+					    tkt_checksum_idx,
+					    i);
+				SAFE_FREE(types);
+				code = EINVAL;
+				goto done;
+			}
+			tkt_checksum_idx = i;
+			break;
+		case PAC_TYPE_ATTRIBUTES_INFO:
+			if (attrs_info_idx != -1) {
+				DBG_WARNING("attributes info type[%u] twice "
+					    "[%zd] and [%zu]: \n",
+					    types[i],
+					    attrs_info_idx,
+					    i);
+				SAFE_FREE(types);
+				code = EINVAL;
+				goto done;
+			}
+			attrs_info_idx = i;
+			break;
+		case PAC_TYPE_REQUESTER_SID:
+			if (requester_sid_idx != -1) {
+				DBG_WARNING("requester sid type[%u] twice"
+					    "[%zd] and [%zu]: \n",
+					    types[i],
+					    requester_sid_idx,
+					    i);
+				SAFE_FREE(types);
+				code = EINVAL;
+				goto done;
+			}
+			requester_sid_idx = i;
+			break;
 		default:
 			continue;
 		}
@@ -766,6 +820,13 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
 		code = EINVAL;
 		goto done;
 	}
+	if (!(flags & KRB5_KDB_FLAG_CONSTRAINED_DELEGATION) &&
+	    requester_sid_idx == -1) {
+		DEBUG(1, ("PAC_TYPE_REQUESTER_SID missing\n"));
+		SAFE_FREE(types);
+		code = KRB5KDC_ERR_TGT_REVOKED;
+		goto done;
+	}
 
 	/* Build an updated PAC */
 	code = krb5_pac_init(context, &new_pac);
@@ -831,6 +892,10 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
 			}
 			break;
 		case PAC_TYPE_SRV_CHECKSUM:
+			if (requester_sid_idx == -1 && requester_sid_blob != NULL) {
+				/* inject REQUESTER_SID */
+				forced_next_type = PAC_TYPE_REQUESTER_SID;
+			}
 			/*
 			 * This is generated in the main KDC code
 			 */
@@ -840,6 +905,26 @@ krb5_error_code mit_samba_reget_pac(struct mit_samba_context *ctx,
 			 * This is generated in the main KDC code
 			 */
 			continue;
+		case PAC_TYPE_ATTRIBUTES_INFO:
+			if (!is_untrusted && is_krbtgt) {
+				/* just copy... */
+				break;
+			}
+
+			continue;
+		case PAC_TYPE_REQUESTER_SID:
+			if (!is_krbtgt) {
+				continue;
+			}
+
+			/*
+			 * Replace in the RODC case, otherwise
+			 * requester_sid_blob is NULL and we just copy.
+			 */
+			if (requester_sid_blob != NULL) {
+				type_blob = *requester_sid_blob;
+			}
+			break;
 		default:
 			/* just copy... */
 			break;
diff --git a/source4/kdc/mit_samba.h b/source4/kdc/mit_samba.h
index 636c77ec97c..4431e82a1b2 100644
--- a/source4/kdc/mit_samba.h
+++ b/source4/kdc/mit_samba.h
@@ -50,6 +50,7 @@ int mit_samba_get_nextkey(struct mit_samba_context *ctx,
 int mit_samba_get_pac(struct mit_samba_context *smb_ctx,
 		      krb5_context context,
 		      krb5_db_entry *client,
+		      krb5_db_entry *server,
 		      krb5_keyblock *client_key,
 		      krb5_pac *pac);
 
-- 
2.25.1
