From 5287b672cbba97a5c30ca79954101bd134a30eca Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Fri, 23 Sep 2016 14:49:09 +0200 Subject: [PATCH 26/36] KCM: Implement KCM server operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implements the actual KCM server operations. On a high level, each operation unmarhalls the needed data from the input buffer, calls into the ccache db and marshalls a response. Only the operations that are also implemented by the MIT client are implemented by our KCM server. Resolves: https://pagure.io/SSSD/sssd/issue/2887 Reviewed-by: Michal Židek Reviewed-by: Simo Sorce --- Makefile.am | 2 + src/responder/kcm/kcmsrv_cmd.c | 151 +++- src/responder/kcm/kcmsrv_ops.c | 1954 ++++++++++++++++++++++++++++++++++++++++ src/responder/kcm/kcmsrv_ops.h | 45 + 4 files changed, 2143 insertions(+), 9 deletions(-) create mode 100644 src/responder/kcm/kcmsrv_ops.c create mode 100644 src/responder/kcm/kcmsrv_ops.h diff --git a/Makefile.am b/Makefile.am index 5605c1a53c44fd9e83394e80b7f71828df1d39b6..49b4cabf9ee3ce1417f955c972376894f3709b33 100644 --- a/Makefile.am +++ b/Makefile.am @@ -714,6 +714,7 @@ dist_noinst_HEADERS = \ src/responder/kcm/kcmsrv_ccache.h \ src/responder/kcm/kcmsrv_ccache_pvt.h \ src/responder/kcm/kcmsrv_ccache_be.h \ + src/responder/kcm/kcmsrv_ops.h \ src/sbus/sbus_client.h \ src/sbus/sssd_dbus.h \ src/sbus/sssd_dbus_meta.h \ @@ -1493,6 +1494,7 @@ sssd_kcm_SOURCES = \ src/responder/kcm/kcmsrv_cmd.c \ src/responder/kcm/kcmsrv_ccache.c \ src/responder/kcm/kcmsrv_ccache_mem.c \ + src/responder/kcm/kcmsrv_ops.c \ src/util/sss_sockets.c \ src/util/sss_krb5.c \ src/util/sss_iobuf.c \ diff --git a/src/responder/kcm/kcmsrv_cmd.c b/src/responder/kcm/kcmsrv_cmd.c index cbf70353730d8a4e03d8f75c97395f4ef007e77f..537e88953fd1a190a9a73bcdd430d8e0db8f9291 100644 --- a/src/responder/kcm/kcmsrv_cmd.c +++ b/src/responder/kcm/kcmsrv_cmd.c @@ -23,10 +23,10 @@ #include "config.h" #include "util/util.h" -#include "util/sss_iobuf.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 */ #define KCM_MSG_LEN_SIZE 4 @@ -133,7 +133,6 @@ static errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf, { size_t lc = 0; size_t mc = 0; - uint16_t opcode = 0; uint16_t opcode_be = 0; uint32_t len_be = 0; uint32_t msglen; @@ -162,7 +161,7 @@ static errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf, return EBADMSG; } - /* First 16 bits are 8 bit major and 8bit major protocol version */ + /* First 16 bits are 8 bit major and 8bit minor protocol version */ SAFEALIGN_COPY_UINT8_CHECK(&proto_maj, reqbuf->v_msg.kiov_base + mc, reqbuf->v_msg.kiov_len, @@ -191,8 +190,16 @@ static errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf, reqbuf->v_msg.kiov_len, &mc); - opcode = be16toh(opcode_be); - DEBUG(SSSDBG_TRACE_LIBS, "Received operation code %"PRIu16"\n", opcode); + op_io->op = kcm_get_opt(be16toh(opcode_be)); + if (op_io->op == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Did not find a KCM operation handler for the requested opcode\n"); + return ERR_KCM_MALFORMED_IN_PKT; + } + + /* The operation only receives the payload, not the opcode or the protocol info */ + op_io->request.data = reqbuf->v_msg.kiov_base + mc; + op_io->request.length = reqbuf->v_msg.nprocessed - mc; return EOK; } @@ -240,6 +247,46 @@ static errno_t kcm_failbuf_construct(errno_t ret, c = 0; SAFEALIGN_SETMEM_UINT32(repbuf->rcbuf, htobe32(ret), &c); + DEBUG(SSSDBG_TRACE_LIBS, "Sent reply with error %d\n", ret); + return EOK; +} + +/* retcode is 0 if the operation at least ran, non-zero if there + * was some kind of internal KCM error, like input couldn't be parsed + */ +static errno_t kcm_output_construct(struct kcm_op_io *op_io, + struct kcm_repbuf *repbuf) +{ + size_t c; + size_t replen; + + replen = sss_iobuf_get_len(op_io->reply); + if (replen > KCM_PACKET_MAX_SIZE) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Reply exceeds the KCM protocol limit, aborting\n"); + return E2BIG; + } + + DEBUG(SSSDBG_TRACE_LIBS, + "Sending a reply with %zu bytes of payload\n", replen); + c = 0; + SAFEALIGN_SETMEM_UINT32(repbuf->lenbuf, htobe32(replen), &c); + + c = 0; + SAFEALIGN_SETMEM_UINT32(repbuf->rcbuf, 0, &c); + + if (replen > 0) { + c = 0; + SAFEALIGN_MEMCPY_CHECK(repbuf->msgbuf, + sss_iobuf_get_data(op_io->reply), + replen, + repbuf->v_msg.kiov_len, + &c); + + /* Length of the buffer to send to KCM client */ + repbuf->v_msg.kiov_len = replen; + } + return EOK; } @@ -260,7 +307,8 @@ static void kcm_reply_error(struct cli_ctx *cctx, ret = kcm_failbuf_construct(kerr, repbuf); if (ret != EOK) { - /* If we can't construct the reply buffer, just terminate the client */ + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot construct the reply buffer, terminating client\n"); talloc_free(cctx); return; } @@ -268,6 +316,24 @@ static void kcm_reply_error(struct cli_ctx *cctx, TEVENT_FD_WRITEABLE(cctx->cfde); } +static void kcm_send_reply(struct cli_ctx *cctx, + struct kcm_op_io *op_io, + struct kcm_repbuf *repbuf) +{ + errno_t ret; + + DEBUG(SSSDBG_TRACE_INTERNAL, "Sending a reply\n"); + ret = kcm_output_construct(op_io, repbuf); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot construct the reply buffer, terminating client\n"); + kcm_reply_error(cctx, ret, repbuf); + return; + } + + TEVENT_FD_WRITEABLE(cctx->cfde); +} + /** * Request-reply dispatcher */ @@ -285,17 +351,67 @@ struct kcm_req_ctx { struct kcm_op_io op_io; }; +static void kcm_cmd_request_done(struct tevent_req *req); + +static errno_t kcm_cmd_dispatch(struct kcm_req_ctx *req_ctx) +{ + struct tevent_req *req; + struct cli_ctx *cctx; + + cctx = req_ctx->cctx; + + req = kcm_cmd_send(req_ctx, cctx->ev, req_ctx->kctx->kcm_data, + req_ctx->cctx->creds, + &req_ctx->op_io.request, + req_ctx->op_io.op); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Failed to schedule KCM operation.\n"); + return ENOMEM; + } + + tevent_req_set_callback(req, kcm_cmd_request_done, req_ctx); + return EOK; +} + +static void kcm_cmd_request_done(struct tevent_req *req) +{ + struct kcm_req_ctx *req_ctx; + struct cli_ctx *cctx; + errno_t ret; + + req_ctx = tevent_req_callback_data(req, struct kcm_req_ctx); + cctx = req_ctx->cctx; + + ret = kcm_cmd_recv(req_ctx, req, + &req_ctx->op_io.reply); + talloc_free(req); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "KCM operation failed [%d]: %s\n", ret, sss_strerror(ret)); + kcm_reply_error(cctx, ret, &req_ctx->repbuf); + return; + } + + kcm_send_reply(cctx, &req_ctx->op_io, &req_ctx->repbuf); +} + static errno_t kcm_recv_data(int fd, struct kcm_reqbuf *reqbuf) { errno_t ret; ret = kcm_read_iovec(fd, &reqbuf->v_len); if (ret != EOK) { + /* Not all errors are fatal, hence we don't print DEBUG messages + * here, but in the caller + */ return ret; } ret = kcm_read_iovec(fd, &reqbuf->v_msg); if (ret != EOK) { + /* Not all errors are fatal, hence we don't print DEBUG messages + * here, but in the caller + */ return ret; } @@ -389,7 +505,15 @@ static void kcm_recv(struct cli_ctx *cctx) /* do not read anymore, client is done sending */ TEVENT_FD_NOT_READABLE(cctx->cfde); - kcm_reply_error(cctx, ret, &req->repbuf); + ret = kcm_cmd_dispatch(req); + if (ret != EOK) { + DEBUG(SSSDBG_FATAL_FAILURE, + "Failed to dispatch KCM operation [%d]: %s\n", + ret, sss_strerror(ret)); + goto fail; + } + + /* Dispatched request resumes in kcm_cmd_request_done */ return; fail: @@ -406,16 +530,25 @@ static int kcm_send_data(struct cli_ctx *cctx) ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_len); if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to write the length iovec [%d]: %s\n", + ret, sss_strerror(ret)); return ret; } ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_rc); if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to write the retcode iovec [%d]: %s\n", + ret, sss_strerror(ret)); return ret; } ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_msg); if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Failed to write the msg iovec [%d]: %s\n", + ret, sss_strerror(ret)); return ret; } @@ -428,7 +561,7 @@ static void kcm_send(struct cli_ctx *cctx) ret = kcm_send_data(cctx); if (ret == EAGAIN) { - /* not all data was sent, loop again */ + DEBUG(SSSDBG_TRACE_ALL, "Sending data again..\n"); return; } else if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting client!\n"); @@ -436,7 +569,7 @@ static void kcm_send(struct cli_ctx *cctx) return; } - /* ok all sent */ + DEBUG(SSSDBG_TRACE_INTERNAL, "All data sent!\n"); TEVENT_FD_NOT_WRITEABLE(cctx->cfde); TEVENT_FD_READABLE(cctx->cfde); talloc_zfree(cctx->state_ctx); diff --git a/src/responder/kcm/kcmsrv_ops.c b/src/responder/kcm/kcmsrv_ops.c new file mode 100644 index 0000000000000000000000000000000000000000..50e8cc635424e15d53e3c8d122c5525044f59c8a --- /dev/null +++ b/src/responder/kcm/kcmsrv_ops.c @@ -0,0 +1,1954 @@ +/* + SSSD + + KCM Server - the KCM server operations + + Copyright (C) Red Hat, 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "config.h" + +#include + +#include "util/sss_iobuf.h" +#include "util/sss_krb5.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" + +#define KCM_REPLY_MAX 2048 + +struct kcm_op_ctx { + struct kcm_resp_ctx *kcm_data; + struct cli_creds *client; + + struct sss_iobuf *input; + struct sss_iobuf *reply; +}; + +/* Each operation follows the same pattern and is implemented using + * functions with this prototype. The operation receives an op_ctx + * that serves as a state of the operation and can be used to keep + * track of any temporary data. The operation writes its output data + * into the op_ctx reply IO buffer and returns the op_ret status code + * separately. + * + * The operation always returns EOK unless an internal error occurs, + * the result of the operation is stored in the op_ret variable + */ +typedef struct tevent_req* +(*kcm_srv_send_method)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx); +typedef errno_t +(*kcm_srv_recv_method)(struct tevent_req *req, + uint32_t *_op_ret); + +struct kcm_op { + const char *name; + kcm_srv_send_method fn_send; + kcm_srv_recv_method fn_recv; +}; + +struct kcm_cmd_state { + struct kcm_op *op; + + struct kcm_op_ctx *op_ctx; + struct sss_iobuf *reply; + + uint32_t op_ret; +}; + +static void kcm_cmd_done(struct tevent_req *subreq); + +struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_resp_ctx *kcm_data, + struct cli_creds *client, + struct kcm_data *input, + struct kcm_op *op) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_cmd_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct kcm_cmd_state); + if (req == NULL) { + return NULL; + } + state->op = op; + + if (op == NULL) { + ret = EINVAL; + goto immediate; + } + + DEBUG(SSSDBG_TRACE_FUNC, "KCM operation %s\n", op->name); + DEBUG(SSSDBG_TRACE_LIBS, "%zu bytes on KCM input\n", input->length); + + state->reply = sss_iobuf_init_empty(state, + KCM_REPLY_MAX, + KCM_REPLY_MAX); + if (state->reply == NULL) { + ret = ENOMEM; + goto immediate; + } + + if (op->fn_send == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + "KCM op %s has no handler\n", kcm_opt_name(op)); + ret = ERR_KCM_OP_NOT_IMPLEMENTED; + goto immediate; + } + + /* Allocating op_ctx on the heap makes it possible for operations to use + * op_ctx as their temporary context and avoid tmp_ctx altogether + */ + state->op_ctx = talloc_zero(state, struct kcm_op_ctx); + if (state->op_ctx == NULL) { + ret = ENOMEM; + goto immediate; + } + + state->op_ctx->kcm_data = kcm_data; + state->op_ctx->client = client; + + state->op_ctx->input = sss_iobuf_init_readonly(state->op_ctx, + input->data, + input->length); + if (state->op_ctx->input == NULL) { + ret = ENOMEM; + goto immediate; + } + + /* + * The internal operation returns the opcode and the buffer separately. + * The KCM server reply to the client also always contains zero if the + * operation ran to completion, both are uint32_t. + * FIXME: + * Alternatively, we could extend iobuf API so that we can just pass + * the reply's buffer+sizeof(2*uint32_t) and avoid the useless allocations + */ + state->op_ctx->reply = sss_iobuf_init_empty( + state, + KCM_REPLY_MAX - 2*sizeof(uint32_t), + KCM_REPLY_MAX - 2*sizeof(uint32_t)); + if (state->reply == NULL) { + ret = ENOMEM; + goto immediate; + } + + subreq = op->fn_send(state, ev, state->op_ctx); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_cmd_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_cmd_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); + struct kcm_cmd_state *state = tevent_req_data(req, struct kcm_cmd_state); + errno_t ret; + krb5_error_code kerr; + + ret = state->op->fn_recv(subreq, &state->op_ret); + talloc_free(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "op receive function failed [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + DEBUG(SSSDBG_TRACE_FUNC, + "KCM operation %s returned [%d]: %s\n", + kcm_opt_name(state->op), state->op_ret, sss_strerror(state->op_ret)); + + kerr = sss2krb5_error(state->op_ret); + + /* The first four bytes of the reply is the operation status code */ + ret = sss_iobuf_write_uint32(state->reply, htobe32(kerr)); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + ret = sss_iobuf_write_len(state->reply, + sss_iobuf_get_data(state->op_ctx->reply), + sss_iobuf_get_len(state->op_ctx->reply)); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t kcm_cmd_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct sss_iobuf **_reply) +{ + struct kcm_cmd_state *state = NULL; + + TEVENT_REQ_RETURN_ON_ERROR(req); + + state = tevent_req_data(req, struct kcm_cmd_state); + + *_reply = talloc_steal(mem_ctx, state->reply); + return EOK; +} + +/* ======= KCM operations ======= */ + +/* Operations that don't return any extra information except for the op_ret + * can use this macro in the _recv function to avoid code duplication + */ +#define KCM_OP_RET_FROM_TYPE(req, state_type, _op_ret_out) do { \ + state_type *state = NULL; \ + state = tevent_req_data(req, state_type); \ + TEVENT_REQ_RETURN_ON_ERROR(req); \ + *_op_ret_out = state->op_ret; \ + return EOK; \ +} while(0); + +struct kcm_op_common_state { + uint32_t op_ret; + struct kcm_op_ctx *op_ctx; + struct tevent_context *ev; +}; + +static errno_t kcm_op_common_recv(struct tevent_req *req, + uint32_t *_op_ret) +{ + struct kcm_op_common_state *state = tevent_req_data(req, + struct kcm_op_common_state); + TEVENT_REQ_RETURN_ON_ERROR(req); + *_op_ret = state->op_ret; + return EOK; +} + +/* () -> (name) */ +static void kcm_op_gen_new_done(struct tevent_req *subreq); + +static struct tevent_req *kcm_op_gen_new_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_op_common_state *state = NULL; + 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; + + subreq = kcm_ccdb_nextid_send(state, ev, + op_ctx->kcm_data->db, + op_ctx->client); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_op_gen_new_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_op_gen_new_done(struct tevent_req *subreq) +{ + errno_t ret; + char *newid; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_common_state *state = tevent_req_data(req, + struct kcm_op_common_state); + + ret = kcm_ccdb_nextid_recv(subreq, state, &newid); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot generate a new ID [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + DEBUG(SSSDBG_TRACE_LIBS, "Generated a new ID %s\n", newid); + + ret = sss_iobuf_write_stringz(state->op_ctx->reply, newid); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot write generated ID %d: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->op_ret = EOK; + tevent_req_done(req); +} + +/* (princ) -> () */ +struct kcm_op_initialize_state { + uint32_t op_ret; + struct kcm_op_ctx *op_ctx; + struct tevent_context *ev; + + struct kcm_ccache *new_cc; + const char *name; + krb5_principal princ; +}; + +static void kcm_op_initialize_got_byname(struct tevent_req *subreq); +static void kcm_op_initialize_cc_create_done(struct tevent_req *subreq); +static void kcm_op_initialize_cc_delete_done(struct tevent_req *subreq); +static void kcm_op_initialize_create_step(struct tevent_req *req); +static void kcm_op_initialize_got_default(struct tevent_req *subreq); +static void kcm_op_initialize_set_default_done(struct tevent_req *subreq); + +static struct tevent_req *kcm_op_initialize_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_op_initialize_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct kcm_op_initialize_state); + if (req == NULL) { + return NULL; + } + state->op_ctx = op_ctx; + state->ev = ev; + + ret = sss_iobuf_read_stringz(op_ctx->input, &state->name); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot read input name [%d]: %s\n", + ret, sss_strerror(ret)); + goto immediate; + } + DEBUG(SSSDBG_TRACE_LIBS, "Initializing ccache %s\n", state->name); + + ret = kcm_check_name(state->name, op_ctx->client); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Name %s is malformed [%d]: %s\n", + state->name, ret, sss_strerror(ret)); + goto immediate; + } + + ret = sss_krb5_unmarshal_princ(op_ctx, op_ctx->input, &state->princ); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot unmarshal principal [%d]: %s\n", + ret, sss_strerror(ret)); + goto immediate; + } + + subreq = kcm_ccdb_getbyname_send(state, ev, + op_ctx->kcm_data->db, + op_ctx->client, + state->name); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_op_initialize_got_byname, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_op_initialize_got_byname(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_initialize_state *state = tevent_req_data(req, + struct kcm_op_initialize_state); + bool ok; + uuid_t uuid; + + ret = kcm_ccdb_getbyname_recv(subreq, state, &state->new_cc); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get ccache by name [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (state->new_cc != NULL) { + ok = kcm_cc_access(state->new_cc, state->op_ctx->client); + if (!ok) { + state->op_ret = EACCES; + tevent_req_done(req); + return; + } + + ret = kcm_cc_get_uuid(state->new_cc, uuid); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get new ccache UUID [%d]: %s\n", + ret, sss_strerror(ret)); + return; + } + + /* Nuke any previous cache and its contents during initialization */ + subreq = kcm_ccdb_delete_cc_send(state, + state->ev, + state->op_ctx->kcm_data->db, + state->op_ctx->client, + uuid); + if (subreq == NULL) { + tevent_req_error(req, ret); + return; + } + tevent_req_set_callback(subreq, kcm_op_initialize_cc_delete_done, req); + return; + } + + kcm_op_initialize_create_step(req); +} + +static void kcm_op_initialize_cc_delete_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + errno_t ret; + + ret = kcm_ccdb_delete_cc_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot delete ccache from the db %d: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + kcm_op_initialize_create_step(req); +} + +static void kcm_op_initialize_create_step(struct tevent_req *req) +{ + struct tevent_req *subreq; + struct kcm_op_initialize_state *state = tevent_req_data(req, + struct kcm_op_initialize_state); + errno_t ret; + + ret = kcm_cc_new(state->op_ctx, + state->op_ctx->kcm_data->k5c, + state->op_ctx->client, + state->name, + state->princ, + &state->new_cc); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot create new ccache %d: %s\n", ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + subreq = kcm_ccdb_create_cc_send(state, + state->ev, + state->op_ctx->kcm_data->db, + state->op_ctx->client, + state->new_cc); + if (subreq == NULL) { + tevent_req_error(req, ret); + return; + } + tevent_req_set_callback(subreq, kcm_op_initialize_cc_create_done, req); +} + +static void kcm_op_initialize_cc_create_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_initialize_state *state = tevent_req_data(req, + struct kcm_op_initialize_state); + errno_t ret; + + ret = kcm_ccdb_create_cc_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot add ccache to db %d: %s\n", ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + /* If there was no previous default ccache, set this one as default */ + subreq = kcm_ccdb_get_default_send(state, state->ev, + state->op_ctx->kcm_data->db, + state->op_ctx->client); + if (subreq == NULL) { + tevent_req_error(req, ret); + return; + } + tevent_req_set_callback(subreq, kcm_op_initialize_got_default, req); +} + +static void kcm_op_initialize_got_default(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_initialize_state *state = tevent_req_data(req, + struct kcm_op_initialize_state); + errno_t ret; + uuid_t dfl_uuid; + uuid_t old_dfl_uuid; + + ret = kcm_ccdb_get_default_recv(subreq, &old_dfl_uuid); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get default ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (uuid_is_null(old_dfl_uuid) == false) { + /* If there was a previous default ccache, switch to the initialized + * one by default + */ + ret = kcm_cc_get_uuid(state->new_cc, dfl_uuid); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get new ccache UUID [%d]: %s\n", + ret, sss_strerror(ret)); + return; + } + + subreq = kcm_ccdb_set_default_send(state, + state->ev, + state->op_ctx->kcm_data->db, + state->op_ctx->client, + dfl_uuid); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, kcm_op_initialize_set_default_done, req); + return; + } + + /* ENOENT, done */ + state->op_ret = EOK; + tevent_req_done(req); +} + +static void kcm_op_initialize_set_default_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_initialize_state *state = tevent_req_data(req, + struct kcm_op_initialize_state); + errno_t ret; + + ret = kcm_ccdb_set_default_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot set default ccache %d: %s\n", ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->op_ret = EOK; + tevent_req_done(req); +} + +static errno_t kcm_op_initialize_recv(struct tevent_req *req, + uint32_t *_op_ret) +{ + KCM_OP_RET_FROM_TYPE(req, struct kcm_op_initialize_state, _op_ret); +} + +/* (name) -> () */ +static void kcm_op_destroy_getbyname_done(struct tevent_req *subreq); +static void kcm_op_destroy_delete_done(struct tevent_req *subreq); + +static struct tevent_req *kcm_op_destroy_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_op_common_state *state = NULL; + errno_t ret; + const char *name; + + req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); + if (req == NULL) { + return NULL; + } + state->op_ctx = op_ctx; + state->ev = ev; + + ret = sss_iobuf_read_stringz(op_ctx->input, &name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot unmarshall input name [%d]: %s\n", + ret, sss_strerror(ret)); + goto immediate; + } + + DEBUG(SSSDBG_TRACE_LIBS, "Destroying credentials of %s\n", name); + + subreq = kcm_ccdb_uuid_by_name_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_destroy_getbyname_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_op_destroy_getbyname_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_common_state *state = tevent_req_data(req, + struct kcm_op_common_state); + uuid_t uuid; + + ret = kcm_ccdb_uuid_by_name_recv(subreq, state, uuid); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get matching ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + subreq = kcm_ccdb_delete_cc_send(state, + state->ev, + state->op_ctx->kcm_data->db, + state->op_ctx->client, + uuid); + if (subreq == NULL) { + tevent_req_error(req, ret); + return; + } + tevent_req_set_callback(subreq, kcm_op_destroy_delete_done, req); +} + +static void kcm_op_destroy_delete_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_common_state *state = tevent_req_data(req, + struct kcm_op_common_state); + + ret = kcm_ccdb_delete_cc_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot delete ccache from the db [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->op_ret = EOK; + tevent_req_done(req); +} + +/* (name, cred) -> () */ +struct kcm_op_store_state { + uint32_t op_ret; + struct kcm_op_ctx *op_ctx; + struct tevent_context *ev; + + struct sss_iobuf *cred_blob; +}; + +static void kcm_op_store_getbyname_done(struct tevent_req *subreq); +static void kcm_op_store_done(struct tevent_req *subreq); + +static struct tevent_req *kcm_op_store_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_op_store_state *state = NULL; + errno_t ret; + const char *name; + size_t creds_len; + + req = tevent_req_create(mem_ctx, &state, struct kcm_op_store_state); + if (req == NULL) { + return NULL; + } + state->op_ctx = op_ctx; + state->ev = ev; + + ret = sss_iobuf_read_stringz(op_ctx->input, &name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot unmarshall input name [%d]: %s\n", + ret, sss_strerror(ret)); + goto immediate; + } + + DEBUG(SSSDBG_TRACE_LIBS, "Storing credentials for %s\n", name); + + creds_len = sss_iobuf_get_size(op_ctx->input) - strlen(name) -1; + if (creds_len > KCM_REPLY_MAX) { + /* Protects against underflows and in general adds sanity */ + ret = E2BIG; + goto immediate; + } + + state->cred_blob = sss_iobuf_init_empty(state, + creds_len, + creds_len); + if (state->cred_blob == NULL) { + ret = ENOMEM; + goto immediate; + } + + ret = sss_iobuf_read(op_ctx->input, + creds_len, + sss_iobuf_get_data(state->cred_blob), + NULL); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot unmarshall input cred blob [%d]: %s\n", + ret, sss_strerror(ret)); + goto immediate; + } + + subreq = kcm_ccdb_uuid_by_name_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_store_getbyname_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_op_store_getbyname_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_store_state *state = tevent_req_data(req, + struct kcm_op_store_state); + uuid_t uuid; + + ret = kcm_ccdb_uuid_by_name_recv(subreq, state, uuid); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get ccache by name [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + subreq = kcm_ccdb_store_cred_blob_send(state, state->ev, + state->op_ctx->kcm_data->db, + state->op_ctx->client, + uuid, + state->cred_blob); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, kcm_op_store_done, req); +} + +static void kcm_op_store_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_store_state *state = tevent_req_data(req, + struct kcm_op_store_state); + + ret = kcm_ccdb_store_cred_blob_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot store credentials [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->op_ret = EOK; + tevent_req_done(req); +} + +static errno_t kcm_op_store_recv(struct tevent_req *req, + uint32_t *_op_ret) +{ + KCM_OP_RET_FROM_TYPE(req, struct kcm_op_store_state, _op_ret); +} + +/* (name) -> (princ) */ +static void kcm_op_get_principal_getbyname_done(struct tevent_req *subreq); + +static struct tevent_req *kcm_op_get_principal_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_op_common_state *state = NULL; + errno_t ret; + const char *name; + + 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, "Requested principal %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_principal_getbyname_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_op_get_principal_getbyname_done(struct tevent_req *subreq) +{ + errno_t ret; + struct kcm_ccache *cc; + krb5_principal princ; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_common_state *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)); + tevent_req_error(req, ret); + return; + } + + if (cc == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that name\n"); + state->op_ret = ERR_NO_MATCHING_CREDS; + tevent_req_done(req); + return; + } + + /* Marshall the principal to the reply */ + princ = kcm_cc_get_client_principal(cc); + if (princ == NULL) { + DEBUG(SSSDBG_OP_FAILURE, + "Credentials with no principal?\n"); + tevent_req_error(req, EIO); + return; + } + + ret = sss_krb5_marshal_princ(princ, state->op_ctx->reply); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot marshall principal [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->op_ret = EOK; + tevent_req_done(req); +} + +/* (name) -> (uuid, ...) */ +static void kcm_op_get_cred_uuid_getbyname_done(struct tevent_req *subreq); + +static struct tevent_req * +kcm_op_get_cred_uuid_list_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_op_common_state *state = NULL; + errno_t ret; + const char *name; + + 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 UUID list 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_uuid_getbyname_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_op_get_cred_uuid_getbyname_done(struct tevent_req *subreq) +{ + errno_t ret; + struct kcm_ccache *cc; + struct kcm_cred *crd; + uuid_t uuid; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_common_state *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)); + tevent_req_error(req, ret); + return; + } + + if (cc == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n"); + state->op_ret = ERR_NO_CREDS; + tevent_req_done(req); + return; + } + + for (crd = kcm_cc_get_cred(cc); + crd != NULL; + crd = kcm_cc_next_cred(crd)) { + ret = kcm_cred_get_uuid(crd, uuid); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, "Credential has no UUID, skipping\n"); + continue; + } + + kcm_debug_uuid(uuid); + + ret = sss_iobuf_write_len(state->op_ctx->reply, + uuid, UUID_BYTES); + if (ret != EOK) { + char uuid_errbuf[UUID_STR_SIZE]; + uuid_parse(uuid_errbuf, uuid); + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot marshall UUID %s [%d]: %s\n", + uuid_errbuf, ret, sss_strerror(ret)); + continue; + } + } + state->op_ret = EOK; + tevent_req_done(req); +} + +/* (name, uuid) -> (cred) */ +static void kcm_op_get_cred_by_uuid_getbyname_done(struct tevent_req *subreq); + +static struct tevent_req * +kcm_op_get_cred_by_uuid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_op_common_state *state = NULL; + errno_t ret; + const char *name; + + 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 creds by UUID 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_by_uuid_getbyname_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_op_get_cred_by_uuid_getbyname_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_common_state *state = tevent_req_data(req, + struct kcm_op_common_state); + errno_t ret; + struct kcm_ccache *cc; + struct kcm_cred *crd; + uuid_t uuid_in; + uuid_t uuid; + struct sss_iobuf *cred_blob; + + 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)); + tevent_req_error(req, ret); + return; + } + + if (cc == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that name\n"); + state->op_ret = ERR_NO_MATCHING_CREDS; + tevent_req_done(req); + return; + } + + ret = sss_iobuf_read_len(state->op_ctx->input, + UUID_BYTES, uuid_in); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot read input UUID [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + for (crd = kcm_cc_get_cred(cc); + crd != NULL; + crd = kcm_cc_next_cred(crd)) { + ret = kcm_cred_get_uuid(crd, uuid); + if (ret != EOK) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot get UUID from creds, skipping\n"); + continue; + } + + if (uuid_compare(uuid, uuid_in) == 0) { + break; + } + kcm_debug_uuid(uuid); + } + + if (crd == NULL) { + state->op_ret = ERR_KCM_CC_END; + DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n"); + tevent_req_done(req); + return; + } + + cred_blob = kcm_cred_get_creds(crd); + if (cred_blob == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Credentials lack the creds blob\n"); + state->op_ret = ERR_NO_CREDS; + tevent_req_done(req); + return; + } + + ret = sss_iobuf_write_len(state->op_ctx->reply, + sss_iobuf_get_data(cred_blob), + sss_iobuf_get_size(cred_blob)); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot write ccache blob [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->op_ret = EOK; + tevent_req_done(req); +} + +/* (name, flags, credtag) -> () */ +/* FIXME */ +static struct tevent_req * +kcm_op_remove_cred_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct kcm_op_common_state *state = NULL; + + req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); + if (req == NULL) { + return NULL; + } + state->op_ctx = op_ctx; + + state->op_ret = ERR_KCM_OP_NOT_IMPLEMENTED; + tevent_req_post(req, ev); + tevent_req_done(req); + return req; +} + +/* () -> (uuid, ...) */ +static void kcm_op_get_cache_uuid_list_done(struct tevent_req *subreq); + +static struct tevent_req * +kcm_op_get_cache_uuid_list_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_op_common_state *state = NULL; + 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; + + DEBUG(SSSDBG_TRACE_LIBS, "Returning full UUID list\n"); + + subreq = kcm_ccdb_list_send(state, ev, + op_ctx->kcm_data->db, + op_ctx->client); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_op_get_cache_uuid_list_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_op_get_cache_uuid_list_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_common_state *state = tevent_req_data(req, + struct kcm_op_common_state); + errno_t ret; + uuid_t *uuid_list; + + ret = kcm_ccdb_list_recv(subreq, state, &uuid_list); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot list the ccache DB [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (uuid_list == NULL || uuid_list[0] == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, "Nothing to list\n"); + state->op_ret = ERR_NO_MATCHING_CREDS; + tevent_req_done(req); + return; + } + + for (int i = 0; + uuid_is_null(uuid_list[i]) == false; + i++) { + kcm_debug_uuid(uuid_list[i]); + + ret = sss_iobuf_write_len(state->op_ctx->reply, + uuid_list[i], + UUID_BYTES); + if (ret != EOK) { + char uuid_errbuf[UUID_STR_SIZE]; + uuid_parse(uuid_errbuf, uuid_list[i]); + DEBUG(SSSDBG_MINOR_FAILURE, + "Cannot marshall UUID %s [%d]: %s\n", + uuid_errbuf, ret, sss_strerror(ret)); + tevent_req_done(req); + return; + } + } + + tevent_req_done(req); +} + +/* (uuid) -> (name) */ +static void kcm_op_get_cache_by_uuid_done(struct tevent_req *subreq); + +static struct tevent_req * +kcm_op_get_cache_by_uuid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_op_common_state *state = NULL; + errno_t ret; + uuid_t uuid_in; + + req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); + if (req == NULL) { + return NULL; + } + state->op_ctx = op_ctx; + + DEBUG(SSSDBG_TRACE_LIBS, "Retrieving cache by UUID\n"); + + ret = sss_iobuf_read_len(op_ctx->input, + UUID_BYTES, uuid_in); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot read input UUID [%d]: %s\n", + ret, sss_strerror(ret)); + goto immediate; + } + kcm_debug_uuid(uuid_in); + + subreq = kcm_ccdb_getbyuuid_send(state, ev, + op_ctx->kcm_data->db, + op_ctx->client, + uuid_in); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_op_get_cache_by_uuid_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_op_get_cache_by_uuid_done(struct tevent_req *subreq) +{ + errno_t ret; + struct kcm_ccache *cc; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_common_state *state = tevent_req_data(req, + struct kcm_op_common_state); + const char *name; + + ret = kcm_ccdb_getbyuuid_recv(subreq, state, &cc); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get ccahe by UUID [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (cc == NULL) { + state->op_ret = ERR_KCM_CC_END; + tevent_req_done(req); + return; + } + + name = kcm_cc_get_name(cc); + DEBUG(SSSDBG_TRACE_INTERNAL, "Found %s by UUID\n", name); + + ret = sss_iobuf_write_stringz(state->op_ctx->reply, + name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot write output name [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->op_ret = EOK; + tevent_req_done(req); +} + +/* () -> (name) */ +struct kcm_op_get_default_ccache_state { + uint32_t op_ret; + struct kcm_op_ctx *op_ctx; + struct tevent_context *ev; + + const char *name; +}; + +static void kcm_op_get_get_default_done(struct tevent_req *subreq); +static void kcm_op_get_default_ccache_byuuid_done(struct tevent_req *subreq); +static void kcm_op_get_default_ccache_list_done(struct tevent_req *subreq); +static errno_t +kcm_op_get_default_ccache_reply_step(struct kcm_op_get_default_ccache_state *state); + +static struct tevent_req * +kcm_op_get_default_ccache_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_op_get_default_ccache_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, + struct kcm_op_get_default_ccache_state); + if (req == NULL) { + return NULL; + } + state->op_ctx = op_ctx; + state->ev = ev; + + DEBUG(SSSDBG_TRACE_LIBS, "Getting client's default ccache\n"); + + subreq = kcm_ccdb_get_default_send(state, ev, + state->op_ctx->kcm_data->db, + state->op_ctx->client); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_op_get_get_default_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_op_get_get_default_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); + struct kcm_op_get_default_ccache_state *state = tevent_req_data(req, + struct kcm_op_get_default_ccache_state); + errno_t ret; + uuid_t dfl_uuid; + + ret = kcm_ccdb_get_default_recv(subreq, &dfl_uuid); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get default ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (uuid_is_null(dfl_uuid) == true) { + /* No cache marked as default -- get an existing ccache for ID + * and treat the default as simply the first one + */ + subreq = kcm_ccdb_list_send(state, state->ev, + state->op_ctx->kcm_data->db, + state->op_ctx->client); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, kcm_op_get_default_ccache_list_done, req); + return; + } + + /* Existing default */ + subreq = kcm_ccdb_name_by_uuid_send(state, + state->ev, + state->op_ctx->kcm_data->db, + state->op_ctx->client, + dfl_uuid); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, kcm_op_get_default_ccache_byuuid_done, req); + return; +} + +static void kcm_op_get_default_ccache_byuuid_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); + struct kcm_op_get_default_ccache_state *state = tevent_req_data(req, + struct kcm_op_get_default_ccache_state); + errno_t ret; + + ret = kcm_ccdb_name_by_uuid_recv(subreq, state, &state->name); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get ccahe by UUID [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + ret = kcm_op_get_default_ccache_reply_step(state); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +static void kcm_op_get_default_ccache_list_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); + struct kcm_op_get_default_ccache_state *state = tevent_req_data(req, + struct kcm_op_get_default_ccache_state); + errno_t ret; + uuid_t *uuid_list; + + ret = kcm_ccdb_list_recv(subreq, state, &uuid_list); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot list ccaches [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (uuid_list == NULL || uuid_is_null(uuid_list[0])) { + /* No cache at all, just send back a reply */ + ret = kcm_op_get_default_ccache_reply_step(state); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); + return; + } + + /* Otherwise resolve the first cache and use it as a default */ + subreq = kcm_ccdb_name_by_uuid_send(state, + state->ev, + state->op_ctx->kcm_data->db, + state->op_ctx->client, + uuid_list[0]); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, kcm_op_get_default_ccache_byuuid_done, req); + return; +} + +static errno_t +kcm_op_get_default_ccache_reply_step(struct kcm_op_get_default_ccache_state *state) +{ + errno_t ret; + + if (state->name == NULL) { + state->name = talloc_asprintf(state, + "%"SPRIuid, + cli_creds_get_uid(state->op_ctx->client)); + if (state->name == NULL) { + return ENOMEM; + } + } + DEBUG(SSSDBG_TRACE_INTERNAL, "The default ccache is %s\n", state->name); + + ret = sss_iobuf_write_stringz(state->op_ctx->reply, state->name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot write output name [%d]: %s\n", + ret, sss_strerror(ret)); + return ret; + } + + return EOK; +} + +static errno_t kcm_op_get_default_ccache_recv(struct tevent_req *req, + uint32_t *_op_ret) +{ + KCM_OP_RET_FROM_TYPE(req, struct kcm_op_get_default_ccache_state, _op_ret); +} + +/* (name) -> () */ +static void kcm_op_set_default_ccache_getbyname_done(struct tevent_req *subreq); +static void kcm_op_set_default_done(struct tevent_req *subreq); + +static struct tevent_req * +kcm_op_set_default_ccache_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_op_common_state *state = NULL; + errno_t ret; + const char *name; + + req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state); + if (req == NULL) { + return NULL; + } + state->op_ctx = op_ctx; + state->ev = ev; + + ret = sss_iobuf_read_stringz(op_ctx->input, &name); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Cannot read input name [%d]: %s\n", + ret, sss_strerror(ret)); + goto immediate; + } + DEBUG(SSSDBG_TRACE_LIBS, "Setting default ccache %s\n", name); + + subreq = kcm_ccdb_uuid_by_name_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_set_default_ccache_getbyname_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_op_set_default_ccache_getbyname_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_common_state *state = tevent_req_data(req, + struct kcm_op_common_state); + uuid_t dfl_uuid; + + ret = kcm_ccdb_uuid_by_name_recv(subreq, state, dfl_uuid); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get ccache by name [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + subreq = kcm_ccdb_set_default_send(state, + state->ev, + state->op_ctx->kcm_data->db, + state->op_ctx->client, + dfl_uuid); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, kcm_op_set_default_done, req); + return; +} + +static void kcm_op_set_default_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_common_state *state = tevent_req_data(req, + struct kcm_op_common_state); + + ret = kcm_ccdb_set_default_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot set default ccache %d: %s\n", ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->op_ret = EOK; + tevent_req_done(req); +} + +/* (name) -> (offset) */ +static void kcm_op_get_kdc_offset_getbyname_done(struct tevent_req *subreq); + +static struct tevent_req * +kcm_op_get_kdc_offset_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_op_common_state *state = NULL; + errno_t ret; + const char *name; + + 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) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot read input name [%d]: %s\n", + ret, sss_strerror(ret)); + goto immediate; + } + DEBUG(SSSDBG_TRACE_LIBS, "Requested offset for principal %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_kdc_offset_getbyname_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_op_get_kdc_offset_getbyname_done(struct tevent_req *subreq) +{ + errno_t ret; + struct kcm_ccache *cc; + int32_t offset; + int32_t offset_be; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_common_state *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 matching ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (cc == NULL) { + DEBUG(SSSDBG_MINOR_FAILURE, "No matching credentials\n"); + state->op_ret = ERR_NO_MATCHING_CREDS; + tevent_req_done(req); + return; + } + + offset = kcm_cc_get_offset(cc); + DEBUG(SSSDBG_TRACE_LIBS, "KDC offset: %"PRIu32"\n", offset); + + offset_be = htobe32(offset); + ret = sss_iobuf_write_int32(state->op_ctx->reply, offset_be); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot write KDC offset [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->op_ret = EOK; + tevent_req_done(req); +} + +/* (name, offset) -> () */ +/* () -> (name) */ +struct kcm_op_set_kdc_offset_state { + uint32_t op_ret; + struct kcm_op_ctx *op_ctx; + struct tevent_context *ev; +}; + +static void kcm_op_set_kdc_offset_getbyname_done(struct tevent_req *subreq); +static void kcm_op_set_kdc_offset_mod_done(struct tevent_req *subreq); + +static struct tevent_req * +kcm_op_set_kdc_offset_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_op_ctx *op_ctx) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_op_set_kdc_offset_state *state = NULL; + errno_t ret; + const char *name; + + req = tevent_req_create(mem_ctx, &state, struct kcm_op_set_kdc_offset_state); + if (req == NULL) { + return NULL; + } + state->op_ctx = op_ctx; + state->ev = ev; + + ret = sss_iobuf_read_stringz(op_ctx->input, &name); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot read input name [%d]: %s\n", + ret, sss_strerror(ret)); + goto immediate; + } + DEBUG(SSSDBG_TRACE_LIBS, "Setting offset for principal %s\n", name); + + subreq = kcm_ccdb_uuid_by_name_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_set_kdc_offset_getbyname_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_op_set_kdc_offset_getbyname_done(struct tevent_req *subreq) +{ + errno_t ret; + struct kcm_mod_ctx *mod_ctx; + int32_t offset_be; + uuid_t uuid; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_set_kdc_offset_state *state = tevent_req_data(req, + struct kcm_op_set_kdc_offset_state); + + ret = kcm_ccdb_uuid_by_name_recv(subreq, state, uuid); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot get matching ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + ret = sss_iobuf_read_int32(state->op_ctx->input, &offset_be); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot read KDC offset [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + mod_ctx = talloc(state, struct kcm_mod_ctx); + if (mod_ctx == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + + kcm_mod_ctx_clear(mod_ctx); + mod_ctx->kdc_offset = be32toh(offset_be); + + subreq = kcm_ccdb_mod_cc_send(state, + state->ev, + state->op_ctx->kcm_data->db, + state->op_ctx->client, + uuid, + mod_ctx); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, kcm_op_set_kdc_offset_mod_done, req); +} + +static void kcm_op_set_kdc_offset_mod_done(struct tevent_req *subreq) +{ + errno_t ret; + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_op_set_kdc_offset_state *state = tevent_req_data(req, + struct kcm_op_set_kdc_offset_state); + + ret = kcm_ccdb_mod_cc_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Cannot modify ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->op_ret = EOK; + tevent_req_done(req); +} + +static errno_t kcm_op_set_kdc_offset_recv(struct tevent_req *req, + uint32_t *_op_ret) +{ + KCM_OP_RET_FROM_TYPE(req, struct kcm_op_set_kdc_offset_state, _op_ret); +} + +static struct kcm_op kcm_optable[] = { + { "NOOP", NULL, NULL }, + { "GET_NAME", NULL, NULL }, + { "RESOLVE", NULL, NULL }, + { "GEN_NEW", kcm_op_gen_new_send, NULL }, + { "INITIALIZE", kcm_op_initialize_send, kcm_op_initialize_recv }, + { "DESTROY", kcm_op_destroy_send, NULL }, + { "STORE", kcm_op_store_send, kcm_op_store_recv }, + { "RETRIEVE", NULL, NULL }, + { "GET_PRINCIPAL", kcm_op_get_principal_send, NULL }, + { "GET_CRED_UUID_LIST", kcm_op_get_cred_uuid_list_send, NULL }, + { "GET_CRED_BY_UUID", kcm_op_get_cred_by_uuid_send, NULL }, + { "REMOVE_CRED", kcm_op_remove_cred_send, NULL }, + { "SET_FLAGS", NULL, NULL }, + { "CHOWN", NULL, NULL }, + { "CHMOD", NULL, NULL }, + { "GET_INITIAL_TICKET", NULL, NULL }, + { "GET_TICKET", NULL, NULL }, + { "MOVE_CACHE", NULL, NULL }, + { "GET_CACHE_UUID_LIST", kcm_op_get_cache_uuid_list_send, NULL }, + { "GET_CACHE_BY_UUID", kcm_op_get_cache_by_uuid_send, NULL }, + { "GET_DEFAULT_CACHE", kcm_op_get_default_ccache_send, kcm_op_get_default_ccache_recv }, + { "SET_DEFAULT_CACHE", kcm_op_set_default_ccache_send, NULL }, + { "GET_KDC_OFFSET", kcm_op_get_kdc_offset_send, NULL }, + { "SET_KDC_OFFSET", kcm_op_set_kdc_offset_send, kcm_op_set_kdc_offset_recv }, + { "ADD_NTLM_CRED", NULL, NULL }, + { "HAVE_NTLM_CRED", NULL, NULL }, + { "DEL_NTLM_CRED", NULL, NULL }, + { "DO_NTLM_AUTH", NULL, NULL }, + { "GET_NTLM_USER_LIST", NULL, NULL }, + + { NULL, NULL, NULL } +}; + +struct kcm_op *kcm_get_opt(uint16_t opcode) +{ + struct kcm_op *op; + + DEBUG(SSSDBG_TRACE_INTERNAL, + "The client requested operation %"PRIu16"\n", opcode); + + if (opcode >= KCM_OP_SENTINEL) { + return NULL; + } + + op = &kcm_optable[opcode]; + if (op->fn_recv == NULL) { + op->fn_recv = kcm_op_common_recv; + } + return op; +} + +const char *kcm_opt_name(struct kcm_op *op) +{ + if (op == NULL || op->name == NULL) { + return "Unknown operation"; + } + + return op->name; +} diff --git a/src/responder/kcm/kcmsrv_ops.h b/src/responder/kcm/kcmsrv_ops.h new file mode 100644 index 0000000000000000000000000000000000000000..8e6feaf56a10b73c8b6375aea9ef26c392b5b492 --- /dev/null +++ b/src/responder/kcm/kcmsrv_ops.h @@ -0,0 +1,45 @@ +/* + SSSD + + KCM Server - private header file + + Copyright (C) Red Hat, 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef __KCMSRV_OPS_H__ +#define __KCMSRV_OPS_H__ + +#include "config.h" + +#include +#include "util/sss_iobuf.h" +#include "responder/kcm/kcmsrv_pvt.h" + +struct kcm_op; +struct kcm_op *kcm_get_opt(uint16_t opcode); +const char *kcm_opt_name(struct kcm_op *op); + +struct tevent_req *kcm_cmd_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_resp_ctx *kcm_data, + struct cli_creds *client, + struct kcm_data *input, + struct kcm_op *op); +errno_t kcm_cmd_recv(TALLOC_CTX *mem_ctx, + struct tevent_req *req, + struct sss_iobuf **_reply); + +#endif /* __KCMSRV_OPS_H__ */ -- 2.9.3