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 | ||||
| Name: krb5 | ||||
| Version: 1.19.1 | ||||
| Release: %{?zdpd}5%{?dist} | ||||
| Release: %{?zdpd}6%{?dist} | ||||
| 
 | ||||
| # 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 | ||||
| @ -73,6 +73,8 @@ Patch7: downstream-FIPS-with-PRNG-and-RADIUS-and-MD4.patch | ||||
| Patch8: Add-APIs-for-marshalling-credentials.patch | ||||
| Patch9: Add-hostname-canonicalization-helper-to-k5test.py.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 | ||||
| URL: https://web.mit.edu/kerberos/www/ | ||||
| @ -635,6 +637,9 @@ exit 0 | ||||
| %{_libdir}/libkadm5srv_mit.so.* | ||||
| 
 | ||||
| %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 | ||||
| - Suppress static analyzer warning in FIPS override | ||||
| 
 | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user