/* SSSD KCM Server - the mainloop and server setup Copyright (C) Red Hat, 2016 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "config.h" #include #include "responder/kcm/kcmsrv_ccache.h" #include "responder/kcm/kcmsrv_pvt.h" #include "responder/kcm/kcm_renew.h" #include "responder/common/responder.h" #include "providers/krb5/krb5_common.h" #include "util/util.h" #include "util/sss_krb5.h" #define DEFAULT_KCM_FD_LIMIT 2048 #define DEFAULT_KCM_CLI_IDLE_TIMEOUT 300 #ifndef SSS_KCM_SOCKET_NAME #define SSS_KCM_SOCKET_NAME DEFAULT_KCM_SOCKET_PATH #endif static int kcm_responder_ctx_destructor(void *ptr) { struct resp_ctx *rctx = talloc_get_type(ptr, struct resp_ctx); /* mark that we are shutting down the responder, so it is propagated * into underlying contexts that are freed right before rctx */ DEBUG(SSSDBG_TRACE_FUNC, "Responder is being shut down\n"); rctx->shutting_down = true; return 0; } static errno_t kcm_renewals_init(struct tevent_context *ev, struct resp_ctx *rctx, struct kcm_ctx *kctx, struct krb5_ctx *krb5_ctx, time_t renew_intv, bool *_renewal_enabled) { #ifndef HAVE_KCM_RENEWAL return EOK; #else errno_t ret; ret = kcm_get_renewal_config(kctx, &krb5_ctx, &renew_intv); if (ret == ENOTSUP) { DEBUG(SSSDBG_TRACE_FUNC, "TGT Renewals support disabled\n"); return EOK; } else if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Unable to read TGT renewal configuration [%d]: %s\n", ret, sss_strerror(ret)); return ret; } if (renew_intv > 0) { *_renewal_enabled = true; ret = kcm_renewal_setup(rctx, krb5_ctx, ev, kctx->kcm_data->db, renew_intv); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Unable to setup TGT renewals [%d]: %s\n", ret, sss_strerror(ret)); return ret; } } return EOK; #endif } static errno_t kcm_get_ccdb_be(struct kcm_ctx *kctx) { errno_t ret; char *str_db; ret = confdb_get_string(kctx->rctx->cdb, kctx->rctx, kctx->rctx->confdb_service_path, CONFDB_KCM_DB, "secdb", &str_db); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot get the KCM database type [%d]: %s\n", ret, strerror(ret)); return ret; } DEBUG(SSSDBG_CONF_SETTINGS, "KCM database type: %s\n", str_db); if (strcasecmp(str_db, "memory") == 0) { kctx->cc_be = CCDB_BE_MEMORY; } else if (strcasecmp(str_db, "secdb") == 0) { kctx->cc_be = CCDB_BE_SECDB; } else { DEBUG(SSSDBG_FATAL_FAILURE, "Unexpected KCM database type %s\n", str_db); } return EOK; } static int kcm_get_config(struct kcm_ctx *kctx) { int ret; char *sock_name; ret = confdb_get_int(kctx->rctx->cdb, CONFDB_KCM_CONF_ENTRY, CONFDB_SERVICE_FD_LIMIT, DEFAULT_KCM_FD_LIMIT, &kctx->fd_limit); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Failed to get file descriptors limit\n"); goto done; } ret = confdb_get_int(kctx->rctx->cdb, kctx->rctx->confdb_service_path, CONFDB_RESPONDER_CLI_IDLE_TIMEOUT, DEFAULT_KCM_CLI_IDLE_TIMEOUT, &kctx->rctx->client_idle_timeout); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot get the client idle timeout [%s] [%d]: %s\n", CONFDB_RESPONDER_CLI_IDLE_TIMEOUT, ret, strerror(ret)); goto done; } /* Ensure that the client timeout is at least ten seconds */ if (kctx->rctx->client_idle_timeout < 10) { kctx->rctx->client_idle_timeout = 10; } ret = confdb_get_string(kctx->rctx->cdb, kctx->rctx, kctx->rctx->confdb_service_path, CONFDB_KCM_SOCKET, SSS_KCM_SOCKET_NAME, &sock_name); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot get KCM socket path [%d]: %s\n", ret, strerror(ret)); goto done; } kctx->rctx->sock_name = sock_name; ret = kcm_get_ccdb_be(kctx); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot get KCM ccache DB [%d]: %s\n", ret, strerror(ret)); goto done; } kctx->qctx = kcm_ops_queue_create(kctx, kctx); if (ret != EOK) { DEBUG(SSSDBG_OP_FAILURE, "Cannot create KCM request queue [%d]: %s\n", ret, strerror(ret)); goto done; } ret = EOK; done: return ret; } static int kcm_data_destructor(void *ptr) { struct kcm_resp_ctx *kcm_data = talloc_get_type(ptr, struct kcm_resp_ctx); if (kcm_data != NULL) { krb5_free_context(kcm_data->k5c); } return 0; } static struct kcm_resp_ctx *kcm_data_setup(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct confdb_ctx *cdb, const char *confdb_service_path, enum kcm_ccdb_be cc_be) { struct kcm_resp_ctx *kcm_data; krb5_error_code kret; kcm_data = talloc_zero(mem_ctx, struct kcm_resp_ctx); if (kcm_data == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing kcm data\n"); return NULL; } kcm_data->db = kcm_ccdb_init(kcm_data, ev, cdb, confdb_service_path, cc_be); if (kcm_data->db == NULL) { talloc_free(kcm_data); return NULL; } kret = sss_krb5_init_context(&kcm_data->k5c); if (kret != EOK) { talloc_free(kcm_data); return NULL; } talloc_set_destructor((TALLOC_CTX*)kcm_data, kcm_data_destructor); return kcm_data; } static int kcm_process_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev, struct confdb_ctx *cdb) { struct resp_ctx *rctx; struct kcm_ctx *kctx; bool renewal_enabled = false; struct krb5_ctx *krb5_ctx = NULL; time_t renew_intv = 0; int ret; rctx = talloc_zero(mem_ctx, struct resp_ctx); if (rctx == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing resp_ctx\n"); return ENOMEM; } rctx->ev = ev; rctx->cdb = cdb; rctx->confdb_service_path = CONFDB_KCM_CONF_ENTRY; rctx->shutting_down = false; rctx->lfd = -1; rctx->priv_lfd = -1; talloc_set_destructor((TALLOC_CTX*)rctx, kcm_responder_ctx_destructor); kctx = talloc_zero(rctx, struct kcm_ctx); if (kctx == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing kcm_ctx\n"); ret = ENOMEM; goto fail; } kctx->rctx = rctx; kctx->rctx->pvt_ctx = kctx; ret = kcm_get_config(kctx); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "fatal error getting KCM config\n"); goto fail; } kctx->kcm_data = kcm_data_setup(kctx, ev, kctx->rctx->cdb, kctx->rctx->confdb_service_path, kctx->cc_be); if (kctx->kcm_data == NULL) { DEBUG(SSSDBG_FATAL_FAILURE, "fatal error initializing responder data\n"); ret = EIO; goto fail; } ret = kcm_renewals_init(ev, rctx, kctx, krb5_ctx, renew_intv, &renewal_enabled); if (ret != EOK) { DEBUG(SSSDBG_FATAL_FAILURE, "Unable to initialize TGT Renewals [%d]: %s\n", ret, sss_strerror(ret)); goto fail; } if (renewal_enabled) { /* Disable resp idle timeout to allow renewals */ rctx->idle_timeout = 0; } else { responder_setup_idle_timeout_config(kctx->rctx); } /* Set up file descriptor limits */ responder_set_fd_limit(kctx->fd_limit); ret = activate_unix_sockets(rctx, kcm_connection_setup); if (ret != EOK) goto fail; DEBUG(SSSDBG_TRACE_FUNC, "KCM Initialization complete\n"); return EOK; fail: talloc_free(rctx); return ret; } int main(int argc, const char *argv[]) { int opt; poptContext pc; char *opt_logger = NULL; struct main_context *main_ctx; int ret; uid_t uid = 0; gid_t gid = 0; struct poptOption long_options[] = { POPT_AUTOHELP SSSD_MAIN_OPTS SSSD_LOGGER_OPTS SSSD_SERVER_OPTS(uid, gid) POPT_TABLEEND }; /* Set debug level to invalid value so we can decide if -d 0 was used. */ debug_level = SSSDBG_INVALID; umask(DFL_RSP_UMASK); pc = poptGetContext(argv[0], argc, argv, long_options, 0); while((opt = poptGetNextOpt(pc)) != -1) { switch(opt) { default: fprintf(stderr, "\nInvalid option %s: %s\n\n", poptBadOption(pc, 0), poptStrerror(opt)); poptPrintUsage(pc, stderr, 0); return 1; } } poptFreeContext(pc); /* set up things like debug, signals, daemonization, etc. */ debug_log_file = "sssd_kcm"; DEBUG_INIT(debug_level, opt_logger); ret = server_setup("kcm", true, 0, uid, gid, CONFDB_KCM_CONF_ENTRY, &main_ctx, true); if (ret != EOK) return 2; ret = die_if_parent_died(); if (ret != EOK) { /* This is not fatal, don't return */ DEBUG(SSSDBG_OP_FAILURE, "Could not set up to exit when parent process does\n"); } ret = kcm_process_init(main_ctx, main_ctx->event_ctx, main_ctx->confdb_ctx); if (ret != EOK) return 3; /* loop on main */ server_loop(main_ctx); return 0; }