"Fossies" - the Fresh Open Source Software Archive

Member "sssd-2.2.3/src/providers/ad/ad_machine_pw_renewal.c" (30 Nov 2019, 13215 Bytes) of package /linux/misc/sssd-2.2.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ad_machine_pw_renewal.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.2.0_vs_2.2.1.

    1 /*
    2     SSSD
    3 
    4     Authors:
    5         Sumit Bose <sbose@redhat.com>
    6 
    7     Copyright (C) 2016 Red Hat
    8 
    9     This program is free software; you can redistribute it and/or modify
   10     it under the terms of the GNU General Public License as published by
   11     the Free Software Foundation; either version 3 of the License, or
   12     (at your option) any later version.
   13 
   14     This program is distributed in the hope that it will be useful,
   15     but WITHOUT ANY WARRANTY; without even the implied warranty of
   16     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17     GNU General Public License for more details.
   18 
   19     You should have received a copy of the GNU General Public License
   20     along with this program.  If not, see <http://www.gnu.org/licenses/>.
   21 */
   22 
   23 
   24 #include "util/util.h"
   25 #include "util/strtonum.h"
   26 #include "providers/be_ptask.h"
   27 #include "providers/ad/ad_common.h"
   28 
   29 #ifndef RENEWAL_PROG_PATH
   30 #define RENEWAL_PROG_PATH "/usr/sbin/adcli"
   31 #endif
   32 
   33 struct renewal_data {
   34     struct be_ctx *be_ctx;
   35     char *prog_path;
   36     const char **extra_args;
   37 };
   38 
   39 static errno_t get_adcli_extra_args(const char *ad_domain,
   40                                     const char *ad_hostname,
   41                                     const char *ad_keytab,
   42                                     size_t pw_lifetime_in_days,
   43                                     size_t period,
   44                                     size_t initial_delay,
   45                                     struct renewal_data *renewal_data)
   46 {
   47     const char **args;
   48     size_t c = 0;
   49 
   50     if (ad_domain == NULL || ad_hostname == NULL) {
   51         DEBUG(SSSDBG_CRIT_FAILURE, "Missing AD domain or hostname.\n");
   52         return EINVAL;
   53     }
   54 
   55     renewal_data->prog_path = talloc_strdup(renewal_data, RENEWAL_PROG_PATH);
   56     if (renewal_data->prog_path == NULL) {
   57         DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
   58         return ENOMEM;
   59     }
   60 
   61     args = talloc_array(renewal_data, const char *, 8);
   62     if (args == NULL) {
   63         DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
   64         return ENOMEM;
   65     }
   66 
   67     /* extra_args are added in revers order */
   68     /* first add NULL as a placeholder for the server name which is determined
   69      * at runtime */
   70     args[c++] = NULL;
   71     args[c++] = talloc_asprintf(args, "--computer-password-lifetime=%zu",
   72                                 pw_lifetime_in_days);
   73     args[c++] = talloc_asprintf(args, "--host-fqdn=%s", ad_hostname);
   74     if (ad_keytab != NULL) {
   75         args[c++] = talloc_asprintf(args, "--host-keytab=%s", ad_keytab);
   76     }
   77     args[c++] = talloc_asprintf(args, "--domain=%s", ad_domain);
   78     if (DEBUG_IS_SET(SSSDBG_TRACE_LIBS)) {
   79         args[c++] = talloc_strdup(args, "--verbose");
   80     }
   81     args[c++] = talloc_strdup(args, "update");
   82     args[c] = NULL;
   83 
   84     do {
   85         if (args[--c] == NULL) {
   86             DEBUG(SSSDBG_OP_FAILURE,
   87                   "talloc failed while copying  arguments.\n");
   88             talloc_free(args);
   89             return ENOMEM;
   90         }
   91     } while (c != 1); /* it is expected that the first element is NULL */
   92 
   93     renewal_data->extra_args = args;
   94 
   95     return EOK;
   96 }
   97 
   98 struct renewal_state {
   99     int child_status;
  100     struct sss_child_ctx_old *child_ctx;
  101     struct tevent_timer *timeout_handler;
  102     struct tevent_context *ev;
  103 
  104     struct child_io_fds *io;
  105 };
  106 
  107 static void ad_machine_account_password_renewal_done(struct tevent_req *subreq);
  108 static void
  109 ad_machine_account_password_renewal_timeout(struct tevent_context *ev,
  110                                             struct tevent_timer *te,
  111                                             struct timeval tv, void *pvt);
  112 
  113 static struct tevent_req *
  114 ad_machine_account_password_renewal_send(TALLOC_CTX *mem_ctx,
  115                                   struct tevent_context *ev,
  116                                   struct be_ctx *be_ctx,
  117                                   struct be_ptask *be_ptask,
  118                                   void *pvt)
  119 {
  120     struct renewal_data *renewal_data;
  121     struct renewal_state *state;
  122     struct tevent_req *req;
  123     struct tevent_req *subreq;
  124     pid_t child_pid;
  125     struct timeval tv;
  126     int pipefd_to_child[2] = PIPE_INIT;
  127     int pipefd_from_child[2] = PIPE_INIT;
  128     int ret;
  129     const char **extra_args;
  130     const char *server_name;
  131 
  132     req = tevent_req_create(mem_ctx, &state, struct renewal_state);
  133     if (req == NULL) {
  134         DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
  135         return NULL;
  136     }
  137 
  138     renewal_data = talloc_get_type(pvt, struct renewal_data);
  139 
  140     state->ev = ev;
  141     state->child_status = EFAULT;
  142     state->io = talloc(state, struct child_io_fds);
  143     if (state->io == NULL) {
  144         DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
  145         ret = ENOMEM;
  146         goto done;
  147     }
  148     state->io->write_to_child_fd = -1;
  149     state->io->read_from_child_fd = -1;
  150     talloc_set_destructor((void *) state->io, child_io_destructor);
  151 
  152     server_name = be_fo_get_active_server_name(be_ctx, AD_SERVICE_NAME);
  153     talloc_zfree(renewal_data->extra_args[0]);
  154     if (server_name != NULL) {
  155         renewal_data->extra_args[0] = talloc_asprintf(renewal_data->extra_args,
  156                                                       "--domain-controller=%s",
  157                                                       server_name);
  158         /* if talloc_asprintf() fails we let adcli try to find a server */
  159     }
  160 
  161     extra_args = renewal_data->extra_args;
  162     if (extra_args[0] == NULL) {
  163         extra_args = &renewal_data->extra_args[1];
  164     }
  165 
  166     ret = pipe(pipefd_from_child);
  167     if (ret == -1) {
  168         ret = errno;
  169         DEBUG(SSSDBG_CRIT_FAILURE,
  170               "pipe failed [%d][%s].\n", ret, strerror(ret));
  171         goto done;
  172     }
  173     ret = pipe(pipefd_to_child);
  174     if (ret == -1) {
  175         ret = errno;
  176         DEBUG(SSSDBG_CRIT_FAILURE,
  177               "pipe failed [%d][%s].\n", ret, strerror(ret));
  178         goto done;
  179     }
  180 
  181     child_pid = fork();
  182     if (child_pid == 0) { /* child */
  183         exec_child_ex(state, pipefd_to_child, pipefd_from_child,
  184                       renewal_data->prog_path, -1,
  185                       extra_args, true,
  186                       STDIN_FILENO, STDERR_FILENO);
  187 
  188         /* We should never get here */
  189         DEBUG(SSSDBG_CRIT_FAILURE, "Could not exec renewal child\n");
  190     } else if (child_pid > 0) { /* parent */
  191 
  192         state->io->read_from_child_fd = pipefd_from_child[0];
  193         PIPE_FD_CLOSE(pipefd_from_child[1]);
  194         sss_fd_nonblocking(state->io->read_from_child_fd);
  195 
  196         state->io->write_to_child_fd = pipefd_to_child[1];
  197         PIPE_FD_CLOSE(pipefd_to_child[0]);
  198         sss_fd_nonblocking(state->io->write_to_child_fd);
  199 
  200         /* Set up SIGCHLD handler */
  201         ret = child_handler_setup(ev, child_pid, NULL, NULL, &state->child_ctx);
  202         if (ret != EOK) {
  203             DEBUG(SSSDBG_OP_FAILURE, "Could not set up child handlers [%d]: %s\n",
  204                 ret, sss_strerror(ret));
  205             ret = ERR_RENEWAL_CHILD;
  206             goto done;
  207         }
  208 
  209         /* Set up timeout handler */
  210         tv = tevent_timeval_current_ofs(be_ptask_get_timeout(be_ptask), 0);
  211         state->timeout_handler = tevent_add_timer(ev, req, tv,
  212                                     ad_machine_account_password_renewal_timeout,
  213                                     req);
  214         if(state->timeout_handler == NULL) {
  215             ret = ERR_RENEWAL_CHILD;
  216             goto done;
  217         }
  218 
  219         subreq = read_pipe_send(state, ev, state->io->read_from_child_fd);
  220         if (subreq == NULL) {
  221             DEBUG(SSSDBG_OP_FAILURE, "read_pipe_send failed.\n");
  222             ret = ERR_RENEWAL_CHILD;
  223             goto done;
  224         }
  225         tevent_req_set_callback(subreq,
  226                                 ad_machine_account_password_renewal_done, req);
  227 
  228         /* Now either wait for the timeout to fire or the child
  229          * to finish
  230          */
  231     } else { /* error */
  232         ret = errno;
  233         DEBUG(SSSDBG_CRIT_FAILURE, "fork failed [%d][%s].\n",
  234                                    ret, sss_strerror(ret));
  235         goto done;
  236     }
  237 
  238     ret = EOK;
  239 
  240 done:
  241     if (ret != EOK) {
  242         PIPE_CLOSE(pipefd_from_child);
  243         PIPE_CLOSE(pipefd_to_child);
  244         tevent_req_error(req, ret);
  245         tevent_req_post(req, ev);
  246     }
  247     return req;
  248 }
  249 
  250 static void ad_machine_account_password_renewal_done(struct tevent_req *subreq)
  251 {
  252     uint8_t *buf;
  253     ssize_t buf_len;
  254     struct tevent_req *req = tevent_req_callback_data(subreq,
  255                                                       struct tevent_req);
  256     struct renewal_state *state = tevent_req_data(req, struct renewal_state);
  257     int ret;
  258 
  259     talloc_zfree(state->timeout_handler);
  260 
  261     ret = read_pipe_recv(subreq, state, &buf, &buf_len);
  262     talloc_zfree(subreq);
  263     if (ret != EOK) {
  264         tevent_req_error(req, ret);
  265         return;
  266     }
  267 
  268     DEBUG(SSSDBG_TRACE_LIBS, "--- adcli output start---\n"
  269                              "%.*s"
  270                              "---adcli output end---\n",
  271                              (int) buf_len, buf);
  272 
  273     tevent_req_done(req);
  274     return;
  275 }
  276 
  277 static void
  278 ad_machine_account_password_renewal_timeout(struct tevent_context *ev,
  279                                             struct tevent_timer *te,
  280                                             struct timeval tv, void *pvt)
  281 {
  282     struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
  283     struct renewal_state *state = tevent_req_data(req, struct renewal_state);
  284 
  285     DEBUG(SSSDBG_CRIT_FAILURE, "Timeout reached for AD renewal child.\n");
  286     child_handler_destroy(state->child_ctx);
  287     state->child_ctx = NULL;
  288     state->child_status = ETIMEDOUT;
  289     tevent_req_error(req, ERR_RENEWAL_CHILD);
  290 }
  291 
  292 static errno_t
  293 ad_machine_account_password_renewal_recv(struct tevent_req *req)
  294 {
  295 
  296     TEVENT_REQ_RETURN_ON_ERROR(req);
  297 
  298     return EOK;
  299 }
  300 
  301 errno_t ad_machine_account_password_renewal_init(struct be_ctx *be_ctx,
  302                                                  struct ad_options *ad_opts)
  303 {
  304     int ret;
  305     struct renewal_data *renewal_data;
  306     int lifetime;
  307     size_t period;
  308     size_t initial_delay;
  309     const char *dummy;
  310     char **opt_list;
  311     int opt_list_size;
  312     char *endptr;
  313 
  314     ret = access(RENEWAL_PROG_PATH, X_OK);
  315     if (ret != 0) {
  316         ret = errno;
  317         DEBUG(SSSDBG_CONF_SETTINGS,
  318               "The helper program ["RENEWAL_PROG_PATH"] for renewal "
  319               "doesn't exist [%d]: %s\n", ret, strerror(ret));
  320         return EOK;
  321     }
  322 
  323     lifetime = dp_opt_get_int(ad_opts->basic,
  324                               AD_MAXIMUM_MACHINE_ACCOUNT_PASSWORD_AGE);
  325 
  326     if (lifetime == 0) {
  327         DEBUG(SSSDBG_CONF_SETTINGS, "Automatic machine account renewal disabled.\n");
  328         return EOK;
  329     }
  330 
  331     if (lifetime < 0) {
  332         DEBUG(SSSDBG_CRIT_FAILURE,
  333               "Illegal value [%d] for password lifetime.\n", lifetime);
  334         return EINVAL;
  335     }
  336 
  337     renewal_data = talloc(be_ctx, struct renewal_data);
  338     if (renewal_data == NULL) {
  339         DEBUG(SSSDBG_OP_FAILURE, "talloc failed.\n");
  340         return ENOMEM;
  341     }
  342 
  343     dummy = dp_opt_get_cstring(ad_opts->basic,
  344                                AD_MACHINE_ACCOUNT_PASSWORD_RENEWAL_OPTS);
  345     ret = split_on_separator(renewal_data, dummy, ':', true, false,
  346                              &opt_list, &opt_list_size);
  347     if (ret != EOK) {
  348         DEBUG(SSSDBG_OP_FAILURE, "split_on_separator failed.\n");
  349         goto done;
  350     }
  351 
  352     if (opt_list_size != 2) {
  353         DEBUG(SSSDBG_CRIT_FAILURE, "Wrong number of renewal options.\n");
  354         ret = EINVAL;
  355         goto done;
  356     }
  357 
  358     errno = 0;
  359     period = strtouint32(opt_list[0], &endptr, 10);
  360     if (errno != 0 || *endptr != '\0' || opt_list[0] == endptr) {
  361         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse first renewal option.\n");
  362         ret = EINVAL;
  363         goto done;
  364     }
  365 
  366     errno = 0;
  367     initial_delay = strtouint32(opt_list[1], &endptr, 10);
  368     if (errno != 0 || *endptr != '\0' || opt_list[0] == endptr) {
  369         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse second renewal option.\n");
  370         ret = EINVAL;
  371         goto done;
  372     }
  373 
  374     ret = get_adcli_extra_args(dp_opt_get_cstring(ad_opts->basic, AD_DOMAIN),
  375                    dp_opt_get_cstring(ad_opts->basic, AD_HOSTNAME),
  376                    dp_opt_get_cstring(ad_opts->id_ctx->sdap_id_ctx->opts->basic,
  377                                       SDAP_KRB5_KEYTAB),
  378                    lifetime, period, initial_delay, renewal_data);
  379     if (ret != EOK) {
  380         DEBUG(SSSDBG_OP_FAILURE, "get_adcli_extra_args failed.\n");
  381         goto done;
  382     }
  383 
  384     ret = be_ptask_create(be_ctx, be_ctx, period, initial_delay, 0, 0, 60, 0,
  385                           ad_machine_account_password_renewal_send,
  386                           ad_machine_account_password_renewal_recv,
  387                           renewal_data,
  388                           "AD machine account password renewal",
  389                           BE_PTASK_OFFLINE_DISABLE |
  390                           BE_PTASK_SCHEDULE_FROM_LAST,
  391                           NULL);
  392     if (ret != EOK) {
  393         DEBUG(SSSDBG_OP_FAILURE, "be_ptask_create failed.\n");
  394         goto done;
  395     }
  396 
  397     ret = EOK;
  398 
  399 done:
  400     if (ret != EOK) {
  401         talloc_free(renewal_data);
  402     }
  403 
  404     return ret;
  405 }