Blame SOURCES/0023-KCM-request-parsing-and-sending-a-reply.patch

bb7cd1
From aface2604c53db717299ac3dfe798dfd0c540f99 Mon Sep 17 00:00:00 2001
bb7cd1
From: Jakub Hrozek <jhrozek@redhat.com>
bb7cd1
Date: Fri, 23 Sep 2016 14:00:10 +0200
bb7cd1
Subject: [PATCH 23/36] KCM: request parsing and sending a reply
bb7cd1
MIME-Version: 1.0
bb7cd1
Content-Type: text/plain; charset=UTF-8
bb7cd1
Content-Transfer-Encoding: 8bit
bb7cd1
bb7cd1
Implements parsing the KCM client request into per-client buffers and
bb7cd1
sending a response for both the failure case and for success.
bb7cd1
bb7cd1
The protocol is documented at:
bb7cd1
    http://k5wiki.kerberos.org/wiki/Projects/KCM_client
bb7cd1
bb7cd1
Several places don't use the sss_iobuf structure, because they don't
bb7cd1
parse variable-length data from the buffer and it's much more efficient
bb7cd1
to just allocate the needed request and reply structure on the stack.
bb7cd1
bb7cd1
Reviewed-by: Michal Židek <mzidek@redhat.com>
bb7cd1
Reviewed-by: Simo Sorce <simo@redhat.com>
bb7cd1
---
bb7cd1
 src/responder/kcm/kcmsrv_cmd.c | 467 ++++++++++++++++++++++++++++++++++++++++-
bb7cd1
 src/responder/kcm/kcmsrv_pvt.h |  21 +-
bb7cd1
 2 files changed, 474 insertions(+), 14 deletions(-)
bb7cd1
bb7cd1
diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c
bb7cd1
index e9a03cbd41169c93e00b0630dc1e05e205881ec9..cbf70353730d8a4e03d8f75c97395f4ef007e77f 100644
bb7cd1
--- a/src/responder/kcm/kcmsrv_cmd.c
bb7cd1
+++ b/src/responder/kcm/kcmsrv_cmd.c
bb7cd1
@@ -19,14 +19,430 @@
bb7cd1
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
bb7cd1
 */
bb7cd1
 
bb7cd1
+#include <krb5/krb5.h>
bb7cd1
+
bb7cd1
 #include "config.h"
bb7cd1
 #include "util/util.h"
bb7cd1
+#include "util/sss_iobuf.h"
bb7cd1
 #include "responder/common/responder.h"
bb7cd1
+#include "responder/kcm/kcmsrv_pvt.h"
bb7cd1
+#include "responder/kcm/kcm.h"
bb7cd1
 
bb7cd1
-struct kcm_proto_ctx {
bb7cd1
-    void *unused;
bb7cd1
+/* The first four bytes of a message is always the size */
bb7cd1
+#define KCM_MSG_LEN_SIZE 4
bb7cd1
+
bb7cd1
+/* The return code is 32bits */
bb7cd1
+#define KCM_RETCODE_SIZE 4
bb7cd1
+
bb7cd1
+/* The maximum length of a request or reply as defined by the RPC
bb7cd1
+ * protocol. This is the same constant size as MIT KRB5 uses
bb7cd1
+ */
bb7cd1
+#define KCM_PACKET_MAX_SIZE 2048
bb7cd1
+
bb7cd1
+/* KCM operation, its raw input and raw output and result */
bb7cd1
+struct kcm_op_io {
bb7cd1
+    struct kcm_op *op;
bb7cd1
+    struct kcm_data request;
bb7cd1
+    struct sss_iobuf *reply;
bb7cd1
+};
bb7cd1
+
bb7cd1
+/**
bb7cd1
+ * KCM IO-vector operations
bb7cd1
+ */
bb7cd1
+struct kcm_iovec {
bb7cd1
+    /* We don't use iovec b/c void pointers don't allow for
bb7cd1
+     * pointer arithmetics and it's convenient to keep track
bb7cd1
+     * of processed bytes
bb7cd1
+     */
bb7cd1
+    uint8_t *kiov_base;
bb7cd1
+    size_t kiov_len;
bb7cd1
+    size_t nprocessed;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static errno_t kcm_iovec_op(int fd, struct kcm_iovec *kiov, bool do_read)
bb7cd1
+{
bb7cd1
+    ssize_t len;
bb7cd1
+    struct iovec iov[1];
bb7cd1
+
bb7cd1
+    iov[0].iov_base = kiov->kiov_base + kiov->nprocessed;
bb7cd1
+    iov[0].iov_len = kiov->kiov_len - kiov->nprocessed;
bb7cd1
+    if (iov[0].iov_len == 0) {
bb7cd1
+        /* This iovec is full (read) or depleted (write), proceed to the next one */
bb7cd1
+        return EOK;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    if (do_read) {
bb7cd1
+        len = readv(fd, iov, 1);
bb7cd1
+    } else {
bb7cd1
+        len = writev(fd, iov, 1);
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    if (len == -1) {
bb7cd1
+        if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
bb7cd1
+            return EAGAIN;
bb7cd1
+        } else {
bb7cd1
+            return errno;
bb7cd1
+        }
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    if (len == 0) {
bb7cd1
+        /* Read event on fd that doesn't yield data? error */
bb7cd1
+        return ENODATA;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    /* Decrease the amount of available free space in the iovec */
bb7cd1
+    kiov->nprocessed += len;
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static errno_t kcm_read_iovec(int fd, struct kcm_iovec *kiov)
bb7cd1
+{
bb7cd1
+    return kcm_iovec_op(fd, kiov, true);
bb7cd1
+}
bb7cd1
+
bb7cd1
+static errno_t kcm_write_iovec(int fd, struct kcm_iovec *kiov)
bb7cd1
+{
bb7cd1
+    return kcm_iovec_op(fd, kiov, false);
bb7cd1
+}
bb7cd1
+
bb7cd1
+/**
bb7cd1
+ * Parsing KCM input
bb7cd1
+ *
bb7cd1
+ * The request is received as two IO vectors:
bb7cd1
+ *
bb7cd1
+ * first iovec:
bb7cd1
+ *  length                      32-bit big-endian integer
bb7cd1
+ *
bb7cd1
+ * second iovec:
bb7cd1
+ *  major protocol number       8-bit big-endian integer
bb7cd1
+ *  minor protocol number       8-bit big-endian integer
bb7cd1
+ *  opcode                      16-bit big-endian integer
bb7cd1
+ *  message payload             buffer
bb7cd1
+ */
bb7cd1
+struct kcm_reqbuf {
bb7cd1
+    uint8_t lenbuf[KCM_MSG_LEN_SIZE];
bb7cd1
+    struct kcm_iovec v_len;
bb7cd1
+
bb7cd1
+    /* Includes the major, minor versions etc */
bb7cd1
+    uint8_t msgbuf[KCM_PACKET_MAX_SIZE];
bb7cd1
+    struct kcm_iovec v_msg;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf,
bb7cd1
+                               struct kcm_op_io *op_io)
bb7cd1
+{
bb7cd1
+    size_t lc = 0;
bb7cd1
+    size_t mc = 0;
bb7cd1
+    uint16_t opcode = 0;
bb7cd1
+    uint16_t opcode_be = 0;
bb7cd1
+    uint32_t len_be = 0;
bb7cd1
+    uint32_t msglen;
bb7cd1
+    uint8_t proto_maj = 0;
bb7cd1
+    uint8_t proto_min = 0;
bb7cd1
+
bb7cd1
+    /* The first 4 bytes before the payload is message length */
bb7cd1
+    SAFEALIGN_COPY_UINT32_CHECK(&len_be,
bb7cd1
+                                reqbuf->v_len.kiov_base,
bb7cd1
+                                reqbuf->v_len.kiov_len,
bb7cd1
+                                &lc);
bb7cd1
+    msglen = be32toh(len_be);
bb7cd1
+    DEBUG(SSSDBG_TRACE_LIBS,
bb7cd1
+          "Received message with length %"PRIu32"\n", msglen);
bb7cd1
+
bb7cd1
+    if (msglen == 0) {
bb7cd1
+        DEBUG(SSSDBG_CRIT_FAILURE, "Illegal zero-length message\n");
bb7cd1
+        return EBADMSG;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    if (msglen != reqbuf->v_msg.nprocessed) {
bb7cd1
+        DEBUG(SSSDBG_CRIT_FAILURE,
bb7cd1
+              "Sender claims the message is %"PRIu32" bytes, "
bb7cd1
+              "but received %zu\n",
bb7cd1
+              msglen, reqbuf->v_msg.nprocessed);
bb7cd1
+        return EBADMSG;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    /* First 16 bits are 8 bit major and 8bit major protocol version */
bb7cd1
+    SAFEALIGN_COPY_UINT8_CHECK(&proto_maj,
bb7cd1
+                               reqbuf->v_msg.kiov_base + mc,
bb7cd1
+                               reqbuf->v_msg.kiov_len,
bb7cd1
+                               &mc);
bb7cd1
+    SAFEALIGN_COPY_UINT8_CHECK(&proto_min,
bb7cd1
+                               reqbuf->v_msg.kiov_base + mc,
bb7cd1
+                               reqbuf->v_msg.kiov_len,
bb7cd1
+                               &mc);
bb7cd1
+
bb7cd1
+    if (proto_maj != KCM_PROTOCOL_VERSION_MAJOR) {
bb7cd1
+        DEBUG(SSSDBG_CRIT_FAILURE,
bb7cd1
+              "Expected major version %d, got %"PRIu16"\n",
bb7cd1
+              KCM_PROTOCOL_VERSION_MAJOR, (uint16_t) proto_maj);
bb7cd1
+        return ERR_KCM_MALFORMED_IN_PKT;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    if (proto_min != KCM_PROTOCOL_VERSION_MINOR) {
bb7cd1
+        DEBUG(SSSDBG_CRIT_FAILURE,
bb7cd1
+              "Expected minor version %d, got %"PRIu16"\n",
bb7cd1
+              KCM_PROTOCOL_VERSION_MINOR, (uint16_t) proto_maj);
bb7cd1
+        return ERR_KCM_MALFORMED_IN_PKT;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    SAFEALIGN_COPY_UINT16_CHECK(&opcode_be,
bb7cd1
+                                reqbuf->v_msg.kiov_base + mc,
bb7cd1
+                                reqbuf->v_msg.kiov_len,
bb7cd1
+                                &mc);
bb7cd1
+
bb7cd1
+    opcode = be16toh(opcode_be);
bb7cd1
+    DEBUG(SSSDBG_TRACE_LIBS, "Received operation code %"PRIu16"\n", opcode);
bb7cd1
+
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+/**
bb7cd1
+ * Constructing a reply for failure and success
bb7cd1
+ *
bb7cd1
+ * The reply consists of three IO vectors:
bb7cd1
+ * 1) length iovec:
bb7cd1
+ *  length:     32-bit big-endian
bb7cd1
+ *
bb7cd1
+ * 2) return code iovec:
bb7cd1
+ *  retcode:    32-bit big-endian. Non-zero on failure in the KCM server,
bb7cd1
+ *              zero if the KCM operation ran (even if the operation itself
bb7cd1
+ *              failed)
bb7cd1
+ *
bb7cd1
+ * 3) reply iovec
bb7cd1
+ *  message:    buffer, first 32-bits of the buffer is the return code of
bb7cd1
+ *              the KCM operation, the rest depends on the operation itself.
bb7cd1
+ *              The buffer's length is specified by the first integer in the
bb7cd1
+ *              reply (very intuitive, right?)
bb7cd1
+ *
bb7cd1
+ *  The client always reads the length and return code iovectors. However, the
bb7cd1
+ *  client reads the reply iovec only if retcode is 0 in the return code iovector
bb7cd1
+ *  (see kcmio_unix_socket_read() in the MIT tree)
bb7cd1
+ */
bb7cd1
+struct kcm_repbuf {
bb7cd1
+    uint8_t lenbuf[KCM_MSG_LEN_SIZE];
bb7cd1
+    struct kcm_iovec v_len;
bb7cd1
+
bb7cd1
+    uint8_t rcbuf[KCM_RETCODE_SIZE];
bb7cd1
+    struct kcm_iovec v_rc;
bb7cd1
+
bb7cd1
+    uint8_t msgbuf[KCM_PACKET_MAX_SIZE];
bb7cd1
+    struct kcm_iovec v_msg;
bb7cd1
+};
bb7cd1
+
bb7cd1
+static errno_t kcm_failbuf_construct(errno_t ret,
bb7cd1
+                                     struct kcm_repbuf *repbuf)
bb7cd1
+{
bb7cd1
+    size_t c;
bb7cd1
+
bb7cd1
+    c = 0;
bb7cd1
+    SAFEALIGN_SETMEM_UINT32(repbuf->lenbuf, 0, &c);
bb7cd1
+    c = 0;
bb7cd1
+    SAFEALIGN_SETMEM_UINT32(repbuf->rcbuf, htobe32(ret), &c);
bb7cd1
+
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+/**
bb7cd1
+ * Construct a reply buffer and send it to the KCM client
bb7cd1
+ */
bb7cd1
+static void kcm_reply_error(struct cli_ctx *cctx,
bb7cd1
+                            errno_t retcode,
bb7cd1
+                            struct kcm_repbuf *repbuf)
bb7cd1
+{
bb7cd1
+    errno_t ret;
bb7cd1
+    krb5_error_code kerr;
bb7cd1
+
bb7cd1
+    DEBUG(SSSDBG_OP_FAILURE,
bb7cd1
+          "KCM operation returs failure [%d]: %s\n",
bb7cd1
+          retcode, sss_strerror(retcode));
bb7cd1
+    kerr = sss2krb5_error(retcode);
bb7cd1
+
bb7cd1
+    ret = kcm_failbuf_construct(kerr, repbuf);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        /* If we can't construct the reply buffer, just terminate the client */
bb7cd1
+        talloc_free(cctx);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    TEVENT_FD_WRITEABLE(cctx->cfde);
bb7cd1
+}
bb7cd1
+
bb7cd1
+/**
bb7cd1
+ * Request-reply dispatcher
bb7cd1
+ */
bb7cd1
+struct kcm_req_ctx {
bb7cd1
+    /* client context owns per-client buffers including this one */
bb7cd1
+    struct cli_ctx *cctx;
bb7cd1
+
bb7cd1
+    /* raw IO buffers */
bb7cd1
+    struct kcm_reqbuf reqbuf;
bb7cd1
+    struct kcm_repbuf repbuf;
bb7cd1
+
bb7cd1
+    /* long-lived responder structures */
bb7cd1
+    struct kcm_ctx *kctx;
bb7cd1
+
bb7cd1
+    struct kcm_op_io op_io;
bb7cd1
 };
bb7cd1
 
bb7cd1
+static errno_t kcm_recv_data(int fd, struct kcm_reqbuf *reqbuf)
bb7cd1
+{
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    ret = kcm_read_iovec(fd, &reqbuf->v_len);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        return ret;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ret = kcm_read_iovec(fd, &reqbuf->v_msg);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        return ret;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static struct kcm_req_ctx *kcm_new_req(TALLOC_CTX *mem_ctx,
bb7cd1
+                                       struct cli_ctx *cctx,
bb7cd1
+                                       struct kcm_ctx *kctx)
bb7cd1
+{
bb7cd1
+    struct kcm_req_ctx *req;
bb7cd1
+
bb7cd1
+    req = talloc_zero(cctx, struct kcm_req_ctx);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        return NULL;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    req->reqbuf.v_len.kiov_base = req->reqbuf.lenbuf;
bb7cd1
+    req->reqbuf.v_len.kiov_len = KCM_MSG_LEN_SIZE;
bb7cd1
+
bb7cd1
+    req->reqbuf.v_msg.kiov_base = req->reqbuf.msgbuf;
bb7cd1
+    req->reqbuf.v_msg.kiov_len = KCM_PACKET_MAX_SIZE;
bb7cd1
+
bb7cd1
+    req->repbuf.v_len.kiov_base = req->repbuf.lenbuf;
bb7cd1
+    req->repbuf.v_len.kiov_len = KCM_MSG_LEN_SIZE;
bb7cd1
+
bb7cd1
+    req->repbuf.v_rc.kiov_base = req->repbuf.rcbuf;
bb7cd1
+    req->repbuf.v_rc.kiov_len = KCM_RETCODE_SIZE;
bb7cd1
+
bb7cd1
+    req->repbuf.v_msg.kiov_base = req->repbuf.msgbuf;
bb7cd1
+    /* Length of the msg iobuf will be adjusted later, so far use the full
bb7cd1
+     * length so that constructing the reply can use that capacity
bb7cd1
+     */
bb7cd1
+    req->repbuf.v_msg.kiov_len = KCM_PACKET_MAX_SIZE;
bb7cd1
+
bb7cd1
+    req->cctx = cctx;
bb7cd1
+    req->kctx = kctx;
bb7cd1
+
bb7cd1
+    return req;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_recv(struct cli_ctx *cctx)
bb7cd1
+{
bb7cd1
+    struct kcm_req_ctx *req;
bb7cd1
+    struct kcm_ctx *kctx;
bb7cd1
+    int ret;
bb7cd1
+
bb7cd1
+    kctx = talloc_get_type(cctx->rctx->pvt_ctx, struct kcm_ctx);
bb7cd1
+    req = talloc_get_type(cctx->state_ctx, struct kcm_req_ctx);
bb7cd1
+    if (req == NULL) {
bb7cd1
+        /* A new request comes in, setup data structures */
bb7cd1
+        req = kcm_new_req(cctx, cctx, kctx);
bb7cd1
+        if (req == NULL) {
bb7cd1
+            DEBUG(SSSDBG_CRIT_FAILURE,
bb7cd1
+                  "Cannot set up client connection\n");
bb7cd1
+            talloc_free(cctx);
bb7cd1
+            return;
bb7cd1
+        }
bb7cd1
+
bb7cd1
+        cctx->state_ctx = req;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ret = kcm_recv_data(cctx->cfd, &req->reqbuf);
bb7cd1
+    switch (ret) {
bb7cd1
+    case ENODATA:
bb7cd1
+        DEBUG(SSSDBG_TRACE_ALL, "Client closed connection.\n");
bb7cd1
+        talloc_free(cctx);
bb7cd1
+        return;
bb7cd1
+    case EAGAIN:
bb7cd1
+        DEBUG(SSSDBG_TRACE_ALL, "Retry later\n");
bb7cd1
+        return;
bb7cd1
+    case EOK:
bb7cd1
+        /* all fine */
bb7cd1
+        break;
bb7cd1
+    default:
bb7cd1
+        DEBUG(SSSDBG_FATAL_FAILURE,
bb7cd1
+              "Failed to receive data (%d, %s), aborting client\n",
bb7cd1
+              ret, sss_strerror(ret));
bb7cd1
+        talloc_free(cctx);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ret = kcm_input_parse(&req->reqbuf, &req->op_io);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_FATAL_FAILURE,
bb7cd1
+            "Failed to parse data (%d, %s), aborting client\n",
bb7cd1
+            ret, sss_strerror(ret));
bb7cd1
+        goto fail;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    /* do not read anymore, client is done sending */
bb7cd1
+    TEVENT_FD_NOT_READABLE(cctx->cfde);
bb7cd1
+
bb7cd1
+    kcm_reply_error(cctx, ret, &req->repbuf);
bb7cd1
+    return;
bb7cd1
+
bb7cd1
+fail:
bb7cd1
+    /* Fail with reply */
bb7cd1
+    kcm_reply_error(cctx, ret, &req->repbuf);
bb7cd1
+}
bb7cd1
+
bb7cd1
+static int kcm_send_data(struct cli_ctx *cctx)
bb7cd1
+{
bb7cd1
+    struct kcm_req_ctx *req;
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    req = talloc_get_type(cctx->state_ctx, struct kcm_req_ctx);
bb7cd1
+
bb7cd1
+    ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_len);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        return ret;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_rc);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        return ret;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_msg);
bb7cd1
+    if (ret != EOK) {
bb7cd1
+        return ret;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    return EOK;
bb7cd1
+}
bb7cd1
+
bb7cd1
+static void kcm_send(struct cli_ctx *cctx)
bb7cd1
+{
bb7cd1
+    errno_t ret;
bb7cd1
+
bb7cd1
+    ret = kcm_send_data(cctx);
bb7cd1
+    if (ret == EAGAIN) {
bb7cd1
+        /* not all data was sent, loop again */
bb7cd1
+        return;
bb7cd1
+    } else if (ret != EOK) {
bb7cd1
+        DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting client!\n");
bb7cd1
+        talloc_free(cctx);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    /* ok all sent */
bb7cd1
+    TEVENT_FD_NOT_WRITEABLE(cctx->cfde);
bb7cd1
+    TEVENT_FD_READABLE(cctx->cfde);
bb7cd1
+    talloc_zfree(cctx->state_ctx);
bb7cd1
+    return;
bb7cd1
+}
bb7cd1
+
bb7cd1
 static void kcm_fd_handler(struct tevent_context *ev,
bb7cd1
                            struct tevent_fd *fde,
bb7cd1
                            uint16_t flags, void *ptr)
bb7cd1
@@ -39,25 +455,54 @@ static void kcm_fd_handler(struct tevent_context *ev,
bb7cd1
     if (ret != EOK) {
bb7cd1
         DEBUG(SSSDBG_CRIT_FAILURE,
bb7cd1
               "Could not create idle timer for client. "
bb7cd1
-               "This connection may not auto-terminate\n");
bb7cd1
+              "This connection may not auto-terminate\n");
bb7cd1
         /* Non-fatal, continue */
bb7cd1
     }
bb7cd1
+
bb7cd1
+    if (flags & TEVENT_FD_READ) {
bb7cd1
+        kcm_recv(cctx);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
+    if (flags & TEVENT_FD_WRITE) {
bb7cd1
+        kcm_send(cctx);
bb7cd1
+        return;
bb7cd1
+    }
bb7cd1
 }
bb7cd1
 
bb7cd1
 int kcm_connection_setup(struct cli_ctx *cctx)
bb7cd1
 {
bb7cd1
-    struct kcm_proto_ctx *protocol_ctx;
bb7cd1
-
bb7cd1
-    protocol_ctx = talloc_zero(cctx, struct kcm_proto_ctx);
bb7cd1
-    if (protocol_ctx == NULL) {
bb7cd1
-        return ENOMEM;
bb7cd1
-    }
bb7cd1
-
bb7cd1
-    cctx->protocol_ctx = protocol_ctx;
bb7cd1
     cctx->cfd_handler = kcm_fd_handler;
bb7cd1
     return EOK;
bb7cd1
 }
bb7cd1
 
bb7cd1
+krb5_error_code sss2krb5_error(errno_t err)
bb7cd1
+{
bb7cd1
+    switch (err) {
bb7cd1
+    case EOK:
bb7cd1
+        return 0;
bb7cd1
+    case ENOMEM:
bb7cd1
+        return KRB5_CC_NOMEM;
bb7cd1
+    case EACCES:
bb7cd1
+        return KRB5_FCC_PERM;
bb7cd1
+    case ERR_KCM_OP_NOT_IMPLEMENTED:
bb7cd1
+        return KRB5_CC_NOSUPP;
bb7cd1
+    case ERR_WRONG_NAME_FORMAT:
bb7cd1
+        return KRB5_CC_BADNAME;
bb7cd1
+    case ERR_NO_MATCHING_CREDS:
bb7cd1
+        return KRB5_FCC_NOFILE;
bb7cd1
+    case ERR_NO_CREDS:
bb7cd1
+        return KRB5_CC_NOTFOUND;
bb7cd1
+    case ERR_KCM_CC_END:
bb7cd1
+        return KRB5_CC_END;
bb7cd1
+    case ERR_KCM_MALFORMED_IN_PKT:
bb7cd1
+    case EINVAL:
bb7cd1
+    case EIO:
bb7cd1
+        return KRB5_CC_IO;
bb7cd1
+    }
bb7cd1
+
bb7cd1
+    return KRB5_FCC_INTERNAL;
bb7cd1
+}
bb7cd1
+
bb7cd1
 /* Dummy, not used here but required to link to other responder files */
bb7cd1
 struct cli_protocol_version *register_cli_protocol_version(void)
bb7cd1
 {
bb7cd1
diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h
bb7cd1
index a7c9d062c17f09986d894064176c3a461d396ac0..fd1fd9fa32d59a323d465def68999f24f84e3923 100644
bb7cd1
--- a/src/responder/kcm/kcmsrv_pvt.h
bb7cd1
+++ b/src/responder/kcm/kcmsrv_pvt.h
bb7cd1
@@ -27,13 +27,20 @@
bb7cd1
 #include <sys/types.h>
bb7cd1
 #include "responder/common/responder.h"
bb7cd1
 
bb7cd1
-/* KCM IO structure */
bb7cd1
+/*
bb7cd1
+ * KCM IO structure
bb7cd1
+ *
bb7cd1
+ * In theory we cold use sss_iobuf there, but since iobuf was
bb7cd1
+ * made opaque, this allows it to allocate the structures on
bb7cd1
+ * the stack in one go.
bb7cd1
+ * */
bb7cd1
 struct kcm_data {
bb7cd1
     uint8_t *data;
bb7cd1
     size_t length;
bb7cd1
 };
bb7cd1
 
bb7cd1
-/* To avoid leaking the sssd-specific responder data to other
bb7cd1
+/*
bb7cd1
+ * To avoid leaking the sssd-specific responder data to other
bb7cd1
  * modules, the ccache databases and other KCM specific data
bb7cd1
  * are kept separately
bb7cd1
  */
bb7cd1
@@ -41,7 +48,8 @@ struct kcm_resp_ctx {
bb7cd1
     krb5_context k5c;
bb7cd1
 };
bb7cd1
 
bb7cd1
-/* responder context that contains both the responder data,
bb7cd1
+/*
bb7cd1
+ * responder context that contains both the responder data,
bb7cd1
  * like the ccaches and the sssd-specific stuff like the
bb7cd1
  * generic responder ctx
bb7cd1
  */
bb7cd1
@@ -55,4 +63,11 @@ struct kcm_ctx {
bb7cd1
 
bb7cd1
 int kcm_connection_setup(struct cli_ctx *cctx);
bb7cd1
 
bb7cd1
+/*
bb7cd1
+ * Internally in SSSD-KCM we use SSSD-internal error codes so that we
bb7cd1
+ * can always the same sss_strerror() functions to format the errors
bb7cd1
+ * nicely, but the client expects libkrb5 error codes.
bb7cd1
+ */
bb7cd1
+krb5_error_code sss2krb5_error(errno_t err);
bb7cd1
+
bb7cd1
 #endif /* __KCMSRV_PVT_H__ */
bb7cd1
-- 
bb7cd1
2.9.3
bb7cd1