summaryrefslogtreecommitdiffstats
path: root/src/responder/kcm/kcmsrv_ops.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 05:31:45 +0000
commit74aa0bc6779af38018a03fd2cf4419fe85917904 (patch)
tree9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/responder/kcm/kcmsrv_ops.c
parentInitial commit. (diff)
downloadsssd-74aa0bc6779af38018a03fd2cf4419fe85917904.tar.xz
sssd-74aa0bc6779af38018a03fd2cf4419fe85917904.zip
Adding upstream version 2.9.4.upstream/2.9.4
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/responder/kcm/kcmsrv_ops.c')
-rw-r--r--src/responder/kcm/kcmsrv_ops.c2458
1 files changed, 2458 insertions, 0 deletions
diff --git a/src/responder/kcm/kcmsrv_ops.c b/src/responder/kcm/kcmsrv_ops.c
new file mode 100644
index 0000000..8935a79
--- /dev/null
+++ b/src/responder/kcm/kcmsrv_ops.c
@@ -0,0 +1,2458 @@
+/*
+ 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include "config.h"
+
+#include <krb5/krb5.h>
+#include <dhash.h>
+
+#include "util/sss_iobuf.h"
+#include "util/sss_krb5.h"
+#include "util/sss_ptr_hash.h"
+#include "util/util_creds.h"
+#include "responder/kcm/kcmsrv_pvt.h"
+#include "responder/kcm/kcmsrv_ops.h"
+#include "responder/kcm/kcmsrv_ccache.h"
+
+struct kcm_op_ctx {
+ struct kcm_resp_ctx *kcm_data;
+ struct kcm_conn_data *conn_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 tevent_context *ev;
+
+ struct kcm_ops_queue_entry *queue_entry;
+ struct kcm_op_ctx *op_ctx;
+ struct sss_iobuf *reply;
+
+ uint32_t op_ret;
+};
+
+static void kcm_cmd_queue_done(struct tevent_req *subreq);
+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_ops_queue_ctx *qctx,
+ struct kcm_resp_ctx *kcm_data,
+ struct kcm_conn_data *conn_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;
+ state->ev = ev;
+
+ 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_PACKET_INITIAL_SIZE,
+ KCM_PACKET_MAX_SIZE);
+ if (state->reply == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ if (op->fn_send == NULL) {
+ DEBUG(SSSDBG_MINOR_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->conn_data = conn_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_PACKET_INITIAL_SIZE,
+ KCM_PACKET_MAX_SIZE - 2*sizeof(uint32_t));
+ if (state->op_ctx->reply == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ subreq = kcm_op_queue_send(state, ev, qctx, client);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+ tevent_req_set_callback(subreq, kcm_cmd_queue_done, req);
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_cmd_queue_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;
+
+ /* When this request finishes, it frees the queue_entry which unblocks
+ * other requests by the same UID
+ */
+ ret = kcm_op_queue_recv(subreq, state, &state->queue_entry);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot acquire queue slot\n");
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ subreq = state->op->fn_send(state, state->ev, state->op_ctx);
+ if (subreq == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_cmd_done, 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_fill_princ_step(struct tevent_req *req);
+static void kcm_op_initialize_fill_princ_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) {
+ if (kcm_cc_get_client_principal(state->new_cc) == NULL) {
+ /* This is a cache that was pre-created w/o a principal (sshd does this),
+ * let's fill in the principal and set the cache as default if not
+ * already
+ */
+ kcm_op_initialize_fill_princ_step(req);
+ return;
+ }
+
+ ok = kcm_cc_access(state->new_cc, state->op_ctx->client);
+ if (!ok) {
+ state->op_ret = EACCES;
+ tevent_req_done(req);
+ return;
+ }
+
+ /* `uuid` is output arg and isn't read in kcm_cc_get_uuid() but
+ * since libuuid is opaque for cppcheck it generates false positive here
+ */
+ /* cppcheck-suppress uninitvar */
+ 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_fill_princ_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;
+ struct kcm_mod_ctx *mod_ctx;
+ uuid_t uuid;
+
+ mod_ctx = kcm_mod_ctx_new(state);
+ if (mod_ctx == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ mod_ctx->client = state->princ;
+
+ /* `uuid` is output arg and isn't read in kcm_cc_get_uuid() but
+ * since libuuid is opaque for cppcheck it generates false positive here
+ */
+ /* cppcheck-suppress uninitvar */
+ ret = kcm_cc_get_uuid(state->new_cc, uuid);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ 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_initialize_fill_princ_done, req);
+}
+
+static void kcm_op_initialize_fill_princ_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_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;
+ }
+
+ /* Make sure the cache we just initialized is the default one */
+ 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_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, ENOMEM);
+ 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)) {
+ /* If there was no previous default ccache, switch to the initialized
+ * one by default
+ */
+ /* `dfl_uuid` is output arg and isn't read in kcm_cc_get_uuid() but
+ * since libuuid is opaque for cppcheck it generates false positive here
+ */
+ /* cppcheck-suppress uninitvar */
+ 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;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ "The default ccached was not set, switching to the "
+ "initialized\n");
+ 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;
+
+ /* `uuid` is output arg and isn't read in kcm_ccdb_uuid_by_name_recv() but
+ * since libuuid is opaque for cppcheck it generates false positive here
+ */
+ /* cppcheck-suppress uninitvar */
+ ret = kcm_ccdb_uuid_by_name_recv(subreq, state, uuid);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_MINOR_FAILURE,
+ "Cannot get matching ccache [%d]: %s\n",
+ ret, sss_strerror(ret));
+ ret = ERR_NO_MATCHING_CREDS;
+ 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_PACKET_MAX_SIZE) {
+ /* 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;
+
+ /* `uuid` is output arg and isn't read in kcm_ccdb_uuid_by_name_recv() but
+ * since libuuid is opaque for cppcheck it generates false positive here
+ */
+ /* cppcheck-suppress uninitvar */
+ 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);
+}
+
+static void
+kcm_creds_table_delete_cb(hash_entry_t *item,
+ hash_destroy_enum deltype,
+ void *pvt)
+{
+ /* Delete the old credential if it is being overwritten. */
+ talloc_free(item->value.ptr);
+}
+
+/* Store credentials in a hash table.
+ *
+ * If the table already exist we add the new credentials to the table and
+ * overwrite the ones that already exist. This allows us to correctly serve
+ * also parallel GET_CRED_UUID_LIST requests from the same connection since
+ * it will have its own uuid list and cursor on the client side and we make
+ * all uuid (old, updated and newly added) available.
+ */
+static errno_t
+kcm_creds_to_table(TALLOC_CTX *mem_ctx,
+ struct kcm_cred *creds,
+ hash_table_t **_table)
+{
+ char str[UUID_STR_SIZE];
+ uuid_t uuid;
+ errno_t ret;
+
+ if (*_table == NULL) {
+ *_table = sss_ptr_hash_create(mem_ctx, kcm_creds_table_delete_cb, NULL);
+ if (*_table == NULL) {
+ return ENOMEM;
+ }
+ }
+
+ for (struct kcm_cred *crd = creds;
+ 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;
+ }
+ uuid_unparse(uuid, str);
+
+ ret = sss_ptr_hash_add_or_override(*_table, str, crd, struct kcm_cred);
+ if (ret != EOK) {
+ return ret;
+ }
+
+ talloc_steal(*_table, crd);
+ }
+
+ return EOK;
+}
+
+static struct kcm_cred *
+kcm_creds_lookup(hash_table_t *table, uuid_t uuid)
+{
+ char str[UUID_STR_SIZE];
+
+ if (uuid == NULL) {
+ return NULL;
+ }
+
+ uuid_unparse(uuid, str);
+ return sss_ptr_hash_lookup(table, str, struct kcm_cred);
+}
+
+/* (name) -> (uuid, ...) */
+static void kcm_op_get_cred_uuid_list_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_list_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_list_getbyname_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct kcm_ccache *cc;
+ struct kcm_cred *crd;
+ struct kcm_conn_data *conn_data;
+ 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);
+
+ conn_data = state->op_ctx->conn_data;
+
+ 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 ccache by that name\n");
+ state->op_ret = ERR_NO_CREDS;
+ tevent_req_done(req);
+ return;
+ }
+
+ ret = kcm_creds_to_table(conn_data, kcm_cc_get_cred(cc), &conn_data->creds);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to build credentials hash table "
+ "[%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, "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);
+}
+
+static errno_t
+kcm_op_get_cred_by_uuid_reply(struct kcm_cred *crd,
+ struct sss_iobuf *reply)
+{
+ struct sss_iobuf *cred_blob;
+ errno_t ret;
+
+ cred_blob = kcm_cred_get_creds(crd);
+ if (cred_blob == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Credentials lack the creds blob\n");
+ return ERR_NO_CREDS;
+ }
+
+ ret = sss_iobuf_write_len(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));
+ }
+
+ return ret;
+}
+
+struct kcm_op_get_cred_by_uuid_state {
+ struct kcm_op_common_state common;
+ uuid_t uuid;
+};
+
+/* (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_get_cred_by_uuid_state *state;
+ struct kcm_cred *crd;
+ errno_t ret;
+ const char *name;
+
+ req = tevent_req_create(mem_ctx, &state,
+ struct kcm_op_get_cred_by_uuid_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->common.op_ctx = op_ctx;
+
+ ret = sss_iobuf_read_stringz(op_ctx->input, &name);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ ret = sss_iobuf_read_len(state->common.op_ctx->input, UUID_BYTES,
+ state->uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot read input UUID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto immediate;
+ }
+
+ if (op_ctx->conn_data->creds != NULL) {
+ crd = kcm_creds_lookup(op_ctx->conn_data->creds, state->uuid);
+ if (crd == NULL) {
+ /* This should not happen, it can only happen if wrong UUID was
+ * requested which suggests bug in the caller application. */
+ DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n");
+ kcm_debug_uuid(state->uuid);
+ state->common.op_ret = ERR_KCM_CC_END;
+ ret = EOK;
+ goto immediate;
+ } else {
+ ret = kcm_op_get_cred_by_uuid_reply(crd, op_ctx->reply);
+ if (ret == ERR_NO_CREDS) {
+ state->common.op_ret = ret;
+ 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:
+ if (ret == EOK) {
+ tevent_req_done(req);
+ } else {
+ 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_get_cred_by_uuid_state *state = tevent_req_data(req,
+ struct kcm_op_get_cred_by_uuid_state);
+ errno_t ret;
+ struct kcm_ccache *cc;
+ struct kcm_cred *crd;
+ struct kcm_conn_data *conn_data;
+
+ conn_data = state->common.op_ctx->conn_data;
+
+ 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;
+ }
+
+ ret = kcm_creds_to_table(conn_data, kcm_cc_get_cred(cc), &conn_data->creds);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Unable to build credentials hash table "
+ "[%d]: %s\n", ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ if (conn_data->creds != NULL) {
+ crd = kcm_creds_lookup(conn_data->creds, state->uuid);
+ if (crd == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "No credentials by that UUID\n");
+ kcm_debug_uuid(state->uuid);
+ state->common.op_ret = ERR_KCM_CC_END;
+ } else {
+ ret = kcm_op_get_cred_by_uuid_reply(crd, state->common.op_ctx->reply);
+ if (ret != EOK && ret != ERR_NO_CREDS) {
+ tevent_req_error(req, ret);
+ return;
+ }
+ state->common.op_ret = ret;
+ }
+ }
+
+ tevent_req_done(req);
+}
+
+static errno_t kcm_op_get_cred_by_uuid_recv(struct tevent_req *req,
+ uint32_t *_op_ret)
+{
+ struct kcm_op_get_cred_by_uuid_state *state;
+
+ state = tevent_req_data(req, struct kcm_op_get_cred_by_uuid_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+ *_op_ret = state->common.op_ret;
+ return EOK;
+}
+
+/* (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_is_null(uuid_list[0])) {
+ 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 ccache 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));
+ /* Instead of failing the whole operation, return the first
+ * ccache as a fallback
+ */
+ 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;
+ }
+
+ 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_create_step(struct tevent_req *req);
+static void kcm_op_set_default_create_step_done(struct tevent_req *subreq);
+static void kcm_op_set_default_step(struct tevent_req *req);
+static void kcm_op_set_default_done(struct tevent_req *subreq);
+
+struct kcm_op_set_default_ccache_state {
+ uint32_t op_ret;
+ struct kcm_op_ctx *op_ctx;
+ struct tevent_context *ev;
+
+ const char *name;
+ uuid_t dfl_uuid;
+ struct kcm_ccache *new_cc;
+};
+
+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_set_default_ccache_state *state = NULL;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx,
+ &state,
+ struct kcm_op_set_default_ccache_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, "Setting default ccache %s\n", state->name);
+
+ subreq = kcm_ccdb_uuid_by_name_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_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_set_default_ccache_state *state = tevent_req_data(req,
+ struct kcm_op_set_default_ccache_state);
+
+ ret = kcm_ccdb_uuid_by_name_recv(subreq, state, state->dfl_uuid);
+ talloc_zfree(subreq);
+ if (ret == ERR_NO_CREDS) {
+ DEBUG(SSSDBG_TRACE_LIBS,
+ "The ccache does not exist, creating a new one\n");
+ kcm_op_set_default_create_step(req);
+ return;
+ } else 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;
+ }
+
+ kcm_op_set_default_step(req);
+}
+
+static void kcm_op_set_default_create_step(struct tevent_req *req)
+{
+ errno_t ret;
+ struct tevent_req *subreq;
+ struct kcm_op_set_default_ccache_state *state = tevent_req_data(req,
+ struct kcm_op_set_default_ccache_state);
+
+ /* Only allow to create ccaches for 'self' */
+ ret = kcm_check_name(state->name, state->op_ctx->client);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Name %s is malformed [%d]: %s\n",
+ state->name, ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ ret = kcm_cc_new(state->op_ctx,
+ state->op_ctx->kcm_data->k5c,
+ state->op_ctx->client,
+ state->name,
+ NULL,
+ &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, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, kcm_op_set_default_create_step_done, req);
+}
+
+static void kcm_op_set_default_create_step_done(struct tevent_req *subreq)
+{
+ errno_t ret;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct kcm_op_set_default_ccache_state *state = tevent_req_data(req,
+ struct kcm_op_set_default_ccache_state);
+
+ 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;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC, "The ccache was created, switching to it");
+
+ ret = kcm_cc_get_uuid(state->new_cc, state->dfl_uuid);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Cannot get new ccache UUID [%d]: %s\n",
+ ret, sss_strerror(ret));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ kcm_op_set_default_step(req);
+}
+
+static void kcm_op_set_default_step(struct tevent_req *req)
+{
+ struct kcm_op_set_default_ccache_state *state = tevent_req_data(req,
+ struct kcm_op_set_default_ccache_state);
+ struct tevent_req *subreq;
+
+ subreq = kcm_ccdb_set_default_send(state,
+ state->ev,
+ state->op_ctx->kcm_data->db,
+ state->op_ctx->client,
+ state->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_set_default_ccache_state *state = tevent_req_data(req,
+ struct kcm_op_set_default_ccache_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);
+}
+
+static errno_t kcm_op_set_default_ccache_recv(struct tevent_req *req,
+ uint32_t *_op_ret)
+{
+ KCM_OP_RET_FROM_TYPE(req, struct kcm_op_set_default_ccache_state, _op_ret);
+}
+
+/* (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: %"PRIi32"\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);
+
+ /* `uuid` is output arg and isn't read in kcm_ccdb_uuid_by_name_recv() but
+ * since libuuid is opaque for cppcheck it generates false positive here
+ */
+ /* cppcheck-suppress uninitvar */
+ 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 = kcm_mod_ctx_new(state);
+ if (mod_ctx == NULL) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ 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 void kcm_op_get_cred_list_done(struct tevent_req *subreq);
+
+static struct tevent_req *
+kcm_op_get_cred_list_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct kcm_op_ctx *op_ctx)
+{
+ struct kcm_op_common_state *state;
+ struct tevent_req *subreq;
+ struct tevent_req *req;
+ const char *name;
+ errno_t ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct kcm_op_common_state);
+ if (req == NULL) {
+ return NULL;
+ }
+ state->op_ctx = op_ctx;
+
+ ret = sss_iobuf_read_stringz(op_ctx->input, &name);
+ if (ret != EOK) {
+ goto immediate;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "Returning credentials for %s\n", name);
+
+ subreq = kcm_ccdb_getbyname_send(state, ev,
+ op_ctx->kcm_data->db,
+ op_ctx->client,
+ name);
+ if (subreq == NULL) {
+ ret = ENOMEM;
+ goto immediate;
+ }
+
+ tevent_req_set_callback(subreq, kcm_op_get_cred_list_done, req);
+
+ return req;
+
+immediate:
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ return req;
+}
+
+static void kcm_op_get_cred_list_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req;
+ struct kcm_op_common_state *state;
+ struct kcm_ccache *cc;
+ struct kcm_cred *crd;
+ uint32_t num_creds;
+ struct sss_iobuf *crd_blob;
+ uint8_t *crd_data;
+ uint32_t crd_size;
+ errno_t ret;
+
+ req = tevent_req_callback_data(subreq, struct tevent_req);
+ state = tevent_req_data(req, struct kcm_op_common_state);
+
+ ret = kcm_ccdb_getbyname_recv(subreq, state, &cc);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Cannot get ccache by name [%d]: %s\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ if (cc == NULL) {
+ DEBUG(SSSDBG_MINOR_FAILURE, "No ccache by that name\n");
+ state->op_ret = ERR_NO_CREDS;
+ ret = EOK;
+ goto done;
+ }
+
+ num_creds = 0;
+ for (crd = kcm_cc_get_cred(cc); crd != NULL; crd = kcm_cc_next_cred(crd)) {
+ num_creds++;
+ }
+
+ ret = sss_iobuf_write_uint32(state->op_ctx->reply, htobe32(num_creds));
+ if (ret != EOK) {
+ goto done;
+ }
+
+ for (crd = kcm_cc_get_cred(cc); crd != NULL; crd = kcm_cc_next_cred(crd)) {
+ crd_blob = kcm_cred_get_creds(crd);
+ if (crd_blob == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Credentials lack the creds blob\n");
+ ret = ERR_NO_CREDS;
+ goto done;
+ }
+
+ crd_data = sss_iobuf_get_data(crd_blob);
+ crd_size = sss_iobuf_get_size(crd_blob);
+
+ ret = sss_iobuf_write_uint32(state->op_ctx->reply, htobe32(crd_size));
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sss_iobuf_write_len(state->op_ctx->reply, crd_data, crd_size);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+ state->op_ret = EOK;
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ tevent_req_done(req);
+}
+
+static struct kcm_op kcm_optable[] = {
+ { "NOOP", NULL, NULL },
+ { "GET_NAME", NULL, NULL },
+ { "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, kcm_op_get_cred_by_uuid_recv },
+ { "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, kcm_op_set_default_ccache_recv },
+ { "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 }
+};
+
+/* MIT EXTENSIONS, see private header src/include/kcm.h in krb5 sources */
+#define KCM_MIT_OFFSET 13001
+static struct kcm_op kcm_mit_optable[] = {
+ { "GET_CRED_LIST", kcm_op_get_cred_list_send, NULL },
+
+ { NULL, NULL, NULL }
+};
+
+struct kcm_op *kcm_get_opt(uint16_t opcode)
+{
+ struct kcm_op *table;
+ struct kcm_op *op;
+ size_t len;
+
+ DEBUG(SSSDBG_TRACE_INTERNAL,
+ "The client requested operation %"PRIu16"\n", opcode);
+
+ table = kcm_optable;
+ len = sizeof(kcm_optable) / sizeof(struct kcm_op);
+ if (opcode >= KCM_MIT_OFFSET) {
+ opcode -= KCM_MIT_OFFSET;
+ table = kcm_mit_optable;
+ len = sizeof(kcm_mit_optable) / sizeof(struct kcm_op);
+ }
+
+ if (opcode >= len) {
+ return NULL;
+ }
+
+ op = &table[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;
+}