252 lines
8.4 KiB
Diff
252 lines
8.4 KiB
Diff
From d68a530c66cf1200db112607d6c6bd91ad828232 Mon Sep 17 00:00:00 2001
|
|
From: Andrew Bartlett <abartlet@samba.org>
|
|
Date: Fri, 8 Oct 2021 08:29:51 +1300
|
|
Subject: [PATCH] CVE-2020-25719 kdc: Avoid races and multiple DB lookups in
|
|
s4u2self check
|
|
|
|
Looking up the DB twice is subject to a race and is a poor
|
|
use of resources, so instead just pass in the record we
|
|
already got when trying to confirm that the server in
|
|
S4U2Self is the same as the requesting client.
|
|
|
|
The client record has already been bound to the the
|
|
original client by the SID check in the PAC.
|
|
|
|
Likewise by looking up server only once we ensure
|
|
that the keys looked up originally are in the record
|
|
we confirm the SID for here.
|
|
|
|
BUG: https://bugzilla.samba.org/show_bug.cgi?id=14686
|
|
|
|
Signed-off-by: Andrew Bartlett <abartlet@samba.org>
|
|
Reviewed-by: Joseph Sutton <josephsutton@catalyst.net.nz>
|
|
---
|
|
source4/heimdal/kdc/krb5tgs.c | 26 +++++++++++------
|
|
source4/heimdal/lib/hdb/hdb.h | 2 +-
|
|
source4/kdc/db-glue.c | 54 ++++++++++++-----------------------
|
|
source4/kdc/db-glue.h | 5 ++--
|
|
source4/kdc/hdb-samba4.c | 43 ++++++++--------------------
|
|
5 files changed, 52 insertions(+), 78 deletions(-)
|
|
|
|
diff --git a/source4/heimdal/kdc/krb5tgs.c b/source4/heimdal/kdc/krb5tgs.c
|
|
index 301ca92091a..d4a1c78e153 100644
|
|
--- a/source4/heimdal/kdc/krb5tgs.c
|
|
+++ b/source4/heimdal/kdc/krb5tgs.c
|
|
@@ -313,7 +313,7 @@ check_constrained_delegation(krb5_context context,
|
|
* Determine if s4u2self is allowed from this client to this server
|
|
*
|
|
* For example, regardless of the principal being impersonated, if the
|
|
- * 'client' and 'server' are the same, then it's safe.
|
|
+ * 'client' and 'server' (target) are the same, then it's safe.
|
|
*/
|
|
|
|
static krb5_error_code
|
|
@@ -321,18 +321,28 @@ check_s4u2self(krb5_context context,
|
|
krb5_kdc_configuration *config,
|
|
HDB *clientdb,
|
|
hdb_entry_ex *client,
|
|
- krb5_const_principal server)
|
|
+ hdb_entry_ex *target_server,
|
|
+ krb5_const_principal target_server_principal)
|
|
{
|
|
krb5_error_code ret;
|
|
|
|
- /* if client does a s4u2self to itself, that ok */
|
|
- if (krb5_principal_compare(context, client->entry.principal, server) == TRUE)
|
|
- return 0;
|
|
-
|
|
+ /*
|
|
+ * Always allow the plugin to check, this might be faster, allow a
|
|
+ * policy or audit check and can look into the DB records
|
|
+ * directly
|
|
+ */
|
|
if (clientdb->hdb_check_s4u2self) {
|
|
- ret = clientdb->hdb_check_s4u2self(context, clientdb, client, server);
|
|
+ ret = clientdb->hdb_check_s4u2self(context,
|
|
+ clientdb,
|
|
+ client,
|
|
+ target_server);
|
|
if (ret == 0)
|
|
return 0;
|
|
+ } else if (krb5_principal_compare(context,
|
|
+ client->entry.principal,
|
|
+ target_server_principal) == TRUE) {
|
|
+ /* if client does a s4u2self to itself, and there is no plugin, that is ok */
|
|
+ return 0;
|
|
} else {
|
|
ret = KRB5KDC_ERR_BADOPTION;
|
|
}
|
|
@@ -1774,7 +1784,7 @@ server_lookup:
|
|
* Check that service doing the impersonating is
|
|
* requesting a ticket to it-self.
|
|
*/
|
|
- ret = check_s4u2self(context, config, clientdb, client, sp);
|
|
+ ret = check_s4u2self(context, config, clientdb, client, server, sp);
|
|
if (ret) {
|
|
kdc_log(context, config, 0, "S4U2Self: %s is not allowed "
|
|
"to impersonate to service "
|
|
diff --git a/source4/heimdal/lib/hdb/hdb.h b/source4/heimdal/lib/hdb/hdb.h
|
|
index 6a09ecb6fe1..5ef9d9565f3 100644
|
|
--- a/source4/heimdal/lib/hdb/hdb.h
|
|
+++ b/source4/heimdal/lib/hdb/hdb.h
|
|
@@ -266,7 +266,7 @@ typedef struct HDB{
|
|
/**
|
|
* Check if s4u2self is allowed from this client to this server
|
|
*/
|
|
- krb5_error_code (*hdb_check_s4u2self)(krb5_context, struct HDB *, hdb_entry_ex *, krb5_const_principal);
|
|
+ krb5_error_code (*hdb_check_s4u2self)(krb5_context, struct HDB *, hdb_entry_ex *, hdb_entry_ex *);
|
|
}HDB;
|
|
|
|
#define HDB_INTERFACE_VERSION 7
|
|
diff --git a/source4/kdc/db-glue.c b/source4/kdc/db-glue.c
|
|
index a560a1cd84b..8fe4f1ea3e9 100644
|
|
--- a/source4/kdc/db-glue.c
|
|
+++ b/source4/kdc/db-glue.c
|
|
@@ -2510,53 +2510,37 @@ krb5_error_code samba_kdc_nextkey(krb5_context context,
|
|
|
|
/* Check if a given entry may delegate or do s4u2self to this target principal
|
|
*
|
|
- * This is currently a very nasty hack - allowing only delegation to itself.
|
|
+ * The safest way to determine 'self' is to check the DB record made at
|
|
+ * the time the principal was presented to the KDC.
|
|
*/
|
|
krb5_error_code
|
|
samba_kdc_check_s4u2self(krb5_context context,
|
|
- struct samba_kdc_db_context *kdc_db_ctx,
|
|
- struct samba_kdc_entry *skdc_entry,
|
|
- krb5_const_principal target_principal)
|
|
+ struct samba_kdc_entry *skdc_entry_client,
|
|
+ struct samba_kdc_entry *skdc_entry_server_target)
|
|
{
|
|
- krb5_error_code ret;
|
|
- struct ldb_dn *realm_dn;
|
|
- struct ldb_message *msg;
|
|
struct dom_sid *orig_sid;
|
|
struct dom_sid *target_sid;
|
|
- const char *delegation_check_attrs[] = {
|
|
- "objectSid", NULL
|
|
- };
|
|
-
|
|
- TALLOC_CTX *mem_ctx = talloc_named(kdc_db_ctx, 0, "samba_kdc_check_s4u2self");
|
|
-
|
|
- if (!mem_ctx) {
|
|
- ret = ENOMEM;
|
|
- krb5_set_error_message(context, ret, "samba_kdc_check_s4u2self: talloc_named() failed!");
|
|
- return ret;
|
|
- }
|
|
-
|
|
- ret = samba_kdc_lookup_server(context, kdc_db_ctx, mem_ctx, target_principal,
|
|
- SDB_F_GET_CLIENT|SDB_F_GET_SERVER,
|
|
- delegation_check_attrs, &realm_dn, &msg);
|
|
-
|
|
- if (ret != 0) {
|
|
- talloc_free(mem_ctx);
|
|
- return ret;
|
|
- }
|
|
+ TALLOC_CTX *frame = talloc_stackframe();
|
|
|
|
- orig_sid = samdb_result_dom_sid(mem_ctx, skdc_entry->msg, "objectSid");
|
|
- target_sid = samdb_result_dom_sid(mem_ctx, msg, "objectSid");
|
|
+ orig_sid = samdb_result_dom_sid(frame,
|
|
+ skdc_entry_client->msg,
|
|
+ "objectSid");
|
|
+ target_sid = samdb_result_dom_sid(frame,
|
|
+ skdc_entry_server_target->msg,
|
|
+ "objectSid");
|
|
|
|
- /* Allow delegation to the same principal, even if by a different
|
|
- * name. The easy and safe way to prove this is by SID
|
|
- * comparison */
|
|
+ /*
|
|
+ * Allow delegation to the same record (representing a
|
|
+ * principal), even if by a different name. The easy and safe
|
|
+ * way to prove this is by SID comparison
|
|
+ */
|
|
if (!(orig_sid && target_sid && dom_sid_equal(orig_sid, target_sid))) {
|
|
- talloc_free(mem_ctx);
|
|
+ talloc_free(frame);
|
|
return KRB5KDC_ERR_BADOPTION;
|
|
}
|
|
|
|
- talloc_free(mem_ctx);
|
|
- return ret;
|
|
+ talloc_free(frame);
|
|
+ return 0;
|
|
}
|
|
|
|
/* Certificates printed by a the Certificate Authority might have a
|
|
diff --git a/source4/kdc/db-glue.h b/source4/kdc/db-glue.h
|
|
index aa630f5d349..cadfac1deb8 100644
|
|
--- a/source4/kdc/db-glue.h
|
|
+++ b/source4/kdc/db-glue.h
|
|
@@ -40,9 +40,8 @@ krb5_error_code samba_kdc_nextkey(krb5_context context,
|
|
|
|
krb5_error_code
|
|
samba_kdc_check_s4u2self(krb5_context context,
|
|
- struct samba_kdc_db_context *kdc_db_ctx,
|
|
- struct samba_kdc_entry *skdc_entry,
|
|
- krb5_const_principal target_principal);
|
|
+ struct samba_kdc_entry *skdc_entry_client,
|
|
+ struct samba_kdc_entry *skdc_entry_server_target);
|
|
|
|
krb5_error_code
|
|
samba_kdc_check_pkinit_ms_upn_match(krb5_context context,
|
|
diff --git a/source4/kdc/hdb-samba4.c b/source4/kdc/hdb-samba4.c
|
|
index 38ce9807c02..f0939193ad7 100644
|
|
--- a/source4/kdc/hdb-samba4.c
|
|
+++ b/source4/kdc/hdb-samba4.c
|
|
@@ -274,38 +274,19 @@ hdb_samba4_check_pkinit_ms_upn_match(krb5_context context, HDB *db,
|
|
|
|
static krb5_error_code
|
|
hdb_samba4_check_s4u2self(krb5_context context, HDB *db,
|
|
- hdb_entry_ex *entry,
|
|
- krb5_const_principal target_principal)
|
|
+ hdb_entry_ex *client_entry,
|
|
+ hdb_entry_ex *server_target_entry)
|
|
{
|
|
- struct samba_kdc_db_context *kdc_db_ctx;
|
|
- struct samba_kdc_entry *skdc_entry;
|
|
- krb5_error_code ret;
|
|
-
|
|
- kdc_db_ctx = talloc_get_type_abort(db->hdb_db,
|
|
- struct samba_kdc_db_context);
|
|
- skdc_entry = talloc_get_type_abort(entry->ctx,
|
|
- struct samba_kdc_entry);
|
|
-
|
|
- ret = samba_kdc_check_s4u2self(context, kdc_db_ctx,
|
|
- skdc_entry,
|
|
- target_principal);
|
|
- switch (ret) {
|
|
- case 0:
|
|
- break;
|
|
- case SDB_ERR_WRONG_REALM:
|
|
- ret = HDB_ERR_WRONG_REALM;
|
|
- break;
|
|
- case SDB_ERR_NOENTRY:
|
|
- ret = HDB_ERR_NOENTRY;
|
|
- break;
|
|
- case SDB_ERR_NOT_FOUND_HERE:
|
|
- ret = HDB_ERR_NOT_FOUND_HERE;
|
|
- break;
|
|
- default:
|
|
- break;
|
|
- }
|
|
-
|
|
- return ret;
|
|
+ struct samba_kdc_entry *skdc_client_entry
|
|
+ = talloc_get_type_abort(client_entry->ctx,
|
|
+ struct samba_kdc_entry);
|
|
+ struct samba_kdc_entry *skdc_server_target_entry
|
|
+ = talloc_get_type_abort(server_target_entry->ctx,
|
|
+ struct samba_kdc_entry);
|
|
+
|
|
+ return samba_kdc_check_s4u2self(context,
|
|
+ skdc_client_entry,
|
|
+ skdc_server_target_entry);
|
|
}
|
|
|
|
static void reset_bad_password_netlogon(TALLOC_CTX *mem_ctx,
|
|
--
|
|
2.33.0
|
|
|