From e2bc02455982ec29e6fb0f84f104b13e16c7dec6 Mon Sep 17 00:00:00 2001 From: Nalin Dahyabhai Date: Mon, 7 Jul 2014 17:56:12 -0400 Subject: [PATCH] Pull in fix for CVE-2014-4341/CVE-2014-4342 - pull in fix for denial of service by injection of malformed GSSAPI tokens (CVE-2014-4341, CVE-2014-4342, #1116181) --- krb5-1.12-CVE-2014-4341_4342-tests.patch | 547 +++++++++++++++++++++++ krb5-1.12-CVE-2014-4341_4342.patch | 163 +++++++ krb5.spec | 10 +- 3 files changed, 719 insertions(+), 1 deletion(-) create mode 100644 krb5-1.12-CVE-2014-4341_4342-tests.patch create mode 100644 krb5-1.12-CVE-2014-4341_4342.patch diff --git a/krb5-1.12-CVE-2014-4341_4342-tests.patch b/krb5-1.12-CVE-2014-4341_4342-tests.patch new file mode 100644 index 0000000..9db28e7 --- /dev/null +++ b/krb5-1.12-CVE-2014-4341_4342-tests.patch @@ -0,0 +1,547 @@ +Changes to .gitignore removed, chunks for tests/gssapi/Makefile.in adjusted to +account for t_prf not being around yet, calls to gssint_g__seqstate_init() +changed to calls to gssint_g_order_init(), changed attempt to export +gssint_g_seqstate_init() to export gssint_g_order_init(). + +commit 7a9990d73537dcdd95bf9b280ebfd560adf8342d +Author: Greg Hudson +Date: Thu Jun 19 13:13:33 2014 -0400 + + Add tests for invalid GSSAPI per-message tokens + + ticket: 7949 + +--- a/src/lib/gssapi/libgssapi_krb5.exports ++++ b/src/lib/gssapi/libgssapi_krb5.exports +@@ -157,3 +157,4 @@ gss_inquire_name + gss_acquire_cred_from + gss_add_cred_from + gss_store_cred_into ++gssint_g_order_init +diff --git a/src/tests/gssapi/Makefile.in b/src/tests/gssapi/Makefile.in +index ac6a435..5effd90 100644 +--- a/src/tests/gssapi/Makefile.in ++++ b/src/tests/gssapi/Makefile.in +@@ -1,6 +1,7 @@ + mydir=tests$(S)gssapi + BUILDTOP=$(REL)..$(S).. + DEFINES = -DUSE_AUTOCONF_H ++LOCALINCLUDES = -I../../lib/gssapi/mechglue -I../../lib/gssapi/generic -I../../lib/gssapi/krb5 + + SRCS= $(srcdir)/ccinit.c $(srcdir)/ccrefresh.c $(srcdir)/common.c \ + $(srcdir)/t_accname.c $(srcdir)/t_ccselect.c $(srcdir)/t_credstore.c \ +@@ -12,27 +13,28 @@ SRCS= $(srcdir)/ccinit.c $(srcdir)/ccrefresh.c $(srcdir)/common.c \ + $(srcdir)/t_accname.c $(srcdir)/t_ccselect.c $(srcdir)/t_credstore.c \ + $(srcdir)/t_enctypes.c $(srcdir)/t_err.c $(srcdir)/t_export_cred.c \ + $(srcdir)/t_export_name.c $(srcdir)/t_gssexts.c \ +- $(srcdir)/t_imp_cred.c $(srcdir)/t_imp_name.c $(srcdir)/t_inq_cred.c \ +- $(srcdir)/t_inq_mechs_name.c $(srcdir)/t_iov.c \ ++ $(srcdir)/t_imp_cred.c $(srcdir)/t_imp_name.c $(srcdir)/t_invalid.c \ ++ $(srcdir)/t_inq_cred.c $(srcdir)/t_inq_mechs_name.c $(srcdir)/t_iov.c \ + $(srcdir)/t_namingexts.c $(srcdir)/t_oid.c $(srcdir)/t_s4u.c \ + $(srcdir)/t_s4u2proxy_krb5.c $(srcdir)/t_saslname.c \ + $(srcdir)/t_spnego.c + + OBJS= ccinit.o ccrefresh.o common.o t_accname.o t_ccselect.o t_credstore.o \ + t_enctypes.o t_err.o t_export_cred.o t_export_name.o t_gssexts.o \ +- t_imp_cred.o t_imp_name.o t_inq_cred.o t_inq_mechs_name.o t_iov.o \ +- t_namingexts.o t_oid.o t_s4u.o t_s4u2proxy_krb5.o t_saslname.o \ ++ t_imp_cred.o t_imp_name.o t_invalid.o t_inq_cred.o t_inq_mechs_name.o \ ++ t_iov.o t_namingexts.o t_oid.o t_s4u.o t_s4u2proxy_krb5.o t_saslname.o \ + t_spnego.o + + COMMON_DEPS= common.o $(GSS_DEPLIBS) $(KRB5_BASE_DEPLIBS) + COMMON_LIBS= common.o $(GSS_LIBS) $(KRB5_BASE_LIBS) + + all:: ccinit ccrefresh t_accname t_ccselect t_credstore t_enctypes t_err \ +- t_export_cred t_export_name t_gssexts t_imp_cred t_imp_name \ ++ t_export_cred t_export_name t_gssexts t_imp_cred t_imp_name t_invalid \ + t_inq_cred t_inq_mechs_name t_iov t_namingexts t_oid t_s4u \ + t_s4u2proxy_krb5 t_saslname t_spnego + + check-unix:: t_oid ++ $(RUN_SETUP) $(VALGRIND) ./t_invalid + $(RUN_SETUP) $(VALGRIND) ./t_oid + + check-pytests:: ccinit ccrefresh t_accname t_ccselect t_credstore t_enctypes \ +@@ -70,6 +72,8 @@ t_imp_cred: t_imp_cred.o $(COMMON_DEPS) + $(CC_LINK) -o $@ t_imp_cred.o $(COMMON_LIBS) + t_imp_name: t_imp_name.o $(COMMON_DEPS) + $(CC_LINK) -o $@ t_imp_name.o $(COMMON_LIBS) ++t_invalid: t_invalid.o $(COMMON_DEPS) ++ $(CC_LINK) -o $@ t_invalid.o $(COMMON_LIBS) + t_inq_cred: t_inq_cred.o $(COMMON_DEPS) + $(CC_LINK) -o $@ t_inq_cred.o $(COMMON_LIBS) + t_inq_mechs_name: t_inq_mechs_name.o $(COMMON_DEPS) +@@ -94,5 +98,5 @@ t_spnego: t_spnego.o $(COMMON_DEPS) + clean:: + $(RM) ccinit ccrefresh t_accname t_ccselect t_credstore t_enctypes + $(RM) t_err t_export_cred t_export_name t_gssexts t_imp_cred t_imp_name +- $(RM) t_inq_cred t_inq_mechs_name t_iov t_namingexts t_oid t_s4u +- $(RM) t_s4u2proxy_krb5 t_saslname t_spnego ++ $(RM) t_invalid t_inq_cred t_inq_mechs_name t_iov t_namingexts t_oid ++ $(RM) t_s4u t_s4u2proxy_krb5 t_saslname t_spnego +diff --git a/src/tests/gssapi/deps b/src/tests/gssapi/deps +index c76d4ca..0a82d5d 100644 +--- a/src/tests/gssapi/deps ++++ b/src/tests/gssapi/deps +@@ -75,6 +75,24 @@ $(OUTPRE)t_imp_name.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \ + $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \ + common.h t_imp_name.c ++$(OUTPRE)t_invalid.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ ++ $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \ ++ $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \ ++ $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \ ++ $(BUILDTOP)/include/profile.h $(BUILDTOP)/lib/gssapi/generic/gssapi_err_generic.h \ ++ $(BUILDTOP)/lib/gssapi/krb5/gssapi_err_krb5.h $(COM_ERR_DEPS) \ ++ $(srcdir)/../../lib/gssapi/generic/gssapiP_generic.h \ ++ $(srcdir)/../../lib/gssapi/generic/gssapi_ext.h $(srcdir)/../../lib/gssapi/generic/gssapi_generic.h \ ++ $(srcdir)/../../lib/gssapi/krb5/gssapiP_krb5.h $(srcdir)/../../lib/gssapi/krb5/gssapi_krb5.h \ ++ $(srcdir)/../../lib/gssapi/mechglue/mechglue.h $(srcdir)/../../lib/gssapi/mechglue/mglueP.h \ ++ $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \ ++ $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \ ++ $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \ ++ $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \ ++ $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \ ++ $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \ ++ $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \ ++ common.h t_invalid.c + $(OUTPRE)t_inq_cred.$(OBJEXT): $(BUILDTOP)/include/gssapi/gssapi.h \ + $(BUILDTOP)/include/gssapi/gssapi_ext.h $(BUILDTOP)/include/gssapi/gssapi_krb5.h \ + $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \ +diff --git a/src/tests/gssapi/t_invalid.c b/src/tests/gssapi/t_invalid.c +new file mode 100644 +index 0000000..5c8ddac +--- /dev/null ++++ b/src/tests/gssapi/t_invalid.c +@@ -0,0 +1,429 @@ ++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ++/* tests/gssapi/t_invalid.c - Invalid message token regression tests */ ++/* ++ * Copyright (C) 2014 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. ++ */ ++ ++/* ++ * This file contains regression tests for some GSSAPI krb5 invalid per-message ++ * token vulnerabilities. ++ * ++ * 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.) ++ * ++ * 2. 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. ++ * ++ * 3. 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 ++ * 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. ++ * ++ * Vulnerabilities #1 and #2 also apply to IOV unwrap, although tokens with ++ * fewer than 16 bytes after the ASN.1 header will be rejected. Vulnerability ++ * #2 can only be robustly detected using a memory-checking environment such as ++ * valgrind. ++ */ ++ ++#include "k5-int.h" ++#include "common.h" ++#include "mglueP.h" ++#include "gssapiP_krb5.h" ++ ++/* ++ * The following samples contain context parameters and otherwise valid seal ++ * tokens where the plain text is padded with byte value 100 instead of the ++ * proper value 1. ++ */ ++struct test { ++ krb5_enctype enctype; ++ krb5_enctype encseq_enctype; ++ int sealalg; ++ int signalg; ++ size_t cksum_size; ++ size_t keylen; ++ const char *keydata; ++ size_t toklen; ++ const char *token; ++} tests[] = { ++ { ++ ENCTYPE_DES_CBC_CRC, ENCTYPE_DES_CBC_RAW, ++ SEAL_ALG_DES, SGN_ALG_DES_MAC_MD5, 8, ++ 8, ++ "\x26\xEC\xBA\xB6\xFE\xBA\x91\xCE", ++ 53, ++ "\x60\x33\x06\x09\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x02\x01\x00" ++ "\x00\x00\x00\xFF\xFF\xF0\x0B\x90\x7B\xC4\xFC\xEB\xF4\x84\x9C\x5A" ++ "\xA8\x56\x41\x3E\xE1\x62\xEE\x38\xD1\x34\x9A\xE3\xFB\xC9\xFD\x0A" ++ "\xDC\x83\xE1\x4A\xE4" ++ }, ++ { ++ ENCTYPE_DES3_CBC_SHA1, ENCTYPE_DES3_CBC_RAW, ++ SEAL_ALG_DES3KD, SGN_ALG_HMAC_SHA1_DES3_KD, 20, ++ 24, ++ "\x4F\xEA\x19\x19\x5E\x0E\x10\xDF\x3D\x29\xB5\x13\x8F\x01\xC7\xA7" ++ "\x92\x3D\x38\xF7\x26\x73\x0D\x6D", ++ 65, ++ "\x60\x3F\x06\x09\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x02\x01\x04" ++ "\x00\x02\x00\xFF\xFF\xEB\xF3\x9A\x89\x24\x57\xB8\x63\x95\x25\xE8" ++ "\x6E\x8E\x79\xE6\x2E\xCA\xD3\xFF\x57\x9F\x8C\xAB\xEF\xDD\x28\x10" ++ "\x2F\x93\x21\x2E\xF2\x52\xB6\x6F\xA8\xBB\x8A\x6D\xAA\x6F\xB7\xF4\xD4" ++ }, ++ { ++ ENCTYPE_ARCFOUR_HMAC, ENCTYPE_ARCFOUR_HMAC, ++ SEAL_ALG_MICROSOFT_RC4, SGN_ALG_HMAC_MD5, 8, ++ 16, ++ "\x66\x64\x41\x64\x55\x78\x21\xD0\xD0\xFD\x05\x6A\xFF\x6F\xE8\x09", ++ 53, ++ "\x60\x33\x06\x09\x2A\x86\x48\x86\xF7\x12\x01\x02\x02\x02\x01\x11" ++ "\x00\x10\x00\xFF\xFF\x35\xD4\x79\xF3\x8C\x47\x8F\x6E\x23\x6F\x3E" ++ "\xCC\x5E\x57\x5C\x6A\x89\xF0\xA2\x03\x4F\x0B\x51\x11\xEE\x89\x7E" ++ "\xD6\xF6\xB5\xD6\x51" ++ } ++}; ++ ++/* Fake up enough of a CFX GSS context for gss_unwrap, using an AES key. */ ++static gss_ctx_id_t ++make_fake_cfx_context() ++{ ++ 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->established = 1; ++ kgctx->proto = 1; ++ if (g_order_init(&kgctx->seqstate, 0, 0, 0, 0) != 0) ++ abort(); ++ kgctx->mech_used = &mech_krb5; ++ 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(); ++ ++ uctx = calloc(1, sizeof(*uctx)); ++ if (uctx == NULL) ++ abort(); ++ uctx->mech_type = &mech_krb5; ++ uctx->internal_ctx_id = (gss_ctx_id_t)kgctx; ++ return (gss_ctx_id_t)uctx; ++} ++ ++/* Fake up enough of a GSS context for gss_unwrap, using keys from test. */ ++static gss_ctx_id_t ++make_fake_context(const struct test *test) ++{ ++ gss_union_ctx_id_t uctx; ++ krb5_gss_ctx_id_t kgctx; ++ krb5_keyblock kb; ++ unsigned char encbuf[8]; ++ size_t i; ++ ++ kgctx = calloc(1, sizeof(*kgctx)); ++ if (kgctx == NULL) ++ abort(); ++ kgctx->established = 1; ++ if (g_order_init(&kgctx->seqstate, 0, 0, 0, 0) != 0) ++ abort(); ++ kgctx->mech_used = &mech_krb5; ++ kgctx->sealalg = test->sealalg; ++ kgctx->signalg = test->signalg; ++ kgctx->cksum_size = test->cksum_size; ++ ++ kb.enctype = test->enctype; ++ kb.length = test->keylen; ++ kb.contents = (unsigned char *)test->keydata; ++ if (krb5_k_create_key(NULL, &kb, &kgctx->subkey) != 0) ++ abort(); ++ ++ kb.enctype = test->encseq_enctype; ++ if (krb5_k_create_key(NULL, &kb, &kgctx->seq) != 0) ++ abort(); ++ ++ if (kb.enctype == ENCTYPE_DES_CBC_RAW) { ++ for (i = 0; i < 8; i++) ++ encbuf[i] = kb.contents[i] ^ 0xF0; ++ kb.contents = encbuf; ++ } ++ if (krb5_k_create_key(NULL, &kb, &kgctx->enc) != 0) ++ abort(); ++ ++ uctx = calloc(1, sizeof(*uctx)); ++ if (uctx == NULL) ++ abort(); ++ uctx->mech_type = &mech_krb5; ++ uctx->internal_ctx_id = (gss_ctx_id_t)kgctx; ++ return (gss_ctx_id_t)uctx; ++} ++ ++/* Free a context created by make_fake_context. */ ++static void ++free_fake_context(gss_ctx_id_t ctx) ++{ ++ gss_union_ctx_id_t uctx = (gss_union_ctx_id_t)ctx; ++ krb5_gss_ctx_id_t kgctx = (krb5_gss_ctx_id_t)uctx->internal_ctx_id; ++ ++ free(kgctx->seqstate); ++ krb5_k_free_key(NULL, kgctx->subkey); ++ krb5_k_free_key(NULL, kgctx->seq); ++ krb5_k_free_key(NULL, kgctx->enc); ++ free(kgctx); ++ free(uctx); ++} ++ ++/* Prefix a token (starting at the two-byte ID) with an ASN.1 header and return ++ * it in an allocated block to facilitate checking by valgrind or similar. */ ++static void ++make_token(unsigned char *token, size_t len, gss_buffer_t out) ++{ ++ char *wrapped; ++ ++ assert(mech_krb5.length == 9); ++ assert(len + 11 < 128); ++ wrapped = malloc(len + 13); ++ if (wrapped == NULL) ++ abort(); ++ wrapped[0] = 0x60; ++ wrapped[1] = len + 11; ++ wrapped[2] = 0x06; ++ wrapped[3] = 9; ++ memcpy(wrapped + 4, mech_krb5.elements, 9); ++ memcpy(wrapped + 13, token, len); ++ out->length = len + 13; ++ out->value = wrapped; ++} ++ ++/* Unwrap a superficially valid RFC 1964 token with a CFX-only context, with ++ * regular and IOV unwrap. */ ++static void ++test_bogus_1964_token(gss_ctx_id_t ctx) ++{ ++ OM_uint32 minor, major; ++ unsigned char tokbuf[128]; ++ gss_buffer_desc in, out; ++ gss_iov_buffer_desc iov; ++ ++ store_16_be(KG_TOK_SIGN_MSG, tokbuf); ++ store_16_le(SGN_ALG_DES_MAC_MD5, tokbuf + 2); ++ store_16_le(SEAL_ALG_NONE, tokbuf + 4); ++ store_16_le(0xFFFF, tokbuf + 6); ++ memset(tokbuf + 8, 0, 16); ++ make_token(tokbuf, 24, &in); ++ ++ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL); ++ if (major != GSS_S_DEFECTIVE_TOKEN) ++ abort(); ++ (void)gss_release_buffer(&minor, &out); ++ ++ iov.type = GSS_IOV_BUFFER_TYPE_HEADER; ++ iov.buffer = in; ++ major = gss_unwrap_iov(&minor, ctx, NULL, NULL, &iov, 1); ++ if (major != GSS_S_DEFECTIVE_TOKEN) ++ abort(); ++ ++ free(in.value); ++} ++ ++/* Process wrap and MIC tokens with incomplete headers. */ ++static void ++test_short_header(gss_ctx_id_t ctx) ++{ ++ OM_uint32 minor, major; ++ unsigned char tokbuf[128]; ++ gss_buffer_desc in, out, empty = GSS_C_EMPTY_BUFFER; ++ ++ /* Seal token, 2-24 bytes */ ++ store_16_be(KG_TOK_SEAL_MSG, tokbuf); ++ make_token(tokbuf, 2, &in); ++ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL); ++ if (major != GSS_S_DEFECTIVE_TOKEN) ++ abort(); ++ free(in.value); ++ (void)gss_release_buffer(&minor, &out); ++ ++ /* Sign token, 2-24 bytes */ ++ store_16_be(KG_TOK_SIGN_MSG, tokbuf); ++ make_token(tokbuf, 2, &in); ++ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL); ++ if (major != GSS_S_DEFECTIVE_TOKEN) ++ abort(); ++ free(in.value); ++ (void)gss_release_buffer(&minor, &out); ++ ++ /* MIC token, 2-24 bytes */ ++ store_16_be(KG_TOK_MIC_MSG, tokbuf); ++ make_token(tokbuf, 2, &in); ++ major = gss_verify_mic(&minor, ctx, &empty, &in, NULL); ++ if (major != GSS_S_DEFECTIVE_TOKEN) ++ abort(); ++ free(in.value); ++} ++ ++/* Process wrap and MIC tokens with incomplete headers. */ ++static void ++test_short_header_iov(gss_ctx_id_t ctx, const struct test *test) ++{ ++ OM_uint32 minor, major; ++ unsigned char tokbuf[128]; ++ gss_iov_buffer_desc iov; ++ ++ /* IOV seal token, 16-23 bytes */ ++ store_16_be(KG_TOK_SEAL_MSG, tokbuf); ++ store_16_le(test->signalg, tokbuf + 2); ++ store_16_le(test->sealalg, tokbuf + 4); ++ store_16_be(0xFFFF, tokbuf + 6); ++ memset(tokbuf + 8, 0, 8); ++ iov.type = GSS_IOV_BUFFER_TYPE_HEADER; ++ make_token(tokbuf, 16, &iov.buffer); ++ major = gss_unwrap_iov(&minor, ctx, NULL, NULL, &iov, 1); ++ if (major != GSS_S_DEFECTIVE_TOKEN) ++ abort(); ++ free(iov.buffer.value); ++ ++ /* IOV sign token, 16-23 bytes */ ++ store_16_be(KG_TOK_SIGN_MSG, tokbuf); ++ store_16_le(test->signalg, tokbuf + 2); ++ store_16_le(SEAL_ALG_NONE, tokbuf + 4); ++ store_16_le(0xFFFF, tokbuf + 6); ++ memset(tokbuf + 8, 0, 8); ++ iov.type = GSS_IOV_BUFFER_TYPE_HEADER; ++ make_token(tokbuf, 16, &iov.buffer); ++ major = gss_unwrap_iov(&minor, ctx, NULL, NULL, &iov, 1); ++ if (major != GSS_S_DEFECTIVE_TOKEN) ++ abort(); ++ free(iov.buffer.value); ++ ++ /* IOV MIC token, 16-23 bytes */ ++ store_16_be(KG_TOK_MIC_MSG, tokbuf); ++ store_16_be(test->signalg, tokbuf + 2); ++ store_16_le(SEAL_ALG_NONE, tokbuf + 4); ++ store_16_le(0xFFFF, tokbuf + 6); ++ memset(tokbuf + 8, 0, 8); ++ iov.type = GSS_IOV_BUFFER_TYPE_MIC_TOKEN; ++ make_token(tokbuf, 16, &iov.buffer); ++ major = gss_verify_mic_iov(&minor, ctx, NULL, &iov, 1); ++ if (major != GSS_S_DEFECTIVE_TOKEN) ++ abort(); ++ free(iov.buffer.value); ++} ++ ++/* Process wrap and MIC tokens with incomplete checksums. */ ++static void ++test_short_checksum(gss_ctx_id_t ctx, const struct test *test) ++{ ++ OM_uint32 minor, major; ++ unsigned char tokbuf[128]; ++ gss_buffer_desc in, out, empty = GSS_C_EMPTY_BUFFER; ++ ++ /* Can only do this with the DES3 checksum, as we can't easily get past ++ * retrieving the sequence number when the checksum is only eight bytes. */ ++ if (test->cksum_size <= 8) ++ return; ++ /* Seal token, fewer than 16 + cksum_size bytes. Use the token from the ++ * test data to get a valid sequence number. */ ++ make_token((unsigned char *)test->token + 13, 24, &in); ++ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL); ++ if (major != GSS_S_DEFECTIVE_TOKEN) ++ abort(); ++ free(in.value); ++ (void)gss_release_buffer(&minor, &out); ++ ++ /* Sign token, fewer than 16 + cksum_size bytes. */ ++ memcpy(tokbuf, test->token + 13, 24); ++ store_16_be(KG_TOK_SIGN_MSG, tokbuf); ++ store_16_le(SEAL_ALG_NONE, tokbuf + 4); ++ make_token(tokbuf, 24, &in); ++ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL); ++ if (major != GSS_S_DEFECTIVE_TOKEN) ++ abort(); ++ free(in.value); ++ (void)gss_release_buffer(&minor, &out); ++ ++ /* MIC token, fewer than 16 + cksum_size bytes. */ ++ memcpy(tokbuf, test->token + 13, 24); ++ store_16_be(KG_TOK_MIC_MSG, tokbuf); ++ store_16_le(SEAL_ALG_NONE, tokbuf + 4); ++ make_token(tokbuf, 24, &in); ++ major = gss_verify_mic(&minor, ctx, &empty, &in, NULL); ++ if (major != GSS_S_DEFECTIVE_TOKEN) ++ abort(); ++ free(in.value); ++} ++ ++/* Unwrap a token with a bogus padding byte in the decrypted ciphertext. */ ++static void ++test_bad_pad(gss_ctx_id_t ctx, const struct test *test) ++{ ++ OM_uint32 minor, major; ++ gss_buffer_desc in, out; ++ ++ in.length = test->toklen; ++ in.value = (char *)test->token; ++ major = gss_unwrap(&minor, ctx, &in, &out, NULL, NULL); ++ if (major != GSS_S_BAD_SIG) ++ abort(); ++ (void)gss_release_buffer(&minor, &out); ++} ++ ++int ++main(int argc, char **argv) ++{ ++ gss_ctx_id_t ctx; ++ size_t i; ++ ++ ctx = make_fake_cfx_context(); ++ test_bogus_1964_token(ctx); ++ free_fake_context(ctx); ++ ++ for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) { ++ ctx = make_fake_context(&tests[i]); ++ test_short_header(ctx); ++ test_short_header_iov(ctx, &tests[i]); ++ test_short_checksum(ctx, &tests[i]); ++ test_bad_pad(ctx, &tests[i]); ++ free_fake_context(ctx); ++ } ++ ++ return 0; ++} diff --git a/krb5-1.12-CVE-2014-4341_4342.patch b/krb5-1.12-CVE-2014-4341_4342.patch new file mode 100644 index 0000000..c7719d2 --- /dev/null +++ b/krb5-1.12-CVE-2014-4341_4342.patch @@ -0,0 +1,163 @@ +commit e6ae703ae597d798e310368d52b8f38ee11c6a73 +Author: Greg Hudson +Date: Thu Jun 19 13:49:16 2014 -0400 + + Handle invalid RFC 1964 tokens [CVE-2014-4341...] + + Detect the following cases which would otherwise cause invalid memory + accesses and/or integer underflow: + + * An RFC 1964 token being processed by an RFC 4121-only context + [CVE-2014-4342] + + * A header with fewer than 22 bytes after the token ID or an + incomplete checksum [CVE-2014-4341 CVE-2014-4342] + + * A ciphertext shorter than the confounder [CVE-2014-4341] + + * A declared padding length longer than the plaintext [CVE-2014-4341] + + If we detect a bad pad byte, continue on to compute the checksum to + avoid creating a padding oracle, but treat the checksum as invalid + even if it compares equal. + + CVE-2014-4341: + + In MIT krb5, an unauthenticated remote attacker with the ability to + inject packets into a legitimately established GSSAPI application + session can cause a program crash due to invalid memory references + when attempting to read beyond the end of a buffer. + + CVSSv2 Vector: AV:N/AC:M/Au:N/C:N/I:N/A:P/E:POC/RL:OF/RC:C + + CVE-2014-4342: + + In MIT krb5 releases krb5-1.7 and later, an unauthenticated remote + attacker with the ability to inject packets into a legitimately + established GSSAPI application session can cause a program crash due + to invalid memory references when reading beyond the end of a buffer + or by causing a null pointer dereference. + + CVSSv2 Vector: AV:N/AC:M/Au:N/C:N/I:N/A:P/E:POC/RL:OF/RC:C + + [tlyu@mit.edu: CVE summaries, CVSS] + + (cherry picked from commit fb99962cbd063ac04c9a9d2cc7c75eab73f3533d) + + ticket: 7949 + version_fixed: 1.12.2 + status: resolved + +diff --git a/src/lib/gssapi/krb5/k5unseal.c b/src/lib/gssapi/krb5/k5unseal.c +index ca21d43..b65c83c 100644 +--- a/src/lib/gssapi/krb5/k5unseal.c ++++ b/src/lib/gssapi/krb5/k5unseal.c +@@ -74,6 +74,7 @@ kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, message_buffer, + int conflen = 0; + int signalg; + int sealalg; ++ int bad_pad = 0; + gss_buffer_desc token; + krb5_checksum cksum; + krb5_checksum md5cksum; +@@ -86,6 +87,7 @@ kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, message_buffer, + krb5_ui_4 seqnum; + OM_uint32 retval; + size_t sumlen; ++ size_t padlen; + krb5_keyusage sign_usage = KG_USAGE_SIGN; + + if (toktype == KG_TOK_SEAL_MSG) { +@@ -93,18 +95,23 @@ kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, message_buffer, + message_buffer->value = NULL; + } + +- /* get the sign and seal algorithms */ +- +- signalg = ptr[0] + (ptr[1]<<8); +- sealalg = ptr[2] + (ptr[3]<<8); +- + /* Sanity checks */ + +- if ((ptr[4] != 0xff) || (ptr[5] != 0xff)) { ++ if (ctx->seq == NULL) { ++ /* ctx was established using a newer enctype, and cannot process RFC ++ * 1964 tokens. */ ++ *minor_status = 0; ++ return GSS_S_DEFECTIVE_TOKEN; ++ } ++ ++ if ((bodysize < 22) || (ptr[4] != 0xff) || (ptr[5] != 0xff)) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } + ++ signalg = ptr[0] + (ptr[1]<<8); ++ sealalg = ptr[2] + (ptr[3]<<8); ++ + if ((toktype != KG_TOK_SEAL_MSG) && + (sealalg != 0xffff)) { + *minor_status = 0; +@@ -153,6 +160,11 @@ kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, message_buffer, + return GSS_S_DEFECTIVE_TOKEN; + } + ++ if ((size_t)bodysize < 14 + cksum_len) { ++ *minor_status = 0; ++ return GSS_S_DEFECTIVE_TOKEN; ++ } ++ + /* get the token parameters */ + + if ((code = kg_get_seq_num(context, ctx->seq, ptr+14, ptr+6, &direction, +@@ -207,7 +219,20 @@ kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, message_buffer, + plainlen = tmsglen; + + conflen = kg_confounder_size(context, ctx->enc->keyblock.enctype); +- token.length = tmsglen - conflen - plain[tmsglen-1]; ++ if (tmsglen < conflen) { ++ if (sealalg != 0xffff) ++ xfree(plain); ++ *minor_status = 0; ++ return(GSS_S_DEFECTIVE_TOKEN); ++ } ++ padlen = plain[tmsglen - 1]; ++ if (tmsglen - conflen < padlen) { ++ /* Don't error out yet, to avoid padding oracle attacks. We will ++ * treat this as a checksum failure later on. */ ++ padlen = 0; ++ bad_pad = 1; ++ } ++ token.length = tmsglen - conflen - padlen; + + if (token.length) { + if ((token.value = (void *) gssalloc_malloc(token.length)) == NULL) { +@@ -403,7 +428,7 @@ kg_unseal_v1(context, minor_status, ctx, ptr, bodysize, message_buffer, + + /* compare the computed checksum against the transmitted checksum */ + +- if (code) { ++ if (code || bad_pad) { + if (toktype == KG_TOK_SEAL_MSG) + gssalloc_free(token.value); + *minor_status = 0; +diff --git a/src/lib/gssapi/krb5/k5unsealiov.c b/src/lib/gssapi/krb5/k5unsealiov.c +index e34bda4..8d6a2da 100644 +--- a/src/lib/gssapi/krb5/k5unsealiov.c ++++ b/src/lib/gssapi/krb5/k5unsealiov.c +@@ -69,7 +69,14 @@ kg_unseal_v1_iov(krb5_context context, + return GSS_S_DEFECTIVE_TOKEN; + } + +- if (header->buffer.length < token_wrapper_len + 14) { ++ if (ctx->seq == NULL) { ++ /* ctx was established using a newer enctype, and cannot process RFC ++ * 1964 tokens. */ ++ *minor_status = 0; ++ return GSS_S_DEFECTIVE_TOKEN; ++ } ++ ++ if (header->buffer.length < token_wrapper_len + 22) { + *minor_status = 0; + return GSS_S_DEFECTIVE_TOKEN; + } diff --git a/krb5.spec b/krb5.spec index f6ad6af..9753759 100644 --- a/krb5.spec +++ b/krb5.spec @@ -41,7 +41,7 @@ Summary: The Kerberos network authentication system Name: krb5 Version: 1.12.1 -Release: 9%{?dist} +Release: 10%{?dist} # Maybe we should explode from the now-available-to-everybody tarball instead? # http://web.mit.edu/kerberos/dist/krb5/1.12/krb5-1.12.1-signed.tar Source0: krb5-%{version}.tar.gz @@ -104,6 +104,8 @@ Patch142: krb5-master-move-otp-sockets.patch Patch143: krb5-master-spnego-preserve-oid.patch Patch144: krb5-1.12-tcl86.patch Patch145: krb5-master-mechd.patch +Patch146: krb5-1.12-CVE-2014-4341_4342.patch +Patch147: krb5-1.12-CVE-2014-4341_4342-tests.patch Patch201: 0001-Don-t-try-to-stat-not-on-disk-ccache-residuals.patch Patch202: 0002-Use-an-in-memory-cache-until-we-need-the-target-s.patch Patch203: 0003-Learn-to-destroy-the-ccache-we-re-copying-from.patch @@ -356,6 +358,8 @@ ln -s NOTICE LICENSE %patch143 -p1 -b .spnego-preserve-oid %patch144 -p1 -b .tcl86 %patch145 -p1 -b .master-mechd +%patch146 -p1 -b .CVE-2014-4341_4342 +%patch147 -p1 -b .CVE-2014-4341_4342 # Take the execute bit off of documentation. chmod -x doc/krb5-protocol/*.txt doc/ccapi/*.html @@ -1030,6 +1034,10 @@ exit 0 %{_sbindir}/uuserver %changelog +* Mon Jul 7 2014 Nalin Dahyabhai - 1.12.1-10 +- pull in fix for denial of service by injection of malformed GSSAPI tokens + (CVE-2014-4341, CVE-2014-4342, #1116181) + * Tue Jun 24 2014 Nalin Dahyabhai - 1.12.1-9 - pull in changes from upstream which add processing of the contents of /etc/gss/mech.d/*.conf when loading GSS modules (#1102839)