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