diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 05:31:45 +0000 |
commit | 74aa0bc6779af38018a03fd2cf4419fe85917904 (patch) | |
tree | 9cb0681aac9a94a49c153d5823e7a55d1513d91f /src/responder/kcm/kcmsrv_ops.c | |
parent | Initial commit. (diff) | |
download | sssd-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.c | 2458 |
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; +} |