Line data Source code
1 : /*
2 : SSSD
3 :
4 : proxy_auth.c
5 :
6 : Authors:
7 : Stephen Gallagher <sgallagh@redhat.com>
8 :
9 : Copyright (C) 2010 Red Hat
10 :
11 : This program is free software; you can redistribute it and/or modify
12 : it under the terms of the GNU General Public License as published by
13 : the Free Software Foundation; either version 3 of the License, or
14 : (at your option) any later version.
15 :
16 : This program is distributed in the hope that it will be useful,
17 : but WITHOUT ANY WARRANTY; without even the implied warranty of
18 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 : GNU General Public License for more details.
20 :
21 : You should have received a copy of the GNU General Public License
22 : along with this program. If not, see <http://www.gnu.org/licenses/>.
23 : */
24 :
25 : #include "providers/proxy/proxy.h"
26 :
27 : struct proxy_client_ctx {
28 : struct be_req *be_req;
29 : struct proxy_auth_ctx *auth_ctx;
30 : };
31 :
32 : static struct tevent_req *proxy_child_send(TALLOC_CTX *mem_ctx,
33 : struct proxy_auth_ctx *ctx,
34 : struct be_req *be_req);
35 : static void proxy_child_done(struct tevent_req *child_req);
36 0 : void proxy_pam_handler(struct be_req *req)
37 : {
38 0 : struct be_ctx *be_ctx = be_req_get_be_ctx(req);
39 : struct pam_data *pd;
40 : struct proxy_auth_ctx *ctx;
41 0 : struct tevent_req *child_req = NULL;
42 : struct proxy_client_ctx *client_ctx;
43 :
44 0 : pd = talloc_get_type(be_req_get_data(req), struct pam_data);
45 :
46 0 : switch (pd->cmd) {
47 : case SSS_PAM_AUTHENTICATE:
48 0 : ctx = talloc_get_type(be_ctx->bet_info[BET_AUTH].pvt_bet_data,
49 : struct proxy_auth_ctx);
50 0 : break;
51 : case SSS_PAM_CHAUTHTOK:
52 : case SSS_PAM_CHAUTHTOK_PRELIM:
53 0 : ctx = talloc_get_type(be_ctx->bet_info[BET_CHPASS].pvt_bet_data,
54 : struct proxy_auth_ctx);
55 0 : break;
56 : case SSS_PAM_ACCT_MGMT:
57 0 : ctx = talloc_get_type(be_ctx->bet_info[BET_ACCESS].pvt_bet_data,
58 : struct proxy_auth_ctx);
59 0 : break;
60 : case SSS_PAM_SETCRED:
61 : case SSS_PAM_OPEN_SESSION:
62 : case SSS_PAM_CLOSE_SESSION:
63 0 : pd->pam_status = PAM_SUCCESS;
64 0 : be_req_terminate(req, DP_ERR_OK, EOK, NULL);
65 0 : return;
66 : default:
67 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unsupported PAM task.\n");
68 0 : pd->pam_status = PAM_MODULE_UNKNOWN;
69 0 : be_req_terminate(req, DP_ERR_OK, EINVAL, "Unsupported PAM task");
70 0 : return;
71 : }
72 :
73 0 : client_ctx = talloc(req, struct proxy_client_ctx);
74 0 : if (client_ctx == NULL) {
75 0 : be_req_terminate(req, DP_ERR_FATAL, ENOMEM, NULL);
76 0 : return;
77 : }
78 0 : client_ctx->auth_ctx = ctx;
79 0 : client_ctx->be_req = req;
80 :
81 : /* Queue the request and spawn a child if there
82 : * is an available slot.
83 : */
84 0 : child_req = proxy_child_send(req, ctx, req);
85 0 : if (child_req == NULL) {
86 : /* Could not queue request
87 : * Return an error
88 : */
89 0 : be_req_terminate(req, DP_ERR_FATAL, EINVAL, "Could not queue request\n");
90 0 : return;
91 : }
92 0 : tevent_req_set_callback(child_req, proxy_child_done, client_ctx);
93 0 : return;
94 : }
95 :
96 : struct pc_init_ctx;
97 :
98 0 : static int proxy_child_destructor(TALLOC_CTX *ctx)
99 : {
100 0 : struct proxy_child_ctx *child_ctx =
101 : talloc_get_type(ctx, struct proxy_child_ctx);
102 : hash_key_t key;
103 : int hret;
104 :
105 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
106 : "Removing proxy child id [%d]\n", child_ctx->id);
107 0 : key.type = HASH_KEY_ULONG;
108 0 : key.ul = child_ctx->id;
109 0 : hret = hash_delete(child_ctx->auth_ctx->request_table, &key);
110 0 : if (!(hret == HASH_SUCCESS ||
111 : hret == HASH_ERROR_KEY_NOT_FOUND)) {
112 0 : DEBUG(SSSDBG_CRIT_FAILURE,
113 : "Hash error [%d][%s]\n", hret, hash_error_string(hret));
114 : /* Nothing we can do about this, so just continue */
115 : }
116 0 : return 0;
117 : }
118 :
119 : static struct tevent_req *proxy_child_init_send(TALLOC_CTX *mem_ctx,
120 : struct proxy_child_ctx *child_ctx,
121 : struct proxy_auth_ctx *auth_ctx);
122 : static void proxy_child_init_done(struct tevent_req *subreq);
123 0 : static struct tevent_req *proxy_child_send(TALLOC_CTX *mem_ctx,
124 : struct proxy_auth_ctx *auth_ctx,
125 : struct be_req *be_req)
126 : {
127 : struct tevent_req *req;
128 : struct tevent_req *subreq;
129 : struct proxy_child_ctx *state;
130 : int hret;
131 : hash_key_t key;
132 : hash_value_t value;
133 : uint32_t first;
134 :
135 0 : req = tevent_req_create(mem_ctx, &state, struct proxy_child_ctx);
136 0 : if (req == NULL) {
137 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not send PAM request to child\n");
138 0 : return NULL;
139 : }
140 :
141 0 : state->be_req = be_req;
142 0 : state->auth_ctx = auth_ctx;
143 0 : state->pd = talloc_get_type(be_req_get_data(be_req), struct pam_data);
144 :
145 : /* Find an available key */
146 0 : key.type = HASH_KEY_ULONG;
147 0 : key.ul = auth_ctx->next_id;
148 :
149 0 : first = auth_ctx->next_id;
150 0 : while (auth_ctx->next_id == 0 ||
151 0 : hash_has_key(auth_ctx->request_table, &key)) {
152 : /* Handle overflow, zero is a reserved value
153 : * Also handle the unlikely case where the next ID
154 : * is still awaiting being run
155 : */
156 0 : auth_ctx->next_id++;
157 0 : key.ul = auth_ctx->next_id;
158 :
159 0 : if (auth_ctx->next_id == first) {
160 : /* We've looped through all possible integers! */
161 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Serious error: queue is too long!\n");
162 0 : talloc_zfree(req);
163 0 : return NULL;
164 : }
165 : }
166 :
167 0 : state->id = auth_ctx->next_id;
168 0 : auth_ctx->next_id++;
169 :
170 0 : value.type = HASH_VALUE_PTR;
171 0 : value.ptr = req;
172 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Queueing request [%lu]\n", key.ul);
173 0 : hret = hash_enter(auth_ctx->request_table,
174 : &key, &value);
175 0 : if (hret != HASH_SUCCESS) {
176 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not add request to the queue\n");
177 0 : talloc_zfree(req);
178 0 : return NULL;
179 : }
180 :
181 0 : talloc_set_destructor((TALLOC_CTX *) state,
182 : proxy_child_destructor);
183 :
184 0 : if (auth_ctx->running < auth_ctx->max_children) {
185 : /* There's an available slot; start a child
186 : * to handle the request
187 : */
188 :
189 0 : auth_ctx->running++;
190 0 : subreq = proxy_child_init_send(auth_ctx, state, auth_ctx);
191 0 : if (!subreq) {
192 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not fork child process\n");
193 0 : auth_ctx->running--;
194 0 : talloc_zfree(req);
195 0 : return NULL;
196 : }
197 0 : tevent_req_set_callback(subreq, proxy_child_init_done, req);
198 :
199 0 : state->running = true;
200 : }
201 : else {
202 : /* If there was no available slot, it will be queued
203 : * until a slot is available
204 : */
205 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
206 : "All available child slots are full, queuing request\n");
207 : }
208 0 : return req;
209 : }
210 :
211 0 : static int pc_init_destructor (TALLOC_CTX *ctx)
212 : {
213 0 : struct pc_init_ctx *init_ctx =
214 : talloc_get_type(ctx, struct pc_init_ctx);
215 :
216 : /* If the init request has died, forcibly kill the child */
217 0 : kill(init_ctx->pid, SIGKILL);
218 0 : return 0;
219 : }
220 :
221 : static void pc_init_sig_handler(struct tevent_context *ev,
222 : struct tevent_signal *sige, int signum,
223 : int count, void *__siginfo, void *pvt);
224 : static void pc_init_timeout(struct tevent_context *ev,
225 : struct tevent_timer *te,
226 : struct timeval t, void *ptr);
227 0 : static struct tevent_req *proxy_child_init_send(TALLOC_CTX *mem_ctx,
228 : struct proxy_child_ctx *child_ctx,
229 : struct proxy_auth_ctx *auth_ctx)
230 : {
231 : struct tevent_req *req;
232 : struct pc_init_ctx *state;
233 : char **proxy_child_args;
234 : struct timeval tv;
235 : errno_t ret;
236 : pid_t pid;
237 :
238 0 : req = tevent_req_create(mem_ctx, &state, struct pc_init_ctx);
239 0 : if (req == NULL) {
240 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not create tevent_req\n");
241 0 : return NULL;
242 : }
243 :
244 0 : state->child_ctx = child_ctx;
245 :
246 0 : state->command = talloc_asprintf(req,
247 : "%s/proxy_child -d %#.4x --debug-timestamps=%d "
248 : "--debug-microseconds=%d%s --domain %s --id %d",
249 : SSSD_LIBEXEC_PATH, debug_level, debug_timestamps,
250 0 : debug_microseconds, (debug_to_file ? " --debug-to-files" : ""),
251 0 : auth_ctx->be->domain->name,
252 : child_ctx->id);
253 0 : if (state->command == NULL) {
254 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
255 0 : return NULL;
256 : }
257 :
258 0 : DEBUG(SSSDBG_TRACE_LIBS,
259 : "Starting proxy child with args [%s]\n", state->command);
260 :
261 0 : pid = fork();
262 0 : if (pid < 0) {
263 0 : ret = errno;
264 0 : DEBUG(SSSDBG_CRIT_FAILURE,
265 : "fork failed [%d][%s].\n", ret, strerror(ret));
266 0 : talloc_zfree(req);
267 0 : return NULL;
268 : }
269 :
270 0 : if (pid == 0) { /* child */
271 0 : proxy_child_args = parse_args(state->command);
272 0 : execvp(proxy_child_args[0], proxy_child_args);
273 :
274 0 : ret = errno;
275 0 : DEBUG(SSSDBG_FATAL_FAILURE,
276 : "Could not start proxy child [%s]: [%d][%s].\n",
277 : state->command, ret, strerror(ret));
278 :
279 0 : _exit(1);
280 : }
281 :
282 : else { /* parent */
283 0 : state->pid = pid;
284 : /* Make sure to kill the child process if we abort */
285 0 : talloc_set_destructor((TALLOC_CTX *)state, pc_init_destructor);
286 :
287 0 : state->sige = tevent_add_signal(auth_ctx->be->ev, req,
288 : SIGCHLD, 0,
289 : pc_init_sig_handler, req);
290 0 : if (state->sige == NULL) {
291 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_signal failed.\n");
292 0 : talloc_zfree(req);
293 0 : return NULL;
294 : }
295 :
296 : /* Save the init request to the child context.
297 : * This is technically a layering violation,
298 : * but it's the only sane way to be able to
299 : * identify which client is which when it
300 : * connects to the backend in
301 : * client_registration()
302 : */
303 0 : child_ctx->init_req = req;
304 :
305 : /* Wait six seconds for the child to connect
306 : * This is because the connection handler will add
307 : * its own five-second timeout, and we don't want to
308 : * be faster here.
309 : */
310 0 : tv = tevent_timeval_current_ofs(6, 0);
311 0 : state->timeout = tevent_add_timer(auth_ctx->be->ev, req,
312 : tv, pc_init_timeout, req);
313 :
314 : /* processing will continue once the connection is received
315 : * in proxy_client_init()
316 : */
317 0 : return req;
318 : }
319 : }
320 :
321 0 : static void pc_init_sig_handler(struct tevent_context *ev,
322 : struct tevent_signal *sige, int signum,
323 : int count, void *__siginfo, void *pvt)
324 : {
325 : int ret;
326 : int child_status;
327 : struct tevent_req *req;
328 : struct pc_init_ctx *init_ctx;
329 :
330 0 : if (count <= 0) {
331 0 : DEBUG(SSSDBG_FATAL_FAILURE,
332 : "SIGCHLD handler called with invalid child count\n");
333 0 : return;
334 : }
335 :
336 0 : req = talloc_get_type(pvt, struct tevent_req);
337 0 : init_ctx = tevent_req_data(req, struct pc_init_ctx);
338 :
339 0 : DEBUG(SSSDBG_TRACE_LIBS, "Waiting for child [%d].\n", init_ctx->pid);
340 :
341 0 : errno = 0;
342 0 : ret = waitpid(init_ctx->pid, &child_status, WNOHANG);
343 :
344 0 : if (ret == -1) {
345 0 : ret = errno;
346 0 : DEBUG(SSSDBG_CRIT_FAILURE,
347 : "waitpid failed [%d][%s].\n", ret, strerror(ret));
348 0 : } else if (ret == 0) {
349 0 : DEBUG(SSSDBG_CRIT_FAILURE,
350 : "waitpid did not find a child with changed status.\n");
351 : } else {
352 0 : if (WIFEXITED(child_status)) {
353 0 : DEBUG(SSSDBG_CONF_SETTINGS,
354 : "child [%d] exited with status [%d].\n", ret,
355 : WEXITSTATUS(child_status));
356 0 : tevent_req_error(req, EIO);
357 0 : } else if (WIFSIGNALED(child_status)) {
358 0 : DEBUG(SSSDBG_CONF_SETTINGS,
359 : "child [%d] was terminate by signal [%d].\n", ret,
360 : WTERMSIG(child_status));
361 0 : tevent_req_error(req, EIO);
362 : } else {
363 0 : if (WIFSTOPPED(child_status)) {
364 0 : DEBUG(SSSDBG_CRIT_FAILURE,
365 : "child [%d] was stopped by signal [%d].\n", ret,
366 : WSTOPSIG(child_status));
367 : }
368 0 : if (WIFCONTINUED(child_status) == true) {
369 0 : DEBUG(SSSDBG_CRIT_FAILURE,
370 : "child [%d] was resumed by delivery of SIGCONT.\n",
371 : ret);
372 : }
373 0 : DEBUG(SSSDBG_CRIT_FAILURE,
374 : "Child is still running, no new child is started.\n");
375 0 : return;
376 : }
377 : }
378 : }
379 :
380 0 : static void pc_init_timeout(struct tevent_context *ev,
381 : struct tevent_timer *te,
382 : struct timeval t, void *ptr)
383 : {
384 : struct tevent_req *req;
385 :
386 0 : DEBUG(SSSDBG_OP_FAILURE, "Client timed out before Identification!\n");
387 0 : req = talloc_get_type(ptr, struct tevent_req);
388 0 : tevent_req_error(req, ETIMEDOUT);
389 0 : }
390 :
391 0 : static errno_t proxy_child_init_recv(struct tevent_req *req,
392 : pid_t *pid,
393 : struct sbus_connection **conn)
394 : {
395 : struct pc_init_ctx *state;
396 :
397 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
398 :
399 0 : state = tevent_req_data(req, struct pc_init_ctx);
400 :
401 : /* Unset the destructor since we initialized successfully.
402 : * We don't want to kill the child now that it's properly
403 : * set up.
404 : */
405 0 : talloc_set_destructor((TALLOC_CTX *)state, NULL);
406 :
407 0 : *pid = state->pid;
408 0 : *conn = state->conn;
409 :
410 0 : return EOK;
411 : }
412 :
413 : struct proxy_child_sig_ctx {
414 : struct proxy_auth_ctx *auth_ctx;
415 : pid_t pid;
416 : };
417 : static void proxy_child_sig_handler(struct tevent_context *ev,
418 : struct tevent_signal *sige, int signum,
419 : int count, void *__siginfo, void *pvt);
420 : static struct tevent_req *proxy_pam_conv_send(TALLOC_CTX *mem_ctx,
421 : struct proxy_auth_ctx *auth_ctx,
422 : struct sbus_connection *conn,
423 : struct pam_data *pd,
424 : pid_t pid);
425 : static void proxy_pam_conv_done(struct tevent_req *subreq);
426 0 : static void proxy_child_init_done(struct tevent_req *subreq) {
427 : int ret;
428 : struct tevent_signal *sige;
429 0 : struct tevent_req *req =
430 0 : tevent_req_callback_data(subreq, struct tevent_req);
431 0 : struct proxy_child_ctx *child_ctx =
432 0 : tevent_req_data(req, struct proxy_child_ctx);
433 : struct proxy_child_sig_ctx *sig_ctx;
434 :
435 0 : ret = proxy_child_init_recv(subreq, &child_ctx->pid, &child_ctx->conn);
436 0 : talloc_zfree(subreq);
437 0 : if (ret != EOK) {
438 0 : DEBUG(SSSDBG_TRACE_FUNC, "Proxy child init failed [%d]\n", ret);
439 0 : tevent_req_error(req, ret);
440 0 : return;
441 : }
442 :
443 : /* An initialized child is available, awaiting the PAM command */
444 0 : subreq = proxy_pam_conv_send(req, child_ctx->auth_ctx,
445 : child_ctx->conn, child_ctx->pd,
446 : child_ctx->pid);
447 0 : if (!subreq) {
448 0 : DEBUG(SSSDBG_CRIT_FAILURE,"Could not start PAM conversation\n");
449 0 : tevent_req_error(req, EIO);
450 0 : return;
451 : }
452 0 : tevent_req_set_callback(subreq, proxy_pam_conv_done, req);
453 :
454 : /* Add a signal handler for the child under the auth_ctx,
455 : * that way if the child exits after completion of the
456 : * request, it will still be handled.
457 : */
458 0 : sig_ctx = talloc_zero(child_ctx->auth_ctx, struct proxy_child_sig_ctx);
459 0 : if(sig_ctx == NULL) {
460 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_signal failed.\n");
461 0 : tevent_req_error(req, ENOMEM);
462 0 : return;
463 : }
464 0 : sig_ctx->auth_ctx = child_ctx->auth_ctx;
465 0 : sig_ctx->pid = child_ctx->pid;
466 :
467 0 : sige = tevent_add_signal(child_ctx->auth_ctx->be->ev,
468 : child_ctx->auth_ctx,
469 : SIGCHLD, 0,
470 : proxy_child_sig_handler,
471 : sig_ctx);
472 0 : if (sige == NULL) {
473 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_signal failed.\n");
474 0 : tevent_req_error(req, ENOMEM);
475 0 : return;
476 : }
477 :
478 : /* Steal the signal context onto the signal event
479 : * so that when the signal is freed, the context
480 : * will go with it.
481 : */
482 0 : talloc_steal(sige, sig_ctx);
483 : }
484 :
485 : static void remove_sige(struct tevent_context *ev,
486 : struct tevent_immediate *imm,
487 : void *pvt);
488 : static void run_proxy_child_queue(struct tevent_context *ev,
489 : struct tevent_immediate *imm,
490 : void *pvt);
491 0 : static void proxy_child_sig_handler(struct tevent_context *ev,
492 : struct tevent_signal *sige, int signum,
493 : int count, void *__siginfo, void *pvt)
494 : {
495 : int ret;
496 : int child_status;
497 : struct proxy_child_sig_ctx *sig_ctx;
498 : struct tevent_immediate *imm;
499 : struct tevent_immediate *imm2;
500 :
501 0 : if (count <= 0) {
502 0 : DEBUG(SSSDBG_FATAL_FAILURE,
503 : "SIGCHLD handler called with invalid child count\n");
504 0 : return;
505 : }
506 :
507 0 : sig_ctx = talloc_get_type(pvt, struct proxy_child_sig_ctx);
508 0 : DEBUG(SSSDBG_TRACE_LIBS, "Waiting for child [%d].\n", sig_ctx->pid);
509 :
510 0 : errno = 0;
511 0 : ret = waitpid(sig_ctx->pid, &child_status, WNOHANG);
512 :
513 0 : if (ret == -1) {
514 0 : ret = errno;
515 0 : DEBUG(SSSDBG_CRIT_FAILURE,
516 : "waitpid failed [%d][%s].\n", ret, strerror(ret));
517 0 : } else if (ret == 0) {
518 0 : DEBUG(SSSDBG_CRIT_FAILURE,
519 : "waitpid did not found a child with changed status.\n");
520 : } else {
521 0 : if (WIFEXITED(child_status)) {
522 0 : DEBUG(SSSDBG_CONF_SETTINGS,
523 : "child [%d] exited with status [%d].\n", ret,
524 : WEXITSTATUS(child_status));
525 0 : } else if (WIFSIGNALED(child_status) == true) {
526 0 : DEBUG(SSSDBG_CONF_SETTINGS,
527 : "child [%d] was terminated by signal [%d].\n", ret,
528 : WTERMSIG(child_status));
529 : } else {
530 0 : if (WIFSTOPPED(child_status)) {
531 0 : DEBUG(SSSDBG_CRIT_FAILURE,
532 : "child [%d] was stopped by signal [%d].\n", ret,
533 : WSTOPSIG(child_status));
534 : }
535 0 : if (WIFCONTINUED(child_status) == true) {
536 0 : DEBUG(SSSDBG_CRIT_FAILURE,
537 : "child [%d] was resumed by delivery of SIGCONT.\n",
538 : ret);
539 : }
540 0 : DEBUG(SSSDBG_CRIT_FAILURE,
541 : "Child is still running, no new child is started.\n");
542 0 : return;
543 : }
544 :
545 0 : imm = tevent_create_immediate(ev);
546 0 : if (imm == NULL) {
547 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_create_immediate failed.\n");
548 0 : return;
549 : }
550 :
551 0 : tevent_schedule_immediate(imm, ev, run_proxy_child_queue,
552 : sig_ctx->auth_ctx);
553 :
554 : /* schedule another immediate timer to delete the sigchld handler */
555 0 : imm2 = tevent_create_immediate(ev);
556 0 : if (imm2 == NULL) {
557 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_create_immediate failed.\n");
558 0 : return;
559 : }
560 :
561 0 : tevent_schedule_immediate(imm2, ev, remove_sige, sige);
562 : }
563 :
564 0 : return;
565 : }
566 :
567 0 : static void remove_sige(struct tevent_context *ev,
568 : struct tevent_immediate *imm,
569 : void *pvt)
570 : {
571 0 : talloc_free(pvt);
572 0 : }
573 :
574 : struct proxy_conv_ctx {
575 : struct proxy_auth_ctx *auth_ctx;
576 : struct sbus_connection *conn;
577 : struct pam_data *pd;
578 : pid_t pid;
579 : };
580 : static void proxy_pam_conv_reply(DBusPendingCall *pending, void *ptr);
581 0 : static struct tevent_req *proxy_pam_conv_send(TALLOC_CTX *mem_ctx,
582 : struct proxy_auth_ctx *auth_ctx,
583 : struct sbus_connection *conn,
584 : struct pam_data *pd,
585 : pid_t pid)
586 : {
587 : errno_t ret;
588 : bool dp_ret;
589 : DBusMessage *msg;
590 : struct tevent_req *req;
591 : struct proxy_conv_ctx *state;
592 :
593 0 : req = tevent_req_create(mem_ctx, &state, struct proxy_conv_ctx);
594 0 : if (req == NULL) {
595 0 : return NULL;
596 : }
597 :
598 0 : state->auth_ctx = auth_ctx;
599 0 : state->conn = conn;
600 0 : state->pd = pd;
601 0 : state->pid = pid;
602 :
603 0 : msg = dbus_message_new_method_call(NULL,
604 : DP_PATH,
605 : DATA_PROVIDER_IFACE,
606 : DATA_PROVIDER_IFACE_PAMHANDLER);
607 0 : if (msg == NULL) {
608 0 : DEBUG(SSSDBG_CRIT_FAILURE, "dbus_message_new_method_call failed.\n");
609 0 : talloc_zfree(req);
610 0 : return NULL;
611 : }
612 :
613 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Sending request with the following data:\n");
614 0 : DEBUG_PAM_DATA(SSSDBG_CONF_SETTINGS, pd);
615 :
616 0 : dp_ret = dp_pack_pam_request(msg, pd);
617 0 : if (!dp_ret) {
618 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
619 0 : dbus_message_unref(msg);
620 0 : talloc_zfree(req);
621 0 : return NULL;
622 : }
623 :
624 0 : ret = sbus_conn_send(state->conn, msg, state->auth_ctx->timeout_ms,
625 : proxy_pam_conv_reply, req, NULL);
626 0 : if (ret != EOK) {
627 0 : dbus_message_unref(msg);
628 0 : talloc_zfree(req);
629 0 : return NULL;
630 : }
631 :
632 0 : dbus_message_unref(msg);
633 0 : return req;
634 : }
635 :
636 0 : static void proxy_pam_conv_reply(DBusPendingCall *pending, void *ptr)
637 : {
638 : struct tevent_req *req;
639 : struct proxy_conv_ctx *state;
640 : DBusError dbus_error;
641 : DBusMessage *reply;
642 : int type;
643 : int ret;
644 :
645 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Handling pam conversation reply\n");
646 :
647 0 : req = talloc_get_type(ptr, struct tevent_req);
648 0 : state = tevent_req_data(req, struct proxy_conv_ctx);
649 :
650 0 : dbus_error_init(&dbus_error);
651 :
652 0 : reply = dbus_pending_call_steal_reply(pending);
653 0 : dbus_pending_call_unref(pending);
654 0 : if (reply == NULL) {
655 0 : DEBUG(SSSDBG_FATAL_FAILURE,
656 : "Severe error. A reply callback was called but no reply was"
657 : "received and no timeout occurred\n");
658 0 : state->pd->pam_status = PAM_SYSTEM_ERR;
659 0 : tevent_req_error(req, EIO);
660 : }
661 :
662 0 : type = dbus_message_get_type(reply);
663 0 : switch (type) {
664 : case DBUS_MESSAGE_TYPE_METHOD_RETURN:
665 0 : ret = dp_unpack_pam_response(reply, state->pd, &dbus_error);
666 0 : if (!ret) {
667 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Failed to parse reply.\n");
668 0 : state->pd->pam_status = PAM_SYSTEM_ERR;
669 0 : dbus_message_unref(reply);
670 0 : tevent_req_error(req, EIO);
671 0 : return;
672 : }
673 0 : DEBUG(SSSDBG_CONF_SETTINGS, "received: [%d][%s]\n",
674 : state->pd->pam_status,
675 : state->pd->domain);
676 0 : break;
677 : case DBUS_MESSAGE_TYPE_ERROR:
678 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Reply error [%s].\n",
679 : dbus_message_get_error_name(reply));
680 0 : state->pd->pam_status = PAM_SYSTEM_ERR;
681 0 : break;
682 : default:
683 0 : DEBUG(SSSDBG_FATAL_FAILURE, "Default... what now?.\n");
684 0 : state->pd->pam_status = PAM_SYSTEM_ERR;
685 : }
686 0 : dbus_message_unref(reply);
687 :
688 : /* Kill the child */
689 0 : kill(state->pid, SIGKILL);
690 :
691 : /* Conversation is finished */
692 0 : tevent_req_done(req);
693 : }
694 :
695 0 : static errno_t proxy_pam_conv_recv(struct tevent_req *req)
696 : {
697 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
698 :
699 0 : return EOK;
700 : }
701 :
702 0 : static void proxy_pam_conv_done(struct tevent_req *subreq)
703 : {
704 : struct tevent_req *req;
705 : int ret;
706 :
707 0 : req = tevent_req_callback_data(subreq, struct tevent_req);
708 :
709 0 : ret = proxy_pam_conv_recv(subreq);
710 0 : talloc_zfree(subreq);
711 0 : if (ret != EOK) {
712 0 : DEBUG(SSSDBG_TRACE_FUNC, "Proxy PAM conversation failed [%d]\n", ret);
713 0 : tevent_req_error(req, ret);
714 0 : return;
715 : }
716 :
717 0 : tevent_req_done(req);
718 : }
719 :
720 0 : static int proxy_child_recv(struct tevent_req *req,
721 : TALLOC_CTX *mem_ctx,
722 : struct pam_data **pd)
723 : {
724 : struct proxy_child_ctx *ctx;
725 :
726 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
727 :
728 0 : ctx = tevent_req_data(req, struct proxy_child_ctx);
729 0 : *pd = talloc_steal(mem_ctx, ctx->pd);
730 :
731 0 : return EOK;
732 : }
733 :
734 0 : static void proxy_child_done(struct tevent_req *req)
735 : {
736 0 : struct proxy_client_ctx *client_ctx =
737 0 : tevent_req_callback_data(req, struct proxy_client_ctx);
738 0 : struct be_ctx *be_ctx = be_req_get_be_ctx(client_ctx->be_req);
739 0 : struct pam_data *pd = NULL;
740 : const char *password;
741 : int ret;
742 : struct tevent_immediate *imm;
743 :
744 0 : ret = proxy_child_recv(req, client_ctx, &pd);
745 0 : talloc_zfree(req);
746 :
747 : /* Start the next auth in the queue, if any */
748 0 : client_ctx->auth_ctx->running--;
749 0 : imm = tevent_create_immediate(be_ctx->ev);
750 0 : if (imm == NULL) {
751 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_create_immediate failed.\n");
752 : /* We'll still finish the current request, but we're
753 : * likely to have problems if there are queued events
754 : * if we've gotten into this state.
755 : * Hopefully this is impossible, since freeing req
756 : * above should guarantee that we have enough memory
757 : * to create this immediate event.
758 : */
759 : } else {
760 0 : tevent_schedule_immediate(imm, be_ctx->ev,
761 : run_proxy_child_queue,
762 : client_ctx->auth_ctx);
763 : }
764 :
765 0 : if (ret != EOK) {
766 : /* Pam child failed */
767 0 : be_req_terminate(client_ctx->be_req, DP_ERR_FATAL, ret,
768 : "PAM child failed");
769 0 : return;
770 : }
771 :
772 : /* Check if we need to save the cached credentials */
773 0 : if ((pd->cmd == SSS_PAM_AUTHENTICATE || pd->cmd == SSS_PAM_CHAUTHTOK) &&
774 0 : (pd->pam_status == PAM_SUCCESS) && be_ctx->domain->cache_credentials) {
775 :
776 0 : ret = sss_authtok_get_password(pd->authtok, &password, NULL);
777 0 : if (ret) {
778 : /* password caching failures are not fatal errors */
779 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to cache password\n");
780 0 : goto done;
781 : }
782 :
783 0 : ret = sysdb_cache_password(be_ctx->domain, pd->user, password);
784 :
785 : /* password caching failures are not fatal errors */
786 : /* so we just log it any return */
787 0 : if (ret != EOK) {
788 0 : DEBUG(SSSDBG_OP_FAILURE, "Failed to cache password (%d)[%s]!?\n",
789 : ret, strerror(ret));
790 : }
791 : }
792 :
793 : done:
794 0 : be_req_terminate(client_ctx->be_req, DP_ERR_OK, EOK, NULL);
795 : }
796 :
797 0 : static void run_proxy_child_queue(struct tevent_context *ev,
798 : struct tevent_immediate *imm,
799 : void *pvt)
800 : {
801 : struct proxy_auth_ctx *auth_ctx;
802 : struct hash_iter_context_t *iter;
803 : struct hash_entry_t *entry;
804 : struct tevent_req *req;
805 : struct tevent_req *subreq;
806 : struct proxy_child_ctx *state;
807 :
808 0 : auth_ctx = talloc_get_type(pvt, struct proxy_auth_ctx);
809 :
810 : /* Launch next queued request */
811 0 : iter = new_hash_iter_context(auth_ctx->request_table);
812 0 : while ((entry = iter->next(iter)) != NULL) {
813 0 : req = talloc_get_type(entry->value.ptr, struct tevent_req);
814 0 : state = tevent_req_data(req, struct proxy_child_ctx);
815 0 : if (!state->running) {
816 0 : break;
817 : }
818 : }
819 0 : free(iter);
820 :
821 0 : if (!entry) {
822 : /* Nothing pending on the queue */
823 0 : return;
824 : }
825 :
826 0 : if (auth_ctx->running < auth_ctx->max_children) {
827 : /* There's an available slot; start a child
828 : * to handle the request
829 : */
830 0 : auth_ctx->running++;
831 0 : subreq = proxy_child_init_send(auth_ctx, state, auth_ctx);
832 0 : if (!subreq) {
833 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not fork child process\n");
834 0 : auth_ctx->running--;
835 0 : talloc_zfree(req);
836 0 : return;
837 : }
838 0 : tevent_req_set_callback(subreq, proxy_child_init_done, req);
839 :
840 0 : state->running = true;
841 : }
842 : }
|