Line data Source code
1 : /*
2 : SSSD
3 :
4 : Helper routines for file descriptor events
5 :
6 : Authors:
7 : Sumit Bose <sbose@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 "util/util.h"
26 : #include "providers/ldap/sdap_async_private.h"
27 :
28 : struct sdap_fd_events {
29 : #ifdef HAVE_LDAP_CONNCB
30 : struct ldap_conncb *conncb;
31 : #else
32 : struct tevent_fd *fde;
33 : #endif
34 : };
35 :
36 0 : int get_fd_from_ldap(LDAP *ldap, int *fd)
37 : {
38 : int ret;
39 :
40 0 : ret = ldap_get_option(ldap, LDAP_OPT_DESC, fd);
41 0 : if (ret != LDAP_OPT_SUCCESS || *fd < 0) {
42 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to get fd from ldap!!\n");
43 0 : *fd = -1;
44 0 : return EIO;
45 : }
46 :
47 0 : return EOK;
48 : }
49 :
50 0 : int remove_ldap_connection_callbacks(struct sdap_handle *sh)
51 : {
52 : /* sdap_fd_events might be NULL here if the back end was marked offline
53 : * before a connection was established.
54 : */
55 0 : if (sh->sdap_fd_events) {
56 : #ifdef HAVE_LDAP_CONNCB
57 0 : talloc_zfree(sh->sdap_fd_events->conncb);
58 : #else
59 : talloc_zfree(sh->sdap_fd_events->fde);
60 : #endif
61 : }
62 0 : return EOK;
63 : }
64 :
65 : #ifdef HAVE_LDAP_CONNCB
66 :
67 0 : static int remove_connection_callback(TALLOC_CTX *mem_ctx)
68 : {
69 : int lret;
70 0 : struct ldap_conncb *conncb = talloc_get_type(mem_ctx, struct ldap_conncb);
71 :
72 0 : struct ldap_cb_data *cb_data = talloc_get_type(conncb->lc_arg,
73 : struct ldap_cb_data);
74 :
75 0 : lret = ldap_get_option(cb_data->sh->ldap, LDAP_OPT_CONNECT_CB, conncb);
76 0 : if (lret != LDAP_OPT_SUCCESS) {
77 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to remove connection callback.\n");
78 : } else {
79 0 : DEBUG(SSSDBG_TRACE_ALL, "Successfully removed connection callback.\n");
80 : }
81 0 : return EOK;
82 : }
83 :
84 0 : static int sdap_ldap_connect_callback_add(LDAP *ld, Sockbuf *sb,
85 : LDAPURLDesc *srv,
86 : struct sockaddr *addr,
87 : struct ldap_conncb *ctx)
88 : {
89 : int ret;
90 : ber_socket_t ber_fd;
91 : struct fd_event_item *fd_event_item;
92 0 : struct ldap_cb_data *cb_data = talloc_get_type(ctx->lc_arg,
93 : struct ldap_cb_data);
94 :
95 0 : if (cb_data == NULL) {
96 0 : DEBUG(SSSDBG_CRIT_FAILURE,
97 : "sdap_ldap_connect_callback_add called without "
98 : "callback data.\n");
99 0 : return EINVAL;
100 : }
101 :
102 0 : ret = ber_sockbuf_ctrl(sb, LBER_SB_OPT_GET_FD, &ber_fd);
103 0 : if (ret == -1) {
104 0 : DEBUG(SSSDBG_CRIT_FAILURE, "ber_sockbuf_ctrl failed.\n");
105 0 : return EINVAL;
106 : }
107 :
108 0 : if (DEBUG_IS_SET(SSSDBG_TRACE_LIBS)) {
109 0 : char *uri = ldap_url_desc2str(srv);
110 0 : DEBUG(SSSDBG_TRACE_LIBS, "New LDAP connection to [%s] with fd [%d].\n",
111 : uri, ber_fd);
112 0 : free(uri);
113 : }
114 :
115 0 : fd_event_item = talloc_zero(cb_data, struct fd_event_item);
116 0 : if (fd_event_item == NULL) {
117 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc failed.\n");
118 0 : return ENOMEM;
119 : }
120 :
121 0 : fd_event_item->fde = tevent_add_fd(cb_data->ev, fd_event_item, ber_fd,
122 : TEVENT_FD_READ, sdap_ldap_result,
123 : cb_data->sh);
124 0 : if (fd_event_item->fde == NULL) {
125 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_fd failed.\n");
126 0 : talloc_free(fd_event_item);
127 0 : return ENOMEM;
128 : }
129 0 : fd_event_item->fd = ber_fd;
130 :
131 0 : DLIST_ADD(cb_data->fd_list, fd_event_item);
132 :
133 0 : return LDAP_SUCCESS;
134 : }
135 :
136 0 : static void sdap_ldap_connect_callback_del(LDAP *ld, Sockbuf *sb,
137 : struct ldap_conncb *ctx)
138 : {
139 : int ret;
140 : ber_socket_t ber_fd;
141 : struct fd_event_item *fd_event_item;
142 0 : struct ldap_cb_data *cb_data = talloc_get_type(ctx->lc_arg,
143 : struct ldap_cb_data);
144 :
145 0 : if (sb == NULL || cb_data == NULL) {
146 0 : return;
147 : }
148 :
149 0 : ret = ber_sockbuf_ctrl(sb, LBER_SB_OPT_GET_FD, &ber_fd);
150 0 : if (ret == -1) {
151 0 : DEBUG(SSSDBG_CRIT_FAILURE, "ber_sockbuf_ctrl failed.\n");
152 0 : return;
153 : }
154 0 : DEBUG(SSSDBG_TRACE_ALL, "Closing LDAP connection with fd [%d].\n", ber_fd);
155 :
156 0 : DLIST_FOR_EACH(fd_event_item, cb_data->fd_list) {
157 0 : if (fd_event_item->fd == ber_fd) {
158 0 : break;
159 : }
160 : }
161 0 : if (fd_event_item == NULL) {
162 0 : DEBUG(SSSDBG_CRIT_FAILURE, "No event for fd [%d] found.\n", ber_fd);
163 0 : return;
164 : }
165 :
166 0 : DLIST_REMOVE(cb_data->fd_list, fd_event_item);
167 0 : talloc_zfree(fd_event_item);
168 :
169 0 : return;
170 : }
171 :
172 : #else
173 :
174 : static int sdap_install_ldap_callbacks(struct sdap_handle *sh,
175 : struct tevent_context *ev)
176 : {
177 : int fd;
178 : int ret;
179 :
180 : if (sh->sdap_fd_events) {
181 : DEBUG(SSSDBG_CRIT_FAILURE,
182 : "sdap_install_ldap_callbacks is called with already "
183 : "initialized sdap_fd_events.\n");
184 : return EINVAL;
185 : }
186 :
187 : sh->sdap_fd_events = talloc_zero(sh, struct sdap_fd_events);
188 : if (!sh->sdap_fd_events) {
189 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
190 : return ENOMEM;
191 : }
192 :
193 : ret = get_fd_from_ldap(sh->ldap, &fd);
194 : if (ret) return ret;
195 :
196 : sh->sdap_fd_events->fde = tevent_add_fd(ev, sh->sdap_fd_events, fd,
197 : TEVENT_FD_READ, sdap_ldap_result,
198 : sh);
199 : if (!sh->sdap_fd_events->fde) {
200 : talloc_zfree(sh->sdap_fd_events);
201 : return ENOMEM;
202 : }
203 :
204 : DEBUG(SSSDBG_TRACE_INTERNAL,
205 : "Trace: sh[%p], connected[%d], ops[%p], fde[%p], ldap[%p]\n",
206 : sh, (int)sh->connected, sh->ops, sh->sdap_fd_events->fde,
207 : sh->ldap);
208 :
209 : return EOK;
210 : }
211 :
212 : #endif
213 :
214 :
215 0 : errno_t setup_ldap_connection_callbacks(struct sdap_handle *sh,
216 : struct tevent_context *ev)
217 : {
218 : #ifdef HAVE_LDAP_CONNCB
219 : int ret;
220 : struct ldap_cb_data *cb_data;
221 :
222 0 : sh->sdap_fd_events = talloc_zero(sh, struct sdap_fd_events);
223 0 : if (sh->sdap_fd_events == NULL) {
224 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
225 0 : ret = ENOMEM;
226 0 : goto fail;
227 : }
228 :
229 0 : sh->sdap_fd_events->conncb = talloc_zero(sh->sdap_fd_events,
230 : struct ldap_conncb);
231 0 : if (sh->sdap_fd_events->conncb == NULL) {
232 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
233 0 : ret = ENOMEM;
234 0 : goto fail;
235 : }
236 :
237 0 : cb_data = talloc_zero(sh->sdap_fd_events->conncb, struct ldap_cb_data);
238 0 : if (cb_data == NULL) {
239 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
240 0 : ret = ENOMEM;
241 0 : goto fail;
242 : }
243 0 : cb_data->sh = sh;
244 0 : cb_data->ev = ev;
245 :
246 0 : sh->sdap_fd_events->conncb->lc_add = sdap_ldap_connect_callback_add;
247 0 : sh->sdap_fd_events->conncb->lc_del = sdap_ldap_connect_callback_del;
248 0 : sh->sdap_fd_events->conncb->lc_arg = cb_data;
249 :
250 0 : ret = ldap_set_option(sh->ldap, LDAP_OPT_CONNECT_CB,
251 0 : sh->sdap_fd_events->conncb);
252 0 : if (ret != LDAP_OPT_SUCCESS) {
253 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set connection callback\n");
254 0 : ret = EFAULT;
255 0 : goto fail;
256 : }
257 :
258 0 : talloc_set_destructor((TALLOC_CTX *) sh->sdap_fd_events->conncb,
259 : remove_connection_callback);
260 :
261 0 : return EOK;
262 :
263 : fail:
264 0 : talloc_zfree(sh->sdap_fd_events);
265 0 : return ret;
266 : #else
267 : DEBUG(SSSDBG_TRACE_ALL, "LDAP connection callbacks are not supported.\n");
268 : return EOK;
269 : #endif
270 : }
271 :
272 0 : errno_t sdap_set_connected(struct sdap_handle *sh, struct tevent_context *ev)
273 : {
274 0 : int ret = EOK;
275 :
276 0 : sh->connected = true;
277 :
278 : #ifndef HAVE_LDAP_CONNCB
279 : ret = sdap_install_ldap_callbacks(sh, ev);
280 : #endif
281 :
282 0 : return ret;
283 : }
284 :
285 0 : errno_t sdap_call_conn_cb(const char *uri,int fd, struct sdap_handle *sh)
286 : {
287 : #ifdef HAVE_LDAP_CONNCB
288 : int ret;
289 : Sockbuf *sb;
290 : LDAPURLDesc *lud;
291 :
292 0 : sb = ber_sockbuf_alloc();
293 0 : if (sb == NULL) {
294 0 : DEBUG(SSSDBG_CRIT_FAILURE, "ber_sockbuf_alloc failed.\n");
295 0 : return ENOMEM;
296 : }
297 :
298 0 : ret = ber_sockbuf_ctrl(sb, LBER_SB_OPT_SET_FD, &fd);
299 0 : if (ret != 1) {
300 0 : DEBUG(SSSDBG_CRIT_FAILURE, "ber_sockbuf_ctrl failed.\n");
301 0 : return EFAULT;
302 : }
303 :
304 0 : ret = ldap_url_parse(uri, &lud);
305 0 : if (ret != 0) {
306 0 : ber_sockbuf_free(sb);
307 0 : DEBUG(SSSDBG_CRIT_FAILURE,
308 : "ldap_url_parse failed to validate [%s] on fd [%d].\n",
309 : uri, fd);
310 0 : return EFAULT;
311 : }
312 :
313 0 : ret = sdap_ldap_connect_callback_add(NULL, sb, lud, NULL,
314 0 : sh->sdap_fd_events->conncb);
315 :
316 0 : ldap_free_urldesc(lud);
317 0 : ber_sockbuf_free(sb);
318 0 : return ret;
319 : #else
320 : DEBUG(SSSDBG_TRACE_ALL, "LDAP connection callbacks are not supported.\n");
321 : return EOK;
322 : #endif
323 : }
|