Add KCM_OP_GET_CRED_LIST and KCM_OP_RETRIEVE support
This commit is contained in:
		
							parent
							
								
									69e05d5e39
								
							
						
					
					
						commit
						904d264a41
					
				
							
								
								
									
										358
									
								
								Add-KCM_OP_GET_CRED_LIST-for-faster-iteration.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										358
									
								
								Add-KCM_OP_GET_CRED_LIST-for-faster-iteration.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,358 @@ | |||||||
|  | From dc92022ad26cec8085a852dec6aeba310fa7a751 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com> | ||||||
|  | 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) | ||||||
|  | ---
 | ||||||
|  |  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 9093f894d..772928e4d 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]) | ||||||
							
								
								
									
										235
									
								
								Use-KCM_OP_RETRIEVE-in-KCM-client.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								Use-KCM_OP_RETRIEVE-in-KCM-client.patch
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,235 @@ | |||||||
|  | From 04a810c642245947d5f32a498ed7b1a6f9a11006 Mon Sep 17 00:00:00 2001 | ||||||
|  | From: Greg Hudson <ghudson@mit.edu> | ||||||
|  | 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) | ||||||
|  | ---
 | ||||||
|  |  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 e4140c3a0..5a3e55ce6 100644
 | ||||||
|  | --- a/src/include/kcm.h
 | ||||||
|  | +++ b/src/include/kcm.h
 | ||||||
|  | @@ -68,7 +68,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 772928e4d..80f8bf631 100644
 | ||||||
|  | --- a/src/lib/krb5/ccache/cc_kcm.c
 | ||||||
|  | +++ b/src/lib/krb5/ccache/cc_kcm.c
 | ||||||
|  | @@ -792,9 +792,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:') | ||||||
|  |   | ||||||
| @ -42,7 +42,7 @@ | |||||||
| Summary: The Kerberos network authentication system | Summary: The Kerberos network authentication system | ||||||
| Name: krb5 | Name: krb5 | ||||||
| Version: 1.19.1 | Version: 1.19.1 | ||||||
| Release: %{?zdpd}5%{?dist} | Release: %{?zdpd}6%{?dist} | ||||||
| 
 | 
 | ||||||
| # rharwood has trust path to signing key and verifies on check-in | # rharwood has trust path to signing key and verifies on check-in | ||||||
| Source0: https://web.mit.edu/kerberos/dist/krb5/%{version}/krb5-%{version}%{?dashpre}.tar.gz | Source0: https://web.mit.edu/kerberos/dist/krb5/%{version}/krb5-%{version}%{?dashpre}.tar.gz | ||||||
| @ -73,6 +73,8 @@ Patch7: downstream-FIPS-with-PRNG-and-RADIUS-and-MD4.patch | |||||||
| Patch8: Add-APIs-for-marshalling-credentials.patch | Patch8: Add-APIs-for-marshalling-credentials.patch | ||||||
| Patch9: Add-hostname-canonicalization-helper-to-k5test.py.patch | Patch9: Add-hostname-canonicalization-helper-to-k5test.py.patch | ||||||
| Patch10: Support-host-based-GSS-initiator-names.patch | Patch10: Support-host-based-GSS-initiator-names.patch | ||||||
|  | Patch11: Add-KCM_OP_GET_CRED_LIST-for-faster-iteration.patch | ||||||
|  | Patch12: Use-KCM_OP_RETRIEVE-in-KCM-client.patch | ||||||
| 
 | 
 | ||||||
| License: MIT | License: MIT | ||||||
| URL: https://web.mit.edu/kerberos/www/ | URL: https://web.mit.edu/kerberos/www/ | ||||||
| @ -635,6 +637,9 @@ exit 0 | |||||||
| %{_libdir}/libkadm5srv_mit.so.* | %{_libdir}/libkadm5srv_mit.so.* | ||||||
| 
 | 
 | ||||||
| %changelog | %changelog | ||||||
|  | * Thu May 20 2021 Robbie Harwood <rharwood@redhat.com> - 1.19.1-6 | ||||||
|  | - Add KCM_OP_GET_CRED_LIST and KCM_OP_RETRIEVE support | ||||||
|  | 
 | ||||||
| * Tue May 04 2021 Robbie Harwood <rharwood@redhat.com> - 1.19.1-5 | * Tue May 04 2021 Robbie Harwood <rharwood@redhat.com> - 1.19.1-5 | ||||||
| - Suppress static analyzer warning in FIPS override | - Suppress static analyzer warning in FIPS override | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user