From 27013b5c7ef7d8abc7d6f9289576f3aee07efab7 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Nov 09 2021 10:04:47 +0000 Subject: import krb5-1.18.2-14.el8 --- diff --git a/SOURCES/Add-APIs-for-marshalling-credentials.patch b/SOURCES/Add-APIs-for-marshalling-credentials.patch new file mode 100644 index 0000000..db04da8 --- /dev/null +++ b/SOURCES/Add-APIs-for-marshalling-credentials.patch @@ -0,0 +1,222 @@ +From de01999b35773196749ba714f233649c9528aaad Mon Sep 17 00:00:00 2001 +From: Robbie Harwood +Date: Thu, 14 Jan 2021 18:13:09 -0500 +Subject: [PATCH] Add APIs for marshalling credentials + +Faciliate KCM daemon implementations by providing functions to +deserialize and reserialize credentials in the FILE v4 format. + +[ghudson@mit.edu: minor editorial changes] + +ticket: 8980 (new) +(cherry picked from commit 18ea3bd2fca55b789b7de9c663624bc11d348fa6) +(cherry picked from commit 3d11179707923b033fa413387a33296b673ff52d) +[rharwood@redhat.com: function backport, so conflict in krb5_32.def] +--- + doc/appdev/refs/api/index.rst | 2 ++ + src/include/krb5/krb5.hin | 36 ++++++++++++++++++++++ + src/lib/krb5/ccache/ccmarshal.c | 53 +++++++++++++++++++++++++++++++++ + src/lib/krb5/ccache/t_marshal.c | 15 +++++++++- + src/lib/krb5/libkrb5.exports | 2 ++ + src/lib/krb5_32.def | 4 +++ + 6 files changed, 111 insertions(+), 1 deletion(-) + +diff --git a/doc/appdev/refs/api/index.rst b/doc/appdev/refs/api/index.rst +index 727d9b492..9e03fd386 100644 +--- a/doc/appdev/refs/api/index.rst ++++ b/doc/appdev/refs/api/index.rst +@@ -232,6 +232,7 @@ Rarely used public interfaces + krb5_kt_remove_entry.rst + krb5_kt_start_seq_get.rst + krb5_make_authdata_kdc_issued.rst ++ krb5_marshal_credentials.rst + krb5_merge_authdata.rst + krb5_mk_1cred.rst + krb5_mk_error.rst +@@ -285,6 +286,7 @@ Rarely used public interfaces + krb5_tkt_creds_get_times.rst + krb5_tkt_creds_init.rst + krb5_tkt_creds_step.rst ++ krb5_unmarshal_credentials.rst + krb5_verify_init_creds.rst + krb5_verify_init_creds_opt_init.rst + krb5_verify_init_creds_opt_set_ap_req_nofail.rst +diff --git a/src/include/krb5/krb5.hin b/src/include/krb5/krb5.hin +index 9264bede1..d2cf1eba2 100644 +--- a/src/include/krb5/krb5.hin ++++ b/src/include/krb5/krb5.hin +@@ -3125,6 +3125,42 @@ krb5_get_credentials(krb5_context context, krb5_flags options, + krb5_ccache ccache, krb5_creds *in_creds, + krb5_creds **out_creds); + ++/** ++ * Serialize a @c krb5_creds object. ++ * ++ * @param [in] context Library context ++ * @param [in] creds The credentials object to serialize ++ * @param [out] data_out The serialized credentials ++ * ++ * Serialize @a creds in the format used by the FILE ccache format (vesion 4) ++ * and KCM ccache protocol. ++ * ++ * Use krb5_free_data() to free @a data_out when it is no longer needed. ++ * ++ * @retval 0 Success; otherwise - Kerberos error codes ++ */ ++krb5_error_code KRB5_CALLCONV ++krb5_marshal_credentials(krb5_context context, krb5_creds *in_creds, ++ krb5_data **data_out); ++ ++/** ++ * Deserialize a @c krb5_creds object. ++ * ++ * @param [in] context Library context ++ * @param [in] data The serialized credentials ++ * @param [out] creds_out The resulting creds object ++ * ++ * Deserialize @a data to credentials in the format used by the FILE ccache ++ * format (vesion 4) and KCM ccache protocol. ++ * ++ * Use krb5_free_creds() to free @a creds_out when it is no longer needed. ++ * ++ * @retval 0 Success; otherwise - Kerberos error codes ++ */ ++krb5_error_code KRB5_CALLCONV ++krb5_unmarshal_credentials(krb5_context context, const krb5_data *data, ++ krb5_creds **creds_out); ++ + /** @deprecated Replaced by krb5_get_validated_creds. */ + krb5_error_code KRB5_CALLCONV + krb5_get_credentials_validate(krb5_context context, krb5_flags options, +diff --git a/src/lib/krb5/ccache/ccmarshal.c b/src/lib/krb5/ccache/ccmarshal.c +index ae634ccab..ab284e721 100644 +--- a/src/lib/krb5/ccache/ccmarshal.c ++++ b/src/lib/krb5/ccache/ccmarshal.c +@@ -515,3 +515,56 @@ k5_marshal_mcred(struct k5buf *buf, krb5_creds *mcred) + if (mcred->second_ticket.length > 0) + put_data(buf, version, &mcred->second_ticket); + } ++ ++krb5_error_code KRB5_CALLCONV ++krb5_marshal_credentials(krb5_context context, krb5_creds *in_creds, ++ krb5_data **data_out) ++{ ++ krb5_error_code ret; ++ krb5_data *data; ++ struct k5buf buf; ++ ++ *data_out = NULL; ++ ++ data = k5alloc(sizeof(krb5_data), &ret); ++ if (ret) ++ return ret; ++ ++ k5_buf_init_dynamic(&buf); ++ k5_marshal_cred(&buf, 4, in_creds); ++ ++ ret = k5_buf_status(&buf); ++ if (ret) { ++ free(data); ++ return ret; ++ } ++ ++ /* Steal payload from buf. */ ++ *data = make_data(buf.data, buf.len); ++ *data_out = data; ++ return 0; ++} ++ ++krb5_error_code KRB5_CALLCONV ++krb5_unmarshal_credentials(krb5_context context, const krb5_data *data, ++ krb5_creds **creds_out) ++{ ++ krb5_error_code ret; ++ krb5_creds *creds; ++ ++ *creds_out = NULL; ++ ++ creds = k5alloc(sizeof(krb5_creds), &ret); ++ if (ret) ++ return ret; ++ ++ ret = k5_unmarshal_cred((unsigned char *)data->data, data->length, 4, ++ creds); ++ if (ret) { ++ free(creds); ++ return ret; ++ } ++ ++ *creds_out = creds; ++ return 0; ++} +diff --git a/src/lib/krb5/ccache/t_marshal.c b/src/lib/krb5/ccache/t_marshal.c +index 144554c30..47ec2e94d 100644 +--- a/src/lib/krb5/ccache/t_marshal.c ++++ b/src/lib/krb5/ccache/t_marshal.c +@@ -268,13 +268,14 @@ main(int argc, char **argv) + krb5_context context; + krb5_ccache cache; + krb5_principal princ; +- krb5_creds cred1, cred2; ++ krb5_creds cred1, cred2, *alloc_cred; + krb5_cc_cursor cursor; + const char *filename; + char *ccname, filebuf[256]; + int version, fd; + const struct test *t; + struct k5buf buf; ++ krb5_data ser_data, *alloc_data; + + if (argc != 2) + abort(); +@@ -285,6 +286,18 @@ main(int argc, char **argv) + if (krb5_init_context(&context) != 0) + abort(); + ++ /* Test public functions for unmarshalling and marshalling. */ ++ ser_data = make_data((char *)tests[3].cred1, tests[3].cred1len); ++ if (krb5_unmarshal_credentials(context, &ser_data, &alloc_cred) != 0) ++ abort(); ++ verify_cred1(alloc_cred); ++ if (krb5_marshal_credentials(context, alloc_cred, &alloc_data) != 0) ++ abort(); ++ assert(alloc_data->length == tests[3].cred1len); ++ assert(memcmp(tests[3].cred1, alloc_data->data, alloc_data->length) == 0); ++ krb5_free_data(context, alloc_data); ++ krb5_free_creds(context, alloc_cred); ++ + for (version = FIRST_VERSION; version <= 4; version++) { + t = &tests[version - 1]; + +diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports +index cab5b3b17..48ae46f5c 100644 +--- a/src/lib/krb5/libkrb5.exports ++++ b/src/lib/krb5/libkrb5.exports +@@ -488,6 +488,7 @@ krb5_lock_file + krb5_make_authdata_kdc_issued + krb5_make_full_ipaddr + krb5_make_fulladdr ++krb5_marshal_credentials + krb5_mcc_ops + krb5_merge_authdata + krb5_mk_1cred +@@ -592,6 +593,7 @@ krb5_timeofday + krb5_timestamp_to_sfstring + krb5_timestamp_to_string + krb5_unlock_file ++krb5_unmarshal_credentials + krb5_unpack_full_ipaddr + krb5_unparse_name + krb5_unparse_name_ext +diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def +index de5823c17..209c6aaef 100644 +--- a/src/lib/krb5_32.def ++++ b/src/lib/krb5_32.def +@@ -502,3 +502,7 @@ EXPORTS + + ; new in 1.19 + k5_cc_store_primary_cred @470 ; PRIVATE ++ ++; new in 1.20 ++ krb5_marshal_credentials @472 ++ krb5_unmarshal_credentials @473 diff --git a/SOURCES/Add-KCM_OP_GET_CRED_LIST-for-faster-iteration.patch b/SOURCES/Add-KCM_OP_GET_CRED_LIST-for-faster-iteration.patch new file mode 100644 index 0000000..3c9028b --- /dev/null +++ b/SOURCES/Add-KCM_OP_GET_CRED_LIST-for-faster-iteration.patch @@ -0,0 +1,360 @@ +From d4a512e571a93318d37cbf7d18a120f317b87e97 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Thu, 11 Feb 2021 15:33:10 +0100 +Subject: [PATCH] Add KCM_OP_GET_CRED_LIST for faster iteration + +For large caches, one IPC operation per credential dominates the cost +of iteration. Instead transfer the whole list of credentials to the +client in one IPC operation. + +Add optional support for the new opcode to the test KCM server to +allow testing of the main and fallback code paths. + +[ghudson@mit.edu: fixed memory leaks and potential memory errors; +adjusted code style and comments; rewrote commit message; added +kcmserver.py support and tests] + +ticket: 8990 (new) +(cherry picked from commit 81bdb47d8ded390263d8ee48f71d5c312b4f1736) +(cherry picked from commit a0ee8b02e56c65e5dcd569caed0e151cef004ef4) +(cherry picked from commit baf60dbdeceb3cad35cad7d9930782f94b6c8221) +--- + src/include/kcm.h | 12 ++- + src/lib/krb5/ccache/cc_kcm.c | 144 ++++++++++++++++++++++++++++++++--- + src/tests/kcmserver.py | 28 ++++++- + src/tests/t_ccache.py | 10 ++- + 4 files changed, 175 insertions(+), 19 deletions(-) + +diff --git a/src/include/kcm.h b/src/include/kcm.h +index 5ea1447cd..e4140c3a0 100644 +--- a/src/include/kcm.h ++++ b/src/include/kcm.h +@@ -51,9 +51,9 @@ + * + * All replies begin with a 32-bit big-endian reply code. + * +- * Parameters are appended to the request or reply with no delimiters. Flags +- * and time offsets are stored as 32-bit big-endian integers. Names are +- * marshalled as zero-terminated strings. Principals and credentials are ++ * Parameters are appended to the request or reply with no delimiters. Flags, ++ * time offsets, and lengths are stored as 32-bit big-endian integers. Names ++ * are marshalled as zero-terminated strings. Principals and credentials are + * marshalled in the v4 FILE ccache format. UUIDs are 16 bytes. UUID lists + * are not delimited, so nothing can come after them. + */ +@@ -89,7 +89,11 @@ typedef enum kcm_opcode { + KCM_OP_HAVE_NTLM_CRED, + KCM_OP_DEL_NTLM_CRED, + KCM_OP_DO_NTLM_AUTH, +- KCM_OP_GET_NTLM_USER_LIST ++ KCM_OP_GET_NTLM_USER_LIST, ++ ++ /* MIT extensions */ ++ KCM_OP_MIT_EXTENSION_BASE = 13000, ++ KCM_OP_GET_CRED_LIST, /* (name) -> (count, count*{len, cred}) */ + } kcm_opcode; + + #endif /* KCM_H */ +diff --git a/src/lib/krb5/ccache/cc_kcm.c b/src/lib/krb5/ccache/cc_kcm.c +index a76a285d9..197a10fba 100644 +--- a/src/lib/krb5/ccache/cc_kcm.c ++++ b/src/lib/krb5/ccache/cc_kcm.c +@@ -61,6 +61,17 @@ struct uuid_list { + size_t pos; + }; + ++struct cred_list { ++ krb5_creds *creds; ++ size_t count; ++ size_t pos; ++}; ++ ++struct kcm_cursor { ++ struct uuid_list *uuids; ++ struct cred_list *creds; ++}; ++ + struct kcmio { + SOCKET fd; + #ifdef __APPLE__ +@@ -489,6 +500,69 @@ free_uuid_list(struct uuid_list *uuids) + free(uuids); + } + ++static void ++free_cred_list(struct cred_list *list) ++{ ++ size_t i; ++ ++ if (list == NULL) ++ return; ++ ++ /* Creds are transferred to the caller as list->pos is incremented, so we ++ * can start freeing there. */ ++ for (i = list->pos; i < list->count; i++) ++ krb5_free_cred_contents(NULL, &list->creds[i]); ++ free(list->creds); ++ free(list); ++} ++ ++/* Fetch a cred list from req->reply. */ ++static krb5_error_code ++kcmreq_get_cred_list(struct kcmreq *req, struct cred_list **creds_out) ++{ ++ struct cred_list *list; ++ const unsigned char *data; ++ krb5_error_code ret = 0; ++ size_t count, len, i; ++ ++ *creds_out = NULL; ++ ++ /* Check a rough bound on the count to prevent very large allocations. */ ++ count = k5_input_get_uint32_be(&req->reply); ++ if (count > req->reply.len / 4) ++ return KRB5_KCM_MALFORMED_REPLY; ++ ++ list = malloc(sizeof(*list)); ++ if (list == NULL) ++ return ENOMEM; ++ ++ list->creds = NULL; ++ list->count = count; ++ list->pos = 0; ++ list->creds = k5calloc(count, sizeof(*list->creds), &ret); ++ if (list->creds == NULL) { ++ free(list); ++ return ret; ++ } ++ ++ for (i = 0; i < count; i++) { ++ len = k5_input_get_uint32_be(&req->reply); ++ data = k5_input_get_bytes(&req->reply, len); ++ if (data == NULL) ++ break; ++ ret = k5_unmarshal_cred(data, len, 4, &list->creds[i]); ++ if (ret) ++ break; ++ } ++ if (i < count) { ++ free_cred_list(list); ++ return (ret == ENOMEM) ? ENOMEM : KRB5_KCM_MALFORMED_REPLY; ++ } ++ ++ *creds_out = list; ++ return 0; ++} ++ + static void + kcmreq_free(struct kcmreq *req) + { +@@ -753,33 +827,53 @@ kcm_start_seq_get(krb5_context context, krb5_ccache cache, + { + krb5_error_code ret; + struct kcmreq req = EMPTY_KCMREQ; +- struct uuid_list *uuids; ++ struct uuid_list *uuids = NULL; ++ struct cred_list *creds = NULL; ++ struct kcm_cursor *cursor; + + *cursor_out = NULL; + + get_kdc_offset(context, cache); + +- kcmreq_init(&req, KCM_OP_GET_CRED_UUID_LIST, cache); ++ kcmreq_init(&req, KCM_OP_GET_CRED_LIST, cache); + ret = cache_call(context, cache, &req); +- if (ret) ++ if (ret == 0) { ++ /* GET_CRED_LIST is available. */ ++ ret = kcmreq_get_cred_list(&req, &creds); ++ if (ret) ++ goto cleanup; ++ } else if (ret == KRB5_FCC_INTERNAL) { ++ /* Fall back to GET_CRED_UUID_LIST. */ ++ kcmreq_free(&req); ++ kcmreq_init(&req, KCM_OP_GET_CRED_UUID_LIST, cache); ++ ret = cache_call(context, cache, &req); ++ if (ret) ++ goto cleanup; ++ ret = kcmreq_get_uuid_list(&req, &uuids); ++ if (ret) ++ goto cleanup; ++ } else { + goto cleanup; +- ret = kcmreq_get_uuid_list(&req, &uuids); +- if (ret) ++ } ++ ++ cursor = k5alloc(sizeof(*cursor), &ret); ++ if (cursor == NULL) + goto cleanup; +- *cursor_out = (krb5_cc_cursor)uuids; ++ cursor->uuids = uuids; ++ cursor->creds = creds; ++ *cursor_out = (krb5_cc_cursor)cursor; + + cleanup: + kcmreq_free(&req); + return ret; + } + +-static krb5_error_code KRB5_CALLCONV +-kcm_next_cred(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor, +- krb5_creds *cred_out) ++static krb5_error_code ++next_cred_by_uuid(krb5_context context, krb5_ccache cache, ++ struct uuid_list *uuids, krb5_creds *cred_out) + { + krb5_error_code ret; + struct kcmreq req; +- struct uuid_list *uuids = (struct uuid_list *)*cursor; + + memset(cred_out, 0, sizeof(*cred_out)); + +@@ -797,11 +891,39 @@ kcm_next_cred(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor, + return map_invalid(ret); + } + ++static krb5_error_code KRB5_CALLCONV ++kcm_next_cred(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor, ++ krb5_creds *cred_out) ++{ ++ struct kcm_cursor *c = (struct kcm_cursor *)*cursor; ++ struct cred_list *list; ++ ++ if (c->uuids != NULL) ++ return next_cred_by_uuid(context, cache, c->uuids, cred_out); ++ ++ list = c->creds; ++ if (list->pos >= list->count) ++ return KRB5_CC_END; ++ ++ /* Transfer memory ownership of one cred to the caller. */ ++ *cred_out = list->creds[list->pos]; ++ memset(&list->creds[list->pos], 0, sizeof(*list->creds)); ++ list->pos++; ++ ++ return 0; ++} ++ + static krb5_error_code KRB5_CALLCONV + kcm_end_seq_get(krb5_context context, krb5_ccache cache, + krb5_cc_cursor *cursor) + { +- free_uuid_list((struct uuid_list *)*cursor); ++ struct kcm_cursor *c = *cursor; ++ ++ if (c == NULL) ++ return 0; ++ free_uuid_list(c->uuids); ++ free_cred_list(c->creds); ++ free(c); + *cursor = NULL; + return 0; + } +diff --git a/src/tests/kcmserver.py b/src/tests/kcmserver.py +index 57432e5a7..8c5e66ff1 100644 +--- a/src/tests/kcmserver.py ++++ b/src/tests/kcmserver.py +@@ -23,6 +23,7 @@ + # traceback.print_exception(etype, value, tb, file=f) + # sys.excepthook = ehook + ++import optparse + import select + import socket + import struct +@@ -49,12 +50,14 @@ class KCMOpcodes(object): + SET_DEFAULT_CACHE = 21 + GET_KDC_OFFSET = 22 + SET_KDC_OFFSET = 23 ++ GET_CRED_LIST = 13001 + + + class KRB5Errors(object): + KRB5_CC_END = -1765328242 + KRB5_CC_NOSUPP = -1765328137 + KRB5_FCC_NOFILE = -1765328189 ++ KRB5_FCC_INTERNAL = -1765328188 + + + def make_uuid(): +@@ -183,6 +186,14 @@ def op_set_kdc_offset(argbytes): + return 0, b'' + + ++def op_get_cred_list(argbytes): ++ name, rest = unmarshal_name(argbytes) ++ cache = get_cache(name) ++ creds = [cache.creds[u] for u in cache.cred_uuids] ++ return 0, (struct.pack('>L', len(creds)) + ++ b''.join(struct.pack('>L', len(c)) + c for c in creds)) ++ ++ + ophandlers = { + KCMOpcodes.GEN_NEW : op_gen_new, + KCMOpcodes.INITIALIZE : op_initialize, +@@ -197,7 +208,8 @@ ophandlers = { + KCMOpcodes.GET_DEFAULT_CACHE : op_get_default_cache, + KCMOpcodes.SET_DEFAULT_CACHE : op_set_default_cache, + KCMOpcodes.GET_KDC_OFFSET : op_get_kdc_offset, +- KCMOpcodes.SET_KDC_OFFSET : op_set_kdc_offset ++ KCMOpcodes.SET_KDC_OFFSET : op_set_kdc_offset, ++ KCMOpcodes.GET_CRED_LIST : op_get_cred_list + } + + # Read and respond to a request from the socket s. +@@ -215,7 +227,11 @@ def service_request(s): + + majver, minver, op = struct.unpack('>BBH', req[:4]) + argbytes = req[4:] +- code, payload = ophandlers[op](argbytes) ++ ++ if op in ophandlers: ++ code, payload = ophandlers[op](argbytes) ++ else: ++ code, payload = KRB5Errors.KRB5_FCC_INTERNAL, b'' + + # The KCM response is the code (4 bytes) and the response payload. + # The Heimdal IPC response is the length of the KCM response (4 +@@ -226,9 +242,15 @@ def service_request(s): + s.sendall(hipc_response) + return True + ++parser = optparse.OptionParser() ++parser.add_option('-c', '--credlist', action='store_true', dest='credlist', ++ default=False, help='Support KCM_OP_GET_CRED_LIST') ++(options, args) = parser.parse_args() ++if not options.credlist: ++ del ophandlers[KCMOpcodes.GET_CRED_LIST] + + server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) +-server.bind(sys.argv[1]) ++server.bind(args[0]) + server.listen(5) + select_input = [server,] + sys.stderr.write('starting...\n') +diff --git a/src/tests/t_ccache.py b/src/tests/t_ccache.py +index 66804afa5..90040fb7b 100755 +--- a/src/tests/t_ccache.py ++++ b/src/tests/t_ccache.py +@@ -125,10 +125,18 @@ def collection_test(realm, ccname): + + + collection_test(realm, 'DIR:' + os.path.join(realm.testdir, 'cc')) ++ ++# Test KCM without and with GET_CRED_LIST support. + kcmserver_path = os.path.join(srctop, 'tests', 'kcmserver.py') +-realm.start_server([sys.executable, kcmserver_path, kcm_socket_path], ++kcmd = realm.start_server([sys.executable, kcmserver_path, kcm_socket_path], ++ 'starting...') ++collection_test(realm, 'KCM:') ++stop_daemon(kcmd) ++os.remove(kcm_socket_path) ++realm.start_server([sys.executable, kcmserver_path, '-c', kcm_socket_path], + 'starting...') + collection_test(realm, 'KCM:') ++ + if test_keyring: + def cleanup_keyring(anchor, name): + out = realm.run(['keyctl', 'list', anchor]) diff --git a/SOURCES/Add-recursion-limit-for-ASN.1-indefinite-lengths.patch b/SOURCES/Add-recursion-limit-for-ASN.1-indefinite-lengths.patch index 0c4a4d0..41cf09c 100644 --- a/SOURCES/Add-recursion-limit-for-ASN.1-indefinite-lengths.patch +++ b/SOURCES/Add-recursion-limit-for-ASN.1-indefinite-lengths.patch @@ -1,4 +1,4 @@ -From 3a5576fab22ecd21bbf72cccec5be2096e0e05c4 Mon Sep 17 00:00:00 2001 +From 3c47e4adbed5e0a2e7f3993a24097889216a9d50 Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Sat, 31 Oct 2020 17:07:05 -0400 Subject: [PATCH] Add recursion limit for ASN.1 indefinite lengths diff --git a/SOURCES/Add-support-for-start_realm-cache-config.patch b/SOURCES/Add-support-for-start_realm-cache-config.patch new file mode 100644 index 0000000..b83092c --- /dev/null +++ b/SOURCES/Add-support-for-start_realm-cache-config.patch @@ -0,0 +1,303 @@ +From bb5552ece2a351dc3ccab52cceea1eaffeacd768 Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Mon, 14 Dec 2020 13:16:17 -0500 +Subject: [PATCH] Add support for start_realm cache config + +When making TGS requests, if start_realm is set in the cache, use the +named realm to look up the initial TGT for referral or cross-realm +requests. (Also correct a comment in struct _tkt_creds_context: the +ccache field is an owner pointer, not an alias.) + +Add an internal API k5_cc_store_primary_cred(), which sets start_realm +if the cred being stored is a TGT for a realm other than the client +realm. Use this API when acquiring initial tickets with a +caller-specified output ccache, when renewing or validating tickets +with kinit, when accepting a delegated credential in a GSS context, +and when storing a single cred with kvno --out-cache. + +ticket: 8332 +tags: pullup +target_version: 1.19 + +(cherry picked from commit 0d56740ab9fcc40dc7f46c6fbebdf8f1214f9d96) +[rharwood@redhat.com: backport around spelling and canonicalization fallback] +--- + doc/formats/ccache_file_format.rst | 6 +++++ + src/clients/kinit/kinit.c | 2 +- + src/clients/kvno/kvno.c | 5 ++++- + src/include/k5-int.h | 4 ++++ + src/lib/gssapi/krb5/accept_sec_context.c | 2 +- + src/lib/krb5/ccache/ccfns.c | 20 +++++++++++++++++ + src/lib/krb5/krb/get_creds.c | 28 ++++++++++++++++++------ + src/lib/krb5/krb/get_in_tkt.c | 2 +- + src/lib/krb5/libkrb5.exports | 1 + + src/lib/krb5_32.def | 3 +++ + src/tests/t_crossrealm.py | 8 +++++++ + src/tests/t_pkinit.py | 3 +++ + 12 files changed, 73 insertions(+), 11 deletions(-) + +diff --git a/doc/formats/ccache_file_format.rst b/doc/formats/ccache_file_format.rst +index 6349e0d29..6138c1b58 100644 +--- a/doc/formats/ccache_file_format.rst ++++ b/doc/formats/ccache_file_format.rst +@@ -174,3 +174,9 @@ refresh_time + decimal representation of a timestamp at which the GSS mechanism + should attempt to refresh the credential cache from the client + keytab. ++ ++start_realm ++ This key indicates the realm of the ticket-granting ticket to be ++ used for TGS requests, when making a referrals request or ++ beginning a cross-realm request. If it is not present, the client ++ realm is used. +diff --git a/src/clients/kinit/kinit.c b/src/clients/kinit/kinit.c +index 3fdae2878..e5ebeb895 100644 +--- a/src/clients/kinit/kinit.c ++++ b/src/clients/kinit/kinit.c +@@ -828,7 +828,7 @@ k5_kinit(struct k_opts *opts, struct k5_data *k5) + if (opts->verbose) + fprintf(stderr, _("Initialized cache\n")); + +- ret = krb5_cc_store_cred(k5->ctx, k5->out_cc, &my_creds); ++ ret = k5_cc_store_primary_cred(k5->ctx, k5->out_cc, &my_creds); + if (ret) { + com_err(progname, ret, _("while storing credentials")); + goto cleanup; +diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c +index c5f6bf700..f83c68a99 100644 +--- a/src/clients/kvno/kvno.c ++++ b/src/clients/kvno/kvno.c +@@ -561,7 +561,10 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr, + } + initialized = 1; + } +- ret = krb5_cc_store_cred(context, out_ccache, creds); ++ if (count == 1) ++ ret = k5_cc_store_primary_cred(context, out_ccache, creds); ++ else ++ ret = krb5_cc_store_cred(context, out_ccache, creds); + if (ret) { + com_err(prog, ret, _("while storing creds in output ccache")); + exit(1); +diff --git a/src/include/k5-int.h b/src/include/k5-int.h +index eb18a4cd6..912aaedac 100644 +--- a/src/include/k5-int.h ++++ b/src/include/k5-int.h +@@ -307,6 +307,7 @@ typedef unsigned char u_char; + #define KRB5_CC_CONF_PA_TYPE "pa_type" + #define KRB5_CC_CONF_PROXY_IMPERSONATOR "proxy_impersonator" + #define KRB5_CC_CONF_REFRESH_TIME "refresh_time" ++#define KRB5_CC_CONF_START_REALM "start_realm" + + /* Error codes used in KRB_ERROR protocol messages. + Return values of library routines are based on a different error table +@@ -1910,6 +1911,9 @@ krb5_ser_unpack_bytes(krb5_octet *, size_t, krb5_octet **, size_t *); + krb5_error_code KRB5_CALLCONV + krb5int_cc_default(krb5_context, krb5_ccache *); + ++krb5_error_code ++k5_cc_store_primary_cred(krb5_context, krb5_ccache, krb5_creds *); ++ + /* Fill in the buffer with random alpha-numeric data. */ + krb5_error_code + krb5int_random_string(krb5_context, char *string, unsigned int length); +diff --git a/src/lib/gssapi/krb5/accept_sec_context.c b/src/lib/gssapi/krb5/accept_sec_context.c +index 3d5b84b15..abccb5d11 100644 +--- a/src/lib/gssapi/krb5/accept_sec_context.c ++++ b/src/lib/gssapi/krb5/accept_sec_context.c +@@ -216,7 +216,7 @@ rd_and_store_for_creds(context, auth_context, inbuf, out_cred) + if ((retval = krb5_cc_initialize(context, ccache, creds[0]->client))) + goto cleanup; + +- if ((retval = krb5_cc_store_cred(context, ccache, creds[0]))) ++ if ((retval = k5_cc_store_primary_cred(context, ccache, creds[0]))) + goto cleanup; + + /* generate a delegated credential handle */ +diff --git a/src/lib/krb5/ccache/ccfns.c b/src/lib/krb5/ccache/ccfns.c +index 62a6983d8..23edc2578 100644 +--- a/src/lib/krb5/ccache/ccfns.c ++++ b/src/lib/krb5/ccache/ccfns.c +@@ -297,3 +297,23 @@ krb5_cc_switch(krb5_context context, krb5_ccache cache) + return 0; + return cache->ops->switch_to(context, cache); + } ++ ++krb5_error_code ++k5_cc_store_primary_cred(krb5_context context, krb5_ccache cache, ++ krb5_creds *creds) ++{ ++ krb5_error_code ret; ++ ++ /* Write a start realm if we're writing a TGT and the client realm isn't ++ * the same as the TGS realm. */ ++ if (IS_TGS_PRINC(creds->server) && ++ !data_eq(creds->client->realm, creds->server->data[1])) { ++ ret = krb5_cc_set_config(context, cache, NULL, ++ KRB5_CC_CONF_START_REALM, ++ &creds->server->data[1]); ++ if (ret) ++ return ret; ++ } ++ ++ return krb5_cc_store_cred(context, cache, creds); ++} +diff --git a/src/lib/krb5/krb/get_creds.c b/src/lib/krb5/krb/get_creds.c +index e0a3b5cd8..b40f705fc 100644 +--- a/src/lib/krb5/krb/get_creds.c ++++ b/src/lib/krb5/krb/get_creds.c +@@ -149,7 +149,8 @@ struct _krb5_tkt_creds_context { + krb5_principal client; /* Caller-requested client principal (alias) */ + krb5_principal server; /* Server principal (alias) */ + krb5_principal req_server; /* Caller-requested server principal */ +- krb5_ccache ccache; /* Caller-provided ccache (alias) */ ++ krb5_ccache ccache; /* Caller-provided ccache */ ++ krb5_data start_realm; /* Realm of starting TGT in ccache */ + krb5_flags req_options; /* Caller-requested KRB5_GC_* options */ + krb5_flags req_kdcopt; /* Caller-requested options as KDC options */ + krb5_authdata **authdata; /* Caller-requested authdata */ +@@ -783,7 +784,7 @@ get_cached_local_tgt(krb5_context context, krb5_tkt_creds_context ctx, + return code; + + /* Construct the principal name. */ +- code = krb5int_tgtname(context, &ctx->client->realm, &ctx->client->realm, ++ code = krb5int_tgtname(context, &ctx->start_realm, &ctx->start_realm, + &tgtname); + if (code != 0) + return code; +@@ -821,7 +822,7 @@ init_realm_path(krb5_context context, krb5_tkt_creds_context ctx) + size_t nrealms; + + /* Get the client realm path and count its length. */ +- code = k5_client_realm_path(context, &ctx->client->realm, ++ code = k5_client_realm_path(context, &ctx->start_realm, + &ctx->server->realm, &realm_path); + if (code != 0) + return code; +@@ -933,7 +934,7 @@ step_get_tgt(krb5_context context, krb5_tkt_creds_context ctx) + ctx->cur_realm = path_realm; + ctx->next_realm = ctx->last_realm; + } +- } else if (data_eq(*tgt_realm, ctx->client->realm)) { ++ } else if (data_eq(*tgt_realm, ctx->start_realm)) { + /* We were referred back to the local realm, which is bad. */ + return KRB5_KDCREP_MODIFIED; + } else { +@@ -963,7 +964,7 @@ begin_get_tgt(krb5_context context, krb5_tkt_creds_context ctx) + + ctx->state = STATE_GET_TGT; + +- is_local_service = data_eq(ctx->client->realm, ctx->server->realm); ++ is_local_service = data_eq(ctx->start_realm, ctx->server->realm); + if (!is_local_service) { + /* See if we have a cached TGT for the server realm. */ + code = get_cached_tgt(context, ctx, &ctx->server->realm, &cached_tgt); +@@ -1048,10 +1049,10 @@ begin(krb5_context context, krb5_tkt_creds_context ctx) + if (code != 0 || ctx->state == STATE_COMPLETE) + return code; + +- /* If the server realm is unspecified, start with the client realm. */ ++ /* If the server realm is unspecified, start with the TGT realm. */ + if (krb5_is_referral_realm(&ctx->server->realm)) { + krb5_free_data_contents(context, &ctx->server->realm); +- code = krb5int_copy_data_contents(context, &ctx->client->realm, ++ code = krb5int_copy_data_contents(context, &ctx->start_realm, + &ctx->server->realm); + TRACE_TKT_CREDS_REFERRAL_REALM(context, ctx->server); + if (code != 0) +@@ -1100,6 +1101,18 @@ krb5_tkt_creds_init(krb5_context context, krb5_ccache ccache, + code = krb5_cc_dup(context, ccache, &ctx->ccache); + if (code != 0) + goto cleanup; ++ ++ /* Get the start realm from the cache config, defaulting to the client ++ * realm. */ ++ code = krb5_cc_get_config(context, ccache, NULL, "start_realm", ++ &ctx->start_realm); ++ if (code != 0) { ++ code = krb5int_copy_data_contents(context, &ctx->client->realm, ++ &ctx->start_realm); ++ if (code != 0) ++ goto cleanup; ++ } ++ + code = krb5_copy_authdata(context, in_creds->authdata, &ctx->authdata); + if (code != 0) + goto cleanup; +@@ -1139,6 +1152,7 @@ krb5_tkt_creds_free(krb5_context context, krb5_tkt_creds_context ctx) + krb5int_fast_free_state(context, ctx->fast_state); + krb5_free_creds(context, ctx->in_creds); + krb5_cc_close(context, ctx->ccache); ++ krb5_free_data_contents(context, &ctx->start_realm); + krb5_free_principal(context, ctx->req_server); + krb5_free_authdata(context, ctx->authdata); + krb5_free_creds(context, ctx->cur_tgt); +diff --git a/src/lib/krb5/krb/get_in_tkt.c b/src/lib/krb5/krb/get_in_tkt.c +index cc0f70e83..f5dd7518b 100644 +--- a/src/lib/krb5/krb/get_in_tkt.c ++++ b/src/lib/krb5/krb/get_in_tkt.c +@@ -1779,7 +1779,7 @@ init_creds_step_reply(krb5_context context, + code = krb5_cc_initialize(context, out_ccache, ctx->cred.client); + if (code != 0) + goto cc_cleanup; +- code = krb5_cc_store_cred(context, out_ccache, &ctx->cred); ++ code = k5_cc_store_primary_cred(context, out_ccache, &ctx->cred); + if (code != 0) + goto cc_cleanup; + if (fast_avail) { +diff --git a/src/lib/krb5/libkrb5.exports b/src/lib/krb5/libkrb5.exports +index 5aba29ee4..cab5b3b17 100644 +--- a/src/lib/krb5/libkrb5.exports ++++ b/src/lib/krb5/libkrb5.exports +@@ -125,6 +125,7 @@ k5_add_pa_data_from_data + k5_alloc_pa_data + k5_authind_decode + k5_build_conf_principals ++k5_cc_store_primary_cred + k5_ccselect_free_context + k5_change_error_message_code + k5_etypes_contains +diff --git a/src/lib/krb5_32.def b/src/lib/krb5_32.def +index a0734c729..de5823c17 100644 +--- a/src/lib/krb5_32.def ++++ b/src/lib/krb5_32.def +@@ -499,3 +499,6 @@ EXPORTS + k5_size_context @467 ; PRIVATE GSSAPI + k5_size_keyblock @468 ; PRIVATE GSSAPI + k5_size_principal @469 ; PRIVATE GSSAPI ++ ++; new in 1.19 ++ k5_cc_store_primary_cred @470 ; PRIVATE +diff --git a/src/tests/t_crossrealm.py b/src/tests/t_crossrealm.py +index fa7fd2604..28b397cfb 100755 +--- a/src/tests/t_crossrealm.py ++++ b/src/tests/t_crossrealm.py +@@ -77,6 +77,14 @@ r1, r2, r3 = cross_realms(3, xtgts=((0,1), (1,2)), + {'realm': 'B.X'})) + test_kvno(r1, r3.host_princ, 'KDC domain walk') + check_klist(r1, (tgt(r1, r1), r3.host_princ)) ++ ++# Test start_realm in this setup. ++r1.run([kvno, '--out-cache', r1.ccache, r2.krbtgt_princ]) ++r1.run([klist, '-C'], expected_msg='config: start_realm = X') ++msgs = ('Requesting TGT krbtgt/B.X@X using TGT krbtgt/X@X', ++ 'Received TGT for service realm: krbtgt/B.X@X') ++r1.run([kvno, r3.host_princ], expected_trace=msgs) ++ + stop(r1, r2, r3) + + # Test client capaths. The client in A will ask for a cross TGT to D, +diff --git a/src/tests/t_pkinit.py b/src/tests/t_pkinit.py +index ecd450e8a..f224383c8 100755 +--- a/src/tests/t_pkinit.py ++++ b/src/tests/t_pkinit.py +@@ -130,6 +130,9 @@ realm.run([kvno, realm.host_princ]) + out = realm.run(['./adata', realm.host_princ]) + if '97:' in out: + fail('auth indicators seen in anonymous PKINIT ticket') ++# Verify start_realm setting and test referrals TGS request. ++realm.run([klist, '-C'], expected_msg='start_realm = KRBTEST.COM') ++realm.run([kvno, '-S', 'host', hostname]) + + # Test anonymous kadmin. + mark('anonymous kadmin') diff --git a/SOURCES/Add-three-kvno-options-from-Heimdal-kgetcred.patch b/SOURCES/Add-three-kvno-options-from-Heimdal-kgetcred.patch new file mode 100644 index 0000000..ee42732 --- /dev/null +++ b/SOURCES/Add-three-kvno-options-from-Heimdal-kgetcred.patch @@ -0,0 +1,403 @@ +From a1f38973435b60c7f147abfca12b95c6a0a64406 Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Wed, 17 Jun 2020 20:48:38 -0400 +Subject: [PATCH] Add three kvno options from Heimdal kgetcred + +Add the flags --cached-only and --no-store, which pass the +corresponding options to krb5_get_credentials(). Add the option +--out-cache to write the retrieved credentials to a specified output +cache. + +Add a Python test script for kvno command-line options, including +tests for the new options. + +ticket: 8917 (new) +--- + doc/user/user_commands/kvno.rst | 13 ++++ + src/clients/kvno/Makefile.in | 3 + + src/clients/kvno/kvno.c | 115 +++++++++++++++++++++++--------- + src/clients/kvno/t_kvno.py | 75 +++++++++++++++++++++ + src/man/kvno.man | 13 ++++ + 5 files changed, 187 insertions(+), 32 deletions(-) + create mode 100644 src/clients/kvno/t_kvno.py + +diff --git a/doc/user/user_commands/kvno.rst b/doc/user/user_commands/kvno.rst +index 3892f0ca5..718313576 100644 +--- a/doc/user/user_commands/kvno.rst ++++ b/doc/user/user_commands/kvno.rst +@@ -74,6 +74,19 @@ OPTIONS + client principal with the X.509 certificate in *cert_file*. The + certificate file must be in PEM format. + ++**--cached-only** ++ Only retrieve credentials already present in the cache, not from ++ the KDC. ++ ++**--no-store** ++ Do not store retrieved credentials in the cache. If ++ **--out-cache** is also specified, credentials will still be ++ stored into the output credential cache. ++ ++**--out-cache** *ccache* ++ Initialize *ccache* and store all retrieved credentials into it. ++ Do not store acquired credentials in the input cache. ++ + **--u2u** *ccache* + Requests a user-to-user ticket. *ccache* must contain a local + krbtgt ticket for the server principal. The reported version +diff --git a/src/clients/kvno/Makefile.in b/src/clients/kvno/Makefile.in +index 1c3f79392..5ba877271 100644 +--- a/src/clients/kvno/Makefile.in ++++ b/src/clients/kvno/Makefile.in +@@ -26,6 +26,9 @@ kvno: kvno.o $(KRB5_BASE_DEPLIBS) + ##WIN32## link $(EXE_LINKOPTS) /out:$@ $** + ##WIN32## $(_VC_MANIFEST_EMBED_EXE) + ++check-pytests: kvno ++ $(RUNPYTEST) $(srcdir)/t_kvno.py $(PYTESTFLAGS) ++ + clean-unix:: + $(RM) kvno.o kvno + +diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c +index 2472c0cfe..9d85864f6 100644 +--- a/src/clients/kvno/kvno.c ++++ b/src/clients/kvno/kvno.c +@@ -44,14 +44,17 @@ xusage() + fprintf(stderr, _("usage: %s [-C] [-u] [-c ccache] [-e etype]\n"), prog); + fprintf(stderr, _("\t[-k keytab] [-S sname] [{-I | -U} for_user | " + "[-F cert_file] [-P]]\n")); +- fprintf(stderr, _("\t[--u2u ccache] service1 service2 ...\n")); ++ fprintf(stderr, _("\t[--cached-only] [--no-store] [--out-cache ccache] " ++ "[--u2u ccache]\n")); ++ fprintf(stderr, _("\tservice1 service2 ...\n")); + exit(1); + } + + static void do_v5_kvno(int argc, char *argv[], char *ccachestr, char *etypestr, +- char *keytab_name, char *sname, int canon, int unknown, +- char *for_user, int for_user_enterprise, +- char *for_user_cert_file, int proxy, ++ char *keytab_name, char *sname, int cached_only, ++ int canon, int no_store, int unknown, char *for_user, ++ int for_user_enterprise, char *for_user_cert_file, ++ int proxy, const char *out_ccname, + const char *u2u_ccname); + + #include +@@ -61,18 +64,21 @@ static void extended_com_err_fn(const char *myprog, errcode_t code, + int + main(int argc, char *argv[]) + { +- enum { OPTION_U2U = 256 }; +- struct option lopts[] = { +- { "u2u", 1, NULL, OPTION_U2U }, +- { NULL, 0, NULL, 0 } +- }; ++ enum { OPTION_U2U = 256, OPTION_OUT_CACHE = 257 }; + const char *shopts = "uCc:e:hk:qPS:I:U:F:"; + int option; + char *etypestr = NULL, *ccachestr = NULL, *keytab_name = NULL; + char *sname = NULL, *for_user = NULL, *u2u_ccname = NULL; +- char *for_user_cert_file = NULL; ++ char *for_user_cert_file = NULL, *out_ccname = NULL; + int canon = 0, unknown = 0, proxy = 0, for_user_enterprise = 0; +- int impersonate = 0; ++ int impersonate = 0, cached_only = 0, no_store = 0; ++ struct option lopts[] = { ++ { "cached-only", 0, &cached_only, 1 }, ++ { "no-store", 0, &no_store, 1 }, ++ { "out-cache", 1, NULL, OPTION_OUT_CACHE }, ++ { "u2u", 1, NULL, OPTION_U2U }, ++ { NULL, 0, NULL, 0 } ++ }; + + setlocale(LC_ALL, ""); + set_com_err_hook(extended_com_err_fn); +@@ -135,6 +141,12 @@ main(int argc, char *argv[]) + case OPTION_U2U: + u2u_ccname = optarg; + break; ++ case OPTION_OUT_CACHE: ++ out_ccname = optarg; ++ break; ++ case 0: ++ /* If this option set a flag, do nothing else now. */ ++ break; + default: + xusage(); + break; +@@ -159,8 +171,9 @@ main(int argc, char *argv[]) + xusage(); + + do_v5_kvno(argc - optind, argv + optind, ccachestr, etypestr, keytab_name, +- sname, canon, unknown, for_user, for_user_enterprise, +- for_user_cert_file, proxy, u2u_ccname); ++ sname, cached_only, canon, no_store, unknown, for_user, ++ for_user_enterprise, for_user_cert_file, proxy, out_ccname, ++ u2u_ccname); + return 0; + } + +@@ -274,14 +287,16 @@ static krb5_error_code + kvno(const char *name, krb5_ccache ccache, krb5_principal me, + krb5_enctype etype, krb5_keytab keytab, const char *sname, + krb5_flags options, int unknown, krb5_principal for_user_princ, +- krb5_data *for_user_cert, int proxy, krb5_data *u2u_ticket) ++ krb5_data *for_user_cert, int proxy, krb5_data *u2u_ticket, ++ krb5_creds **creds_out) + { + krb5_error_code ret; + krb5_principal server = NULL; + krb5_ticket *ticket = NULL; +- krb5_creds in_creds, *out_creds = NULL; ++ krb5_creds in_creds, *creds = NULL; + char *princ = NULL; + ++ *creds_out = NULL; + memset(&in_creds, 0, sizeof(in_creds)); + + if (sname != NULL) { +@@ -321,13 +336,12 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me, + in_creds.client = for_user_princ; + in_creds.server = me; + ret = krb5_get_credentials_for_user(context, options, ccache, +- &in_creds, for_user_cert, +- &out_creds); ++ &in_creds, for_user_cert, &creds); + } else { + in_creds.client = me; + in_creds.server = server; + ret = krb5_get_credentials(context, options, ccache, &in_creds, +- &out_creds); ++ &creds); + } + + if (ret) { +@@ -336,7 +350,7 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me, + } + + /* We need a native ticket. */ +- ret = krb5_decode_ticket(&out_creds->ticket, &ticket); ++ ret = krb5_decode_ticket(&creds->ticket, &ticket); + if (ret) { + com_err(prog, ret, _("while decoding ticket for %s"), princ); + goto cleanup; +@@ -362,15 +376,15 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me, + } + + if (proxy) { +- in_creds.client = out_creds->client; +- out_creds->client = NULL; +- krb5_free_creds(context, out_creds); +- out_creds = NULL; ++ in_creds.client = creds->client; ++ creds->client = NULL; ++ krb5_free_creds(context, creds); ++ creds = NULL; + in_creds.server = server; + + ret = krb5_get_credentials_for_proxy(context, KRB5_GC_CANONICALIZE, + ccache, &in_creds, ticket, +- &out_creds); ++ &creds); + krb5_free_principal(context, in_creds.client); + if (ret) { + com_err(prog, ret, _("%s: constrained delegation failed"), +@@ -379,10 +393,13 @@ kvno(const char *name, krb5_ccache ccache, krb5_principal me, + } + } + ++ *creds_out = creds; ++ creds = NULL; ++ + cleanup: + krb5_free_principal(context, server); + krb5_free_ticket(context, ticket); +- krb5_free_creds(context, out_creds); ++ krb5_free_creds(context, creds); + krb5_free_unparsed_name(context, princ); + return ret; + } +@@ -428,19 +445,28 @@ cleanup: + + static void + do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr, +- char *keytab_name, char *sname, int canon, int unknown, +- char *for_user, int for_user_enterprise, +- char *for_user_cert_file, int proxy, const char *u2u_ccname) ++ char *keytab_name, char *sname, int cached_only, int canon, ++ int no_store, int unknown, char *for_user, int for_user_enterprise, ++ char *for_user_cert_file, int proxy, const char *out_ccname, ++ const char *u2u_ccname) + { + krb5_error_code ret; +- int i, errors, flags; ++ int i, errors, flags, initialized = 0; + krb5_enctype etype; +- krb5_ccache ccache; ++ krb5_ccache ccache, out_ccache = NULL; + krb5_principal me; + krb5_keytab keytab = NULL; + krb5_principal for_user_princ = NULL; +- krb5_flags options = canon ? KRB5_GC_CANONICALIZE : 0; ++ krb5_flags options = 0; + krb5_data cert_data = empty_data(), *user_cert = NULL, *u2u_ticket = NULL; ++ krb5_creds *creds; ++ ++ if (canon) ++ options |= KRB5_GC_CANONICALIZE; ++ if (cached_only) ++ options |= KRB5_GC_CACHED; ++ if (no_store || out_ccname != NULL) ++ options |= KRB5_GC_NO_STORE; + + ret = krb5_init_context(&context); + if (ret) { +@@ -467,6 +493,14 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr, + exit(1); + } + ++ if (out_ccname != NULL) { ++ ret = krb5_cc_resolve(context, out_ccname, &out_ccache); ++ if (ret) { ++ com_err(prog, ret, _("while resolving output ccache")); ++ exit(1); ++ } ++ } ++ + if (keytab_name != NULL) { + ret = krb5_kt_resolve(context, keytab_name, &keytab); + if (ret) { +@@ -513,8 +547,25 @@ do_v5_kvno(int count, char *names[], char * ccachestr, char *etypestr, + errors = 0; + for (i = 0; i < count; i++) { + if (kvno(names[i], ccache, me, etype, keytab, sname, options, unknown, +- for_user_princ, user_cert, proxy, u2u_ticket) != 0) ++ for_user_princ, user_cert, proxy, u2u_ticket, &creds) != 0) { + errors++; ++ } else if (out_ccache != NULL) { ++ if (!initialized) { ++ ret = krb5_cc_initialize(context, out_ccache, creds->client); ++ if (ret) { ++ com_err(prog, ret, _("while initializing output ccache")); ++ exit(1); ++ } ++ initialized = 1; ++ } ++ ret = krb5_cc_store_cred(context, out_ccache, creds); ++ if (ret) { ++ com_err(prog, ret, _("while storing creds in output ccache")); ++ exit(1); ++ } ++ } ++ ++ krb5_free_creds(context, creds); + } + + if (keytab != NULL) +diff --git a/src/clients/kvno/t_kvno.py b/src/clients/kvno/t_kvno.py +new file mode 100644 +index 000000000..e98b90e8a +--- /dev/null ++++ b/src/clients/kvno/t_kvno.py +@@ -0,0 +1,75 @@ ++from k5test import * ++ ++realm = K5Realm() ++ ++def check_cache(ccache, expected_services): ++ # Fetch the klist output and skip past the header. ++ lines = realm.run([klist, '-c', ccache]).splitlines() ++ lines = lines[4:] ++ ++ # For each line not beginning with an indent, match against the ++ # expected service principals. ++ svcs = {x: True for x in expected_services} ++ for l in lines: ++ if not l.startswith('\t'): ++ svcprinc = l.split()[4] ++ if svcprinc in svcs: ++ del svcs[svcprinc] ++ else: ++ fail('unexpected service princ ' + svcprinc) ++ ++ if svcs: ++ fail('services not found in klist output: ' + ' '.join(svcs.keys())) ++ ++ ++mark('no options') ++realm.run([kvno, realm.user_princ], expected_msg='user@KRBTEST.COM: kvno = 1') ++check_cache(realm.ccache, [realm.krbtgt_princ, realm.user_princ]) ++ ++mark('-e') ++msgs = ('etypes requested in TGS request: camellia128-cts', ++ '/KDC has no support for encryption type') ++realm.run([kvno, '-e', 'camellia128-cts', realm.host_princ], ++ expected_code=1, expected_trace=msgs) ++ ++mark('--cached-only') ++realm.run([kvno, '--cached-only', realm.user_princ], expected_msg='kvno = 1') ++realm.run([kvno, '--cached-only', realm.host_princ], ++ expected_code=1, expected_msg='Matching credential not found') ++check_cache(realm.ccache, [realm.krbtgt_princ, realm.user_princ]) ++ ++mark('--no-store') ++realm.run([kvno, '--no-store', realm.host_princ], expected_msg='kvno = 1') ++check_cache(realm.ccache, [realm.krbtgt_princ, realm.user_princ]) ++ ++mark('--out-cache') # and multiple services ++out_ccache = os.path.join(realm.testdir, 'ccache.out') ++realm.run([kvno, '--out-cache', out_ccache, ++ realm.host_princ, realm.admin_princ]) ++check_cache(realm.ccache, [realm.krbtgt_princ, realm.user_princ]) ++check_cache(out_ccache, [realm.host_princ, realm.admin_princ]) ++ ++mark('--out-cache --cached-only') # tests out-cache overwriting, and -q ++realm.run([kvno, '--out-cache', out_ccache, '--cached-only', realm.host_princ], ++ expected_code=1, expected_msg='Matching credential not found') ++out = realm.run([kvno, '-q', '--out-cache', out_ccache, '--cached-only', ++ realm.user_princ]) ++if out: ++ fail('unexpected kvno output with -q') ++check_cache(out_ccache, [realm.user_princ]) ++ ++mark('-U') # and -c ++svc_ccache = os.path.join(realm.testdir, 'ccache.svc') ++realm.run([kinit, '-k', '-c', svc_ccache, realm.host_princ]) ++realm.run([kvno, '-c', svc_ccache, '-U', 'user', realm.host_princ]) ++realm.run([klist, '-c', svc_ccache], expected_msg='for client user@') ++realm.run([kvno, '-c', svc_ccache, '-U', 'user', '--out-cache', out_ccache, ++ realm.host_princ]) ++out = realm.run([klist, '-c', out_ccache]) ++if ('Default principal: user@KRBTEST.COM' not in out): ++ fail('wrong default principal in klist output') ++ ++# More S4U options are tested in tests/gssapi/t_s4u.py. ++# --u2u is tested in tests/t_u2u.py. ++ ++success('kvno tests') +diff --git a/src/man/kvno.man b/src/man/kvno.man +index 005a2ec97..b9f6739eb 100644 +--- a/src/man/kvno.man ++++ b/src/man/kvno.man +@@ -95,6 +95,19 @@ Specifies that protocol transition is to be used, identifying the + client principal with the X.509 certificate in \fIcert_file\fP\&. The + certificate file must be in PEM format. + .TP ++\fB\-\-cached\-only\fP ++Only retrieve credentials already present in the cache, not from ++the KDC. ++.TP ++\fB\-\-no\-store\fP ++Do not store retrieved credentials in the cache. If ++\fB\-\-out\-cache\fP is also specified, credentials will still be ++stored into the output credential cache. ++.TP ++\fB\-\-out\-cache\fP \fIccache\fP ++Initialize \fIccache\fP and store all retrieved credentials into it. ++Do not store acquired credentials in the input cache. ++.TP + \fB\-\-u2u\fP \fIccache\fP + Requests a user\-to\-user ticket. \fIccache\fP must contain a local + krbtgt ticket for the server principal. The reported version diff --git a/SOURCES/Document-k-option-in-kvno-1-synopsis.patch b/SOURCES/Document-k-option-in-kvno-1-synopsis.patch index 85fb523..d2c2b0c 100644 --- a/SOURCES/Document-k-option-in-kvno-1-synopsis.patch +++ b/SOURCES/Document-k-option-in-kvno-1-synopsis.patch @@ -1,4 +1,4 @@ -From e9200e874f33defec7193c11a093675b70e588b6 Mon Sep 17 00:00:00 2001 +From 5c1c391a80edd8ceb9e8bba9f7bdfb6639883ae6 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Tue, 24 Nov 2020 12:52:02 -0500 Subject: [PATCH] Document -k option in kvno(1) synopsis @@ -14,7 +14,7 @@ synopsis, option descriptions, and xusage(), but missed one option. 2 files changed, 2 insertions(+) diff --git a/doc/user/user_commands/kvno.rst b/doc/user/user_commands/kvno.rst -index 53e569651..00689ab4c 100644 +index 65c44e1c0..93a5132b2 100644 --- a/doc/user/user_commands/kvno.rst +++ b/doc/user/user_commands/kvno.rst @@ -9,6 +9,7 @@ SYNOPSIS @@ -26,7 +26,7 @@ index 53e569651..00689ab4c 100644 [**-u** | **-S** *sname*] [**-P**] diff --git a/src/man/kvno.man b/src/man/kvno.man -index e156df723..3eeab41b2 100644 +index 22318324d..4e5b43b3b 100644 --- a/src/man/kvno.man +++ b/src/man/kvno.man @@ -35,6 +35,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] diff --git a/SOURCES/Fix-KCM-flag-transmission-for-remove_cred.patch b/SOURCES/Fix-KCM-flag-transmission-for-remove_cred.patch new file mode 100644 index 0000000..34c2a3b --- /dev/null +++ b/SOURCES/Fix-KCM-flag-transmission-for-remove_cred.patch @@ -0,0 +1,105 @@ +From 261b0ed68fb83c34c70679ae8452cae2dba7e4e3 Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Mon, 29 Mar 2021 14:32:56 -0400 +Subject: [PATCH] Fix KCM flag transmission for remove_cred + +MIT krb5 uses low bits for KRB5_TC flags, while Heimdal uses high bits +so that the same flag word can also hold KRB5_GC flags. Add a mapping +function and send the Heimdal flag values when performing a +remove_cred operation. + +ticket: 8995 +(cherry picked from commit 11a82cf424f9c905bb73680c64524f087090d4ef) +(cherry picked from commit 04f0de4420508161ce439f262f2761ff51a07ab0) +(cherry picked from commit ddbb295dee2adcc6cec26944974420bba188f191) +--- + src/include/kcm.h | 19 +++++++++++++++++++ + src/lib/krb5/ccache/cc_kcm.c | 36 +++++++++++++++++++++++++++++++++++- + 2 files changed, 54 insertions(+), 1 deletion(-) + +diff --git a/src/include/kcm.h b/src/include/kcm.h +index e4140c3a0..9b66f1cbd 100644 +--- a/src/include/kcm.h ++++ b/src/include/kcm.h +@@ -56,8 +56,27 @@ + * are marshalled as zero-terminated strings. Principals and credentials are + * marshalled in the v4 FILE ccache format. UUIDs are 16 bytes. UUID lists + * are not delimited, so nothing can come after them. ++ * ++ * Flag words must use Heimdal flag values, which are not the same as MIT krb5 ++ * values for KRB5_GC and KRB5_TC constants. The same flag word may contain ++ * both kinds of flags in Heimdal, but not in MIT krb5. Defines for the ++ * applicable Heimdal flag values are given below using KCM_GC and KCM_TC ++ * prefixes. + */ + ++#define KCM_GC_CACHED (1U << 0) ++ ++#define KCM_TC_DONT_MATCH_REALM (1U << 31) ++#define KCM_TC_MATCH_KEYTYPE (1U << 30) ++#define KCM_TC_MATCH_SRV_NAMEONLY (1U << 29) ++#define KCM_TC_MATCH_FLAGS_EXACT (1U << 28) ++#define KCM_TC_MATCH_FLAGS (1U << 27) ++#define KCM_TC_MATCH_TIMES_EXACT (1U << 26) ++#define KCM_TC_MATCH_TIMES (1U << 25) ++#define KCM_TC_MATCH_AUTHDATA (1U << 24) ++#define KCM_TC_MATCH_2ND_TKT (1U << 23) ++#define KCM_TC_MATCH_IS_SKEY (1U << 22) ++ + /* Opcodes without comments are currently unused in the MIT client + * implementation. */ + typedef enum kcm_opcode { +diff --git a/src/lib/krb5/ccache/cc_kcm.c b/src/lib/krb5/ccache/cc_kcm.c +index 197a10fba..4141140c3 100644 +--- a/src/lib/krb5/ccache/cc_kcm.c ++++ b/src/lib/krb5/ccache/cc_kcm.c +@@ -110,6 +110,40 @@ map_invalid(krb5_error_code code) + KRB5_KCM_MALFORMED_REPLY : code; + } + ++/* ++ * Map an MIT krb5 KRB5_TC flag word to the equivalent Heimdal flag word. Note ++ * that there is no MIT krb5 equivalent for Heimdal's KRB5_TC_DONT_MATCH_REALM ++ * (which is like KRB5_TC_MATCH_SRV_NAMEONLY but also applies to the client ++ * principal) and no Heimdal equivalent for MIT krb5's KRB5_TC_SUPPORTED_KTYPES ++ * (which matches against enctypes from the krb5_context rather than the ++ * matching cred). ++ */ ++static inline krb5_flags ++map_tcflags(krb5_flags mitflags) ++{ ++ krb5_flags heimflags = 0; ++ ++ if (mitflags & KRB5_TC_MATCH_TIMES) ++ heimflags |= KCM_TC_MATCH_TIMES; ++ if (mitflags & KRB5_TC_MATCH_IS_SKEY) ++ heimflags |= KCM_TC_MATCH_IS_SKEY; ++ if (mitflags & KRB5_TC_MATCH_FLAGS) ++ heimflags |= KCM_TC_MATCH_FLAGS; ++ if (mitflags & KRB5_TC_MATCH_TIMES_EXACT) ++ heimflags |= KCM_TC_MATCH_TIMES_EXACT; ++ if (mitflags & KRB5_TC_MATCH_FLAGS_EXACT) ++ heimflags |= KCM_TC_MATCH_FLAGS_EXACT; ++ if (mitflags & KRB5_TC_MATCH_AUTHDATA) ++ heimflags |= KCM_TC_MATCH_AUTHDATA; ++ if (mitflags & KRB5_TC_MATCH_SRV_NAMEONLY) ++ heimflags |= KCM_TC_MATCH_SRV_NAMEONLY; ++ if (mitflags & KRB5_TC_MATCH_2ND_TKT) ++ heimflags |= KCM_TC_MATCH_2ND_TKT; ++ if (mitflags & KRB5_TC_MATCH_KTYPE) ++ heimflags |= KCM_TC_MATCH_KEYTYPE; ++ return heimflags; ++} ++ + /* Begin a request for the given opcode. If cache is non-null, supply the + * cache name as a request parameter. */ + static void +@@ -936,7 +970,7 @@ kcm_remove_cred(krb5_context context, krb5_ccache cache, krb5_flags flags, + struct kcmreq req; + + kcmreq_init(&req, KCM_OP_REMOVE_CRED, cache); +- k5_buf_add_uint32_be(&req.reqbuf, flags); ++ k5_buf_add_uint32_be(&req.reqbuf, map_tcflags(flags)); + k5_marshal_mcred(&req.reqbuf, mcred); + ret = cache_call(context, cache, &req); + kcmreq_free(&req); diff --git a/SOURCES/Fix-KCM-retrieval-support-for-sssd.patch b/SOURCES/Fix-KCM-retrieval-support-for-sssd.patch new file mode 100644 index 0000000..506f039 --- /dev/null +++ b/SOURCES/Fix-KCM-retrieval-support-for-sssd.patch @@ -0,0 +1,64 @@ +From 0bfe0b2bc0a8ee0e9a8cee26528030c16d4fd15f Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Tue, 11 May 2021 14:04:07 -0400 +Subject: [PATCH] Fix KCM retrieval support for sssd + +Commit 795ebba8c039be172ab93cd41105c73ffdba0fdb added a retrieval +handler using KCM_OP_RETRIEVE, falling back on the same error codes as +the previous KCM_OP_GET_CRED_LIST support. But sssd (as of 2.4) +returns KRB5_CC_NOSUPP instead of KRB5_CC_IO if it recognizes an +opcode but does not implement it. Add a helper function to recognize +all known unsupported-opcode error codes, and use it in kcm_retrieve() +and kcm_start_seq_get(). + +ticket: 8997 +(cherry picked from commit da103e36e13f3c846bcddbe38dd518a21e5260a0) +(cherry picked from commit a5b2cff51808cd86fe8195e7ac074ecd25c3344d) +(cherry picked from commit 6a00fd149edd017ece894566771e2e9d4ba089f4) +--- + src/lib/krb5/ccache/cc_kcm.c | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/src/lib/krb5/ccache/cc_kcm.c b/src/lib/krb5/ccache/cc_kcm.c +index b600c6f15..6a36cfdce 100644 +--- a/src/lib/krb5/ccache/cc_kcm.c ++++ b/src/lib/krb5/ccache/cc_kcm.c +@@ -144,6 +144,20 @@ map_tcflags(krb5_flags mitflags) + return heimflags; + } + ++/* ++ * Return true if code could indicate an unsupported operation. Heimdal's KCM ++ * returns KRB5_FCC_INTERNAL. sssd's KCM daemon (as of sssd 2.4) returns ++ * KRB5_CC_NO_SUPP if it recognizes the operation but does not implement it, ++ * and KRB5_CC_IO if it doesn't recognize the operation (which is unfortunate ++ * since it could also indicate a communication failure). ++ */ ++static krb5_boolean ++unsupported_op_error(krb5_error_code code) ++{ ++ return code == KRB5_FCC_INTERNAL || code == KRB5_CC_IO || ++ code == KRB5_CC_NOSUPP; ++} ++ + /* Begin a request for the given opcode. If cache is non-null, supply the + * cache name as a request parameter. */ + static void +@@ -841,7 +855,7 @@ kcm_retrieve(krb5_context context, krb5_ccache cache, krb5_flags flags, + ret = cache_call(context, cache, &req); + + /* Fall back to iteration if the server does not support retrieval. */ +- if (ret == KRB5_FCC_INTERNAL || ret == KRB5_CC_IO) { ++ if (unsupported_op_error(ret)) { + ret = k5_cc_retrieve_cred_default(context, cache, flags, mcred, + cred_out); + goto cleanup; +@@ -922,7 +936,7 @@ kcm_start_seq_get(krb5_context context, krb5_ccache cache, + ret = kcmreq_get_cred_list(&req, &creds); + if (ret) + goto cleanup; +- } else if (ret == KRB5_FCC_INTERNAL || ret == KRB5_CC_IO) { ++ } else if (unsupported_op_error(ret)) { + /* Fall back to GET_CRED_UUID_LIST. */ + kcmreq_free(&req); + kcmreq_init(&req, KCM_OP_GET_CRED_UUID_LIST, cache); diff --git a/SOURCES/Fix-KDC-null-deref-on-TGS-inner-body-null-server.patch b/SOURCES/Fix-KDC-null-deref-on-TGS-inner-body-null-server.patch index f008d83..e756c95 100644 --- a/SOURCES/Fix-KDC-null-deref-on-TGS-inner-body-null-server.patch +++ b/SOURCES/Fix-KDC-null-deref-on-TGS-inner-body-null-server.patch @@ -1,4 +1,4 @@ -From fa1d7d8d5ade1a1ed8279e5f8753776e470e22c1 Mon Sep 17 00:00:00 2001 +From 0a8dfc380fe3b210662ba1b1d452fcec2f84841b Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Tue, 3 Aug 2021 01:15:27 -0400 Subject: [PATCH] Fix KDC null deref on TGS inner body null server @@ -25,7 +25,6 @@ target_version: 1.18-next (cherry picked from commit d775c95af7606a51bf79547a94fa52ddd1cb7f49) (cherry picked from commit bb8fa495d00ccd931eec87a01b8920636cf7903e) (cherry picked from commit dfe383f8251d0edc7e5e08ec5e4fdd9b7f902b2a) -(cherry picked from commit 0a8dfc380fe3b210662ba1b1d452fcec2f84841b) --- src/kdc/do_tgs_req.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/SOURCES/Fix-KDC-null-deref-on-bad-encrypted-challenge.patch b/SOURCES/Fix-KDC-null-deref-on-bad-encrypted-challenge.patch index bc609cb..6929363 100644 --- a/SOURCES/Fix-KDC-null-deref-on-bad-encrypted-challenge.patch +++ b/SOURCES/Fix-KDC-null-deref-on-bad-encrypted-challenge.patch @@ -1,4 +1,4 @@ -From c11cbb496dae9a133e8a3b503d9944392509491f Mon Sep 17 00:00:00 2001 +From 4e8579f0a41b66ed8029f21a52082e1c27ab3996 Mon Sep 17 00:00:00 2001 From: Joseph Sutton Date: Wed, 7 Jul 2021 11:47:44 +1200 Subject: [PATCH] Fix KDC null deref on bad encrypted challenge @@ -26,7 +26,6 @@ ticket: 9007 version_fixed: 1.18.4 (cherry picked from commit c4a406095b3ea4a67ae5b8ea586cbe9abdbae76f) -(cherry picked from commit 4e8579f0a41b66ed8029f21a52082e1c27ab3996) --- src/kdc/kdc_preauth_ec.c | 3 ++- src/tests/Makefile.in | 1 + diff --git a/SOURCES/Fix-leak-in-KERB_AP_OPTIONS_CBT-server-support.patch b/SOURCES/Fix-leak-in-KERB_AP_OPTIONS_CBT-server-support.patch index a0785b9..ab45a2e 100644 --- a/SOURCES/Fix-leak-in-KERB_AP_OPTIONS_CBT-server-support.patch +++ b/SOURCES/Fix-leak-in-KERB_AP_OPTIONS_CBT-server-support.patch @@ -1,4 +1,4 @@ -From ce6defae3595fc3d9980bcf5ddc4f1a6ee90d391 Mon Sep 17 00:00:00 2001 +From 7a87189f7bdabc144e22d4caa6a0785a06416d8f Mon Sep 17 00:00:00 2001 From: Greg Hudson Date: Fri, 24 Jul 2020 16:05:24 -0400 Subject: [PATCH] Fix leak in KERB_AP_OPTIONS_CBT server support diff --git a/SOURCES/Ignore-bad-enctypes-in-krb5_string_to_keysalts.patch b/SOURCES/Ignore-bad-enctypes-in-krb5_string_to_keysalts.patch index da1503e..86f8e85 100644 --- a/SOURCES/Ignore-bad-enctypes-in-krb5_string_to_keysalts.patch +++ b/SOURCES/Ignore-bad-enctypes-in-krb5_string_to_keysalts.patch @@ -1,4 +1,4 @@ -From 087794ce6a9a529f4e6b0474fbfe3b6be3bc01b2 Mon Sep 17 00:00:00 2001 +From 42e29f27ce64fece2839bcce910813e97ca31210 Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Wed, 15 Jul 2020 15:42:20 -0400 Subject: [PATCH] Ignore bad enctypes in krb5_string_to_keysalts() diff --git a/SOURCES/Make-KCM-iteration-fallback-work-with-sssd-kcm.patch b/SOURCES/Make-KCM-iteration-fallback-work-with-sssd-kcm.patch new file mode 100644 index 0000000..784cc4d --- /dev/null +++ b/SOURCES/Make-KCM-iteration-fallback-work-with-sssd-kcm.patch @@ -0,0 +1,28 @@ +From b96983de501f185a06e8b3d2909ef71033bd9e48 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavel=20B=C5=99ezina?= +Date: Tue, 30 Mar 2021 14:35:28 +0200 +Subject: [PATCH] Make KCM iteration fallback work with sssd-kcm + +sssd-kcm returns KRB5_CC_IO if the operation code is not known. + +ticket: 8990 +(cherry picked from commit 06afae820a44c1dc96ad88a0b16c3e50bc938b2a) +(cherry picked from commit 2dbca7e14c945d6394e0e05f285a068dcd541295) +(cherry picked from commit f7702c5b11bdd186d03fed32568c9a252d049d44) +--- + src/lib/krb5/ccache/cc_kcm.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/lib/krb5/ccache/cc_kcm.c b/src/lib/krb5/ccache/cc_kcm.c +index 4141140c3..dae622feb 100644 +--- a/src/lib/krb5/ccache/cc_kcm.c ++++ b/src/lib/krb5/ccache/cc_kcm.c +@@ -876,7 +876,7 @@ kcm_start_seq_get(krb5_context context, krb5_ccache cache, + ret = kcmreq_get_cred_list(&req, &creds); + if (ret) + goto cleanup; +- } else if (ret == KRB5_FCC_INTERNAL) { ++ } else if (ret == KRB5_FCC_INTERNAL || ret == KRB5_CC_IO) { + /* Fall back to GET_CRED_UUID_LIST. */ + kcmreq_free(&req); + kcmreq_init(&req, KCM_OP_GET_CRED_UUID_LIST, cache); diff --git a/SOURCES/Unify-kvno-option-documentation.patch b/SOURCES/Unify-kvno-option-documentation.patch index 0a1c522..8106564 100644 --- a/SOURCES/Unify-kvno-option-documentation.patch +++ b/SOURCES/Unify-kvno-option-documentation.patch @@ -1,4 +1,4 @@ -From 54dade355262fafab54572384c4215cc6c63ecfb Mon Sep 17 00:00:00 2001 +From 6858ecbb9c407ff6d2b22cac283ea2461af1757b Mon Sep 17 00:00:00 2001 From: Robbie Harwood Date: Thu, 20 Aug 2020 17:49:29 -0400 Subject: [PATCH] Unify kvno option documentation @@ -25,15 +25,14 @@ target_version: 1.18-next (cherry picked from commit becd1ad6830b526d08ddaf5b2b6f213154c6446c) (cherry picked from commit 52e3695cc5ef00766e12adfe8ed276c2885e71bb) -[rharwood@redhat.com: backport around added kvno options] --- - doc/user/user_commands/kvno.rst | 17 +++++++++-------- - src/clients/kvno/kvno.c | 12 ++++++++---- - src/man/kvno.man | 17 +++++++++-------- - 3 files changed, 26 insertions(+), 20 deletions(-) + doc/user/user_commands/kvno.rst | 24 +++++++++++++----------- + src/clients/kvno/kvno.c | 15 +++++++++------ + src/man/kvno.man | 24 +++++++++++++----------- + 3 files changed, 35 insertions(+), 28 deletions(-) diff --git a/doc/user/user_commands/kvno.rst b/doc/user/user_commands/kvno.rst -index 3892f0ca5..53e569651 100644 +index 718313576..65c44e1c0 100644 --- a/doc/user/user_commands/kvno.rst +++ b/doc/user/user_commands/kvno.rst @@ -10,13 +10,9 @@ SYNOPSIS @@ -73,11 +72,32 @@ index 3892f0ca5..53e569651 100644 **-P** Specifies that the *service1 service2* ... arguments are to be +@@ -76,16 +77,17 @@ OPTIONS + + **--cached-only** + Only retrieve credentials already present in the cache, not from +- the KDC. ++ the KDC. (Added in release 1.19.) + + **--no-store** + Do not store retrieved credentials in the cache. If + **--out-cache** is also specified, credentials will still be +- stored into the output credential cache. ++ stored into the output credential cache. (Added in release 1.19.) + + **--out-cache** *ccache* + Initialize *ccache* and store all retrieved credentials into it. +- Do not store acquired credentials in the input cache. ++ Do not store acquired credentials in the input cache. (Added in ++ release 1.19.) + + **--u2u** *ccache* + Requests a user-to-user ticket. *ccache* must contain a local diff --git a/src/clients/kvno/kvno.c b/src/clients/kvno/kvno.c -index 2472c0cfe..8edd97361 100644 +index 9d85864f6..c5f6bf700 100644 --- a/src/clients/kvno/kvno.c +++ b/src/clients/kvno/kvno.c -@@ -38,13 +38,17 @@ +@@ -38,15 +38,18 @@ static char *prog; static int quiet = 0; @@ -89,18 +109,21 @@ index 2472c0cfe..8edd97361 100644 - fprintf(stderr, _("usage: %s [-C] [-u] [-c ccache] [-e etype]\n"), prog); - fprintf(stderr, _("\t[-k keytab] [-S sname] [{-I | -U} for_user | " - "[-F cert_file] [-P]]\n")); -- fprintf(stderr, _("\t[--u2u ccache] service1 service2 ...\n")); +- fprintf(stderr, _("\t[--cached-only] [--no-store] [--out-cache ccache] " +- "[--u2u ccache]\n")); +- fprintf(stderr, _("\tservice1 service2 ...\n")); + fprintf(stderr, _("usage: %s [-c ccache] [-e etype] [-k keytab] [-q] " + "[-u | -S sname]" XUSAGE_BREAK + "[[{-F cert_file | {-I | -U} for_user} [-P]] | " + "--u2u ccache]" XUSAGE_BREAK ++ "[--cached-only] [--no-store] [--out-cache] " + "service1 service2 ...\n"), + prog); exit(1); } diff --git a/src/man/kvno.man b/src/man/kvno.man -index 005a2ec97..e156df723 100644 +index b9f6739eb..22318324d 100644 --- a/src/man/kvno.man +++ b/src/man/kvno.man @@ -36,13 +36,9 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] @@ -140,3 +163,24 @@ index 005a2ec97..e156df723 100644 .TP \fB\-P\fP Specifies that the \fIservice1 service2\fP ... arguments are to be +@@ -97,16 +98,17 @@ certificate file must be in PEM format. + .TP + \fB\-\-cached\-only\fP + Only retrieve credentials already present in the cache, not from +-the KDC. ++the KDC. (Added in release 1.19.) + .TP + \fB\-\-no\-store\fP + Do not store retrieved credentials in the cache. If + \fB\-\-out\-cache\fP is also specified, credentials will still be +-stored into the output credential cache. ++stored into the output credential cache. (Added in release 1.19.) + .TP + \fB\-\-out\-cache\fP \fIccache\fP + Initialize \fIccache\fP and store all retrieved credentials into it. +-Do not store acquired credentials in the input cache. ++Do not store acquired credentials in the input cache. (Added in ++release 1.19.) + .TP + \fB\-\-u2u\fP \fIccache\fP + Requests a user\-to\-user ticket. \fIccache\fP must contain a local diff --git a/SOURCES/Use-KCM_OP_RETRIEVE-in-KCM-client.patch b/SOURCES/Use-KCM_OP_RETRIEVE-in-KCM-client.patch new file mode 100644 index 0000000..a401b8a --- /dev/null +++ b/SOURCES/Use-KCM_OP_RETRIEVE-in-KCM-client.patch @@ -0,0 +1,237 @@ +From 00a2ccfeaeac7a0019a73a97cfe33063ba90c7f3 Mon Sep 17 00:00:00 2001 +From: Greg Hudson +Date: Fri, 26 Mar 2021 23:38:54 -0400 +Subject: [PATCH] Use KCM_OP_RETRIEVE in KCM client + +In kcm_retrieve(), try KCM_OP_RETRIEVE. Fall back to iteration if the +server doesn't implement it, or if we can an answer incompatible with +KRB5_TC_SUPPORTED_KTYPES. + +In kcmserver.py, implement partial decoding for creds and cred tags so +that we can do a basic principal name match. + +ticket: 8997 (new) +(cherry picked from commit 795ebba8c039be172ab93cd41105c73ffdba0fdb) +(cherry picked from commit c56d4b87de0f30a38dc61d374ad225d02d581eb3) +(cherry picked from commit ac0a117096324fa73afae291ed467f2ea66e279b) +--- + src/include/kcm.h | 2 +- + src/lib/krb5/ccache/cc_kcm.c | 52 +++++++++++++++++++++++++++++++++--- + src/tests/kcmserver.py | 44 +++++++++++++++++++++++++++--- + src/tests/t_ccache.py | 11 +++++--- + 4 files changed, 99 insertions(+), 10 deletions(-) + +diff --git a/src/include/kcm.h b/src/include/kcm.h +index 9b66f1cbd..85c20d345 100644 +--- a/src/include/kcm.h ++++ b/src/include/kcm.h +@@ -87,7 +87,7 @@ typedef enum kcm_opcode { + KCM_OP_INITIALIZE, /* (name, princ) -> () */ + KCM_OP_DESTROY, /* (name) -> () */ + KCM_OP_STORE, /* (name, cred) -> () */ +- KCM_OP_RETRIEVE, ++ KCM_OP_RETRIEVE, /* (name, flags, credtag) -> (cred) */ + KCM_OP_GET_PRINCIPAL, /* (name) -> (princ) */ + KCM_OP_GET_CRED_UUID_LIST, /* (name) -> (uuid, ...) */ + KCM_OP_GET_CRED_BY_UUID, /* (name, uuid) -> (cred) */ +diff --git a/src/lib/krb5/ccache/cc_kcm.c b/src/lib/krb5/ccache/cc_kcm.c +index dae622feb..b600c6f15 100644 +--- a/src/lib/krb5/ccache/cc_kcm.c ++++ b/src/lib/krb5/ccache/cc_kcm.c +@@ -826,9 +826,55 @@ static krb5_error_code KRB5_CALLCONV + kcm_retrieve(krb5_context context, krb5_ccache cache, krb5_flags flags, + krb5_creds *mcred, krb5_creds *cred_out) + { +- /* There is a KCM opcode for retrieving creds, but Heimdal's client doesn't +- * use it. It causes the KCM daemon to actually make a TGS request. */ +- return k5_cc_retrieve_cred_default(context, cache, flags, mcred, cred_out); ++ krb5_error_code ret; ++ struct kcmreq req = EMPTY_KCMREQ; ++ krb5_creds cred; ++ krb5_enctype *enctypes = NULL; ++ ++ memset(&cred, 0, sizeof(cred)); ++ ++ /* Include KCM_GC_CACHED in flags to prevent Heimdal's sssd from making a ++ * TGS request itself. */ ++ kcmreq_init(&req, KCM_OP_RETRIEVE, cache); ++ k5_buf_add_uint32_be(&req.reqbuf, map_tcflags(flags) | KCM_GC_CACHED); ++ k5_marshal_mcred(&req.reqbuf, mcred); ++ ret = cache_call(context, cache, &req); ++ ++ /* Fall back to iteration if the server does not support retrieval. */ ++ if (ret == KRB5_FCC_INTERNAL || ret == KRB5_CC_IO) { ++ ret = k5_cc_retrieve_cred_default(context, cache, flags, mcred, ++ cred_out); ++ goto cleanup; ++ } ++ if (ret) ++ goto cleanup; ++ ++ ret = k5_unmarshal_cred(req.reply.ptr, req.reply.len, 4, &cred); ++ if (ret) ++ goto cleanup; ++ ++ /* In rare cases we might retrieve a credential with a session key this ++ * context can't support, in which case we must retry using iteration. */ ++ if (flags & KRB5_TC_SUPPORTED_KTYPES) { ++ ret = krb5_get_tgs_ktypes(context, cred.server, &enctypes); ++ if (ret) ++ goto cleanup; ++ if (!k5_etypes_contains(enctypes, cred.keyblock.enctype)) { ++ ret = k5_cc_retrieve_cred_default(context, cache, flags, mcred, ++ cred_out); ++ goto cleanup; ++ } ++ } ++ ++ *cred_out = cred; ++ memset(&cred, 0, sizeof(cred)); ++ ++cleanup: ++ kcmreq_free(&req); ++ krb5_free_cred_contents(context, &cred); ++ free(enctypes); ++ /* Heimdal's KCM returns KRB5_CC_END if no cred is found. */ ++ return (ret == KRB5_CC_END) ? KRB5_CC_NOTFOUND : map_invalid(ret); + } + + static krb5_error_code KRB5_CALLCONV +diff --git a/src/tests/kcmserver.py b/src/tests/kcmserver.py +index 8c5e66ff1..25e6f2bbe 100644 +--- a/src/tests/kcmserver.py ++++ b/src/tests/kcmserver.py +@@ -40,6 +40,7 @@ class KCMOpcodes(object): + INITIALIZE = 4 + DESTROY = 5 + STORE = 6 ++ RETRIEVE = 7 + GET_PRINCIPAL = 8 + GET_CRED_UUID_LIST = 9 + GET_CRED_BY_UUID = 10 +@@ -54,6 +55,7 @@ class KCMOpcodes(object): + + + class KRB5Errors(object): ++ KRB5_CC_NOTFOUND = -1765328243 + KRB5_CC_END = -1765328242 + KRB5_CC_NOSUPP = -1765328137 + KRB5_FCC_NOFILE = -1765328189 +@@ -86,11 +88,29 @@ def get_cache(name): + return cache + + ++def unpack_data(argbytes): ++ dlen, = struct.unpack('>L', argbytes[:4]) ++ return argbytes[4:dlen+4], argbytes[dlen+4:] ++ ++ + def unmarshal_name(argbytes): + offset = argbytes.find(b'\0') + return argbytes[0:offset], argbytes[offset+1:] + + ++def unmarshal_princ(argbytes): ++ # Ignore the type at argbytes[0:4]. ++ ncomps, = struct.unpack('>L', argbytes[4:8]) ++ realm, rest = unpack_data(argbytes[8:]) ++ comps = [] ++ for i in range(ncomps): ++ comp, rest = unpack_data(rest) ++ comps.append(comp) ++ # Asssume no quoting is needed. ++ princ = b'/'.join(comps) + b'@' + realm ++ return princ, rest ++ ++ + def op_gen_new(argbytes): + # Does not actually check for uniqueness. + global next_unique +@@ -126,6 +146,22 @@ def op_store(argbytes): + return 0, b'' + + ++def op_retrieve(argbytes): ++ name, rest = unmarshal_name(argbytes) ++ # Ignore the flags at rest[0:4] and the header at rest[4:8]. ++ # Assume there are client and server creds in the tag and match ++ # only against them. ++ cprinc, rest = unmarshal_princ(rest[8:]) ++ sprinc, rest = unmarshal_princ(rest) ++ cache = get_cache(name) ++ for cred in (cache.creds[u] for u in cache.cred_uuids): ++ cred_cprinc, rest = unmarshal_princ(cred) ++ cred_sprinc, rest = unmarshal_princ(rest) ++ if cred_cprinc == cprinc and cred_sprinc == sprinc: ++ return 0, cred ++ return KRB5Errors.KRB5_CC_NOTFOUND, b'' ++ ++ + def op_get_principal(argbytes): + name, rest = unmarshal_name(argbytes) + cache = get_cache(name) +@@ -199,6 +235,7 @@ ophandlers = { + KCMOpcodes.INITIALIZE : op_initialize, + KCMOpcodes.DESTROY : op_destroy, + KCMOpcodes.STORE : op_store, ++ KCMOpcodes.RETRIEVE : op_retrieve, + KCMOpcodes.GET_PRINCIPAL : op_get_principal, + KCMOpcodes.GET_CRED_UUID_LIST : op_get_cred_uuid_list, + KCMOpcodes.GET_CRED_BY_UUID : op_get_cred_by_uuid, +@@ -243,10 +280,11 @@ def service_request(s): + return True + + parser = optparse.OptionParser() +-parser.add_option('-c', '--credlist', action='store_true', dest='credlist', +- default=False, help='Support KCM_OP_GET_CRED_LIST') ++parser.add_option('-f', '--fallback', action='store_true', dest='fallback', ++ default=False, help='Do not support RETRIEVE/GET_CRED_LIST') + (options, args) = parser.parse_args() +-if not options.credlist: ++if options.fallback: ++ del ophandlers[KCMOpcodes.RETRIEVE] + del ophandlers[KCMOpcodes.GET_CRED_LIST] + + server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) +diff --git a/src/tests/t_ccache.py b/src/tests/t_ccache.py +index 90040fb7b..6ea9fb969 100755 +--- a/src/tests/t_ccache.py ++++ b/src/tests/t_ccache.py +@@ -25,7 +25,7 @@ from k5test import * + kcm_socket_path = os.path.join(os.getcwd(), 'testdir', 'kcm') + conf = {'libdefaults': {'kcm_socket': kcm_socket_path, + 'kcm_mach_service': '-'}} +-realm = K5Realm(create_host=False, krb5_conf=conf) ++realm = K5Realm(krb5_conf=conf) + + keyctl = which('keyctl') + out = realm.run([klist, '-c', 'KEYRING:process:abcd'], expected_code=1) +@@ -71,6 +71,11 @@ def collection_test(realm, ccname): + realm.kinit('alice', password('alice')) + realm.run([klist], expected_msg='Default principal: alice@') + realm.run([klist, '-A', '-s']) ++ realm.run([kvno, realm.host_princ], expected_msg = 'kvno = 1') ++ realm.run([kvno, realm.host_princ], expected_msg = 'kvno = 1') ++ out = realm.run([klist]) ++ if out.count(realm.host_princ) != 1: ++ fail('Wrong number of service tickets in cache') + realm.run([kdestroy]) + output = realm.run([klist], expected_code=1) + if 'No credentials cache' not in output and 'not found' not in output: +@@ -126,14 +131,14 @@ def collection_test(realm, ccname): + + collection_test(realm, 'DIR:' + os.path.join(realm.testdir, 'cc')) + +-# Test KCM without and with GET_CRED_LIST support. ++# Test KCM with and without RETRIEVE and GET_CRED_LIST support. + kcmserver_path = os.path.join(srctop, 'tests', 'kcmserver.py') + kcmd = realm.start_server([sys.executable, kcmserver_path, kcm_socket_path], + 'starting...') + collection_test(realm, 'KCM:') + stop_daemon(kcmd) + os.remove(kcm_socket_path) +-realm.start_server([sys.executable, kcmserver_path, '-c', kcm_socket_path], ++realm.start_server([sys.executable, kcmserver_path, '-f', kcm_socket_path], + 'starting...') + collection_test(realm, 'KCM:') + diff --git a/SOURCES/krb5-krb5kdc.conf b/SOURCES/krb5-krb5kdc.conf index eadeb51..5160b28 100644 --- a/SOURCES/krb5-krb5kdc.conf +++ b/SOURCES/krb5-krb5kdc.conf @@ -1 +1 @@ -d /var/run/krb5kdc 0755 root root +d /run/krb5kdc 0755 root root diff --git a/SPECS/krb5.spec b/SPECS/krb5.spec index fe40f58..5e13c23 100644 --- a/SPECS/krb5.spec +++ b/SPECS/krb5.spec @@ -18,7 +18,7 @@ Summary: The Kerberos network authentication system Name: krb5 Version: 1.18.2 # for prerelease, should be e.g., 0.% {prerelease}.1% { ?dist } (without spaces) -Release: 8.3%{?dist} +Release: 14%{?dist} # lookaside-cached sources; two downloads and a build artifact Source0: https://web.mit.edu/kerberos/dist/krb5/1.18/krb5-%{version}%{prerelease}.tar.gz @@ -71,13 +71,21 @@ Patch125: Implement-KERB_AP_OPTIONS_CBT-server-side.patch Patch126: Add-client_aware_channel_bindings-option.patch Patch127: Pass-channel-bindings-through-SPNEGO.patch Patch128: Add-channel-bindings-tests.patch -Patch129: Ignore-bad-enctypes-in-krb5_string_to_keysalts.patch -Patch130: Fix-leak-in-KERB_AP_OPTIONS_CBT-server-support.patch -Patch131: Unify-kvno-option-documentation.patch -Patch132: Document-k-option-in-kvno-1-synopsis.patch -Patch133: Add-recursion-limit-for-ASN.1-indefinite-lengths.patch -Patch134: Fix-KDC-null-deref-on-bad-encrypted-challenge.patch -Patch135: Fix-KDC-null-deref-on-TGS-inner-body-null-server.patch +Patch129: Add-three-kvno-options-from-Heimdal-kgetcred.patch +Patch130: Ignore-bad-enctypes-in-krb5_string_to_keysalts.patch +Patch131: Fix-leak-in-KERB_AP_OPTIONS_CBT-server-support.patch +Patch132: Unify-kvno-option-documentation.patch +Patch133: Document-k-option-in-kvno-1-synopsis.patch +Patch134: Add-recursion-limit-for-ASN.1-indefinite-lengths.patch +Patch135: Add-support-for-start_realm-cache-config.patch +Patch136: Add-APIs-for-marshalling-credentials.patch +Patch137: Add-KCM_OP_GET_CRED_LIST-for-faster-iteration.patch +Patch138: Fix-KCM-flag-transmission-for-remove_cred.patch +Patch139: Make-KCM-iteration-fallback-work-with-sssd-kcm.patch +Patch140: Use-KCM_OP_RETRIEVE-in-KCM-client.patch +Patch141: Fix-KCM-retrieval-support-for-sssd.patch +Patch142: Fix-KDC-null-deref-on-bad-encrypted-challenge.patch +Patch143: Fix-KDC-null-deref-on-TGS-inner-body-null-server.patch License: MIT URL: http://web.mit.edu/kerberos/www/ @@ -688,17 +696,29 @@ exit 0 %{_libdir}/libkadm5srv_mit.so.* %changelog -* Wed Aug 25 2021 Robbie Harwood - 1.18.2-8.3 +* Wed Aug 25 2021 Robbie Harwood - 1.18.2-14 - Fix KDC null deref on TGS inner body null server (CVE-2021-37750) -- Resolves: #1997600 +- Resolves: #1997601 -* Wed Jul 21 2021 Robbie Harwood - 1.18.2-8.2 -- Rebuild for rpminspect; no code changes -- Resolves: #1983728 - -* Tue Jul 20 2021 Robbie Harwood - 1.18.2-8.1 +* Tue Jul 20 2021 Robbie Harwood - 1.18.2-13 - Fix KDC null deref on bad encrypted challenge (CVE-2021-36222) -- Resolves: #1983728 +- Resolves: #1983729 + +* Thu Jun 10 2021 Robbie Harwood - 1.18.2-12 +- Backport KCM performance enablements +- Resolves: #1956388 + +* Thu Jun 10 2021 Robbie Harwood - 1.18.2-11 +- Add APIs for marshalling credentials +- Resolves: #1964619 + +* Mon May 03 2021 Robbie Harwood - 1.18.2-10 +- Update tmpfiles dropin to use /run instead of /var/run +- Resolves: #1945679 + +* Tue Apr 20 2021 Robbie Harwood - 1.18.2-9 +- Add support for start_realm cache config +- Resolves: #1901195 * Wed Dec 16 2020 Robbie Harwood - 1.18.2-8 - Add recursion limit for ASN.1 indefinite lengths (CVE-2020-28196)