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 : }
|