Line data Source code
1 : /*
2 : Authors:
3 : Simo Sorce <ssorce@redhat.com>
4 : Stephen Gallagher <sgallagh@redhat.com>
5 :
6 : Copyright (C) 2009 Red Hat
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 :
23 : #include <sys/time.h>
24 : #include <time.h>
25 : #include "util/util.h"
26 : #include "responder/common/responder_packet.h"
27 : #include "responder/common/responder.h"
28 : #include "providers/data_provider.h"
29 : #include "sbus/sbus_client.h"
30 :
31 : struct sss_dp_req;
32 :
33 : struct sss_dp_callback {
34 : struct sss_dp_callback *prev;
35 : struct sss_dp_callback *next;
36 :
37 : struct tevent_req *req;
38 : struct sss_dp_req *sdp_req;
39 : };
40 :
41 : struct sss_dp_req {
42 : struct resp_ctx *rctx;
43 : struct tevent_context *ev;
44 : DBusPendingCall *pending_reply;
45 :
46 : hash_key_t *key;
47 :
48 : struct sss_dp_callback *cb_list;
49 :
50 : dbus_uint16_t dp_err;
51 : dbus_uint32_t dp_ret;
52 : char *err_msg;
53 : };
54 :
55 0 : static int sss_dp_callback_destructor(void *ptr)
56 : {
57 0 : struct sss_dp_callback *cb =
58 : talloc_get_type(ptr, struct sss_dp_callback);
59 :
60 0 : DLIST_REMOVE(cb->sdp_req->cb_list, cb);
61 :
62 0 : return EOK;
63 : }
64 :
65 0 : static int sss_dp_req_destructor(void *ptr)
66 : {
67 : struct sss_dp_callback *cb;
68 0 : struct sss_dp_req *sdp_req = talloc_get_type(ptr, struct sss_dp_req);
69 : struct sss_dp_req_state *state;
70 : int hret;
71 :
72 : /* Cancel Dbus pending reply if still pending */
73 0 : if (sdp_req->pending_reply) {
74 0 : dbus_pending_call_cancel(sdp_req->pending_reply);
75 0 : sdp_req->pending_reply = NULL;
76 : }
77 :
78 : /* Do not call callbacks if the responder is shutting down, because
79 : * the top level responder context (pam_ctx, sudo_ctx, ...) may be
80 : * already semi-freed and we may end up accessing freed memory.
81 : */
82 0 : if (sdp_req->rctx->shutting_down) {
83 0 : return 0;
84 : }
85 :
86 : /* If there are callbacks that haven't been invoked, return
87 : * an error now.
88 : */
89 0 : while ((cb = sdp_req->cb_list) != NULL) {
90 0 : state = tevent_req_data(cb->req, struct sss_dp_req_state);
91 0 : state->dp_err = DP_ERR_FATAL;
92 0 : state->dp_ret = EIO;
93 :
94 : /* tevent_req_done/error will free cb */
95 0 : tevent_req_error(cb->req, EIO);
96 :
97 : /* Freeing the cb removes it from the cb_list.
98 : * Therefore, the cb_list should now be pointing
99 : * at a new callback. If it's not, it means the
100 : * callback handler didn't free cb and may leak
101 : * memory. Be paranoid and protect against this
102 : * situation.
103 : */
104 0 : if (cb == sdp_req->cb_list) {
105 0 : DEBUG(SSSDBG_FATAL_FAILURE,
106 : "BUG: a callback did not free its request. "
107 : "May leak memory\n");
108 : /* Skip to the next since a memory leak is non-fatal */
109 0 : sdp_req->cb_list = sdp_req->cb_list->next;
110 : }
111 : }
112 :
113 : /* Destroy the hash entry */
114 0 : DEBUG(SSSDBG_TRACE_FUNC, "Deleting request: [%s]\n", sdp_req->key->str);
115 0 : hret = hash_delete(sdp_req->rctx->dp_request_table, sdp_req->key);
116 0 : if (hret != HASH_SUCCESS) {
117 : /* This should never happen */
118 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
119 : "BUG: Could not clear [%d:%lu:%s] from request queue: [%s]\n",
120 : sdp_req->key->type, sdp_req->key->ul, sdp_req->key->str,
121 : hash_error_string(hret));
122 0 : return -1;
123 : }
124 :
125 0 : return 0;
126 : }
127 :
128 0 : static void sss_dp_req_timeout(struct tevent_context *ev,
129 : struct tevent_timer *te,
130 : struct timeval t, void *ptr)
131 : {
132 : /* ptr is a pointer to sidereq */
133 : /* Just free it to kill all waiting requests when the timeout fires */
134 0 : talloc_zfree(ptr);
135 0 : }
136 :
137 0 : void handle_requests_after_reconnect(struct resp_ctx *rctx)
138 : {
139 : int ret;
140 : hash_value_t *values;
141 : unsigned long count, i;
142 : struct sss_dp_req *sdp_req;
143 :
144 0 : if (!rctx->dp_request_table) {
145 0 : DEBUG(SSSDBG_TRACE_LIBS, "No requests to handle after reconnect\n");
146 0 : return;
147 : }
148 :
149 0 : ret = hash_values(rctx->dp_request_table, &count, &values);
150 0 : if (ret != HASH_SUCCESS) {
151 0 : DEBUG(SSSDBG_CRIT_FAILURE, "hash_values failed, "
152 : "not all request might be handled after reconnect.\n");
153 0 : return;
154 : }
155 :
156 0 : DEBUG(SSSDBG_TRACE_LIBS,
157 : "Will handle %lu requests after reconnect\n", count);
158 0 : for (i=0; i<count; i++) {
159 0 : sdp_req = talloc_get_type(values[i].ptr, struct sss_dp_req);
160 0 : talloc_free(sdp_req);
161 : }
162 : }
163 :
164 0 : static int sss_dp_get_reply(DBusPendingCall *pending,
165 : dbus_uint16_t *dp_err,
166 : dbus_uint32_t *dp_ret,
167 : char **err_msg)
168 : {
169 : DBusMessage *reply;
170 : DBusError dbus_error;
171 : dbus_bool_t ret;
172 : int type;
173 0 : int err = EOK;
174 :
175 0 : dbus_error_init(&dbus_error);
176 :
177 0 : reply = dbus_pending_call_steal_reply(pending);
178 0 : if (!reply) {
179 : /* reply should never be null. This function shouldn't be called
180 : * until reply is valid or timeout has occurred. If reply is NULL
181 : * here, something is seriously wrong and we should bail out.
182 : */
183 0 : DEBUG(SSSDBG_CRIT_FAILURE,
184 : "Severe error. A reply callback was called but no reply "
185 : "was received and no timeout occurred\n");
186 :
187 : /* FIXME: Destroy this connection ? */
188 0 : err = EIO;
189 0 : goto done;
190 : }
191 :
192 0 : type = dbus_message_get_type(reply);
193 0 : switch (type) {
194 : case DBUS_MESSAGE_TYPE_METHOD_RETURN:
195 0 : ret = dbus_message_get_args(reply, &dbus_error,
196 : DBUS_TYPE_UINT16, dp_err,
197 : DBUS_TYPE_UINT32, dp_ret,
198 : DBUS_TYPE_STRING, err_msg,
199 : DBUS_TYPE_INVALID);
200 0 : if (!ret) {
201 0 : DEBUG(SSSDBG_CRIT_FAILURE,"Failed to parse message\n");
202 : /* FIXME: Destroy this connection ? */
203 0 : if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
204 0 : err = EIO;
205 0 : goto done;
206 : }
207 0 : DEBUG(SSSDBG_TRACE_LIBS,
208 : "Got reply from Data Provider - "
209 : "DP error code: %u errno: %u error message: %s\n",
210 : (unsigned int)*dp_err, (unsigned int)*dp_ret,
211 : *err_msg ? *err_msg : "none");
212 0 : break;
213 :
214 : case DBUS_MESSAGE_TYPE_ERROR:
215 0 : if (strcmp(dbus_message_get_error_name(reply),
216 : DBUS_ERROR_NO_REPLY) == 0) {
217 0 : err = ETIME;
218 0 : goto done;
219 : }
220 0 : DEBUG(SSSDBG_FATAL_FAILURE,"The Data Provider returned an error [%s]\n",
221 : dbus_message_get_error_name(reply));
222 : /* Falling through to default intentionally*/
223 : default:
224 : /*
225 : * Timeout or other error occurred or something
226 : * unexpected happened.
227 : * It doesn't matter which, because either way we
228 : * know that this connection isn't trustworthy.
229 : * We'll destroy it now.
230 : */
231 :
232 : /* FIXME: Destroy this connection ? */
233 0 : err = EIO;
234 : }
235 :
236 : done:
237 0 : dbus_pending_call_unref(pending);
238 0 : dbus_message_unref(reply);
239 :
240 0 : return err;
241 : }
242 :
243 : static struct tevent_req *
244 : sss_dp_internal_get_send(struct resp_ctx *rctx,
245 : hash_key_t *key,
246 : struct sss_domain_info *dom,
247 : DBusMessage *msg);
248 :
249 : static void
250 : sss_dp_req_done(struct tevent_req *sidereq);
251 :
252 : errno_t
253 0 : sss_dp_issue_request(TALLOC_CTX *mem_ctx, struct resp_ctx *rctx,
254 : const char *strkey, struct sss_domain_info *dom,
255 : dbus_msg_constructor msg_create, void *pvt,
256 : struct tevent_req *nreq)
257 : {
258 : int hret;
259 : hash_value_t value;
260 : hash_key_t *key;
261 : struct tevent_req *sidereq;
262 : struct sss_dp_req *sdp_req;
263 : struct sss_dp_callback *cb;
264 : struct tevent_timer *te;
265 : struct timeval tv;
266 : DBusMessage *msg;
267 0 : TALLOC_CTX *tmp_ctx = NULL;
268 : errno_t ret;
269 :
270 0 : tmp_ctx = talloc_new(NULL);
271 0 : if (!tmp_ctx) {
272 0 : return ENOMEM;
273 : }
274 :
275 0 : key = talloc(tmp_ctx, hash_key_t);
276 0 : if (!key) {
277 0 : ret = ENOMEM;
278 0 : goto fail;
279 : }
280 :
281 0 : key->type = HASH_KEY_STRING;
282 0 : key->str = talloc_asprintf(key, "%p:%s", msg_create, strkey);
283 0 : if (!key->str) {
284 0 : ret = ENOMEM;
285 0 : goto fail;
286 : }
287 :
288 0 : DEBUG(SSSDBG_TRACE_FUNC, "Issuing request for [%s]\n", key->str);
289 :
290 : /* Check the hash for existing references to this request */
291 0 : hret = hash_lookup(rctx->dp_request_table, key, &value);
292 0 : switch (hret) {
293 : case HASH_SUCCESS:
294 : /* Request already in progress */
295 0 : DEBUG(SSSDBG_TRACE_FUNC,
296 : "Identical request in progress: [%s]\n", key->str);
297 0 : break;
298 :
299 : case HASH_ERROR_KEY_NOT_FOUND:
300 : /* No such request in progress
301 : * Create a new request
302 : */
303 0 : msg = msg_create(pvt);
304 0 : if (!msg) {
305 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot create D-Bus message\n");
306 0 : ret = EIO;
307 0 : goto fail;
308 : }
309 :
310 0 : value.type = HASH_VALUE_PTR;
311 0 : sidereq = sss_dp_internal_get_send(rctx, key, dom, msg);
312 0 : dbus_message_unref(msg);
313 0 : if (!sidereq) {
314 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot send D-Bus message\n");
315 0 : ret = EIO;
316 0 : goto fail;
317 : }
318 0 : tevent_req_set_callback(sidereq, sss_dp_req_done, NULL);
319 :
320 : /* add timeout handling so we do not hang forever should something
321 : * go worng in the provider. Use 2 sec less than the idle timeout to
322 : * give it a chance to reply to the client before closing the
323 : * connection. */
324 0 : tv = tevent_timeval_current_ofs(rctx->client_idle_timeout - 2, 0);
325 0 : te = tevent_add_timer(rctx->ev, sidereq, tv,
326 : sss_dp_req_timeout, sidereq);
327 0 : if (!te) {
328 : /* Nothing much we can do */
329 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
330 0 : ret = ENOMEM;
331 0 : goto fail;
332 : }
333 :
334 : /* We should now be able to find the sdp_req in the hash table */
335 0 : hret = hash_lookup(rctx->dp_request_table, key, &value);
336 0 : if (hret != HASH_SUCCESS) {
337 : /* Something must have gone wrong with creating the request */
338 0 : DEBUG(SSSDBG_CRIT_FAILURE, "The request has disappeared?\n");
339 0 : ret = EIO;
340 0 : goto fail;
341 : }
342 0 : break;
343 :
344 : default:
345 0 : DEBUG(SSSDBG_CRIT_FAILURE,
346 : "Could not query request list (%s)\n",
347 : hash_error_string(hret));
348 0 : ret = EIO;
349 0 : goto fail;
350 : }
351 :
352 : /* Register this request for results */
353 0 : sdp_req = talloc_get_type(value.ptr, struct sss_dp_req);
354 0 : if (!sdp_req) {
355 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not retrieve DP request context\n");
356 0 : ret = EIO;
357 0 : goto fail;
358 : }
359 :
360 0 : cb = talloc_zero(mem_ctx, struct sss_dp_callback);
361 0 : if (!cb) {
362 0 : ret = ENOMEM;
363 0 : goto fail;
364 : }
365 :
366 0 : cb->req = nreq;
367 0 : cb->sdp_req = sdp_req;
368 :
369 : /* Add it to the list of requests to call */
370 0 : DLIST_ADD_END(sdp_req->cb_list, cb,
371 : struct sss_dp_callback *);
372 0 : talloc_set_destructor((TALLOC_CTX *)cb,
373 : sss_dp_callback_destructor);
374 :
375 0 : ret = EOK;
376 : fail:
377 0 : talloc_free(tmp_ctx);
378 0 : return ret;
379 : }
380 :
381 : static void
382 0 : sss_dp_req_done(struct tevent_req *sidereq)
383 : {
384 : /* Nothing to do here. The callbacks have already been invoked */
385 0 : talloc_zfree(sidereq);
386 0 : }
387 :
388 : errno_t
389 0 : sss_dp_req_recv(TALLOC_CTX *mem_ctx,
390 : struct tevent_req *sidereq,
391 : dbus_uint16_t *dp_err,
392 : dbus_uint32_t *dp_ret,
393 : char **err_msg)
394 : {
395 0 : struct sss_dp_req_state *state =
396 0 : tevent_req_data(sidereq, struct sss_dp_req_state);
397 :
398 : enum tevent_req_state TRROEstate;
399 : uint64_t TRROEerr;
400 :
401 0 : *dp_err = state->dp_err;
402 0 : *dp_ret = state->dp_ret;
403 0 : *err_msg = talloc_steal(mem_ctx, state->err_msg);
404 :
405 0 : if (tevent_req_is_error(sidereq, &TRROEstate, &TRROEerr)) {
406 0 : if (TRROEstate == TEVENT_REQ_USER_ERROR) {
407 0 : *dp_err = DP_ERR_FATAL;
408 0 : *dp_ret = TRROEerr;
409 : } else {
410 0 : return EIO;
411 : }
412 : }
413 :
414 0 : return EOK;
415 : }
416 :
417 : /* Send a request to the data provider
418 : * Once this function is called, the communication
419 : * with the data provider will always run to
420 : * completion. Freeing the returned tevent_req will
421 : * cancel the notification of completion, but not
422 : * the data provider action.
423 : */
424 : static DBusMessage *sss_dp_get_account_msg(void *pvt);
425 :
426 : struct sss_dp_account_info {
427 : struct sss_domain_info *dom;
428 :
429 : bool fast_reply;
430 : enum sss_dp_acct_type type;
431 : const char *opt_name;
432 : const char *extra;
433 : uint32_t opt_id;
434 : };
435 :
436 : struct tevent_req *
437 0 : sss_dp_get_account_send(TALLOC_CTX *mem_ctx,
438 : struct resp_ctx *rctx,
439 : struct sss_domain_info *dom,
440 : bool fast_reply,
441 : enum sss_dp_acct_type type,
442 : const char *opt_name,
443 : uint32_t opt_id,
444 : const char *extra)
445 : {
446 : errno_t ret;
447 : struct tevent_req *req;
448 : struct sss_dp_account_info *info;
449 : struct sss_dp_req_state *state;
450 : char *key;
451 :
452 0 : req = tevent_req_create(mem_ctx, &state, struct sss_dp_req_state);
453 0 : if (!req) {
454 0 : return NULL;
455 : }
456 :
457 : /* either, or, not both */
458 0 : if (opt_name && opt_id) {
459 0 : ret = EINVAL;
460 0 : goto error;
461 : }
462 :
463 0 : if (!dom) {
464 0 : ret = EINVAL;
465 0 : goto error;
466 : }
467 :
468 0 : info = talloc_zero(state, struct sss_dp_account_info);
469 0 : info->fast_reply = fast_reply;
470 0 : info->type = type;
471 0 : info->opt_name = opt_name;
472 0 : info->opt_id = opt_id;
473 0 : info->extra = extra;
474 0 : info->dom = dom;
475 :
476 0 : if (opt_name) {
477 0 : if (extra) {
478 0 : key = talloc_asprintf(state, "%d:%s:%s@%s",
479 : type, opt_name, extra, dom->name);
480 : } else {
481 0 : key = talloc_asprintf(state, "%d:%s@%s",
482 : type, opt_name, dom->name);
483 : }
484 0 : } else if (opt_id) {
485 0 : if (extra) {
486 0 : key = talloc_asprintf(state, "%d:%d:%s@%s",
487 : type, opt_id, extra, dom->name);
488 : } else {
489 0 : key = talloc_asprintf(state, "%d:%d@%s", type, opt_id, dom->name);
490 : }
491 : } else {
492 0 : key = talloc_asprintf(state, "%d:*@%s", type, dom->name);
493 : }
494 0 : if (!key) {
495 0 : ret = ENOMEM;
496 0 : goto error;
497 : }
498 :
499 0 : ret = sss_dp_issue_request(state, rctx, key, dom, sss_dp_get_account_msg,
500 : info, req);
501 0 : talloc_free(key);
502 0 : if (ret != EOK) {
503 0 : DEBUG(SSSDBG_OP_FAILURE,
504 : "Could not issue DP request [%d]: %s\n",
505 : ret, strerror(ret));
506 0 : goto error;
507 : }
508 :
509 0 : return req;
510 :
511 : error:
512 0 : tevent_req_error(req, ret);
513 0 : tevent_req_post(req, rctx->ev);
514 0 : return req;
515 : }
516 :
517 : static DBusMessage *
518 0 : sss_dp_get_account_msg(void *pvt)
519 : {
520 : DBusMessage *msg;
521 : dbus_bool_t dbret;
522 : struct sss_dp_account_info *info;
523 : uint32_t be_type;
524 0 : uint32_t attrs = BE_ATTR_CORE;
525 : char *filter;
526 :
527 0 : info = talloc_get_type(pvt, struct sss_dp_account_info);
528 :
529 0 : switch (info->type) {
530 : case SSS_DP_USER:
531 : case SSS_DP_WILDCARD_USER:
532 0 : be_type = BE_REQ_USER;
533 0 : break;
534 : case SSS_DP_GROUP:
535 : case SSS_DP_WILDCARD_GROUP:
536 0 : be_type = BE_REQ_GROUP;
537 0 : break;
538 : case SSS_DP_INITGROUPS:
539 0 : be_type = BE_REQ_INITGROUPS;
540 0 : break;
541 : case SSS_DP_NETGR:
542 0 : be_type = BE_REQ_NETGROUP;
543 0 : break;
544 : case SSS_DP_SERVICES:
545 0 : be_type = BE_REQ_SERVICES;
546 0 : break;
547 : case SSS_DP_SECID:
548 0 : be_type = BE_REQ_BY_SECID;
549 0 : break;
550 : case SSS_DP_USER_AND_GROUP:
551 0 : be_type = BE_REQ_USER_AND_GROUP;
552 0 : break;
553 : case SSS_DP_CERT:
554 0 : be_type = BE_REQ_BY_CERT;
555 0 : break;
556 : }
557 :
558 0 : if (info->fast_reply) {
559 0 : be_type |= BE_REQ_FAST;
560 : }
561 :
562 0 : if (info->opt_name) {
563 0 : if (info->type == SSS_DP_SECID) {
564 0 : if (info->extra) {
565 0 : filter = talloc_asprintf(info, "%s=%s:%s", DP_SEC_ID,
566 : info->opt_name, info->extra);
567 : } else {
568 0 : filter = talloc_asprintf(info, "%s=%s", DP_SEC_ID,
569 : info->opt_name);
570 : }
571 0 : } else if (info->type == SSS_DP_CERT) {
572 0 : if (info->extra) {
573 0 : filter = talloc_asprintf(info, "%s=%s:%s", DP_CERT,
574 : info->opt_name, info->extra);
575 : } else {
576 0 : filter = talloc_asprintf(info, "%s=%s", DP_CERT,
577 : info->opt_name);
578 : }
579 0 : } else if (info->type == SSS_DP_WILDCARD_USER ||
580 0 : info->type == SSS_DP_WILDCARD_GROUP) {
581 0 : if (info->extra) {
582 0 : filter = talloc_asprintf(info, "%s=%s:%s", DP_WILDCARD,
583 : info->opt_name, info->extra);
584 : } else {
585 0 : filter = talloc_asprintf(info, "%s=%s", DP_WILDCARD,
586 : info->opt_name);
587 : }
588 : } else {
589 0 : if (info->extra) {
590 0 : filter = talloc_asprintf(info, "name=%s:%s",
591 : info->opt_name, info->extra);
592 : } else {
593 0 : filter = talloc_asprintf(info, "name=%s", info->opt_name);
594 : }
595 : }
596 0 : } else if (info->opt_id) {
597 0 : if (info->extra) {
598 0 : filter = talloc_asprintf(info, "idnumber=%u:%s",
599 : info->opt_id, info->extra);
600 : } else {
601 0 : filter = talloc_asprintf(info, "idnumber=%u", info->opt_id);
602 : }
603 : } else {
604 0 : filter = talloc_strdup(info, ENUM_INDICATOR);
605 : }
606 0 : if (!filter) {
607 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
608 0 : return NULL;
609 : }
610 :
611 0 : msg = dbus_message_new_method_call(NULL,
612 : DP_PATH,
613 : DATA_PROVIDER_IFACE,
614 : DATA_PROVIDER_IFACE_GETACCOUNTINFO);
615 0 : if (msg == NULL) {
616 0 : talloc_free(filter);
617 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory?!\n");
618 0 : return NULL;
619 : }
620 :
621 : /* create the message */
622 0 : DEBUG(SSSDBG_TRACE_FUNC,
623 : "Creating request for [%s][%#x][%s][%d][%s]\n",
624 : info->dom->name, be_type, be_req2str(be_type), attrs, filter);
625 :
626 0 : dbret = dbus_message_append_args(msg,
627 : DBUS_TYPE_UINT32, &be_type,
628 : DBUS_TYPE_UINT32, &attrs,
629 : DBUS_TYPE_STRING, &filter,
630 0 : DBUS_TYPE_STRING, &info->dom->name,
631 : DBUS_TYPE_INVALID);
632 0 : talloc_free(filter);
633 0 : if (!dbret) {
634 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to build message\n");
635 0 : dbus_message_unref(msg);
636 0 : return NULL;
637 : }
638 :
639 0 : return msg;
640 : }
641 :
642 : errno_t
643 0 : sss_dp_get_account_recv(TALLOC_CTX *mem_ctx,
644 : struct tevent_req *req,
645 : dbus_uint16_t *dp_err,
646 : dbus_uint32_t *dp_ret,
647 : char **err_msg)
648 : {
649 0 : return sss_dp_req_recv(mem_ctx, req, dp_err, dp_ret, err_msg);
650 : }
651 :
652 : struct dp_internal_get_state {
653 : struct resp_ctx *rctx;
654 : struct sss_domain_info *dom;
655 :
656 : struct sss_dp_req *sdp_req;
657 : DBusPendingCall *pending_reply;
658 : };
659 :
660 : static void sss_dp_internal_get_done(DBusPendingCall *pending, void *ptr);
661 :
662 : static struct tevent_req *
663 0 : sss_dp_internal_get_send(struct resp_ctx *rctx,
664 : hash_key_t *key,
665 : struct sss_domain_info *dom,
666 : DBusMessage *msg)
667 : {
668 : errno_t ret;
669 : int hret;
670 : struct tevent_req *req;
671 : struct dp_internal_get_state *state;
672 : struct be_conn *be_conn;
673 : hash_value_t value;
674 :
675 : /* Internal requests need to be allocated on the responder context
676 : * so that they don't go away if a client disconnects. The worst-
677 : * case scenario here is that the cache is updated without any
678 : * client expecting a response.
679 : */
680 0 : req = tevent_req_create(rctx,
681 : &state,
682 : struct dp_internal_get_state);
683 0 : if (!req) return NULL;
684 :
685 0 : state->rctx = rctx;
686 0 : state->dom = dom;
687 :
688 0 : state->sdp_req = talloc_zero(state, struct sss_dp_req);
689 0 : if (!state->sdp_req) {
690 0 : ret = ENOMEM;
691 0 : goto error;
692 : }
693 0 : state->sdp_req->rctx = rctx;
694 0 : state->sdp_req->ev = rctx->ev;
695 :
696 : /* Copy the key to use when calling the destructor
697 : * It needs to be a copy because the original request
698 : * might be freed if it no longer cares about the reply.
699 : */
700 0 : state->sdp_req->key = talloc_steal(state->sdp_req, key);
701 :
702 : /* double check dp_ctx has actually been initialized.
703 : * in some pathological cases it may happen that nss starts up before
704 : * dp connection code is actually able to establish a connection.
705 : */
706 0 : ret = sss_dp_get_domain_conn(rctx, dom->conn_name, &be_conn);
707 0 : if (ret != EOK) {
708 0 : DEBUG(SSSDBG_CRIT_FAILURE,
709 : "BUG: The Data Provider connection for %s is not available!\n",
710 : dom->name);
711 0 : ret = EIO;
712 0 : goto error;
713 : }
714 :
715 0 : ret = sbus_conn_send(be_conn->conn, msg,
716 : SSS_CLI_SOCKET_TIMEOUT / 2,
717 : sss_dp_internal_get_done,
718 : req,
719 0 : &state->sdp_req->pending_reply);
720 0 : if (ret != EOK) {
721 : /*
722 : * Critical Failure
723 : * We can't communicate on this connection
724 : */
725 0 : DEBUG(SSSDBG_CRIT_FAILURE,
726 : "D-BUS send failed.\n");
727 0 : ret = EIO;
728 0 : goto error;
729 : }
730 :
731 : /* Add this sdp_req to the hash table */
732 0 : value.type = HASH_VALUE_PTR;
733 0 : value.ptr = state->sdp_req;
734 :
735 0 : DEBUG(SSSDBG_TRACE_FUNC, "Entering request [%s]\n", key->str);
736 0 : hret = hash_enter(rctx->dp_request_table, key, &value);
737 0 : if (hret != HASH_SUCCESS) {
738 0 : DEBUG(SSSDBG_CRIT_FAILURE,
739 : "Could not store request query (%s)\n",
740 : hash_error_string(hret));
741 0 : ret = EIO;
742 0 : goto error;
743 : }
744 0 : talloc_set_destructor((TALLOC_CTX *)state->sdp_req,
745 : sss_dp_req_destructor);
746 :
747 0 : return req;
748 :
749 : error:
750 0 : tevent_req_error(req, ret);
751 0 : tevent_req_post(req, rctx->ev);
752 0 : return req;
753 : }
754 :
755 0 : static void sss_dp_internal_get_done(DBusPendingCall *pending, void *ptr)
756 : {
757 : int ret;
758 : struct tevent_req *req;
759 : struct sss_dp_req *sdp_req;
760 : struct sss_dp_callback *cb;
761 : struct dp_internal_get_state *state;
762 : struct sss_dp_req_state *cb_state;
763 :
764 0 : req = talloc_get_type(ptr, struct tevent_req);
765 0 : state = tevent_req_data(req, struct dp_internal_get_state);
766 0 : sdp_req = state->sdp_req;
767 :
768 : /* prevent trying to cancel a reply that we already received */
769 0 : sdp_req->pending_reply = NULL;
770 :
771 0 : ret = sss_dp_get_reply(pending,
772 : &sdp_req->dp_err,
773 : &sdp_req->dp_ret,
774 : &sdp_req->err_msg);
775 0 : if (ret != EOK) {
776 0 : if (ret == ETIME) {
777 0 : sdp_req->dp_err = DP_ERR_TIMEOUT;
778 0 : sdp_req->dp_ret = ret;
779 0 : sdp_req->err_msg = talloc_strdup(sdp_req, "Request timed out");
780 : }
781 : else {
782 0 : sdp_req->dp_err = DP_ERR_FATAL;
783 0 : sdp_req->dp_ret = ret;
784 0 : sdp_req->err_msg =
785 0 : talloc_strdup(sdp_req,
786 : "Failed to get reply from Data Provider");
787 : }
788 : }
789 :
790 : /* Check whether we need to issue any callbacks */
791 0 : while ((cb = sdp_req->cb_list) != NULL) {
792 0 : cb_state = tevent_req_data(cb->req, struct sss_dp_req_state);
793 0 : cb_state->dp_err = sdp_req->dp_err;
794 0 : cb_state->dp_ret = sdp_req->dp_ret;
795 0 : cb_state->err_msg = talloc_strdup(cb_state, sdp_req->err_msg);
796 : /* Don't bother checking for NULL. If it fails due to ENOMEM,
797 : * we can't really handle it anyway.
798 : */
799 :
800 : /* tevent_req_done/error will free cb */
801 0 : if (ret == EOK) {
802 0 : tevent_req_done(cb->req);
803 : } else {
804 0 : tevent_req_error(cb->req, ret);
805 : }
806 :
807 : /* Freeing the cb removes it from the cb_list.
808 : * Therefore, the cb_list should now be pointing
809 : * at a new callback. If it's not, it means the
810 : * callback handler didn't free cb and may leak
811 : * memory. Be paranoid and protect against this
812 : * situation.
813 : */
814 0 : if (cb == sdp_req->cb_list) {
815 0 : DEBUG(SSSDBG_FATAL_FAILURE,
816 : "BUG: a callback did not free its request. "
817 : "May leak memory\n");
818 : /* Skip to the next since a memory leak is non-fatal */
819 0 : sdp_req->cb_list = sdp_req->cb_list->next;
820 : }
821 : }
822 :
823 : /* We're done with this request. Free the sdp_req
824 : * This will clean up the hash table entry as well
825 : */
826 0 : talloc_zfree(sdp_req);
827 :
828 : /* Free the sidereq to free the rest of the memory allocated with the
829 : * internal dp request. */
830 0 : if (ret == EOK) {
831 0 : tevent_req_done(req);
832 : } else {
833 0 : tevent_req_error(req, ret);
834 : }
835 0 : }
|