aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NOTICE2
-rw-r--r--README2
-rw-r--r--doc/appdev/refs/macros/index.rst1
-rw-r--r--doc/conf.py2
-rw-r--r--doc/copyright.rst2
-rw-r--r--doc/notice.rst2
-rw-r--r--src/include/krb5/krb5.hin1
-rw-r--r--src/lib/gssapi/krb5/accept_sec_context.c2
-rw-r--r--src/lib/gssapi/krb5/gssapiP_krb5.h2
-rw-r--r--src/lib/gssapi/krb5/iakerb.c45
-rw-r--r--src/lib/gssapi/krb5/init_sec_context.c2
-rw-r--r--src/lib/gssapi/krb5/k5sealv3.c5
-rw-r--r--src/lib/gssapi/krb5/k5sealv3iov.c3
-rw-r--r--src/lib/gssapi/krb5/k5unsealiov.c80
-rw-r--r--src/lib/krb5/asn.1/asn1_k_encode.c2
-rw-r--r--src/prototype/prototype.c2
-rw-r--r--src/prototype/prototype.h2
-rw-r--r--src/tests/asn.1/krb5_decode_test.c2
-rw-r--r--src/tests/asn.1/reference_encode.out2
-rw-r--r--src/tests/asn.1/trval.c4
-rw-r--r--src/tests/asn.1/trval_reference.out7
-rw-r--r--src/tests/gssapi/t_invalid.c233
-rw-r--r--src/windows/version.rc2
23 files changed, 309 insertions, 98 deletions
diff --git a/NOTICE b/NOTICE
index 9788c8e..6c900dc 100644
--- a/NOTICE
+++ b/NOTICE
@@ -1,4 +1,4 @@
-Copyright (C) 1985-2023 by the Massachusetts Institute of Technology.
+Copyright (C) 1985-2024 by the Massachusetts Institute of Technology.
All rights reserved.
diff --git a/README b/README
index 35acf03..2c1478e 100644
--- a/README
+++ b/README
@@ -6,7 +6,7 @@
Copyright and Other Notices
---------------------------
-Copyright (C) 1985-2023 by the Massachusetts Institute of Technology
+Copyright (C) 1985-2024 by the Massachusetts Institute of Technology
and its contributors. All rights reserved.
Please see the file named NOTICE for additional notices.
diff --git a/doc/appdev/refs/macros/index.rst b/doc/appdev/refs/macros/index.rst
index ee9fe2c..645b59f 100644
--- a/doc/appdev/refs/macros/index.rst
+++ b/doc/appdev/refs/macros/index.rst
@@ -178,6 +178,7 @@ Public
KRB5_KEYUSAGE_GSS_TOK_MIC.rst
KRB5_KEYUSAGE_GSS_TOK_WRAP_INTEG.rst
KRB5_KEYUSAGE_GSS_TOK_WRAP_PRIV.rst
+ KRB5_KEYUSAGE_FINISHED.rst
KRB5_KEYUSAGE_IAKERB_FINISHED.rst
KRB5_KEYUSAGE_KDC_REP_TICKET.rst
KRB5_KEYUSAGE_KRB_CRED_ENCPART.rst
diff --git a/doc/conf.py b/doc/conf.py
index 8a8a126..ada2674 100644
--- a/doc/conf.py
+++ b/doc/conf.py
@@ -45,7 +45,7 @@ else:
# General information about the project.
project = u'MIT Kerberos'
-copyright = u'1985-2023, MIT'
+copyright = u'1985-2024, MIT'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
diff --git a/doc/copyright.rst b/doc/copyright.rst
index a98b268..85ecebe 100644
--- a/doc/copyright.rst
+++ b/doc/copyright.rst
@@ -1,7 +1,7 @@
Copyright
=========
-Copyright |copy| 1985-2023 by the Massachusetts Institute of
+Copyright |copy| 1985-2024 by the Massachusetts Institute of
Technology and its contributors. All rights reserved.
See :ref:`mitK5license` for additional copyright and license
diff --git a/doc/notice.rst b/doc/notice.rst
index dbb5a80..68efe43 100644
--- a/doc/notice.rst
+++ b/doc/notice.rst
@@ -1,4 +1,4 @@
-Copyright |copy| 1985-2023 by the Massachusetts Institute of Technology.
+Copyright |copy| 1985-2024 by the Massachusetts Institute of Technology.
All rights reserved.
diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin
index 7496383..7c4fc10 100644
--- a/src/include/krb5/krb5.hin
+++ b/src/include/krb5/krb5.hin
@@ -977,6 +977,7 @@ krb5_c_keyed_checksum_types(krb5_context context, krb5_enctype enctype,
#define KRB5_KEYUSAGE_PA_REFERRAL 26
#define KRB5_KEYUSAGE_AD_SIGNEDPATH -21
+#define KRB5_KEYUSAGE_FINISHED 41
#define KRB5_KEYUSAGE_IAKERB_FINISHED 42
#define KRB5_KEYUSAGE_PA_PKINIT_KX 44
#define KRB5_KEYUSAGE_PA_OTP_REQUEST 45 /**< See RFC 6560 section 4.2 */
diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c
index 227aa54..c224ee9 100644
--- a/src/lib/gssapi/krb5/accept_sec_context.c
+++ b/src/lib/gssapi/krb5/accept_sec_context.c
@@ -378,7 +378,7 @@ kg_process_extension(krb5_context context,
assert(exts != NULL);
switch (ext_type) {
- case KRB5_GSS_EXTS_IAKERB_FINISHED:
+ case GSS_EXTS_FINISHED:
if (exts->iakerb.conv == NULL) {
code = KRB5KRB_AP_ERR_MSG_TYPE; /* XXX */
} else {
diff --git a/src/lib/gssapi/krb5/gssapiP_krb5.h b/src/lib/gssapi/krb5/gssapiP_krb5.h
index 7364607..0ebe3a6 100644
--- a/src/lib/gssapi/krb5/gssapiP_krb5.h
+++ b/src/lib/gssapi/krb5/gssapiP_krb5.h
@@ -1291,7 +1291,7 @@ data_to_gss(krb5_data *input_k5data, gss_buffer_t output_buffer)
return code;
}
-#define KRB5_GSS_EXTS_IAKERB_FINISHED 1
+#define GSS_EXTS_FINISHED 2
/* Credential store extensions */
diff --git a/src/lib/gssapi/krb5/iakerb.c b/src/lib/gssapi/krb5/iakerb.c
index 3ee926e..9e8bc05 100644
--- a/src/lib/gssapi/krb5/iakerb.c
+++ b/src/lib/gssapi/krb5/iakerb.c
@@ -97,8 +97,8 @@ iakerb_make_finished(krb5_context context,
if (key == NULL)
return KRB5KDC_ERR_NULL_KEY;
- code = krb5_k_make_checksum(context, 0, key, KRB5_KEYUSAGE_IAKERB_FINISHED,
- conv, &iaf.checksum);
+ code = krb5_k_make_checksum(context, 0, key, KRB5_KEYUSAGE_FINISHED, conv,
+ &iaf.checksum);
if (code != 0)
return code;
@@ -129,8 +129,8 @@ iakerb_verify_finished(krb5_context context,
if (code != 0)
return code;
- code = krb5_k_verify_checksum(context, key, KRB5_KEYUSAGE_IAKERB_FINISHED,
- conv, &iaf->checksum, &valid);
+ code = krb5_k_verify_checksum(context, key, KRB5_KEYUSAGE_FINISHED, conv,
+ &iaf->checksum, &valid);
if (code == 0 && valid == FALSE)
code = KRB5KRB_AP_ERR_BAD_INTEGRITY;
@@ -163,7 +163,6 @@ iakerb_save_token(iakerb_ctx_id_t ctx, const gss_buffer_t token)
*/
static krb5_error_code
iakerb_parse_token(iakerb_ctx_id_t ctx,
- int initialContextToken,
const gss_buffer_t token,
krb5_data *realm,
krb5_data **cookie,
@@ -173,7 +172,6 @@ iakerb_parse_token(iakerb_ctx_id_t ctx,
krb5_iakerb_header *iah = NULL;
unsigned int bodysize;
uint8_t *body;
- int flags = 0;
krb5_data data;
struct k5input in, seq;
@@ -182,12 +180,10 @@ iakerb_parse_token(iakerb_ctx_id_t ctx,
goto cleanup;
}
- if (initialContextToken)
- flags |= G_VFY_TOKEN_HDR_WRAPPER_REQUIRED;
-
body = token->value;
code = g_verify_token_header(gss_mech_iakerb, &bodysize, &body,
- IAKERB_TOK_PROXY, token->length, flags);
+ IAKERB_TOK_PROXY, token->length,
+ G_VFY_TOKEN_HDR_WRAPPER_REQUIRED);
if (code != 0)
goto cleanup;
@@ -232,7 +228,6 @@ iakerb_make_token(iakerb_ctx_id_t ctx,
krb5_data *realm,
krb5_data *cookie,
krb5_data *request,
- int initialContextToken,
gss_buffer_t token)
{
krb5_error_code code;
@@ -269,11 +264,7 @@ iakerb_make_token(iakerb_ctx_id_t ctx,
memcpy(data->data + data->length, request->data, request->length);
data->length += request->length;
- if (initialContextToken)
- tokenSize = g_token_size(gss_mech_iakerb, data->length);
- else
- tokenSize = 2 + data->length;
-
+ tokenSize = g_token_size(gss_mech_iakerb, data->length);
token->value = gssalloc_malloc(tokenSize);
if (token->value == NULL) {
code = ENOMEM;
@@ -282,12 +273,7 @@ iakerb_make_token(iakerb_ctx_id_t ctx,
token->length = tokenSize;
k5_buf_init_fixed(&buf, token->value, token->length);
- if (initialContextToken) {
- g_make_token_header(&buf, gss_mech_iakerb, data->length,
- IAKERB_TOK_PROXY);
- } else {
- k5_buf_add_uint16_be(&buf, IAKERB_TOK_PROXY);
- }
+ g_make_token_header(&buf, gss_mech_iakerb, data->length, IAKERB_TOK_PROXY);
k5_buf_add_len(&buf, data->data, data->length);
assert(buf.len == token->length);
@@ -305,7 +291,6 @@ cleanup:
*/
static krb5_error_code
iakerb_acceptor_step(iakerb_ctx_id_t ctx,
- int initialContextToken,
const gss_buffer_t input_token,
gss_buffer_t output_token)
{
@@ -324,8 +309,7 @@ iakerb_acceptor_step(iakerb_ctx_id_t ctx,
goto cleanup;
}
- code = iakerb_parse_token(ctx, initialContextToken, input_token, &realm,
- NULL, &request);
+ code = iakerb_parse_token(ctx, input_token, &realm, NULL, &request);
if (code != 0)
goto cleanup;
@@ -374,7 +358,7 @@ iakerb_acceptor_step(iakerb_ctx_id_t ctx,
} else if (code != 0)
goto cleanup;
- code = iakerb_make_token(ctx, &realm, NULL, &reply, 0, output_token);
+ code = iakerb_make_token(ctx, &realm, NULL, &reply, output_token);
if (code != 0)
goto cleanup;
@@ -524,7 +508,7 @@ iakerb_initiator_step(iakerb_ctx_id_t ctx,
output_token->value = NULL;
if (input_token != GSS_C_NO_BUFFER && input_token->length > 0) {
- code = iakerb_parse_token(ctx, 0, input_token, NULL, &cookie, &in);
+ code = iakerb_parse_token(ctx, input_token, NULL, &cookie, &in);
if (code != 0)
goto cleanup;
@@ -593,9 +577,7 @@ iakerb_initiator_step(iakerb_ctx_id_t ctx,
if (out.length != 0) {
assert(ctx->state != IAKERB_AP_REQ);
- code = iakerb_make_token(ctx, &realm, cookie, &out,
- (input_token == GSS_C_NO_BUFFER),
- output_token);
+ code = iakerb_make_token(ctx, &realm, cookie, &out, output_token);
if (code != 0)
goto cleanup;
@@ -788,8 +770,7 @@ iakerb_gss_accept_sec_context(OM_uint32 *minor_status,
major_status = GSS_S_DEFECTIVE_TOKEN;
goto cleanup;
}
- code = iakerb_acceptor_step(ctx, initialContextToken,
- input_token, output_token);
+ code = iakerb_acceptor_step(ctx, input_token, output_token);
if (code == (OM_uint32)KRB5_BAD_MSIZE)
major_status = GSS_S_DEFECTIVE_TOKEN;
if (code != 0)
diff --git a/src/lib/gssapi/krb5/init_sec_context.c b/src/lib/gssapi/krb5/init_sec_context.c
index 3cc9d4c..0a088c5 100644
--- a/src/lib/gssapi/krb5/init_sec_context.c
+++ b/src/lib/gssapi/krb5/init_sec_context.c
@@ -339,7 +339,7 @@ make_gss_checksum (krb5_context context, krb5_auth_context auth_context,
k5_buf_add_len(&buf, credmsg.data, credmsg.length);
}
if (data->exts->iakerb.conv != NULL) {
- k5_buf_add_uint32_be(&buf, KRB5_GSS_EXTS_IAKERB_FINISHED);
+ k5_buf_add_uint32_be(&buf, GSS_EXTS_FINISHED);
k5_buf_add_uint32_be(&buf, finished->length);
k5_buf_add_len(&buf, finished->data, finished->length);
}
diff --git a/src/lib/gssapi/krb5/k5sealv3.c b/src/lib/gssapi/krb5/k5sealv3.c
index e881eee..d3210c1 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 333ee12..f8e90c3 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 85a9574..21b5017 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
@@ -265,6 +266,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
@@ -285,8 +353,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);
@@ -316,15 +383,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;
}
@@ -332,7 +398,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/lib/krb5/asn.1/asn1_k_encode.c b/src/lib/krb5/asn.1/asn1_k_encode.c
index 5378b5c..ad5a18a 100644
--- a/src/lib/krb5/asn.1/asn1_k_encode.c
+++ b/src/lib/krb5/asn.1/asn1_k_encode.c
@@ -1092,7 +1092,7 @@ DEFOPTIONALEMPTYTYPE(opt_ptr_seqof_princ_plus_realm,
ptr_seqof_princ_plus_realm);
/* First context tag is 1, not 0. */
-DEFFIELD(iakerb_header_1, krb5_iakerb_header, target_realm, 1, ostring_data);
+DEFFIELD(iakerb_header_1, krb5_iakerb_header, target_realm, 1, utf8_data);
DEFFIELD(iakerb_header_2, krb5_iakerb_header, cookie, 2, opt_ostring_data_ptr);
static const struct atype_info *iakerb_header_fields[] = {
&k5_atype_iakerb_header_1, &k5_atype_iakerb_header_2
diff --git a/src/prototype/prototype.c b/src/prototype/prototype.c
index 2670316..77cc8c2 100644
--- a/src/prototype/prototype.c
+++ b/src/prototype/prototype.c
@@ -1,7 +1,7 @@
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* prototype/prototype.c - <<< One-line description of file >>> */
/*
- * Copyright (C) 2023 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2024 by the Massachusetts Institute of Technology.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/src/prototype/prototype.h b/src/prototype/prototype.h
index 7c0fab7..c6d4241 100644
--- a/src/prototype/prototype.h
+++ b/src/prototype/prototype.h
@@ -1,7 +1,7 @@
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
/* prototype/prototype.h - <<< One-line description of file >>> */
/*
- * Copyright (C) 2023 by the Massachusetts Institute of Technology.
+ * Copyright (C) 2024 by the Massachusetts Institute of Technology.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
diff --git a/src/tests/asn.1/krb5_decode_test.c b/src/tests/asn.1/krb5_decode_test.c
index 2fa6dce..391bf0f 100644
--- a/src/tests/asn.1/krb5_decode_test.c
+++ b/src/tests/asn.1/krb5_decode_test.c
@@ -1001,7 +1001,7 @@ main(int argc, char **argv)
/* decode_iakerb_header */
{
setup(krb5_iakerb_header,ktest_make_sample_iakerb_header);
- decode_run("iakerb_header","","30 18 A1 0A 04 08 6B 72 62 35 64 61 74 61 A2 0A 04 08 6B 72 62 35 64 61 74 61",decode_krb5_iakerb_header,ktest_equal_iakerb_header,krb5_free_iakerb_header);
+ decode_run("iakerb_header","","30 18 A1 0A 0C 08 6B 72 62 35 64 61 74 61 A2 0A 04 08 6B 72 62 35 64 61 74 61",decode_krb5_iakerb_header,ktest_equal_iakerb_header,krb5_free_iakerb_header);
ktest_empty_iakerb_header(&ref);
}
diff --git a/src/tests/asn.1/reference_encode.out b/src/tests/asn.1/reference_encode.out
index faa3dba..20c6ce1 100644
--- a/src/tests/asn.1/reference_encode.out
+++ b/src/tests/asn.1/reference_encode.out
@@ -55,7 +55,7 @@ encode_krb5_enc_sam_response_enc_2: 30 1F A0 03 02 01 58 A1 18 04 16 65 6E 63 5F
encode_krb5_pa_for_user: 30 4B A0 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A3 0A 1B 08 6B 72 62 35 64 61 74 61
encode_krb5_pa_s4u_x509_user: 30 68 A0 55 30 53 A0 06 02 04 00 CA 14 9A A1 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A2 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 12 04 10 70 61 5F 73 34 75 5F 78 35 30 39 5F 75 73 65 72 A4 07 03 05 00 80 00 00 00 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34
encode_krb5_ad_kdcissued: 30 65 A0 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A1 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A2 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A3 24 30 22 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72 30 0F A0 03 02 01 01 A1 08 04 06 66 6F 6F 62 61 72
-encode_krb5_iakerb_header: 30 18 A1 0A 04 08 6B 72 62 35 64 61 74 61 A2 0A 04 08 6B 72 62 35 64 61 74 61
+encode_krb5_iakerb_header: 30 18 A1 0A 0C 08 6B 72 62 35 64 61 74 61 A2 0A 04 08 6B 72 62 35 64 61 74 61
encode_krb5_iakerb_finished: 30 11 A1 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34
encode_krb5_fast_response: 30 81 9F A0 26 30 24 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 30 10 A1 03 02 01 0D A2 09 04 07 70 61 2D 64 61 74 61 A1 13 30 11 A0 03 02 01 01 A1 0A 04 08 31 32 33 34 35 36 37 38 A2 5B 30 59 A0 11 18 0F 31 39 39 34 30 36 31 30 30 36 30 33 31 37 5A A1 05 02 03 01 E2 40 A2 10 1B 0E 41 54 48 45 4E 41 2E 4D 49 54 2E 45 44 55 A3 1A 30 18 A0 03 02 01 01 A1 11 30 0F 1B 06 68 66 74 73 61 69 1B 05 65 78 74 72 61 A4 0F 30 0D A0 03 02 01 01 A1 06 04 04 31 32 33 34 A3 03 02 01 2A
encode_krb5_pa_fx_fast_reply: A0 29 30 27 A0 25 30 23 A0 03 02 01 00 A1 03 02 01 05 A2 17 04 15 6B 72 62 41 53 4E 2E 31 20 74 65 73 74 20 6D 65 73 73 61 67 65
diff --git a/src/tests/asn.1/trval.c b/src/tests/asn.1/trval.c
index e0e58cc..f84c515 100644
--- a/src/tests/asn.1/trval.c
+++ b/src/tests/asn.1/trval.c
@@ -72,6 +72,7 @@
#define PRIM_REAL 0x09 /* Real */
#define PRIM_ENUM 0x0a /* Enumerated type */
#define PRIM_ENCR 0x0b /* Encrypted */
+#define PRIM_UTF8 0x0c /* UTF8String */
#define CONS_SEQ 0x10 /* SEQUENCE/SEQUENCE OF */
#define CONS_SET 0x11 /* SET/SET OF */
#define DEFN_NUMS 0x12 /* Numeric String */
@@ -321,7 +322,7 @@ do_prim_string(FILE *fp, int tag, unsigned char *enc, int len, int lev)
/*
* Only try this printing function with "reasonable" types
*/
- if ((tag < DEFN_NUMS) && (tag != PRIM_OCTS))
+ if ((tag < DEFN_NUMS) && (tag != PRIM_OCTS) && (tag != PRIM_UTF8))
return 0;
for (i=0; i < len; i++)
@@ -433,6 +434,7 @@ struct typestring_table univ_types[] = {
{ PRIM_REAL, -1, "Real"},
{ PRIM_ENUM, -1, "Enumerated type"},
{ PRIM_ENCR, -1, "Encrypted"},
+ { PRIM_UTF8, -1, "UTF8String"},
{ CONS_SEQ, -1, "Sequence/Sequence Of"},
{ CONS_SET, -1, "Set/Set Of"},
{ DEFN_NUMS, -1, "Numeric String"},
diff --git a/src/tests/asn.1/trval_reference.out b/src/tests/asn.1/trval_reference.out
index 9bedad4..29f88c5 100644
--- a/src/tests/asn.1/trval_reference.out
+++ b/src/tests/asn.1/trval_reference.out
@@ -1254,7 +1254,7 @@ encode_krb5_ad_kdcissued:
encode_krb5_iakerb_header:
[Sequence/Sequence Of]
-. [1] [Octet String] "krb5data"
+. [1] [UTF8String] "krb5data"
. [2] [Octet String] "krb5data"
encode_krb5_iakerb_finished:
@@ -1417,10 +1417,7 @@ encode_krb5_pa_otp_req:
02 .
. [11] <9>
79 6f 75 72 74 6f 6b 65 6e yourtoken
-. [12] <40>
- 75 72 6e 3a 69 65 74 66 3a 70 61 72 61 6d 73 3a urn:ietf:params:
- 78 6d 6c 3a 6e 73 3a 6b 65 79 70 72 6f 76 3a 70 xml:ns:keyprov:p
- 73 6b 63 3a 68 6f 74 70 skc:hotp
+. [12] "urn:ietf:params:xml:ns:keyprov:pskc:hotp"
. [13] <11>
45 78 61 6d 70 6c 65 63 6f 72 70 Examplecorp
diff --git a/src/tests/gssapi/t_invalid.c b/src/tests/gssapi/t_invalid.c
index 7ffa67d..c4a5a99 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"
@@ -109,17 +119,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(void)
+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)
@@ -128,15 +146,10 @@ make_fake_cfx_context(void)
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;
@@ -150,9 +163,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();
@@ -174,9 +185,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;
@@ -206,9 +215,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;
@@ -219,6 +226,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
@@ -250,6 +269,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)
@@ -399,9 +546,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,
@@ -436,11 +581,23 @@ test_short_encapsulation(void)
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++) {
diff --git a/src/windows/version.rc b/src/windows/version.rc
index b9ab57a..53294e7 100644
--- a/src/windows/version.rc
+++ b/src/windows/version.rc
@@ -41,7 +41,7 @@
#define K5_PRODUCT_VERSION_STRING MAJOR_MINOR MAYBE_PATCH RELTAIL "\0"
#define K5_PRODUCT_VERSION KRB5_MAJOR_RELEASE, KRB5_MINOR_RELEASE, KRB5_PATCHLEVEL, KRB5_BUILDLEVEL
-#define K5_COPYRIGHT "Copyright (C) 1997-2023 by the Massachusetts Institute of Technology\0"
+#define K5_COPYRIGHT "Copyright (C) 1997-2024 by the Massachusetts Institute of Technology\0"
#define K5_COMPANY_NAME "Massachusetts Institute of Technology.\0"
/*