diff --git a/SOURCES/Add-a-hash-table-implementation-to-libkrb5support.patch b/SOURCES/Add-a-hash-table-implementation-to-libkrb5support.patch new file mode 100644 index 0000000..21ce64d --- /dev/null +++ b/SOURCES/Add-a-hash-table-implementation-to-libkrb5support.patch @@ -0,0 +1,657 @@ +From b3f5d3b0542ae314acfb94e1bc5a8f22201b8ac3 Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Sat, 4 Aug 2018 20:11:09 -0400 +Subject: [PATCH] Add a hash table implementation to libkrb5support + +(cherry picked from commit 09e814fe47f5ceeb35bee15ced6e346db8a5e81d) +[rharwood@redhat.com: gitignore, no utf16] +--- + src/include/k5-hashtab.h | 79 ++++++ + src/util/support/Makefile.in | 15 +- + src/util/support/deps | 11 + + src/util/support/hashtab.c | 243 ++++++++++++++++++ + src/util/support/libkrb5support-fixed.exports | 5 + + src/util/support/t_hashtab.c | 176 +++++++++++++ + 6 files changed, 526 insertions(+), 3 deletions(-) + create mode 100644 src/include/k5-hashtab.h + create mode 100644 src/util/support/hashtab.c + create mode 100644 src/util/support/t_hashtab.c + +diff --git a/src/include/k5-hashtab.h b/src/include/k5-hashtab.h +new file mode 100644 +index 000000000..dc0ef3613 +--- /dev/null ++++ b/src/include/k5-hashtab.h +@@ -0,0 +1,79 @@ ++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ++/* include/k5-hash.h - hash table interface definitions */ ++/* ++ * Copyright (C) 2018 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 declarations for a simple hash table using siphash. Some ++ * limitations which might need to be addressed in the future: ++ * ++ * - The table does not manage caller memory. This limitation could be ++ * addressed by adding an optional free callback to k5_hashtab_create(), to ++ * be called by k5_hashtab_free() and k5_hashtab_remove(). ++ * ++ * - There is no way to iterate over a hash table. ++ * ++ * - k5_hashtab_add() does not check for duplicate entries. ++ */ ++ ++#ifndef K5_HASH_H ++#define K5_HASH_H ++ ++#define K5_HASH_SEED_LEN 16 ++ ++struct k5_hashtab; ++ ++/* ++ * Create a new hash table in *ht_out. seed must point to random bytes if keys ++ * might be under the control of an attacker; otherwise it may be NULL. ++ * initial_buckets controls the initial allocation of hash buckets; pass zero ++ * to use a default value. The number of hash buckets will be doubled as the ++ * number of entries increases. Return 0 on success, ENOMEM on failure. ++ */ ++int k5_hashtab_create(const uint8_t seed[K5_HASH_SEED_LEN], ++ size_t initial_buckets, struct k5_hashtab **ht_out); ++ ++/* Release the memory used by a hash table. Keys and values are the caller's ++ * responsibility. */ ++void k5_hashtab_free(struct k5_hashtab *ht); ++ ++/* Add an entry to a hash table. key and val must remain valid until the entry ++ * is removed or the hash table is freed. The caller must avoid duplicates. */ ++int k5_hashtab_add(struct k5_hashtab *ht, const void *key, size_t klen, ++ void *val); ++ ++/* Remove an entry from a hash table by key. Does not free key or the ++ * associated value. Return 1 if the key was found and removed, 0 if not. */ ++int k5_hashtab_remove(struct k5_hashtab *ht, const void *key, size_t klen); ++ ++/* Retrieve a value from a hash table by key. */ ++void *k5_hashtab_get(struct k5_hashtab *ht, const void *key, size_t klen); ++ ++#endif /* K5_HASH_H */ +diff --git a/src/util/support/Makefile.in b/src/util/support/Makefile.in +index b3576f0b7..12797068f 100644 +--- a/src/util/support/Makefile.in ++++ b/src/util/support/Makefile.in +@@ -83,6 +83,7 @@ STLIBOBJS= \ + base64.o \ + json.o \ + hex.o \ ++ hashtab.o \ + bcmp.o \ + strerror_r.o \ + dir_filenames.o \ +@@ -110,6 +111,7 @@ LIBOBJS= \ + $(OUTPRE)base64.$(OBJEXT) \ + $(OUTPRE)json.$(OBJEXT) \ + $(OUTPRE)hex.$(OBJEXT) \ ++ $(OUTPRE)hashtab.$(OBJEXT) \ + $(OUTPRE)bcmp.$(OBJEXT) \ + $(OUTPRE)strerror_r.$(OBJEXT) \ + $(OUTPRE)dir_filenames.$(OBJEXT) \ +@@ -142,11 +144,13 @@ SRCS=\ + $(srcdir)/t_path.c \ + $(srcdir)/t_json.c \ + $(srcdir)/t_hex.c \ ++ $(srcdir)/t_hashtab.c \ + $(srcdir)/zap.c \ + $(srcdir)/path.c \ + $(srcdir)/base64.c \ + $(srcdir)/json.c \ + $(srcdir)/hex.c \ ++ $(srcdir)/hashtab.c \ + $(srcdir)/bcmp.c \ + $(srcdir)/strerror_r.c \ + $(srcdir)/dir_filenames.c \ +@@ -225,13 +229,17 @@ t_json: $(T_JSON_OBJS) + t_hex: t_hex.o hex.o + $(CC_LINK) -o $@ t_hex.o hex.o + ++t_hashtab: t_hashtab.o ++ $(CC_LINK) -o $@ t_hashtab.o ++ + t_unal: t_unal.o + $(CC_LINK) -o t_unal t_unal.o + + t_utf8: t_utf8.o utf8.o + $(CC_LINK) -o t_utf8 t_utf8.o utf8.o + +-TEST_PROGS= t_k5buf t_path t_path_win t_base64 t_json t_hex t_unal t_utf8 ++TEST_PROGS= t_k5buf t_path t_path_win t_base64 t_json t_hex t_hashtab t_unal \ ++ t_utf8 + + check-unix: $(TEST_PROGS) + ./t_k5buf +@@ -240,14 +248,15 @@ check-unix: $(TEST_PROGS) + ./t_base64 + ./t_json + ./t_hex ++ ./t_hashtab + ./t_unal + ./t_utf8 + + clean: + $(RM) t_k5buf.o t_k5buf t_unal.o t_unal path_win.o path_win + $(RM) t_path_win.o t_path_win t_path.o t_path t_base64.o t_base64 +- $(RM) t_json.o t_json t_hex.o t_hex libkrb5support.exports +- $(RM) t_utf8.o t_utf8 ++ $(RM) t_json.o t_json t_hex.o t_hex t_hashtab.o t_hashtab ++ $(RM) t_utf8.o t_utf8 libkrb5support.exports + + @lib_frag@ + @libobj_frag@ +diff --git a/src/util/support/deps b/src/util/support/deps +index 551843357..d43baea13 100644 +--- a/src/util/support/deps ++++ b/src/util/support/deps +@@ -65,6 +65,10 @@ t_json.so t_json.po $(OUTPRE)t_json.$(OBJEXT): $(top_srcdir)/include/k5-json.h \ + t_hex.so t_hex.po $(OUTPRE)t_hex.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h t_hex.c ++t_hashtab.so t_hashtab.po $(OUTPRE)t_hashtab.$(OBJEXT): \ ++ $(BUILDTOP)/include/autoconf.h $(top_srcdir)/include/k5-hashtab.h \ ++ $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-queue.h \ ++ $(top_srcdir)/include/k5-thread.h hashtab.c t_hashtab.c + zap.so zap.po $(OUTPRE)zap.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ + zap.c +@@ -81,12 +85,19 @@ json.so json.po $(OUTPRE)json.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + hex.so hex.po $(OUTPRE)hex.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h hex.c ++hashtab.so hashtab.po $(OUTPRE)hashtab.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ ++ $(top_srcdir)/include/k5-hashtab.h $(top_srcdir)/include/k5-platform.h \ ++ $(top_srcdir)/include/k5-queue.h $(top_srcdir)/include/k5-thread.h \ ++ hashtab.c + bcmp.so bcmp.po $(OUTPRE)bcmp.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ + bcmp.c + strerror_r.so strerror_r.po $(OUTPRE)strerror_r.$(OBJEXT): \ + $(BUILDTOP)/include/autoconf.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h strerror_r.c ++dir_filenames.so dir_filenames.po $(OUTPRE)dir_filenames.$(OBJEXT): \ ++ $(BUILDTOP)/include/autoconf.h $(top_srcdir)/include/k5-platform.h \ ++ $(top_srcdir)/include/k5-thread.h dir_filenames.c + t_utf8.so t_utf8.po $(OUTPRE)t_utf8.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ + $(top_srcdir)/include/k5-utf8.h t_utf8.c +diff --git a/src/util/support/hashtab.c b/src/util/support/hashtab.c +new file mode 100644 +index 000000000..e04e491b2 +--- /dev/null ++++ b/src/util/support/hashtab.c +@@ -0,0 +1,243 @@ ++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ++/* util/support/hash.c - hash table implementation */ ++/* ++ * Copyright (C) 2018 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. ++ */ ++ ++#include "k5-platform.h" ++#include "k5-hashtab.h" ++#include "k5-queue.h" ++ ++struct entry { ++ const void *key; ++ size_t klen; ++ void *val; ++ K5_SLIST_ENTRY(entry) next; ++}; ++ ++struct k5_hashtab { ++ uint64_t k0; ++ uint64_t k1; ++ size_t nbuckets; ++ size_t nentries; ++ K5_SLIST_HEAD(bucket_list, entry) *buckets; ++}; ++ ++/* Return x rotated to the left by r bits. */ ++static inline uint64_t ++rotl64(uint64_t x, int r) ++{ ++ return (x << r) | (x >> (64 - r)); ++} ++ ++static inline void ++sipround(uint64_t *v0, uint64_t *v1, uint64_t *v2, uint64_t *v3) ++{ ++ *v0 += *v1; ++ *v2 += *v3; ++ *v1 = rotl64(*v1, 13) ^ *v0; ++ *v3 = rotl64(*v3, 16) ^ *v2; ++ *v0 = rotl64(*v0, 32); ++ *v2 += *v1; ++ *v0 += *v3; ++ *v1 = rotl64(*v1, 17) ^ *v2; ++ *v3 = rotl64(*v3, 21) ^ *v0; ++ *v2 = rotl64(*v2, 32); ++} ++ ++/* SipHash-2-4 from https://131002.net/siphash/siphash.pdf (Jean-Philippe ++ * Aumasson and Daniel J. Bernstein) */ ++static uint64_t ++siphash24(const uint8_t *data, size_t len, uint64_t k0, uint64_t k1) ++{ ++ uint64_t v0 = k0 ^ 0x736F6D6570736575; ++ uint64_t v1 = k1 ^ 0x646F72616E646F6D; ++ uint64_t v2 = k0 ^ 0x6C7967656E657261; ++ uint64_t v3 = k1 ^ 0x7465646279746573; ++ uint64_t mi; ++ const uint8_t *p, *end = data + (len - len % 8); ++ uint8_t last[8] = { 0 }; ++ ++ /* Process each full 8-byte chunk of data. */ ++ for (p = data; p < end; p += 8) { ++ mi = load_64_le(p); ++ v3 ^= mi; ++ sipround(&v0, &v1, &v2, &v3); ++ sipround(&v0, &v1, &v2, &v3); ++ v0 ^= mi; ++ } ++ ++ /* Process the last 0-7 bytes followed by the length mod 256. */ ++ memcpy(last, end, len % 8); ++ last[7] = len & 0xFF; ++ mi = load_64_le(last); ++ v3 ^= mi; ++ sipround(&v0, &v1, &v2, &v3); ++ sipround(&v0, &v1, &v2, &v3); ++ v0 ^= mi; ++ ++ /* Finalize. */ ++ v2 ^= 0xFF; ++ sipround(&v0, &v1, &v2, &v3); ++ sipround(&v0, &v1, &v2, &v3); ++ sipround(&v0, &v1, &v2, &v3); ++ sipround(&v0, &v1, &v2, &v3); ++ return v0 ^ v1 ^ v2 ^ v3; ++} ++ ++int ++k5_hashtab_create(const uint8_t seed[K5_HASH_SEED_LEN], size_t initial_buckets, ++ struct k5_hashtab **ht_out) ++{ ++ struct k5_hashtab *ht; ++ ++ *ht_out = NULL; ++ ++ ht = malloc(sizeof(*ht)); ++ if (ht == NULL) ++ return ENOMEM; ++ ++ if (seed != NULL) { ++ ht->k0 = load_64_le(seed); ++ ht->k1 = load_64_le(seed + 8); ++ } else { ++ ht->k0 = ht->k1 = 0; ++ } ++ ht->nbuckets = (initial_buckets > 0) ? initial_buckets : 64; ++ ht->nentries = 0; ++ ht->buckets = calloc(ht->nbuckets, sizeof(*ht->buckets)); ++ if (ht->buckets == NULL) { ++ free(ht); ++ return ENOMEM; ++ } ++ ++ *ht_out = ht; ++ return 0; ++} ++ ++void ++k5_hashtab_free(struct k5_hashtab *ht) ++{ ++ size_t i; ++ struct entry *ent; ++ ++ for (i = 0; i < ht->nbuckets; i++) { ++ while (!K5_SLIST_EMPTY(&ht->buckets[i])) { ++ ent = K5_SLIST_FIRST(&ht->buckets[i]); ++ K5_SLIST_REMOVE_HEAD(&ht->buckets[i], next); ++ free(ent); ++ } ++ } ++ free(ht->buckets); ++ free(ht); ++} ++ ++static int ++resize_table(struct k5_hashtab *ht) ++{ ++ size_t i, j, newsize = ht->nbuckets * 2; ++ struct bucket_list *newbuckets; ++ struct entry *ent; ++ ++ newbuckets = calloc(newsize, sizeof(*newbuckets)); ++ if (newbuckets == NULL) ++ return ENOMEM; ++ ++ /* Rehash all the entries into the new buckets. */ ++ for (i = 0; i < ht->nbuckets; i++) { ++ while (!K5_SLIST_EMPTY(&ht->buckets[i])) { ++ ent = K5_SLIST_FIRST(&ht->buckets[i]); ++ j = siphash24(ent->key, ent->klen, ht->k0, ht->k1) % newsize; ++ K5_SLIST_REMOVE_HEAD(&ht->buckets[i], next); ++ K5_SLIST_INSERT_HEAD(&newbuckets[j], ent, next); ++ } ++ } ++ ++ free(ht->buckets); ++ ht->buckets = newbuckets; ++ ht->nbuckets = newsize; ++ return 0; ++} ++ ++int ++k5_hashtab_add(struct k5_hashtab *ht, const void *key, size_t klen, void *val) ++{ ++ size_t i; ++ struct entry *ent; ++ ++ if (ht->nentries == ht->nbuckets) { ++ if (resize_table(ht) != 0) ++ return ENOMEM; ++ } ++ ++ ent = malloc(sizeof(*ent)); ++ if (ent == NULL) ++ return ENOMEM; ++ ent->key = key; ++ ent->klen = klen; ++ ent->val = val; ++ ++ i = siphash24(key, klen, ht->k0, ht->k1) % ht->nbuckets; ++ K5_SLIST_INSERT_HEAD(&ht->buckets[i], ent, next); ++ ++ ht->nentries++; ++ return 0; ++} ++ ++int ++k5_hashtab_remove(struct k5_hashtab *ht, const void *key, size_t klen) ++{ ++ size_t i; ++ struct entry *ent; ++ ++ i = siphash24(key, klen, ht->k0, ht->k1) % ht->nbuckets; ++ K5_SLIST_FOREACH(ent, &ht->buckets[i], next) { ++ if (ent->klen == klen && memcmp(ent->key, key, klen) == 0) { ++ K5_SLIST_REMOVE(&ht->buckets[i], ent, entry, next); ++ free(ent); ++ ht->nentries--; ++ return 1; ++ } ++ } ++ return 0; ++} ++ ++void * ++k5_hashtab_get(struct k5_hashtab *ht, const void *key, size_t klen) ++{ ++ size_t i; ++ struct entry *ent; ++ ++ i = siphash24(key, klen, ht->k0, ht->k1) % ht->nbuckets; ++ K5_SLIST_FOREACH(ent, &ht->buckets[i], next) { ++ if (ent->klen == klen && memcmp(ent->key, key, klen) == 0) ++ return ent->val; ++ } ++ return NULL; ++} +diff --git a/src/util/support/libkrb5support-fixed.exports b/src/util/support/libkrb5support-fixed.exports +index 6193d7331..c63c5fbc3 100644 +--- a/src/util/support/libkrb5support-fixed.exports ++++ b/src/util/support/libkrb5support-fixed.exports +@@ -16,6 +16,11 @@ k5_get_error + k5_free_error + k5_clear_error + k5_set_error_info_callout_fn ++k5_hashtab_add ++k5_hashtab_create ++k5_hashtab_free ++k5_hashtab_get ++k5_hashtab_remove + k5_hex_decode + k5_hex_encode + k5_json_array_add +diff --git a/src/util/support/t_hashtab.c b/src/util/support/t_hashtab.c +new file mode 100644 +index 000000000..f51abc4f1 +--- /dev/null ++++ b/src/util/support/t_hashtab.c +@@ -0,0 +1,176 @@ ++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ++/* util/support/t_hash.c - tests for hash table code */ ++/* ++ * Copyright (C) 2018 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. ++ */ ++ ++/* hash.c has no linker dependencies, so we can simply include its source code ++ * to test its static functions and look inside its structures. */ ++#include "hashtab.c" ++ ++/* These match the sip64 test vectors in the reference C implementation of ++ * siphash at https://github.com/veorq/SipHash */ ++const uint64_t vectors[64] = { ++ 0x726FDB47DD0E0E31, ++ 0x74F839C593DC67FD, ++ 0x0D6C8009D9A94F5A, ++ 0x85676696D7FB7E2D, ++ 0xCF2794E0277187B7, ++ 0x18765564CD99A68D, ++ 0xCBC9466E58FEE3CE, ++ 0xAB0200F58B01D137, ++ 0x93F5F5799A932462, ++ 0x9E0082DF0BA9E4B0, ++ 0x7A5DBBC594DDB9F3, ++ 0xF4B32F46226BADA7, ++ 0x751E8FBC860EE5FB, ++ 0x14EA5627C0843D90, ++ 0xF723CA908E7AF2EE, ++ 0xA129CA6149BE45E5, ++ 0x3F2ACC7F57C29BDB, ++ 0x699AE9F52CBE4794, ++ 0x4BC1B3F0968DD39C, ++ 0xBB6DC91DA77961BD, ++ 0xBED65CF21AA2EE98, ++ 0xD0F2CBB02E3B67C7, ++ 0x93536795E3A33E88, ++ 0xA80C038CCD5CCEC8, ++ 0xB8AD50C6F649AF94, ++ 0xBCE192DE8A85B8EA, ++ 0x17D835B85BBB15F3, ++ 0x2F2E6163076BCFAD, ++ 0xDE4DAAACA71DC9A5, ++ 0xA6A2506687956571, ++ 0xAD87A3535C49EF28, ++ 0x32D892FAD841C342, ++ 0x7127512F72F27CCE, ++ 0xA7F32346F95978E3, ++ 0x12E0B01ABB051238, ++ 0x15E034D40FA197AE, ++ 0x314DFFBE0815A3B4, ++ 0x027990F029623981, ++ 0xCADCD4E59EF40C4D, ++ 0x9ABFD8766A33735C, ++ 0x0E3EA96B5304A7D0, ++ 0xAD0C42D6FC585992, ++ 0x187306C89BC215A9, ++ 0xD4A60ABCF3792B95, ++ 0xF935451DE4F21DF2, ++ 0xA9538F0419755787, ++ 0xDB9ACDDFF56CA510, ++ 0xD06C98CD5C0975EB, ++ 0xE612A3CB9ECBA951, ++ 0xC766E62CFCADAF96, ++ 0xEE64435A9752FE72, ++ 0xA192D576B245165A, ++ 0x0A8787BF8ECB74B2, ++ 0x81B3E73D20B49B6F, ++ 0x7FA8220BA3B2ECEA, ++ 0x245731C13CA42499, ++ 0xB78DBFAF3A8D83BD, ++ 0xEA1AD565322A1A0B, ++ 0x60E61C23A3795013, ++ 0x6606D7E446282B93, ++ 0x6CA4ECB15C5F91E1, ++ 0x9F626DA15C9625F3, ++ 0xE51B38608EF25F57, ++ 0x958A324CEB064572 ++}; ++ ++static void ++test_siphash() ++{ ++ uint8_t seq[64]; ++ uint64_t k0, k1, hval; ++ size_t i; ++ ++ for (i = 0; i < sizeof(seq); i++) ++ seq[i] = i; ++ k0 = load_64_le(seq); ++ k1 = load_64_le(seq + 8); ++ ++ for (i = 0; i < sizeof(seq); i++) { ++ hval = siphash24(seq, i, k0, k1); ++ assert(hval == vectors[i]); ++ } ++} ++ ++static void ++test_hashtab() ++{ ++ int st; ++ struct k5_hashtab *ht; ++ size_t i; ++ char zeros[100] = { 0 }; ++ ++ st = k5_hashtab_create(NULL, 4, &ht); ++ assert(st == 0 && ht != NULL && ht->nentries == 0); ++ ++ st = k5_hashtab_add(ht, "abc", 3, &st); ++ assert(st == 0 && ht->nentries == 1); ++ assert(k5_hashtab_get(ht, "abc", 3) == &st); ++ assert(k5_hashtab_get(ht, "bcde", 4) == NULL); ++ ++ st = k5_hashtab_add(ht, "bcde", 4, &ht); ++ assert(st == 0 && ht->nentries == 2); ++ assert(k5_hashtab_get(ht, "abc", 3) == &st); ++ assert(k5_hashtab_get(ht, "bcde", 4) == &ht); ++ ++ k5_hashtab_remove(ht, "abc", 3); ++ assert(ht->nentries == 1); ++ assert(k5_hashtab_get(ht, "abc", 3) == NULL); ++ assert(k5_hashtab_get(ht, "bcde", 4) == &ht); ++ ++ k5_hashtab_remove(ht, "bcde", 4); ++ assert(ht->nentries == 0); ++ assert(k5_hashtab_get(ht, "abc", 3) == NULL); ++ assert(k5_hashtab_get(ht, "bcde", 4) == NULL); ++ ++ for (i = 0; i < sizeof(zeros); i++) { ++ st = k5_hashtab_add(ht, zeros, i, zeros + i); ++ assert(st == 0 && ht->nentries == i + 1 && ht->nbuckets >= i + 1); ++ } ++ for (i = 0; i < sizeof(zeros); i++) { ++ assert(k5_hashtab_get(ht, zeros, i) == zeros + i); ++ k5_hashtab_remove(ht, zeros, i); ++ assert(ht->nentries == sizeof(zeros) - i - 1); ++ if (i > 0) ++ assert(k5_hashtab_get(ht, zeros, i - 1) == NULL); ++ } ++ ++ k5_hashtab_free(ht); ++} ++ ++int ++main() ++{ ++ test_siphash(); ++ test_hashtab(); ++ return 0; ++} diff --git a/SOURCES/Add-libkrb5support-hex-functions-and-tests.patch b/SOURCES/Add-libkrb5support-hex-functions-and-tests.patch new file mode 100644 index 0000000..becd51e --- /dev/null +++ b/SOURCES/Add-libkrb5support-hex-functions-and-tests.patch @@ -0,0 +1,480 @@ +From 868cbb573b512eac4561046fe7540c37406637fb Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Mon, 19 Feb 2018 00:51:44 -0500 +Subject: [PATCH] Add libkrb5support hex functions and tests + +(cherry picked from commit 720dea558da0062d3cea4385327161e62cf09a5e) +[rharwood@redhat.com: gitignore, backport around no utf16] +--- + src/include/k5-hex.h | 53 ++++++ + src/util/support/Makefile.in | 13 +- + src/util/support/deps | 6 + + src/util/support/hex.c | 116 ++++++++++++ + src/util/support/libkrb5support-fixed.exports | 2 + + src/util/support/t_hex.c | 169 ++++++++++++++++++ + 6 files changed, 357 insertions(+), 2 deletions(-) + create mode 100644 src/include/k5-hex.h + create mode 100644 src/util/support/hex.c + create mode 100644 src/util/support/t_hex.c + +diff --git a/src/include/k5-hex.h b/src/include/k5-hex.h +new file mode 100644 +index 000000000..75bd2cb19 +--- /dev/null ++++ b/src/include/k5-hex.h +@@ -0,0 +1,53 @@ ++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ++/* include/k5-hex.h - libkrb5support hex encoding/decoding declarations */ ++/* ++ * Copyright (C) 2018 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. ++ */ ++ ++#ifndef K5_HEX_H ++#define K5_HEX_H ++ ++#include "k5-platform.h" ++ ++/* ++ * Encode len bytes in hex, placing the result in allocated storage in ++ * *hex_out. Use uppercase hex digits if uppercase is non-zero. Return 0 on ++ * success, ENOMEM on error. ++ */ ++int k5_hex_encode(const void *bytes, size_t len, int uppercase, ++ char **hex_out); ++ ++/* ++ * Decode hex bytes, placing the result in allocated storage in *bytes_out and ++ * *len_out. Null-terminate the result (primarily for decoding passwords in ++ * libkdb_ldap). Return 0 on success, ENOMEM or EINVAL on error. ++ */ ++int k5_hex_decode(const char *hex, uint8_t **bytes_out, size_t *len_out); ++ ++#endif /* K5_HEX_H */ +diff --git a/src/util/support/Makefile.in b/src/util/support/Makefile.in +index 9326742d7..b3576f0b7 100644 +--- a/src/util/support/Makefile.in ++++ b/src/util/support/Makefile.in +@@ -82,6 +82,7 @@ STLIBOBJS= \ + path.o \ + base64.o \ + json.o \ ++ hex.o \ + bcmp.o \ + strerror_r.o \ + dir_filenames.o \ +@@ -108,6 +109,7 @@ LIBOBJS= \ + $(OUTPRE)path.$(OBJEXT) \ + $(OUTPRE)base64.$(OBJEXT) \ + $(OUTPRE)json.$(OBJEXT) \ ++ $(OUTPRE)hex.$(OBJEXT) \ + $(OUTPRE)bcmp.$(OBJEXT) \ + $(OUTPRE)strerror_r.$(OBJEXT) \ + $(OUTPRE)dir_filenames.$(OBJEXT) \ +@@ -139,10 +141,12 @@ SRCS=\ + $(srcdir)/t_unal.c \ + $(srcdir)/t_path.c \ + $(srcdir)/t_json.c \ ++ $(srcdir)/t_hex.c \ + $(srcdir)/zap.c \ + $(srcdir)/path.c \ + $(srcdir)/base64.c \ + $(srcdir)/json.c \ ++ $(srcdir)/hex.c \ + $(srcdir)/bcmp.c \ + $(srcdir)/strerror_r.c \ + $(srcdir)/dir_filenames.c \ +@@ -218,13 +222,16 @@ T_JSON_OBJS= t_json.o json.o base64.o k5buf.o $(PRINTF_ST_OBJ) + t_json: $(T_JSON_OBJS) + $(CC_LINK) -o $@ $(T_JSON_OBJS) + ++t_hex: t_hex.o hex.o ++ $(CC_LINK) -o $@ t_hex.o hex.o ++ + t_unal: t_unal.o + $(CC_LINK) -o t_unal t_unal.o + + t_utf8: t_utf8.o utf8.o + $(CC_LINK) -o t_utf8 t_utf8.o utf8.o + +-TEST_PROGS= t_k5buf t_path t_path_win t_base64 t_json t_unal t_utf8 ++TEST_PROGS= t_k5buf t_path t_path_win t_base64 t_json t_hex t_unal t_utf8 + + check-unix: $(TEST_PROGS) + ./t_k5buf +@@ -232,13 +239,15 @@ check-unix: $(TEST_PROGS) + ./t_path_win + ./t_base64 + ./t_json ++ ./t_hex + ./t_unal + ./t_utf8 + + clean: + $(RM) t_k5buf.o t_k5buf t_unal.o t_unal path_win.o path_win + $(RM) t_path_win.o t_path_win t_path.o t_path t_base64.o t_base64 +- $(RM) t_json.o t_json libkrb5support.exports t_utf8.o t_utf8 ++ $(RM) t_json.o t_json t_hex.o t_hex libkrb5support.exports ++ $(RM) t_utf8.o t_utf8 + + @lib_frag@ + @libobj_frag@ +diff --git a/src/util/support/deps b/src/util/support/deps +index 4dff014f4..551843357 100644 +--- a/src/util/support/deps ++++ b/src/util/support/deps +@@ -62,6 +62,9 @@ t_path.so t_path.po $(OUTPRE)t_path.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + t_path.c + t_json.so t_json.po $(OUTPRE)t_json.$(OBJEXT): $(top_srcdir)/include/k5-json.h \ + t_json.c ++t_hex.so t_hex.po $(OUTPRE)t_hex.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ ++ $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-platform.h \ ++ $(top_srcdir)/include/k5-thread.h t_hex.c + zap.so zap.po $(OUTPRE)zap.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ + zap.c +@@ -75,6 +78,9 @@ json.so json.po $(OUTPRE)json.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(top_srcdir)/include/k5-base64.h $(top_srcdir)/include/k5-buf.h \ + $(top_srcdir)/include/k5-json.h $(top_srcdir)/include/k5-platform.h \ + $(top_srcdir)/include/k5-thread.h json.c ++hex.so hex.po $(OUTPRE)hex.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ ++ $(top_srcdir)/include/k5-hex.h $(top_srcdir)/include/k5-platform.h \ ++ $(top_srcdir)/include/k5-thread.h hex.c + bcmp.so bcmp.po $(OUTPRE)bcmp.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \ + $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-thread.h \ + bcmp.c +diff --git a/src/util/support/hex.c b/src/util/support/hex.c +new file mode 100644 +index 000000000..4407ff9ff +--- /dev/null ++++ b/src/util/support/hex.c +@@ -0,0 +1,116 @@ ++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ++/* util/support/hex.c - hex encoding/decoding implementation */ ++/* ++ * Copyright (C) 2018 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. ++ */ ++ ++#include ++#include ++#include ++ ++static inline char ++hex_digit(uint8_t bval, int uppercase) ++{ ++ assert(bval >= 0 && bval <= 0xF); ++ if (bval < 10) ++ return '0' + bval; ++ else if (uppercase) ++ return 'A' + (bval - 10); ++ else ++ return 'a' + (bval - 10); ++} ++ ++int ++k5_hex_encode(const void *bytes, size_t len, int uppercase, char **hex_out) ++{ ++ size_t i; ++ const uint8_t *p = bytes; ++ char *hex; ++ ++ *hex_out = NULL; ++ ++ hex = malloc(len * 2 + 1); ++ if (hex == NULL) ++ return ENOMEM; ++ ++ for (i = 0; i < len; i++) { ++ hex[i * 2] = hex_digit(p[i] >> 4, uppercase); ++ hex[i * 2 + 1] = hex_digit(p[i] & 0xF, uppercase); ++ } ++ hex[len * 2] = '\0'; ++ ++ *hex_out = hex; ++ return 0; ++} ++ ++/* Decode a hex digit. Return 0-15 on success, -1 on invalid input. */ ++static inline int ++decode_hexchar(unsigned char c) ++{ ++ if (isdigit(c)) ++ return c - '0'; ++ if (c >= 'A' && c <= 'F') ++ return c - 'A' + 10; ++ if (c >= 'a' && c <= 'f') ++ return c - 'a' + 10; ++ return -1; ++} ++ ++int ++k5_hex_decode(const char *hex, uint8_t **bytes_out, size_t *len_out) ++{ ++ size_t hexlen, i; ++ int h1, h2; ++ uint8_t *bytes; ++ ++ *bytes_out = NULL; ++ *len_out = 0; ++ ++ hexlen = strlen(hex); ++ if (hexlen % 2 != 0) ++ return EINVAL; ++ bytes = malloc(hexlen / 2 + 1); ++ if (bytes == NULL) ++ return ENOMEM; ++ ++ for (i = 0; i < hexlen / 2; i++) { ++ h1 = decode_hexchar(hex[i * 2]); ++ h2 = decode_hexchar(hex[i * 2 + 1]); ++ if (h1 == -1 || h2 == -1) { ++ free(bytes); ++ return EINVAL; ++ } ++ bytes[i] = h1 * 16 + h2; ++ } ++ bytes[i] = 0; ++ ++ *bytes_out = bytes; ++ *len_out = hexlen / 2; ++ return 0; ++} +diff --git a/src/util/support/libkrb5support-fixed.exports b/src/util/support/libkrb5support-fixed.exports +index 2cdcddfe0..6193d7331 100644 +--- a/src/util/support/libkrb5support-fixed.exports ++++ b/src/util/support/libkrb5support-fixed.exports +@@ -16,6 +16,8 @@ k5_get_error + k5_free_error + k5_clear_error + k5_set_error_info_callout_fn ++k5_hex_decode ++k5_hex_encode + k5_json_array_add + k5_json_array_create + k5_json_array_fmt +diff --git a/src/util/support/t_hex.c b/src/util/support/t_hex.c +new file mode 100644 +index 000000000..a586a1bc8 +--- /dev/null ++++ b/src/util/support/t_hex.c +@@ -0,0 +1,169 @@ ++/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ ++/* util/support/t_hex.c - Test hex encoding and decoding */ ++/* ++ * Copyright (C) 2018 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. ++ */ ++ ++#include ++#include ++ ++struct { ++ const char *hex; ++ const char *binary; ++ size_t binary_len; ++ int uppercase; ++} tests[] = { ++ /* Invalid hex strings */ ++ { "1" }, ++ { "123" }, ++ { "0/" }, ++ { "/0" }, ++ { "0:" }, ++ { ":0" }, ++ { "0@" }, ++ { "@0" }, ++ { "0G" }, ++ { "G0" }, ++ { "0`" }, ++ { "`0" }, ++ { "0g" }, ++ { "g0" }, ++ { " 00 " }, ++ { "0\x01" }, ++ ++ { "", "", 0 }, ++ { "00", "\x00", 1 }, ++ { "01", "\x01", 1 }, ++ { "10", "\x10", 1 }, ++ { "01ff", "\x01\xFF", 2 }, ++ { "A0B0C0", "\xA0\xB0\xC0", 3, 1 }, ++ { "1a2b3c4d5e6f", "\x1A\x2B\x3C\x4D\x5E\x6F", 6 }, ++ { "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", ++ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF" ++ "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF", 32 }, ++ ++ /* All byte values, lowercase */ ++ { "0001020304050607", "\x00\x01\x02\x03\x04\x05\x06\x07", 8 }, ++ { "08090a0b0c0d0e0f", "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 8 }, ++ { "1011121314151617", "\x10\x11\x12\x13\x14\x15\x16\x17", 8 }, ++ { "18191a1b1c1d1e1f", "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 8 }, ++ { "2021222324252627", "\x20\x21\x22\x23\x24\x25\x26\x27", 8 }, ++ { "28292a2b2c2d2e2f", "\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F", 8 }, ++ { "3031323334353637", "\x30\x31\x32\x33\x34\x35\x36\x37", 8 }, ++ { "38393a3b3c3d3e3f", "\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F", 8 }, ++ { "4041424344454647", "\x40\x41\x42\x43\x44\x45\x46\x47", 8 }, ++ { "48494a4b4c4d4e4f", "\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F", 8 }, ++ { "5051525354555657", "\x50\x51\x52\x53\x54\x55\x56\x57", 8 }, ++ { "58595a5b5c5d5e5f", "\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F", 8 }, ++ { "6061626364656667", "\x60\x61\x62\x63\x64\x65\x66\x67", 8 }, ++ { "68696a6b6c6d6e6f", "\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F", 8 }, ++ { "7071727374757677", "\x70\x71\x72\x73\x74\x75\x76\x77", 8 }, ++ { "78797a7b7c7d7e7f", "\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F", 8 }, ++ { "8081828384858687", "\x80\x81\x82\x83\x84\x85\x86\x87", 8 }, ++ { "88898a8b8c8d8e8f", "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F", 8 }, ++ { "9091929394959697", "\x90\x91\x92\x93\x94\x95\x96\x97", 8 }, ++ { "98999a9b9c9d9e9f", "\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F", 8 }, ++ { "a0a1a2a3a4a5a6a7", "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7", 8 }, ++ { "a8a9aaabacadaeaf", "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF", 8 }, ++ { "b0b1b2b3b4b5b6b7", "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7", 8 }, ++ { "b8b9babbbcbdbebf", "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF", 8 }, ++ { "c0c1c2c3c4c5c6c7", "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7", 8 }, ++ { "c8c9cacbcccdcecf", "\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF", 8 }, ++ { "d0d1d2d3d4d5d6d7", "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7", 8 }, ++ { "d8d9dadbdcdddedf", "\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF", 8 }, ++ { "e0e1e2e3e4e5e6e7", "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7", 8 }, ++ { "e8e9eaebecedeeef", "\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF", 8 }, ++ { "f0f1f2f3f4f5f6f7", "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7", 8 }, ++ { "f8f9fafbfcfdfeff", "\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF", 8 }, ++ ++ /* All byte values, uppercase */ ++ { "0001020304050607", "\x00\x01\x02\x03\x04\x05\x06\x07", 8, 1 }, ++ { "08090A0B0C0D0E0F", "\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", 8, 1 }, ++ { "1011121314151617", "\x10\x11\x12\x13\x14\x15\x16\x17", 8, 1 }, ++ { "18191A1B1C1D1E1F", "\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", 8, 1 }, ++ { "2021222324252627", "\x20\x21\x22\x23\x24\x25\x26\x27", 8, 1 }, ++ { "28292A2B2C2D2E2F", "\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F", 8, 1 }, ++ { "3031323334353637", "\x30\x31\x32\x33\x34\x35\x36\x37", 8, 1 }, ++ { "38393A3B3C3D3E3F", "\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F", 8, 1 }, ++ { "4041424344454647", "\x40\x41\x42\x43\x44\x45\x46\x47", 8, 1 }, ++ { "48494A4B4C4D4E4F", "\x48\x49\x4A\x4B\x4C\x4D\x4E\x4F", 8, 1 }, ++ { "5051525354555657", "\x50\x51\x52\x53\x54\x55\x56\x57", 8, 1 }, ++ { "58595A5B5C5D5E5F", "\x58\x59\x5A\x5B\x5C\x5D\x5E\x5F", 8, 1 }, ++ { "6061626364656667", "\x60\x61\x62\x63\x64\x65\x66\x67", 8, 1 }, ++ { "68696A6B6C6D6E6F", "\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F", 8, 1 }, ++ { "7071727374757677", "\x70\x71\x72\x73\x74\x75\x76\x77", 8, 1 }, ++ { "78797A7B7C7D7E7F", "\x78\x79\x7A\x7B\x7C\x7D\x7E\x7F", 8, 1 }, ++ { "8081828384858687", "\x80\x81\x82\x83\x84\x85\x86\x87", 8, 1 }, ++ { "88898A8B8C8D8E8F", "\x88\x89\x8A\x8B\x8C\x8D\x8E\x8F", 8, 1 }, ++ { "9091929394959697", "\x90\x91\x92\x93\x94\x95\x96\x97", 8, 1 }, ++ { "98999A9B9C9D9E9F", "\x98\x99\x9A\x9B\x9C\x9D\x9E\x9F", 8, 1 }, ++ { "A0A1A2A3A4A5A6A7", "\xA0\xA1\xA2\xA3\xA4\xA5\xA6\xA7", 8, 1 }, ++ { "A8A9AAABACADAEAF", "\xA8\xA9\xAA\xAB\xAC\xAD\xAE\xAF", 8, 1 }, ++ { "B0B1B2B3B4B5B6B7", "\xB0\xB1\xB2\xB3\xB4\xB5\xB6\xB7", 8, 1 }, ++ { "B8B9BABBBCBDBEBF", "\xB8\xB9\xBA\xBB\xBC\xBD\xBE\xBF", 8, 1 }, ++ { "C0C1C2C3C4C5C6C7", "\xC0\xC1\xC2\xC3\xC4\xC5\xC6\xC7", 8, 1 }, ++ { "C8C9CACBCCCDCECF", "\xC8\xC9\xCA\xCB\xCC\xCD\xCE\xCF", 8, 1 }, ++ { "D0D1D2D3D4D5D6D7", "\xD0\xD1\xD2\xD3\xD4\xD5\xD6\xD7", 8, 1 }, ++ { "D8D9DADBDCDDDEDF", "\xD8\xD9\xDA\xDB\xDC\xDD\xDE\xDF", 8, 1 }, ++ { "E0E1E2E3E4E5E6E7", "\xE0\xE1\xE2\xE3\xE4\xE5\xE6\xE7", 8, 1 }, ++ { "E8E9EAEBECEDEEEF", "\xE8\xE9\xEA\xEB\xEC\xED\xEE\xEF", 8, 1 }, ++ { "F0F1F2F3F4F5F6F7", "\xF0\xF1\xF2\xF3\xF4\xF5\xF6\xF7", 8, 1 }, ++ { "F8F9FAFBFCFDFEFF", "\xF8\xF9\xFA\xFB\xFC\xFD\xFE\xFF", 8, 1 }, ++}; ++ ++int main() ++{ ++ size_t i; ++ char *hex; ++ int ret; ++ uint8_t *bytes; ++ size_t len; ++ ++ for (i = 0; i < sizeof(tests) / sizeof(*tests); i++) { ++ if (tests[i].binary == NULL) { ++ ret = k5_hex_decode(tests[i].hex, &bytes, &len); ++ assert(ret == EINVAL && bytes == NULL && len == 0); ++ continue; ++ } ++ ++ ret = k5_hex_decode(tests[i].hex, &bytes, &len); ++ assert(ret == 0); ++ assert(len == tests[i].binary_len); ++ assert(memcmp(bytes, tests[i].binary, len) == 0); ++ assert(bytes[len] == 0); ++ free(bytes); ++ ++ ret = k5_hex_encode((uint8_t *)tests[i].binary, tests[i].binary_len, ++ tests[i].uppercase, &hex); ++ assert(ret == 0); ++ assert(strcmp(tests[i].hex, hex) == 0); ++ free(hex); ++ } ++ return 0; ++} diff --git a/SOURCES/Don-t-include-all-MEMORY-ccaches-in-collection.patch b/SOURCES/Don-t-include-all-MEMORY-ccaches-in-collection.patch new file mode 100644 index 0000000..4adb9e8 --- /dev/null +++ b/SOURCES/Don-t-include-all-MEMORY-ccaches-in-collection.patch @@ -0,0 +1,91 @@ +From 763420ead602d5b17b27f6bad07fdb1cc2f61119 Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Wed, 1 Aug 2018 15:53:12 -0400 +Subject: [PATCH] Don't include all MEMORY ccaches in collection + +In the MEMORY ccache implementation, only yield a cache in the +per-type cursor if it is the context default cache, matching the +behavior of FILE after commit 45360c9688ca963f75a2480f2cf818424fc3dc7b +(ticket 6955). + +ticket: 8720 (new) +(cherry picked from commit 49bb627fed70c5258c151c5135ac3d95ed1ee55d) +--- + src/lib/krb5/ccache/cc_memory.c | 25 ++++++++++--------------- + src/lib/krb5/ccache/t_cccol.py | 7 ++++--- + 2 files changed, 14 insertions(+), 18 deletions(-) + +diff --git a/src/lib/krb5/ccache/cc_memory.c b/src/lib/krb5/ccache/cc_memory.c +index 8cdaff7fb..cfd5c6389 100644 +--- a/src/lib/krb5/ccache/cc_memory.c ++++ b/src/lib/krb5/ccache/cc_memory.c +@@ -132,7 +132,7 @@ struct mcc_cursor { + + /* Iterator over memory caches. */ + struct krb5_mcc_ptcursor_data { +- struct krb5_mcc_list_node *cur; ++ krb5_boolean first; + }; + + k5_cc_mutex krb5int_mcc_mutex = K5_CC_MUTEX_PARTIAL_INITIALIZER; +@@ -693,9 +693,7 @@ krb5_mcc_ptcursor_new( + return ENOMEM; + } + n->data = cdata; +- k5_cc_mutex_lock(context, &krb5int_mcc_mutex); +- cdata->cur = mcc_head; +- k5_cc_mutex_unlock(context, &krb5int_mcc_mutex); ++ cdata->first = TRUE; + *cursor = n; + return 0; + } +@@ -707,22 +705,19 @@ krb5_mcc_ptcursor_next( + krb5_ccache *ccache) + { + struct krb5_mcc_ptcursor_data *cdata = NULL; ++ const char *defname; + + *ccache = NULL; + cdata = cursor->data; +- if (cdata->cur == NULL) ++ if (!cdata->first) ++ return 0; ++ cdata->first = FALSE; ++ ++ defname = krb5_cc_default_name(context); ++ if (defname == NULL || strncmp(defname, "MEMORY:", 7) != 0) + return 0; + +- *ccache = malloc(sizeof(**ccache)); +- if (*ccache == NULL) +- return ENOMEM; +- +- (*ccache)->ops = &krb5_mcc_ops; +- (*ccache)->data = cdata->cur->cache; +- k5_cc_mutex_lock(context, &krb5int_mcc_mutex); +- cdata->cur = cdata->cur->next; +- k5_cc_mutex_unlock(context, &krb5int_mcc_mutex); +- return 0; ++ return krb5_cc_resolve(context, defname, ccache); + } + + static krb5_error_code KRB5_CALLCONV +diff --git a/src/lib/krb5/ccache/t_cccol.py b/src/lib/krb5/ccache/t_cccol.py +index f7f178564..c6d5f514d 100755 +--- a/src/lib/krb5/ccache/t_cccol.py ++++ b/src/lib/krb5/ccache/t_cccol.py +@@ -97,10 +97,11 @@ if test_keyring: + + mfoo = 'MEMORY:foo' + mbar = 'MEMORY:bar' +-cursor_test('filemem', [fccname, mfoo, mbar], [fccname, mfoo, mbar]) +-cursor_test('dirmem', [dccname, mfoo], [duser, dalice, dbob, mfoo]) ++cursor_test('filemem', [fccname, mfoo], [fccname]) ++cursor_test('dirmem', [dccname, mfoo], [duser, dalice, dbob]) ++cursor_test('mem', [mfoo, mbar], [mfoo]) + if test_keyring: +- cursor_test('keyringmem', [krccname, mfoo], [kruser, kralice, krbob, mfoo]) ++ cursor_test('keyringmem', [krccname, mfoo], [kruser, kralice, krbob]) + + # Test krb5_cccol_have_content. + realm.run(['./t_cccursor', dccname, 'CONTENT']) diff --git a/SOURCES/Fix-bugs-with-concurrent-use-of-MEMORY-ccaches.patch b/SOURCES/Fix-bugs-with-concurrent-use-of-MEMORY-ccaches.patch new file mode 100644 index 0000000..26399e2 --- /dev/null +++ b/SOURCES/Fix-bugs-with-concurrent-use-of-MEMORY-ccaches.patch @@ -0,0 +1,396 @@ +From c0873e9b9de0570c97e88598f17b72bf51fd7f5d Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Sun, 1 Jul 2018 00:12:25 -0400 +Subject: [PATCH] Fix bugs with concurrent use of MEMORY ccaches + +A memory ccache iterator stores an alias into the cache object's +linked list of credentials. If the cache is reinitialized while the +iterator is active, the alias becomes invalid. Also, multiple handles +referencing the same memory ccache all use aliases to the same data +object; if one of the handles is destroyed, the other contains a +dangling pointer. + +Fix the first issue by adding a generation counter to the cache and to +cursors, incremented each time the cache is initialized or destroyed. +Check the generation on each cursor step and end the iteration if the +list was invalidated. Fix the second issue by adding a reference +count to the cache object, counting one reference for the table slot +and one for each open handle. Empty the cache object on each destroy +operation, but only release the object when the last handle to it is +destroyed or closed. + +Add regression tests for the two issues to t_cc.c. + +The first issue was reported by Sorin Manolache. + +ticket: 8202 +tags: pullup +target_version: 1.16-next +target_version: 1.15-next + +(cherry picked from commit 146dadec8fe7ccc4149eb2e3f577cc320aee6efb) +--- + src/lib/krb5/ccache/cc_memory.c | 164 ++++++++++++++++++++------------ + src/lib/krb5/ccache/t_cc.c | 51 ++++++++++ + 2 files changed, 154 insertions(+), 61 deletions(-) + +diff --git a/src/lib/krb5/ccache/cc_memory.c b/src/lib/krb5/ccache/cc_memory.c +index c5425eb3a..8cdaff7fb 100644 +--- a/src/lib/krb5/ccache/cc_memory.c ++++ b/src/lib/krb5/ccache/cc_memory.c +@@ -102,18 +102,20 @@ extern krb5_error_code krb5_change_cache (void); + typedef struct _krb5_mcc_link { + struct _krb5_mcc_link *next; + krb5_creds *creds; +-} krb5_mcc_link, *krb5_mcc_cursor; ++} krb5_mcc_link; + + /* Per-cache data header. */ + typedef struct _krb5_mcc_data { + char *name; + k5_cc_mutex lock; + krb5_principal prin; +- krb5_mcc_cursor link; ++ krb5_mcc_link *link; + krb5_timestamp changetime; + /* Time offsets for clock-skewed clients. */ + krb5_int32 time_offset; + krb5_int32 usec_offset; ++ int refcount; /* One for the table slot, one per handle */ ++ int generation; /* Incremented at each initialize */ + } krb5_mcc_data; + + /* List of memory caches. */ +@@ -122,6 +124,12 @@ typedef struct krb5_mcc_list_node { + krb5_mcc_data *cache; + } krb5_mcc_list_node; + ++/* Iterator over credentials in a memory cache. */ ++struct mcc_cursor { ++ int generation; ++ krb5_mcc_link *next_link; ++}; ++ + /* Iterator over memory caches. */ + struct krb5_mcc_ptcursor_data { + struct krb5_mcc_list_node *cur; +@@ -132,7 +140,23 @@ static krb5_mcc_list_node *mcc_head = 0; + + static void update_mcc_change_time(krb5_mcc_data *); + +-static void krb5_mcc_free (krb5_context context, krb5_ccache id); ++/* Remove creds from d, invalidate any existing cursors, and unset the client ++ * principal. The caller is responsible for locking. */ ++static void ++empty_mcc_cache(krb5_context context, krb5_mcc_data *d) ++{ ++ krb5_mcc_link *curr, *next; ++ ++ for (curr = d->link; curr != NULL; curr = next) { ++ next = curr->next; ++ krb5_free_creds(context, curr->creds); ++ free(curr); ++ } ++ d->link = NULL; ++ d->generation++; ++ krb5_free_principal(context, d->prin); ++ d->prin = NULL; ++} + + /* + * Modifies: +@@ -150,16 +174,12 @@ krb5_mcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ) + { + krb5_os_context os_ctx = &context->os_context; + krb5_error_code ret; +- krb5_mcc_data *d; ++ krb5_mcc_data *d = id->data; + +- d = (krb5_mcc_data *)id->data; + k5_cc_mutex_lock(context, &d->lock); ++ empty_mcc_cache(context, d); + +- krb5_mcc_free(context, id); +- +- d = (krb5_mcc_data *)id->data; +- ret = krb5_copy_principal(context, princ, +- &d->prin); ++ ret = krb5_copy_principal(context, princ, &d->prin); + update_mcc_change_time(d); + + if (os_ctx->os_flags & KRB5_OS_TOFFSET_VALID) { +@@ -185,61 +205,59 @@ krb5_mcc_initialize(krb5_context context, krb5_ccache id, krb5_principal princ) + krb5_error_code KRB5_CALLCONV + krb5_mcc_close(krb5_context context, krb5_ccache id) + { ++ krb5_mcc_data *d = id->data; ++ int count; ++ + free(id); +- return KRB5_OK; +-} +- +-static void +-krb5_mcc_free(krb5_context context, krb5_ccache id) +-{ +- krb5_mcc_cursor curr,next; +- krb5_mcc_data *d; +- +- d = (krb5_mcc_data *) id->data; +- for (curr = d->link; curr;) { +- krb5_free_creds(context, curr->creds); +- next = curr->next; +- free(curr); +- curr = next; ++ k5_cc_mutex_lock(context, &d->lock); ++ count = --d->refcount; ++ k5_cc_mutex_unlock(context, &d->lock); ++ if (count == 0) { ++ /* This is the last active handle referencing d and d has been removed ++ * from the table, so we can release it. */ ++ empty_mcc_cache(context, d); ++ free(d->name); ++ k5_cc_mutex_destroy(&d->lock); ++ free(d); + } +- d->link = NULL; +- krb5_free_principal(context, d->prin); ++ return KRB5_OK; + } + + /* + * Effects: + * Destroys the contents of id. id is invalid after call. +- * +- * Errors: +- * system errors (locks related) + */ + krb5_error_code KRB5_CALLCONV + krb5_mcc_destroy(krb5_context context, krb5_ccache id) + { + krb5_mcc_list_node **curr, *node; +- krb5_mcc_data *d; ++ krb5_mcc_data *d = id->data; ++ krb5_boolean removed_from_table = FALSE; + + k5_cc_mutex_lock(context, &krb5int_mcc_mutex); + +- d = (krb5_mcc_data *)id->data; + for (curr = &mcc_head; *curr; curr = &(*curr)->next) { + if ((*curr)->cache == d) { + node = *curr; + *curr = node->next; + free(node); ++ removed_from_table = TRUE; + break; + } + } + k5_cc_mutex_unlock(context, &krb5int_mcc_mutex); + ++ /* Empty the cache and remove the reference for the table slot. There will ++ * always be at least one reference left for the handle being destroyed. */ + k5_cc_mutex_lock(context, &d->lock); +- +- krb5_mcc_free(context, id); +- free(d->name); ++ empty_mcc_cache(context, d); ++ if (removed_from_table) ++ d->refcount--; + k5_cc_mutex_unlock(context, &d->lock); +- k5_cc_mutex_destroy(&d->lock); +- free(d); +- free(id); ++ ++ /* Invalidate the handle, possibly removing the last reference to d and ++ * freeing it. */ ++ krb5_mcc_close(context, id); + + krb5_change_cache (); + return KRB5_OK; +@@ -279,9 +297,12 @@ krb5_mcc_resolve (krb5_context context, krb5_ccache *id, const char *residual) + for (ptr = mcc_head; ptr; ptr=ptr->next) + if (!strcmp(ptr->cache->name, residual)) + break; +- if (ptr) ++ if (ptr != NULL) { + d = ptr->cache; +- else { ++ k5_cc_mutex_lock(context, &d->lock); ++ d->refcount++; ++ k5_cc_mutex_unlock(context, &d->lock); ++ } else { + err = new_mcc_data(residual, &d); + if (err) { + k5_cc_mutex_unlock(context, &krb5int_mcc_mutex); +@@ -326,14 +347,18 @@ krb5_error_code KRB5_CALLCONV + krb5_mcc_start_seq_get(krb5_context context, krb5_ccache id, + krb5_cc_cursor *cursor) + { +- krb5_mcc_cursor mcursor; ++ struct mcc_cursor *mcursor; + krb5_mcc_data *d; + ++ mcursor = malloc(sizeof(*mcursor)); ++ if (mcursor == NULL) ++ return KRB5_CC_NOMEM; + d = id->data; + k5_cc_mutex_lock(context, &d->lock); +- mcursor = d->link; ++ mcursor->generation = d->generation; ++ mcursor->next_link = d->link; + k5_cc_mutex_unlock(context, &d->lock); +- *cursor = (krb5_cc_cursor) mcursor; ++ *cursor = mcursor; + return KRB5_OK; + } + +@@ -361,23 +386,34 @@ krb5_error_code KRB5_CALLCONV + krb5_mcc_next_cred(krb5_context context, krb5_ccache id, + krb5_cc_cursor *cursor, krb5_creds *creds) + { +- krb5_mcc_cursor mcursor; ++ struct mcc_cursor *mcursor; + krb5_error_code retval; ++ krb5_mcc_data *d = id->data; + +- /* Once the node in the linked list is created, it's never +- modified, so we don't need to worry about locking here. (Note +- that we don't support _remove_cred.) */ +- mcursor = (krb5_mcc_cursor) *cursor; +- if (mcursor == NULL) +- return KRB5_CC_END; + memset(creds, 0, sizeof(krb5_creds)); +- if (mcursor->creds) { +- retval = k5_copy_creds_contents(context, mcursor->creds, creds); +- if (retval) +- return retval; ++ mcursor = *cursor; ++ if (mcursor->next_link == NULL) ++ return KRB5_CC_END; ++ ++ /* ++ * Check the cursor generation against the cache generation in case the ++ * cache has been reinitialized or destroyed, freeing the pointer in the ++ * cursor. Keep the cache locked while we copy the creds and advance the ++ * pointer, in case another thread reinitializes the cache after we check ++ * the generation. ++ */ ++ k5_cc_mutex_lock(context, &d->lock); ++ if (mcursor->generation != d->generation) { ++ k5_cc_mutex_unlock(context, &d->lock); ++ return KRB5_CC_END; + } +- *cursor = (krb5_cc_cursor)mcursor->next; +- return KRB5_OK; ++ ++ retval = k5_copy_creds_contents(context, mcursor->next_link->creds, creds); ++ if (retval == 0) ++ mcursor->next_link = mcursor->next_link->next; ++ ++ k5_cc_mutex_unlock(context, &d->lock); ++ return retval; + } + + /* +@@ -396,14 +432,18 @@ krb5_mcc_next_cred(krb5_context context, krb5_ccache id, + krb5_error_code KRB5_CALLCONV + krb5_mcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor) + { +- *cursor = 0L; ++ free(*cursor); ++ *cursor = NULL; + return KRB5_OK; + } + +-/* Utility routine: Creates the back-end data for a memory cache, and +- threads it into the global linked list. +- +- Call with the global list lock held. */ ++/* ++ * Utility routine: Creates the back-end data for a memory cache, and threads ++ * it into the global linked list. Give the new object two references, one for ++ * the table slot and one for the caller's handle. ++ * ++ * Call with the global list lock held. ++ */ + static krb5_error_code + new_mcc_data (const char *name, krb5_mcc_data **dataptr) + { +@@ -432,6 +472,8 @@ new_mcc_data (const char *name, krb5_mcc_data **dataptr) + d->changetime = 0; + d->time_offset = 0; + d->usec_offset = 0; ++ d->refcount = 2; ++ d->generation = 0; + update_mcc_change_time(d); + + n = malloc(sizeof(krb5_mcc_list_node)); +diff --git a/src/lib/krb5/ccache/t_cc.c b/src/lib/krb5/ccache/t_cc.c +index 6069cabd3..cd4569c4c 100644 +--- a/src/lib/krb5/ccache/t_cc.c ++++ b/src/lib/krb5/ccache/t_cc.c +@@ -386,6 +386,55 @@ test_misc(krb5_context context) + krb5_cc_dfl_ops = ops_save; + + } ++ ++/* ++ * Regression tests for #8202. Because memory ccaches share objects between ++ * different handles to the same cache and between iterators and caches, ++ * historically there have been some bugs when those objects are released. ++ */ ++static void ++test_memory_concurrent(krb5_context context) ++{ ++ krb5_error_code kret; ++ krb5_ccache id1, id2; ++ krb5_cc_cursor cursor; ++ krb5_creds creds; ++ ++ /* Create two handles to the same memory ccache and destroy them. */ ++ kret = krb5_cc_resolve(context, "MEMORY:x", &id1); ++ CHECK(kret, "resolve 1"); ++ kret = krb5_cc_resolve(context, "MEMORY:x", &id2); ++ CHECK(kret, "resolve 2"); ++ kret = krb5_cc_destroy(context, id1); ++ CHECK(kret, "destroy 1"); ++ kret = krb5_cc_destroy(context, id2); ++ CHECK(kret, "destroy 2"); ++ ++ kret = init_test_cred(context); ++ CHECK(kret, "init_creds"); ++ ++ /* Reinitialize the cache after creating an iterator for it, and verify ++ * that the iterator ends gracefully. */ ++ kret = krb5_cc_resolve(context, "MEMORY:x", &id1); ++ CHECK(kret, "resolve"); ++ kret = krb5_cc_initialize(context, id1, test_creds.client); ++ CHECK(kret, "initialize"); ++ kret = krb5_cc_store_cred(context, id1, &test_creds); ++ CHECK(kret, "store"); ++ kret = krb5_cc_start_seq_get(context, id1, &cursor); ++ CHECK(kret, "start_seq_get"); ++ kret = krb5_cc_initialize(context, id1, test_creds.client); ++ CHECK(kret, "initialize again"); ++ kret = krb5_cc_next_cred(context, id1, &cursor, &creds); ++ CHECK_BOOL(kret != KRB5_CC_END, "iterator should end", "next_cred"); ++ kret = krb5_cc_end_seq_get(context, id1, &cursor); ++ CHECK(kret, "end_seq_get"); ++ kret = krb5_cc_destroy(context, id1); ++ CHECK(kret, "destroy"); ++ ++ free_test_cred(context); ++} ++ + extern const krb5_cc_ops krb5_mcc_ops; + extern const krb5_cc_ops krb5_fcc_ops; + +@@ -434,6 +483,8 @@ main(void) + do_test(context, "MEMORY:"); + do_test(context, "FILE:"); + ++ test_memory_concurrent(context); ++ + krb5_free_context(context); + return 0; + } diff --git a/SOURCES/Use-a-hash-table-for-MEMORY-ccache-resolution.patch b/SOURCES/Use-a-hash-table-for-MEMORY-ccache-resolution.patch new file mode 100644 index 0000000..77ccf6a --- /dev/null +++ b/SOURCES/Use-a-hash-table-for-MEMORY-ccache-resolution.patch @@ -0,0 +1,185 @@ +From 779a298a583d64cb9a200cc35a4def3a120e03f7 Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Sat, 4 Aug 2018 23:55:18 -0400 +Subject: [PATCH] Use a hash table for MEMORY ccache resolution + +In cc_memory.c, replace the linked list of caches with a hash table, +for better performance with large numbers of memory caches. + +ticket: 8722 (new) +(cherry picked from commit 088ba228acce4fd55bbb7c30122fe2703b8beeb8) +--- + src/lib/krb5/ccache/cc_memory.c | 77 +++++++++++++++------------------ + 1 file changed, 34 insertions(+), 43 deletions(-) + +diff --git a/src/lib/krb5/ccache/cc_memory.c b/src/lib/krb5/ccache/cc_memory.c +index cfd5c6389..114ef6913 100644 +--- a/src/lib/krb5/ccache/cc_memory.c ++++ b/src/lib/krb5/ccache/cc_memory.c +@@ -26,6 +26,7 @@ + + #include "cc-int.h" + #include "../krb/int-proto.h" ++#include "k5-hashtab.h" + #include + + static krb5_error_code KRB5_CALLCONV krb5_mcc_close +@@ -118,12 +119,6 @@ typedef struct _krb5_mcc_data { + int generation; /* Incremented at each initialize */ + } krb5_mcc_data; + +-/* List of memory caches. */ +-typedef struct krb5_mcc_list_node { +- struct krb5_mcc_list_node *next; +- krb5_mcc_data *cache; +-} krb5_mcc_list_node; +- + /* Iterator over credentials in a memory cache. */ + struct mcc_cursor { + int generation; +@@ -136,10 +131,27 @@ struct krb5_mcc_ptcursor_data { + }; + + k5_cc_mutex krb5int_mcc_mutex = K5_CC_MUTEX_PARTIAL_INITIALIZER; +-static krb5_mcc_list_node *mcc_head = 0; ++static struct k5_hashtab *mcc_hashtab = NULL; + + static void update_mcc_change_time(krb5_mcc_data *); + ++/* Ensure that mcc_hashtab is initialized. Call with krb5int_mcc_mutex ++ * locked. */ ++static krb5_error_code ++init_table(krb5_context context) ++{ ++ krb5_error_code ret; ++ uint8_t seed[K5_HASH_SEED_LEN]; ++ krb5_data d = make_data(seed, sizeof(seed)); ++ ++ if (mcc_hashtab != NULL) ++ return 0; ++ ret = krb5_c_random_make_octets(context, &d); ++ if (ret) ++ return ret; ++ return k5_hashtab_create(seed, 64, &mcc_hashtab); ++} ++ + /* Remove creds from d, invalidate any existing cursors, and unset the client + * principal. The caller is responsible for locking. */ + static void +@@ -230,21 +242,13 @@ krb5_mcc_close(krb5_context context, krb5_ccache id) + krb5_error_code KRB5_CALLCONV + krb5_mcc_destroy(krb5_context context, krb5_ccache id) + { +- krb5_mcc_list_node **curr, *node; + krb5_mcc_data *d = id->data; + krb5_boolean removed_from_table = FALSE; + ++ /* Remove this node from the table if it is still present. */ + k5_cc_mutex_lock(context, &krb5int_mcc_mutex); +- +- for (curr = &mcc_head; *curr; curr = &(*curr)->next) { +- if ((*curr)->cache == d) { +- node = *curr; +- *curr = node->next; +- free(node); +- removed_from_table = TRUE; +- break; +- } +- } ++ if (k5_hashtab_remove(mcc_hashtab, d->name, strlen(d->name))) ++ removed_from_table = TRUE; + k5_cc_mutex_unlock(context, &krb5int_mcc_mutex); + + /* Empty the cache and remove the reference for the table slot. There will +@@ -289,16 +293,13 @@ krb5_mcc_resolve (krb5_context context, krb5_ccache *id, const char *residual) + { + krb5_os_context os_ctx = &context->os_context; + krb5_ccache lid; +- krb5_mcc_list_node *ptr; + krb5_error_code err; + krb5_mcc_data *d; + + k5_cc_mutex_lock(context, &krb5int_mcc_mutex); +- for (ptr = mcc_head; ptr; ptr=ptr->next) +- if (!strcmp(ptr->cache->name, residual)) +- break; +- if (ptr != NULL) { +- d = ptr->cache; ++ init_table(context); ++ d = k5_hashtab_get(mcc_hashtab, residual, strlen(residual)); ++ if (d != NULL) { + k5_cc_mutex_lock(context, &d->lock); + d->refcount++; + k5_cc_mutex_unlock(context, &d->lock); +@@ -438,18 +439,17 @@ krb5_mcc_end_seq_get(krb5_context context, krb5_ccache id, krb5_cc_cursor *curso + } + + /* +- * Utility routine: Creates the back-end data for a memory cache, and threads +- * it into the global linked list. Give the new object two references, one for +- * the table slot and one for the caller's handle. ++ * Utility routine: Creates the back-end data for a memory cache, and adds it ++ * to the global table. Give the new object two references, one for the table ++ * slot and one for the caller's handle. + * +- * Call with the global list lock held. ++ * Call with the global table lock held. + */ + static krb5_error_code + new_mcc_data (const char *name, krb5_mcc_data **dataptr) + { + krb5_error_code err; + krb5_mcc_data *d; +- krb5_mcc_list_node *n; + + d = malloc(sizeof(krb5_mcc_data)); + if (d == NULL) +@@ -476,18 +476,13 @@ new_mcc_data (const char *name, krb5_mcc_data **dataptr) + d->generation = 0; + update_mcc_change_time(d); + +- n = malloc(sizeof(krb5_mcc_list_node)); +- if (n == NULL) { ++ if (k5_hashtab_add(mcc_hashtab, d->name, strlen(d->name), d) != 0) { + free(d->name); + k5_cc_mutex_destroy(&d->lock); + free(d); + return KRB5_CC_NOMEM; + } + +- n->cache = d; +- n->next = mcc_head; +- mcc_head = n; +- + *dataptr = d; + return 0; + } +@@ -522,11 +517,10 @@ krb5_mcc_generate_new (krb5_context context, krb5_ccache *id) + lid->ops = &krb5_mcc_ops; + + k5_cc_mutex_lock(context, &krb5int_mcc_mutex); ++ init_table(context); + + /* Check for uniqueness with mutex locked to avoid race conditions */ + while (1) { +- krb5_mcc_list_node *ptr; +- + err = krb5int_random_string (context, uniquename, sizeof (uniquename)); + if (err) { + k5_cc_mutex_unlock(context, &krb5int_mcc_mutex); +@@ -534,12 +528,9 @@ krb5_mcc_generate_new (krb5_context context, krb5_ccache *id) + return err; + } + +- for (ptr = mcc_head; ptr; ptr=ptr->next) { +- if (!strcmp(ptr->cache->name, uniquename)) { +- break; /* got a match, loop again */ +- } +- } +- if (!ptr) break; /* got to the end without finding a match */ ++ if (k5_hashtab_get(mcc_hashtab, uniquename, ++ strlen(uniquename)) == NULL) ++ break; + } + + err = new_mcc_data(uniquename, &d); diff --git a/SPECS/krb5.spec b/SPECS/krb5.spec index 9e84678..bc5177a 100644 --- a/SPECS/krb5.spec +++ b/SPECS/krb5.spec @@ -12,7 +12,7 @@ Summary: The Kerberos network authentication system Name: krb5 Version: 1.15.1 -Release: 34%{?dist} +Release: 37%{?dist} # - Maybe we should explode from the now-available-to-everybody tarball instead? # http://web.mit.edu/kerberos/dist/krb5/1.13/krb5-1.13.2-signed.tar @@ -121,7 +121,12 @@ Patch206: Add-test-cases-for-preauth-fallback-behavior.patch Patch207: Include-preauth-name-in-trace-output-if-possible.patch Patch208: Add-vector-support-to-k5_sha256.patch Patch209: Use-SHA-256-instead-of-MD5-for-audit-ticket-IDs.patch -Patch210: In-FIPS-mode-add-plaintext-fallback-for-RC4-usages-a.patch +Patch211: In-FIPS-mode-add-plaintext-fallback-for-RC4-usages-a.patch +Patch212: Fix-bugs-with-concurrent-use-of-MEMORY-ccaches.patch +Patch213: Don-t-include-all-MEMORY-ccaches-in-collection.patch +Patch214: Add-libkrb5support-hex-functions-and-tests.patch +Patch215: Add-a-hash-table-implementation-to-libkrb5support.patch +Patch216: Use-a-hash-table-for-MEMORY-ccache-resolution.patch License: MIT URL: http://web.mit.edu/kerberos/www/ @@ -376,7 +381,12 @@ ONLY by kerberos itself. Do not depend on this package. %patch207 -p1 -b .Include-preauth-name-in-trace-output-if-possible %patch208 -p1 -b .Add-vector-support-to-k5_sha256 %patch209 -p1 -b .Use-SHA-256-instead-of-MD5-for-audit-ticket-IDs -%patch210 -p1 -b .In-FIPS-mode-add-plaintext-fallback-for-RC4-usages-a +%patch211 -p1 -b .In-FIPS-mode-add-plaintext-fallback-for-RC4-usages-a +%patch212 -p1 -b .Fix-bugs-with-concurrent-use-of-MEMORY-ccaches +%patch213 -p1 -b .Don-t-include-all-MEMORY-ccaches-in-collection +%patch214 -p1 -b .Add-libkrb5support-hex-functions-and-tests +%patch215 -p1 -b .Add-a-hash-table-implementation-to-libkrb5support +%patch216 -p1 -b .Use-a-hash-table-for-MEMORY-ccache-resolution ln NOTICE LICENSE @@ -460,7 +470,7 @@ CPPFLAGS="`echo $DEFINES $INCLUDES`" --with-dirsrv-account-locking \ %endif --enable-pkinit \ - --with-crypto-impl=openssl \ + --with-crypto-impl=builtin \ --with-pkinit-crypto-impl=openssl \ --with-tls-impl=openssl \ --with-system-verto \ @@ -882,6 +892,18 @@ exit 0 %{_libdir}/libkadm5srv_mit.so.* %changelog +* Tue Dec 18 2018 Robbie Harwood - 1.15.1-37 +- Bring back builtin crypto (openssl broke too many FIPS setups) +- Resolves: #1657890 + +* Mon Dec 17 2018 Robbie Harwood - 1.15.1-36 +- Clean up MEMORY ccache behavior to match upstream more closely +- Resolves: #1657890 + +* Tue Dec 11 2018 Robbie Harwood - 1.15.1-35 +- Fix bugs with concurrent use of MEMORY ccaches +- Resolves: #1657890 + * Wed Aug 01 2018 Robbie Harwood - 1.15.1-34 - In FIPS mode, add plaintext fallback for RC4 usages and taint - Resolves: #1570600