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 hash_table_t *dp_requests = NULL;
32
33 struct sss_dp_req;
34
35 struct sss_dp_callback {
36 struct sss_dp_callback *prev;
37 struct sss_dp_callback *next;
38
39 struct sss_dp_req *sdp_req;
40
41 sss_dp_callback_t callback;
42 void *callback_ctx;
43 };
44
45 struct sss_dp_req {
46 struct tevent_context *ev;
47 DBusPendingCall *pending_reply;
48
49 char *key;
50
51 struct tevent_timer *tev;
52 struct sss_dp_callback *cb_list;
53
54 dbus_uint16_t err_maj;
55 dbus_uint32_t err_min;
56 char *err_msg;
57 };
58
59 static int sss_dp_callback_destructor(void *ptr)
60 {
61 struct sss_dp_callback *cb = talloc_get_type(ptr, struct sss_dp_callback);
62
63 DLIST_REMOVE(cb->sdp_req->cb_list, cb);
64
65 return EOK;
66 }
67
68 static int sss_dp_req_destructor(void *ptr)
69 {
70 struct sss_dp_req *sdp_req = talloc_get_type(ptr, struct sss_dp_req);
71 struct sss_dp_callback *cb, *next;
72 hash_key_t key;
73
74 /* Cancel Dbus pending reply if still pending */
75 if (sdp_req->pending_reply) {
76 dbus_pending_call_cancel(sdp_req->pending_reply);
77 sdp_req->pending_reply = NULL;
78 }
79
80 /* Destroy the hash entry */
81 key.type = HASH_KEY_STRING;
82 key.str = sdp_req->key;
83 int hret = hash_delete(dp_requests, &key);
84 if (hret != HASH_SUCCESS) {
85 /* This should never happen */
86 DEBUG(0, ("Could not clear entry from request queue\n"));
87 }
88
89 /* Free any remaining callback */
90 if (sdp_req->err_maj == DP_ERR_OK) {
91 sdp_req->err_maj = DP_ERR_FATAL;
92 sdp_req->err_min = EIO;
93 sdp_req->err_msg = discard_const_p(char, "Internal Error");
94 }
95
96 cb = sdp_req->cb_list;
97 while (cb) {
98 cb->callback(sdp_req->err_maj,
99 sdp_req->err_min,
100 sdp_req->err_msg,
101 cb->callback_ctx);
102 next = cb->next;
103 talloc_free(cb);
104 cb = next;
105 }
106
107 return 0;
108 }
109
110 static void sdp_req_timeout(struct tevent_context *ev,
111 struct tevent_timer *te,
112 struct timeval t, void *ptr)
113 {
114 struct sss_dp_req *sdp_req = talloc_get_type(ptr, struct sss_dp_req);
115
116 sdp_req->err_maj = DP_ERR_FATAL;
117 sdp_req->err_min = ETIMEDOUT;
118 sdp_req->err_msg = discard_const_p(char, "Timed out");
119
120 /* steal te on NULL because it will be freed as soon as the handler
121 * returns. Causing a double free if we don't, as te is allocated on
122 * sdp_req and we are just going to free it */
123 talloc_steal(NULL, te);
124
125 talloc_free(sdp_req);
126 }
127
128 static int sss_dp_get_reply(DBusPendingCall *pending,
129 dbus_uint16_t *err_maj,
130 dbus_uint32_t *err_min,
131 char **err_msg);
132
133 static void sss_dp_invoke_callback(struct tevent_context *ev,
134 struct tevent_timer *te,
135 struct timeval t, void *ptr)
136 {
137 struct sss_dp_req *sdp_req = talloc_get_type(ptr, struct sss_dp_req);
138 struct sss_dp_callback *cb;
139 struct timeval tv;
140 struct tevent_timer *tev;
141
142 cb = sdp_req->cb_list;
143 /* Remove the callback from the list, the caller may free it, within the
144 * callback. */
145 talloc_set_destructor((TALLOC_CTX *)cb, NULL);
146 DLIST_REMOVE(sdp_req->cb_list, cb);
147
148 cb->callback(sdp_req->err_maj,
149 sdp_req->err_min,
150 sdp_req->err_msg,
151 cb->callback_ctx);
152
153 /* Call the next callback if needed */
154 if (sdp_req->cb_list != NULL) {
155 tv = tevent_timeval_current();
Event returned_pointer: Pointer "tev" returned by "_tevent_add_timer(sdp_req->ev, sdp_req, tv, sss_dp_invoke_callback, sdp_req, &"sss_dp_invoke_callback", &"responder/common/responder_dp.c:156")" is never used.
|
156 tev = tevent_add_timer(sdp_req->ev, sdp_req, tv,
157 sss_dp_invoke_callback, sdp_req);
158 if (!te) {
159 /* Out of memory or other serious error */
160 goto done;
161 }
162
163 return;
164 }
165
166 /* No more callbacks to invoke. Destroy the request */
167 done:
168 /* steal te on NULL because it will be freed as soon as the handler
169 * returns. Causing a double free if we don't, as te is allocated on
170 * sdp_req and we are just going to free it */
171 talloc_steal(NULL, te);
172
173 talloc_zfree(sdp_req);
174 }
175
176 static void sss_dp_send_acct_callback(DBusPendingCall *pending, void *ptr)
177 {
178 int ret;
179 struct sss_dp_req *sdp_req;
180 struct sss_dp_callback *cb;
181 struct timeval tv;
182 struct tevent_timer *te;
183
184 sdp_req = talloc_get_type(ptr, struct sss_dp_req);
185
186 /* prevent trying to cancel a reply that we already received */
187 sdp_req->pending_reply = NULL;
188
189 ret = sss_dp_get_reply(pending,
190 &sdp_req->err_maj,
191 &sdp_req->err_min,
192 &sdp_req->err_msg);
193 if (ret != EOK) {
194 if (ret == ETIME) {
195 sdp_req->err_maj = DP_ERR_TIMEOUT;
196 sdp_req->err_min = ret;
197 sdp_req->err_msg = talloc_strdup(sdp_req, "Request timed out");
198 }
199 else {
200 sdp_req->err_maj = DP_ERR_FATAL;
201 sdp_req->err_min = ret;
202 sdp_req->err_msg =
203 talloc_strdup(sdp_req,
204 "Failed to get reply from Data Provider");
205 }
206 }
207
208 /* Check whether we need to issue any callbacks */
209 cb = sdp_req->cb_list;
210 if (sdp_req->cb_list == NULL) {
211 if (cb == NULL) {
212 /* No callbacks to invoke. Destroy the hash entry */
213 talloc_zfree(sdp_req);
214 return;
215 }
216 }
217
218 /* Queue up all callbacks */
219 tv = tevent_timeval_current();
220 te = tevent_add_timer(sdp_req->ev, sdp_req, tv,
221 sss_dp_invoke_callback, sdp_req);
222 if (!te) {
223 /* Out of memory or other serious error */
224 goto error;
225 }
226
227 return;
228
229 error:
230 talloc_zfree(sdp_req);
231 }
232
233 static int sss_dp_send_acct_req_create(struct resp_ctx *rctx,
234 TALLOC_CTX *callback_memctx,
235 const char *domain,
236 uint32_t be_type,
237 char *filter,
238 int timeout,
239 sss_dp_callback_t callback,
240 void *callback_ctx,
241 struct sss_dp_req **ndp);
242
243 int sss_dp_send_acct_req(struct resp_ctx *rctx, TALLOC_CTX *callback_memctx,
244 sss_dp_callback_t callback, void *callback_ctx,
245 int timeout, const char *domain,
246 bool fast_reply, int type,
247 const char *opt_name, uint32_t opt_id)
248 {
249 int ret, hret;
250 uint32_t be_type;
251 char *filter;
252 hash_key_t key;
253 hash_value_t value;
254 TALLOC_CTX *tmp_ctx;
255 struct timeval tv;
256 struct sss_dp_req *sdp_req = NULL;
257 struct sss_dp_callback *cb;
258
259 /* either, or, not both */
260 if (opt_name && opt_id) {
261 return EINVAL;
262 }
263
264 if (!domain) {
265 return EINVAL;
266 }
267
268 switch (type) {
269 case SSS_DP_USER:
270 be_type = BE_REQ_USER;
271 break;
272 case SSS_DP_GROUP:
273 be_type = BE_REQ_GROUP;
274 break;
275 case SSS_DP_INITGROUPS:
276 be_type = BE_REQ_INITGROUPS;
277 break;
278 default:
279 return EINVAL;
280 }
281
282 if (fast_reply) {
283 be_type |= BE_REQ_FAST;
284 }
285
286 if (dp_requests == NULL) {
287 /* Create a hash table to handle queued update requests */
288 ret = hash_create(10, &dp_requests, NULL, NULL);
289 if (ret != HASH_SUCCESS) {
290 fprintf(stderr, "cannot create hash table (%s)\n", hash_error_string(ret));
291 return EIO;
292 }
293 }
294
295 tmp_ctx = talloc_new(NULL);
296 if (!tmp_ctx) {
297 return ENOMEM;
298 }
299
300 key.type = HASH_KEY_STRING;
301 key.str = NULL;
302
303 if (opt_name) {
304 filter = talloc_asprintf(tmp_ctx, "name=%s", opt_name);
305 key.str = talloc_asprintf(tmp_ctx, "%d%s@%s", type, opt_name, domain);
306 } else if (opt_id) {
307 filter = talloc_asprintf(tmp_ctx, "idnumber=%u", opt_id);
308 key.str = talloc_asprintf(tmp_ctx, "%d%d@%s", type, opt_id, domain);
309 } else {
310 filter = talloc_strdup(tmp_ctx, "name=*");
311 key.str = talloc_asprintf(tmp_ctx, "%d*@%s", type, domain);
312 }
313 if (!filter || !key.str) {
314 talloc_zfree(tmp_ctx);
315 return ENOMEM;
316 }
317
318 /* Check whether there's already a request in progress */
319 hret = hash_lookup(dp_requests, &key, &value);
320 switch (hret) {
321 case HASH_SUCCESS:
322 /* Request already in progress
323 * Add an additional callback if needed and return
324 */
325 DEBUG(2, ("Identical request in progress\n"));
326
327 if (callback) {
328 /* We have a new request asking for a callback */
329 sdp_req = talloc_get_type(value.ptr, struct sss_dp_req);
330 if (!sdp_req) {
331 DEBUG(0, ("Could not retrieve DP request context\n"));
332 ret = EIO;
333 goto done;
334 }
335
336 cb = talloc_zero(callback_memctx, struct sss_dp_callback);
337 if (!cb) {
338 ret = ENOMEM;
339 goto done;
340 }
341
342 cb->callback = callback;
343 cb->callback_ctx = callback_ctx;
344 cb->sdp_req = sdp_req;
345
346 DLIST_ADD_END(sdp_req->cb_list, cb, struct sss_dp_callback *);
347 talloc_set_destructor((TALLOC_CTX *)cb, sss_dp_callback_destructor);
348 }
349
350 ret = EOK;
351 break;
352
353 case HASH_ERROR_KEY_NOT_FOUND:
354 /* No such request in progress
355 * Create a new request
356 */
357 ret = sss_dp_send_acct_req_create(rctx, callback_memctx, domain,
358 be_type, filter, timeout,
359 callback, callback_ctx,
360 &sdp_req);
361 if (ret != EOK) {
362 goto done;
363 }
364
365 value.type = HASH_VALUE_PTR;
366 value.ptr = sdp_req;
367 hret = hash_enter(dp_requests, &key, &value);
368 if (hret != HASH_SUCCESS) {
369 DEBUG(0, ("Could not store request query (%s)\n",
370 hash_error_string(hret)));
371 talloc_zfree(sdp_req);
372 ret = EIO;
373 goto done;
374 }
375
376 sdp_req->key = talloc_strdup(sdp_req, key.str);
377
378 tv = tevent_timeval_current_ofs(timeout, 0);
379 sdp_req->tev = tevent_add_timer(sdp_req->ev, sdp_req, tv,
380 sdp_req_timeout, sdp_req);
381 if (!sdp_req->tev) {
382 DEBUG(0, ("Out of Memory!?\n"));
383 talloc_zfree(sdp_req);
384 ret = ENOMEM;
385 goto done;
386 }
387
388 talloc_set_destructor((TALLOC_CTX *)sdp_req, sss_dp_req_destructor);
389
390 ret = EOK;
391 break;
392
393 default:
394 DEBUG(0,("Could not query request list (%s)\n",
395 hash_error_string(hret)));
396 talloc_zfree(sdp_req);
397 ret = EIO;
398 }
399
400 done:
401 talloc_zfree(tmp_ctx);
402 return ret;
403 }
404
405 static int sss_dp_send_acct_req_create(struct resp_ctx *rctx,
406 TALLOC_CTX *callback_memctx,
407 const char *domain,
408 uint32_t be_type,
409 char *filter,
410 int timeout,
411 sss_dp_callback_t callback,
412 void *callback_ctx,
413 struct sss_dp_req **ndp)
414 {
415 DBusMessage *msg;
416 DBusPendingCall *pending_reply;
417 dbus_bool_t dbret;
418 struct sss_dp_callback *cb;
419 struct sss_dp_req *sdp_req;
420 uint32_t attrs = BE_ATTR_CORE;
421 struct be_conn *be_conn;
422 int ret;
423
424 /* double check dp_ctx has actually been initialized.
425 * in some pathological cases it may happen that nss starts up before
426 * dp connection code is actually able to establish a connection.
427 */
428 ret = sss_dp_get_domain_conn(rctx, domain, &be_conn);
429 if (ret != EOK) {
430 DEBUG(1, ("The Data Provider connection for %s is not available!"
431 " This maybe a bug, it shouldn't happen!\n", domain));
432 return EIO;
433 }
434
435 /* create the message */
436 msg = dbus_message_new_method_call(NULL,
437 DP_PATH,
438 DP_INTERFACE,
439 DP_METHOD_GETACCTINFO);
440 if (msg == NULL) {
441 DEBUG(0,("Out of memory?!\n"));
442 return ENOMEM;
443 }
444
445 DEBUG(4, ("Sending request for [%s][%u][%d][%s]\n",
446 domain, be_type, attrs, filter));
447
448 dbret = dbus_message_append_args(msg,
449 DBUS_TYPE_UINT32, &be_type,
450 DBUS_TYPE_UINT32, &attrs,
451 DBUS_TYPE_STRING, &filter,
452 DBUS_TYPE_INVALID);
453 if (!dbret) {
454 DEBUG(1,("Failed to build message\n"));
455 return EIO;
456 }
457
458 sdp_req = talloc_zero(rctx, struct sss_dp_req);
459 if (!sdp_req) {
460 dbus_message_unref(msg);
461 return ENOMEM;
462 }
463
464 ret = sbus_conn_send(be_conn->conn, msg, timeout,
465 sss_dp_send_acct_callback,
466 sdp_req, &pending_reply);
467 dbus_message_unref(msg);
468 if (ret != EOK) {
469 /*
470 * Critical Failure
471 * We can't communicate on this connection
472 * We'll drop it using the default destructor.
473 */
474 DEBUG(0, ("D-BUS send failed.\n"));
475 return EIO;
476 }
477
478 sdp_req->ev = rctx->ev;
479 sdp_req->pending_reply = pending_reply;
480
481 if (callback) {
482 cb = talloc_zero(callback_memctx, struct sss_dp_callback);
483 if (!cb) {
484 talloc_zfree(sdp_req);
485 return ENOMEM;
486 }
487 cb->callback = callback;
488 cb->callback_ctx = callback_ctx;
489 cb->sdp_req = sdp_req;
490
491 DLIST_ADD(sdp_req->cb_list, cb);
492 talloc_set_destructor((TALLOC_CTX *)cb, sss_dp_callback_destructor);
493 }
494
495 *ndp = sdp_req;
496
497 return EOK;
498 }
499
500 static int sss_dp_get_reply(DBusPendingCall *pending,
501 dbus_uint16_t *err_maj,
502 dbus_uint32_t *err_min,
503 char **err_msg)
504 {
505 DBusMessage *reply;
506 DBusError dbus_error;
507 dbus_bool_t ret;
508 int type;
509 int err = EOK;
510
511 dbus_error_init(&dbus_error);
512
513 reply = dbus_pending_call_steal_reply(pending);
514 if (!reply) {
515 /* reply should never be null. This function shouldn't be called
516 * until reply is valid or timeout has occurred. If reply is NULL
517 * here, something is seriously wrong and we should bail out.
518 */
519 DEBUG(0, ("Severe error. A reply callback was called but no reply was received and no timeout occurred\n"));
520
521 /* FIXME: Destroy this connection ? */
522 err = EIO;
523 goto done;
524 }
525
526 type = dbus_message_get_type(reply);
527 switch (type) {
528 case DBUS_MESSAGE_TYPE_METHOD_RETURN:
529 ret = dbus_message_get_args(reply, &dbus_error,
530 DBUS_TYPE_UINT16, err_maj,
531 DBUS_TYPE_UINT32, err_min,
532 DBUS_TYPE_STRING, err_msg,
533 DBUS_TYPE_INVALID);
534 if (!ret) {
535 DEBUG(1,("Failed to parse message\n"));
536 /* FIXME: Destroy this connection ? */
537 if (dbus_error_is_set(&dbus_error)) dbus_error_free(&dbus_error);
538 err = EIO;
539 goto done;
540 }
541
542 DEBUG(4, ("Got reply (%u, %u, %s) from Data Provider\n",
543 (unsigned int)*err_maj, (unsigned int)*err_min, *err_msg));
544
545 break;
546
547 case DBUS_MESSAGE_TYPE_ERROR:
548 if (strcmp(dbus_message_get_error_name(reply),
549 DBUS_ERROR_NO_REPLY) == 0) {
550 err = ETIME;
551 goto done;
552 }
553 DEBUG(0,("The Data Provider returned an error [%s]\n",
554 dbus_message_get_error_name(reply)));
555 /* Falling through to default intentionally*/
556 default:
557 /*
558 * Timeout or other error occurred or something
559 * unexpected happened.
560 * It doesn't matter which, because either way we
561 * know that this connection isn't trustworthy.
562 * We'll destroy it now.
563 */
564
565 /* FIXME: Destroy this connection ? */
566 err = EIO;
567 }
568
569 done:
570 dbus_pending_call_unref(pending);
571 dbus_message_unref(reply);
572
573 return err;
574 }
575