Compare commits

...

4 Commits

Author SHA1 Message Date
d29ac3a0b1 import UBI krb5-1.18.2-29.el8_10 2024-08-13 19:19:58 +00:00
d760d9c2dc import UBI krb5-1.18.2-28.el8_10 2024-07-02 19:43:58 +00:00
81bd838f78 import UBI krb5-1.18.2-27.el8_10 2024-05-22 14:50:30 +00:00
eabdullin
72f8fad7b9 import UBI krb5-1.18.2-26.el8_9 2023-11-14 23:10:42 +00:00
14 changed files with 1529 additions and 22 deletions

View File

@ -1,4 +1,4 @@
From 1159d1e6057b6bc5b2a83bd6c8fc9f5fe38816d8 Mon Sep 17 00:00:00 2001
From bf3e55bcd66c5d35fddadc94fd680bdd57508bce Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 22 Dec 2022 03:05:23 -0500
Subject: [PATCH] Add PAC full checksums
@ -669,5 +669,5 @@ index 4fbdbec052..b0666c3b81 100644
# Get another S4U2Proxy ticket including request-authdata.
realm.run(['./s4u2proxy', usercache, 'service/2', '-2', 'proxy_ad'])
--
2.39.1
2.39.2

View File

@ -1,4 +1,4 @@
From d9ba43315f4a3e0fe2af9acbe47413ccb1f69af2 Mon Sep 17 00:00:00 2001
From 48be25aaa27487fcbbba76044083de37211b30e7 Mon Sep 17 00:00:00 2001
From: Isaac Boukris <iboukris@gmail.com>
Date: Fri, 7 Jan 2022 13:46:24 -0500
Subject: [PATCH] Add PAC ticket signature APIs
@ -836,5 +836,5 @@ index 38d371cb86..7d033acae4 100644
static krb5_error_code
--
2.39.1
2.39.2

View File

@ -0,0 +1,169 @@
From 5c2f409c360560c8b99926d6cf1a80419e758b22 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 7 Mar 2023 00:19:33 -0500
Subject: [PATCH] Add a simple DER support header
(cherry picked from commit 548da160b52b25a106e9f6077d6a42c2c049586c)
---
src/include/k5-der.h | 149 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 149 insertions(+)
create mode 100644 src/include/k5-der.h
diff --git a/src/include/k5-der.h b/src/include/k5-der.h
new file mode 100644
index 0000000000..b8371d9b4d
--- /dev/null
+++ b/src/include/k5-der.h
@@ -0,0 +1,149 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* include/k5-der.h - Distinguished Encoding Rules (DER) declarations */
+/*
+ * Copyright (C) 2023 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Most ASN.1 encoding and decoding is done using the table-driven framework in
+ * libkrb5. When that is not an option, these helpers can be used to encode
+ * and decode simple types.
+ */
+
+#ifndef K5_DER_H
+#define K5_DER_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include "k5-buf.h"
+#include "k5-input.h"
+
+/* Return the number of bytes needed to encode len as a DER encoding length. */
+static inline size_t
+k5_der_len_len(size_t len)
+{
+ size_t llen;
+
+ if (len < 128)
+ return 1;
+ llen = 1;
+ while (len > 0) {
+ len >>= 8;
+ llen++;
+ }
+ return llen;
+}
+
+/* Return the number of bytes needed to encode a DER value (with identifier
+ * byte and length) for a given contents length. */
+static inline size_t
+k5_der_value_len(size_t contents_len)
+{
+ return 1 + k5_der_len_len(contents_len) + contents_len;
+}
+
+/* Add a DER identifier byte (composed by the caller, including the ASN.1
+ * class, tag, and constructed bit) and length. */
+static inline void
+k5_der_add_taglen(struct k5buf *buf, uint8_t idbyte, size_t len)
+{
+ uint8_t *p;
+ size_t llen = k5_der_len_len(len);
+
+ p = k5_buf_get_space(buf, 1 + llen);
+ if (p == NULL)
+ return;
+ *p++ = idbyte;
+ if (len < 128) {
+ *p = len;
+ } else {
+ *p = 0x80 | (llen - 1);
+ /* Encode the length bytes backwards so the most significant byte is
+ * first. */
+ p += llen;
+ while (len > 0) {
+ *--p = len & 0xFF;
+ len >>= 8;
+ }
+ }
+}
+
+/* Add a DER value (identifier byte, length, and contents). */
+static inline void
+k5_der_add_value(struct k5buf *buf, uint8_t idbyte, const void *contents,
+ size_t len)
+{
+ k5_der_add_taglen(buf, idbyte, len);
+ k5_buf_add_len(buf, contents, len);
+}
+
+/*
+ * If the next byte in in matches idbyte and the subsequent DER length is
+ * valid, advance in past the value, set *contents_out to the value contents,
+ * and return true. Otherwise return false. Only set an error on in if the
+ * next bytes matches idbyte but the ensuing length is invalid. contents_out
+ * may be aliased to in; it will only be written to on successful decoding of a
+ * value.
+ */
+static inline bool
+k5_der_get_value(struct k5input *in, uint8_t idbyte,
+ struct k5input *contents_out)
+{
+ uint8_t lenbyte, i;
+ size_t len;
+ const void *bytes;
+
+ /* Do nothing if in is empty or the next byte doesn't match idbyte. */
+ if (in->status || in->len == 0 || *in->ptr != idbyte)
+ return false;
+
+ /* Advance past the identifier byte and decode the length. */
+ (void)k5_input_get_byte(in);
+ lenbyte = k5_input_get_byte(in);
+ if (lenbyte < 128) {
+ len = lenbyte;
+ } else {
+ len = 0;
+ for (i = 0; i < (lenbyte & 0x7F); i++) {
+ if (len > (SIZE_MAX >> 8)) {
+ k5_input_set_status(in, EOVERFLOW);
+ return false;
+ }
+ len = (len << 8) | k5_input_get_byte(in);
+ }
+ }
+
+ bytes = k5_input_get_bytes(in, len);
+ if (bytes == NULL)
+ return false;
+ k5_input_init(contents_out, bytes, len);
+ return true;
+}
+
+#endif /* K5_DER_H */
--
2.45.1

View File

@ -0,0 +1,226 @@
From 433dd85aaf8d9ed0e923c873f107995232b94422 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 26 Oct 2023 14:20:34 -0400
Subject: [PATCH] Add request_timeout configuration parameter
Add a parameter to limit the total amount of time taken for a KDC or
password change request.
ticket: 9106 (new)
(cherry picked from commit 802318cda963456b3ed7856c836e89da891483be)
---
doc/admin/conf_files/krb5_conf.rst | 9 ++++++
src/include/k5-int.h | 2 ++
src/lib/krb5/krb/init_ctx.c | 14 +++++++-
src/lib/krb5/os/sendto_kdc.c | 51 ++++++++++++++++++++----------
4 files changed, 58 insertions(+), 18 deletions(-)
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
index 315253e378..557094f6a2 100644
--- a/doc/admin/conf_files/krb5_conf.rst
+++ b/doc/admin/conf_files/krb5_conf.rst
@@ -357,6 +357,15 @@ The libdefaults section may contain any of the following relations:
(:ref:`duration` string.) Sets the default renewable lifetime
for initial ticket requests. The default value is 0.
+**request_timeout**
+ (:ref:`duration` string.) Sets the maximum total time for KDC or
+ password change requests. This timeout does not affect the
+ intervals between requests, so setting a low timeout may result in
+ fewer requests being attempted and/or some servers not being
+ contacted. A value of 0 indicates no specific maximum, in which
+ case requests will time out if no server responds after several
+ tries. The default value is 0. (New in release 1.22.)
+
**spake_preauth_groups**
A whitespace or comma-separated list of words which specifies the
groups allowed for SPAKE preauthentication. The possible values
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 912aaedac4..9d5e41ca2c 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -293,6 +293,7 @@ typedef unsigned char u_char;
#define KRB5_CONF_SPAKE_PREAUTH_INDICATOR "spake_preauth_indicator"
#define KRB5_CONF_SPAKE_PREAUTH_KDC_CHALLENGE "spake_preauth_kdc_challenge"
#define KRB5_CONF_SPAKE_PREAUTH_GROUPS "spake_preauth_groups"
+#define KRB5_CONF_REQUEST_TIMEOUT "request_timeout"
#define KRB5_CONF_TICKET_LIFETIME "ticket_lifetime"
#define KRB5_CONF_UDP_PREFERENCE_LIMIT "udp_preference_limit"
#define KRB5_CONF_UNLOCKITER "unlockiter"
@@ -1218,6 +1219,7 @@ struct _krb5_context {
kdb5_dal_handle *dal_handle;
/* allowable clock skew */
krb5_deltat clockskew;
+ krb5_deltat req_timeout;
krb5_flags kdc_default_options;
krb5_flags library_options;
krb5_boolean profile_secure;
diff --git a/src/lib/krb5/krb/init_ctx.c b/src/lib/krb5/krb/init_ctx.c
index 9a4741fa64..1a6e0bf672 100644
--- a/src/lib/krb5/krb/init_ctx.c
+++ b/src/lib/krb5/krb/init_ctx.c
@@ -163,7 +163,7 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags,
} seed_data;
krb5_data seed;
int tmp;
- char *plugin_dir = NULL;
+ char *plugin_dir = NULL, *timeout_str = NULL;
/* Verify some assumptions. If the assumptions hold and the
compiler is optimizing, this should result in no code being
@@ -257,6 +257,17 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags,
get_integer(ctx, KRB5_CONF_CLOCKSKEW, DEFAULT_CLOCKSKEW, &tmp);
ctx->clockskew = tmp;
+ retval = profile_get_string(ctx->profile, KRB5_CONF_LIBDEFAULTS,
+ KRB5_CONF_REQUEST_TIMEOUT, NULL, NULL,
+ &timeout_str);
+ if (retval)
+ goto cleanup;
+ if (timeout_str != NULL) {
+ retval = krb5_string_to_deltat(timeout_str, &ctx->req_timeout);
+ if (retval)
+ goto cleanup;
+ }
+
get_integer(ctx, KRB5_CONF_KDC_DEFAULT_OPTIONS, KDC_OPT_RENEWABLE_OK,
&tmp);
ctx->kdc_default_options = tmp;
@@ -298,6 +309,7 @@ krb5_init_context_profile(profile_t profile, krb5_flags flags,
cleanup:
profile_release_string(plugin_dir);
+ profile_release_string(timeout_str);
krb5_free_context(ctx);
return retval;
}
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index 8e4fcd2a38..f57117126e 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -1390,34 +1390,41 @@ get_endtime(time_ms endtime, struct conn_state *conns)
static krb5_boolean
service_fds(krb5_context context, struct select_state *selstate,
- time_ms interval, struct conn_state *conns,
+ time_ms interval, time_ms timeout, struct conn_state *conns,
struct select_state *seltemp, const krb5_data *realm,
int (*msg_handler)(krb5_context, const krb5_data *, void *),
void *msg_handler_data, struct conn_state **winner_out)
{
int e, selret = 0;
- time_ms endtime;
+ time_ms curtime, interval_end, endtime;
struct conn_state *state;
*winner_out = NULL;
- e = get_curtime_ms(&endtime);
+ e = get_curtime_ms(&curtime);
if (e)
return TRUE;
- endtime += interval;
+ interval_end = curtime + interval;
e = 0;
while (selstate->nfds > 0) {
- e = cm_select_or_poll(selstate, get_endtime(endtime, conns),
- seltemp, &selret);
+ endtime = get_endtime(interval_end, conns);
+ /* Don't wait longer than the whole request should last. */
+ if (timeout && endtime > timeout)
+ endtime = timeout;
+ e = cm_select_or_poll(selstate, endtime, seltemp, &selret);
if (e == EINTR)
continue;
if (e != 0)
break;
- if (selret == 0)
- /* Timeout, return to caller. */
+ if (selret == 0) {
+ /* We timed out. Stop if we hit the overall request timeout. */
+ if (timeout && (get_curtime_ms(&curtime) || curtime >= timeout))
+ return TRUE;
+ /* Otherwise return to the caller to send the next request. */
return FALSE;
+ }
/* Got something on a socket, process it. */
for (state = conns; state != NULL; state = state->next) {
@@ -1490,7 +1497,7 @@ k5_sendto(krb5_context context, const krb5_data *message,
void *msg_handler_data)
{
int pass;
- time_ms delay;
+ time_ms delay, timeout = 0;
krb5_error_code retval;
struct conn_state *conns = NULL, *state, **tailptr, *next, *winner;
size_t s;
@@ -1500,6 +1507,13 @@ k5_sendto(krb5_context context, const krb5_data *message,
*reply = empty_data();
+ if (context->req_timeout) {
+ retval = get_curtime_ms(&timeout);
+ if (retval)
+ return retval;
+ timeout += 1000 * context->req_timeout;
+ }
+
/* One for use here, listing all our fds in use, and one for
* temporary use in service_fds, for the fds of interest. */
sel_state = malloc(2 * sizeof(*sel_state));
@@ -1527,8 +1541,9 @@ k5_sendto(krb5_context context, const krb5_data *message,
if (maybe_send(context, state, message, sel_state, realm,
callback_info))
continue;
- done = service_fds(context, sel_state, 1000, conns, seltemp,
- realm, msg_handler, msg_handler_data, &winner);
+ done = service_fds(context, sel_state, 1000, timeout, conns,
+ seltemp, realm, msg_handler, msg_handler_data,
+ &winner);
}
}
@@ -1540,13 +1555,13 @@ k5_sendto(krb5_context context, const krb5_data *message,
if (maybe_send(context, state, message, sel_state, realm,
callback_info))
continue;
- done = service_fds(context, sel_state, 1000, conns, seltemp,
+ done = service_fds(context, sel_state, 1000, timeout, conns, seltemp,
realm, msg_handler, msg_handler_data, &winner);
}
/* Wait for two seconds at the end of the first pass. */
if (!done) {
- done = service_fds(context, sel_state, 2000, conns, seltemp,
+ done = service_fds(context, sel_state, 2000, timeout, conns, seltemp,
realm, msg_handler, msg_handler_data, &winner);
}
@@ -1557,15 +1572,17 @@ k5_sendto(krb5_context context, const krb5_data *message,
if (maybe_send(context, state, message, sel_state, realm,
callback_info))
continue;
- done = service_fds(context, sel_state, 1000, conns, seltemp,
- realm, msg_handler, msg_handler_data, &winner);
+ done = service_fds(context, sel_state, 1000, timeout, conns,
+ seltemp, realm, msg_handler, msg_handler_data,
+ &winner);
if (sel_state->nfds == 0)
break;
}
/* Wait for the delay backoff at the end of this pass. */
if (!done) {
- done = service_fds(context, sel_state, delay, conns, seltemp,
- realm, msg_handler, msg_handler_data, &winner);
+ done = service_fds(context, sel_state, delay, timeout, conns,
+ seltemp, realm, msg_handler, msg_handler_data,
+ &winner);
}
if (sel_state->nfds == 0)
break;
--
2.44.0

View File

@ -0,0 +1,34 @@
From a68fba22588cc21dcd1dc28550529187dca58331 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 27 Oct 2023 00:44:53 -0400
Subject: [PATCH] End connection on KDC_ERR_SVC_UNAVAILABLE
In sendto_kdc.c:service_fds(), if a message handler indicates that a
message should be discarded, kill the connection so we don't continue
waiting on it for more data.
ticket: 7899
(cherry picked from commit ca80f64c786341d5871ae1de18142e62af64f7b9)
---
src/lib/krb5/os/sendto_kdc.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index d76e24ccf0..8e4fcd2a38 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -1435,7 +1435,10 @@ service_fds(krb5_context context, struct select_state *selstate,
if (msg_handler != NULL) {
krb5_data reply = make_data(state->in.buf, state->in.pos);
- stop = (msg_handler(context, &reply, msg_handler_data) != 0);
+ if (!msg_handler(context, &reply, msg_handler_data)) {
+ kill_conn(context, state, selstate);
+ stop = 0;
+ }
}
if (stop) {
--
2.44.0

View File

@ -1,4 +1,4 @@
From ae7c326c3c074266d8c80d71561494d785172251 Mon Sep 17 00:00:00 2001
From b0372e31b81321a820204450a35c7633caf1b7dd Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 14 Jan 2022 02:05:58 -0500
Subject: [PATCH] Factor out PAC checksum verification
@ -254,5 +254,5 @@ index 6eb23d8090..2f6ad4e1df 100644
return ret;
}
--
2.39.1
2.39.2

View File

@ -0,0 +1,45 @@
From 058dfbaed97c8e09ac4f3f7a1655b64ab3cf0144 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Wed, 21 Jul 2021 13:44:30 -0400
Subject: [PATCH] Fix defcred leak in krb5 gss_inquire_cred()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Commit 1cd2821c19b2b95e39d5fc2f451a035585a40fa5 altered the memory
management of krb5_gss_inquire_cred(), introducing defcred to act as
an owner pointer when the function must acquire a default credential.
The commit neglected to update the code to release the default cred
along the successful path. The old code does not trigger because
cred_handle is now reassigned, so the default credential is leaked.
Reported by Pavel Březina.
(a minimal alternative to commit 593e16448e1af23eef74689afe06a7bcc86e79c7)
ticket: 9016
version_fixed: 1.18.4
(cherry picked from commit b92be484630b38e26f5ee4bd67973fbd7627009c)
---
src/lib/gssapi/krb5/inq_cred.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/src/lib/gssapi/krb5/inq_cred.c b/src/lib/gssapi/krb5/inq_cred.c
index a8f2541102..cd8384d08c 100644
--- a/src/lib/gssapi/krb5/inq_cred.c
+++ b/src/lib/gssapi/krb5/inq_cred.c
@@ -197,9 +197,7 @@ krb5_gss_inquire_cred(minor_status, cred_handle, name, lifetime_ret,
mechs = GSS_C_NO_OID_SET;
}
- if (cred_handle == GSS_C_NO_CREDENTIAL)
- krb5_gss_release_cred(minor_status, (gss_cred_id_t *)&cred);
-
+ krb5_gss_release_cred(minor_status, &defcred);
krb5_free_context(context);
*minor_status = 0;
return((lifetime == 0)?GSS_S_CREDENTIALS_EXPIRED:GSS_S_COMPLETE);
--
2.44.0

View File

@ -0,0 +1,205 @@
From efb3acd20cbe6330439635a9f297b9dae8a0a5d3 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Tue, 5 Mar 2024 19:53:07 -0500
Subject: [PATCH] Fix two unlikely memory leaks
In gss_krb5int_make_seal_token_v3(), one of the bounds checks (which
could probably never be triggered) leaks plain.data. Fix this leak
and use current practices for cleanup throughout the function.
In xmt_rmtcallres() (unused within the tree and likely elsewhere),
store port_ptr into crp->port_ptr as soon as it is allocated;
otherwise it could leak if the subsequent xdr_u_int32() operation
fails.
(cherry picked from commit c5f9c816107f70139de11b38aa02db2f1774ee0d)
---
src/lib/gssapi/krb5/k5sealv3.c | 56 +++++++++++++++-------------------
src/lib/rpc/pmap_rmt.c | 9 +++---
2 files changed, 29 insertions(+), 36 deletions(-)
diff --git a/src/lib/gssapi/krb5/k5sealv3.c b/src/lib/gssapi/krb5/k5sealv3.c
index 3b4f8cb837..e881eee835 100644
--- a/src/lib/gssapi/krb5/k5sealv3.c
+++ b/src/lib/gssapi/krb5/k5sealv3.c
@@ -65,7 +65,7 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
int conf_req_flag, int toktype)
{
size_t bufsize = 16;
- unsigned char *outbuf = 0;
+ unsigned char *outbuf = NULL;
krb5_error_code err;
int key_usage;
unsigned char acceptor_flag;
@@ -75,9 +75,13 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
#endif
size_t ec;
unsigned short tok_id;
- krb5_checksum sum;
+ krb5_checksum sum = { 0 };
krb5_key key;
krb5_cksumtype cksumtype;
+ krb5_data plain = empty_data();
+
+ token->value = NULL;
+ token->length = 0;
acceptor_flag = ctx->initiate ? 0 : FLAG_SENDER_IS_ACCEPTOR;
key_usage = (toktype == KG_TOK_WRAP_MSG
@@ -107,14 +111,15 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
#endif
if (toktype == KG_TOK_WRAP_MSG && conf_req_flag) {
- krb5_data plain;
krb5_enc_data cipher;
size_t ec_max;
size_t encrypt_size;
/* 300: Adds some slop. */
- if (SIZE_MAX - 300 < message->length)
- return ENOMEM;
+ if (SIZE_MAX - 300 < message->length) {
+ err = ENOMEM;
+ goto cleanup;
+ }
ec_max = SIZE_MAX - message->length - 300;
if (ec_max > 0xffff)
ec_max = 0xffff;
@@ -126,20 +131,20 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
#endif
err = alloc_data(&plain, message->length + 16 + ec);
if (err)
- return err;
+ goto cleanup;
/* Get size of ciphertext. */
encrypt_size = krb5_encrypt_size(plain.length, key->keyblock.enctype);
if (encrypt_size > SIZE_MAX / 2) {
err = ENOMEM;
- goto error;
+ goto cleanup;
}
bufsize = 16 + encrypt_size;
/* Allocate space for header plus encrypted data. */
outbuf = gssalloc_malloc(bufsize);
if (outbuf == NULL) {
- free(plain.data);
- return ENOMEM;
+ err = ENOMEM;
+ goto cleanup;
}
/* TOK_ID */
@@ -164,11 +169,8 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
cipher.ciphertext.length = bufsize - 16;
cipher.enctype = key->keyblock.enctype;
err = krb5_k_encrypt(context, key, key_usage, 0, &plain, &cipher);
- zap(plain.data, plain.length);
- free(plain.data);
- plain.data = 0;
if (err)
- goto error;
+ goto cleanup;
/* Now that we know we're returning a valid token.... */
ctx->seq_send++;
@@ -181,7 +183,6 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
/* If the rotate fails, don't worry about it. */
#endif
} else if (toktype == KG_TOK_WRAP_MSG && !conf_req_flag) {
- krb5_data plain;
size_t cksumsize;
/* Here, message is the application-supplied data; message2 is
@@ -193,21 +194,19 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
wrap_with_checksum:
err = alloc_data(&plain, message->length + 16);
if (err)
- return err;
+ goto cleanup;
err = krb5_c_checksum_length(context, cksumtype, &cksumsize);
if (err)
- goto error;
+ goto cleanup;
assert(cksumsize <= 0xffff);
bufsize = 16 + message2->length + cksumsize;
outbuf = gssalloc_malloc(bufsize);
if (outbuf == NULL) {
- free(plain.data);
- plain.data = 0;
err = ENOMEM;
- goto error;
+ goto cleanup;
}
/* TOK_ID */
@@ -239,23 +238,15 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
if (message2->length)
memcpy(outbuf + 16, message2->value, message2->length);
- sum.contents = outbuf + 16 + message2->length;
- sum.length = cksumsize;
-
err = krb5_k_make_checksum(context, cksumtype, key,
key_usage, &plain, &sum);
- zap(plain.data, plain.length);
- free(plain.data);
- plain.data = 0;
if (err) {
zap(outbuf,bufsize);
- goto error;
+ goto cleanup;
}
if (sum.length != cksumsize)
abort();
memcpy(outbuf + 16 + message2->length, sum.contents, cksumsize);
- krb5_free_checksum_contents(context, &sum);
- sum.contents = 0;
/* Now that we know we're actually generating the token... */
ctx->seq_send++;
@@ -285,12 +276,13 @@ gss_krb5int_make_seal_token_v3 (krb5_context context,
token->value = outbuf;
token->length = bufsize;
- return 0;
+ outbuf = NULL;
+ err = 0;
-error:
+cleanup:
+ krb5_free_checksum_contents(context, &sum);
+ zapfree(plain.data, plain.length);
gssalloc_free(outbuf);
- token->value = NULL;
- token->length = 0;
return err;
}
diff --git a/src/lib/rpc/pmap_rmt.c b/src/lib/rpc/pmap_rmt.c
index 8c7e30c21a..0748af34a7 100644
--- a/src/lib/rpc/pmap_rmt.c
+++ b/src/lib/rpc/pmap_rmt.c
@@ -160,11 +160,12 @@ xdr_rmtcallres(
caddr_t port_ptr;
port_ptr = (caddr_t)(void *)crp->port_ptr;
- if (xdr_reference(xdrs, &port_ptr, sizeof (uint32_t),
- xdr_u_int32) && xdr_u_int32(xdrs, &crp->resultslen)) {
- crp->port_ptr = (uint32_t *)(void *)port_ptr;
+ if (!xdr_reference(xdrs, &port_ptr, sizeof (uint32_t),
+ (xdrproc_t)xdr_u_int32))
+ return (FALSE);
+ crp->port_ptr = (uint32_t *)(void *)port_ptr;
+ if (xdr_u_int32(xdrs, &crp->resultslen))
return ((*(crp->xdr_results))(xdrs, crp->results_ptr));
- }
return (FALSE);
}
--
2.44.0

View File

@ -0,0 +1,535 @@
From c73e8ed9f89fdc709d15656b6431492d43de94ce Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Fri, 14 Jun 2024 10:56:12 -0400
Subject: [PATCH] Fix vulnerabilities in GSS message token handling
In gss_krb5int_unseal_token_v3() and gss_krb5int_unseal_v3_iov(),
verify the Extra Count field of CFX wrap tokens against the encrypted
header. Reported by Jacob Champion.
In gss_krb5int_unseal_token_v3(), check for a decrypted plaintext
length too short to contain the encrypted header and extra count
bytes. Reported by Jacob Champion.
In kg_unseal_iov_token(), separately track the header IOV length and
complete token length when parsing the token's ASN.1 wrapper. This
fix contains modified versions of functions from k5-der.h and
util_token.c; this duplication will be cleaned up in a future commit.
CVE-2024-37370:
In MIT krb5 release 1.3 and later, an attacker can modify the
plaintext Extra Count field of a confidential GSS krb5 wrap token,
causing the unwrapped token to appear truncated to the application.
CVE-2024-37371:
In MIT krb5 release 1.3 and later, an attacker can cause invalid
memory reads by sending message tokens with invalid length fields.
ticket: 9128 (new)
tags: pullup
target_version: 1.21-next
(cherry picked from commit b0a2f8a5365f2eec3e27d78907de9f9d2c80505a)
---
src/lib/gssapi/krb5/k5sealv3.c | 5 +
src/lib/gssapi/krb5/k5sealv3iov.c | 3 +-
src/lib/gssapi/krb5/k5unsealiov.c | 80 +++++++++-
src/tests/gssapi/t_invalid.c | 233 +++++++++++++++++++++++++-----
4 files changed, 275 insertions(+), 46 deletions(-)
diff --git a/src/lib/gssapi/krb5/k5sealv3.c b/src/lib/gssapi/krb5/k5sealv3.c
index e881eee835..d3210c1107 100644
--- a/src/lib/gssapi/krb5/k5sealv3.c
+++ b/src/lib/gssapi/krb5/k5sealv3.c
@@ -400,10 +400,15 @@ gss_krb5int_unseal_token_v3(krb5_context *contextptr,
/* Don't use bodysize here! Use the fact that
cipher.ciphertext.length has been adjusted to the
correct length. */
+ if (plain.length < 16 + ec) {
+ free(plain.data);
+ goto defective;
+ }
althdr = (unsigned char *)plain.data + plain.length - 16;
if (load_16_be(althdr) != KG2_TOK_WRAP_MSG
|| althdr[2] != ptr[2]
|| althdr[3] != ptr[3]
+ || load_16_be(althdr+4) != ec
|| memcmp(althdr+8, ptr+8, 8)) {
free(plain.data);
goto defective;
diff --git a/src/lib/gssapi/krb5/k5sealv3iov.c b/src/lib/gssapi/krb5/k5sealv3iov.c
index 333ee124dd..f8e90c35b4 100644
--- a/src/lib/gssapi/krb5/k5sealv3iov.c
+++ b/src/lib/gssapi/krb5/k5sealv3iov.c
@@ -402,9 +402,10 @@ gss_krb5int_unseal_v3_iov(krb5_context context,
if (load_16_be(althdr) != KG2_TOK_WRAP_MSG
|| althdr[2] != ptr[2]
|| althdr[3] != ptr[3]
+ || load_16_be(althdr + 4) != ec
|| memcmp(althdr + 8, ptr + 8, 8) != 0) {
*minor_status = 0;
- return GSS_S_BAD_SIG;
+ return GSS_S_DEFECTIVE_TOKEN;
}
} else {
/* Verify checksum: note EC is checksum size here, not padding */
diff --git a/src/lib/gssapi/krb5/k5unsealiov.c b/src/lib/gssapi/krb5/k5unsealiov.c
index 3ce2a90ce9..6a6585d9af 100644
--- a/src/lib/gssapi/krb5/k5unsealiov.c
+++ b/src/lib/gssapi/krb5/k5unsealiov.c
@@ -25,6 +25,7 @@
*/
#include "k5-int.h"
+#include "k5-der.h"
#include "gssapiP_krb5.h"
static OM_uint32
@@ -247,6 +248,73 @@ cleanup:
return retval;
}
+/* Similar to k5_der_get_value(), but output an unchecked content length
+ * instead of a k5input containing the contents. */
+static inline bool
+get_der_tag(struct k5input *in, uint8_t idbyte, size_t *len_out)
+{
+ uint8_t lenbyte, i;
+ size_t len;
+
+ /* Do nothing if in is empty or the next byte doesn't match idbyte. */
+ if (in->status || in->len == 0 || *in->ptr != idbyte)
+ return false;
+
+ /* Advance past the identifier byte and decode the length. */
+ (void)k5_input_get_byte(in);
+ lenbyte = k5_input_get_byte(in);
+ if (lenbyte < 128) {
+ len = lenbyte;
+ } else {
+ len = 0;
+ for (i = 0; i < (lenbyte & 0x7F); i++) {
+ if (len > (SIZE_MAX >> 8)) {
+ k5_input_set_status(in, EOVERFLOW);
+ return false;
+ }
+ len = (len << 8) | k5_input_get_byte(in);
+ }
+ }
+
+ if (in->status)
+ return false;
+
+ *len_out = len;
+ return true;
+}
+
+/*
+ * Similar to g_verify_token_header() without toktype or flags, but do not read
+ * more than *header_len bytes of ASN.1 wrapper, and on output set *header_len
+ * to the remaining number of header bytes. Verify the outer DER tag's length
+ * against token_len, which may be larger (but not smaller) than *header_len.
+ */
+static gss_int32
+verify_detached_wrapper(const gss_OID_desc *mech, size_t *header_len,
+ uint8_t **header_in, size_t token_len)
+{
+ struct k5input in, mech_der;
+ gss_OID_desc toid;
+ size_t len;
+
+ k5_input_init(&in, *header_in, *header_len);
+
+ if (get_der_tag(&in, 0x60, &len)) {
+ if (len != token_len - (in.ptr - *header_in))
+ return G_BAD_TOK_HEADER;
+ if (!k5_der_get_value(&in, 0x06, &mech_der))
+ return G_BAD_TOK_HEADER;
+ toid.elements = (uint8_t *)mech_der.ptr;
+ toid.length = mech_der.len;
+ if (!g_OID_equal(&toid, mech))
+ return G_WRONG_MECH;
+ }
+
+ *header_in = (uint8_t *)in.ptr;
+ *header_len = in.len;
+ return 0;
+}
+
/*
* Caller must provide TOKEN | DATA | PADDING | TRAILER, except
* for DCE in which case it can just provide TOKEN | DATA (must
@@ -267,8 +335,7 @@ kg_unseal_iov_token(OM_uint32 *minor_status,
gss_iov_buffer_t header;
gss_iov_buffer_t padding;
gss_iov_buffer_t trailer;
- size_t input_length;
- unsigned int bodysize;
+ size_t input_length, hlen;
int toktype2;
header = kg_locate_header_iov(iov, iov_count, toktype);
@@ -298,15 +365,14 @@ kg_unseal_iov_token(OM_uint32 *minor_status,
input_length += trailer->buffer.length;
}
- code = g_verify_token_header(ctx->mech_used,
- &bodysize, &ptr, -1,
- input_length, 0);
+ hlen = header->buffer.length;
+ code = verify_detached_wrapper(ctx->mech_used, &hlen, &ptr, input_length);
if (code != 0) {
*minor_status = code;
return GSS_S_DEFECTIVE_TOKEN;
}
- if (bodysize < 2) {
+ if (hlen < 2) {
*minor_status = (OM_uint32)G_BAD_TOK_HEADER;
return GSS_S_DEFECTIVE_TOKEN;
}
@@ -314,7 +380,7 @@ kg_unseal_iov_token(OM_uint32 *minor_status,
toktype2 = load_16_be(ptr);
ptr += 2;
- bodysize -= 2;
+ hlen -= 2;
switch (toktype2) {
case KG2_TOK_MIC_MSG:
diff --git a/src/tests/gssapi/t_invalid.c b/src/tests/gssapi/t_invalid.c
index fb8fe55111..8192935099 100644
--- a/src/tests/gssapi/t_invalid.c
+++ b/src/tests/gssapi/t_invalid.c
@@ -36,31 +36,41 @@
*
* 1. A pre-CFX wrap or MIC token processed with a CFX-only context causes a
* null pointer dereference. (The token must use SEAL_ALG_NONE or it will
- * be rejected.)
+ * be rejected.) This vulnerability also applies to IOV unwrap.
*
- * 2. A pre-CFX wrap or MIC token with fewer than 24 bytes after the ASN.1
+ * 2. A CFX wrap token with a different value of EC between the plaintext and
+ * encrypted copies will be erroneously accepted, which allows a message
+ * truncation attack. This vulnerability also applies to IOV unwrap.
+ *
+ * 3. A CFX wrap token with a plaintext length fewer than 16 bytes causes an
+ * access before the beginning of the input buffer, possibly leading to a
+ * crash.
+ *
+ * 4. A CFX wrap token with a plaintext EC value greater than the plaintext
+ * length - 16 causes an integer underflow when computing the result length,
+ * likely causing a crash.
+ *
+ * 5. An IOV unwrap operation will overrun the header buffer if an ASN.1
+ * wrapper longer than the header buffer is present.
+ *
+ * 6. A pre-CFX wrap or MIC token with fewer than 24 bytes after the ASN.1
* header causes an input buffer overrun, usually leading to either a segv
* or a GSS_S_DEFECTIVE_TOKEN error due to garbage algorithm, filler, or
- * sequence number values.
+ * sequence number values. This vulnerability also applies to IOV unwrap.
*
- * 3. A pre-CFX wrap token with fewer than 16 + cksumlen bytes after the ASN.1
+ * 7. A pre-CFX wrap token with fewer than 16 + cksumlen bytes after the ASN.1
* header causes an integer underflow when computing the ciphertext length,
* leading to an allocation error on 32-bit platforms or a segv on 64-bit
* platforms. A pre-CFX MIC token of this size causes an input buffer
* overrun when comparing the checksum, perhaps leading to a segv.
*
- * 4. A pre-CFX wrap token with fewer than conflen + padlen bytes in the
+ * 8. A pre-CFX wrap token with fewer than conflen + padlen bytes in the
* ciphertext (where padlen is the last byte of the decrypted ciphertext)
* causes an integer underflow when computing the original message length,
* leading to an allocation error.
*
- * 5. In the mechglue, truncated encapsulation in the initial context token can
+ * 9. In the mechglue, truncated encapsulation in the initial context token can
* cause input buffer overruns in gss_accept_sec_context().
- *
- * Vulnerabilities #1 and #2 also apply to IOV unwrap, although tokens with
- * fewer than 16 bytes after the ASN.1 header will be rejected.
- * Vulnerabilities #2 and #5 can only be robustly detected using a
- * memory-checking environment such as valgrind.
*/
#include "k5-int.h"
@@ -97,17 +107,25 @@ struct test {
}
};
-/* Fake up enough of a CFX GSS context for gss_unwrap, using an AES key. */
+static void *
+ealloc(size_t len)
+{
+ void *ptr = calloc(len, 1);
+
+ if (ptr == NULL)
+ abort();
+ return ptr;
+}
+
+/* Fake up enough of a CFX GSS context for gss_unwrap, using an AES key.
+ * The context takes ownership of subkey. */
static gss_ctx_id_t
-make_fake_cfx_context()
+make_fake_cfx_context(krb5_key subkey)
{
gss_union_ctx_id_t uctx;
krb5_gss_ctx_id_t kgctx;
- krb5_keyblock kb;
- kgctx = calloc(1, sizeof(*kgctx));
- if (kgctx == NULL)
- abort();
+ kgctx = ealloc(sizeof(*kgctx));
kgctx->established = 1;
kgctx->proto = 1;
if (g_seqstate_init(&kgctx->seqstate, 0, 0, 0, 0) != 0)
@@ -116,15 +134,10 @@ make_fake_cfx_context()
kgctx->sealalg = -1;
kgctx->signalg = -1;
- kb.enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
- kb.length = 16;
- kb.contents = (unsigned char *)"1234567887654321";
- if (krb5_k_create_key(NULL, &kb, &kgctx->subkey) != 0)
- abort();
+ kgctx->subkey = subkey;
+ kgctx->cksumtype = CKSUMTYPE_HMAC_SHA1_96_AES128;
- uctx = calloc(1, sizeof(*uctx));
- if (uctx == NULL)
- abort();
+ uctx = ealloc(sizeof(*uctx));
uctx->mech_type = &mech_krb5;
uctx->internal_ctx_id = (gss_ctx_id_t)kgctx;
return (gss_ctx_id_t)uctx;
@@ -138,9 +151,7 @@ make_fake_context(const struct test *test)
krb5_gss_ctx_id_t kgctx;
krb5_keyblock kb;
- kgctx = calloc(1, sizeof(*kgctx));
- if (kgctx == NULL)
- abort();
+ kgctx = ealloc(sizeof(*kgctx));
kgctx->established = 1;
if (g_seqstate_init(&kgctx->seqstate, 0, 0, 0, 0) != 0)
abort();
@@ -162,9 +173,7 @@ make_fake_context(const struct test *test)
if (krb5_k_create_key(NULL, &kb, &kgctx->enc) != 0)
abort();
- uctx = calloc(1, sizeof(*uctx));
- if (uctx == NULL)
- abort();
+ uctx = ealloc(sizeof(*uctx));
uctx->mech_type = &mech_krb5;
uctx->internal_ctx_id = (gss_ctx_id_t)kgctx;
return (gss_ctx_id_t)uctx;
@@ -194,9 +203,7 @@ make_token(unsigned char *token, size_t len, gss_buffer_t out)
assert(mech_krb5.length == 9);
assert(len + 11 < 128);
- wrapped = malloc(len + 13);
- if (wrapped == NULL)
- abort();
+ wrapped = ealloc(len + 13);
wrapped[0] = 0x60;
wrapped[1] = len + 11;
wrapped[2] = 0x06;
@@ -207,6 +214,18 @@ make_token(unsigned char *token, size_t len, gss_buffer_t out)
out->value = wrapped;
}
+/* Create a 16-byte header for a CFX confidential wrap token to be processed by
+ * the fake CFX context. */
+static void
+write_cfx_header(uint16_t ec, uint8_t *out)
+{
+ memset(out, 0, 16);
+ store_16_be(KG2_TOK_WRAP_MSG, out);
+ out[2] = FLAG_WRAP_CONFIDENTIAL;
+ out[3] = 0xFF;
+ store_16_be(ec, out + 4);
+}
+
/* Unwrap a superficially valid RFC 1964 token with a CFX-only context, with
* regular and IOV unwrap. */
static void
@@ -238,6 +257,134 @@ test_bogus_1964_token(gss_ctx_id_t ctx)
free(in.value);
}
+static void
+test_cfx_altered_ec(gss_ctx_id_t ctx, krb5_key subkey)
+{
+ OM_uint32 major, minor;
+ uint8_t tokbuf[128], plainbuf[24];
+ krb5_data plain;
+ krb5_enc_data cipher;
+ gss_buffer_desc in, out;
+ gss_iov_buffer_desc iov[2];
+
+ /* Construct a header with a plaintext EC value of 3. */
+ write_cfx_header(3, tokbuf);
+
+ /* Encrypt a plaintext and a copy of the header with the EC value 0. */
+ memcpy(plainbuf, "truncate", 8);
+ memcpy(plainbuf + 8, tokbuf, 16);
+ store_16_be(0, plainbuf + 12);
+ plain = make_data(plainbuf, 24);
+ cipher.ciphertext.data = (char *)tokbuf + 16;
+ cipher.ciphertext.length = sizeof(tokbuf) - 16;
+ cipher.enctype = subkey->keyblock.enctype;
+ if (krb5_k_encrypt(NULL, subkey, KG_USAGE_INITIATOR_SEAL, NULL,
+ &plain, &cipher) != 0)
+ abort();
+
+ /* Verify that the token is rejected by gss_unwrap(). */
+ in.value = tokbuf;
+ in.length = 16 + cipher.ciphertext.length;
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ (void)gss_release_buffer(&minor, &out);
+
+ /* Verify that the token is rejected by gss_unwrap_iov(). */
+ iov[0].type = GSS_IOV_BUFFER_TYPE_STREAM;
+ iov[0].buffer = in;
+ iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
+ major = gss_unwrap_iov(&minor, ctx, NULL, NULL, iov, 2);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+}
+
+static void
+test_cfx_short_plaintext(gss_ctx_id_t ctx, krb5_key subkey)
+{
+ OM_uint32 major, minor;
+ uint8_t tokbuf[128], zerobyte = 0;
+ krb5_data plain;
+ krb5_enc_data cipher;
+ gss_buffer_desc in, out;
+
+ write_cfx_header(0, tokbuf);
+
+ /* Encrypt a single byte, with no copy of the header. */
+ plain = make_data(&zerobyte, 1);
+ cipher.ciphertext.data = (char *)tokbuf + 16;
+ cipher.ciphertext.length = sizeof(tokbuf) - 16;
+ cipher.enctype = subkey->keyblock.enctype;
+ if (krb5_k_encrypt(NULL, subkey, KG_USAGE_INITIATOR_SEAL, NULL,
+ &plain, &cipher) != 0)
+ abort();
+
+ /* Verify that the token is rejected by gss_unwrap(). */
+ in.value = tokbuf;
+ in.length = 16 + cipher.ciphertext.length;
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ (void)gss_release_buffer(&minor, &out);
+}
+
+static void
+test_cfx_large_ec(gss_ctx_id_t ctx, krb5_key subkey)
+{
+ OM_uint32 major, minor;
+ uint8_t tokbuf[128] = { 0 }, plainbuf[20];
+ krb5_data plain;
+ krb5_enc_data cipher;
+ gss_buffer_desc in, out;
+
+ /* Construct a header with an EC value of 5. */
+ write_cfx_header(5, tokbuf);
+
+ /* Encrypt a 4-byte plaintext plus the header. */
+ memcpy(plainbuf, "abcd", 4);
+ memcpy(plainbuf + 4, tokbuf, 16);
+ plain = make_data(plainbuf, 20);
+ cipher.ciphertext.data = (char *)tokbuf + 16;
+ cipher.ciphertext.length = sizeof(tokbuf) - 16;
+ cipher.enctype = subkey->keyblock.enctype;
+ if (krb5_k_encrypt(NULL, subkey, KG_USAGE_INITIATOR_SEAL, NULL,
+ &plain, &cipher) != 0)
+ abort();
+
+ /* Verify that the token is rejected by gss_unwrap(). */
+ in.value = tokbuf;
+ in.length = 16 + cipher.ciphertext.length;
+ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ (void)gss_release_buffer(&minor, &out);
+}
+
+static void
+test_iov_large_asn1_wrapper(gss_ctx_id_t ctx)
+{
+ OM_uint32 minor, major;
+ uint8_t databuf[10] = { 0 };
+ gss_iov_buffer_desc iov[2];
+
+ /*
+ * In this IOV array, the header contains a DER tag with a dangling eight
+ * bytes of length field. The data IOV indicates a total token length
+ * sufficient to contain the length bytes.
+ */
+ iov[0].type = GSS_IOV_BUFFER_TYPE_HEADER;
+ iov[0].buffer.value = ealloc(2);
+ iov[0].buffer.length = 2;
+ memcpy(iov[0].buffer.value, "\x60\x88", 2);
+ iov[1].type = GSS_IOV_BUFFER_TYPE_DATA;
+ iov[1].buffer.value = databuf;
+ iov[1].buffer.length = 10;
+ major = gss_unwrap_iov(&minor, ctx, NULL, NULL, iov, 2);
+ if (major != GSS_S_DEFECTIVE_TOKEN)
+ abort();
+ free(iov[0].buffer.value);
+}
+
/* Process wrap and MIC tokens with incomplete headers. */
static void
test_short_header(gss_ctx_id_t ctx)
@@ -387,9 +534,7 @@ try_accept(void *value, size_t len)
gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
/* Copy the provided value to make input overruns more obvious. */
- in.value = malloc(len);
- if (in.value == NULL)
- abort();
+ in.value = ealloc(len);
memcpy(in.value, value, len);
in.length = len;
(void)gss_accept_sec_context(&minor, &ctx, GSS_C_NO_CREDENTIAL, &in,
@@ -424,11 +569,23 @@ test_short_encapsulation()
int
main(int argc, char **argv)
{
+ krb5_keyblock kb;
+ krb5_key cfx_subkey;
gss_ctx_id_t ctx;
size_t i;
- ctx = make_fake_cfx_context();
+ kb.enctype = ENCTYPE_AES128_CTS_HMAC_SHA1_96;
+ kb.length = 16;
+ kb.contents = (unsigned char *)"1234567887654321";
+ if (krb5_k_create_key(NULL, &kb, &cfx_subkey) != 0)
+ abort();
+
+ ctx = make_fake_cfx_context(cfx_subkey);
test_bogus_1964_token(ctx);
+ test_cfx_altered_ec(ctx, cfx_subkey);
+ test_cfx_short_plaintext(ctx, cfx_subkey);
+ test_cfx_large_ec(ctx, cfx_subkey);
+ test_iov_large_asn1_wrapper(ctx);
free_fake_context(ctx);
for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) {
--
2.45.1

View File

@ -1,4 +1,4 @@
From 9f3e127806d14b84d40abdfeae8fcd0daa1e6a4f Mon Sep 17 00:00:00 2001
From b6ada496a285a7b350e28c97b53b6f659a9a94b9 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Sat, 11 Dec 2021 01:25:34 -0500
Subject: [PATCH] Use 14 instead of 9 for unkeyed SHA-1 checksum
@ -180,5 +180,5 @@ index 3ae56c0641..3bff456f8f 100644
pkiDebug("unable to calculate AS REQ checksum\n");
goto cleanup;
--
2.39.1
2.39.2

View File

@ -0,0 +1,138 @@
From 2e871df888d526a15e9e91807480c15ca4e40618 Mon Sep 17 00:00:00 2001
From: Greg Hudson <ghudson@mit.edu>
Date: Thu, 26 Oct 2023 16:26:42 -0400
Subject: [PATCH] Wait indefinitely on KDC TCP connections
When making a KDC or password change request, wait indefinitely
(limited only by request_timeout if set) once a KDC has accepted a TCP
connection.
ticket: 9105 (new)
(cherry picked from commit 6436a3808061da787a43c6810f5f0370cdfb6e36)
---
doc/admin/conf_files/krb5_conf.rst | 2 +-
src/lib/krb5/os/sendto_kdc.c | 50 ++++++++++++++++--------------
2 files changed, 27 insertions(+), 25 deletions(-)
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
index 557094f6a2..98fe231813 100644
--- a/doc/admin/conf_files/krb5_conf.rst
+++ b/doc/admin/conf_files/krb5_conf.rst
@@ -358,7 +358,7 @@ The libdefaults section may contain any of the following relations:
for initial ticket requests. The default value is 0.
**request_timeout**
- (:ref:`duration` string.) Sets the maximum total time for KDC or
+ (:ref:`duration` string.) Sets the maximum total time for KDC and
password change requests. This timeout does not affect the
intervals between requests, so setting a low timeout may result in
fewer requests being attempted and/or some servers not being
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index f57117126e..19b78aba24 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -134,7 +134,6 @@ struct conn_state {
krb5_data callback_buffer;
size_t server_index;
struct conn_state *next;
- time_ms endtime;
krb5_boolean defer;
struct {
const char *uri_path;
@@ -344,15 +343,19 @@ cm_select_or_poll(const struct select_state *in, time_ms endtime,
struct select_state *out, int *sret)
{
#ifndef USE_POLL
- struct timeval tv;
+ struct timeval tv, *tvp;
#endif
krb5_error_code retval;
time_ms curtime, interval;
- retval = get_curtime_ms(&curtime);
- if (retval != 0)
- return retval;
- interval = (curtime < endtime) ? endtime - curtime : 0;
+ if (endtime != 0) {
+ retval = get_curtime_ms(&curtime);
+ if (retval != 0)
+ return retval;
+ interval = (curtime < endtime) ? endtime - curtime : 0;
+ } else {
+ interval = -1;
+ }
/* We don't need a separate copy of the selstate for poll, but use one for
* consistency with how we use select. */
@@ -361,9 +364,14 @@ cm_select_or_poll(const struct select_state *in, time_ms endtime,
#ifdef USE_POLL
*sret = poll(out->fds, out->nfds, interval);
#else
- tv.tv_sec = interval / 1000;
- tv.tv_usec = interval % 1000 * 1000;
- *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, &tv);
+ if (interval != -1) {
+ tv.tv_sec = interval / 1000;
+ tv.tv_usec = interval % 1000 * 1000;
+ tvp = &tv;
+ } else {
+ tvp = NULL;
+ }
+ *sret = select(out->max, &out->rfds, &out->wfds, &out->xfds, tvp);
#endif
return (*sret < 0) ? SOCKET_ERRNO : 0;
@@ -1094,11 +1102,6 @@ service_tcp_connect(krb5_context context, const krb5_data *realm,
}
conn->state = WRITING;
-
- /* Record this connection's timeout for service_fds. */
- if (get_curtime_ms(&conn->endtime) == 0)
- conn->endtime += 10000;
-
return conn->service_write(context, realm, conn, selstate);
}
@@ -1373,19 +1376,18 @@ kill_conn:
return FALSE;
}
-/* Return the maximum of endtime and the endtime fields of all currently active
- * TCP connections. */
-static time_ms
-get_endtime(time_ms endtime, struct conn_state *conns)
+/* Return true if conns contains any states with connected TCP sockets. */
+static krb5_boolean
+any_tcp_connections(struct conn_state *conns)
{
struct conn_state *state;
for (state = conns; state != NULL; state = state->next) {
- if ((state->state == READING || state->state == WRITING) &&
- state->endtime > endtime)
- endtime = state->endtime;
+ if (state->addr.transport != UDP &&
+ (state->state == READING || state->state == WRITING))
+ return TRUE;
}
- return endtime;
+ return FALSE;
}
static krb5_boolean
@@ -1408,9 +1410,9 @@ service_fds(krb5_context context, struct select_state *selstate,
e = 0;
while (selstate->nfds > 0) {
- endtime = get_endtime(interval_end, conns);
+ endtime = any_tcp_connections(conns) ? 0 : interval_end;
/* Don't wait longer than the whole request should last. */
- if (timeout && endtime > timeout)
+ if (timeout && (!endtime || endtime > timeout))
endtime = timeout;
e = cm_select_or_poll(selstate, endtime, seltemp, &selret);
if (e == EINTR)
--
2.44.0

View File

@ -0,0 +1,126 @@
From 274464a6faaee694c30ae4d1412a8ab516b1a982 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Wed, 20 Sep 2023 16:22:06 +0200
Subject: [PATCH] [downstream] Allow to make AD-SIGNEDPATH optional
MIT krb5 1.20 and newer KDCs do generate a minimal PAC instead of
AD-SIGNEDPATH. As a consequence, an evidence ticket generated by an
older KDC would fail to be processed by a newer KDC for a constrained
delegation request.
This commit modifies this behavior to check the AD-SIGNEDPATH whenever
it is present in a TGS-REQ, but do not require it in case a PAC is
provided AND the KDB plugin supports its verification. This is done
regardless to the fact the constrained delegation request is from a
local realm or a cross-realm.
To enable this mechanism, the KDB plugin must set the
"optional_ab_signedpath" string attribute to "true" for the local TGS
principal.
---
src/include/kdb.h | 1 +
src/kdc/kdc_authdata.c | 65 +++++++++++++++++++++++++++++++++---------
2 files changed, 52 insertions(+), 14 deletions(-)
diff --git a/src/include/kdb.h b/src/include/kdb.h
index c56947ab81..95d07d0195 100644
--- a/src/include/kdb.h
+++ b/src/include/kdb.h
@@ -136,6 +136,7 @@
/* String attribute names recognized by krb5 */
#define KRB5_KDB_SK_SESSION_ENCTYPES "session_enctypes"
#define KRB5_KDB_SK_REQUIRE_AUTH "require_auth"
+#define KRB5_KDB_SK_OPTIONAL_AD_SIGNEDPATH "optional_ad_signedpath"
#if !defined(_WIN32)
diff --git a/src/kdc/kdc_authdata.c b/src/kdc/kdc_authdata.c
index 1ebe872467..c0fcccdf21 100644
--- a/src/kdc/kdc_authdata.c
+++ b/src/kdc/kdc_authdata.c
@@ -668,6 +668,13 @@ has_pac(krb5_context context, krb5_authdata **authdata)
return has_kdc_issued_authdata(context, authdata, KRB5_AUTHDATA_WIN2K_PAC);
}
+/* Return true if the AD-SIGNEDPATH is present in authorization data. */
+static krb5_boolean
+has_ad_signedpath(krb5_context context, krb5_authdata **authdata)
+{
+ return has_kdc_issued_authdata(context, authdata, KRB5_AUTHDATA_SIGNTICKET);
+}
+
/* Verify AD-SIGNTICKET authdata if we need to, and insert an AD-SIGNEDPATH
* element if we should. */
static krb5_error_code
@@ -680,24 +687,54 @@ handle_signticket(krb5_context context, unsigned int flags,
{
krb5_error_code ret = 0;
krb5_principal *deleg_path = NULL;
- krb5_boolean signed_path = FALSE;
- krb5_boolean s4u2proxy;
+ krb5_boolean s4u2proxy, adsp_present, adsp_optional, adsp_valid = FALSE;
+ char *str;
s4u2proxy = isflagset(flags, KRB5_KDB_FLAG_CONSTRAINED_DELEGATION);
- /* For cross-realm the Windows PAC must have been verified, and it
- * fulfills the same role as the signed path. */
- if (req->msg_type == KRB5_TGS_REQ &&
- (!isflagset(flags, KRB5_KDB_FLAG_CROSS_REALM) ||
- !has_pac(context, enc_tkt_req->authorization_data))) {
- ret = verify_signedpath(context, local_tgt, local_tgt_key, enc_tkt_req,
- &deleg_path, &signed_path);
- if (ret)
- goto cleanup;
+ if (req->msg_type == KRB5_TGS_REQ) {
+ adsp_present = has_ad_signedpath(context,
+ enc_tkt_req->authorization_data);
+
+ /* In case of constained delegation, based on the value of the
+ * "optional_ad_signedpath" string attribute of the local TGS principal:
+ * - "true": in case AD-SIGNEDPATH is absent, the PAC must be present
+ * - "false" or undefined: AD-SIGNEDPATH must be present
+ */
+ if (s4u2proxy && !adsp_present) {
+ ret = krb5_dbe_get_string(context, local_tgt,
+ KRB5_KDB_SK_OPTIONAL_AD_SIGNEDPATH,
+ &str);
+ /* TODO: should be using _krb5_conf_boolean(), but os-proto.h is not
+ * available here.
+ */
+ adsp_optional = !ret && str && (strncasecmp(str, "true", 4) == 0
+ || strncasecmp(str, "t", 1) == 0
+ || strncasecmp(str, "yes", 3) == 0
+ || strncasecmp(str, "y", 1) == 0
+ || strncasecmp(str, "1", 1) == 0
+ || strncasecmp(str, "on", 2) == 0);
+
+ if (!adsp_optional ||
+ !has_pac(context, enc_tkt_req->authorization_data)) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ goto cleanup;
+ }
+ }
- if (s4u2proxy && signed_path == FALSE) {
- ret = KRB5KDC_ERR_BADOPTION;
- goto cleanup;
+ /* If AD-SIGNEDPATH is present, verify it */
+ if (adsp_present) {
+ ret = verify_signedpath(context, local_tgt, local_tgt_key,
+ enc_tkt_req, &deleg_path, &adsp_valid);
+ if (ret)
+ goto cleanup;
+
+ /* In case of contrained delegation, if AD-SIGNEDPATH is present, it
+ * has to be valid */
+ if (s4u2proxy && !adsp_valid) {
+ ret = KRB5KDC_ERR_BADOPTION;
+ goto cleanup;
+ }
}
}
--
2.41.0

View File

@ -1,4 +1,4 @@
From 3e40a47cee51fb855c71d425eb572253b6fc41eb Mon Sep 17 00:00:00 2001
From 8ded82fb279198f3fa20fb7c836e77290e7bc6f6 Mon Sep 17 00:00:00 2001
From: Julien Rische <jrische@redhat.com>
Date: Fri, 24 Mar 2023 16:22:06 +0100
Subject: [PATCH] [downstream] Support PAC full checksum w/o ticket checksum
@ -136,5 +136,5 @@ index 28784ec67c..ac94e0c236 100644
krb5_pac_get_types
krb5_pac_init
--
2.39.2
2.40.1

View File

@ -18,7 +18,7 @@ Summary: The Kerberos network authentication system
Name: krb5
Version: 1.18.2
# for prerelease, should be e.g., 0.% {prerelease}.1% { ?dist } (without spaces)
Release: 25%{?dist}
Release: 29%{?dist}
# lookaside-cached sources; two downloads and a build artifact
Source0: https://web.mit.edu/kerberos/dist/krb5/1.18/krb5-%{version}%{prerelease}.tar.gz
@ -94,11 +94,19 @@ Patch148: downstream-Fix-dejagnu-unit-tests-directory-name-for-RPC-lib.patch
Patch149: krb5-krad-larger-attrs.patch
Patch150: krb5-krad-remote.patch
Patch151: Fix-integer-overflows-in-PAC-parsing.patch
Patch152: 0152-Use-14-instead-of-9-for-unkeyed-SHA-1-checksum.patch
Patch153: 0153-Add-PAC-ticket-signature-APIs.patch
Patch154: 0154-Factor-out-PAC-checksum-verification.patch
Patch155: 0155-Add-PAC-full-checksums.patch
Patch156: 0156-downstream-Support-PAC-full-checksum-w-o-ticket-chec.patch
Patch152: Use-14-instead-of-9-for-unkeyed-SHA-1-checksum.patch
Patch153: Add-PAC-ticket-signature-APIs.patch
Patch154: Factor-out-PAC-checksum-verification.patch
Patch155: Add-PAC-full-checksums.patch
Patch156: downstream-Support-PAC-full-checksum-w-o-ticket-chec.patch
Patch157: downstream-Allow-to-make-AD-SIGNEDPATH-optional.patch
Patch158: End-connection-on-KDC_ERR_SVC_UNAVAILABLE.patch
Patch159: Add-request_timeout-configuration-parameter.patch
Patch160: Wait-indefinitely-on-KDC-TCP-connections.patch
Patch161: Fix-two-unlikely-memory-leaks.patch
Patch162: Fix-defcred-leak-in-krb5-gss_inquire_cred.patch
Patch163: Add-a-simple-DER-support-header.patch
Patch164: Fix-vulnerabilities-in-GSS-message-token-handling.patch
License: MIT
URL: http://web.mit.edu/kerberos/www/
@ -709,15 +717,36 @@ exit 0
%{_libdir}/libkadm5srv_mit.so.*
%changelog
* Tue Jun 06 2023 Julien Rische <jrische@redhat.com> - 1.18.2-25
* Mon Jul 01 2024 Julien Rische <jrische@redhat.com> - 1.18.2-29
- CVE-2024-37370 CVE-2024-37371
Fix vulnerabilities in GSS message token handling
Resolves: RHEL-45398 RHEL-45386
* Tue Apr 09 2024 Julien Rische <jrische@redhat.com> - 1.18.2-28
- Fix leak of default credentials in gss_inquire_cred()
Resolves: RHEL-32258
* Thu Mar 21 2024 Julien Rische <jrische@redhat.com> - 1.18.2-27
- Fix memory leak in GSSAPI interface
Resolves: RHEL-27250
- Fix memory leak in PMAP RPC interface
Resolves: RHEL-27244
- Make TCP waiting time configurable
Resolves: RHEL-17131
* Wed Sep 27 2023 Julien Rische <jrische@redhat.com> - 1.18.2-26
- Allow to make AD-SIGNEDPATH optional
Resolves: RHEL-10514
* Thu Jul 06 2023 Julien Rische <jrische@redhat.com> - 1.18.2-25
- Bump release number
* Fri Mar 24 2023 Julien Rische <jrische@redhat.com> - 1.18.2-24
- Support PAC with KDC extended signature and without ticket signature
- Resolves: rhbz#2211390
* Wed Jul 05 2023 Julien Rische <jrische@redhat.com> - 1.18.2-24
- Remove downloadable source signature file
- Resolves: rhbz#2219654
* Tue Feb 14 2023 Julien Rische <jrische@redhat.com> - 1.18.2-23
- Add support for MS-PAC extended KDC signature (CVE-2022-37967)
* Wed May 31 2023 Julien Rische <jrische@redhat.com> - 1.18.2-23
- Support PAC with KDC extended signature and without ticket signature
- Resolves: rhbz#2169477
* Tue Nov 08 2022 Julien Rische <jrische@redhat.com> - 1.18.2-22