From 79f6ccd2dc3f0c1369d5a93678c88ee76ec761e0 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Tue, 7 Mar 2017 13:49:21 +0100 Subject: [PATCH 24/36] KCM: Implement an internal ccache storage and retrieval API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order for the KCM server to work with ccaches stored in different locations, implement a middle-man between the KCM server and the ccache storage. This module has asynchronous API because we can't assume anything about where the ccaches are stored. Reviewed-by: Michal Židek Reviewed-by: Simo Sorce --- Makefile.am | 9 + configure.ac | 1 + contrib/sssd.spec.in | 1 + src/external/libuuid.m4 | 17 + src/responder/kcm/kcmsrv_ccache.c | 1423 +++++++++++++++++++++++++++++++++ src/responder/kcm/kcmsrv_ccache.h | 306 +++++++ src/responder/kcm/kcmsrv_ccache_be.h | 204 +++++ src/responder/kcm/kcmsrv_ccache_pvt.h | 62 ++ src/responder/kcm/kcmsrv_pvt.h | 1 + 9 files changed, 2024 insertions(+) create mode 100644 src/external/libuuid.m4 create mode 100644 src/responder/kcm/kcmsrv_ccache.c create mode 100644 src/responder/kcm/kcmsrv_ccache.h create mode 100644 src/responder/kcm/kcmsrv_ccache_be.h create mode 100644 src/responder/kcm/kcmsrv_ccache_pvt.h diff --git a/Makefile.am b/Makefile.am index 4248536e90370c1aab59549a9c18408ef314e6d4..a2b9dc49e95fa2d025f5174d2902866fab180a78 100644 --- a/Makefile.am +++ b/Makefile.am @@ -711,6 +711,9 @@ dist_noinst_HEADERS = \ src/responder/secrets/secsrv_proxy.h \ src/responder/kcm/kcm.h \ src/responder/kcm/kcmsrv_pvt.h \ + src/responder/kcm/kcmsrv_ccache.h \ + src/responder/kcm/kcmsrv_ccache_pvt.h \ + src/responder/kcm/kcmsrv_ccache_be.h \ src/sbus/sbus_client.h \ src/sbus/sssd_dbus.h \ src/sbus/sssd_dbus_meta.h \ @@ -1488,16 +1491,22 @@ if BUILD_KCM sssd_kcm_SOURCES = \ src/responder/kcm/kcm.c \ src/responder/kcm/kcmsrv_cmd.c \ + src/responder/kcm/kcmsrv_ccache.c \ src/util/sss_sockets.c \ + src/util/sss_krb5.c \ + src/util/sss_iobuf.c \ $(SSSD_RESPONDER_OBJ) \ $(NULL) sssd_kcm_CFLAGS = \ $(AM_CFLAGS) \ $(KRB5_CFLAGS) \ + $(UUID_CFLAGS) \ $(NULL) sssd_kcm_LDADD = \ $(KRB5_LIBS) \ $(SSSD_LIBS) \ + $(UUID_LIBS) \ + $(SYSTEMD_DAEMON_LIBS) \ $(SSSD_INTERNAL_LTLIBS) \ $(NULL) endif diff --git a/configure.ac b/configure.ac index c363d48a806cc1998e85779a92b6b59b0e2a5c9c..cf5e2557ef0a1bd6374200aa33abea6c509d03aa 100644 --- a/configure.ac +++ b/configure.ac @@ -202,6 +202,7 @@ fi if test x$with_kcm = xyes; then m4_include([src/external/libcurl.m4]) + m4_include([src/external/libuuid.m4]) fi # This variable is defined by external/libcurl.m4, but conditionals # must be always evaluated diff --git a/contrib/sssd.spec.in b/contrib/sssd.spec.in index 5c7c2af521a84ef2ca6cca7b2d6cd1f9b3057056..52d33b4de281dc1d91a9027ac1c8c878e66fb396 100644 --- a/contrib/sssd.spec.in +++ b/contrib/sssd.spec.in @@ -222,6 +222,7 @@ BuildRequires: systemtap-sdt-devel %endif BuildRequires: http-parser-devel BuildRequires: jansson-devel +BuildRequires: libuuid-devel %description Provides a set of daemons to manage access to remote directories and diff --git a/src/external/libuuid.m4 b/src/external/libuuid.m4 new file mode 100644 index 0000000000000000000000000000000000000000..55411a2118bd787c9d50ba61f9cb791e1c76088d --- /dev/null +++ b/src/external/libuuid.m4 @@ -0,0 +1,17 @@ +AC_SUBST(UUID_LIBS) +AC_SUBST(UUID_CFLAGS) + +PKG_CHECK_MODULES([UUID], [uuid], [found_uuid=yes], [found_uuid=no]) + +SSS_AC_EXPAND_LIB_DIR() +AS_IF([test x"$found_uuid" = xyes], + [AC_CHECK_HEADERS([uuid/uuid.h], + [AC_CHECK_LIB([uuid], + [uuid_generate], + [UUID_LIBS="-L$sss_extra_libdir -luuid"], + [AC_MSG_ERROR([libuuid missing uuid_generate])], + [-L$sss_extra_libdir -luuid])], + [AC_MSG_ERROR([ +You must have the header file uuid.h installed to build sssd +with KCM responder. If you want to build sssd without KCM responder +then specify --without-kcm when running configure.])])]) diff --git a/src/responder/kcm/kcmsrv_ccache.c b/src/responder/kcm/kcmsrv_ccache.c new file mode 100644 index 0000000000000000000000000000000000000000..2c565b8378e3ec297faf655d3c48d7ab902713d3 --- /dev/null +++ b/src/responder/kcm/kcmsrv_ccache.c @@ -0,0 +1,1423 @@ +/* + SSSD + + KCM Server - the KCM ccache operations + + Copyright (C) Red Hat, 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include "config.h" + +#include "util/crypto/sss_crypto.h" +#include "util/util.h" +#include "util/sss_krb5.h" +#include "responder/kcm/kcmsrv_ccache.h" +#include "responder/kcm/kcmsrv_ccache_pvt.h" +#include "responder/kcm/kcmsrv_ccache_be.h" + +static int kcm_cc_destructor(struct kcm_ccache *cc) +{ + if (cc == NULL) { + return 0; + } + + krb5_free_principal(NULL, cc->client); + return 0; +} + +errno_t kcm_cc_new(TALLOC_CTX *mem_ctx, + krb5_context k5c, + struct cli_creds *owner, + const char *name, + krb5_principal princ, + struct kcm_ccache **_cc) +{ + struct kcm_ccache *cc; + krb5_error_code kret; + errno_t ret; + + cc = talloc_zero(mem_ctx, struct kcm_ccache); + if (cc == NULL) { + return ENOMEM; + } + + ret = kcm_check_name(name, owner); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Name %s is malformed\n", name); + return ret; + } + + cc->name = talloc_strdup(cc, name); + if (cc->name == NULL) { + talloc_free(cc); + return ENOMEM; + } + + uuid_generate(cc->uuid); + + kret = krb5_copy_principal(k5c, princ, &cc->client); + if (kret != 0) { + const char *err_msg = sss_krb5_get_error_message(k5c, kret); + DEBUG(SSSDBG_OP_FAILURE, + "krb5_copy_principal failed: [%d][%s]\n", kret, err_msg); + sss_krb5_free_error_message(k5c, err_msg); + talloc_free(cc); + return ERR_INTERNAL; + } + + cc->owner.uid = cli_creds_get_uid(owner); + cc->owner.gid = cli_creds_get_gid(owner); + cc->kdc_offset = INT32_MAX; + + talloc_set_destructor(cc, kcm_cc_destructor); + *_cc = cc; + return EOK; +} + +const char *kcm_cc_get_name(struct kcm_ccache *cc) +{ + return cc ? cc->name : NULL; +} + +errno_t kcm_cc_get_uuid(struct kcm_ccache *cc, uuid_t _uuid) +{ + if (cc == NULL) { + return EINVAL; + } + uuid_copy(_uuid, cc->uuid); + return EOK; +} + +krb5_principal kcm_cc_get_client_principal(struct kcm_ccache *cc) +{ + return cc ? cc->client : NULL; +} + +bool kcm_cc_access(struct kcm_ccache *cc, + struct cli_creds *client) +{ + bool ok; + uid_t uid = cli_creds_get_uid(client); + gid_t gid = cli_creds_get_gid(client); + + if (cc == NULL) { + return false; + } + + if (uid == 0 && gid == 0) { + /* root can access any ccache */ + return true; + } + + ok = ((cc->owner.uid == uid) && (cc->owner.gid == gid)); + if (!ok) { + DEBUG(SSSDBG_MINOR_FAILURE, + "Client %"SPRIuid":%"SPRIgid" has no access to ccache %s\n", + cli_creds_get_uid(client), + cli_creds_get_gid(client), + cc->name); + } + return ok; +} + +int32_t kcm_cc_get_offset(struct kcm_ccache *cc) +{ + return cc ? cc->kdc_offset : INT32_MAX; +} + +errno_t kcm_cc_store_cred_blob(struct kcm_ccache *cc, + struct sss_iobuf *cred_blob) +{ + struct kcm_cred *kcreds; + uuid_t uuid; + errno_t ret; + + if (cc == NULL || cred_blob == NULL) { + return EINVAL; + } + + uuid_generate(uuid); + kcreds = kcm_cred_new(cc, uuid, cred_blob); + if (kcreds == NULL) { + return ENOMEM; + } + + ret = kcm_cc_store_creds(cc, kcreds); + if (ret != EOK) { + return ret; + } + + return EOK; +} + +struct kcm_cred *kcm_cc_get_cred(struct kcm_ccache *cc) +{ + if (cc == NULL) { + return NULL; + } + + return cc->creds; +} + +struct kcm_cred *kcm_cc_next_cred(struct kcm_cred *crd) +{ + if (crd == NULL) { + return NULL; + } + + return crd->next; +} + +struct kcm_cred *kcm_cred_new(TALLOC_CTX *mem_ctx, + uuid_t uuid, + struct sss_iobuf *cred_blob) +{ + struct kcm_cred *kcreds; + + kcreds = talloc_zero(mem_ctx, struct kcm_cred); + if (kcreds == NULL) { + return NULL; + } + + uuid_copy(kcreds->uuid, uuid); + kcreds->cred_blob = talloc_steal(kcreds, cred_blob); + return kcreds; +} + +/* Add a cred to ccache */ +errno_t kcm_cc_store_creds(struct kcm_ccache *cc, + struct kcm_cred *crd) +{ + DLIST_ADD(cc->creds, crd); + talloc_steal(cc, crd); + return EOK; +} + +errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t _uuid) +{ + if (crd == NULL) { + return EINVAL; + } + uuid_copy(_uuid, crd->uuid); + return EOK; +} + +struct sss_iobuf *kcm_cred_get_creds(struct kcm_cred *crd) +{ + return crd ? crd->cred_blob : NULL; +} + +struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + enum kcm_ccdb_be cc_be) +{ + errno_t ret; + struct kcm_ccdb *ccdb = NULL; + + if (ev == NULL) { + return NULL; + } + + ccdb = talloc_zero(mem_ctx, struct kcm_ccdb); + if (ccdb == NULL) { + return NULL; + } + ccdb->ev = ev; + + switch (cc_be) { + case CCDB_BE_MEMORY: + DEBUG(SSSDBG_FUNC_DATA, "KCM back end: memory\n"); + /* Not implemented yet */ + break; + case CCDB_BE_SECRETS: + DEBUG(SSSDBG_FUNC_DATA, "KCM back end: sssd-secrets\n"); + /* Not implemented yet */ + break; + default: + DEBUG(SSSDBG_CRIT_FAILURE, "Unknown ccache database\n"); + break; + } + + if (ccdb->ops == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "Ccache database not initialized\n"); + talloc_free(ccdb); + return NULL; + } + + ret = ccdb->ops->init(ccdb); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, "Cannot initialize ccache database\n"); + talloc_free(ccdb); + return NULL; + } + + return ccdb; +} + +struct kcm_ccdb_nextid_state { + char *next_cc; + struct kcm_ccdb *db; + struct cli_creds *client; +}; + +static void kcm_ccdb_nextid_done(struct tevent_req *subreq); + +struct tevent_req *kcm_ccdb_nextid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_ccdb_nextid_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_nextid_state); + if (req == NULL) { + return NULL; + } + state->db = db; + state->client = client; + + if (ev == NULL || db == NULL || client == NULL) { + ret = EINVAL; + goto immediate; + } + + subreq = state->db->ops->nextid_send(mem_ctx, ev, state->db, client); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_ccdb_nextid_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_ccdb_nextid_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_nextid_state *state = tevent_req_data(req, + struct kcm_ccdb_nextid_state); + errno_t ret; + unsigned int nextid; + + ret = state->db->ops->nextid_recv(subreq, &nextid); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to generate next UID [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + state->next_cc = talloc_asprintf(state, "%"SPRIuid":%u", + cli_creds_get_uid(state->client), + nextid); + if (state->next_cc == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed\n"); + tevent_req_error(req, ENOMEM); + return; + } + + DEBUG(SSSDBG_TRACE_LIBS, "generated %s\n", state->next_cc); + tevent_req_done(req); +} + +errno_t kcm_ccdb_nextid_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + char **_next_cc) +{ + struct kcm_ccdb_nextid_state *state = tevent_req_data(req, + struct kcm_ccdb_nextid_state); + TEVENT_REQ_RETURN_ON_ERROR(req); + *_next_cc = talloc_steal(mem_ctx, state->next_cc); + return EOK; +} + +struct kcm_ccdb_list_state { + struct kcm_ccdb *db; + struct cli_creds *client; + + uuid_t *uuid_list; +}; + +static void kcm_ccdb_list_done(struct tevent_req *subreq); + +struct tevent_req *kcm_ccdb_list_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_ccdb_list_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_list_state); + if (req == NULL) { + return NULL; + } + state->db = db; + state->client = client; + + if (ev == NULL || db == NULL || client == NULL) { + ret = EINVAL; + goto immediate; + } + + subreq = state->db->ops->list_send(mem_ctx, + ev, + state->db, + client); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_ccdb_list_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_ccdb_list_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_list_state *state = tevent_req_data(req, + struct kcm_ccdb_list_state); + errno_t ret; + + ret = state->db->ops->list_recv(subreq, state, &state->uuid_list); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to list all ccaches [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t kcm_ccdb_list_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uuid_t **_uuid_list) +{ + struct kcm_ccdb_list_state *state = tevent_req_data(req, + struct kcm_ccdb_list_state); + TEVENT_REQ_RETURN_ON_ERROR(req); + *_uuid_list = talloc_steal(mem_ctx, state->uuid_list); + return EOK; +} + +struct kcm_ccdb_get_default_state { + struct kcm_ccdb *db; + uuid_t uuid; +}; + +static void kcm_ccdb_get_default_done(struct tevent_req *subreq); + +struct tevent_req *kcm_ccdb_get_default_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_ccdb_get_default_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_get_default_state); + if (req == NULL) { + return NULL; + } + state->db = db; + + if (ev == NULL || db == NULL || client == NULL) { + ret = EINVAL; + goto immediate; + } + + subreq = db->ops->get_default_send(mem_ctx, ev, db, client); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_ccdb_get_default_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_ccdb_get_default_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_get_default_state *state = tevent_req_data(req, + struct kcm_ccdb_get_default_state); + errno_t ret; + + ret = state->db->ops->get_default_recv(subreq, state->uuid); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to get the default ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t kcm_ccdb_get_default_recv(struct tevent_req *req, + uuid_t *uuid) +{ + struct kcm_ccdb_get_default_state *state = tevent_req_data(req, + struct kcm_ccdb_get_default_state); + TEVENT_REQ_RETURN_ON_ERROR(req); + + if (uuid != NULL) { + /* The caller might supply a NULL dfl to just check if there is + * some default ccache + */ + uuid_copy(*uuid, state->uuid); + } + + return EOK; +} + +struct kcm_ccdb_set_default_state { + struct tevent_context *ev; + struct kcm_ccdb *db; + struct cli_creds *client; + uuid_t uuid; +}; + +static void kcm_ccdb_set_default_uuid_resolved(struct tevent_req *subreq); +static void kcm_ccdb_set_default_done(struct tevent_req *subreq); + +struct tevent_req *kcm_ccdb_set_default_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_ccdb_set_default_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_set_default_state); + if (req == NULL) { + return NULL; + } + state->db = db; + state->ev = ev; + state->client = client; + uuid_copy(state->uuid, uuid); + + if (ev == NULL || db == NULL || client == NULL) { + ret = EINVAL; + goto immediate; + } + + if (uuid_is_null(uuid)) { + /* NULL UUID means to just reset the default to 'no default' */ + subreq = state->db->ops->set_default_send(state, + state->ev, + state->db, + state->client, + state->uuid); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_ccdb_set_default_done, req); + } else { + /* Otherwise we need to check if the client can access the UUID + * about to be set as default + */ + subreq = db->ops->getbyuuid_send(state, ev, db, client, uuid); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_ccdb_set_default_uuid_resolved, req); + } + + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_ccdb_set_default_uuid_resolved(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_set_default_state *state = tevent_req_data(req, + struct kcm_ccdb_set_default_state); + errno_t ret; + bool ok; + struct kcm_ccache *cc; + + ret = state->db->ops->getbyuuid_recv(subreq, state, &cc); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to get cache by UUID [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (cc == NULL) { + DEBUG(SSSDBG_TRACE_LIBS, "No cache found by UUID\n"); + tevent_req_error(req, ERR_KCM_CC_END); + return; + } + + ok = kcm_cc_access(cc, state->client); + if (!ok) { + tevent_req_error(req, EACCES); + return; + } + + subreq = state->db->ops->set_default_send(state, + state->ev, + state->db, + state->client, + state->uuid); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, kcm_ccdb_set_default_done, req); +} + +static void kcm_ccdb_set_default_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_set_default_state *state = tevent_req_data(req, + struct kcm_ccdb_set_default_state); + errno_t ret; + + ret = state->db->ops->set_default_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to set the default ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t kcm_ccdb_set_default_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} + +struct kcm_ccdb_getbyname_state { + struct kcm_ccdb *db; + struct cli_creds *client; + + struct kcm_ccache *cc; +}; + +static void kcm_ccdb_getbyname_done(struct tevent_req *subreq); + +struct tevent_req *kcm_ccdb_getbyname_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + const char *name) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_ccdb_getbyname_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_getbyname_state); + if (req == NULL) { + return NULL; + } + state->db = db; + state->client = client; + + if (ev == NULL || db == NULL || client == NULL || name == NULL) { + ret = EINVAL; + goto immediate; + } + + subreq = db->ops->getbyname_send(state, ev, db, client, name); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_ccdb_getbyname_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_ccdb_getbyname_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_getbyname_state *state = tevent_req_data(req, + struct kcm_ccdb_getbyname_state); + errno_t ret; + bool ok; + + ret = state->db->ops->getbyname_recv(subreq, state, &state->cc); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to get cache by name [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (state->cc == NULL) { + DEBUG(SSSDBG_TRACE_LIBS, "No cache found by name\n"); + tevent_req_done(req); + return; + } + + ok = kcm_cc_access(state->cc, state->client); + if (!ok) { + tevent_req_error(req, EACCES); + return; + } + + tevent_req_done(req); +} + +errno_t kcm_ccdb_getbyname_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct kcm_ccache **_cc) +{ + struct kcm_ccdb_getbyname_state *state = tevent_req_data(req, + struct kcm_ccdb_getbyname_state); + TEVENT_REQ_RETURN_ON_ERROR(req); + *_cc = talloc_steal(mem_ctx, state->cc); + return EOK; +} + +struct kcm_ccdb_getbyuuid_state { + struct kcm_ccdb *db; + struct cli_creds *client; + + struct kcm_ccache *cc; +}; + +static void kcm_ccdb_getbyuuid_done(struct tevent_req *subreq); + +struct tevent_req *kcm_ccdb_getbyuuid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_ccdb_getbyuuid_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_getbyuuid_state); + if (req == NULL) { + return NULL; + } + state->db = db; + state->client = client; + + if (ev == NULL || db == NULL || client == NULL) { + ret = EINVAL; + goto immediate; + } + + subreq = db->ops->getbyuuid_send(state, ev, db, client, uuid); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_ccdb_getbyuuid_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_ccdb_getbyuuid_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_getbyuuid_state *state = tevent_req_data(req, + struct kcm_ccdb_getbyuuid_state); + errno_t ret; + bool ok; + + ret = state->db->ops->getbyuuid_recv(subreq, state, &state->cc); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to get cache by UUID [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (state->cc == NULL) { + DEBUG(SSSDBG_TRACE_LIBS, "No cache found by UUID\n"); + tevent_req_done(req); + return; + } + + ok = kcm_cc_access(state->cc, state->client); + if (!ok) { + tevent_req_error(req, EACCES); + return; + } + + tevent_req_done(req); +} + +errno_t kcm_ccdb_getbyuuid_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct kcm_ccache **_cc) +{ + struct kcm_ccdb_getbyuuid_state *state = tevent_req_data(req, + struct kcm_ccdb_getbyuuid_state); + TEVENT_REQ_RETURN_ON_ERROR(req); + *_cc = talloc_steal(mem_ctx, state->cc); + return EOK; +} + +struct kcm_ccdb_name_by_uuid_state { + struct kcm_ccdb *db; + struct cli_creds *client; + + const char *name; +}; + +static void kcm_ccdb_name_by_uuid_done(struct tevent_req *subreq); + +struct tevent_req *kcm_ccdb_name_by_uuid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_ccdb_name_by_uuid_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, + &state, + struct kcm_ccdb_name_by_uuid_state); + if (req == NULL) { + return NULL; + } + state->db = db; + state->client = client; + + if (ev == NULL || db == NULL || client == NULL || uuid_is_null(uuid)) { + ret = EINVAL; + goto immediate; + } + + subreq = db->ops->name_by_uuid_send(state, ev, db, client, uuid); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_ccdb_name_by_uuid_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_ccdb_name_by_uuid_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_name_by_uuid_state *state = tevent_req_data(req, + struct kcm_ccdb_name_by_uuid_state); + errno_t ret; + + ret = state->db->ops->name_by_uuid_recv(subreq, state, &state->name); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to resolve cache by UUID [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t kcm_ccdb_name_by_uuid_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + const char **_name) +{ + struct kcm_ccdb_name_by_uuid_state *state = tevent_req_data(req, + struct kcm_ccdb_name_by_uuid_state); + TEVENT_REQ_RETURN_ON_ERROR(req); + *_name = talloc_steal(mem_ctx, state->name); + return EOK; +} + +struct kcm_ccdb_uuid_by_name_state { + struct kcm_ccdb *db; + struct cli_creds *client; + + uuid_t uuid; +}; + +static void kcm_ccdb_uuid_by_name_done(struct tevent_req *subreq); + +struct tevent_req *kcm_ccdb_uuid_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + const char *name) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_ccdb_uuid_by_name_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, + &state, + struct kcm_ccdb_uuid_by_name_state); + if (req == NULL) { + return NULL; + } + state->db = db; + state->client = client; + + if (ev == NULL || db == NULL || client == NULL || name == NULL) { + ret = EINVAL; + goto immediate; + } + + subreq = db->ops->uuid_by_name_send(state, ev, db, client, name); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_ccdb_uuid_by_name_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_ccdb_uuid_by_name_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_uuid_by_name_state *state = tevent_req_data(req, + struct kcm_ccdb_uuid_by_name_state); + errno_t ret; + + ret = state->db->ops->uuid_by_name_recv(subreq, state, state->uuid); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to resolve cache by UUID [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t kcm_ccdb_uuid_by_name_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uuid_t _uuid) +{ + struct kcm_ccdb_uuid_by_name_state *state = tevent_req_data(req, + struct kcm_ccdb_uuid_by_name_state); + TEVENT_REQ_RETURN_ON_ERROR(req); + uuid_copy(_uuid, state->uuid); + return EOK; +} + +struct kcm_ccdb_create_cc_state { + struct kcm_ccdb *db; +}; + +static void kcm_ccdb_create_done(struct tevent_req *subreq); + +struct tevent_req *kcm_ccdb_create_cc_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + struct kcm_ccache *cc) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_ccdb_create_cc_state *state = NULL; + errno_t ret; + bool ok; + + req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_create_cc_state); + if (req == NULL) { + return NULL; + } + state->db = db; + + if (ev == NULL || db == NULL || client == NULL || cc == NULL) { + ret = EINVAL; + goto immediate; + } + + ok = kcm_cc_access(cc, client); + if (!ok) { + ret = EACCES; + goto immediate; + } + + subreq = state->db->ops->create_send(mem_ctx, + ev, + state->db, + client, + cc); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_ccdb_create_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_ccdb_create_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_create_cc_state *state = tevent_req_data(req, + struct kcm_ccdb_create_cc_state); + errno_t ret; + + ret = state->db->ops->create_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to create ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t kcm_ccdb_create_cc_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} + +void kcm_mod_ctx_clear(struct kcm_mod_ctx *mod_ctx) +{ + if (mod_ctx == NULL) { + return; + } + + mod_ctx->kdc_offset = INT32_MAX; +} + +void kcm_mod_cc(struct kcm_ccache *cc, struct kcm_mod_ctx *mod_ctx) +{ + if (cc == NULL || mod_ctx == NULL) { + return; + } + + if (mod_ctx->kdc_offset != INT32_MAX) { + cc->kdc_offset = mod_ctx->kdc_offset; + } + +} + +struct kcm_ccdb_mod_cc_state { + struct kcm_ccdb *db; +}; + +static void kcm_ccdb_mod_done(struct tevent_req *subreq); + +struct tevent_req *kcm_ccdb_mod_cc_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid, + struct kcm_mod_ctx *mod_cc) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_ccdb_mod_cc_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_mod_cc_state); + if (req == NULL) { + return NULL; + } + state->db = db; + + if (ev == NULL || db == NULL || client == NULL || mod_cc == NULL) { + ret = EINVAL; + goto immediate; + } + + subreq = state->db->ops->mod_send(mem_ctx, + ev, + state->db, + client, + uuid, + mod_cc); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_ccdb_mod_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_ccdb_mod_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_mod_cc_state *state = tevent_req_data(req, + struct kcm_ccdb_mod_cc_state); + errno_t ret; + + ret = state->db->ops->mod_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to create ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t kcm_ccdb_mod_cc_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} + +struct kcm_ccdb_store_cred_blob_state { + struct kcm_ccdb *db; +}; + +static void kcm_ccdb_store_cred_blob_done(struct tevent_req *subreq); + +struct tevent_req *kcm_ccdb_store_cred_blob_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid, + struct sss_iobuf *cred_blob) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_ccdb_store_cred_blob_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_store_cred_blob_state); + if (req == NULL) { + return NULL; + } + state->db = db; + + if (ev == NULL || db == NULL || client == NULL || cred_blob == NULL) { + ret = EINVAL; + goto immediate; + } + + subreq = state->db->ops->store_cred_send(mem_ctx, + ev, + state->db, + client, + uuid, + cred_blob); + if (subreq == NULL) { + ret = ENOMEM; + goto immediate; + } + tevent_req_set_callback(subreq, kcm_ccdb_store_cred_blob_done, req); + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_ccdb_store_cred_blob_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_store_cred_blob_state *state = tevent_req_data(req, + struct kcm_ccdb_store_cred_blob_state); + errno_t ret; + + ret = state->db->ops->store_cred_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to create ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t kcm_ccdb_store_cred_blob_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} + +struct kcm_ccdb_delete_cc_state { + struct tevent_context *ev; + struct kcm_ccdb *db; + struct cli_creds *client; + uuid_t uuid; +}; + +static void kcm_ccdb_delete_done(struct tevent_req *subreq); +static void kcm_ccdb_delete_get_default_done(struct tevent_req *subreq); +static void kcm_ccdb_delete_default_reset_done(struct tevent_req *subreq); + +struct tevent_req *kcm_ccdb_delete_cc_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid) +{ + struct tevent_req *req = NULL; + struct tevent_req *subreq = NULL; + struct kcm_ccdb_delete_cc_state *state = NULL; + errno_t ret; + + req = tevent_req_create(mem_ctx, &state, struct kcm_ccdb_delete_cc_state); + if (req == NULL) { + return NULL; + } + state->db = db; + state->ev = ev; + state->client = client; + uuid_copy(state->uuid, uuid); + + if (ev == NULL || db == NULL || client == NULL) { + ret = EINVAL; + goto immediate; + } + + subreq = state->db->ops->delete_send(state, + state->ev, + state->db, + state->client, + state->uuid); + tevent_req_set_callback(subreq, kcm_ccdb_delete_done, req); + + return req; + +immediate: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static void kcm_ccdb_delete_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req, + struct kcm_ccdb_delete_cc_state); + errno_t ret; + + ret = state->db->ops->delete_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to delete ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + /* The delete operation must also check if the deleted ccache was + * the default and reset the default if it was + */ + subreq = state->db->ops->get_default_send(state, + state->ev, + state->db, + state->client); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, kcm_ccdb_delete_get_default_done, req); +} + +static void kcm_ccdb_delete_get_default_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req, + struct kcm_ccdb_delete_cc_state); + errno_t ret; + uuid_t dfl_uuid; + uuid_t null_uuid; + + ret = state->db->ops->get_default_recv(subreq, dfl_uuid); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to get the default ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + if (uuid_compare(dfl_uuid, state->uuid) != 0) { + /* The ccache about to be deleted was not the default, quit */ + tevent_req_done(req); + return; + } + + /* If we deleted the default ccache, reset the default ccache to 'none' */ + uuid_clear(null_uuid); + + subreq = state->db->ops->set_default_send(state, + state->ev, + state->db, + state->client, + null_uuid); + if (subreq == NULL) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, kcm_ccdb_delete_default_reset_done, req); +} + +static void kcm_ccdb_delete_default_reset_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct kcm_ccdb_delete_cc_state *state = tevent_req_data(req, + struct kcm_ccdb_delete_cc_state); + errno_t ret; + + ret = state->db->ops->set_default_recv(subreq); + talloc_zfree(subreq); + if (ret != EOK) { + DEBUG(SSSDBG_OP_FAILURE, + "Failed to NULL the default ccache [%d]: %s\n", + ret, sss_strerror(ret)); + tevent_req_error(req, ret); + return; + } + + tevent_req_done(req); +} + +errno_t kcm_ccdb_delete_cc_recv(struct tevent_req *req) +{ + TEVENT_REQ_RETURN_ON_ERROR(req); + return EOK; +} + +void kcm_debug_uuid(uuid_t uuid) +{ + char dbgbuf[UUID_STR_SIZE]; + + if (!(debug_level & SSSDBG_TRACE_ALL) || uuid == NULL) { + return; + } + + uuid_unparse(uuid, dbgbuf); + DEBUG(SSSDBG_TRACE_ALL, "UUID: %s\n", dbgbuf); +} + +errno_t kcm_check_name(const char *name, struct cli_creds *client) +{ + char prefix[64]; + size_t prefix_len; + + prefix_len = snprintf(prefix, sizeof(prefix), + "%"SPRIuid, cli_creds_get_uid(client)); + + if (strncmp(name, prefix, prefix_len) != 0) { + return ERR_KCM_WRONG_CCNAME_FORMAT; + } + return EOK; +} diff --git a/src/responder/kcm/kcmsrv_ccache.h b/src/responder/kcm/kcmsrv_ccache.h new file mode 100644 index 0000000000000000000000000000000000000000..130ae48ae30d5e1e2ab238a647a9b9dc76cc4945 --- /dev/null +++ b/src/responder/kcm/kcmsrv_ccache.h @@ -0,0 +1,306 @@ +/* + SSSD + + KCM Server - the KCM ccache 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 . +*/ +#ifndef _KCMSRV_CCACHE_H_ +#define _KCMSRV_CCACHE_H_ + +#include "config.h" + +#include +#include + +#include "util/util.h" +#include "util/sss_iobuf.h" +#include "util/util_creds.h" + +#define UUID_BYTES 16 +#define UUID_STR_SIZE 37 + +/* + * Credentials are opaque to the KCM server + * + * Each ccache has a unique UUID. + */ +struct kcm_cred; + +/* + * An opaque ccache type and its operations + * + * Contains zero or some KCM credentials. One credential in the cache + * is marked as the default one. The client can set and get the default + * cache (e.g. with kswitch) but one cache is always the default -- we + * fall back to the one created first. + * + * Each cache has a name and a UUID. Heimdal allows the name to be changed, + * we don't (yet, because the MIT client doesn't allow that either) + * + * Each ccache also stores a client principal. + */ +struct kcm_ccache; + +/* + * Create a new KCM ccache owned by mem_ctx on the + * memory level. + * + * When created, the ccache contains no credendials + */ +errno_t kcm_cc_new(TALLOC_CTX *mem_ctx, + krb5_context k5c, + struct cli_creds *owner, + const char *name, + krb5_principal princ, + struct kcm_ccache **_cc); + +/* + * Returns true if a client can access a ccache. + * + * Note that root can access any ccache */ +bool kcm_cc_access(struct kcm_ccache *cc, + struct cli_creds *client); + +/* + * Since the kcm_ccache structure is opaque, the kcmsrv_ccache + * layer contains a number of getsetters to read and write + * properties of the kcm_ccache structure + */ +const char *kcm_cc_get_name(struct kcm_ccache *cc); +errno_t kcm_cc_get_uuid(struct kcm_ccache *cc, uuid_t _uuid); +krb5_principal kcm_cc_get_client_principal(struct kcm_ccache *cc); +int32_t kcm_cc_get_offset(struct kcm_ccache *cc); + +/* Mainly useful for creating a cred structure from a persistent + * storage + */ +struct kcm_cred *kcm_cred_new(TALLOC_CTX *mem_ctx, + uuid_t uuid, + struct sss_iobuf *cred_blob); + +/* Add a cred to ccache */ +errno_t kcm_cc_store_creds(struct kcm_ccache *cc, + struct kcm_cred *crd); + +errno_t kcm_cred_get_uuid(struct kcm_cred *crd, uuid_t uuid); + +/* + * At the moment, the credentials are stored without unmarshalling + * them, just as the clients sends the credentials. + */ +struct sss_iobuf *kcm_cred_get_creds(struct kcm_cred *crd); +errno_t kcm_cc_store_cred_blob(struct kcm_ccache *cc, + struct sss_iobuf *cred_blob); + /* + * The KCM server can call kcm_cred_get_creds to fetch the first + * credential, then iterate over the credentials with + * kcm_cc_next_cred until it returns NULL + */ +struct kcm_cred *kcm_cc_get_cred(struct kcm_ccache *cc); +struct kcm_cred *kcm_cc_next_cred(struct kcm_cred *crd); + +enum kcm_ccdb_be { + CCDB_BE_MEMORY, + CCDB_BE_SECRETS, +}; + +/* An opaque database that contains all the ccaches */ +struct kcm_ccdb; + +/* + * Initialize a ccache database of type cc_be + */ +struct kcm_ccdb *kcm_ccdb_init(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + enum kcm_ccdb_be cc_be); + +/* + * In KCM, each ccache name is usually in the form of "UID: + * + * The is generated by the KCM ccache database. Use this function + * to retrieve the next number + */ +struct tevent_req *kcm_ccdb_nextid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client); +errno_t kcm_ccdb_nextid_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + char **_nextid); + +/* + * List all ccaches that belong to a given client + * + * The cc_list the recv function returns is NULL-terminated. + * + * NOTE: Contrary to how Heimdal behaves, root CAN NOT list all ccaches + * of all users. This is a deliberate decision to treat root as any other + * user, except it can access a ccache of another user by name, just not + * list them. + * + * If a client has no ccaches, the function returns OK, but an empty list + * containing just the NULL sentinel. + */ +struct tevent_req *kcm_ccdb_list_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client); +errno_t kcm_ccdb_list_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uuid_t **_uuid_list); + +/* + * Retrieve a ccache by name. + * + * If there is no such ccache, return EOK, but a NULL _cc pointer + */ +struct tevent_req *kcm_ccdb_getbyname_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + const char *name); +errno_t kcm_ccdb_getbyname_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct kcm_ccache **_cc); + +/* + * Retrieve a ccache by UUID + * + * If there is no such ccache, return EOK, but a NULL _cc pointer + */ +struct tevent_req *kcm_ccdb_getbyuuid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid); +errno_t kcm_ccdb_getbyuuid_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct kcm_ccache **_cc); + +/* + * Retrieve the default ccache. If there is no default cache, + * return EOK, but a NULL UUID. + */ +struct tevent_req *kcm_ccdb_get_default_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client); +errno_t kcm_ccdb_get_default_recv(struct tevent_req *req, + uuid_t *uuid); + +/* + * Translating name to UUID is often considerably faster than doing a full + * CC retrieval, hence this function and the converse. If the UUID cannot + * be found in the database, return ERR_KCM_CC_END + */ +struct tevent_req *kcm_ccdb_name_by_uuid_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid); +errno_t kcm_ccdb_name_by_uuid_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + const char **_name); + +/* + * Translating UUID to name is often considerably faster than doing a full + * CC retrieval, hence this function and the converse. If the UUID cannot + * be found in the database, return ERR_KCM_CC_END + */ +struct tevent_req *kcm_ccdb_uuid_by_name_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + const char *name); +errno_t kcm_ccdb_uuid_by_name_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uuid_t _uuid); + +/* + * Set the default ccache. Passing a NULL UUID is a legal operation + * that 'unsets' the default ccache. + */ +struct tevent_req *kcm_ccdb_set_default_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid); +errno_t kcm_ccdb_set_default_recv(struct tevent_req *req); + +/* + * Add a ccache to the database. + */ +struct tevent_req *kcm_ccdb_create_cc_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + struct kcm_ccache *cc); +errno_t kcm_ccdb_create_cc_recv(struct tevent_req *req); + +/* + * Modify cache properties in a db + */ +struct kcm_mod_ctx { + int32_t kdc_offset; + /* More settable properties (like name, when we support renames + * will be added later + */ +}; + +void kcm_mod_ctx_clear(struct kcm_mod_ctx *mod_ctx); +void kcm_mod_cc(struct kcm_ccache *cc, struct kcm_mod_ctx *mod_ctx); + +struct tevent_req *kcm_ccdb_mod_cc_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid, + struct kcm_mod_ctx *mod_cc); +errno_t kcm_ccdb_mod_cc_recv(struct tevent_req *req); + +/* + * Store a credential in a cache + */ +struct tevent_req *kcm_ccdb_store_cred_blob_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid, + struct sss_iobuf *cred_blob); +errno_t kcm_ccdb_store_cred_blob_recv(struct tevent_req *req); + +/* + * Delete a ccache from the database + */ +struct tevent_req *kcm_ccdb_delete_cc_send(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid); +errno_t kcm_ccdb_delete_cc_recv(struct tevent_req *req); + +void kcm_debug_uuid(uuid_t uuid); + +/* + * The KCM clients are not allowed (except root) to create ccaches + * with arbitrary names. Instead, we assert that the ccache name + * begins with UID where UID is the stringified representation of + * the client's UID number + */ +errno_t kcm_check_name(const char *name, struct cli_creds *client); + +#endif /* _KCMSRV_CCACHE_H_ */ diff --git a/src/responder/kcm/kcmsrv_ccache_be.h b/src/responder/kcm/kcmsrv_ccache_be.h new file mode 100644 index 0000000000000000000000000000000000000000..1bd2b6981e227675866e82e0a5389445cac4df66 --- /dev/null +++ b/src/responder/kcm/kcmsrv_ccache_be.h @@ -0,0 +1,204 @@ +/* + SSSD + + KCM Server - the KCM ccache database interface + + This file should only be included from the ccache.c module. + + Copyright (C) Red Hat, 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#ifndef _KCMSRV_CCACHE_BE_ +#define _KCMSRV_CCACHE_BE_ + +#include "config.h" + +#include +#include "responder/kcm/kcmsrv_ccache.h" + +typedef errno_t +(*ccdb_init_fn)(struct kcm_ccdb *db); + +typedef struct tevent_req * +(*ccdb_nextid_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client); +typedef errno_t +(*ccdb_nextid_recv_fn)(struct tevent_req *req, + unsigned int *_nextid); + +typedef struct tevent_req * +(*ccdb_set_default_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid); +typedef errno_t +(*ccdb_set_default_recv_fn)(struct tevent_req *req); + +typedef struct tevent_req * +(*ccdb_get_default_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client); +typedef errno_t +(*ccdb_get_default_recv_fn)(struct tevent_req *req, + uuid_t dfl); + + +typedef struct tevent_req * +(*ccdb_list_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client); +typedef errno_t +(*ccdb_list_recv_fn)(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uuid_t **_uuid_list); + +typedef struct tevent_req * +(*ccdb_getbyname_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + const char *name); +typedef errno_t +(*ccdb_getbyname_recv_fn)(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct kcm_ccache **_cc); + +typedef struct tevent_req * +(*ccdb_getbyuuid_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid); +typedef errno_t +(*ccdb_getbyuuid_recv_fn)(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + struct kcm_ccache **_cc); + +typedef struct tevent_req * +(*ccdb_name_by_uuid_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid); +typedef errno_t +(*ccdb_name_by_uuid_recv_fn)(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + const char **_name); + +typedef struct tevent_req * +(*ccdb_uuid_by_name_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + const char *name); +typedef errno_t +(*ccdb_uuid_by_name_recv_fn)(struct tevent_req *req, + TALLOC_CTX *mem_ctx, + uuid_t _uuid); + +typedef struct tevent_req * +(*ccdb_create_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + struct kcm_ccache *cc); +typedef errno_t +(*ccdb_create_recv_fn)(struct tevent_req *req); + +typedef struct tevent_req * +(*ccdb_mod_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid, + struct kcm_mod_ctx *mod_cc); +typedef errno_t +(*ccdb_mod_recv_fn)(struct tevent_req *req); + +typedef struct tevent_req * +(*kcm_ccdb_store_cred_blob_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid, + struct sss_iobuf *cred_blob); +typedef errno_t +(*kcm_ccdb_store_cred_blob_recv_fn)(struct tevent_req *req); + +typedef struct tevent_req * +(*ccdb_delete_send_fn)(TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct kcm_ccdb *db, + struct cli_creds *client, + uuid_t uuid); +typedef errno_t +(*ccdb_delete_recv_fn)(struct tevent_req *req); + +/* + * Each ccache back end (for example memory or secrets) must implement + * all these functions. The functions are wrapped by the kcm_ccdb + * interface that performs additional sanity checks or contains shared + * logic such as access checks but in general doesn't assume anything + * about how the operations work. + */ +struct kcm_ccdb_ops { + ccdb_init_fn init; + + ccdb_nextid_send_fn nextid_send; + ccdb_nextid_recv_fn nextid_recv; + + ccdb_set_default_send_fn set_default_send; + ccdb_set_default_recv_fn set_default_recv; + + ccdb_get_default_send_fn get_default_send; + ccdb_get_default_recv_fn get_default_recv; + + ccdb_list_send_fn list_send; + ccdb_list_recv_fn list_recv; + + ccdb_getbyname_send_fn getbyname_send; + ccdb_getbyname_recv_fn getbyname_recv; + + ccdb_getbyuuid_send_fn getbyuuid_send; + ccdb_getbyuuid_recv_fn getbyuuid_recv; + + ccdb_name_by_uuid_send_fn name_by_uuid_send; + ccdb_name_by_uuid_recv_fn name_by_uuid_recv; + + ccdb_uuid_by_name_send_fn uuid_by_name_send; + ccdb_uuid_by_name_recv_fn uuid_by_name_recv; + + ccdb_create_send_fn create_send; + ccdb_create_recv_fn create_recv; + + ccdb_mod_send_fn mod_send; + ccdb_mod_recv_fn mod_recv; + + kcm_ccdb_store_cred_blob_send_fn store_cred_send; + kcm_ccdb_store_cred_blob_recv_fn store_cred_recv; + + ccdb_delete_send_fn delete_send; + ccdb_delete_recv_fn delete_recv; +}; + +extern const struct kcm_ccdb_ops ccdb_mem_ops; + +#endif /* _KCMSRV_CCACHE_BE_ */ diff --git a/src/responder/kcm/kcmsrv_ccache_pvt.h b/src/responder/kcm/kcmsrv_ccache_pvt.h new file mode 100644 index 0000000000000000000000000000000000000000..0cc24c2b8cd4d44080d2aa4384f7b2a73520c5a0 --- /dev/null +++ b/src/responder/kcm/kcmsrv_ccache_pvt.h @@ -0,0 +1,62 @@ +/* + SSSD + + KCM Server - the KCM ccache operations - private structures + + Should be accessed only from the ccache layer. + + Copyright (C) Red Hat, 2016 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ +#ifndef _KCMSRV_CCACHE_PVT_H +#define _KCMSRV_CCACHE_PVT_H + +#include "responder/kcm/kcmsrv_ccache.h" +#include "responder/kcm/kcmsrv_ccache_be.h" + +struct kcm_ccache_owner { + uid_t uid; + gid_t gid; +}; + +struct kcm_cred { + struct sss_iobuf *cred_blob; + /* Randomly generated 16 bytes */ + uuid_t uuid; + + struct kcm_cred *next; + struct kcm_cred *prev; +}; + +struct kcm_ccdb { + enum kcm_ccdb_be cc_be_type; + struct tevent_context *ev; + + void *db_handle; + const struct kcm_ccdb_ops *ops; +}; + +struct kcm_ccache { + const char *name; + struct kcm_ccache_owner owner; + uuid_t uuid; + + krb5_principal client; + int32_t kdc_offset; + + struct kcm_cred *creds; +}; + +#endif /* _KCMSRV_CCACHE_PVT_H */ diff --git a/src/responder/kcm/kcmsrv_pvt.h b/src/responder/kcm/kcmsrv_pvt.h index fd1fd9fa32d59a323d465def68999f24f84e3923..a29680246c1e616da75e1bbff951ce2fad66fb65 100644 --- a/src/responder/kcm/kcmsrv_pvt.h +++ b/src/responder/kcm/kcmsrv_pvt.h @@ -46,6 +46,7 @@ struct kcm_data { */ struct kcm_resp_ctx { krb5_context k5c; + struct kcm_ccdb *db; }; /* -- 2.9.3