|
|
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 |
|