From 16dd4955983aee00ed8bb4a6e8a02814827ce0a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pavel=20B=C5=99ezina?= Date: Mon, 19 Dec 2011 15:46:17 +0100 Subject: [PATCH 4/4] SUDO Integration - periodical update of rules in data provider https://fedorahosted.org/sssd/ticket/1110 Adds new configuration options: - ldap_sudo_refresh_enabled - enable/disable periodical updates - ldap_sudo_refresh_timeout - rules timeout (refresh period) --- Makefile.am | 2 + src/config/SSSDConfig.py | 2 + src/config/etc/sssd.api.d/sssd-ldap.conf | 2 + src/providers/ipa/ipa_common.c | 2 + src/providers/ipa/ipa_common.h | 2 +- src/providers/ldap/ldap_common.c | 60 ++++++++ src/providers/ldap/ldap_common.h | 1 + src/providers/ldap/ldap_init.c | 5 + src/providers/ldap/sdap.h | 2 + src/providers/ldap/sdap_sudo_timer.c | 236 ++++++++++++++++++++++++++++++ src/providers/ldap/sdap_sudo_timer.h | 41 +++++ 11 files changed, 354 insertions(+), 1 deletions(-) create mode 100644 src/providers/ldap/sdap_sudo_timer.c create mode 100644 src/providers/ldap/sdap_sudo_timer.h diff --git a/Makefile.am b/Makefile.am index a0855ae..f8082ed 100644 --- a/Makefile.am +++ b/Makefile.am @@ -340,6 +340,7 @@ dist_noinst_HEADERS = \ src/providers/ldap/sdap_async.h \ src/providers/ldap/sdap_async_private.h \ src/providers/ldap/sdap_sudo_cache.h \ + src/providers/ldap/sdap_sudo_timer.h \ src/providers/ldap/sdap_sudo.h \ src/providers/ldap/sdap_id_op.h \ src/providers/ipa/ipa_common.h \ @@ -913,6 +914,7 @@ libsss_ldap_la_LDFLAGS = \ -module if BUILD_SUDO libsss_ldap_la_SOURCES += src/providers/ldap/sdap_sudo_cache.c \ + src/providers/ldap/sdap_sudo_timer.c \ src/providers/ldap/sdap_sudo.c endif diff --git a/src/config/SSSDConfig.py b/src/config/SSSDConfig.py index 0a73893..c5c73b1 100644 --- a/src/config/SSSDConfig.py +++ b/src/config/SSSDConfig.py @@ -240,6 +240,8 @@ option_strings = { # [provider/ldap/sudo] 'ldap_sudo_search_base' : _('Base DN for sudo rules lookups'), + 'ldap_sudo_refresh_enabled' : _('Enable periodical update of all sudo rules'), + 'ldap_sudo_refresh_timeout' : _('Length of time between rules updates'), 'ldap_sudorule_object_class' : _('Object class for sudo rules'), 'ldap_sudorule_name' : _('Sudo rule name'), 'ldap_sudorule_command' : _('Sudo rule command attribute'), diff --git a/src/config/etc/sssd.api.d/sssd-ldap.conf b/src/config/etc/sssd.api.d/sssd-ldap.conf index b155c2b..fb48dcd 100644 --- a/src/config/etc/sssd.api.d/sssd-ldap.conf +++ b/src/config/etc/sssd.api.d/sssd-ldap.conf @@ -109,6 +109,8 @@ ldap_chpass_dns_service_name = str, None, false [provider/ldap/sudo] ldap_sudo_search_base = str, None, false +ldap_sudo_refresh_enabled = bool, None, false +ldap_sudo_refresh_timeout = int, None, false ldap_sudorule_object_class = str, None, false ldap_sudorule_name = str, None, false ldap_sudorule_command = str, None, false diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c index c3ea8c3..12c69dd 100644 --- a/src/providers/ipa/ipa_common.c +++ b/src/providers/ipa/ipa_common.c @@ -61,6 +61,8 @@ struct dp_option ipa_def_ldap_opts[] = { { "ldap_group_search_scope", DP_OPT_STRING, { "sub" }, NULL_STRING }, { "ldap_group_search_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_sudo_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "ldap_sudo_refresh_enabled", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, + { "ldap_sudo_refresh_timeout", DP_OPT_NUMBER, { .number = 300 }, NULL_NUMBER }, { "ldap_schema", DP_OPT_STRING, { "ipa_v1" }, NULL_STRING }, { "ldap_offline_timeout", DP_OPT_NUMBER, { .number = 60 }, NULL_NUMBER }, { "ldap_force_upper_case_realm", DP_OPT_BOOL, BOOL_TRUE, BOOL_TRUE }, diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h index 577e3e1..f29a238 100644 --- a/src/providers/ipa/ipa_common.h +++ b/src/providers/ipa/ipa_common.h @@ -35,7 +35,7 @@ struct ipa_service { /* the following defines are used to keep track of the options in the ldap * module, so that if they change and ipa is not updated correspondingly * this will trigger a runtime abort error */ -#define IPA_OPTS_BASIC_TEST 56 +#define IPA_OPTS_BASIC_TEST 58 /* the following define is used to keep track of the options in the krb5 * module, so that if they change and ipa is not updated correspondingly diff --git a/src/providers/ldap/ldap_common.c b/src/providers/ldap/ldap_common.c index 6ca6f34..18df5ba 100644 --- a/src/providers/ldap/ldap_common.c +++ b/src/providers/ldap/ldap_common.c @@ -26,6 +26,7 @@ #include "providers/fail_over.h" #include "providers/ldap/sdap_async_private.h" #include "providers/krb5/krb5_common.h" +#include "providers/ldap/sdap_sudo_timer.h" #include "db/sysdb_sudo.h" #include "util/sss_krb5.h" @@ -51,6 +52,8 @@ struct dp_option default_basic_opts[] = { { "ldap_group_search_scope", DP_OPT_STRING, { "sub" }, NULL_STRING }, { "ldap_group_search_filter", DP_OPT_STRING, NULL_STRING, NULL_STRING }, { "ldap_sudo_search_base", DP_OPT_STRING, NULL_STRING, NULL_STRING }, + { "ldap_sudo_refresh_enabled", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, + { "ldap_sudo_refresh_timeout", DP_OPT_NUMBER, { .number = 300 }, NULL_NUMBER }, { "ldap_schema", DP_OPT_STRING, { "rfc2307" }, NULL_STRING }, { "ldap_offline_timeout", DP_OPT_NUMBER, { .number = 60 }, NULL_NUMBER }, { "ldap_force_upper_case_realm", DP_OPT_BOOL, BOOL_FALSE, BOOL_FALSE }, @@ -564,6 +567,63 @@ int ldap_get_sudo_options(TALLOC_CTX *memctx, return EOK; } +int sdap_sudo_setup_tasks(struct sdap_id_ctx *id_ctx) +{ + struct sdap_sudo_refresh_ctx *refresh_ctx = NULL; + struct timeval tv; + int ret = EOK; + bool refreshed = false; + bool refresh_enabled = dp_opt_get_bool(id_ctx->opts->basic, + SDAP_SUDO_REFRESH_ENABLED); + + /* set up periodical update of sudo rules */ + if (refresh_enabled) { + refresh_ctx = sdap_sudo_refresh_ctx_init(id_ctx, id_ctx->be, id_ctx, + id_ctx->opts, + tevent_timeval_zero()); + if (refresh_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("sdap_sudo_refresh_ctx_init() failed!\n")); + return ENOMEM; + } + + /* If this is the first startup, we need to kick off + * an refresh immediately, to close a window where + * clients requesting sudo information won't get an + * immediate reply with no entries + */ + ret = sysdb_sudo_get_refreshed(id_ctx->be->sysdb, &refreshed); + if (ret != EOK) { + return ret; + } + if (refreshed) { + /* At least one update has previously run, + * so clients will get cached data. We will delay + * starting to enumerate by 10s so we don't slow + * down the startup process if this is happening + * during system boot. + */ + tv = tevent_timeval_current_ofs(10, 0); + DEBUG(SSSDBG_FUNC_DATA, ("Delaying first refresh of SUDO rules " + "for 10 seconds\n")); + } else { + /* This is our first startup. Schedule the + * update to start immediately once we + * enter the mainloop. + */ + tv = tevent_timeval_current(); + } + + ret = sdap_sudo_refresh_set_timer(refresh_ctx, tv); + if (ret != EOK) { + talloc_free(refresh_ctx); + return ret; + } + } + + return EOK; +} + errno_t sdap_parse_search_base(TALLOC_CTX *mem_ctx, struct dp_option *opts, int class, struct sdap_search_base ***_search_bases) diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h index e6bb8b6..0ab0a5e 100644 --- a/src/providers/ldap/ldap_common.h +++ b/src/providers/ldap/ldap_common.h @@ -88,6 +88,7 @@ void sdap_pam_access_handler(struct be_req *breq); #ifdef BUILD_SUDO /* sudo */ void sdap_sudo_handler(struct be_req *breq); +int sdap_sudo_setup_tasks(struct sdap_id_ctx *ctx); #endif diff --git a/src/providers/ldap/ldap_init.c b/src/providers/ldap/ldap_init.c index 8c4d3e6..2721b00 100644 --- a/src/providers/ldap/ldap_init.c +++ b/src/providers/ldap/ldap_init.c @@ -419,6 +419,11 @@ int sssm_ldap_sudo_init(struct be_ctx *be_ctx, return ret; } + ret = sdap_sudo_setup_tasks(id_ctx); + if (ret != EOK) { + return ret; + } + return ret; #else return EOK; diff --git a/src/providers/ldap/sdap.h b/src/providers/ldap/sdap.h index da328d2..5d6bfb8 100644 --- a/src/providers/ldap/sdap.h +++ b/src/providers/ldap/sdap.h @@ -165,6 +165,8 @@ enum sdap_basic_opt { SDAP_GROUP_SEARCH_SCOPE, SDAP_GROUP_SEARCH_FILTER, SDAP_SUDO_SEARCH_BASE, + SDAP_SUDO_REFRESH_ENABLED, + SDAP_SUDO_REFRESH_TIMEOUT, SDAP_SCHEMA, SDAP_OFFLINE_TIMEOUT, SDAP_FORCE_UPPER_CASE_REALM, diff --git a/src/providers/ldap/sdap_sudo_timer.c b/src/providers/ldap/sdap_sudo_timer.c new file mode 100644 index 0000000..cc664ab --- /dev/null +++ b/src/providers/ldap/sdap_sudo_timer.c @@ -0,0 +1,236 @@ +/* + Authors: + Pavel Březina + + Copyright (C) 2011 Red Hat + + 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 +#include + +#include "util/util.h" +#include "providers/ldap/ldap_common.h" +#include "providers/ldap/sdap_sudo_timer.h" +#include "providers/ldap/sdap_sudo.h" +#include "db/sysdb_sudo.h" + +struct sdap_sudo_refresh_ctx { + struct be_ctx *be_ctx; + struct sdap_id_ctx *id_ctx; + struct sdap_options *opts; + + struct timeval last_refresh; +}; + +static void sdap_sudo_refresh_timer(struct tevent_context *ev, + struct tevent_timer *tt, + struct timeval tv, void *pvt); + +static void sdap_sudo_refresh_reschedule(struct tevent_req *req); + +static void sdap_sudo_refresh_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt); + +struct sdap_sudo_refresh_ctx * +sdap_sudo_refresh_ctx_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct sdap_id_ctx *id_ctx, + struct sdap_options *opts, + struct timeval last_refresh) +{ + struct sdap_sudo_refresh_ctx *refresh_ctx = NULL; + + refresh_ctx = talloc_zero(mem_ctx, struct sdap_sudo_refresh_ctx); + if (refresh_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero() failed!\n")); + return NULL; + } + + refresh_ctx->be_ctx = be_ctx; + refresh_ctx->id_ctx = id_ctx; + refresh_ctx->opts = opts; + refresh_ctx->last_refresh = last_refresh; + + return refresh_ctx; +} + +int sdap_sudo_refresh_set_timer(struct sdap_sudo_refresh_ctx *ctx, + struct timeval tv) +{ + struct tevent_timer *enum_task; + + DEBUG(SSSDBG_TRACE_FUNC, ("Scheduling next refresh of SUDO rules at " + "%ld.%ld\n", (long)tv.tv_sec, (long)tv.tv_usec)); + + enum_task = tevent_add_timer(ctx->be_ctx->ev, ctx, + tv, sdap_sudo_refresh_timer, ctx); + if (!enum_task) { + DEBUG(SSSDBG_FATAL_FAILURE, + ("FATAL: failed to setup SUDO rules refresh task!\n")); + return EFAULT; + } + + return EOK; +} + +static void sdap_sudo_refresh_timer(struct tevent_context *ev, + struct tevent_timer *tt, + struct timeval tv, void *pvt) +{ + struct sdap_sudo_refresh_ctx *refresh_ctx = NULL; + struct sdap_sudo_ctx *sudo_ctx = NULL; + struct tevent_timer *timeout = NULL; + struct tevent_req *req = NULL; + int delay = 0; + int ret; + + refresh_ctx = talloc_get_type(pvt, struct sdap_sudo_refresh_ctx); + + delay = dp_opt_get_int(refresh_ctx->opts->basic, SDAP_SUDO_REFRESH_TIMEOUT); + + if (be_is_offline(refresh_ctx->be_ctx)) { + DEBUG(SSSDBG_TRACE_FUNC, ("Backend is marked offline, retry later!\n")); + tv = tevent_timeval_current_ofs(delay, 0); + ret = sdap_sudo_refresh_set_timer(refresh_ctx, tv); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Error setting up SUDO refresh timer\n")); + } + return; + } + + /* create sudo context */ + sudo_ctx = talloc_zero(refresh_ctx, struct sdap_sudo_ctx); + if (sudo_ctx == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("talloc_zero() failed!\n")); + tv = tevent_timeval_current_ofs(delay, 0); + ret = sdap_sudo_refresh_set_timer(refresh_ctx, tv); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Error setting up SUDO refresh timer\n")); + } + + return; + } + + sudo_ctx->be_ctx = refresh_ctx->be_ctx; + sudo_ctx->be_req = NULL; + sudo_ctx->sdap_ctx = refresh_ctx->id_ctx; + sudo_ctx->sdap_op = NULL; + sudo_ctx->sdap_conn_cache = refresh_ctx->id_ctx->conn_cache; + sudo_ctx->username = NULL; /* download all rules */ + sudo_ctx->uid = 0; + sudo_ctx->groups = NULL; + + /* send request */ + req = sdap_sudo_refresh_send(sudo_ctx, sudo_ctx); + if (req == NULL) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to schedule refresh of SUDO rules, " + "retrying later!\n")); + tv = tevent_timeval_current_ofs(delay, 0); + ret = sdap_sudo_refresh_set_timer(refresh_ctx, tv); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Error setting up SUDO refresh timer\n")); + } + + return; + } + refresh_ctx->last_refresh = tevent_timeval_current(); + + tevent_req_set_callback(req, sdap_sudo_refresh_reschedule, refresh_ctx); + + /* schedule timeout */ + tv = tevent_timeval_current_ofs(delay, 0); + timeout = tevent_add_timer(refresh_ctx->be_ctx->ev, req, tv, + sdap_sudo_refresh_timeout, req); + if (timeout == NULL) { + /* If we can't guarantee a timeout, we + * need to cancel the request, to avoid + * the possibility of starting another + * concurrently + */ + talloc_zfree(req); + + DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to schedule refresh of SUDO rules, " + "retrying later!\n")); + tv = tevent_timeval_current_ofs(delay, 0); + ret = sdap_sudo_refresh_set_timer(refresh_ctx, tv); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Error setting up SUDO refresh timer\n")); + } + } + + return; +} + +static void sdap_sudo_refresh_reschedule(struct tevent_req *req) +{ + struct sdap_sudo_refresh_ctx *refresh_ctx = NULL; + struct sdap_sudo_ctx *sudo_ctx = NULL; + struct timeval tv; + int delay; + int dp_error; + int error; + int ret; + + refresh_ctx = tevent_req_callback_data(req, struct sdap_sudo_refresh_ctx); + ret = sdap_sudo_refresh_recv(req, &sudo_ctx, &dp_error, &error); + talloc_zfree(req); + talloc_zfree(sudo_ctx); + if (ret != EOK) { + tv = tevent_timeval_current(); + } else { + tv = refresh_ctx->last_refresh; + + /* Ok, we've completed a refresh. Save this to the + * sysdb so we can postpone starting up the refresh + * process on the next SSSD service restart (to avoid + * slowing down system boot-up + */ + ret = sysdb_sudo_set_refreshed(refresh_ctx->be_ctx->sysdb, true); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, + ("Could not mark domain as having refreshed.\n")); + /* This error is non-fatal, so continue */ + } + } + + delay = dp_opt_get_int(refresh_ctx->opts->basic, SDAP_SUDO_REFRESH_TIMEOUT); + tv = tevent_timeval_add(&tv, delay, 0); + ret = sdap_sudo_refresh_set_timer(refresh_ctx, tv); + if (ret != EOK) { + DEBUG(SSSDBG_CRIT_FAILURE, ("Error setting up SUDO refresh timer\n")); + } +} + +static void sdap_sudo_refresh_timeout(struct tevent_context *ev, + struct tevent_timer *te, + struct timeval tv, void *pvt) +{ + struct tevent_req *req = talloc_get_type(pvt, struct tevent_req); + struct sdap_sudo_refresh_ctx *refresh_ctx = NULL; + int delay; + + refresh_ctx = tevent_req_callback_data(req, struct sdap_sudo_refresh_ctx); + + delay = dp_opt_get_int(refresh_ctx->opts->basic, SDAP_SUDO_REFRESH_TIMEOUT); + DEBUG(SSSDBG_CRIT_FAILURE, ("Refreshing SUDO rules timed out!" + " Timeout too small? (%ds)!\n", delay)); + + tv = tevent_timeval_current_ofs(delay, 0); + sdap_sudo_refresh_set_timer(refresh_ctx, tv); + + talloc_zfree(req); +} diff --git a/src/providers/ldap/sdap_sudo_timer.h b/src/providers/ldap/sdap_sudo_timer.h new file mode 100644 index 0000000..ad7dcad --- /dev/null +++ b/src/providers/ldap/sdap_sudo_timer.h @@ -0,0 +1,41 @@ +/* + Authors: + Pavel Březina + + Copyright (C) 2011 Red Hat + + 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 _SDAP_SUDO_TIMER_H_ +#define _SDAP_SUDO_TIMER_H_ + +#include + +#include "providers/dp_backend.h" +#include "providers/ldap/ldap_common.h" + +struct sdap_sudo_refresh_ctx; + +int sdap_sudo_refresh_set_timer(struct sdap_sudo_refresh_ctx *ctx, + struct timeval tv); + +struct sdap_sudo_refresh_ctx * +sdap_sudo_refresh_ctx_init(TALLOC_CTX *mem_ctx, + struct be_ctx *be_ctx, + struct sdap_id_ctx *id_ctx, + struct sdap_options *opts, + struct timeval last_refresh); + +#endif /* _SDAP_SUDO_TIMER_H_ */ -- 1.7.6.4