|
|
57b2bb |
From d4a512e571a93318d37cbf7d18a120f317b87e97 Mon Sep 17 00:00:00 2001
|
|
|
57b2bb |
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
|
|
|
57b2bb |
Date: Thu, 11 Feb 2021 15:33:10 +0100
|
|
|
57b2bb |
Subject: [PATCH] Add KCM_OP_GET_CRED_LIST for faster iteration
|
|
|
57b2bb |
|
|
|
57b2bb |
For large caches, one IPC operation per credential dominates the cost
|
|
|
57b2bb |
of iteration. Instead transfer the whole list of credentials to the
|
|
|
57b2bb |
client in one IPC operation.
|
|
|
57b2bb |
|
|
|
57b2bb |
Add optional support for the new opcode to the test KCM server to
|
|
|
57b2bb |
allow testing of the main and fallback code paths.
|
|
|
57b2bb |
|
|
|
57b2bb |
[ghudson@mit.edu: fixed memory leaks and potential memory errors;
|
|
|
57b2bb |
adjusted code style and comments; rewrote commit message; added
|
|
|
57b2bb |
kcmserver.py support and tests]
|
|
|
57b2bb |
|
|
|
57b2bb |
ticket: 8990 (new)
|
|
|
57b2bb |
(cherry picked from commit 81bdb47d8ded390263d8ee48f71d5c312b4f1736)
|
|
|
57b2bb |
(cherry picked from commit a0ee8b02e56c65e5dcd569caed0e151cef004ef4)
|
|
|
57b2bb |
(cherry picked from commit baf60dbdeceb3cad35cad7d9930782f94b6c8221)
|
|
|
57b2bb |
---
|
|
|
57b2bb |
src/include/kcm.h | 12 ++-
|
|
|
57b2bb |
src/lib/krb5/ccache/cc_kcm.c | 144 ++++++++++++++++++++++++++++++++---
|
|
|
57b2bb |
src/tests/kcmserver.py | 28 ++++++-
|
|
|
57b2bb |
src/tests/t_ccache.py | 10 ++-
|
|
|
57b2bb |
4 files changed, 175 insertions(+), 19 deletions(-)
|
|
|
57b2bb |
|
|
|
57b2bb |
diff --git a/src/include/kcm.h b/src/include/kcm.h
|
|
|
57b2bb |
index 5ea1447cd..e4140c3a0 100644
|
|
|
57b2bb |
--- a/src/include/kcm.h
|
|
|
57b2bb |
+++ b/src/include/kcm.h
|
|
|
57b2bb |
@@ -51,9 +51,9 @@
|
|
|
57b2bb |
*
|
|
|
57b2bb |
* All replies begin with a 32-bit big-endian reply code.
|
|
|
57b2bb |
*
|
|
|
57b2bb |
- * Parameters are appended to the request or reply with no delimiters. Flags
|
|
|
57b2bb |
- * and time offsets are stored as 32-bit big-endian integers. Names are
|
|
|
57b2bb |
- * marshalled as zero-terminated strings. Principals and credentials are
|
|
|
57b2bb |
+ * Parameters are appended to the request or reply with no delimiters. Flags,
|
|
|
57b2bb |
+ * time offsets, and lengths are stored as 32-bit big-endian integers. Names
|
|
|
57b2bb |
+ * are marshalled as zero-terminated strings. Principals and credentials are
|
|
|
57b2bb |
* marshalled in the v4 FILE ccache format. UUIDs are 16 bytes. UUID lists
|
|
|
57b2bb |
* are not delimited, so nothing can come after them.
|
|
|
57b2bb |
*/
|
|
|
57b2bb |
@@ -89,7 +89,11 @@ typedef enum kcm_opcode {
|
|
|
57b2bb |
KCM_OP_HAVE_NTLM_CRED,
|
|
|
57b2bb |
KCM_OP_DEL_NTLM_CRED,
|
|
|
57b2bb |
KCM_OP_DO_NTLM_AUTH,
|
|
|
57b2bb |
- KCM_OP_GET_NTLM_USER_LIST
|
|
|
57b2bb |
+ KCM_OP_GET_NTLM_USER_LIST,
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ /* MIT extensions */
|
|
|
57b2bb |
+ KCM_OP_MIT_EXTENSION_BASE = 13000,
|
|
|
57b2bb |
+ KCM_OP_GET_CRED_LIST, /* (name) -> (count, count*{len, cred}) */
|
|
|
57b2bb |
} kcm_opcode;
|
|
|
57b2bb |
|
|
|
57b2bb |
#endif /* KCM_H */
|
|
|
57b2bb |
diff --git a/src/lib/krb5/ccache/cc_kcm.c b/src/lib/krb5/ccache/cc_kcm.c
|
|
|
57b2bb |
index a76a285d9..197a10fba 100644
|
|
|
57b2bb |
--- a/src/lib/krb5/ccache/cc_kcm.c
|
|
|
57b2bb |
+++ b/src/lib/krb5/ccache/cc_kcm.c
|
|
|
57b2bb |
@@ -61,6 +61,17 @@ struct uuid_list {
|
|
|
57b2bb |
size_t pos;
|
|
|
57b2bb |
};
|
|
|
57b2bb |
|
|
|
57b2bb |
+struct cred_list {
|
|
|
57b2bb |
+ krb5_creds *creds;
|
|
|
57b2bb |
+ size_t count;
|
|
|
57b2bb |
+ size_t pos;
|
|
|
57b2bb |
+};
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+struct kcm_cursor {
|
|
|
57b2bb |
+ struct uuid_list *uuids;
|
|
|
57b2bb |
+ struct cred_list *creds;
|
|
|
57b2bb |
+};
|
|
|
57b2bb |
+
|
|
|
57b2bb |
struct kcmio {
|
|
|
57b2bb |
SOCKET fd;
|
|
|
57b2bb |
#ifdef __APPLE__
|
|
|
57b2bb |
@@ -489,6 +500,69 @@ free_uuid_list(struct uuid_list *uuids)
|
|
|
57b2bb |
free(uuids);
|
|
|
57b2bb |
}
|
|
|
57b2bb |
|
|
|
57b2bb |
+static void
|
|
|
57b2bb |
+free_cred_list(struct cred_list *list)
|
|
|
57b2bb |
+{
|
|
|
57b2bb |
+ size_t i;
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ if (list == NULL)
|
|
|
57b2bb |
+ return;
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ /* Creds are transferred to the caller as list->pos is incremented, so we
|
|
|
57b2bb |
+ * can start freeing there. */
|
|
|
57b2bb |
+ for (i = list->pos; i < list->count; i++)
|
|
|
57b2bb |
+ krb5_free_cred_contents(NULL, &list->creds[i]);
|
|
|
57b2bb |
+ free(list->creds);
|
|
|
57b2bb |
+ free(list);
|
|
|
57b2bb |
+}
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+/* Fetch a cred list from req->reply. */
|
|
|
57b2bb |
+static krb5_error_code
|
|
|
57b2bb |
+kcmreq_get_cred_list(struct kcmreq *req, struct cred_list **creds_out)
|
|
|
57b2bb |
+{
|
|
|
57b2bb |
+ struct cred_list *list;
|
|
|
57b2bb |
+ const unsigned char *data;
|
|
|
57b2bb |
+ krb5_error_code ret = 0;
|
|
|
57b2bb |
+ size_t count, len, i;
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ *creds_out = NULL;
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ /* Check a rough bound on the count to prevent very large allocations. */
|
|
|
57b2bb |
+ count = k5_input_get_uint32_be(&req->reply);
|
|
|
57b2bb |
+ if (count > req->reply.len / 4)
|
|
|
57b2bb |
+ return KRB5_KCM_MALFORMED_REPLY;
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ list = malloc(sizeof(*list));
|
|
|
57b2bb |
+ if (list == NULL)
|
|
|
57b2bb |
+ return ENOMEM;
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ list->creds = NULL;
|
|
|
57b2bb |
+ list->count = count;
|
|
|
57b2bb |
+ list->pos = 0;
|
|
|
57b2bb |
+ list->creds = k5calloc(count, sizeof(*list->creds), &ret;;
|
|
|
57b2bb |
+ if (list->creds == NULL) {
|
|
|
57b2bb |
+ free(list);
|
|
|
57b2bb |
+ return ret;
|
|
|
57b2bb |
+ }
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ for (i = 0; i < count; i++) {
|
|
|
57b2bb |
+ len = k5_input_get_uint32_be(&req->reply);
|
|
|
57b2bb |
+ data = k5_input_get_bytes(&req->reply, len);
|
|
|
57b2bb |
+ if (data == NULL)
|
|
|
57b2bb |
+ break;
|
|
|
57b2bb |
+ ret = k5_unmarshal_cred(data, len, 4, &list->creds[i]);
|
|
|
57b2bb |
+ if (ret)
|
|
|
57b2bb |
+ break;
|
|
|
57b2bb |
+ }
|
|
|
57b2bb |
+ if (i < count) {
|
|
|
57b2bb |
+ free_cred_list(list);
|
|
|
57b2bb |
+ return (ret == ENOMEM) ? ENOMEM : KRB5_KCM_MALFORMED_REPLY;
|
|
|
57b2bb |
+ }
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ *creds_out = list;
|
|
|
57b2bb |
+ return 0;
|
|
|
57b2bb |
+}
|
|
|
57b2bb |
+
|
|
|
57b2bb |
static void
|
|
|
57b2bb |
kcmreq_free(struct kcmreq *req)
|
|
|
57b2bb |
{
|
|
|
57b2bb |
@@ -753,33 +827,53 @@ kcm_start_seq_get(krb5_context context, krb5_ccache cache,
|
|
|
57b2bb |
{
|
|
|
57b2bb |
krb5_error_code ret;
|
|
|
57b2bb |
struct kcmreq req = EMPTY_KCMREQ;
|
|
|
57b2bb |
- struct uuid_list *uuids;
|
|
|
57b2bb |
+ struct uuid_list *uuids = NULL;
|
|
|
57b2bb |
+ struct cred_list *creds = NULL;
|
|
|
57b2bb |
+ struct kcm_cursor *cursor;
|
|
|
57b2bb |
|
|
|
57b2bb |
*cursor_out = NULL;
|
|
|
57b2bb |
|
|
|
57b2bb |
get_kdc_offset(context, cache);
|
|
|
57b2bb |
|
|
|
57b2bb |
- kcmreq_init(&req, KCM_OP_GET_CRED_UUID_LIST, cache);
|
|
|
57b2bb |
+ kcmreq_init(&req, KCM_OP_GET_CRED_LIST, cache);
|
|
|
57b2bb |
ret = cache_call(context, cache, &req;;
|
|
|
57b2bb |
- if (ret)
|
|
|
57b2bb |
+ if (ret == 0) {
|
|
|
57b2bb |
+ /* GET_CRED_LIST is available. */
|
|
|
57b2bb |
+ ret = kcmreq_get_cred_list(&req, &creds);
|
|
|
57b2bb |
+ if (ret)
|
|
|
57b2bb |
+ goto cleanup;
|
|
|
57b2bb |
+ } else if (ret == KRB5_FCC_INTERNAL) {
|
|
|
57b2bb |
+ /* Fall back to GET_CRED_UUID_LIST. */
|
|
|
57b2bb |
+ kcmreq_free(&req;;
|
|
|
57b2bb |
+ kcmreq_init(&req, KCM_OP_GET_CRED_UUID_LIST, cache);
|
|
|
57b2bb |
+ ret = cache_call(context, cache, &req;;
|
|
|
57b2bb |
+ if (ret)
|
|
|
57b2bb |
+ goto cleanup;
|
|
|
57b2bb |
+ ret = kcmreq_get_uuid_list(&req, &uuids);
|
|
|
57b2bb |
+ if (ret)
|
|
|
57b2bb |
+ goto cleanup;
|
|
|
57b2bb |
+ } else {
|
|
|
57b2bb |
goto cleanup;
|
|
|
57b2bb |
- ret = kcmreq_get_uuid_list(&req, &uuids);
|
|
|
57b2bb |
- if (ret)
|
|
|
57b2bb |
+ }
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ cursor = k5alloc(sizeof(*cursor), &ret;;
|
|
|
57b2bb |
+ if (cursor == NULL)
|
|
|
57b2bb |
goto cleanup;
|
|
|
57b2bb |
- *cursor_out = (krb5_cc_cursor)uuids;
|
|
|
57b2bb |
+ cursor->uuids = uuids;
|
|
|
57b2bb |
+ cursor->creds = creds;
|
|
|
57b2bb |
+ *cursor_out = (krb5_cc_cursor)cursor;
|
|
|
57b2bb |
|
|
|
57b2bb |
cleanup:
|
|
|
57b2bb |
kcmreq_free(&req;;
|
|
|
57b2bb |
return ret;
|
|
|
57b2bb |
}
|
|
|
57b2bb |
|
|
|
57b2bb |
-static krb5_error_code KRB5_CALLCONV
|
|
|
57b2bb |
-kcm_next_cred(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor,
|
|
|
57b2bb |
- krb5_creds *cred_out)
|
|
|
57b2bb |
+static krb5_error_code
|
|
|
57b2bb |
+next_cred_by_uuid(krb5_context context, krb5_ccache cache,
|
|
|
57b2bb |
+ struct uuid_list *uuids, krb5_creds *cred_out)
|
|
|
57b2bb |
{
|
|
|
57b2bb |
krb5_error_code ret;
|
|
|
57b2bb |
struct kcmreq req;
|
|
|
57b2bb |
- struct uuid_list *uuids = (struct uuid_list *)*cursor;
|
|
|
57b2bb |
|
|
|
57b2bb |
memset(cred_out, 0, sizeof(*cred_out));
|
|
|
57b2bb |
|
|
|
57b2bb |
@@ -797,11 +891,39 @@ kcm_next_cred(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor,
|
|
|
57b2bb |
return map_invalid(ret);
|
|
|
57b2bb |
}
|
|
|
57b2bb |
|
|
|
57b2bb |
+static krb5_error_code KRB5_CALLCONV
|
|
|
57b2bb |
+kcm_next_cred(krb5_context context, krb5_ccache cache, krb5_cc_cursor *cursor,
|
|
|
57b2bb |
+ krb5_creds *cred_out)
|
|
|
57b2bb |
+{
|
|
|
57b2bb |
+ struct kcm_cursor *c = (struct kcm_cursor *)*cursor;
|
|
|
57b2bb |
+ struct cred_list *list;
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ if (c->uuids != NULL)
|
|
|
57b2bb |
+ return next_cred_by_uuid(context, cache, c->uuids, cred_out);
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ list = c->creds;
|
|
|
57b2bb |
+ if (list->pos >= list->count)
|
|
|
57b2bb |
+ return KRB5_CC_END;
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ /* Transfer memory ownership of one cred to the caller. */
|
|
|
57b2bb |
+ *cred_out = list->creds[list->pos];
|
|
|
57b2bb |
+ memset(&list->creds[list->pos], 0, sizeof(*list->creds));
|
|
|
57b2bb |
+ list->pos++;
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ return 0;
|
|
|
57b2bb |
+}
|
|
|
57b2bb |
+
|
|
|
57b2bb |
static krb5_error_code KRB5_CALLCONV
|
|
|
57b2bb |
kcm_end_seq_get(krb5_context context, krb5_ccache cache,
|
|
|
57b2bb |
krb5_cc_cursor *cursor)
|
|
|
57b2bb |
{
|
|
|
57b2bb |
- free_uuid_list((struct uuid_list *)*cursor);
|
|
|
57b2bb |
+ struct kcm_cursor *c = *cursor;
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ if (c == NULL)
|
|
|
57b2bb |
+ return 0;
|
|
|
57b2bb |
+ free_uuid_list(c->uuids);
|
|
|
57b2bb |
+ free_cred_list(c->creds);
|
|
|
57b2bb |
+ free(c);
|
|
|
57b2bb |
*cursor = NULL;
|
|
|
57b2bb |
return 0;
|
|
|
57b2bb |
}
|
|
|
57b2bb |
diff --git a/src/tests/kcmserver.py b/src/tests/kcmserver.py
|
|
|
57b2bb |
index 57432e5a7..8c5e66ff1 100644
|
|
|
57b2bb |
--- a/src/tests/kcmserver.py
|
|
|
57b2bb |
+++ b/src/tests/kcmserver.py
|
|
|
57b2bb |
@@ -23,6 +23,7 @@
|
|
|
57b2bb |
# traceback.print_exception(etype, value, tb, file=f)
|
|
|
57b2bb |
# sys.excepthook = ehook
|
|
|
57b2bb |
|
|
|
57b2bb |
+import optparse
|
|
|
57b2bb |
import select
|
|
|
57b2bb |
import socket
|
|
|
57b2bb |
import struct
|
|
|
57b2bb |
@@ -49,12 +50,14 @@ class KCMOpcodes(object):
|
|
|
57b2bb |
SET_DEFAULT_CACHE = 21
|
|
|
57b2bb |
GET_KDC_OFFSET = 22
|
|
|
57b2bb |
SET_KDC_OFFSET = 23
|
|
|
57b2bb |
+ GET_CRED_LIST = 13001
|
|
|
57b2bb |
|
|
|
57b2bb |
|
|
|
57b2bb |
class KRB5Errors(object):
|
|
|
57b2bb |
KRB5_CC_END = -1765328242
|
|
|
57b2bb |
KRB5_CC_NOSUPP = -1765328137
|
|
|
57b2bb |
KRB5_FCC_NOFILE = -1765328189
|
|
|
57b2bb |
+ KRB5_FCC_INTERNAL = -1765328188
|
|
|
57b2bb |
|
|
|
57b2bb |
|
|
|
57b2bb |
def make_uuid():
|
|
|
57b2bb |
@@ -183,6 +186,14 @@ def op_set_kdc_offset(argbytes):
|
|
|
57b2bb |
return 0, b''
|
|
|
57b2bb |
|
|
|
57b2bb |
|
|
|
57b2bb |
+def op_get_cred_list(argbytes):
|
|
|
57b2bb |
+ name, rest = unmarshal_name(argbytes)
|
|
|
57b2bb |
+ cache = get_cache(name)
|
|
|
57b2bb |
+ creds = [cache.creds[u] for u in cache.cred_uuids]
|
|
|
57b2bb |
+ return 0, (struct.pack('>L', len(creds)) +
|
|
|
57b2bb |
+ b''.join(struct.pack('>L', len(c)) + c for c in creds))
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+
|
|
|
57b2bb |
ophandlers = {
|
|
|
57b2bb |
KCMOpcodes.GEN_NEW : op_gen_new,
|
|
|
57b2bb |
KCMOpcodes.INITIALIZE : op_initialize,
|
|
|
57b2bb |
@@ -197,7 +208,8 @@ ophandlers = {
|
|
|
57b2bb |
KCMOpcodes.GET_DEFAULT_CACHE : op_get_default_cache,
|
|
|
57b2bb |
KCMOpcodes.SET_DEFAULT_CACHE : op_set_default_cache,
|
|
|
57b2bb |
KCMOpcodes.GET_KDC_OFFSET : op_get_kdc_offset,
|
|
|
57b2bb |
- KCMOpcodes.SET_KDC_OFFSET : op_set_kdc_offset
|
|
|
57b2bb |
+ KCMOpcodes.SET_KDC_OFFSET : op_set_kdc_offset,
|
|
|
57b2bb |
+ KCMOpcodes.GET_CRED_LIST : op_get_cred_list
|
|
|
57b2bb |
}
|
|
|
57b2bb |
|
|
|
57b2bb |
# Read and respond to a request from the socket s.
|
|
|
57b2bb |
@@ -215,7 +227,11 @@ def service_request(s):
|
|
|
57b2bb |
|
|
|
57b2bb |
majver, minver, op = struct.unpack('>BBH', req[:4])
|
|
|
57b2bb |
argbytes = req[4:]
|
|
|
57b2bb |
- code, payload = ophandlers[op](argbytes)
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+ if op in ophandlers:
|
|
|
57b2bb |
+ code, payload = ophandlers[op](argbytes)
|
|
|
57b2bb |
+ else:
|
|
|
57b2bb |
+ code, payload = KRB5Errors.KRB5_FCC_INTERNAL, b''
|
|
|
57b2bb |
|
|
|
57b2bb |
# The KCM response is the code (4 bytes) and the response payload.
|
|
|
57b2bb |
# The Heimdal IPC response is the length of the KCM response (4
|
|
|
57b2bb |
@@ -226,9 +242,15 @@ def service_request(s):
|
|
|
57b2bb |
s.sendall(hipc_response)
|
|
|
57b2bb |
return True
|
|
|
57b2bb |
|
|
|
57b2bb |
+parser = optparse.OptionParser()
|
|
|
57b2bb |
+parser.add_option('-c', '--credlist', action='store_true', dest='credlist',
|
|
|
57b2bb |
+ default=False, help='Support KCM_OP_GET_CRED_LIST')
|
|
|
57b2bb |
+(options, args) = parser.parse_args()
|
|
|
57b2bb |
+if not options.credlist:
|
|
|
57b2bb |
+ del ophandlers[KCMOpcodes.GET_CRED_LIST]
|
|
|
57b2bb |
|
|
|
57b2bb |
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
|
57b2bb |
-server.bind(sys.argv[1])
|
|
|
57b2bb |
+server.bind(args[0])
|
|
|
57b2bb |
server.listen(5)
|
|
|
57b2bb |
select_input = [server,]
|
|
|
57b2bb |
sys.stderr.write('starting...\n')
|
|
|
57b2bb |
diff --git a/src/tests/t_ccache.py b/src/tests/t_ccache.py
|
|
|
57b2bb |
index 66804afa5..90040fb7b 100755
|
|
|
57b2bb |
--- a/src/tests/t_ccache.py
|
|
|
57b2bb |
+++ b/src/tests/t_ccache.py
|
|
|
57b2bb |
@@ -125,10 +125,18 @@ def collection_test(realm, ccname):
|
|
|
57b2bb |
|
|
|
57b2bb |
|
|
|
57b2bb |
collection_test(realm, 'DIR:' + os.path.join(realm.testdir, 'cc'))
|
|
|
57b2bb |
+
|
|
|
57b2bb |
+# Test KCM without and with GET_CRED_LIST support.
|
|
|
57b2bb |
kcmserver_path = os.path.join(srctop, 'tests', 'kcmserver.py')
|
|
|
57b2bb |
-realm.start_server([sys.executable, kcmserver_path, kcm_socket_path],
|
|
|
57b2bb |
+kcmd = realm.start_server([sys.executable, kcmserver_path, kcm_socket_path],
|
|
|
57b2bb |
+ 'starting...')
|
|
|
57b2bb |
+collection_test(realm, 'KCM:')
|
|
|
57b2bb |
+stop_daemon(kcmd)
|
|
|
57b2bb |
+os.remove(kcm_socket_path)
|
|
|
57b2bb |
+realm.start_server([sys.executable, kcmserver_path, '-c', kcm_socket_path],
|
|
|
57b2bb |
'starting...')
|
|
|
57b2bb |
collection_test(realm, 'KCM:')
|
|
|
57b2bb |
+
|
|
|
57b2bb |
if test_keyring:
|
|
|
57b2bb |
def cleanup_keyring(anchor, name):
|
|
|
57b2bb |
out = realm.run(['keyctl', 'list', anchor])
|