summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am1
-rw-r--r--src/config/SSSDConfig/__init__.py.in2
-rw-r--r--src/config/etc/sssd.api.d/sssd-ad.conf2
-rw-r--r--src/man/sssd-ad.5.xml33
-rw-r--r--src/providers/ad/ad_common.h5
-rw-r--r--src/providers/ad/ad_init.c7
-rw-r--r--src/providers/ad/ad_machine_pw_renewal.c372
-rw-r--r--src/providers/ad/ad_opts.c2
-rw-r--r--src/util/util_errors.c1
-rw-r--r--src/util/util_errors.h1
10 files changed, 426 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index c5ed2857..6008b06d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3058,6 +3058,7 @@ libsss_ad_la_SOURCES = \
src/providers/ad/ad_common.h \
src/providers/ad/ad_init.c \
src/providers/ad/ad_dyndns.c \
+ src/providers/ad/ad_machine_pw_renewal.c \
src/providers/ad/ad_id.c \
src/providers/ad/ad_id.h \
src/providers/ad/ad_access.c \
diff --git a/src/config/SSSDConfig/__init__.py.in b/src/config/SSSDConfig/__init__.py.in
index fe2971d9..647d0812 100644
--- a/src/config/SSSDConfig/__init__.py.in
+++ b/src/config/SSSDConfig/__init__.py.in
@@ -200,6 +200,8 @@ option_strings = {
'ad_gpo_map_deny' : _('PAM service names for which GPO-based access is always denied'),
'ad_gpo_default_right' : _('Default logon right (or permit/deny) to use for unmapped PAM service names'),
'ad_site' : _('a particular site to be used by the client'),
+ 'ad_maximum_machine_account_password_age' : _('Maximum age in days before the machine account password should be renewed'),
+ 'ad_machine_account_password_renewal_opts' : _('Option for tuing the machine account renewal task'),
# [provider/krb5]
'krb5_kdcip' : _('Kerberos server address'),
diff --git a/src/config/etc/sssd.api.d/sssd-ad.conf b/src/config/etc/sssd.api.d/sssd-ad.conf
index 5eb546ca..0ea73d14 100644
--- a/src/config/etc/sssd.api.d/sssd-ad.conf
+++ b/src/config/etc/sssd.api.d/sssd-ad.conf
@@ -17,6 +17,8 @@ ad_gpo_map_permit = str, None, false
ad_gpo_map_deny = str, None, false
ad_gpo_default_right = str, None, false
ad_site = str, None, false
+ad_maximum_machine_account_password_age = int, None, false
+ad_machine_account_password_renewal_opts = str, None, false
ldap_uri = str, None, false
ldap_backup_uri = str, None, false
ldap_search_base = str, None, false
diff --git a/src/man/sssd-ad.5.xml b/src/man/sssd-ad.5.xml
index 725663b7..05520d14 100644
--- a/src/man/sssd-ad.5.xml
+++ b/src/man/sssd-ad.5.xml
@@ -719,6 +719,39 @@ ad_gpo_map_deny = +my_pam_service
</varlistentry>
<varlistentry>
+ <term>ad_maximum_machine_account_password_age (integer)</term>
+ <listitem>
+ <para>
+ SSSD will check once a day if the machine account
+ password is older than the given age in days and try
+ to renew it. A value of 0 will disable the renewal
+ attempt.
+ </para>
+ <para>
+ Default: 30 days
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>ad_machine_account_password_renewal_opts (string)</term>
+ <listitem>
+ <para>
+ This option should only be used to test the machine
+ account renewal task. The option expect 2 integers
+ seperated by a colon (':'). The first integer
+ defines the interval in seconds how often the task
+ is run. The second specifies the inital timeout in
+ seconds before the task is run for the first time
+ after startup.
+ </para>
+ <para>
+ Default: 86400:750 (24h and 15m)
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term>dyndns_update (boolean)</term>
<listitem>
<para>
diff --git a/src/providers/ad/ad_common.h b/src/providers/ad/ad_common.h
index 49e97b0b..d61be42c 100644
--- a/src/providers/ad/ad_common.h
+++ b/src/providers/ad/ad_common.h
@@ -62,6 +62,8 @@ enum ad_basic_opt {
AD_GPO_DEFAULT_RIGHT,
AD_SITE,
AD_KRB5_CONFD_PATH,
+ AD_MAXIMUM_MACHINE_ACCOUNT_PASSWORD_AGE,
+ AD_MACHINE_ACCOUNT_PASSWORD_RENEWAL_OPTS,
AD_OPTS_BASIC /* opts counter */
};
@@ -179,4 +181,7 @@ int ad_autofs_init(struct be_ctx *be_ctx,
struct bet_ops **ops,
void **pvt_data);
+errno_t ad_machine_account_password_renewal_init(struct be_ctx *be_ctx,
+ struct ad_options *ad_opts);
+
#endif /* AD_COMMON_H_ */
diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
index 72ce5536..e40fb6f1 100644
--- a/src/providers/ad/ad_init.c
+++ b/src/providers/ad/ad_init.c
@@ -308,6 +308,13 @@ sssm_ad_id_init(struct be_ctx *bectx,
"will not work [%d]: %s\n", ret, strerror(ret));
}
+ ret = ad_machine_account_password_renewal_init(bectx, ad_options);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Cannot setup task for machine account "
+ "password renewal.\n");
+ goto done;
+ }
+
*ops = &ad_id_ops;
*pvt_data = ad_ctx;
diff --git a/src/providers/ad/ad_machine_pw_renewal.c b/src/providers/ad/ad_machine_pw_renewal.c
new file mode 100644
index 00000000..e42c700e
--- /dev/null
+++ b/src/providers/ad/ad_machine_pw_renewal.c
@@ -0,0 +1,372 @@
+/*
+ SSSD
+
+ Authors:
+ Sumit Bose <sbose@redhat.com>
+
+ Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "util/util.h"
+#include "util/strtonum.h"
+#include "providers/dp_ptask.h"
+#include "providers/ad/ad_common.h"
+
+#ifndef RENEWAL_PROG_PATH
+#define RENEWAL_PROG_PATH "/usr/sbin/adcli"
+#endif
+
+struct renewal_data {
+ char *prog_path;
+ const char **extra_args;
+};
+
+static errno_t get_adcli_extra_args(const char *ad_domain,
+ const char *ad_hostname,
+ const char *ad_keytab,
+ size_t pw_lifetime_in_days,
+ size_t period,
+ size_t initial_delay,
+ struct renewal_data *renewal_data)
+{
+ const char **args;
+ size_t c = 0;
+
+ if (ad_domain == NULL || ad_hostname == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Missing AD domain or hostname.\n");
+ return EINVAL;
+ }
+
+ renewal_data->prog_path = talloc_strdup(renewal_data, RENEWAL_PROG_PATH);
+ if (renewal_data->prog_path == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
+ return ENOMEM;
+ }
+
+ args = talloc_array(renewal_data, const char *, 7);
+ if (args == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
+ return ENOMEM;
+ }
+
+ /* extra_args are added in revers order */
+ args[c++] = talloc_asprintf(args, "--computer-password-lifetime=%zu",
+ pw_lifetime_in_days);
+ args[c++] = talloc_asprintf(args, "--host-fqdn=%s", ad_hostname);
+ if (ad_keytab != NULL) {
+ args[c++] = talloc_asprintf(args, "--host-keytab=%s", ad_keytab);
+ }
+ args[c++] = talloc_asprintf(args, "--domain=%s", ad_domain);
+ if (DEBUG_IS_SET(SSSDBG_TRACE_LIBS)) {
+ args[c++] = talloc_strdup(args, "--verbose");
+ }
+ args[c++] = talloc_strdup(args, "update");
+ args[c] = NULL;
+
+ do {
+ if (args[--c] == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "talloc failed while copying arguments.\n");
+ talloc_free(args);
+ return ENOMEM;
+ }
+ } while (c != 0);
+
+ renewal_data->extra_args = args;
+
+ return EOK;
+}
+
+struct renewal_state {
+ int child_status;
+ struct sss_child_ctx_old *child_ctx;
+ struct tevent_timer *timeout_handler;
+ struct tevent_context *ev;
+
+ int write_to_child_fd;
+ int read_from_child_fd;
+};
+
+static void ad_machine_account_password_renewal_done(struct tevent_req *subreq);
+static void
+ad_machine_account_password_renewal_timeout(struct tevent_context *ev,
+ struct tevent_timer *te,
+ struct timeval tv, void *pvt);
+
+static struct tevent_req *
+ad_machine_account_password_renewal_send(TALLOC_CTX *mem_ctx,
+ struct tevent_context *ev,
+ struct be_ctx *be_ctx,
+ struct be_ptask *be_ptask,
+ void *pvt)
+{
+ struct renewal_data *renewal_data;
+ struct renewal_state *state;
+ struct tevent_req *req;
+ struct tevent_req *subreq;
+ pid_t child_pid;
+ struct timeval tv;
+ int pipefd_to_child[2];
+ int pipefd_from_child[2];
+ int ret;
+
+ req = tevent_req_create(mem_ctx, &state, struct renewal_state);
+ if (req == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
+ return NULL;
+ }
+
+ renewal_data = talloc_get_type(pvt, struct renewal_data);
+
+ state->ev = ev;
+ state->child_status = EFAULT;
+ state->read_from_child_fd = -1;
+ state->write_to_child_fd = -1;
+
+ ret = pipe(pipefd_from_child);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pipe failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+ ret = pipe(pipefd_to_child);
+ if (ret == -1) {
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "pipe failed [%d][%s].\n", ret, strerror(ret));
+ goto done;
+ }
+
+ child_pid = fork();
+ if (child_pid == 0) { /* child */
+ ret = exec_child_ex(state, pipefd_to_child, pipefd_from_child,
+ renewal_data->prog_path, -1,
+ renewal_data->extra_args, true,
+ STDIN_FILENO, STDERR_FILENO);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Could not exec renewal child: [%d][%s].\n",
+ ret, strerror(ret));
+ goto done;
+ }
+ } else if (child_pid > 0) { /* parent */
+
+ state->read_from_child_fd = pipefd_from_child[0];
+ close(pipefd_from_child[1]);
+ sss_fd_nonblocking(state->read_from_child_fd);
+
+ state->write_to_child_fd = pipefd_to_child[1];
+ close(pipefd_to_child[0]);
+ sss_fd_nonblocking(state->write_to_child_fd);
+
+ /* Set up SIGCHLD handler */
+ ret = child_handler_setup(ev, child_pid, NULL, NULL, &state->child_ctx);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "Could not set up child handlers [%d]: %s\n",
+ ret, sss_strerror(ret));
+ ret = ERR_RENEWAL_CHILD;
+ goto done;
+ }
+
+ /* Set up timeout handler */
+ tv = tevent_timeval_current_ofs(be_ptask_get_timeout(be_ptask), 0);
+ state->timeout_handler = tevent_add_timer(ev, req, tv,
+ ad_machine_account_password_renewal_timeout,
+ req);
+ if(state->timeout_handler == NULL) {
+ ret = ERR_RENEWAL_CHILD;
+ goto done;
+ }
+
+ subreq = read_pipe_send(state, ev, state->read_from_child_fd);
+ if (subreq == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "read_pipe_send failed.\n");
+ ret = ERR_RENEWAL_CHILD;
+ goto done;
+ }
+ tevent_req_set_callback(subreq,
+ ad_machine_account_password_renewal_done, req);
+
+ /* Now either wait for the timeout to fire or the child
+ * to finish
+ */
+ } else { /* error */
+ ret = errno;
+ DEBUG(SSSDBG_CRIT_FAILURE, "fork failed [%d][%s].\n",
+ ret, sss_strerror(ret));
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+ return req;
+}
+
+static void ad_machine_account_password_renewal_done(struct tevent_req *subreq)
+{
+ uint8_t *buf;
+ ssize_t buf_len;
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct renewal_state *state = tevent_req_data(req, struct renewal_state);
+ int ret;
+
+ talloc_zfree(state->timeout_handler);
+
+ ret = read_pipe_recv(subreq, state, &buf, &buf_len);
+ talloc_zfree(subreq);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ DEBUG(SSSDBG_TRACE_LIBS, "--- adcli output start---\n"
+ "%.*s"
+ "---adcli output end---\n",
+ (int) buf_len, buf);
+
+ close(state->read_from_child_fd);
+ state->read_from_child_fd = -1;
+
+
+ tevent_req_done(req);
+ return;
+}
+
+static void
+ad_machine_account_password_renewal_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 renewal_state *state = tevent_req_data(req, struct renewal_state);
+
+ DEBUG(SSSDBG_CRIT_FAILURE, "Timeout reached for AD renewal child.\n");
+ child_handler_destroy(state->child_ctx);
+ state->child_ctx = NULL;
+ state->child_status = ETIMEDOUT;
+ tevent_req_error(req, ERR_RENEWAL_CHILD);
+}
+
+static errno_t
+ad_machine_account_password_renewal_recv(struct tevent_req *req)
+{
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+errno_t ad_machine_account_password_renewal_init(struct be_ctx *be_ctx,
+ struct ad_options *ad_opts)
+{
+ int ret;
+ struct renewal_data *renewal_data;
+ int lifetime;
+ size_t period;
+ size_t initial_delay;
+ const char *dummy;
+ char **opt_list;
+ int opt_list_size;
+ char *endptr;
+
+ lifetime = dp_opt_get_int(ad_opts->basic,
+ AD_MAXIMUM_MACHINE_ACCOUNT_PASSWORD_AGE);
+
+ if (lifetime == 0) {
+ DEBUG(SSSDBG_CONF_SETTINGS, "Automatic machine account renewal disabled.\n");
+ return EOK;
+ }
+
+ if (lifetime < 0) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "Illegal value [%d] for password lifetime.\n", lifetime);
+ return EINVAL;
+ }
+
+ renewal_data = talloc(be_ctx, struct renewal_data);
+ if (renewal_data == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE, "talloc failed.\n");
+ return ENOMEM;
+ }
+
+ dummy = dp_opt_get_cstring(ad_opts->basic,
+ AD_MACHINE_ACCOUNT_PASSWORD_RENEWAL_OPTS);
+ ret = split_on_separator(renewal_data, dummy, ':', true, false,
+ &opt_list, &opt_list_size);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "split_on_separator failed.\n");
+ goto done;
+ }
+
+ if (opt_list_size != 2) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Wrong number of renewal options.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ errno = 0;
+ period = strtouint32(opt_list[0], &endptr, 10);
+ if (errno != 0 || *endptr != '\0' || opt_list[0] == endptr) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse first renewal option.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ errno = 0;
+ initial_delay = strtouint32(opt_list[1], &endptr, 10);
+ if (errno != 0 || *endptr != '\0' || opt_list[0] == endptr) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse second renewal option.\n");
+ ret = EINVAL;
+ goto done;
+ }
+
+ ret = get_adcli_extra_args(dp_opt_get_cstring(ad_opts->basic, AD_DOMAIN),
+ dp_opt_get_cstring(ad_opts->basic, AD_HOSTNAME),
+ dp_opt_get_cstring(ad_opts->id_ctx->sdap_id_ctx->opts->basic,
+ SDAP_KRB5_KEYTAB),
+ lifetime, period, initial_delay, renewal_data);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "get_adcli_extra_args failed.\n");
+ goto done;
+ }
+
+ ret = be_ptask_create(be_ctx, be_ctx, period, initial_delay, 0, 0, 60,
+ BE_PTASK_OFFLINE_DISABLE, 0,
+ ad_machine_account_password_renewal_send,
+ ad_machine_account_password_renewal_recv,
+ renewal_data,
+ "AD machine account password renewal", NULL);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE, "be_ptask_create failed.\n");
+ goto done;
+ }
+
+ ret = EOK;
+
+done:
+ if (ret != EOK) {
+ talloc_free(renewal_data);
+ }
+
+ return ret;
+}
diff --git a/src/providers/ad/ad_opts.c b/src/providers/ad/ad_opts.c
index 4ea96637..8b2841ea 100644
--- a/src/providers/ad/ad_opts.c
+++ b/src/providers/ad/ad_opts.c
@@ -48,6 +48,8 @@ struct dp_option ad_basic_opts[] = {
{ "ad_gpo_default_right", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "ad_site", DP_OPT_STRING, NULL_STRING, NULL_STRING },
{ "krb5_confd_path", DP_OPT_STRING, { KRB5_MAPPING_DIR }, NULL_STRING },
+ { "ad_maximum_machine_account_password_age", DP_OPT_NUMBER, { .number = 30 }, NULL_NUMBER },
+ { "ad_machine_account_password_renewal_opts", DP_OPT_STRING, { "86400:750" }, NULL_STRING },
DP_OPTION_TERMINATOR
};
diff --git a/src/util/util_errors.c b/src/util/util_errors.c
index e7f30ab3..59ae63ab 100644
--- a/src/util/util_errors.c
+++ b/src/util/util_errors.c
@@ -83,6 +83,7 @@ struct err_string error_to_str[] = {
{ "Message sender is the bus" }, /* ERR_SBUS_SENDER_BUS */
{ "Subdomain is inactive" }, /* ERR_SUBDOM_INACTIVE */
{ "Account is locked" }, /* ERR_ACCOUNT_LOCKED */
+ { "AD renewal child failed" }, /* ERR_RENEWAL_CHILD */
{ "ERR_LAST" } /* ERR_LAST */
};
diff --git a/src/util/util_errors.h b/src/util/util_errors.h
index a1c822c4..05791f2f 100644
--- a/src/util/util_errors.h
+++ b/src/util/util_errors.h
@@ -105,6 +105,7 @@ enum sssd_errors {
ERR_SBUS_SENDER_BUS,
ERR_SUBDOM_INACTIVE,
ERR_ACCOUNT_LOCKED,
+ ERR_RENEWAL_CHILD,
ERR_LAST /* ALWAYS LAST */
};