From 2a9e0ef60d206552d2e9fe00508ce8b0fe80448c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Thu, 4 Mar 2021 14:35:45 +0100
Subject: [PATCH 1/3] kcm: remove unneeded kcm.h

This file was copied from MIT Kerberos code, but we do not really
need it.
---
 Makefile.am                    |  1 -
 src/responder/kcm/kcm.c        |  1 -
 src/responder/kcm/kcm.h        | 97 ----------------------------------
 src/responder/kcm/kcmsrv_cmd.c |  1 -
 src/responder/kcm/kcmsrv_ops.c |  6 +--
 src/responder/kcm/kcmsrv_pvt.h |  7 +++
 6 files changed, 10 insertions(+), 103 deletions(-)
 delete mode 100644 src/responder/kcm/kcm.h

diff --git a/Makefile.am b/Makefile.am
index cae9eae838..a9ebc65170 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -770,7 +770,6 @@ dist_noinst_HEADERS = \
     src/responder/secrets/secsrv_private.h \
     src/responder/secrets/secsrv_local.h \
     src/responder/secrets/secsrv_proxy.h \
-    src/responder/kcm/kcm.h \
     src/responder/kcm/kcmsrv_pvt.h \
     src/responder/kcm/kcmsrv_ccache.h \
     src/responder/kcm/kcmsrv_ccache_pvt.h \
diff --git a/src/responder/kcm/kcm.c b/src/responder/kcm/kcm.c
index f1fc958be2..2dc6010b00 100644
--- a/src/responder/kcm/kcm.c
+++ b/src/responder/kcm/kcm.c
@@ -23,7 +23,6 @@
 
 #include <popt.h>
 
-#include "responder/kcm/kcm.h"
 #include "responder/kcm/kcmsrv_ccache.h"
 #include "responder/kcm/kcmsrv_pvt.h"
 #include "responder/common/responder.h"
diff --git a/src/responder/kcm/kcm.h b/src/responder/kcm/kcm.h
deleted file mode 100644
index 1ea7e9bbca..0000000000
--- a/src/responder/kcm/kcm.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/* include/kcm.h - Kerberos cache manager protocol declarations */
-/*
- * Copyright (C) 2014 by the Massachusetts Institute of Technology.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above copyright
- *   notice, this list of conditions and the following disclaimer.
- *
- * * Redistributions in binary form must reproduce the above copyright
- *   notice, this list of conditions and the following disclaimer in
- *   the documentation and/or other materials provided with the
- *   distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
- * OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef KCM_H
-#define KCM_H
-
-#define KCM_PROTOCOL_VERSION_MAJOR 2
-#define KCM_PROTOCOL_VERSION_MINOR 0
-
-#define KCM_UUID_LEN 16
-
-/* This should ideally be in RUNSTATEDIR, but Heimdal uses a hardcoded
- * /var/run, and we need to use the same default path. */
-#define DEFAULT_KCM_SOCKET_PATH "/var/run/.heim_org.h5l.kcm-socket"
-#define DEFAULT_KCM_MACH_SERVICE "org.h5l.kcm"
-
-/*
- * All requests begin with:
- *   major version (1 bytes)
- *   minor version (1 bytes)
- *   opcode (16-bit big-endian)
- *
- * 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
- * marshalled in the v4 FILE ccache format.  UUIDs are 16 bytes.  UUID lists
- * are not delimited, so nothing can come after them.
- */
-
-/* Opcodes without comments are currently unused in the MIT client
- * implementation. */
-typedef enum kcm_opcode {
-    KCM_OP_NOOP,
-    KCM_OP_GET_NAME,
-    KCM_OP_RESOLVE,
-    KCM_OP_GEN_NEW,             /* 0x3                 () -> (name)      */
-    KCM_OP_INITIALIZE,          /* 0x4      (name, princ) -> ()          */
-    KCM_OP_DESTROY,             /* 0x4             (name) -> ()          */
-    KCM_OP_STORE,               /* 0x6       (name, cred) -> ()          */
-    KCM_OP_RETRIEVE,
-    KCM_OP_GET_PRINCIPAL,       /* 0x8             (name) -> (princ)     */
-    KCM_OP_GET_CRED_UUID_LIST,  /* 0x9             (name) -> (uuid, ...) */
-    KCM_OP_GET_CRED_BY_UUID,    /* 0xa       (name, uuid) -> (cred)      */
-    KCM_OP_REMOVE_CRED,         /* (name, flags, credtag) -> ()          */
-    KCM_OP_SET_FLAGS,
-    KCM_OP_CHOWN,
-    KCM_OP_CHMOD,
-    KCM_OP_GET_INITIAL_TICKET,
-    KCM_OP_GET_TICKET,
-    KCM_OP_MOVE_CACHE,
-    KCM_OP_GET_CACHE_UUID_LIST, /* 0x12                () -> (uuid, ...) */
-    KCM_OP_GET_CACHE_BY_UUID,   /* 0x13            (uuid) -> (name)      */
-    KCM_OP_GET_DEFAULT_CACHE,   /* 0x14                () -> (name)      */
-    KCM_OP_SET_DEFAULT_CACHE,   /* 0x15            (name) -> ()          */
-    KCM_OP_GET_KDC_OFFSET,      /* 0x16            (name) -> (offset)    */
-    KCM_OP_SET_KDC_OFFSET,      /* 0x17    (name, offset) -> ()          */
-    KCM_OP_ADD_NTLM_CRED,
-    KCM_OP_HAVE_NTLM_CRED,
-    KCM_OP_DEL_NTLM_CRED,
-    KCM_OP_DO_NTLM_AUTH,
-    KCM_OP_GET_NTLM_USER_LIST,
-
-    KCM_OP_SENTINEL,            /* SSSD addition, not in the MIT header */
-} kcm_opcode;
-
-#endif /* KCM_H */
diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c
index a1aa9aa20f..6b11b18412 100644
--- a/src/responder/kcm/kcmsrv_cmd.c
+++ b/src/responder/kcm/kcmsrv_cmd.c
@@ -26,7 +26,6 @@
 #include "util/util.h"
 #include "responder/common/responder.h"
 #include "responder/kcm/kcmsrv_pvt.h"
-#include "responder/kcm/kcm.h"
 #include "responder/kcm/kcmsrv_ops.h"
 
 /* The first four bytes of a message is always the size */
diff --git a/src/responder/kcm/kcmsrv_ops.c b/src/responder/kcm/kcmsrv_ops.c
index f458c724b0..0634968151 100644
--- a/src/responder/kcm/kcmsrv_ops.c
+++ b/src/responder/kcm/kcmsrv_ops.c
@@ -28,7 +28,6 @@
 #include "util/sss_krb5.h"
 #include "util/sss_ptr_hash.h"
 #include "util/util_creds.h"
-#include "responder/kcm/kcm.h"
 #include "responder/kcm/kcmsrv_pvt.h"
 #include "responder/kcm/kcmsrv_ops.h"
 #include "responder/kcm/kcmsrv_ccache.h"
@@ -2305,14 +2304,15 @@ struct kcm_op *kcm_get_opt(uint16_t opcode)
     DEBUG(SSSDBG_TRACE_INTERNAL,
           "The client requested operation %"PRIu16"\n", opcode);
 
-    if (opcode >= KCM_OP_SENTINEL) {
+    if (opcode >= sizeof(kcm_optable) / sizeof(struct kcm_op)) {
         return NULL;
     }
 
-    op = &kcm_optable[opcode];
+    op = &table[opcode];
     if (op->fn_recv == NULL) {
         op->fn_recv = kcm_op_common_recv;
     }
+
     return op;
 }
 
diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h
index 97f35a701e..a919c03109 100644
--- a/src/responder/kcm/kcmsrv_pvt.h
+++ b/src/responder/kcm/kcmsrv_pvt.h
@@ -28,6 +28,13 @@
 #include <krb5/krb5.h>
 #include "responder/common/responder.h"
 
+#define KCM_PROTOCOL_VERSION_MAJOR 2
+#define KCM_PROTOCOL_VERSION_MINOR 0
+
+/* This should ideally be in RUNSTATEDIR, but Heimdal uses a hardcoded
+ * /var/run, and we need to use the same default path. */
+#define DEFAULT_KCM_SOCKET_PATH "/var/run/.heim_org.h5l.kcm-socket"
+
 /*
  * KCM IO structure
  *

From e82e1f764bb00074c773a875d45b27fa6f70d44a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Thu, 4 Mar 2021 14:38:11 +0100
Subject: [PATCH 2/3] kcm: add support for MIT extensions

---
 src/responder/kcm/kcmsrv_ops.c | 18 +++++++++++++++++-
 1 file changed, 17 insertions(+), 1 deletion(-)

diff --git a/src/responder/kcm/kcmsrv_ops.c b/src/responder/kcm/kcmsrv_ops.c
index 0634968151..4a42b99257 100644
--- a/src/responder/kcm/kcmsrv_ops.c
+++ b/src/responder/kcm/kcmsrv_ops.c
@@ -2297,14 +2297,30 @@ static struct kcm_op kcm_optable[] = {
     { NULL, NULL, NULL }
 };
 
+/* MIT EXTENSIONS. */
+#define KCM_MIT_OFFSET 13000
+static struct kcm_op kcm_mit_optable[] = {
+    { NULL, NULL, NULL }
+};
+
 struct kcm_op *kcm_get_opt(uint16_t opcode)
 {
+    struct kcm_op *table;
     struct kcm_op *op;
+    size_t len;
 
     DEBUG(SSSDBG_TRACE_INTERNAL,
           "The client requested operation %"PRIu16"\n", opcode);
 
-    if (opcode >= sizeof(kcm_optable) / sizeof(struct kcm_op)) {
+    table = kcm_optable;
+    len = sizeof(kcm_optable) / sizeof(struct kcm_op);
+    if (opcode >= KCM_MIT_OFFSET) {
+        opcode -= KCM_MIT_OFFSET;
+        table = kcm_mit_optable;
+        len = sizeof(kcm_mit_optable) / sizeof(struct kcm_op);
+    }
+
+    if (opcode >= len) {
         return NULL;
     }
 

From 08558dc24bbce001f49cd22b0014d4ac0e263f62 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Wed, 3 Mar 2021 09:03:17 +0100
Subject: [PATCH 3/3] kcm: add 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.

Resolves: https://github.com/SSSD/sssd/issues/5545
---
 src/responder/kcm/kcmsrv_ops.c | 123 ++++++++++++++++++++++++++++++++-
 1 file changed, 122 insertions(+), 1 deletion(-)

diff --git a/src/responder/kcm/kcmsrv_ops.c b/src/responder/kcm/kcmsrv_ops.c
index 4a42b99257..6f57f99d64 100644
--- a/src/responder/kcm/kcmsrv_ops.c
+++ b/src/responder/kcm/kcmsrv_ops.c
@@ -2263,6 +2263,125 @@ static errno_t kcm_op_set_kdc_offset_recv(struct tevent_req *req,
     KCM_OP_RET_FROM_TYPE(req, struct kcm_op_set_kdc_offset_state, _op_ret);
 }
 
+static void kcm_op_get_cred_list_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+kcm_op_get_cred_list_send(TALLOC_CTX *mem_ctx,
+                          struct tevent_context *ev,
+                          struct kcm_op_ctx *op_ctx)
+{
+    struct kcm_op_common_state *state;
+    struct tevent_req *subreq;
+    struct tevent_req *req;
+    const char *name;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
+    if (req == NULL) {
+        return NULL;
+    }
+    state->op_ctx = op_ctx;
+
+    ret = sss_iobuf_read_stringz(op_ctx->input, &name);
+    if (ret != EOK) {
+        goto immediate;
+    }
+
+    DEBUG(SSSDBG_TRACE_LIBS, "Returning credentials for %s\n", name);
+
+    subreq = kcm_ccdb_getbyname_send(state, ev,
+                                     op_ctx->kcm_data->db,
+                                     op_ctx->client,
+                                     name);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto immediate;
+    }
+
+    tevent_req_set_callback(subreq, kcm_op_get_cred_list_done, req);
+
+    return req;
+
+immediate:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static void kcm_op_get_cred_list_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req;
+    struct kcm_op_common_state *state;
+    struct kcm_ccache *cc;
+    struct kcm_cred *crd;
+    uint32_t num_creds;
+    struct sss_iobuf *crd_blob;
+    uint8_t *crd_data;
+    uint32_t crd_size;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct kcm_op_common_state);
+
+    ret = kcm_ccdb_getbyname_recv(subreq, state, &cc);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, "Cannot get ccache by name [%d]: %s\n",
+              ret, sss_strerror(ret));
+        goto done;
+    }
+
+    if (cc == NULL) {
+        DEBUG(SSSDBG_MINOR_FAILURE, "No ccache by that name\n");
+        state->op_ret = ERR_NO_CREDS;
+        ret = EOK;
+        goto done;
+    }
+
+    num_creds = 0;
+    for (crd = kcm_cc_get_cred(cc); crd != NULL; crd = kcm_cc_next_cred(crd)) {
+        num_creds++;
+    }
+
+    ret = sss_iobuf_write_uint32(state->op_ctx->reply, htobe32(num_creds));
+    if (ret != EOK) {
+        goto done;
+    }
+
+    for (crd = kcm_cc_get_cred(cc); crd != NULL; crd = kcm_cc_next_cred(crd)) {
+        crd_blob = kcm_cred_get_creds(crd);
+        if (crd_blob == NULL) {
+            DEBUG(SSSDBG_CRIT_FAILURE, "Credentials lack the creds blob\n");
+            ret = ERR_NO_CREDS;
+            goto done;
+        }
+
+        crd_data = sss_iobuf_get_data(crd_blob);
+        crd_size = sss_iobuf_get_size(crd_blob);
+
+        ret = sss_iobuf_write_uint32(state->op_ctx->reply, htobe32(crd_size));
+        if (ret != EOK) {
+            goto done;
+        }
+
+        ret = sss_iobuf_write_len(state->op_ctx->reply, crd_data, crd_size);
+        if (ret != EOK) {
+            goto done;
+        }
+    }
+
+    state->op_ret = EOK;
+    ret = EOK;
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
 static struct kcm_op kcm_optable[] = {
     { "NOOP",                NULL, NULL },
     { "GET_NAME",            NULL, NULL },
@@ -2298,8 +2417,10 @@ static struct kcm_op kcm_optable[] = {
 };
 
 /* MIT EXTENSIONS. */
-#define KCM_MIT_OFFSET 13000
+#define KCM_MIT_OFFSET 13001
 static struct kcm_op kcm_mit_optable[] = {
+    { "GET_CRED_LIST", kcm_op_get_cred_list_send, NULL },
+
     { NULL, NULL, NULL }
 };
 
