LCOV - code coverage report
Current view: top level - providers/ad - ad_machine_pw_renewal.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 178 0.0 %
Date: 2016-06-29 Functions: 0 6 0.0 %

          Line data    Source code
       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           0 : 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           0 :     size_t c = 0;
      49             : 
      50           0 :     if (ad_domain == NULL || ad_hostname == NULL) {
      51           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Missing AD domain or hostname.\n");
      52           0 :         return EINVAL;
      53             :     }
      54             : 
      55           0 :     renewal_data->prog_path = talloc_strdup(renewal_data, RENEWAL_PROG_PATH);
      56           0 :     if (renewal_data->prog_path == NULL) {
      57           0 :         DEBUG(SSSDBG_OP_FAILURE, "talloc_strdup failed.\n");
      58           0 :         return ENOMEM;
      59             :     }
      60             : 
      61           0 :     args = talloc_array(renewal_data, const char *, 8);
      62           0 :     if (args == NULL) {
      63           0 :         DEBUG(SSSDBG_OP_FAILURE, "talloc_array failed.\n");
      64           0 :         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           0 :     args[c++] = NULL;
      71           0 :     args[c++] = talloc_asprintf(args, "--computer-password-lifetime=%zu",
      72             :                                 pw_lifetime_in_days);
      73           0 :     args[c++] = talloc_asprintf(args, "--host-fqdn=%s", ad_hostname);
      74           0 :     if (ad_keytab != NULL) {
      75           0 :         args[c++] = talloc_asprintf(args, "--host-keytab=%s", ad_keytab);
      76             :     }
      77           0 :     args[c++] = talloc_asprintf(args, "--domain=%s", ad_domain);
      78           0 :     if (DEBUG_IS_SET(SSSDBG_TRACE_LIBS)) {
      79           0 :         args[c++] = talloc_strdup(args, "--verbose");
      80             :     }
      81           0 :     args[c++] = talloc_strdup(args, "update");
      82           0 :     args[c] = NULL;
      83             : 
      84             :     do {
      85           0 :         if (args[--c] == NULL) {
      86           0 :             DEBUG(SSSDBG_OP_FAILURE,
      87             :                   "talloc failed while copying  arguments.\n");
      88           0 :             talloc_free(args);
      89           0 :             return ENOMEM;
      90             :         }
      91           0 :     } while (c != 1); /* is is expected that the first element is NULL */
      92             : 
      93           0 :     renewal_data->extra_args = args;
      94             : 
      95           0 :     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           0 : 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           0 :     int pipefd_to_child[2] = PIPE_INIT;
     127           0 :     int pipefd_from_child[2] = PIPE_INIT;
     128             :     int ret;
     129             :     const char **extra_args;
     130             :     const char *server_name;
     131             : 
     132           0 :     req = tevent_req_create(mem_ctx, &state, struct renewal_state);
     133           0 :     if (req == NULL) {
     134           0 :         DEBUG(SSSDBG_OP_FAILURE, "tevent_req_create failed.\n");
     135           0 :         return NULL;
     136             :     }
     137             : 
     138           0 :     renewal_data = talloc_get_type(pvt, struct renewal_data);
     139             : 
     140           0 :     state->ev = ev;
     141           0 :     state->child_status = EFAULT;
     142           0 :     state->io = talloc(state, struct child_io_fds);
     143           0 :     if (state->io == NULL) {
     144           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
     145           0 :         ret = ENOMEM;
     146           0 :         goto done;
     147             :     }
     148           0 :     state->io->write_to_child_fd = -1;
     149           0 :     state->io->read_from_child_fd = -1;
     150           0 :     talloc_set_destructor((void *) state->io, child_io_destructor);
     151             : 
     152           0 :     server_name = be_fo_get_active_server_name(be_ctx, AD_SERVICE_NAME);
     153           0 :     talloc_zfree(renewal_data->extra_args[0]);
     154           0 :     if (server_name != NULL) {
     155           0 :         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           0 :     extra_args = renewal_data->extra_args;
     162           0 :     if (extra_args[0] == NULL) {
     163           0 :         extra_args = &renewal_data->extra_args[1];
     164             :     }
     165             : 
     166           0 :     ret = pipe(pipefd_from_child);
     167           0 :     if (ret == -1) {
     168           0 :         ret = errno;
     169           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     170             :               "pipe failed [%d][%s].\n", ret, strerror(ret));
     171           0 :         goto done;
     172             :     }
     173           0 :     ret = pipe(pipefd_to_child);
     174           0 :     if (ret == -1) {
     175           0 :         ret = errno;
     176           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     177             :               "pipe failed [%d][%s].\n", ret, strerror(ret));
     178           0 :         goto done;
     179             :     }
     180             : 
     181           0 :     child_pid = fork();
     182           0 :     if (child_pid == 0) { /* child */
     183           0 :         exec_child_ex(state, pipefd_to_child, pipefd_from_child,
     184           0 :                       renewal_data->prog_path, -1,
     185             :                       extra_args, true,
     186             :                       STDIN_FILENO, STDERR_FILENO);
     187             : 
     188             :         /* We should never get here */
     189           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Could not exec renewal child\n");
     190           0 :     } else if (child_pid > 0) { /* parent */
     191             : 
     192           0 :         state->io->read_from_child_fd = pipefd_from_child[0];
     193           0 :         PIPE_FD_CLOSE(pipefd_from_child[1]);
     194           0 :         sss_fd_nonblocking(state->io->read_from_child_fd);
     195             : 
     196           0 :         state->io->write_to_child_fd = pipefd_to_child[1];
     197           0 :         PIPE_FD_CLOSE(pipefd_to_child[0]);
     198           0 :         sss_fd_nonblocking(state->io->write_to_child_fd);
     199             : 
     200             :         /* Set up SIGCHLD handler */
     201           0 :         ret = child_handler_setup(ev, child_pid, NULL, NULL, &state->child_ctx);
     202           0 :         if (ret != EOK) {
     203           0 :             DEBUG(SSSDBG_OP_FAILURE, "Could not set up child handlers [%d]: %s\n",
     204             :                 ret, sss_strerror(ret));
     205           0 :             ret = ERR_RENEWAL_CHILD;
     206           0 :             goto done;
     207             :         }
     208             : 
     209             :         /* Set up timeout handler */
     210           0 :         tv = tevent_timeval_current_ofs(be_ptask_get_timeout(be_ptask), 0);
     211           0 :         state->timeout_handler = tevent_add_timer(ev, req, tv,
     212             :                                     ad_machine_account_password_renewal_timeout,
     213             :                                     req);
     214           0 :         if(state->timeout_handler == NULL) {
     215           0 :             ret = ERR_RENEWAL_CHILD;
     216           0 :             goto done;
     217             :         }
     218             : 
     219           0 :         subreq = read_pipe_send(state, ev, state->io->read_from_child_fd);
     220           0 :         if (subreq == NULL) {
     221           0 :             DEBUG(SSSDBG_OP_FAILURE, "read_pipe_send failed.\n");
     222           0 :             ret = ERR_RENEWAL_CHILD;
     223           0 :             goto done;
     224             :         }
     225           0 :         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           0 :         ret = errno;
     233           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "fork failed [%d][%s].\n",
     234             :                                    ret, sss_strerror(ret));
     235           0 :         goto done;
     236             :     }
     237             : 
     238           0 :     ret = EOK;
     239             : 
     240             : done:
     241           0 :     if (ret != EOK) {
     242           0 :         PIPE_CLOSE(pipefd_from_child);
     243           0 :         PIPE_CLOSE(pipefd_to_child);
     244           0 :         tevent_req_error(req, ret);
     245           0 :         tevent_req_post(req, ev);
     246             :     }
     247           0 :     return req;
     248             : }
     249             : 
     250           0 : static void ad_machine_account_password_renewal_done(struct tevent_req *subreq)
     251             : {
     252             :     uint8_t *buf;
     253             :     ssize_t buf_len;
     254           0 :     struct tevent_req *req = tevent_req_callback_data(subreq,
     255             :                                                       struct tevent_req);
     256           0 :     struct renewal_state *state = tevent_req_data(req, struct renewal_state);
     257             :     int ret;
     258             : 
     259           0 :     talloc_zfree(state->timeout_handler);
     260             : 
     261           0 :     ret = read_pipe_recv(subreq, state, &buf, &buf_len);
     262           0 :     talloc_zfree(subreq);
     263           0 :     if (ret != EOK) {
     264           0 :         tevent_req_error(req, ret);
     265           0 :         return;
     266             :     }
     267             : 
     268           0 :     DEBUG(SSSDBG_TRACE_LIBS, "--- adcli output start---\n"
     269             :                              "%.*s"
     270             :                              "---adcli output end---\n",
     271             :                              (int) buf_len, buf);
     272             : 
     273           0 :     tevent_req_done(req);
     274           0 :     return;
     275             : }
     276             : 
     277             : static void
     278           0 : ad_machine_account_password_renewal_timeout(struct tevent_context *ev,
     279             :                                             struct tevent_timer *te,
     280             :                                             struct timeval tv, void *pvt)
     281             : {
     282           0 :     struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
     283           0 :     struct renewal_state *state = tevent_req_data(req, struct renewal_state);
     284             : 
     285           0 :     DEBUG(SSSDBG_CRIT_FAILURE, "Timeout reached for AD renewal child.\n");
     286           0 :     child_handler_destroy(state->child_ctx);
     287           0 :     state->child_ctx = NULL;
     288           0 :     state->child_status = ETIMEDOUT;
     289           0 :     tevent_req_error(req, ERR_RENEWAL_CHILD);
     290           0 : }
     291             : 
     292             : static errno_t
     293           0 : ad_machine_account_password_renewal_recv(struct tevent_req *req)
     294             : {
     295             : 
     296           0 :     TEVENT_REQ_RETURN_ON_ERROR(req);
     297             : 
     298           0 :     return EOK;
     299             : }
     300             : 
     301           0 : 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           0 :     ret = access(RENEWAL_PROG_PATH, X_OK);
     315           0 :     if (ret != 0) {
     316           0 :         ret = errno;
     317           0 :         DEBUG(SSSDBG_CONF_SETTINGS,
     318             :               "The helper program ["RENEWAL_PROG_PATH"] for renewal "
     319             :               "doesn't exist [%d]: %s\n", ret, strerror(ret));
     320           0 :         return EOK;
     321             :     }
     322             : 
     323           0 :     lifetime = dp_opt_get_int(ad_opts->basic,
     324             :                               AD_MAXIMUM_MACHINE_ACCOUNT_PASSWORD_AGE);
     325             : 
     326           0 :     if (lifetime == 0) {
     327           0 :         DEBUG(SSSDBG_CONF_SETTINGS, "Automatic machine account renewal disabled.\n");
     328           0 :         return EOK;
     329             :     }
     330             : 
     331           0 :     if (lifetime < 0) {
     332           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     333             :               "Illegal value [%d] for password lifetime.\n", lifetime);
     334           0 :         return EINVAL;
     335             :     }
     336             : 
     337           0 :     renewal_data = talloc(be_ctx, struct renewal_data);
     338           0 :     if (renewal_data == NULL) {
     339           0 :         DEBUG(SSSDBG_OP_FAILURE, "talloc failed.\n");
     340           0 :         return ENOMEM;
     341             :     }
     342             : 
     343           0 :     dummy = dp_opt_get_cstring(ad_opts->basic,
     344             :                                AD_MACHINE_ACCOUNT_PASSWORD_RENEWAL_OPTS);
     345           0 :     ret = split_on_separator(renewal_data, dummy, ':', true, false,
     346             :                              &opt_list, &opt_list_size);
     347           0 :     if (ret != EOK) {
     348           0 :         DEBUG(SSSDBG_OP_FAILURE, "split_on_separator failed.\n");
     349           0 :         goto done;
     350             :     }
     351             : 
     352           0 :     if (opt_list_size != 2) {
     353           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Wrong number of renewal options.\n");
     354           0 :         ret = EINVAL;
     355           0 :         goto done;
     356             :     }
     357             : 
     358           0 :     errno = 0;
     359           0 :     period = strtouint32(opt_list[0], &endptr, 10);
     360           0 :     if (errno != 0 || *endptr != '\0' || opt_list[0] == endptr) {
     361           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse first renewal option.\n");
     362           0 :         ret = EINVAL;
     363           0 :         goto done;
     364             :     }
     365             : 
     366           0 :     errno = 0;
     367           0 :     initial_delay = strtouint32(opt_list[1], &endptr, 10);
     368           0 :     if (errno != 0 || *endptr != '\0' || opt_list[0] == endptr) {
     369           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Unable to parse second renewal option.\n");
     370           0 :         ret = EINVAL;
     371           0 :         goto done;
     372             :     }
     373             : 
     374           0 :     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           0 :                    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           0 :     if (ret != EOK) {
     380           0 :         DEBUG(SSSDBG_OP_FAILURE, "get_adcli_extra_args failed.\n");
     381           0 :         goto done;
     382             :     }
     383             : 
     384           0 :     ret = be_ptask_create(be_ctx, be_ctx, period, initial_delay, 0, 0, 60,
     385             :                           BE_PTASK_OFFLINE_DISABLE, 0,
     386             :                           ad_machine_account_password_renewal_send,
     387             :                           ad_machine_account_password_renewal_recv,
     388             :                           renewal_data,
     389             :                           "AD machine account password renewal", NULL);
     390           0 :     if (ret != EOK) {
     391           0 :         DEBUG(SSSDBG_OP_FAILURE, "be_ptask_create failed.\n");
     392           0 :         goto done;
     393             :     }
     394             : 
     395           0 :     ret = EOK;
     396             : 
     397             : done:
     398           0 :     if (ret != EOK) {
     399           0 :         talloc_free(renewal_data);
     400             :     }
     401             : 
     402           0 :     return ret;
     403             : }

Generated by: LCOV version 1.10