Line data Source code
1 : /*
2 : Authors:
3 : Sumit Bose <sbose@redhat.com>
4 :
5 : Copyright (C) 2009 Red Hat
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 : #include <stdlib.h>
21 : #include <unistd.h>
22 : #include <fcntl.h>
23 : #include <sys/socket.h>
24 : #include <netinet/in.h>
25 : #include <netinet/tcp.h>
26 :
27 : #include "config.h"
28 :
29 : #include "providers/ldap/sdap.h"
30 : #include "util/sss_ldap.h"
31 : #include "util/util.h"
32 :
33 1 : const char* sss_ldap_err2string(int err)
34 : {
35 1 : if (IS_SSSD_ERROR(err)) {
36 0 : return sss_strerror(err);
37 : } else {
38 1 : return ldap_err2string(err);
39 : }
40 : }
41 :
42 0 : int sss_ldap_get_diagnostic_msg(TALLOC_CTX *mem_ctx, LDAP *ld, char **_errmsg)
43 : {
44 0 : char *errmsg = NULL;
45 : int optret;
46 :
47 0 : optret = ldap_get_option(ld, SDAP_DIAGNOSTIC_MESSAGE, (void*)&errmsg);
48 0 : if (optret != LDAP_SUCCESS) {
49 0 : return EINVAL;
50 : }
51 :
52 0 : *_errmsg = talloc_strdup(mem_ctx, errmsg ? errmsg : "unknown error");
53 0 : ldap_memfree(errmsg);
54 0 : if (*_errmsg == NULL) {
55 0 : return ENOMEM;
56 : }
57 0 : return EOK;
58 : }
59 :
60 0 : int sss_ldap_control_create(const char *oid, int iscritical,
61 : struct berval *value, int dupval,
62 : LDAPControl **ctrlp)
63 : {
64 : #ifdef HAVE_LDAP_CONTROL_CREATE
65 0 : return ldap_control_create(oid, iscritical, value, dupval, ctrlp);
66 : #else
67 : LDAPControl *lc = NULL;
68 :
69 : if (oid == NULL || ctrlp == NULL) {
70 : return LDAP_PARAM_ERROR;
71 : }
72 :
73 : lc = calloc(sizeof(LDAPControl), 1);
74 : if (lc == NULL) {
75 : return LDAP_NO_MEMORY;
76 : }
77 :
78 : lc->ldctl_oid = strdup(oid);
79 : if (lc->ldctl_oid == NULL) {
80 : free(lc);
81 : return LDAP_NO_MEMORY;
82 : }
83 :
84 : if (value != NULL && value->bv_val != NULL) {
85 : if (dupval == 0) {
86 : lc->ldctl_value = *value;
87 : } else {
88 : ber_dupbv(&lc->ldctl_value, value);
89 : if (lc->ldctl_value.bv_val == NULL) {
90 : free(lc->ldctl_oid);
91 : free(lc);
92 : return LDAP_NO_MEMORY;
93 : }
94 : }
95 : }
96 :
97 : lc->ldctl_iscritical = iscritical;
98 :
99 : *ctrlp = lc;
100 :
101 : return LDAP_SUCCESS;
102 : #endif
103 : }
104 :
105 : #ifdef HAVE_LDAP_INIT_FD
106 : struct sdap_async_sys_connect_state {
107 : long old_flags;
108 : struct tevent_fd *fde;
109 : int fd;
110 : socklen_t addr_len;
111 : struct sockaddr_storage addr;
112 : };
113 :
114 : static void sdap_async_sys_connect_done(struct tevent_context *ev,
115 : struct tevent_fd *fde, uint16_t flags,
116 : void *priv);
117 :
118 0 : static struct tevent_req *sdap_async_sys_connect_send(TALLOC_CTX *mem_ctx,
119 : struct tevent_context *ev,
120 : int fd,
121 : const struct sockaddr *addr,
122 : socklen_t addr_len)
123 : {
124 : struct tevent_req *req;
125 : struct sdap_async_sys_connect_state *state;
126 : long flags;
127 : int ret;
128 : int fret;
129 :
130 0 : flags = fcntl(fd, F_GETFL, 0);
131 0 : if (flags == -1) {
132 0 : DEBUG(SSSDBG_CRIT_FAILURE, "fcntl F_GETFL failed.\n");
133 0 : return NULL;
134 : }
135 :
136 0 : req = tevent_req_create(mem_ctx, &state,
137 : struct sdap_async_sys_connect_state);
138 0 : if (req == NULL) {
139 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n");
140 0 : return NULL;
141 : }
142 :
143 0 : state->old_flags = flags;
144 0 : state->fd = fd;
145 0 : state->addr_len = addr_len;
146 0 : memcpy(&state->addr, addr, addr_len);
147 :
148 0 : ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
149 0 : if (ret != EOK) {
150 0 : DEBUG(SSSDBG_CRIT_FAILURE, "fcntl F_SETFL failed.\n");
151 0 : goto done;
152 : }
153 :
154 0 : ret = connect(fd, addr, addr_len);
155 0 : if (ret == EOK) {
156 0 : goto done;
157 : }
158 :
159 0 : ret = errno;
160 0 : switch(ret) {
161 : case EINPROGRESS:
162 : case EINTR:
163 0 : state->fde = tevent_add_fd(ev, state, fd,
164 : TEVENT_FD_READ | TEVENT_FD_WRITE,
165 : sdap_async_sys_connect_done, req);
166 0 : if (state->fde == NULL) {
167 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_fd failed.\n");
168 0 : ret = ENOMEM;
169 0 : goto done;
170 : }
171 :
172 0 : return req;
173 :
174 : break;
175 : default:
176 0 : DEBUG(SSSDBG_CRIT_FAILURE,
177 : "connect failed [%d][%s].\n", ret, strerror(ret));
178 : }
179 :
180 : done:
181 0 : fret = fcntl(fd, F_SETFL, flags);
182 0 : if (fret != EOK) {
183 0 : DEBUG(SSSDBG_CRIT_FAILURE, "fcntl F_SETFL failed.\n");
184 : }
185 :
186 0 : if (ret == EOK) {
187 0 : tevent_req_done(req);
188 : } else {
189 0 : tevent_req_error(req, ret);
190 : }
191 :
192 0 : tevent_req_post(req, ev);
193 0 : return req;
194 : }
195 :
196 0 : static void sdap_async_sys_connect_done(struct tevent_context *ev,
197 : struct tevent_fd *fde, uint16_t flags,
198 : void *priv)
199 : {
200 0 : struct tevent_req *req = talloc_get_type(priv, struct tevent_req);
201 0 : struct sdap_async_sys_connect_state *state = tevent_req_data(req,
202 : struct sdap_async_sys_connect_state);
203 : int ret;
204 : int fret;
205 :
206 0 : errno = 0;
207 0 : ret = connect(state->fd, (struct sockaddr *) &state->addr,
208 : state->addr_len);
209 0 : if (ret != EOK) {
210 0 : ret = errno;
211 0 : if (ret == EINPROGRESS || ret == EINTR) {
212 0 : return; /* Try again later */
213 : }
214 0 : DEBUG(SSSDBG_CRIT_FAILURE,
215 : "connect failed [%d][%s].\n", ret, strerror(ret));
216 : }
217 :
218 0 : talloc_zfree(fde);
219 :
220 0 : fret = fcntl(state->fd, F_SETFL, state->old_flags);
221 0 : if (fret != EOK) {
222 0 : DEBUG(SSSDBG_CRIT_FAILURE, "fcntl F_SETFL failed.\n");
223 : }
224 :
225 0 : if (ret == EOK) {
226 0 : tevent_req_done(req);
227 : } else {
228 0 : tevent_req_error(req, ret);
229 : }
230 :
231 0 : return;
232 : }
233 :
234 0 : static int sdap_async_sys_connect_recv(struct tevent_req *req)
235 : {
236 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
237 :
238 0 : return EOK;
239 : }
240 :
241 0 : static errno_t set_fd_flags_and_opts(int fd)
242 : {
243 : int ret;
244 : long flags;
245 0 : int dummy = 1;
246 :
247 0 : flags = fcntl(fd, F_GETFD, 0);
248 0 : if (flags == -1) {
249 0 : ret = errno;
250 0 : DEBUG(SSSDBG_CRIT_FAILURE,
251 : "fcntl F_GETFD failed [%d][%s].\n", ret, strerror(ret));
252 0 : return ret;
253 : }
254 :
255 0 : flags = fcntl(fd, F_SETFD, flags| FD_CLOEXEC);
256 0 : if (flags == -1) {
257 0 : ret = errno;
258 0 : DEBUG(SSSDBG_CRIT_FAILURE,
259 : "fcntl F_SETFD failed [%d][%s].\n", ret, strerror(ret));
260 0 : return ret;
261 : }
262 :
263 : /* SO_KEEPALIVE and TCP_NODELAY are set by OpenLDAP client libraries but
264 : * failures are ignored.*/
265 0 : ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &dummy, sizeof(dummy));
266 0 : if (ret != 0) {
267 0 : ret = errno;
268 0 : DEBUG(SSSDBG_FUNC_DATA,
269 : "setsockopt SO_KEEPALIVE failed.[%d][%s].\n", ret,
270 : strerror(ret));
271 : }
272 :
273 0 : ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &dummy, sizeof(dummy));
274 0 : if (ret != 0) {
275 0 : ret = errno;
276 0 : DEBUG(SSSDBG_FUNC_DATA,
277 : "setsockopt TCP_NODELAY failed.[%d][%s].\n", ret,
278 : strerror(ret));
279 : }
280 :
281 0 : return EOK;
282 : }
283 :
284 : #define LDAP_PROTO_TCP 1 /* ldap:// */
285 : #define LDAP_PROTO_UDP 2 /* reserved */
286 : #define LDAP_PROTO_IPC 3 /* ldapi:// */
287 : #define LDAP_PROTO_EXT 4 /* user-defined socket/sockbuf */
288 :
289 : extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld);
290 :
291 : static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq);
292 : static void sdap_async_sys_connect_timeout(struct tevent_context *ev,
293 : struct tevent_timer *te,
294 : struct timeval tv, void *pvt);
295 : #endif
296 :
297 : struct sss_ldap_init_state {
298 : LDAP *ldap;
299 : int sd;
300 : const char *uri;
301 :
302 : #ifdef HAVE_LDAP_INIT_FD
303 : struct tevent_timer *connect_timeout;
304 : #endif
305 : };
306 :
307 :
308 0 : struct tevent_req *sss_ldap_init_send(TALLOC_CTX *mem_ctx,
309 : struct tevent_context *ev,
310 : const char *uri,
311 : struct sockaddr_storage *addr,
312 : int addr_len, int timeout)
313 : {
314 0 : int ret = EOK;
315 : struct tevent_req *req;
316 : struct sss_ldap_init_state *state;
317 :
318 0 : req = tevent_req_create(mem_ctx, &state, struct sss_ldap_init_state);
319 0 : if (req == NULL) {
320 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n");
321 0 : return NULL;
322 : }
323 :
324 0 : state->ldap = NULL;
325 0 : state->uri = uri;
326 :
327 : #ifdef HAVE_LDAP_INIT_FD
328 : struct tevent_req *subreq;
329 : struct timeval tv;
330 :
331 0 : state->sd = socket(addr->ss_family, SOCK_STREAM, 0);
332 0 : if (state->sd == -1) {
333 0 : ret = errno;
334 0 : DEBUG(SSSDBG_CRIT_FAILURE,
335 : "socket failed [%d][%s].\n", ret, strerror(ret));
336 0 : goto fail;
337 : }
338 :
339 0 : ret = set_fd_flags_and_opts(state->sd);
340 0 : if (ret != EOK) {
341 0 : DEBUG(SSSDBG_CRIT_FAILURE, "set_fd_flags_and_opts failed.\n");
342 0 : goto fail;
343 : }
344 :
345 0 : DEBUG(SSSDBG_TRACE_ALL,
346 : "Using file descriptor [%d] for LDAP connection.\n", state->sd);
347 :
348 0 : subreq = sdap_async_sys_connect_send(state, ev, state->sd,
349 : (struct sockaddr *) addr, addr_len);
350 0 : if (subreq == NULL) {
351 0 : ret = ENOMEM;
352 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sdap_async_sys_connect_send failed.\n");
353 0 : goto fail;
354 : }
355 :
356 0 : DEBUG(SSSDBG_TRACE_FUNC,
357 : "Setting %d seconds timeout for connecting\n", timeout);
358 0 : tv = tevent_timeval_current_ofs(timeout, 0);
359 :
360 0 : state->connect_timeout = tevent_add_timer(ev, subreq, tv,
361 : sdap_async_sys_connect_timeout,
362 : subreq);
363 0 : if (state->connect_timeout == NULL) {
364 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_timer failed.\n");
365 0 : ret = ENOMEM;
366 0 : goto fail;
367 : }
368 :
369 0 : tevent_req_set_callback(subreq, sss_ldap_init_sys_connect_done, req);
370 0 : return req;
371 :
372 : fail:
373 0 : if(state->sd >= 0) {
374 0 : close(state->sd);
375 : }
376 0 : tevent_req_error(req, ret);
377 : #else
378 : DEBUG(SSSDBG_MINOR_FAILURE, "ldap_init_fd not available, "
379 : "will use ldap_initialize with uri [%s].\n", uri);
380 : state->sd = -1;
381 : ret = ldap_initialize(&state->ldap, uri);
382 : if (ret == LDAP_SUCCESS) {
383 : tevent_req_done(req);
384 : } else {
385 : DEBUG(SSSDBG_CRIT_FAILURE,
386 : "ldap_initialize failed [%s].\n", sss_ldap_err2string(ret));
387 : if (ret == LDAP_SERVER_DOWN) {
388 : tevent_req_error(req, ETIMEDOUT);
389 : } else {
390 : tevent_req_error(req, EIO);
391 : }
392 : }
393 : #endif
394 :
395 0 : tevent_req_post(req, ev);
396 0 : return req;
397 : }
398 :
399 : #ifdef HAVE_LDAP_INIT_FD
400 0 : static void sdap_async_sys_connect_timeout(struct tevent_context *ev,
401 : struct tevent_timer *te,
402 : struct timeval tv, void *pvt)
403 : {
404 : struct tevent_req *connection_request;
405 :
406 0 : DEBUG(SSSDBG_CONF_SETTINGS, "The LDAP connection timed out\n");
407 :
408 0 : connection_request = talloc_get_type(pvt, struct tevent_req);
409 0 : tevent_req_error(connection_request, ETIMEDOUT);
410 0 : }
411 :
412 0 : static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq)
413 : {
414 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
415 : struct tevent_req);
416 0 : struct sss_ldap_init_state *state = tevent_req_data(req,
417 : struct sss_ldap_init_state);
418 : int ret;
419 : int lret;
420 :
421 0 : talloc_zfree(state->connect_timeout);
422 :
423 0 : ret = sdap_async_sys_connect_recv(subreq);
424 0 : talloc_zfree(subreq);
425 0 : if (ret != EOK) {
426 0 : DEBUG(SSSDBG_CRIT_FAILURE,
427 : "sdap_async_sys_connect request failed: [%d]: %s.\n",
428 : ret, sss_strerror(ret));
429 0 : goto fail;
430 : }
431 : /* Initialize LDAP handler */
432 :
433 0 : lret = ldap_init_fd(state->sd, LDAP_PROTO_TCP, state->uri, &state->ldap);
434 0 : if (lret != LDAP_SUCCESS) {
435 0 : DEBUG(SSSDBG_CRIT_FAILURE,
436 : "ldap_init_fd failed: %s. [%d][%s]\n",
437 : sss_ldap_err2string(lret), state->sd, state->uri);
438 0 : ret = lret == LDAP_SERVER_DOWN ? ETIMEDOUT : EIO;
439 0 : goto fail;
440 : }
441 :
442 0 : if (ldap_is_ldaps_url(state->uri)) {
443 0 : lret = ldap_install_tls(state->ldap);
444 0 : if (lret != LDAP_SUCCESS) {
445 0 : if (lret == LDAP_LOCAL_ERROR) {
446 0 : DEBUG(SSSDBG_FUNC_DATA, "TLS/SSL already in place.\n");
447 : } else {
448 0 : DEBUG(SSSDBG_CRIT_FAILURE, "ldap_install_tls failed: %s\n",
449 : sss_ldap_err2string(lret));
450 0 : ret = EIO;
451 0 : goto fail;
452 : }
453 : }
454 : }
455 :
456 0 : tevent_req_done(req);
457 0 : return;
458 :
459 : fail:
460 0 : if (state->ldap) {
461 0 : ldap_unbind_ext(state->ldap, NULL, NULL);
462 : } else {
463 0 : close(state->sd);
464 : }
465 0 : tevent_req_error(req, ret);
466 : }
467 : #endif
468 :
469 0 : int sss_ldap_init_recv(struct tevent_req *req, LDAP **ldap, int *sd)
470 : {
471 0 : struct sss_ldap_init_state *state = tevent_req_data(req,
472 : struct sss_ldap_init_state);
473 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
474 :
475 0 : *ldap = state->ldap;
476 0 : *sd = state->sd;
477 :
478 0 : return EOK;
479 : }
480 :
481 : /*
482 : * _filter will contain combined filters from all possible search bases
483 : * or NULL if it should be empty
484 : */
485 :
486 :
487 94 : bool sss_ldap_dn_in_search_bases_len(TALLOC_CTX *mem_ctx,
488 : const char *dn,
489 : struct sdap_search_base **search_bases,
490 : char **_filter,
491 : int *_match_len)
492 : {
493 : struct sdap_search_base *base;
494 : int basedn_len, dn_len;
495 : int len_diff;
496 : int i, j;
497 94 : bool base_confirmed = false;
498 94 : bool comma_found = false;
499 94 : bool backslash_found = false;
500 94 : char *filter = NULL;
501 94 : bool ret = false;
502 : int match_len;
503 :
504 94 : if (dn == NULL) {
505 0 : DEBUG(SSSDBG_FUNC_DATA, "dn is NULL\n");
506 0 : ret = false;
507 0 : goto done;
508 : }
509 :
510 94 : if (search_bases == NULL) {
511 30 : DEBUG(SSSDBG_FUNC_DATA, "search_bases is NULL\n");
512 30 : ret = false;
513 30 : goto done;
514 : }
515 :
516 64 : dn_len = strlen(dn);
517 87 : for (i = 0; search_bases[i] != NULL; i++) {
518 65 : base = search_bases[i];
519 65 : basedn_len = strlen(base->basedn);
520 :
521 65 : if (basedn_len > dn_len) {
522 0 : continue;
523 : }
524 :
525 65 : len_diff = dn_len - basedn_len;
526 65 : base_confirmed = (strncasecmp(&dn[len_diff], base->basedn, basedn_len) == 0);
527 65 : if (!base_confirmed) {
528 23 : continue;
529 : }
530 42 : match_len = basedn_len;
531 :
532 42 : switch (base->scope) {
533 : case LDAP_SCOPE_BASE:
534 : /* dn > base? */
535 0 : if (len_diff != 0) {
536 0 : continue;
537 : }
538 0 : break;
539 : case LDAP_SCOPE_ONELEVEL:
540 0 : if (len_diff == 0) {
541 : /* Base object doesn't belong to scope=one
542 : * search */
543 0 : continue;
544 : }
545 :
546 0 : comma_found = false;
547 0 : for (j = 0; j < len_diff - 1; j++) { /* ignore comma before base */
548 0 : if (dn[j] == '\\') {
549 0 : backslash_found = true;
550 0 : } else if (dn[j] == ',' && !backslash_found) {
551 0 : comma_found = true;
552 0 : break;
553 : } else {
554 0 : backslash_found = false;
555 : }
556 : }
557 :
558 : /* it has at least one more level */
559 0 : if (comma_found) {
560 0 : continue;
561 : }
562 :
563 0 : break;
564 : case LDAP_SCOPE_SUBTREE:
565 : /* dn length >= base dn length && base_confirmed == true */
566 42 : break;
567 : default:
568 0 : DEBUG(SSSDBG_FUNC_DATA, "Unsupported scope: %d\n", base->scope);
569 0 : continue;
570 : }
571 :
572 : /*
573 : * If we get here, the dn is valid.
574 : * If no filter is set, than return true immediately.
575 : * Append filter otherwise.
576 : */
577 42 : ret = true;
578 42 : if (_match_len) {
579 25 : *_match_len = match_len;
580 : }
581 :
582 42 : if (base->filter == NULL || _filter == NULL) {
583 : goto done;
584 : } else {
585 0 : filter = talloc_strdup_append(filter, base->filter);
586 0 : if (filter == NULL) {
587 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup_append() failed\n");
588 0 : ret = false;
589 0 : goto done;
590 : }
591 : }
592 : }
593 :
594 22 : if (_filter != NULL) {
595 16 : if (filter != NULL) {
596 0 : *_filter = talloc_asprintf(mem_ctx, "(|%s)", filter);
597 0 : if (*_filter == NULL) {
598 0 : DEBUG(SSSDBG_CRIT_FAILURE,
599 : "talloc_asprintf_append() failed\n");
600 0 : ret = false;
601 0 : goto done;
602 : }
603 : } else {
604 16 : *_filter = NULL;
605 : }
606 : }
607 :
608 : done:
609 94 : talloc_free(filter);
610 94 : return ret;
611 : }
612 :
613 34 : bool sss_ldap_dn_in_search_bases(TALLOC_CTX *mem_ctx,
614 : const char *dn,
615 : struct sdap_search_base **search_bases,
616 : char **_filter)
617 : {
618 34 : return sss_ldap_dn_in_search_bases_len(mem_ctx, dn, search_bases, _filter,
619 : NULL);
620 : }
621 :
622 0 : char *sss_ldap_encode_ndr_uint32(TALLOC_CTX *mem_ctx, uint32_t flags)
623 : {
624 : char hex[9]; /* 4 bytes in hex + terminating zero */
625 : errno_t ret;
626 :
627 0 : ret = snprintf(hex, 9, "%08x", flags);
628 0 : if (ret != 8) {
629 0 : return NULL;
630 : }
631 :
632 0 : return talloc_asprintf(mem_ctx, "\\%c%c\\%c%c\\%c%c\\%c%c",
633 0 : hex[6], hex[7], hex[4], hex[5],
634 0 : hex[2], hex[3], hex[0], hex[1]);
635 : }
|