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