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 :
21 : #include "config.h"
22 : #include "util/util.h"
23 : #include "util/sss_sockets.h"
24 : #include "util/sss_ldap.h"
25 :
26 : #include "providers/ldap/sdap.h"
27 :
28 1 : const char* sss_ldap_err2string(int err)
29 : {
30 1 : if (IS_SSSD_ERROR(err)) {
31 0 : return sss_strerror(err);
32 : } else {
33 1 : return ldap_err2string(err);
34 : }
35 : }
36 :
37 0 : int sss_ldap_get_diagnostic_msg(TALLOC_CTX *mem_ctx, LDAP *ld, char **_errmsg)
38 : {
39 0 : char *errmsg = NULL;
40 : int optret;
41 :
42 0 : optret = ldap_get_option(ld, SDAP_DIAGNOSTIC_MESSAGE, (void*)&errmsg);
43 0 : if (optret != LDAP_SUCCESS) {
44 0 : return EINVAL;
45 : }
46 :
47 0 : *_errmsg = talloc_strdup(mem_ctx, errmsg ? errmsg : "unknown error");
48 0 : ldap_memfree(errmsg);
49 0 : if (*_errmsg == NULL) {
50 0 : return ENOMEM;
51 : }
52 0 : return EOK;
53 : }
54 :
55 0 : int sss_ldap_control_create(const char *oid, int iscritical,
56 : struct berval *value, int dupval,
57 : LDAPControl **ctrlp)
58 : {
59 : #ifdef HAVE_LDAP_CONTROL_CREATE
60 0 : return ldap_control_create(oid, iscritical, value, dupval, ctrlp);
61 : #else
62 : LDAPControl *lc = NULL;
63 :
64 : if (oid == NULL || ctrlp == NULL) {
65 : return LDAP_PARAM_ERROR;
66 : }
67 :
68 : lc = calloc(sizeof(LDAPControl), 1);
69 : if (lc == NULL) {
70 : return LDAP_NO_MEMORY;
71 : }
72 :
73 : lc->ldctl_oid = strdup(oid);
74 : if (lc->ldctl_oid == NULL) {
75 : free(lc);
76 : return LDAP_NO_MEMORY;
77 : }
78 :
79 : if (value != NULL && value->bv_val != NULL) {
80 : if (dupval == 0) {
81 : lc->ldctl_value = *value;
82 : } else {
83 : ber_dupbv(&lc->ldctl_value, value);
84 : if (lc->ldctl_value.bv_val == NULL) {
85 : free(lc->ldctl_oid);
86 : free(lc);
87 : return LDAP_NO_MEMORY;
88 : }
89 : }
90 : }
91 :
92 : lc->ldctl_iscritical = iscritical;
93 :
94 : *ctrlp = lc;
95 :
96 : return LDAP_SUCCESS;
97 : #endif
98 : }
99 :
100 : #ifdef HAVE_LDAP_INIT_FD
101 :
102 : #define LDAP_PROTO_TCP 1 /* ldap:// */
103 : #define LDAP_PROTO_UDP 2 /* reserved */
104 : #define LDAP_PROTO_IPC 3 /* ldapi:// */
105 : #define LDAP_PROTO_EXT 4 /* user-defined socket/sockbuf */
106 :
107 : extern int ldap_init_fd(ber_socket_t fd, int proto, const char *url, LDAP **ld);
108 :
109 : static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq);
110 : #endif
111 :
112 : struct sss_ldap_init_state {
113 : LDAP *ldap;
114 : int sd;
115 : const char *uri;
116 : };
117 :
118 0 : static int sss_ldap_init_state_destructor(void *data)
119 : {
120 0 : struct sss_ldap_init_state *state = (struct sss_ldap_init_state *)data;
121 :
122 0 : if (state->ldap) {
123 0 : DEBUG(SSSDBG_TRACE_FUNC,
124 : "calling ldap_unbind_ext for ldap:[%p] sd:[%d]\n",
125 : state->ldap, state->sd);
126 0 : ldap_unbind_ext(state->ldap, NULL, NULL);
127 : }
128 0 : if (state->sd != -1) {
129 0 : DEBUG(SSSDBG_TRACE_FUNC, "closing socket [%d]\n", state->sd);
130 0 : close(state->sd);
131 0 : state->sd = -1;
132 : }
133 :
134 0 : return 0;
135 : }
136 :
137 :
138 0 : struct tevent_req *sss_ldap_init_send(TALLOC_CTX *mem_ctx,
139 : struct tevent_context *ev,
140 : const char *uri,
141 : struct sockaddr_storage *addr,
142 : int addr_len, int timeout)
143 : {
144 0 : int ret = EOK;
145 : struct tevent_req *req;
146 : struct sss_ldap_init_state *state;
147 :
148 0 : req = tevent_req_create(mem_ctx, &state, struct sss_ldap_init_state);
149 0 : if (req == NULL) {
150 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_req_create failed.\n");
151 0 : return NULL;
152 : }
153 :
154 0 : talloc_set_destructor((TALLOC_CTX *)state, sss_ldap_init_state_destructor);
155 :
156 0 : state->ldap = NULL;
157 0 : state->sd = -1;
158 0 : state->uri = uri;
159 :
160 : #ifdef HAVE_LDAP_INIT_FD
161 : struct tevent_req *subreq;
162 :
163 0 : subreq = sssd_async_socket_init_send(state, ev, addr, addr_len, timeout);
164 0 : if (subreq == NULL) {
165 0 : ret = ENOMEM;
166 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sssd_async_socket_init_send failed.\n");
167 0 : goto fail;
168 : }
169 :
170 0 : tevent_req_set_callback(subreq, sss_ldap_init_sys_connect_done, req);
171 0 : return req;
172 :
173 : fail:
174 0 : tevent_req_error(req, ret);
175 : #else
176 : DEBUG(SSSDBG_MINOR_FAILURE, "ldap_init_fd not available, "
177 : "will use ldap_initialize with uri [%s].\n", uri);
178 : ret = ldap_initialize(&state->ldap, uri);
179 : if (ret == LDAP_SUCCESS) {
180 : tevent_req_done(req);
181 : } else {
182 : DEBUG(SSSDBG_CRIT_FAILURE,
183 : "ldap_initialize failed [%s].\n", sss_ldap_err2string(ret));
184 : if (ret == LDAP_SERVER_DOWN) {
185 : tevent_req_error(req, ETIMEDOUT);
186 : } else {
187 : tevent_req_error(req, EIO);
188 : }
189 : }
190 : #endif
191 :
192 0 : tevent_req_post(req, ev);
193 0 : return req;
194 : }
195 :
196 : #ifdef HAVE_LDAP_INIT_FD
197 0 : static void sss_ldap_init_sys_connect_done(struct tevent_req *subreq)
198 : {
199 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
200 : struct tevent_req);
201 0 : struct sss_ldap_init_state *state = tevent_req_data(req,
202 : struct sss_ldap_init_state);
203 : char *tlserr;
204 : int ret;
205 : int lret;
206 : int optret;
207 :
208 0 : ret = sssd_async_socket_init_recv(subreq, &state->sd);
209 0 : talloc_zfree(subreq);
210 0 : if (ret != EOK) {
211 0 : DEBUG(SSSDBG_CRIT_FAILURE,
212 : "sssd_async_socket_init request failed: [%d]: %s.\n",
213 : ret, sss_strerror(ret));
214 0 : goto fail;
215 : }
216 : /* Initialize LDAP handler */
217 :
218 0 : lret = ldap_init_fd(state->sd, LDAP_PROTO_TCP, state->uri, &state->ldap);
219 0 : if (lret != LDAP_SUCCESS) {
220 0 : DEBUG(SSSDBG_CRIT_FAILURE,
221 : "ldap_init_fd failed: %s. [%d][%s]\n",
222 : sss_ldap_err2string(lret), state->sd, state->uri);
223 0 : ret = lret == LDAP_SERVER_DOWN ? ETIMEDOUT : EIO;
224 0 : goto fail;
225 : }
226 :
227 0 : if (ldap_is_ldaps_url(state->uri)) {
228 0 : lret = ldap_install_tls(state->ldap);
229 0 : if (lret != LDAP_SUCCESS) {
230 0 : if (lret == LDAP_LOCAL_ERROR) {
231 0 : DEBUG(SSSDBG_FUNC_DATA, "TLS/SSL already in place.\n");
232 : } else {
233 :
234 0 : optret = sss_ldap_get_diagnostic_msg(state, state->ldap,
235 : &tlserr);
236 0 : if (optret == LDAP_SUCCESS) {
237 0 : DEBUG(SSSDBG_CRIT_FAILURE,
238 : "ldap_install_tls failed: [%s] [%s]\n",
239 : sss_ldap_err2string(lret), tlserr);
240 0 : sss_log(SSS_LOG_ERR,
241 : "Could not start TLS encryption. %s", tlserr);
242 : } else {
243 0 : DEBUG(SSSDBG_CRIT_FAILURE,
244 : "ldap_install_tls failed: [%s]\n",
245 : sss_ldap_err2string(lret));
246 0 : sss_log(SSS_LOG_ERR, "Could not start TLS encryption. "
247 : "Check for certificate issues.");
248 : }
249 :
250 0 : ret = EIO;
251 0 : goto fail;
252 : }
253 : }
254 : }
255 :
256 0 : tevent_req_done(req);
257 0 : return;
258 :
259 : fail:
260 0 : tevent_req_error(req, ret);
261 : }
262 : #endif
263 :
264 0 : int sss_ldap_init_recv(struct tevent_req *req, LDAP **ldap, int *sd)
265 : {
266 0 : struct sss_ldap_init_state *state = tevent_req_data(req,
267 : struct sss_ldap_init_state);
268 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
269 :
270 : /* Everything went well therefore we do not want to release resources */
271 0 : talloc_set_destructor(state, NULL);
272 :
273 0 : *ldap = state->ldap;
274 0 : *sd = state->sd;
275 :
276 0 : return EOK;
277 : }
278 :
279 : /*
280 : * _filter will contain combined filters from all possible search bases
281 : * or NULL if it should be empty
282 : */
283 :
284 :
285 103 : bool sss_ldap_dn_in_search_bases_len(TALLOC_CTX *mem_ctx,
286 : const char *dn,
287 : struct sdap_search_base **search_bases,
288 : char **_filter,
289 : int *_match_len)
290 : {
291 : struct sdap_search_base *base;
292 : int basedn_len, dn_len;
293 : int len_diff;
294 : int i, j;
295 103 : bool base_confirmed = false;
296 103 : bool comma_found = false;
297 103 : bool backslash_found = false;
298 103 : char *filter = NULL;
299 103 : bool ret = false;
300 : int match_len;
301 :
302 103 : if (dn == NULL) {
303 0 : DEBUG(SSSDBG_FUNC_DATA, "dn is NULL\n");
304 0 : ret = false;
305 0 : goto done;
306 : }
307 :
308 103 : if (search_bases == NULL) {
309 30 : DEBUG(SSSDBG_FUNC_DATA, "search_bases is NULL\n");
310 30 : ret = false;
311 30 : goto done;
312 : }
313 :
314 73 : dn_len = strlen(dn);
315 99 : for (i = 0; search_bases[i] != NULL; i++) {
316 74 : base = search_bases[i];
317 74 : basedn_len = strlen(base->basedn);
318 :
319 74 : if (basedn_len > dn_len) {
320 0 : continue;
321 : }
322 :
323 74 : len_diff = dn_len - basedn_len;
324 74 : base_confirmed = (strncasecmp(&dn[len_diff], base->basedn, basedn_len) == 0);
325 74 : if (!base_confirmed) {
326 26 : continue;
327 : }
328 48 : match_len = basedn_len;
329 :
330 48 : switch (base->scope) {
331 : case LDAP_SCOPE_BASE:
332 : /* dn > base? */
333 0 : if (len_diff != 0) {
334 0 : continue;
335 : }
336 0 : break;
337 : case LDAP_SCOPE_ONELEVEL:
338 0 : if (len_diff == 0) {
339 : /* Base object doesn't belong to scope=one
340 : * search */
341 0 : continue;
342 : }
343 :
344 0 : comma_found = false;
345 0 : for (j = 0; j < len_diff - 1; j++) { /* ignore comma before base */
346 0 : if (dn[j] == '\\') {
347 0 : backslash_found = true;
348 0 : } else if (dn[j] == ',' && !backslash_found) {
349 0 : comma_found = true;
350 0 : break;
351 : } else {
352 0 : backslash_found = false;
353 : }
354 : }
355 :
356 : /* it has at least one more level */
357 0 : if (comma_found) {
358 0 : continue;
359 : }
360 :
361 0 : break;
362 : case LDAP_SCOPE_SUBTREE:
363 : /* dn length >= base dn length && base_confirmed == true */
364 48 : break;
365 : default:
366 0 : DEBUG(SSSDBG_FUNC_DATA, "Unsupported scope: %d\n", base->scope);
367 0 : continue;
368 : }
369 :
370 : /*
371 : * If we get here, the dn is valid.
372 : * If no filter is set, than return true immediately.
373 : * Append filter otherwise.
374 : */
375 48 : ret = true;
376 48 : if (_match_len) {
377 28 : *_match_len = match_len;
378 : }
379 :
380 48 : if (base->filter == NULL || _filter == NULL) {
381 : goto done;
382 : } else {
383 0 : filter = talloc_strdup_append(filter, base->filter);
384 0 : if (filter == NULL) {
385 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup_append() failed\n");
386 0 : ret = false;
387 0 : goto done;
388 : }
389 : }
390 : }
391 :
392 25 : if (_filter != NULL) {
393 19 : if (filter != NULL) {
394 0 : *_filter = talloc_asprintf(mem_ctx, "(|%s)", filter);
395 0 : if (*_filter == NULL) {
396 0 : DEBUG(SSSDBG_CRIT_FAILURE,
397 : "talloc_asprintf_append() failed\n");
398 0 : ret = false;
399 0 : goto done;
400 : }
401 : } else {
402 19 : *_filter = NULL;
403 : }
404 : }
405 :
406 : done:
407 103 : talloc_free(filter);
408 103 : return ret;
409 : }
410 :
411 40 : bool sss_ldap_dn_in_search_bases(TALLOC_CTX *mem_ctx,
412 : const char *dn,
413 : struct sdap_search_base **search_bases,
414 : char **_filter)
415 : {
416 40 : return sss_ldap_dn_in_search_bases_len(mem_ctx, dn, search_bases, _filter,
417 : NULL);
418 : }
419 :
420 0 : char *sss_ldap_encode_ndr_uint32(TALLOC_CTX *mem_ctx, uint32_t flags)
421 : {
422 : char hex[9]; /* 4 bytes in hex + terminating zero */
423 : errno_t ret;
424 :
425 0 : ret = snprintf(hex, 9, "%08x", flags);
426 0 : if (ret != 8) {
427 0 : return NULL;
428 : }
429 :
430 0 : return talloc_asprintf(mem_ctx, "\\%c%c\\%c%c\\%c%c\\%c%c",
431 0 : hex[6], hex[7], hex[4], hex[5],
432 0 : hex[2], hex[3], hex[0], hex[1]);
433 : }
|