On Thu, Jan 14, 2016 at 05:54:06PM +0100, Sumit Bose wrote:
> Hi,
> 
> this patch adds a task to the AD provider which calls adcli on a regular
> basis to update the machine account password if needed. adcli supports
> this functionality since version 0.8.0. Adding support other utilities like
> msktutil shouldn't be hard.
> 
> Since adcli (and other external tools) does not understand the SSSD
> default options like --debug_level a change in exec_child_ex() was
> needed which is covered in the first patch.
> 
> bye,
> Sumit

patches are now rebased on current master.

bye,
Sumit
From d69bde0f81e92b296d3bdfcd8e5b5dd89cee67f7 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Thu, 14 Jan 2016 13:33:53 +0100
Subject: [PATCH 1/2] UTIL: allow to skip default options for child processes

Currently the SSSD default options like e.g. --debug-level are added
unconditionally to the command line options of a child process when
started with the child helper functions.

If a binary from a different source should be started as a child by SSSD
those options might not be known or used differently. This patch adds an
option to exec_child_ex() which allows to skip the default options and
only add specific options.
---
 src/providers/ad/ad_gpo.c               |  2 +-
 src/providers/krb5/krb5_child_handler.c |  2 +-
 src/responder/pam/pamsrv_p11.c          |  2 +-
 src/tests/cmocka/test_child_common.c    |  4 +-
 src/util/child_common.c                 | 73 ++++++++++++++++++---------------
 src/util/child_common.h                 |  2 +-
 6 files changed, 47 insertions(+), 38 deletions(-)

diff --git a/src/providers/ad/ad_gpo.c b/src/providers/ad/ad_gpo.c
index 
cca5e58618bf861c0d754ef1916583a73c32141a..069196c3b616ebf7661ac9ffe577504b6ebcb674
 100644
--- a/src/providers/ad/ad_gpo.c
+++ b/src/providers/ad/ad_gpo.c
@@ -4144,7 +4144,7 @@ gpo_fork_child(struct tevent_req *req)
     if (pid == 0) { /* child */
         err = exec_child_ex(state,
                             pipefd_to_child, pipefd_from_child,
-                            GPO_CHILD, gpo_child_debug_fd, NULL,
+                            GPO_CHILD, gpo_child_debug_fd, NULL, false,
                             STDIN_FILENO, AD_GPO_CHILD_OUT_FILENO);
         DEBUG(SSSDBG_CRIT_FAILURE, "Could not exec gpo_child: [%d][%s].\n",
               err, strerror(err));
diff --git a/src/providers/krb5/krb5_child_handler.c 
b/src/providers/krb5/krb5_child_handler.c
index 
fa1055eb7fc7e9aa6fabef1c1759c272b217a395..167a2b2ad09b67908cdce8051d8a37e557c91545
 100644
--- a/src/providers/krb5/krb5_child_handler.c
+++ b/src/providers/krb5/krb5_child_handler.c
@@ -312,7 +312,7 @@ static errno_t fork_child(struct tevent_req *req)
         err = exec_child_ex(state,
                             pipefd_to_child, pipefd_from_child,
                             KRB5_CHILD, state->kr->krb5_ctx->child_debug_fd,
-                            k5c_extra_args, STDIN_FILENO, STDOUT_FILENO);
+                            k5c_extra_args, false, STDIN_FILENO, 
STDOUT_FILENO);
         if (err != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Could not exec KRB5 child: 
[%d][%s].\n",
                       err, strerror(err));
diff --git a/src/responder/pam/pamsrv_p11.c b/src/responder/pam/pamsrv_p11.c
index 
bcac439dde57bda2adeab07a4123672060be9259..ad1670136dbf8efc41df6950af744ff8b06e6a11
 100644
--- a/src/responder/pam/pamsrv_p11.c
+++ b/src/responder/pam/pamsrv_p11.c
@@ -322,7 +322,7 @@ struct tevent_req *pam_check_cert_send(TALLOC_CTX *mem_ctx,
     child_pid = fork();
     if (child_pid == 0) { /* child */
         ret = exec_child_ex(state, pipefd_to_child, pipefd_from_child,
-                            P11_CHILD_PATH, child_debug_fd, extra_args,
+                            P11_CHILD_PATH, child_debug_fd, extra_args, false,
                             STDIN_FILENO, STDOUT_FILENO);
         if (ret != EOK) {
             DEBUG(SSSDBG_CRIT_FAILURE, "Could not exec p11 child: [%d][%s].\n",
diff --git a/src/tests/cmocka/test_child_common.c 
b/src/tests/cmocka/test_child_common.c
index 
bf500fa5a1f2b2fe79833e23a53cdf0b06b81260..9ed9c1ae42dd93cef833b738c29259a18e791339
 100644
--- a/src/tests/cmocka/test_child_common.c
+++ b/src/tests/cmocka/test_child_common.c
@@ -139,7 +139,7 @@ void test_exec_child_extra_args(void **state)
         ret = exec_child_ex(child_tctx,
                             child_tctx->pipefd_to_child,
                             child_tctx->pipefd_from_child,
-                            CHILD_DIR"/"TEST_BIN, 2, extra_args,
+                            CHILD_DIR"/"TEST_BIN, 2, extra_args, false,
                             STDIN_FILENO, STDOUT_FILENO);
         assert_int_equal(ret, EOK);
     } else {
@@ -287,7 +287,7 @@ void test_exec_child_echo(void **state)
         ret = exec_child_ex(child_tctx,
                             child_tctx->pipefd_to_child,
                             child_tctx->pipefd_from_child,
-                            CHILD_DIR"/"TEST_BIN, 2, NULL,
+                            CHILD_DIR"/"TEST_BIN, 2, NULL, false,
                             STDIN_FILENO, 3);
         assert_int_equal(ret, EOK);
     }
diff --git a/src/util/child_common.c b/src/util/child_common.c
index 
a6131cd20e7cfff5e5d58806aa2c178327eb9baa..60466c146b5bd9147e9425736072f1ea6ed73663
 100644
--- a/src/util/child_common.c
+++ b/src/util/child_common.c
@@ -612,6 +612,7 @@ static errno_t prepare_child_argv(TALLOC_CTX *mem_ctx,
                                   int child_debug_fd,
                                   const char *binary,
                                   const char *extra_argv[],
+                                  bool extra_args_only,
                                   char ***_argv)
 {
     /*
@@ -619,18 +620,24 @@ static errno_t prepare_child_argv(TALLOC_CTX *mem_ctx,
      * debug_microseconds and NULL
      */
     uint_t argc = 5;
-    char ** argv;
+    char ** argv = NULL;
     errno_t ret = EINVAL;
     size_t i;
 
+    if (extra_args_only) {
+        argc = 2; /* program name and NULL */
+    }
+
     /* Save the current state in case an interrupt changes it */
     bool child_debug_to_file = debug_to_file;
     bool child_debug_timestamps = debug_timestamps;
     bool child_debug_microseconds = debug_microseconds;
     bool child_debug_stderr = debug_to_stderr;
 
-    if (child_debug_to_file) argc++;
-    if (child_debug_stderr) argc++;
+    if (!extra_args_only) {
+        if (child_debug_to_file) argc++;
+        if (child_debug_stderr) argc++;
+    }
 
     if (extra_argv) {
         for (i = 0; extra_argv[i]; i++) argc++;
@@ -659,42 +666,44 @@ static errno_t prepare_child_argv(TALLOC_CTX *mem_ctx,
         }
     }
 
-    argv[--argc] = talloc_asprintf(argv, "--debug-level=%#.4x",
-                              debug_level);
-    if (argv[argc] == NULL) {
-        ret = ENOMEM;
-        goto fail;
-    }
-
-    if (child_debug_stderr) {
-        argv[--argc] = talloc_strdup(argv, "--debug-to-stderr");
+    if (!extra_args_only) {
+        argv[--argc] = talloc_asprintf(argv, "--debug-level=%#.4x",
+                                  debug_level);
         if (argv[argc] == NULL) {
             ret = ENOMEM;
             goto fail;
         }
-    }
 
-    if (child_debug_to_file) {
-        argv[--argc] = talloc_asprintf(argv, "--debug-fd=%d",
-                                       child_debug_fd);
+        if (child_debug_stderr) {
+            argv[--argc] = talloc_strdup(argv, "--debug-to-stderr");
+            if (argv[argc] == NULL) {
+                ret = ENOMEM;
+                goto fail;
+            }
+        }
+
+        if (child_debug_to_file) {
+            argv[--argc] = talloc_asprintf(argv, "--debug-fd=%d",
+                                           child_debug_fd);
+            if (argv[argc] == NULL) {
+                ret = ENOMEM;
+                goto fail;
+            }
+        }
+
+        argv[--argc] = talloc_asprintf(argv, "--debug-timestamps=%d",
+                                       child_debug_timestamps);
         if (argv[argc] == NULL) {
             ret = ENOMEM;
             goto fail;
         }
-    }
 
-    argv[--argc] = talloc_asprintf(argv, "--debug-timestamps=%d",
-                                   child_debug_timestamps);
-    if (argv[argc] == NULL) {
-        ret = ENOMEM;
-        goto fail;
-    }
-
-    argv[--argc] = talloc_asprintf(argv, "--debug-microseconds=%d",
-                                       child_debug_microseconds);
-    if (argv[argc] == NULL) {
-        ret = ENOMEM;
-        goto fail;
+        argv[--argc] = talloc_asprintf(argv, "--debug-microseconds=%d",
+                                           child_debug_microseconds);
+        if (argv[argc] == NULL) {
+            ret = ENOMEM;
+            goto fail;
+        }
     }
 
     argv[--argc] = talloc_strdup(argv, binary);
@@ -720,7 +729,7 @@ fail:
 errno_t exec_child_ex(TALLOC_CTX *mem_ctx,
                       int *pipefd_to_child, int *pipefd_from_child,
                       const char *binary, int debug_fd,
-                      const char *extra_argv[],
+                      const char *extra_argv[], bool extra_args_only,
                       int child_in_fd, int child_out_fd)
 {
     int ret;
@@ -746,7 +755,7 @@ errno_t exec_child_ex(TALLOC_CTX *mem_ctx,
     }
 
     ret = prepare_child_argv(mem_ctx, debug_fd,
-                             binary, extra_argv,
+                             binary, extra_argv, extra_args_only,
                              &argv);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, "prepare_child_argv.\n");
@@ -764,7 +773,7 @@ errno_t exec_child(TALLOC_CTX *mem_ctx,
                    const char *binary, int debug_fd)
 {
     return exec_child_ex(mem_ctx, pipefd_to_child, pipefd_from_child,
-                         binary, debug_fd, NULL,
+                         binary, debug_fd, NULL, false,
                          STDIN_FILENO, STDOUT_FILENO);
 }
 
diff --git a/src/util/child_common.h b/src/util/child_common.h
index 
b93991832b7389177f9da05e694ab729ef50cdc7..0111f2cdb26af8543d68e6a6661d656d1c9c45ac
 100644
--- a/src/util/child_common.h
+++ b/src/util/child_common.h
@@ -104,7 +104,7 @@ void fd_nonblocking(int fd);
 errno_t exec_child_ex(TALLOC_CTX *mem_ctx,
                       int *pipefd_to_child, int *pipefd_from_child,
                       const char *binary, int debug_fd,
-                      const char *extra_argv[],
+                      const char *extra_argv[], bool extra_args_only,
                       int child_in_fd, int child_out_fd);
 
 /* Same as exec_child_ex() except child_in_fd is set to STDIN_FILENO and
-- 
2.1.0

From eee0ca691dbf19942b479122e14f413b45b2ba39 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sb...@redhat.com>
Date: Tue, 12 Jan 2016 11:05:02 +0100
Subject: [PATCH 2/2] AD: add task to renew the machine account password if
 needed

AD expects its clients to renew the machine account password on a
regular basis, be default every 30 days. Even if a client does not renew
the password it might not cause issues because AD does not enforce the
renewal. But the password age might be used to identify unused machine
accounts in large environments which might get disabled or deleted
automatically.

With this patch SSSD calls an external program to check the age of the
machine account password and renew it if needed. Currently 'adcli' is
used as external program which is able to renew the password since
version 0.8.0.

Resolves https://fedorahosted.org/sssd/ticket/1041
---
 Makefile.am                              |   1 +
 src/config/SSSDConfig/__init__.py.in     |   2 +
 src/config/etc/sssd.api.d/sssd-ad.conf   |   2 +
 src/man/sssd-ad.5.xml                    |  33 +++
 src/providers/ad/ad_common.h             |   5 +
 src/providers/ad/ad_init.c               |   7 +
 src/providers/ad/ad_machine_pw_renewal.c | 376 +++++++++++++++++++++++++++++++
 src/providers/ad/ad_opts.c               |   2 +
 src/util/util_errors.c                   |   1 +
 src/util/util_errors.h                   |   1 +
 10 files changed, 430 insertions(+)
 create mode 100644 src/providers/ad/ad_machine_pw_renewal.c

diff --git a/Makefile.am b/Makefile.am
index 
a9d3f25d3775f6ac824b9f9b85dd0412417c33d3..8f72c4b440d0745becd1675c3cc23fdb003645bf
 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -3028,6 +3028,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 
fe2971d993ccf2975bbddbe6c74572ab9f5d4e51..647d081255d738c028f73bc5d65eff58cebdbdf1
 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 
5eb546caac913b839112a70bd81dbde2c7ff2d9f..0ea73d14112d1c7cf7a6d4cbda0d2b2e53a3a7be
 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 
725663b7d1e730fbec8cbabc1ad5526a7bb1aac5..05520d14d0b6d7b61210e6a67d7f1b88ee492ad0
 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 
49e97b0bebf18e795be2549c22de74111ae06583..a932a14d0362a961a83efa254419c5a32f353458
 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 dp_option *opts);
+
 #endif /* AD_COMMON_H_ */
diff --git a/src/providers/ad/ad_init.c b/src/providers/ad/ad_init.c
index 
72ce5536b0f0f69a530bda0ffc41ae93180c1a94..2c6027b04aafc995188d006cc5b0bdb97f684b30
 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->basic);
+    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 
0000000000000000000000000000000000000000..37fab3e953c219ba593ab1feb39ef80302b6e47a
--- /dev/null
+++ b/src/providers/ad/ad_machine_pw_renewal.c
@@ -0,0 +1,376 @@
+/*
+    SSSD
+
+    Authors:
+        Sumit Bose <sb...@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 *ad_domain;
+    char *ad_hostname;
+    char *ad_keytab;
+    size_t pw_lifetime_in_days;
+    size_t period;
+    size_t initial_delay;
+    char *prog_path;
+    const char **extra_args;
+};
+
+static errno_t get_adcli_extra_args(struct renewal_data *renewal_data)
+{
+    const char **args;
+    size_t c = 0;
+
+    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",
+                                renewal_data->pw_lifetime_in_days);
+    args[c++] = talloc_asprintf(args, "--host-fqdn=%s",
+                                renewal_data->ad_hostname);
+    args[c++] = talloc_asprintf(args, "--host-keytab=%s",
+                                renewal_data->ad_keytab);
+    args[c++] = talloc_asprintf(args, "--domain=%s", renewal_data->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_period(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",
+                             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 dp_option *opts)
+{
+    int ret;
+    struct renewal_data *renewal_data;
+    int lifetime;
+    const char *dummy;
+    char **opt_list;
+    int opt_list_size;
+    char *endptr;
+
+    lifetime = dp_opt_get_int(opts, 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(opts, 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;
+    renewal_data->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;
+    renewal_data->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;
+    }
+
+    renewal_data->pw_lifetime_in_days = lifetime;
+    renewal_data->ad_domain = talloc_strdup(renewal_data,
+                                            dp_opt_get_string(opts, 
AD_DOMAIN));
+
+    renewal_data->ad_hostname = talloc_strdup(renewal_data,
+                                              dp_opt_get_string(opts,
+                                                                AD_HOSTNAME));
+    renewal_data->ad_keytab = talloc_strdup(renewal_data,
+                                            dp_opt_get_string(opts, 
AD_KEYTAB));
+    if (renewal_data->ad_domain == NULL || renewal_data->ad_hostname == NULL
+            || renewal_data->ad_keytab == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, "Missing AD domain or hostname.\n");
+        ret = EINVAL;
+        goto done;
+    }
+
+    ret = get_adcli_extra_args(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, renewal_data->period,
+                          renewal_data->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 
4ea96637ca7264c76109ed8c2f7b5e8a94f73bfe..8b2841eadc0236b51f8c9c2c02b7c98837fbe416
 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 
e7f30ab3c982ebca32844ec3755a69bc00f85f4f..59ae63ab8d6e834a772349b162bf282f9a4f1c72
 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 
a1c822c4b03817cb8ebf32a9adeb657de57c9de6..05791f2f08f107a8b4830b810b8826983763174f
 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 */
 };
 
-- 
2.1.0

_______________________________________________
sssd-devel mailing list
sssd-devel@lists.fedorahosted.org
https://lists.fedorahosted.org/admin/lists/sssd-devel@lists.fedorahosted.org

Reply via email to