Line data Source code
1 : /*
2 : SSSD
3 :
4 : Async LDAP Helper routines
5 :
6 : Copyright (C) Simo Sorce <ssorce@redhat.com> - 2009
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 <ctype.h>
24 : #include "util/util.h"
25 : #include "util/strtonum.h"
26 : #include "util/probes.h"
27 : #include "providers/ldap/sdap_async_private.h"
28 :
29 : #define REPLY_REALLOC_INCREMENT 10
30 :
31 : /* ==LDAP-Memory-Handling================================================= */
32 :
33 0 : static int lmsg_destructor(void *mem)
34 : {
35 0 : ldap_msgfree((LDAPMessage *)mem);
36 0 : return 0;
37 : }
38 :
39 0 : static int sdap_msg_attach(TALLOC_CTX *memctx, LDAPMessage *msg)
40 : {
41 : void *h;
42 :
43 0 : if (!msg) return EINVAL;
44 :
45 0 : h = sss_mem_attach(memctx, msg, lmsg_destructor);
46 0 : if (!h) return ENOMEM;
47 :
48 0 : return EOK;
49 : }
50 :
51 : /* ==sdap-hanlde-utility-functions======================================== */
52 :
53 : static inline void sdap_handle_release(struct sdap_handle *sh);
54 : static int sdap_handle_destructor(void *mem);
55 :
56 0 : struct sdap_handle *sdap_handle_create(TALLOC_CTX *memctx)
57 : {
58 : struct sdap_handle *sh;
59 :
60 0 : sh = talloc_zero(memctx, struct sdap_handle);
61 0 : if (!sh) return NULL;
62 :
63 0 : talloc_set_destructor((TALLOC_CTX *)sh, sdap_handle_destructor);
64 :
65 0 : return sh;
66 : }
67 :
68 0 : static int sdap_handle_destructor(void *mem)
69 : {
70 0 : struct sdap_handle *sh = talloc_get_type(mem, struct sdap_handle);
71 :
72 : /* if the structure is currently locked, then mark it to be released
73 : * and prevent talloc from freeing the memory */
74 0 : if (sh->destructor_lock) {
75 0 : sh->release_memory = true;
76 0 : return -1;
77 : }
78 :
79 0 : sdap_handle_release(sh);
80 0 : return 0;
81 : }
82 :
83 0 : static void sdap_handle_release(struct sdap_handle *sh)
84 : {
85 : struct sdap_op *op;
86 :
87 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
88 : "Trace: sh[%p], connected[%d], ops[%p], ldap[%p], "
89 : "destructor_lock[%d], release_memory[%d]\n",
90 : sh, (int)sh->connected, sh->ops, sh->ldap,
91 : (int)sh->destructor_lock, (int)sh->release_memory);
92 :
93 0 : if (sh->destructor_lock) return;
94 0 : sh->destructor_lock = true;
95 :
96 : /* make sure nobody tries to reuse this connection from now on */
97 0 : sh->connected = false;
98 :
99 0 : remove_ldap_connection_callbacks(sh);
100 :
101 0 : while (sh->ops) {
102 0 : op = sh->ops;
103 0 : op->callback(op, NULL, EIO, op->data);
104 : /* calling the callback may result in freeing the op */
105 : /* check if it is still the same or avoid freeing */
106 0 : if (op == sh->ops) talloc_free(op);
107 : }
108 :
109 0 : if (sh->ldap) {
110 0 : ldap_unbind_ext(sh->ldap, NULL, NULL);
111 0 : sh->ldap = NULL;
112 : }
113 :
114 : /* ok, we have done the job, unlock now */
115 0 : sh->destructor_lock = false;
116 :
117 : /* finally if a destructor was ever called, free sh before
118 : * exiting */
119 0 : if (sh->release_memory) {
120 : /* neutralize the destructor as we already handled
121 : * all was needed to be released */
122 0 : talloc_set_destructor((TALLOC_CTX *)sh, NULL);
123 0 : talloc_free(sh);
124 : }
125 : }
126 :
127 : /* ==Parse-Results-And-Handle-Disconnections============================== */
128 : static void sdap_process_message(struct tevent_context *ev,
129 : struct sdap_handle *sh, LDAPMessage *msg);
130 : static void sdap_process_result(struct tevent_context *ev, void *pvt);
131 : static void sdap_process_next_reply(struct tevent_context *ev,
132 : struct tevent_timer *te,
133 : struct timeval tv, void *pvt);
134 :
135 0 : void sdap_ldap_result(struct tevent_context *ev, struct tevent_fd *fde,
136 : uint16_t flags, void *pvt)
137 : {
138 0 : sdap_process_result(ev, pvt);
139 0 : }
140 :
141 0 : static void sdap_ldap_next_result(struct tevent_context *ev,
142 : struct tevent_timer *te,
143 : struct timeval tv, void *pvt)
144 : {
145 0 : sdap_process_result(ev, pvt);
146 0 : }
147 :
148 0 : static void sdap_process_result(struct tevent_context *ev, void *pvt)
149 : {
150 0 : struct sdap_handle *sh = talloc_get_type(pvt, struct sdap_handle);
151 0 : struct timeval no_timeout = {0, 0};
152 : struct tevent_timer *te;
153 : LDAPMessage *msg;
154 : int ret;
155 :
156 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
157 : "Trace: sh[%p], connected[%d], ops[%p], ldap[%p]\n",
158 : sh, (int)sh->connected, sh->ops, sh->ldap);
159 :
160 0 : if (!sh->connected || !sh->ldap) {
161 0 : DEBUG(SSSDBG_OP_FAILURE, "ERROR: LDAP connection is not connected!\n");
162 0 : sdap_handle_release(sh);
163 0 : return;
164 : }
165 :
166 0 : ret = ldap_result(sh->ldap, LDAP_RES_ANY, 0, &no_timeout, &msg);
167 0 : if (ret == 0) {
168 : /* this almost always means we have reached the end of
169 : * the list of received messages */
170 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Trace: ldap_result found nothing!\n");
171 0 : return;
172 : }
173 :
174 0 : if (ret == -1) {
175 0 : ldap_get_option(sh->ldap, LDAP_OPT_RESULT_CODE, &ret);
176 0 : DEBUG(SSSDBG_OP_FAILURE,
177 : "ldap_result error: [%s]\n", ldap_err2string(ret));
178 0 : sdap_handle_release(sh);
179 0 : return;
180 : }
181 :
182 : /* We don't know if this will be the last result.
183 : *
184 : * important: we must do this before actually processing the message
185 : * because the message processing might even free the sdap_handler
186 : * so it must be the last operation.
187 : * FIXME: use tevent_immediate/tevent_queues, when avilable */
188 0 : memset(&no_timeout, 0, sizeof(struct timeval));
189 :
190 0 : te = tevent_add_timer(ev, sh, no_timeout, sdap_ldap_next_result, sh);
191 0 : if (!te) {
192 0 : DEBUG(SSSDBG_CRIT_FAILURE,
193 : "Failed to add critical timer to fetch next result!\n");
194 : }
195 :
196 : /* now process this message */
197 0 : sdap_process_message(ev, sh, msg);
198 : }
199 :
200 0 : static const char *sdap_ldap_result_str(int msgtype)
201 : {
202 0 : switch (msgtype) {
203 : case LDAP_RES_BIND:
204 0 : return "LDAP_RES_BIND";
205 :
206 : case LDAP_RES_SEARCH_ENTRY:
207 0 : return "LDAP_RES_SEARCH_ENTRY";
208 :
209 : case LDAP_RES_SEARCH_REFERENCE:
210 0 : return "LDAP_RES_SEARCH_REFERENCE";
211 :
212 : case LDAP_RES_SEARCH_RESULT:
213 0 : return "LDAP_RES_SEARCH_RESULT";
214 :
215 : case LDAP_RES_MODIFY:
216 0 : return "LDAP_RES_MODIFY";
217 :
218 : case LDAP_RES_ADD:
219 0 : return "LDAP_RES_ADD";
220 :
221 : case LDAP_RES_DELETE:
222 0 : return "LDAP_RES_DELETE";
223 :
224 : case LDAP_RES_MODDN:
225 : /* These are the same result
226 : case LDAP_RES_MODRDN:
227 : case LDAP_RES_RENAME:
228 : */
229 0 : return "LDAP_RES_RENAME";
230 :
231 : case LDAP_RES_COMPARE:
232 0 : return "LDAP_RES_COMPARE";
233 :
234 : case LDAP_RES_EXTENDED:
235 0 : return "LDAP_RES_EXTENDED";
236 :
237 : case LDAP_RES_INTERMEDIATE:
238 0 : return "LDAP_RES_INTERMEDIATE";
239 :
240 : case LDAP_RES_ANY:
241 0 : return "LDAP_RES_ANY";
242 :
243 : case LDAP_RES_UNSOLICITED:
244 0 : return "LDAP_RES_UNSOLICITED";
245 :
246 : default:
247 : /* Unmatched, fall through */
248 0 : break;
249 : }
250 :
251 : /* Unknown result type */
252 0 : return "Unknown result type!";
253 : }
254 :
255 : /* process a messgae calling the right operation callback.
256 : * msg is completely taken care of (including freeeing it)
257 : * NOTE: this function may even end up freeing the sdap_handle
258 : * so sdap_hanbdle must not be used after this function is called
259 : */
260 0 : static void sdap_process_message(struct tevent_context *ev,
261 : struct sdap_handle *sh, LDAPMessage *msg)
262 : {
263 : struct sdap_msg *reply;
264 : struct sdap_op *op;
265 : int msgid;
266 : int msgtype;
267 : int ret;
268 :
269 0 : msgid = ldap_msgid(msg);
270 0 : if (msgid == -1) {
271 0 : DEBUG(SSSDBG_OP_FAILURE, "can't fire callback, message id invalid!\n");
272 0 : ldap_msgfree(msg);
273 0 : return;
274 : }
275 :
276 0 : msgtype = ldap_msgtype(msg);
277 :
278 0 : for (op = sh->ops; op; op = op->next) {
279 0 : if (op->msgid == msgid) break;
280 : }
281 :
282 0 : if (op == NULL) {
283 0 : DEBUG(SSSDBG_OP_FAILURE,
284 : "Unmatched msgid, discarding message (type: %0x)\n",
285 : msgtype);
286 0 : ldap_msgfree(msg);
287 0 : return;
288 : }
289 :
290 : /* shouldn't happen */
291 0 : if (op->done) {
292 0 : DEBUG(SSSDBG_OP_FAILURE,
293 : "Operation [%p] already handled (type: %0x)\n", op, msgtype);
294 0 : ldap_msgfree(msg);
295 0 : return;
296 : }
297 :
298 0 : DEBUG(SSSDBG_TRACE_ALL,
299 : "Message type: [%s]\n", sdap_ldap_result_str(msgtype));
300 :
301 0 : switch (msgtype) {
302 : case LDAP_RES_SEARCH_ENTRY:
303 : case LDAP_RES_SEARCH_REFERENCE:
304 : /* go and process entry */
305 0 : break;
306 :
307 : case LDAP_RES_BIND:
308 : case LDAP_RES_SEARCH_RESULT:
309 : case LDAP_RES_MODIFY:
310 : case LDAP_RES_ADD:
311 : case LDAP_RES_DELETE:
312 : case LDAP_RES_MODDN:
313 : case LDAP_RES_COMPARE:
314 : case LDAP_RES_EXTENDED:
315 : case LDAP_RES_INTERMEDIATE:
316 : /* no more results expected with this msgid */
317 0 : op->done = true;
318 0 : break;
319 :
320 : default:
321 : /* unkwon msg type ?? */
322 0 : DEBUG(SSSDBG_CRIT_FAILURE,
323 : "Couldn't figure out the msg type! [%0x]\n", msgtype);
324 0 : ldap_msgfree(msg);
325 0 : return;
326 : }
327 :
328 0 : reply = talloc_zero(op, struct sdap_msg);
329 0 : if (!reply) {
330 0 : ldap_msgfree(msg);
331 0 : ret = ENOMEM;
332 : } else {
333 0 : reply->msg = msg;
334 0 : ret = sdap_msg_attach(reply, msg);
335 0 : if (ret != EOK) {
336 0 : ldap_msgfree(msg);
337 0 : talloc_zfree(reply);
338 : }
339 : }
340 :
341 0 : if (op->list) {
342 : /* list exist, queue it */
343 :
344 0 : op->last->next = reply;
345 0 : op->last = reply;
346 :
347 : } else {
348 : /* create list, then call callback */
349 0 : op->list = op->last = reply;
350 :
351 : /* must be the last operation as it may end up freeing all memory
352 : * including all ops handlers */
353 0 : op->callback(op, reply, ret, op->data);
354 : }
355 : }
356 :
357 0 : static void sdap_unlock_next_reply(struct sdap_op *op)
358 : {
359 : struct timeval tv;
360 : struct tevent_timer *te;
361 : struct sdap_msg *next_reply;
362 :
363 0 : if (op->list) {
364 0 : next_reply = op->list->next;
365 : /* get rid of the previous reply, it has been processed already */
366 0 : talloc_zfree(op->list);
367 0 : op->list = next_reply;
368 : }
369 :
370 : /* if there are still replies to parse, queue a new operation */
371 0 : if (op->list) {
372 : /* use a very small timeout, so that fd operations have a chance to be
373 : * served while processing a long reply */
374 0 : tv = tevent_timeval_current();
375 :
376 : /* wait 5 microsecond */
377 0 : tv.tv_usec += 5;
378 0 : tv.tv_sec += tv.tv_usec / 1000000;
379 0 : tv.tv_usec = tv.tv_usec % 1000000;
380 :
381 0 : te = tevent_add_timer(op->ev, op, tv,
382 : sdap_process_next_reply, op);
383 0 : if (!te) {
384 0 : DEBUG(SSSDBG_CRIT_FAILURE,
385 : "Failed to add critical timer for next reply!\n");
386 0 : op->callback(op, NULL, EFAULT, op->data);
387 : }
388 : }
389 0 : }
390 :
391 0 : static void sdap_process_next_reply(struct tevent_context *ev,
392 : struct tevent_timer *te,
393 : struct timeval tv, void *pvt)
394 : {
395 0 : struct sdap_op *op = talloc_get_type(pvt, struct sdap_op);
396 :
397 0 : op->callback(op, op->list, EOK, op->data);
398 0 : }
399 :
400 : /* ==LDAP-Operations-Helpers============================================== */
401 :
402 0 : static int sdap_op_destructor(void *mem)
403 : {
404 0 : struct sdap_op *op = (struct sdap_op *)mem;
405 :
406 0 : DLIST_REMOVE(op->sh->ops, op);
407 :
408 0 : if (op->done) {
409 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Operation %d finished\n", op->msgid);
410 0 : return 0;
411 : }
412 :
413 : /* we don't check the result here, if a message was really abandoned,
414 : * hopefully the server will get an abandon.
415 : * If the operation was already fully completed, this is going to be
416 : * just a noop */
417 0 : DEBUG(SSSDBG_TRACE_LIBS, "Abandoning operation %d\n", op->msgid);
418 0 : ldap_abandon_ext(op->sh->ldap, op->msgid, NULL, NULL);
419 :
420 0 : return 0;
421 : }
422 :
423 0 : static void sdap_op_timeout(struct tevent_req *req)
424 : {
425 0 : struct sdap_op *op = tevent_req_callback_data(req, struct sdap_op);
426 :
427 : /* should never happen, but just in case */
428 0 : if (op->done) {
429 0 : DEBUG(SSSDBG_OP_FAILURE, "Timeout happened after op was finished !?\n");
430 0 : return;
431 : }
432 :
433 : /* signal the caller that we have a timeout */
434 0 : DEBUG(SSSDBG_TRACE_LIBS, "Issuing timeout for %d\n", op->msgid);
435 0 : op->callback(op, NULL, ETIMEDOUT, op->data);
436 : }
437 :
438 0 : int sdap_op_add(TALLOC_CTX *memctx, struct tevent_context *ev,
439 : struct sdap_handle *sh, int msgid,
440 : sdap_op_callback_t *callback, void *data,
441 : int timeout, struct sdap_op **_op)
442 : {
443 : struct sdap_op *op;
444 :
445 0 : op = talloc_zero(memctx, struct sdap_op);
446 0 : if (!op) return ENOMEM;
447 :
448 0 : op->sh = sh;
449 0 : op->msgid = msgid;
450 0 : op->callback = callback;
451 0 : op->data = data;
452 0 : op->ev = ev;
453 :
454 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
455 : "New operation %d timeout %d\n", op->msgid, timeout);
456 :
457 : /* check if we need to set a timeout */
458 0 : if (timeout) {
459 : struct tevent_req *req;
460 : struct timeval tv;
461 :
462 0 : tv = tevent_timeval_current();
463 0 : tv = tevent_timeval_add(&tv, timeout, 0);
464 :
465 : /* allocate on op, so when it get freed the timeout is removed */
466 0 : req = tevent_wakeup_send(op, ev, tv);
467 0 : if (!req) {
468 0 : talloc_zfree(op);
469 0 : return ENOMEM;
470 : }
471 0 : tevent_req_set_callback(req, sdap_op_timeout, op);
472 : }
473 :
474 0 : DLIST_ADD(sh->ops, op);
475 :
476 0 : talloc_set_destructor((TALLOC_CTX *)op, sdap_op_destructor);
477 :
478 0 : *_op = op;
479 0 : return EOK;
480 : }
481 :
482 : /* ==Modify-Password====================================================== */
483 :
484 : struct sdap_exop_modify_passwd_state {
485 : struct sdap_handle *sh;
486 :
487 : struct sdap_op *op;
488 :
489 : char *user_error_message;
490 : };
491 :
492 : static void sdap_exop_modify_passwd_done(struct sdap_op *op,
493 : struct sdap_msg *reply,
494 : int error, void *pvt);
495 :
496 0 : struct tevent_req *sdap_exop_modify_passwd_send(TALLOC_CTX *memctx,
497 : struct tevent_context *ev,
498 : struct sdap_handle *sh,
499 : char *user_dn,
500 : const char *password,
501 : const char *new_password,
502 : int timeout)
503 : {
504 0 : struct tevent_req *req = NULL;
505 : struct sdap_exop_modify_passwd_state *state;
506 : int ret;
507 0 : BerElement *ber = NULL;
508 0 : struct berval *bv = NULL;
509 : int msgid;
510 0 : LDAPControl **request_controls = NULL;
511 0 : LDAPControl *ctrls[2] = { NULL, NULL };
512 :
513 0 : req = tevent_req_create(memctx, &state,
514 : struct sdap_exop_modify_passwd_state);
515 0 : if (!req) return NULL;
516 :
517 0 : state->sh = sh;
518 0 : state->user_error_message = NULL;
519 :
520 0 : ber = ber_alloc_t( LBER_USE_DER );
521 0 : if (ber == NULL) {
522 0 : DEBUG(SSSDBG_TRACE_LIBS, "ber_alloc_t failed.\n");
523 0 : talloc_zfree(req);
524 0 : return NULL;
525 : }
526 :
527 0 : ret = ber_printf( ber, "{tststs}", LDAP_TAG_EXOP_MODIFY_PASSWD_ID,
528 : user_dn,
529 : LDAP_TAG_EXOP_MODIFY_PASSWD_OLD, password,
530 : LDAP_TAG_EXOP_MODIFY_PASSWD_NEW, new_password);
531 0 : if (ret == -1) {
532 0 : DEBUG(SSSDBG_CRIT_FAILURE, "ber_printf failed.\n");
533 0 : ber_free(ber, 1);
534 0 : talloc_zfree(req);
535 0 : return NULL;
536 : }
537 :
538 0 : ret = ber_flatten(ber, &bv);
539 0 : ber_free(ber, 1);
540 0 : if (ret == -1) {
541 0 : DEBUG(SSSDBG_CRIT_FAILURE, "ber_flatten failed.\n");
542 0 : talloc_zfree(req);
543 0 : return NULL;
544 : }
545 :
546 0 : ret = sdap_control_create(state->sh, LDAP_CONTROL_PASSWORDPOLICYREQUEST,
547 : 0, NULL, 0, &ctrls[0]);
548 0 : if (ret != LDAP_SUCCESS && ret != LDAP_NOT_SUPPORTED) {
549 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sdap_control_create failed to create "
550 : "Password Policy control.\n");
551 0 : ret = ERR_INTERNAL;
552 0 : goto fail;
553 : }
554 0 : request_controls = ctrls;
555 :
556 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Executing extended operation\n");
557 :
558 0 : ret = ldap_extended_operation(state->sh->ldap, LDAP_EXOP_MODIFY_PASSWD,
559 : bv, request_controls, NULL, &msgid);
560 0 : ber_bvfree(bv);
561 0 : if (ctrls[0]) ldap_control_free(ctrls[0]);
562 0 : if (ret == -1 || msgid == -1) {
563 0 : DEBUG(SSSDBG_CRIT_FAILURE, "ldap_extended_operation failed\n");
564 0 : ret = ERR_NETWORK_IO;
565 0 : goto fail;
566 : }
567 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
568 : "ldap_extended_operation sent, msgid = %d\n", msgid);
569 :
570 0 : ret = sdap_op_add(state, ev, state->sh, msgid,
571 0 : sdap_exop_modify_passwd_done, req, timeout, &state->op);
572 0 : if (ret) {
573 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set up operation!\n");
574 0 : ret = ERR_INTERNAL;
575 0 : goto fail;
576 : }
577 :
578 0 : return req;
579 :
580 : fail:
581 0 : tevent_req_error(req, ret);
582 0 : tevent_req_post(req, ev);
583 0 : return req;
584 : }
585 :
586 0 : static void sdap_exop_modify_passwd_done(struct sdap_op *op,
587 : struct sdap_msg *reply,
588 : int error, void *pvt)
589 : {
590 0 : struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
591 0 : struct sdap_exop_modify_passwd_state *state = tevent_req_data(req,
592 : struct sdap_exop_modify_passwd_state);
593 0 : char *errmsg = NULL;
594 : int ret;
595 0 : LDAPControl **response_controls = NULL;
596 : int c;
597 : ber_int_t pp_grace;
598 : ber_int_t pp_expire;
599 : LDAPPasswordPolicyError pp_error;
600 : int result;
601 :
602 0 : if (error) {
603 0 : tevent_req_error(req, error);
604 0 : return;
605 : }
606 :
607 0 : ret = ldap_parse_result(state->sh->ldap, reply->msg,
608 : &result, NULL, &errmsg, NULL,
609 : &response_controls, 0);
610 0 : if (ret != LDAP_SUCCESS) {
611 0 : DEBUG(SSSDBG_OP_FAILURE,
612 : "ldap_parse_result failed (%d)\n", state->op->msgid);
613 0 : ret = ERR_INTERNAL;
614 0 : goto done;
615 : }
616 :
617 0 : if (response_controls == NULL) {
618 0 : DEBUG(SSSDBG_FUNC_DATA, "Server returned no controls.\n");
619 : } else {
620 0 : for (c = 0; response_controls[c] != NULL; c++) {
621 0 : DEBUG(SSSDBG_TRACE_ALL, "Server returned control [%s].\n",
622 : response_controls[c]->ldctl_oid);
623 0 : if (strcmp(response_controls[c]->ldctl_oid,
624 : LDAP_CONTROL_PASSWORDPOLICYRESPONSE) == 0) {
625 0 : ret = ldap_parse_passwordpolicy_control(state->sh->ldap,
626 0 : response_controls[c],
627 : &pp_expire, &pp_grace,
628 : &pp_error);
629 0 : if (ret != LDAP_SUCCESS) {
630 0 : DEBUG(SSSDBG_CRIT_FAILURE,
631 : "ldap_parse_passwordpolicy_control failed.\n");
632 0 : ret = ERR_NETWORK_IO;
633 0 : goto done;
634 : }
635 :
636 0 : DEBUG(SSSDBG_TRACE_LIBS,
637 : "Password Policy Response: expire [%d] grace [%d] "
638 : "error [%s].\n", pp_expire, pp_grace,
639 : ldap_passwordpolicy_err2txt(pp_error));
640 : }
641 : }
642 : }
643 :
644 0 : DEBUG(SSSDBG_MINOR_FAILURE, "ldap_extended_operation result: %s(%d), %s\n",
645 : sss_ldap_err2string(result), result, errmsg);
646 :
647 0 : switch (result) {
648 : case LDAP_SUCCESS:
649 0 : ret = EOK;
650 0 : break;
651 : case LDAP_CONSTRAINT_VIOLATION:
652 0 : if (errmsg && strlen(errmsg) != 0) {
653 0 : state->user_error_message = talloc_strdup(state, errmsg);
654 : } else {
655 0 : state->user_error_message = talloc_strdup(state,
656 : "Please make sure the password meets the "
657 : "complexity constraints.");
658 : }
659 :
660 0 : if (state->user_error_message == NULL) {
661 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed\n");
662 0 : ret = ENOMEM;
663 0 : goto done;
664 : }
665 :
666 0 : ret = ERR_CHPASS_DENIED;
667 0 : break;
668 : default:
669 0 : if (errmsg) {
670 0 : state->user_error_message = talloc_strdup(state, errmsg);
671 0 : if (state->user_error_message == NULL) {
672 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup failed.\n");
673 0 : ret = ENOMEM;
674 0 : goto done;
675 : }
676 : }
677 0 : ret = ERR_NETWORK_IO;
678 0 : break;
679 : }
680 :
681 : done:
682 0 : ldap_controls_free(response_controls);
683 0 : ldap_memfree(errmsg);
684 :
685 0 : if (ret == EOK) {
686 0 : tevent_req_done(req);
687 : } else {
688 0 : tevent_req_error(req, ret);
689 : }
690 : }
691 :
692 0 : errno_t sdap_exop_modify_passwd_recv(struct tevent_req *req,
693 : TALLOC_CTX * mem_ctx,
694 : char **user_error_message)
695 : {
696 0 : struct sdap_exop_modify_passwd_state *state = tevent_req_data(req,
697 : struct sdap_exop_modify_passwd_state);
698 :
699 0 : *user_error_message = talloc_steal(mem_ctx, state->user_error_message);
700 :
701 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
702 :
703 0 : return EOK;
704 : }
705 :
706 : /* ==Update-passwordLastChanged-attribute====================== */
707 : struct update_last_changed_state {
708 : struct tevent_context *ev;
709 : struct sdap_handle *sh;
710 : struct sdap_op *op;
711 :
712 : const char *dn;
713 : LDAPMod **mods;
714 : };
715 :
716 : static void sdap_modify_shadow_lastchange_done(struct sdap_op *op,
717 : struct sdap_msg *reply,
718 : int error, void *pvt);
719 :
720 : struct tevent_req *
721 0 : sdap_modify_shadow_lastchange_send(TALLOC_CTX *mem_ctx,
722 : struct tevent_context *ev,
723 : struct sdap_handle *sh,
724 : const char *dn,
725 : char *lastchanged_name)
726 : {
727 : struct tevent_req *req;
728 : struct update_last_changed_state *state;
729 : char **values;
730 : errno_t ret;
731 : int msgid;
732 :
733 0 : req = tevent_req_create(mem_ctx, &state, struct update_last_changed_state);
734 0 : if (req == NULL) {
735 0 : return NULL;
736 : }
737 :
738 0 : state->ev = ev;
739 0 : state->sh = sh;
740 0 : state->dn = dn;
741 0 : state->mods = talloc_zero_array(state, LDAPMod *, 2);
742 0 : if (state->mods == NULL) {
743 0 : ret = ENOMEM;
744 0 : goto done;
745 : }
746 0 : state->mods[0] = talloc_zero(state->mods, LDAPMod);
747 0 : state->mods[1] = talloc_zero(state->mods, LDAPMod);
748 0 : if (!state->mods[0] || !state->mods[1]) {
749 0 : ret = ENOMEM;
750 0 : goto done;
751 : }
752 0 : values = talloc_zero_array(state->mods[0], char *, 2);
753 0 : if (values == NULL) {
754 0 : ret = ENOMEM;
755 0 : goto done;
756 : }
757 : /* The attribute contains number of days since the epoch */
758 0 : values[0] = talloc_asprintf(values, "%ld", (long)time(NULL)/86400);
759 0 : if (values[0] == NULL) {
760 0 : ret = ENOMEM;
761 0 : goto done;
762 : }
763 0 : state->mods[0]->mod_op = LDAP_MOD_REPLACE;
764 0 : state->mods[0]->mod_type = lastchanged_name;
765 0 : state->mods[0]->mod_vals.modv_strvals = values;
766 0 : state->mods[1] = NULL;
767 :
768 0 : ret = ldap_modify_ext(state->sh->ldap, state->dn, state->mods,
769 : NULL, NULL, &msgid);
770 0 : if (ret) {
771 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to send operation!\n");
772 0 : goto done;
773 : }
774 :
775 0 : ret = sdap_op_add(state, state->ev, state->sh, msgid,
776 0 : sdap_modify_shadow_lastchange_done, req, 5, &state->op);
777 0 : if (ret) {
778 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set up operation!\n");
779 0 : goto done;
780 : }
781 :
782 : done:
783 0 : if (ret != EOK) {
784 0 : tevent_req_error(req, ret);
785 0 : tevent_req_post(req, ev);
786 : }
787 0 : return req;
788 : }
789 :
790 0 : static void sdap_modify_shadow_lastchange_done(struct sdap_op *op,
791 : struct sdap_msg *reply,
792 : int error, void *pvt)
793 : {
794 0 : struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
795 : struct update_last_changed_state *state;
796 0 : state = tevent_req_data(req, struct update_last_changed_state);
797 : char *errmsg;
798 : int result;
799 0 : errno_t ret = EOK;
800 : int lret;
801 :
802 0 : if (error) {
803 0 : tevent_req_error(req, error);
804 0 : return;
805 : }
806 :
807 0 : lret = ldap_parse_result(state->sh->ldap, reply->msg,
808 : &result, NULL, &errmsg, NULL,
809 : NULL, 0);
810 0 : if (lret != LDAP_SUCCESS) {
811 0 : DEBUG(SSSDBG_OP_FAILURE, "ldap_parse_result failed (%d)\n",
812 : state->op->msgid);
813 0 : ret = EIO;
814 0 : goto done;
815 : }
816 :
817 0 : DEBUG(SSSDBG_TRACE_LIBS, "Updating lastPwdChange result: %s(%d), %s\n",
818 : sss_ldap_err2string(result),
819 : result, errmsg);
820 :
821 : done:
822 0 : ldap_memfree(errmsg);
823 :
824 0 : if (ret == EOK) {
825 0 : tevent_req_done(req);
826 : } else {
827 0 : tevent_req_error(req, ret);
828 : }
829 : }
830 :
831 0 : errno_t sdap_modify_shadow_lastchange_recv(struct tevent_req *req)
832 : {
833 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
834 :
835 0 : return EOK;
836 : }
837 :
838 :
839 : /* ==Fetch-RootDSE============================================= */
840 :
841 : struct sdap_get_rootdse_state {
842 : struct tevent_context *ev;
843 : struct sdap_options *opts;
844 : struct sdap_handle *sh;
845 :
846 : struct sysdb_attrs *rootdse;
847 : };
848 :
849 : static void sdap_get_rootdse_done(struct tevent_req *subreq);
850 : static void sdap_get_matching_rule_done(struct tevent_req *subreq);
851 :
852 0 : struct tevent_req *sdap_get_rootdse_send(TALLOC_CTX *memctx,
853 : struct tevent_context *ev,
854 : struct sdap_options *opts,
855 : struct sdap_handle *sh)
856 : {
857 : struct tevent_req *req, *subreq;
858 : struct sdap_get_rootdse_state *state;
859 0 : const char *attrs[] = {
860 : "*",
861 : "altServer",
862 : SDAP_ROOTDSE_ATTR_NAMING_CONTEXTS,
863 : "supportedControl",
864 : "supportedExtension",
865 : "supportedFeatures",
866 : "supportedLDAPVersion",
867 : "supportedSASLMechanisms",
868 : SDAP_ROOTDSE_ATTR_AD_VERSION,
869 : SDAP_ROOTDSE_ATTR_DEFAULT_NAMING_CONTEXT,
870 : SDAP_IPA_LAST_USN, SDAP_AD_LAST_USN,
871 : NULL
872 : };
873 :
874 0 : DEBUG(SSSDBG_TRACE_ALL, "Getting rootdse\n");
875 :
876 0 : req = tevent_req_create(memctx, &state, struct sdap_get_rootdse_state);
877 0 : if (!req) return NULL;
878 :
879 0 : state->ev = ev;
880 0 : state->opts = opts;
881 0 : state->sh = sh;
882 0 : state->rootdse = NULL;
883 :
884 0 : subreq = sdap_get_generic_send(state, ev, opts, sh,
885 : "", LDAP_SCOPE_BASE,
886 : "(objectclass=*)", attrs, NULL, 0,
887 0 : dp_opt_get_int(state->opts->basic,
888 : SDAP_SEARCH_TIMEOUT),
889 : false);
890 0 : if (!subreq) {
891 0 : talloc_zfree(req);
892 0 : return NULL;
893 : }
894 0 : tevent_req_set_callback(subreq, sdap_get_rootdse_done, req);
895 :
896 0 : return req;
897 : }
898 :
899 : /* This is not a real attribute, it's just there to avoid
900 : * actually pulling real data down, to save bandwidth
901 : */
902 : #define SDAP_MATCHING_RULE_TEST_ATTR "sssmatchingruletest"
903 :
904 0 : static void sdap_get_rootdse_done(struct tevent_req *subreq)
905 : {
906 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
907 : struct tevent_req);
908 0 : struct sdap_get_rootdse_state *state = tevent_req_data(req,
909 : struct sdap_get_rootdse_state);
910 : struct sysdb_attrs **results;
911 : size_t num_results;
912 : int ret;
913 : const char *filter;
914 0 : const char *attrs[] = { SDAP_MATCHING_RULE_TEST_ATTR, NULL };
915 :
916 0 : ret = sdap_get_generic_recv(subreq, state, &num_results, &results);
917 0 : talloc_zfree(subreq);
918 0 : if (ret) {
919 0 : tevent_req_error(req, ret);
920 0 : return;
921 : }
922 :
923 0 : if (num_results == 0 || !results) {
924 0 : DEBUG(SSSDBG_OP_FAILURE, "RootDSE could not be retrieved. "
925 : "Please check that anonymous access to RootDSE is allowed\n"
926 : );
927 0 : tevent_req_error(req, ENOENT);
928 0 : return;
929 : }
930 :
931 0 : if (num_results > 1) {
932 0 : DEBUG(SSSDBG_OP_FAILURE,
933 : "Multiple replies when searching for RootDSE ??\n");
934 0 : tevent_req_error(req, EIO);
935 0 : return;
936 : }
937 :
938 0 : state->rootdse = talloc_steal(state, results[0]);
939 0 : talloc_zfree(results);
940 :
941 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Got rootdse\n");
942 :
943 : /* Auto-detect the ldap matching rule if requested */
944 0 : if ((!dp_opt_get_bool(state->opts->basic,
945 : SDAP_AD_MATCHING_RULE_INITGROUPS))
946 0 : && !dp_opt_get_bool(state->opts->basic,
947 : SDAP_AD_MATCHING_RULE_GROUPS)) {
948 : /* This feature is disabled for both groups
949 : * and initgroups. Skip the auto-detection
950 : * lookup.
951 : */
952 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
953 : "Skipping auto-detection of match rule\n");
954 0 : tevent_req_done(req);
955 0 : return;
956 : }
957 :
958 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
959 : "Auto-detecting support for match rule\n");
960 :
961 : /* Create a filter using the matching rule. It need not point
962 : * at any valid data. We're only going to be looking for the
963 : * error code.
964 : */
965 0 : filter = "("SDAP_MATCHING_RULE_TEST_ATTR":"
966 : SDAP_MATCHING_RULE_IN_CHAIN":=)";
967 :
968 : /* Perform a trivial query with the matching rule in play.
969 : * If it returns success, we know it is available. If it
970 : * returns EIO, we know it isn't.
971 : */
972 0 : subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
973 : "", LDAP_SCOPE_BASE, filter, attrs, NULL,
974 0 : 0, dp_opt_get_int(state->opts->basic,
975 : SDAP_SEARCH_TIMEOUT),
976 : false);
977 0 : if (!subreq) {
978 0 : tevent_req_error(req, ENOMEM);
979 0 : return;
980 : }
981 0 : tevent_req_set_callback(subreq, sdap_get_matching_rule_done, req);
982 : }
983 :
984 0 : static void sdap_get_matching_rule_done(struct tevent_req *subreq)
985 : {
986 : errno_t ret;
987 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
988 : struct tevent_req);
989 0 : struct sdap_get_rootdse_state *state = tevent_req_data(req,
990 : struct sdap_get_rootdse_state);
991 : size_t num_results;
992 : struct sysdb_attrs **results;
993 :
994 0 : ret = sdap_get_generic_recv(subreq, state, &num_results, &results);
995 0 : talloc_zfree(subreq);
996 0 : if (ret == EOK) {
997 : /* The search succeeded */
998 0 : state->opts->support_matching_rule = true;
999 0 : } else if (ret == EIO) {
1000 : /* The search failed. Disable support for
1001 : * matching rule lookups.
1002 : */
1003 0 : state->opts->support_matching_rule = false;
1004 : } else {
1005 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1006 : "Unexpected error while testing for matching rule support\n");
1007 0 : tevent_req_error(req, ret);
1008 0 : return;
1009 : }
1010 :
1011 0 : DEBUG(SSSDBG_CONF_SETTINGS,
1012 : "LDAP server %s the matching rule extension\n",
1013 : state->opts->support_matching_rule
1014 : ? "supports"
1015 : : "does not support");
1016 :
1017 0 : tevent_req_done(req);
1018 : }
1019 :
1020 0 : int sdap_get_rootdse_recv(struct tevent_req *req,
1021 : TALLOC_CTX *memctx,
1022 : struct sysdb_attrs **rootdse)
1023 : {
1024 0 : struct sdap_get_rootdse_state *state = tevent_req_data(req,
1025 : struct sdap_get_rootdse_state);
1026 :
1027 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1028 :
1029 0 : *rootdse = talloc_steal(memctx, state->rootdse);
1030 :
1031 0 : return EOK;
1032 : }
1033 :
1034 : /* ==Helpers for parsing replies============================== */
1035 : struct sdap_reply {
1036 : size_t reply_max;
1037 : size_t reply_count;
1038 : struct sysdb_attrs **reply;
1039 : };
1040 :
1041 0 : static errno_t add_to_reply(TALLOC_CTX *mem_ctx,
1042 : struct sdap_reply *sreply,
1043 : struct sysdb_attrs *msg)
1044 : {
1045 0 : if (sreply->reply == NULL || sreply->reply_max == sreply->reply_count) {
1046 0 : sreply->reply_max += REPLY_REALLOC_INCREMENT;
1047 0 : sreply->reply = talloc_realloc(mem_ctx, sreply->reply,
1048 : struct sysdb_attrs *,
1049 : sreply->reply_max);
1050 0 : if (sreply->reply == NULL) {
1051 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc failed.\n");
1052 0 : return ENOMEM;
1053 : }
1054 : }
1055 :
1056 0 : sreply->reply[sreply->reply_count++] = talloc_steal(sreply->reply, msg);
1057 :
1058 0 : return EOK;
1059 : }
1060 :
1061 : struct sdap_deref_reply {
1062 : size_t reply_max;
1063 : size_t reply_count;
1064 : struct sdap_deref_attrs **reply;
1065 : };
1066 :
1067 0 : static errno_t add_to_deref_reply(TALLOC_CTX *mem_ctx,
1068 : int num_maps,
1069 : struct sdap_deref_reply *dreply,
1070 : struct sdap_deref_attrs **res)
1071 : {
1072 : int i;
1073 :
1074 0 : if (res == NULL) {
1075 : /* Nothing to add, probably ACIs prevented us from dereferencing
1076 : * the attribute */
1077 0 : return EOK;
1078 : }
1079 :
1080 0 : for (i=0; i < num_maps; i++) {
1081 0 : if (res[i]->attrs == NULL) continue; /* Nothing in this map */
1082 :
1083 0 : if (dreply->reply == NULL ||
1084 0 : dreply->reply_max == dreply->reply_count) {
1085 0 : dreply->reply_max += REPLY_REALLOC_INCREMENT;
1086 0 : dreply->reply = talloc_realloc(mem_ctx, dreply->reply,
1087 : struct sdap_deref_attrs *,
1088 : dreply->reply_max);
1089 0 : if (dreply->reply == NULL) {
1090 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_realloc failed.\n");
1091 0 : return ENOMEM;
1092 : }
1093 : }
1094 :
1095 0 : dreply->reply[dreply->reply_count++] =
1096 0 : talloc_steal(dreply->reply, res[i]);
1097 : }
1098 :
1099 0 : return EOK;
1100 : }
1101 :
1102 0 : static void sdap_print_server(struct sdap_handle *sh)
1103 : {
1104 : int ret;
1105 : int fd;
1106 : struct sockaddr_storage ss;
1107 0 : socklen_t ss_len = sizeof(ss);
1108 0 : struct sockaddr *s_addr = (struct sockaddr *)&ss;
1109 : char ip[NI_MAXHOST];
1110 : int port;
1111 :
1112 0 : if (!DEBUG_IS_SET(SSSDBG_TRACE_INTERNAL)) {
1113 0 : return;
1114 : }
1115 :
1116 0 : ret = get_fd_from_ldap(sh->ldap, &fd);
1117 0 : if (ret != EOK) {
1118 0 : DEBUG(SSSDBG_MINOR_FAILURE, "cannot get sdap fd\n");
1119 0 : return;
1120 : }
1121 :
1122 0 : ret = getpeername(fd, s_addr, &ss_len);
1123 0 : if (ret == -1) {
1124 0 : DEBUG(SSSDBG_MINOR_FAILURE, "getsockname failed\n");
1125 0 : return;
1126 : }
1127 :
1128 0 : ret = getnameinfo(s_addr, ss_len,
1129 : ip, sizeof(ip), NULL, 0, NI_NUMERICHOST);
1130 0 : if (ret != 0) {
1131 0 : DEBUG(SSSDBG_MINOR_FAILURE, "getnameinfo failed\n");
1132 0 : return;
1133 : }
1134 :
1135 0 : switch (s_addr->sa_family) {
1136 : case AF_INET:
1137 0 : port = ntohs(((struct sockaddr_in *)s_addr)->sin_port);
1138 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Searching %s:%d\n", ip, port);
1139 0 : break;
1140 : case AF_INET6:
1141 0 : port = ntohs(((struct sockaddr_in6 *)s_addr)->sin6_port);
1142 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Searching %s:%d\n", ip, port);
1143 0 : break;
1144 : default:
1145 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Searching %s\n", ip);
1146 : }
1147 : }
1148 :
1149 : /* ==Generic Search exposing all options======================= */
1150 : typedef errno_t (*sdap_parse_cb)(struct sdap_handle *sh,
1151 : struct sdap_msg *msg,
1152 : void *pvt);
1153 :
1154 : struct sdap_get_generic_ext_state {
1155 : struct tevent_context *ev;
1156 : struct sdap_options *opts;
1157 : struct sdap_handle *sh;
1158 : const char *search_base;
1159 : int scope;
1160 : const char *filter;
1161 : const char **attrs;
1162 : int timeout;
1163 : int sizelimit;
1164 :
1165 : struct sdap_op *op;
1166 :
1167 : struct berval cookie;
1168 :
1169 : LDAPControl **serverctrls;
1170 : int nserverctrls;
1171 : LDAPControl **clientctrls;
1172 :
1173 : size_t ref_count;
1174 : char **refs;
1175 :
1176 : sdap_parse_cb parse_cb;
1177 : void *cb_data;
1178 :
1179 : unsigned int flags;
1180 : };
1181 :
1182 : static errno_t sdap_get_generic_ext_step(struct tevent_req *req);
1183 :
1184 : static void sdap_get_generic_op_finished(struct sdap_op *op,
1185 : struct sdap_msg *reply,
1186 : int error, void *pvt);
1187 :
1188 : enum {
1189 : /* Be silent about exceeded size limit */
1190 : SDAP_SRCH_FLG_SIZELIMIT_SILENT = 1 << 0,
1191 :
1192 : /* Allow paging */
1193 : SDAP_SRCH_FLG_PAGING = 1 << 1,
1194 :
1195 : /* Only attribute descriptions are requested */
1196 : SDAP_SRCH_FLG_ATTRS_ONLY = 1 << 2,
1197 : };
1198 :
1199 : static struct tevent_req *
1200 0 : sdap_get_generic_ext_send(TALLOC_CTX *memctx,
1201 : struct tevent_context *ev,
1202 : struct sdap_options *opts,
1203 : struct sdap_handle *sh,
1204 : const char *search_base,
1205 : int scope,
1206 : const char *filter,
1207 : const char **attrs,
1208 : LDAPControl **serverctrls,
1209 : LDAPControl **clientctrls,
1210 : int sizelimit,
1211 : int timeout,
1212 : sdap_parse_cb parse_cb,
1213 : void *cb_data,
1214 : unsigned int flags)
1215 : {
1216 : errno_t ret;
1217 : struct sdap_get_generic_ext_state *state;
1218 : struct tevent_req *req;
1219 : int i;
1220 : LDAPControl *control;
1221 :
1222 0 : req = tevent_req_create(memctx, &state, struct sdap_get_generic_ext_state);
1223 0 : if (!req) return NULL;
1224 :
1225 0 : state->ev = ev;
1226 0 : state->opts = opts;
1227 0 : state->sh = sh;
1228 0 : state->search_base = search_base;
1229 0 : state->scope = scope;
1230 0 : state->filter = filter;
1231 0 : state->attrs = attrs;
1232 0 : state->op = NULL;
1233 0 : state->sizelimit = sizelimit;
1234 0 : state->timeout = timeout;
1235 0 : state->cookie.bv_len = 0;
1236 0 : state->cookie.bv_val = NULL;
1237 0 : state->parse_cb = parse_cb;
1238 0 : state->cb_data = cb_data;
1239 0 : state->clientctrls = clientctrls;
1240 0 : state->flags = flags;
1241 :
1242 0 : if (state->sh == NULL || state->sh->ldap == NULL) {
1243 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1244 : "Trying LDAP search while not connected.\n");
1245 0 : tevent_req_error(req, EIO);
1246 0 : tevent_req_post(req, ev);
1247 0 : return req;
1248 : }
1249 :
1250 0 : sdap_print_server(sh);
1251 :
1252 : /* Be extra careful and never allow paging for BASE searches,
1253 : * even if requested.
1254 : */
1255 0 : if (scope == LDAP_SCOPE_BASE && (flags & SDAP_SRCH_FLG_PAGING)) {
1256 : /* Disable paging */
1257 0 : flags &= ~SDAP_SRCH_FLG_PAGING;
1258 0 : DEBUG(SSSDBG_TRACE_FUNC,
1259 : "WARNING: Disabling paging because scope is set to base.\n");
1260 : }
1261 :
1262 : /* Also check for deref/asq requests and force
1263 : * paging on for those requests
1264 : */
1265 : /* X-DEREF */
1266 0 : control = ldap_control_find(LDAP_CONTROL_X_DEREF,
1267 : serverctrls,
1268 : NULL);
1269 0 : if (control) {
1270 0 : flags |= SDAP_SRCH_FLG_PAGING;
1271 : }
1272 :
1273 : /* ASQ */
1274 0 : control = ldap_control_find(LDAP_SERVER_ASQ_OID,
1275 : serverctrls,
1276 : NULL);
1277 0 : if (control) {
1278 0 : flags |= SDAP_SRCH_FLG_PAGING;
1279 : }
1280 :
1281 0 : for (state->nserverctrls=0;
1282 0 : serverctrls && serverctrls[state->nserverctrls];
1283 0 : state->nserverctrls++) ;
1284 :
1285 : /* One extra space for NULL, one for page control */
1286 0 : state->serverctrls = talloc_array(state, LDAPControl *,
1287 : state->nserverctrls+2);
1288 0 : if (!state->serverctrls) {
1289 0 : tevent_req_error(req, ENOMEM);
1290 0 : tevent_req_post(req, ev);
1291 0 : return req;
1292 : }
1293 :
1294 0 : for (i=0; i < state->nserverctrls; i++) {
1295 0 : state->serverctrls[i] = serverctrls[i];
1296 : }
1297 0 : state->serverctrls[i] = NULL;
1298 :
1299 : PROBE(SDAP_GET_GENERIC_EXT_SEND, state->search_base,
1300 : state->scope, state->filter);
1301 :
1302 0 : ret = sdap_get_generic_ext_step(req);
1303 0 : if (ret != EOK) {
1304 0 : tevent_req_error(req, ret);
1305 0 : tevent_req_post(req, ev);
1306 0 : return req;
1307 : }
1308 :
1309 0 : return req;
1310 : }
1311 :
1312 0 : static errno_t sdap_get_generic_ext_step(struct tevent_req *req)
1313 : {
1314 0 : struct sdap_get_generic_ext_state *state =
1315 0 : tevent_req_data(req, struct sdap_get_generic_ext_state);
1316 : char *errmsg;
1317 : int lret;
1318 : int optret;
1319 : errno_t ret;
1320 : int msgid;
1321 : bool disable_paging;
1322 :
1323 0 : LDAPControl *page_control = NULL;
1324 :
1325 : /* Make sure to free any previous operations so
1326 : * if we are handling a large number of pages we
1327 : * don't waste memory.
1328 : */
1329 0 : talloc_zfree(state->op);
1330 :
1331 0 : DEBUG(SSSDBG_TRACE_FUNC,
1332 : "calling ldap_search_ext with [%s][%s].\n",
1333 : state->filter ? state->filter : "no filter",
1334 : state->search_base);
1335 0 : if (DEBUG_IS_SET(SSSDBG_TRACE_LIBS)) {
1336 : int i;
1337 :
1338 0 : if (state->attrs) {
1339 0 : for (i = 0; state->attrs[i]; i++) {
1340 0 : DEBUG(SSSDBG_TRACE_LIBS,
1341 : "Requesting attrs: [%s]\n", state->attrs[i]);
1342 : }
1343 : }
1344 : }
1345 :
1346 0 : disable_paging = dp_opt_get_bool(state->opts->basic, SDAP_DISABLE_PAGING);
1347 :
1348 0 : if (!disable_paging
1349 0 : && (state->flags & SDAP_SRCH_FLG_PAGING)
1350 0 : && sdap_is_control_supported(state->sh,
1351 : LDAP_CONTROL_PAGEDRESULTS)) {
1352 0 : lret = ldap_create_page_control(state->sh->ldap,
1353 0 : state->sh->page_size,
1354 0 : state->cookie.bv_val ?
1355 : &state->cookie :
1356 : NULL,
1357 : false,
1358 : &page_control);
1359 0 : if (lret != LDAP_SUCCESS) {
1360 0 : ret = EIO;
1361 0 : goto done;
1362 : }
1363 0 : state->serverctrls[state->nserverctrls] = page_control;
1364 0 : state->serverctrls[state->nserverctrls+1] = NULL;
1365 : }
1366 :
1367 0 : lret = ldap_search_ext(state->sh->ldap, state->search_base,
1368 : state->scope, state->filter,
1369 0 : discard_const(state->attrs),
1370 0 : (state->flags & SDAP_SRCH_FLG_ATTRS_ONLY),
1371 : state->serverctrls,
1372 : state->clientctrls, NULL, state->sizelimit, &msgid);
1373 0 : ldap_control_free(page_control);
1374 0 : state->serverctrls[state->nserverctrls] = NULL;
1375 0 : if (lret != LDAP_SUCCESS) {
1376 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1377 : "ldap_search_ext failed: %s\n", sss_ldap_err2string(lret));
1378 0 : if (lret == LDAP_SERVER_DOWN) {
1379 0 : ret = ETIMEDOUT;
1380 0 : optret = sss_ldap_get_diagnostic_msg(state, state->sh->ldap,
1381 : &errmsg);
1382 0 : if (optret == LDAP_SUCCESS) {
1383 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Connection error: %s\n", errmsg);
1384 0 : sss_log(SSS_LOG_ERR, "LDAP connection error: %s", errmsg);
1385 : }
1386 : else {
1387 0 : sss_log(SSS_LOG_ERR, "LDAP connection error, %s",
1388 : sss_ldap_err2string(lret));
1389 : }
1390 0 : } else if (lret == LDAP_FILTER_ERROR) {
1391 0 : ret = ERR_INVALID_FILTER;
1392 : } else {
1393 0 : ret = EIO;
1394 : }
1395 0 : goto done;
1396 : }
1397 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "ldap_search_ext called, msgid = %d\n", msgid);
1398 :
1399 0 : ret = sdap_op_add(state, state->ev, state->sh, msgid,
1400 : sdap_get_generic_op_finished, req,
1401 : state->timeout,
1402 : &state->op);
1403 0 : if (ret != EOK) {
1404 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to set up operation!\n");
1405 0 : goto done;
1406 : }
1407 :
1408 : done:
1409 0 : return ret;
1410 : }
1411 :
1412 : static errno_t
1413 0 : sdap_get_generic_ext_add_references(struct sdap_get_generic_ext_state *state,
1414 : char **refs)
1415 : {
1416 : int i;
1417 :
1418 0 : if (refs == NULL) {
1419 : /* Rare, but it's possible that we might get a reference result with
1420 : * no references attached.
1421 : */
1422 0 : return EOK;
1423 : }
1424 :
1425 0 : for (i = 0; refs[i]; i++) {
1426 0 : DEBUG(SSSDBG_TRACE_LIBS, "Additional References: %s\n", refs[i]);
1427 : }
1428 :
1429 : /* Extend the size of the ref array */
1430 0 : state->refs = talloc_realloc(state, state->refs, char *,
1431 : state->ref_count + i);
1432 0 : if (state->refs == NULL) {
1433 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1434 : "talloc_realloc failed extending ref_array.\n");
1435 0 : return ENOMEM;
1436 : }
1437 :
1438 : /* Copy in all the references */
1439 0 : for (i = 0; refs[i]; i++) {
1440 0 : state->refs[state->ref_count + i] =
1441 0 : talloc_strdup(state->refs, refs[i]);
1442 :
1443 0 : if (state->refs[state->ref_count + i] == NULL) {
1444 0 : return ENOMEM;
1445 : }
1446 : }
1447 :
1448 0 : state->ref_count += i;
1449 :
1450 0 : return EOK;
1451 : }
1452 :
1453 0 : static void sdap_get_generic_op_finished(struct sdap_op *op,
1454 : struct sdap_msg *reply,
1455 : int error, void *pvt)
1456 : {
1457 0 : struct tevent_req *req = talloc_get_type(pvt, struct tevent_req);
1458 0 : struct sdap_get_generic_ext_state *state = tevent_req_data(req,
1459 : struct sdap_get_generic_ext_state);
1460 0 : char *errmsg = NULL;
1461 0 : char **refs = NULL;
1462 : int result;
1463 : int ret;
1464 : int lret;
1465 : ber_int_t total_count;
1466 : struct berval cookie;
1467 0 : LDAPControl **returned_controls = NULL;
1468 : LDAPControl *page_control;
1469 :
1470 0 : if (error) {
1471 0 : tevent_req_error(req, error);
1472 0 : return;
1473 : }
1474 :
1475 0 : switch (ldap_msgtype(reply->msg)) {
1476 : case LDAP_RES_SEARCH_REFERENCE:
1477 0 : ret = ldap_parse_reference(state->sh->ldap, reply->msg,
1478 : &refs, NULL, 0);
1479 0 : if (ret != LDAP_SUCCESS) {
1480 0 : DEBUG(SSSDBG_OP_FAILURE,
1481 : "ldap_parse_reference failed (%d)\n", state->op->msgid);
1482 0 : tevent_req_error(req, EIO);
1483 0 : return;
1484 : }
1485 :
1486 0 : ret = sdap_get_generic_ext_add_references(state, refs);
1487 0 : if (ret != EOK) {
1488 0 : DEBUG(SSSDBG_OP_FAILURE,
1489 : "sdap_get_generic_ext_add_references failed: %s(%d)\n",
1490 : sss_strerror(ret), ret);
1491 0 : ldap_memvfree((void **)refs);
1492 0 : tevent_req_error(req, ret);
1493 0 : return;
1494 : }
1495 :
1496 : /* Remove the original strings */
1497 0 : ldap_memvfree((void **)refs);
1498 :
1499 : /* unlock the operation so that we can proceed with the next result */
1500 0 : sdap_unlock_next_reply(state->op);
1501 0 : break;
1502 :
1503 : case LDAP_RES_SEARCH_ENTRY:
1504 0 : ret = state->parse_cb(state->sh, reply, state->cb_data);
1505 0 : if (ret != EOK) {
1506 0 : DEBUG(SSSDBG_CRIT_FAILURE, "reply parsing callback failed.\n");
1507 0 : tevent_req_error(req, ret);
1508 0 : return;
1509 : }
1510 :
1511 0 : sdap_unlock_next_reply(state->op);
1512 0 : break;
1513 :
1514 : case LDAP_RES_SEARCH_RESULT:
1515 0 : ret = ldap_parse_result(state->sh->ldap, reply->msg,
1516 : &result, NULL, &errmsg, &refs,
1517 : &returned_controls, 0);
1518 0 : if (ret != LDAP_SUCCESS) {
1519 0 : DEBUG(SSSDBG_OP_FAILURE,
1520 : "ldap_parse_result failed (%d)\n", state->op->msgid);
1521 0 : tevent_req_error(req, EIO);
1522 0 : return;
1523 : }
1524 :
1525 0 : DEBUG(SSSDBG_TRACE_FUNC, "Search result: %s(%d), %s\n",
1526 : sss_ldap_err2string(result), result,
1527 : errmsg ? errmsg : "no errmsg set");
1528 :
1529 0 : if (result == LDAP_SIZELIMIT_EXCEEDED) {
1530 : /* Try to return what we've got */
1531 :
1532 0 : if ( ! (state->flags & SDAP_SRCH_FLG_SIZELIMIT_SILENT)) {
1533 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1534 : "LDAP sizelimit was exceeded, "
1535 : "returning incomplete data\n");
1536 : }
1537 0 : } else if (result == LDAP_INAPPROPRIATE_MATCHING) {
1538 : /* This error should only occur when we're testing for
1539 : * specialized functionality like the ldap matching rule
1540 : * filter for Active Directory. Warn at a higher log
1541 : * level and return EIO.
1542 : */
1543 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
1544 : "LDAP_INAPPROPRIATE_MATCHING: %s\n",
1545 : errmsg ? errmsg : "no errmsg set");
1546 0 : ldap_memfree(errmsg);
1547 0 : tevent_req_error(req, EIO);
1548 0 : return;
1549 0 : } else if (result == LDAP_UNAVAILABLE_CRITICAL_EXTENSION) {
1550 0 : ldap_memfree(errmsg);
1551 0 : tevent_req_error(req, ENOTSUP);
1552 0 : return;
1553 0 : } else if (result == LDAP_REFERRAL) {
1554 0 : ret = sdap_get_generic_ext_add_references(state, refs);
1555 0 : if (ret != EOK) {
1556 0 : DEBUG(SSSDBG_OP_FAILURE,
1557 : "sdap_get_generic_ext_add_references failed: %s(%d)\n",
1558 : sss_strerror(ret), ret);
1559 0 : tevent_req_error(req, ret);
1560 : }
1561 : /* For referrals, we need to fall through as if it was LDAP_SUCCESS */
1562 0 : } else if (result != LDAP_SUCCESS && result != LDAP_NO_SUCH_OBJECT) {
1563 0 : DEBUG(SSSDBG_OP_FAILURE,
1564 : "Unexpected result from ldap: %s(%d), %s\n",
1565 : sss_ldap_err2string(result), result,
1566 : errmsg ? errmsg : "no errmsg set");
1567 0 : ldap_memfree(errmsg);
1568 0 : tevent_req_error(req, EIO);
1569 0 : return;
1570 : }
1571 0 : ldap_memfree(errmsg);
1572 :
1573 : /* Determine if there are more pages to retrieve */
1574 0 : page_control = ldap_control_find(LDAP_CONTROL_PAGEDRESULTS,
1575 : returned_controls, NULL );
1576 0 : if (!page_control) {
1577 : /* No paging support. We are done */
1578 0 : tevent_req_done(req);
1579 0 : return;
1580 : }
1581 :
1582 0 : lret = ldap_parse_pageresponse_control(state->sh->ldap, page_control,
1583 : &total_count, &cookie);
1584 0 : ldap_controls_free(returned_controls);
1585 0 : if (lret != LDAP_SUCCESS) {
1586 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not determine page control\n");
1587 0 : tevent_req_error(req, EIO);
1588 0 : return;
1589 : }
1590 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Total count [%d]\n", total_count);
1591 :
1592 0 : if (cookie.bv_val != NULL && cookie.bv_len > 0) {
1593 : /* Cookie contains data, which means there are more requests
1594 : * to be processed.
1595 : */
1596 0 : talloc_zfree(state->cookie.bv_val);
1597 0 : state->cookie.bv_len = cookie.bv_len;
1598 0 : state->cookie.bv_val = talloc_memdup(state,
1599 : cookie.bv_val,
1600 : cookie.bv_len);
1601 0 : if (!state->cookie.bv_val) {
1602 0 : tevent_req_error(req, ENOMEM);
1603 0 : return;
1604 : }
1605 0 : ber_memfree(cookie.bv_val);
1606 :
1607 0 : ret = sdap_get_generic_ext_step(req);
1608 0 : if (ret != EOK) {
1609 0 : tevent_req_error(req, ENOMEM);
1610 0 : return;
1611 : }
1612 :
1613 0 : return;
1614 : }
1615 : /* The cookie must be freed even if len == 0 */
1616 0 : ber_memfree(cookie.bv_val);
1617 :
1618 : /* This was the last page. We're done */
1619 :
1620 0 : tevent_req_done(req);
1621 0 : return;
1622 :
1623 : default:
1624 : /* what is going on here !? */
1625 0 : tevent_req_error(req, EIO);
1626 0 : return;
1627 : }
1628 : }
1629 :
1630 : static int
1631 0 : sdap_get_generic_ext_recv(struct tevent_req *req,
1632 : TALLOC_CTX *mem_ctx,
1633 : size_t *ref_count,
1634 : char ***refs)
1635 : {
1636 0 : struct sdap_get_generic_ext_state *state =
1637 0 : tevent_req_data(req, struct sdap_get_generic_ext_state);
1638 :
1639 : PROBE(SDAP_GET_GENERIC_EXT_RECV, state->search_base,
1640 : state->scope, state->filter);
1641 :
1642 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1643 :
1644 0 : if (ref_count) {
1645 0 : *ref_count = state->ref_count;
1646 : }
1647 :
1648 0 : if (refs) {
1649 0 : *refs = talloc_steal(mem_ctx, state->refs);
1650 : }
1651 :
1652 0 : return EOK;
1653 : }
1654 :
1655 : /* This search handler can be used by most calls */
1656 0 : static void generic_ext_search_handler(struct tevent_req *subreq,
1657 : struct sdap_options *opts)
1658 : {
1659 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
1660 : struct tevent_req);
1661 : int ret;
1662 : size_t ref_count, i;
1663 : char **refs;
1664 :
1665 0 : ret = sdap_get_generic_ext_recv(subreq, req, &ref_count, &refs);
1666 0 : talloc_zfree(subreq);
1667 0 : if (ret != EOK) {
1668 0 : DEBUG(SSSDBG_OP_FAILURE,
1669 : "sdap_get_generic_ext_recv failed [%d]: %s\n",
1670 : ret, sss_strerror(ret));
1671 0 : tevent_req_error(req, ret);
1672 0 : return;
1673 : }
1674 :
1675 0 : if (ref_count > 0) {
1676 : /* We will ignore referrals in the generic handler */
1677 0 : DEBUG(SSSDBG_TRACE_ALL,
1678 : "Request included referrals which were ignored.\n");
1679 0 : if (debug_level & SSSDBG_TRACE_ALL) {
1680 0 : for(i = 0; i < ref_count; i++) {
1681 0 : DEBUG(SSSDBG_TRACE_ALL,
1682 : " Ref: %s\n", refs[i]);
1683 : }
1684 : }
1685 : }
1686 :
1687 0 : talloc_free(refs);
1688 0 : tevent_req_done(req);
1689 : }
1690 :
1691 : /* ==Generic Search exposing all options======================= */
1692 : struct sdap_get_and_parse_generic_state {
1693 : struct sdap_attr_map *map;
1694 : int map_num_attrs;
1695 :
1696 : struct sdap_reply sreply;
1697 : struct sdap_options *opts;
1698 : };
1699 :
1700 : static void sdap_get_and_parse_generic_done(struct tevent_req *subreq);
1701 : static errno_t sdap_get_and_parse_generic_parse_entry(struct sdap_handle *sh,
1702 : struct sdap_msg *msg,
1703 : void *pvt);
1704 :
1705 0 : struct tevent_req *sdap_get_and_parse_generic_send(TALLOC_CTX *memctx,
1706 : struct tevent_context *ev,
1707 : struct sdap_options *opts,
1708 : struct sdap_handle *sh,
1709 : const char *search_base,
1710 : int scope,
1711 : const char *filter,
1712 : const char **attrs,
1713 : struct sdap_attr_map *map,
1714 : int map_num_attrs,
1715 : int attrsonly,
1716 : LDAPControl **serverctrls,
1717 : LDAPControl **clientctrls,
1718 : int sizelimit,
1719 : int timeout,
1720 : bool allow_paging)
1721 : {
1722 0 : struct tevent_req *req = NULL;
1723 0 : struct tevent_req *subreq = NULL;
1724 0 : struct sdap_get_and_parse_generic_state *state = NULL;
1725 0 : unsigned int flags = 0;
1726 :
1727 0 : req = tevent_req_create(memctx, &state,
1728 : struct sdap_get_and_parse_generic_state);
1729 0 : if (!req) return NULL;
1730 :
1731 0 : state->map = map;
1732 0 : state->map_num_attrs = map_num_attrs;
1733 0 : state->opts = opts;
1734 :
1735 0 : if (allow_paging) {
1736 0 : flags |= SDAP_SRCH_FLG_PAGING;
1737 : }
1738 :
1739 0 : if (attrsonly) {
1740 0 : flags |= SDAP_SRCH_FLG_ATTRS_ONLY;
1741 : }
1742 :
1743 0 : subreq = sdap_get_generic_ext_send(state, ev, opts, sh, search_base,
1744 : scope, filter, attrs, serverctrls,
1745 : clientctrls, sizelimit, timeout,
1746 : sdap_get_and_parse_generic_parse_entry,
1747 : state, flags);
1748 0 : if (!subreq) {
1749 0 : talloc_zfree(req);
1750 0 : return NULL;
1751 : }
1752 0 : tevent_req_set_callback(subreq, sdap_get_and_parse_generic_done, req);
1753 :
1754 0 : return req;
1755 : }
1756 :
1757 0 : static errno_t sdap_get_and_parse_generic_parse_entry(struct sdap_handle *sh,
1758 : struct sdap_msg *msg,
1759 : void *pvt)
1760 : {
1761 : errno_t ret;
1762 : struct sysdb_attrs *attrs;
1763 0 : struct sdap_get_and_parse_generic_state *state =
1764 : talloc_get_type(pvt, struct sdap_get_and_parse_generic_state);
1765 :
1766 0 : bool disable_range_rtrvl = dp_opt_get_bool(state->opts->basic,
1767 : SDAP_DISABLE_RANGE_RETRIEVAL);
1768 :
1769 0 : ret = sdap_parse_entry(state, sh, msg,
1770 : state->map, state->map_num_attrs,
1771 : &attrs, disable_range_rtrvl);
1772 0 : if (ret != EOK) {
1773 0 : DEBUG(SSSDBG_MINOR_FAILURE,
1774 : "sdap_parse_entry failed [%d]: %s\n", ret, strerror(ret));
1775 0 : return ret;
1776 : }
1777 :
1778 0 : ret = add_to_reply(state, &state->sreply, attrs);
1779 0 : if (ret != EOK) {
1780 0 : talloc_free(attrs);
1781 0 : DEBUG(SSSDBG_CRIT_FAILURE, "add_to_reply failed.\n");
1782 0 : return ret;
1783 : }
1784 :
1785 : /* add_to_reply steals attrs, no need to free them here */
1786 0 : return EOK;
1787 : }
1788 :
1789 0 : static void sdap_get_and_parse_generic_done(struct tevent_req *subreq)
1790 : {
1791 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
1792 : struct tevent_req);
1793 0 : struct sdap_get_and_parse_generic_state *state =
1794 0 : tevent_req_data(req, struct sdap_get_and_parse_generic_state);
1795 :
1796 0 : return generic_ext_search_handler(subreq, state->opts);
1797 : }
1798 :
1799 0 : int sdap_get_and_parse_generic_recv(struct tevent_req *req,
1800 : TALLOC_CTX *mem_ctx,
1801 : size_t *reply_count,
1802 : struct sysdb_attrs ***reply)
1803 : {
1804 0 : struct sdap_get_and_parse_generic_state *state = tevent_req_data(req,
1805 : struct sdap_get_and_parse_generic_state);
1806 :
1807 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1808 :
1809 0 : *reply_count = state->sreply.reply_count;
1810 0 : *reply = talloc_steal(mem_ctx, state->sreply.reply);
1811 :
1812 0 : return EOK;
1813 : }
1814 :
1815 :
1816 : /* ==Simple generic search============================================== */
1817 : struct sdap_get_generic_state {
1818 : size_t reply_count;
1819 : struct sysdb_attrs **reply;
1820 : };
1821 :
1822 : static void sdap_get_generic_done(struct tevent_req *subreq);
1823 :
1824 0 : struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx,
1825 : struct tevent_context *ev,
1826 : struct sdap_options *opts,
1827 : struct sdap_handle *sh,
1828 : const char *search_base,
1829 : int scope,
1830 : const char *filter,
1831 : const char **attrs,
1832 : struct sdap_attr_map *map,
1833 : int map_num_attrs,
1834 : int timeout,
1835 : bool allow_paging)
1836 : {
1837 0 : struct tevent_req *req = NULL;
1838 0 : struct tevent_req *subreq = NULL;
1839 0 : struct sdap_get_generic_state *state = NULL;
1840 :
1841 0 : req = tevent_req_create(memctx, &state, struct sdap_get_generic_state);
1842 0 : if (!req) return NULL;
1843 :
1844 0 : subreq = sdap_get_and_parse_generic_send(memctx, ev, opts, sh, search_base,
1845 : scope, filter, attrs,
1846 : map, map_num_attrs,
1847 : false, NULL, NULL, 0, timeout,
1848 : allow_paging);
1849 0 : if (subreq == NULL) {
1850 0 : return NULL;
1851 : }
1852 0 : tevent_req_set_callback(subreq, sdap_get_generic_done, req);
1853 :
1854 0 : return req;
1855 : }
1856 :
1857 0 : static void sdap_get_generic_done(struct tevent_req *subreq)
1858 : {
1859 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
1860 : struct tevent_req);
1861 0 : struct sdap_get_generic_state *state =
1862 0 : tevent_req_data(req, struct sdap_get_generic_state);
1863 : errno_t ret;
1864 :
1865 0 : ret = sdap_get_and_parse_generic_recv(subreq, state,
1866 : &state->reply_count, &state->reply);
1867 0 : if (ret != EOK) {
1868 0 : tevent_req_error(req, ret);
1869 0 : return;
1870 : }
1871 0 : tevent_req_done(req);
1872 : }
1873 :
1874 0 : int sdap_get_generic_recv(struct tevent_req *req,
1875 : TALLOC_CTX *mem_ctx,
1876 : size_t *reply_count,
1877 : struct sysdb_attrs ***reply)
1878 : {
1879 0 : struct sdap_get_generic_state *state =
1880 0 : tevent_req_data(req, struct sdap_get_generic_state);
1881 :
1882 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
1883 :
1884 0 : *reply_count = state->reply_count;
1885 0 : *reply = talloc_steal(mem_ctx, state->reply);
1886 :
1887 0 : return EOK;
1888 : }
1889 :
1890 : /* ==OpenLDAP deref search============================================== */
1891 : static int sdap_x_deref_create_control(struct sdap_handle *sh,
1892 : const char *deref_attr,
1893 : const char **attrs,
1894 : LDAPControl **ctrl);
1895 :
1896 : static void sdap_x_deref_search_done(struct tevent_req *subreq);
1897 : static int sdap_x_deref_search_ctrls_destructor(void *ptr);
1898 :
1899 : static errno_t sdap_x_deref_parse_entry(struct sdap_handle *sh,
1900 : struct sdap_msg *msg,
1901 : void *pvt);
1902 : struct sdap_x_deref_search_state {
1903 : struct sdap_handle *sh;
1904 : struct sdap_op *op;
1905 : struct sdap_attr_map_info *maps;
1906 : LDAPControl **ctrls;
1907 : struct sdap_options *opts;
1908 :
1909 : struct sdap_deref_reply dreply;
1910 : int num_maps;
1911 : };
1912 :
1913 : static struct tevent_req *
1914 0 : sdap_x_deref_search_send(TALLOC_CTX *memctx, struct tevent_context *ev,
1915 : struct sdap_options *opts, struct sdap_handle *sh,
1916 : const char *base_dn, const char *filter,
1917 : const char *deref_attr, const char **attrs,
1918 : struct sdap_attr_map_info *maps, int num_maps,
1919 : int timeout)
1920 : {
1921 0 : struct tevent_req *req = NULL;
1922 0 : struct tevent_req *subreq = NULL;
1923 : struct sdap_x_deref_search_state *state;
1924 : int ret;
1925 :
1926 0 : req = tevent_req_create(memctx, &state, struct sdap_x_deref_search_state);
1927 0 : if (!req) return NULL;
1928 :
1929 0 : state->sh = sh;
1930 0 : state->maps = maps;
1931 0 : state->op = NULL;
1932 0 : state->opts = opts;
1933 0 : state->num_maps = num_maps;
1934 0 : state->ctrls = talloc_zero_array(state, LDAPControl *, 2);
1935 0 : if (state->ctrls == NULL) {
1936 0 : talloc_zfree(req);
1937 0 : return NULL;
1938 : }
1939 0 : talloc_set_destructor((TALLOC_CTX *) state->ctrls,
1940 : sdap_x_deref_search_ctrls_destructor);
1941 :
1942 0 : ret = sdap_x_deref_create_control(sh, deref_attr,
1943 0 : attrs, &state->ctrls[0]);
1944 0 : if (ret != EOK) {
1945 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not create OpenLDAP deref control\n");
1946 0 : talloc_zfree(req);
1947 0 : return NULL;
1948 : }
1949 :
1950 0 : DEBUG(SSSDBG_TRACE_FUNC,
1951 : "Dereferencing entry [%s] using OpenLDAP deref\n", base_dn);
1952 0 : subreq = sdap_get_generic_ext_send(state, ev, opts, sh, base_dn,
1953 : filter == NULL ? LDAP_SCOPE_BASE
1954 : : LDAP_SCOPE_SUBTREE,
1955 : filter, attrs,
1956 0 : state->ctrls, NULL, 0, timeout,
1957 : sdap_x_deref_parse_entry,
1958 : state, SDAP_SRCH_FLG_PAGING);
1959 0 : if (!subreq) {
1960 0 : talloc_zfree(req);
1961 0 : return NULL;
1962 : }
1963 0 : tevent_req_set_callback(subreq, sdap_x_deref_search_done, req);
1964 :
1965 0 : return req;
1966 : }
1967 :
1968 0 : static int sdap_x_deref_create_control(struct sdap_handle *sh,
1969 : const char *deref_attr,
1970 : const char **attrs,
1971 : LDAPControl **ctrl)
1972 : {
1973 : struct berval derefval;
1974 : int ret;
1975 : struct LDAPDerefSpec ds[2];
1976 :
1977 0 : ds[0].derefAttr = discard_const(deref_attr);
1978 0 : ds[0].attributes = discard_const(attrs);
1979 :
1980 0 : ds[1].derefAttr = NULL; /* sentinel */
1981 :
1982 0 : ret = ldap_create_deref_control_value(sh->ldap, ds, &derefval);
1983 0 : if (ret != LDAP_SUCCESS) {
1984 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sss_ldap_control_create failed: %s\n",
1985 : ldap_err2string(ret));
1986 0 : return ret;
1987 : }
1988 :
1989 0 : ret = sdap_control_create(sh, LDAP_CONTROL_X_DEREF,
1990 : 1, &derefval, 1, ctrl);
1991 0 : ldap_memfree(derefval.bv_val);
1992 0 : if (ret != EOK) {
1993 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sss_ldap_control_create failed\n");
1994 0 : return ret;
1995 : }
1996 :
1997 0 : return EOK;
1998 : }
1999 :
2000 0 : static errno_t sdap_x_deref_parse_entry(struct sdap_handle *sh,
2001 : struct sdap_msg *msg,
2002 : void *pvt)
2003 : {
2004 : errno_t ret;
2005 0 : LDAPControl **ctrls = NULL;
2006 0 : LDAPControl *derefctrl = NULL;
2007 0 : LDAPDerefRes *deref_res = NULL;
2008 : LDAPDerefRes *dref;
2009 : struct sdap_deref_attrs **res;
2010 : TALLOC_CTX *tmp_ctx;
2011 :
2012 0 : struct sdap_x_deref_search_state *state = talloc_get_type(pvt,
2013 : struct sdap_x_deref_search_state);
2014 :
2015 0 : tmp_ctx = talloc_new(NULL);
2016 0 : if (!tmp_ctx) return ENOMEM;
2017 :
2018 0 : ret = ldap_get_entry_controls(state->sh->ldap, msg->msg,
2019 : &ctrls);
2020 0 : if (ret != LDAP_SUCCESS) {
2021 0 : DEBUG(SSSDBG_OP_FAILURE, "ldap_parse_result failed\n");
2022 0 : goto done;
2023 : }
2024 :
2025 0 : if (!ctrls) {
2026 : /* When we attempt to request attributes that are not present in
2027 : * the dereferenced links, some serves might not send the dereference
2028 : * control back at all. Be permissive and treat the search as if
2029 : * it didn't find anything.
2030 : */
2031 0 : DEBUG(SSSDBG_MINOR_FAILURE, "No controls found for entry\n");
2032 0 : ret = EOK;
2033 0 : goto done;
2034 : }
2035 :
2036 0 : res = NULL;
2037 :
2038 0 : derefctrl = ldap_control_find(LDAP_CONTROL_X_DEREF, ctrls, NULL);
2039 0 : if (!derefctrl) {
2040 0 : DEBUG(SSSDBG_FUNC_DATA, "No deref controls found\n");
2041 0 : ret = EOK;
2042 0 : goto done;
2043 : }
2044 :
2045 0 : DEBUG(SSSDBG_TRACE_FUNC, "Got deref control\n");
2046 :
2047 0 : ret = ldap_parse_derefresponse_control(state->sh->ldap,
2048 : derefctrl,
2049 : &deref_res);
2050 0 : if (ret != LDAP_SUCCESS) {
2051 0 : DEBUG(SSSDBG_OP_FAILURE,
2052 : "ldap_parse_derefresponse_control failed: %s\n",
2053 : ldap_err2string(ret));
2054 0 : goto done;
2055 : }
2056 :
2057 0 : for (dref = deref_res; dref; dref=dref->next) {
2058 0 : ret = sdap_parse_deref(tmp_ctx, state->maps, state->num_maps,
2059 : dref, &res);
2060 0 : if (ret) {
2061 0 : DEBUG(SSSDBG_OP_FAILURE, "sdap_parse_deref failed [%d]: %s\n",
2062 : ret, strerror(ret));
2063 0 : goto done;
2064 : }
2065 :
2066 0 : ret = add_to_deref_reply(state, state->num_maps,
2067 : &state->dreply, res);
2068 0 : if (ret != EOK) {
2069 0 : DEBUG(SSSDBG_OP_FAILURE, "add_to_deref_reply failed.\n");
2070 0 : goto done;
2071 : }
2072 : }
2073 :
2074 0 : DEBUG(SSSDBG_TRACE_FUNC,
2075 : "All deref results from a single control parsed\n");
2076 0 : ldap_derefresponse_free(deref_res);
2077 0 : deref_res = NULL;
2078 :
2079 0 : ret = EOK;
2080 : done:
2081 0 : talloc_zfree(tmp_ctx);
2082 0 : ldap_controls_free(ctrls);
2083 0 : ldap_derefresponse_free(deref_res);
2084 0 : return ret;
2085 : }
2086 :
2087 0 : static void sdap_x_deref_search_done(struct tevent_req *subreq)
2088 : {
2089 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
2090 : struct tevent_req);
2091 0 : struct sdap_x_deref_search_state *state =
2092 0 : tevent_req_data(req, struct sdap_x_deref_search_state);
2093 :
2094 0 : return generic_ext_search_handler(subreq, state->opts);
2095 : }
2096 :
2097 0 : static int sdap_x_deref_search_ctrls_destructor(void *ptr)
2098 : {
2099 0 : LDAPControl **ctrls = talloc_get_type(ptr, LDAPControl *);;
2100 :
2101 0 : if (ctrls && ctrls[0]) {
2102 0 : ldap_control_free(ctrls[0]);
2103 : }
2104 :
2105 0 : return 0;
2106 : }
2107 :
2108 : static int
2109 0 : sdap_x_deref_search_recv(struct tevent_req *req,
2110 : TALLOC_CTX *mem_ctx,
2111 : size_t *reply_count,
2112 : struct sdap_deref_attrs ***reply)
2113 : {
2114 0 : struct sdap_x_deref_search_state *state = tevent_req_data(req,
2115 : struct sdap_x_deref_search_state);
2116 :
2117 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
2118 :
2119 0 : *reply_count = state->dreply.reply_count;
2120 0 : *reply = talloc_steal(mem_ctx, state->dreply.reply);
2121 :
2122 0 : return EOK;
2123 : }
2124 :
2125 : /* ==Security Descriptor (ACL) search=================================== */
2126 : struct sdap_sd_search_state {
2127 : LDAPControl **ctrls;
2128 : struct sdap_options *opts;
2129 : size_t reply_count;
2130 : struct sysdb_attrs **reply;
2131 : struct sdap_reply sreply;
2132 :
2133 : /* Referrals returned by the search */
2134 : size_t ref_count;
2135 : char **refs;
2136 : };
2137 :
2138 : static int sdap_sd_search_create_control(struct sdap_handle *sh,
2139 : int val,
2140 : LDAPControl **ctrl);
2141 : static int sdap_sd_search_ctrls_destructor(void *ptr);
2142 : static errno_t sdap_sd_search_parse_entry(struct sdap_handle *sh,
2143 : struct sdap_msg *msg,
2144 : void *pvt);
2145 : static void sdap_sd_search_done(struct tevent_req *subreq);
2146 :
2147 : struct tevent_req *
2148 0 : sdap_sd_search_send(TALLOC_CTX *memctx, struct tevent_context *ev,
2149 : struct sdap_options *opts, struct sdap_handle *sh,
2150 : const char *base_dn, int sd_flags,
2151 : const char **attrs, int timeout)
2152 : {
2153 0 : struct tevent_req *req = NULL;
2154 0 : struct tevent_req *subreq = NULL;
2155 : struct sdap_sd_search_state *state;
2156 : int ret;
2157 :
2158 0 : req = tevent_req_create(memctx, &state, struct sdap_sd_search_state);
2159 0 : if (!req) return NULL;
2160 :
2161 0 : state->ctrls = talloc_zero_array(state, LDAPControl *, 2);
2162 0 : state->opts = opts;
2163 0 : if (state->ctrls == NULL) {
2164 0 : ret = EIO;
2165 0 : goto fail;
2166 : }
2167 0 : talloc_set_destructor((TALLOC_CTX *) state->ctrls,
2168 : sdap_sd_search_ctrls_destructor);
2169 :
2170 0 : ret = sdap_sd_search_create_control(sh, sd_flags, &state->ctrls[0]);
2171 0 : if (ret != EOK) {
2172 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not create SD control\n");
2173 0 : ret = EIO;
2174 0 : goto fail;
2175 : }
2176 :
2177 0 : DEBUG(SSSDBG_TRACE_FUNC, "Searching entry [%s] using SD\n", base_dn);
2178 0 : subreq = sdap_get_generic_ext_send(state, ev, opts, sh, base_dn,
2179 : LDAP_SCOPE_BASE, "(objectclass=*)", attrs,
2180 0 : state->ctrls, NULL, 0, timeout,
2181 : sdap_sd_search_parse_entry,
2182 : state, SDAP_SRCH_FLG_PAGING);
2183 0 : if (!subreq) {
2184 0 : ret = EIO;
2185 0 : goto fail;
2186 : }
2187 0 : tevent_req_set_callback(subreq, sdap_sd_search_done, req);
2188 0 : return req;
2189 :
2190 : fail:
2191 0 : tevent_req_error(req, ret);
2192 0 : tevent_req_post(req, ev);
2193 0 : return req;
2194 : }
2195 :
2196 0 : static int sdap_sd_search_create_control(struct sdap_handle *sh,
2197 : int val,
2198 : LDAPControl **ctrl)
2199 : {
2200 : struct berval *sdval;
2201 : int ret;
2202 0 : BerElement *ber = NULL;
2203 0 : ber = ber_alloc_t(LBER_USE_DER);
2204 0 : if (ber == NULL) {
2205 0 : DEBUG(SSSDBG_OP_FAILURE, "ber_alloc_t failed.\n");
2206 0 : return ENOMEM;
2207 : }
2208 :
2209 0 : ret = ber_printf(ber, "{i}", val);
2210 0 : if (ret == -1) {
2211 0 : DEBUG(SSSDBG_OP_FAILURE, "ber_printf failed.\n");
2212 0 : ber_free(ber, 1);
2213 0 : return EIO;
2214 : }
2215 :
2216 0 : ret = ber_flatten(ber, &sdval);
2217 0 : ber_free(ber, 1);
2218 0 : if (ret == -1) {
2219 0 : DEBUG(SSSDBG_CRIT_FAILURE, "ber_flatten failed.\n");
2220 0 : return EIO;
2221 : }
2222 :
2223 0 : ret = sdap_control_create(sh, LDAP_SERVER_SD_OID, 1, sdval, 1, ctrl);
2224 0 : ber_bvfree(sdval);
2225 0 : if (ret != EOK) {
2226 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sdap_control_create failed\n");
2227 0 : return ret;
2228 : }
2229 :
2230 0 : return EOK;
2231 : }
2232 :
2233 0 : static errno_t sdap_sd_search_parse_entry(struct sdap_handle *sh,
2234 : struct sdap_msg *msg,
2235 : void *pvt)
2236 : {
2237 : errno_t ret;
2238 : struct sysdb_attrs *attrs;
2239 0 : struct sdap_sd_search_state *state =
2240 : talloc_get_type(pvt, struct sdap_sd_search_state);
2241 :
2242 0 : bool disable_range_rtrvl = dp_opt_get_bool(state->opts->basic,
2243 : SDAP_DISABLE_RANGE_RETRIEVAL);
2244 :
2245 0 : ret = sdap_parse_entry(state, sh, msg,
2246 : NULL, 0,
2247 : &attrs, disable_range_rtrvl);
2248 0 : if (ret != EOK) {
2249 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2250 : "sdap_parse_entry failed [%d]: %s\n", ret, strerror(ret));
2251 0 : return ret;
2252 : }
2253 :
2254 0 : ret = add_to_reply(state, &state->sreply, attrs);
2255 0 : if (ret != EOK) {
2256 0 : talloc_free(attrs);
2257 0 : DEBUG(SSSDBG_CRIT_FAILURE, "add_to_reply failed.\n");
2258 0 : return ret;
2259 : }
2260 :
2261 : /* add_to_reply steals attrs, no need to free them here */
2262 0 : return EOK;
2263 : }
2264 :
2265 0 : static void sdap_sd_search_done(struct tevent_req *subreq)
2266 : {
2267 : int ret;
2268 :
2269 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
2270 : struct tevent_req);
2271 0 : struct sdap_sd_search_state *state =
2272 0 : tevent_req_data(req, struct sdap_sd_search_state);
2273 :
2274 0 : ret = sdap_get_generic_ext_recv(subreq, state,
2275 : &state->ref_count,
2276 : &state->refs);
2277 0 : talloc_zfree(subreq);
2278 0 : if (ret != EOK) {
2279 0 : DEBUG(SSSDBG_OP_FAILURE,
2280 : "sdap_get_generic_ext_recv failed [%d]: %s\n",
2281 : ret, sss_strerror(ret));
2282 0 : tevent_req_error(req, ret);
2283 0 : return;
2284 : }
2285 :
2286 0 : tevent_req_done(req);
2287 : }
2288 :
2289 0 : static int sdap_sd_search_ctrls_destructor(void *ptr)
2290 : {
2291 0 : LDAPControl **ctrls = talloc_get_type(ptr, LDAPControl *);;
2292 0 : if (ctrls && ctrls[0]) {
2293 0 : ldap_control_free(ctrls[0]);
2294 : }
2295 :
2296 0 : return 0;
2297 : }
2298 :
2299 0 : int sdap_sd_search_recv(struct tevent_req *req,
2300 : TALLOC_CTX *mem_ctx,
2301 : size_t *_reply_count,
2302 : struct sysdb_attrs ***_reply,
2303 : size_t *_ref_count,
2304 : char ***_refs)
2305 : {
2306 0 : struct sdap_sd_search_state *state = tevent_req_data(req,
2307 : struct sdap_sd_search_state);
2308 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
2309 :
2310 0 : *_reply_count = state->sreply.reply_count;
2311 0 : *_reply = talloc_steal(mem_ctx, state->sreply.reply);
2312 :
2313 0 : if(_ref_count) {
2314 0 : *_ref_count = state->ref_count;
2315 : }
2316 :
2317 0 : if (_refs) {
2318 0 : *_refs = talloc_steal(mem_ctx, state->refs);
2319 : }
2320 :
2321 0 : return EOK;
2322 : }
2323 :
2324 : /* ==Attribute scoped search============================================ */
2325 : struct sdap_asq_search_state {
2326 : struct sdap_attr_map_info *maps;
2327 : int num_maps;
2328 : LDAPControl **ctrls;
2329 : struct sdap_options *opts;
2330 :
2331 : struct sdap_deref_reply dreply;
2332 : };
2333 :
2334 : static int sdap_asq_search_create_control(struct sdap_handle *sh,
2335 : const char *attr,
2336 : LDAPControl **ctrl);
2337 : static int sdap_asq_search_ctrls_destructor(void *ptr);
2338 : static errno_t sdap_asq_search_parse_entry(struct sdap_handle *sh,
2339 : struct sdap_msg *msg,
2340 : void *pvt);
2341 : static void sdap_asq_search_done(struct tevent_req *subreq);
2342 :
2343 : static struct tevent_req *
2344 0 : sdap_asq_search_send(TALLOC_CTX *memctx, struct tevent_context *ev,
2345 : struct sdap_options *opts, struct sdap_handle *sh,
2346 : const char *base_dn, const char *deref_attr,
2347 : const char **attrs, struct sdap_attr_map_info *maps,
2348 : int num_maps, int timeout)
2349 : {
2350 0 : struct tevent_req *req = NULL;
2351 0 : struct tevent_req *subreq = NULL;
2352 : struct sdap_asq_search_state *state;
2353 : int ret;
2354 :
2355 0 : req = tevent_req_create(memctx, &state, struct sdap_asq_search_state);
2356 0 : if (!req) return NULL;
2357 :
2358 0 : state->maps = maps;
2359 0 : state->num_maps = num_maps;
2360 0 : state->ctrls = talloc_zero_array(state, LDAPControl *, 2);
2361 0 : state->opts = opts;
2362 0 : if (state->ctrls == NULL) {
2363 0 : talloc_zfree(req);
2364 0 : return NULL;
2365 : }
2366 0 : talloc_set_destructor((TALLOC_CTX *) state->ctrls,
2367 : sdap_asq_search_ctrls_destructor);
2368 :
2369 0 : ret = sdap_asq_search_create_control(sh, deref_attr, &state->ctrls[0]);
2370 0 : if (ret != EOK) {
2371 0 : talloc_zfree(req);
2372 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not create ASQ control\n");
2373 0 : return NULL;
2374 : }
2375 :
2376 0 : DEBUG(SSSDBG_TRACE_FUNC, "Dereferencing entry [%s] using ASQ\n", base_dn);
2377 0 : subreq = sdap_get_generic_ext_send(state, ev, opts, sh, base_dn,
2378 : LDAP_SCOPE_BASE, NULL, attrs,
2379 0 : state->ctrls, NULL, 0, timeout,
2380 : sdap_asq_search_parse_entry,
2381 : state, SDAP_SRCH_FLG_PAGING);
2382 0 : if (!subreq) {
2383 0 : talloc_zfree(req);
2384 0 : return NULL;
2385 : }
2386 0 : tevent_req_set_callback(subreq, sdap_asq_search_done, req);
2387 :
2388 0 : return req;
2389 : }
2390 :
2391 :
2392 0 : static int sdap_asq_search_create_control(struct sdap_handle *sh,
2393 : const char *attr,
2394 : LDAPControl **ctrl)
2395 : {
2396 : struct berval *asqval;
2397 : int ret;
2398 0 : BerElement *ber = NULL;
2399 :
2400 0 : ber = ber_alloc_t(LBER_USE_DER);
2401 0 : if (ber == NULL) {
2402 0 : DEBUG(SSSDBG_OP_FAILURE, "ber_alloc_t failed.\n");
2403 0 : return ENOMEM;
2404 : }
2405 :
2406 0 : ret = ber_printf(ber, "{s}", attr);
2407 0 : if (ret == -1) {
2408 0 : DEBUG(SSSDBG_OP_FAILURE, "ber_printf failed.\n");
2409 0 : ber_free(ber, 1);
2410 0 : return EIO;
2411 : }
2412 :
2413 0 : ret = ber_flatten(ber, &asqval);
2414 0 : ber_free(ber, 1);
2415 0 : if (ret == -1) {
2416 0 : DEBUG(SSSDBG_CRIT_FAILURE, "ber_flatten failed.\n");
2417 0 : return EIO;
2418 : }
2419 :
2420 0 : ret = sdap_control_create(sh, LDAP_SERVER_ASQ_OID, 1, asqval, 1, ctrl);
2421 0 : ber_bvfree(asqval);
2422 0 : if (ret != EOK) {
2423 0 : DEBUG(SSSDBG_CRIT_FAILURE, "sdap_control_create failed\n");
2424 0 : return ret;
2425 : }
2426 :
2427 0 : return EOK;
2428 : }
2429 :
2430 0 : static errno_t sdap_asq_search_parse_entry(struct sdap_handle *sh,
2431 : struct sdap_msg *msg,
2432 : void *pvt)
2433 : {
2434 : errno_t ret;
2435 0 : struct sdap_asq_search_state *state =
2436 : talloc_get_type(pvt, struct sdap_asq_search_state);
2437 : struct berval **vals;
2438 : int i, mi;
2439 : struct sdap_attr_map *map;
2440 : int num_attrs;
2441 : struct sdap_deref_attrs **res;
2442 : char *tmp;
2443 : char *dn;
2444 : TALLOC_CTX *tmp_ctx;
2445 : bool disable_range_rtrvl;
2446 :
2447 0 : tmp_ctx = talloc_new(NULL);
2448 0 : if (!tmp_ctx) return ENOMEM;
2449 :
2450 0 : res = talloc_array(tmp_ctx, struct sdap_deref_attrs *,
2451 : state->num_maps);
2452 0 : if (!res) {
2453 0 : ret = ENOMEM;
2454 0 : goto done;
2455 : }
2456 :
2457 0 : for (mi =0; mi < state->num_maps; mi++) {
2458 0 : res[mi] = talloc_zero(res, struct sdap_deref_attrs);
2459 0 : if (!res[mi]) {
2460 0 : ret = ENOMEM;
2461 0 : goto done;
2462 : }
2463 0 : res[mi]->map = state->maps[mi].map;
2464 0 : res[mi]->attrs = NULL;
2465 : }
2466 :
2467 :
2468 0 : tmp = ldap_get_dn(sh->ldap, msg->msg);
2469 0 : if (!tmp) {
2470 0 : ret = EINVAL;
2471 0 : goto done;
2472 : }
2473 :
2474 0 : dn = talloc_strdup(tmp_ctx, tmp);
2475 0 : ldap_memfree(tmp);
2476 0 : if (!dn) {
2477 0 : ret = ENOMEM;
2478 0 : goto done;
2479 : }
2480 :
2481 : /* Find all suitable maps in the list */
2482 0 : vals = ldap_get_values_len(sh->ldap, msg->msg, "objectClass");
2483 0 : if (!vals) {
2484 0 : DEBUG(SSSDBG_OP_FAILURE,
2485 : "Unknown entry type, no objectClass found for DN [%s]!\n", dn);
2486 0 : ret = EINVAL;
2487 0 : goto done;
2488 : }
2489 0 : for (mi =0; mi < state->num_maps; mi++) {
2490 0 : map = NULL;
2491 0 : for (i = 0; vals[i]; i++) {
2492 : /* the objectclass is always the first name in the map */
2493 0 : if (strncasecmp(state->maps[mi].map[0].name,
2494 0 : vals[i]->bv_val, vals[i]->bv_len) == 0) {
2495 : /* it's an entry of the right type */
2496 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
2497 : "Matched objectclass [%s] on DN [%s], will use associated map\n",
2498 : state->maps[mi].map[0].name, dn);
2499 0 : map = state->maps[mi].map;
2500 0 : num_attrs = state->maps[mi].num_attrs;
2501 0 : break;
2502 : }
2503 : }
2504 0 : if (!map) {
2505 0 : DEBUG(SSSDBG_TRACE_INTERNAL,
2506 : "DN [%s] did not match the objectClass [%s]\n",
2507 : dn, state->maps[mi].map[0].name);
2508 0 : continue;
2509 : }
2510 :
2511 0 : disable_range_rtrvl = dp_opt_get_bool(state->opts->basic,
2512 : SDAP_DISABLE_RANGE_RETRIEVAL);
2513 :
2514 0 : ret = sdap_parse_entry(res[mi], sh, msg,
2515 : map, num_attrs,
2516 0 : &res[mi]->attrs, disable_range_rtrvl);
2517 0 : if (ret != EOK) {
2518 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2519 : "sdap_parse_entry failed [%d]: %s\n", ret, strerror(ret));
2520 0 : goto done;
2521 : }
2522 : }
2523 0 : ldap_value_free_len(vals);
2524 :
2525 0 : ret = add_to_deref_reply(state, state->num_maps,
2526 : &state->dreply, res);
2527 0 : if (ret != EOK) {
2528 0 : DEBUG(SSSDBG_CRIT_FAILURE, "add_to_deref_reply failed.\n");
2529 0 : goto done;
2530 : }
2531 :
2532 0 : ret = EOK;
2533 : done:
2534 0 : talloc_zfree(tmp_ctx);
2535 0 : return ret;
2536 : }
2537 :
2538 0 : static void sdap_asq_search_done(struct tevent_req *subreq)
2539 : {
2540 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
2541 : struct tevent_req);
2542 0 : struct sdap_asq_search_state *state =
2543 0 : tevent_req_data(req, struct sdap_asq_search_state);
2544 :
2545 0 : return generic_ext_search_handler(subreq, state->opts);
2546 : }
2547 :
2548 0 : static int sdap_asq_search_ctrls_destructor(void *ptr)
2549 : {
2550 0 : LDAPControl **ctrls = talloc_get_type(ptr, LDAPControl *);;
2551 :
2552 0 : if (ctrls && ctrls[0]) {
2553 0 : ldap_control_free(ctrls[0]);
2554 : }
2555 :
2556 0 : return 0;
2557 : }
2558 :
2559 0 : int sdap_asq_search_recv(struct tevent_req *req,
2560 : TALLOC_CTX *mem_ctx,
2561 : size_t *reply_count,
2562 : struct sdap_deref_attrs ***reply)
2563 : {
2564 0 : struct sdap_asq_search_state *state = tevent_req_data(req,
2565 : struct sdap_asq_search_state);
2566 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
2567 :
2568 0 : *reply_count = state->dreply.reply_count;
2569 0 : *reply = talloc_steal(mem_ctx, state->dreply.reply);
2570 :
2571 0 : return EOK;
2572 : }
2573 :
2574 : /* ==Posix attribute presence test================================= */
2575 : static errno_t sdap_posix_check_next(struct tevent_req *req);
2576 : static void sdap_posix_check_done(struct tevent_req *subreq);
2577 : static errno_t sdap_posix_check_parse(struct sdap_handle *sh,
2578 : struct sdap_msg *msg,
2579 : void *pvt);
2580 :
2581 : struct sdap_posix_check_state {
2582 : struct tevent_context *ev;
2583 : struct sdap_options *opts;
2584 : struct sdap_handle *sh;
2585 : struct sdap_search_base **search_bases;
2586 : int timeout;
2587 :
2588 : const char **attrs;
2589 : const char *filter;
2590 : size_t base_iter;
2591 :
2592 : bool has_posix;
2593 : };
2594 :
2595 : struct tevent_req *
2596 0 : sdap_posix_check_send(TALLOC_CTX *memctx, struct tevent_context *ev,
2597 : struct sdap_options *opts, struct sdap_handle *sh,
2598 : struct sdap_search_base **search_bases,
2599 : int timeout)
2600 : {
2601 0 : struct tevent_req *req = NULL;
2602 : struct sdap_posix_check_state *state;
2603 : errno_t ret;
2604 :
2605 0 : req = tevent_req_create(memctx, &state, struct sdap_posix_check_state);
2606 0 : if (req == NULL) {
2607 0 : return NULL;
2608 : }
2609 0 : state->ev = ev;
2610 0 : state->sh = sh;
2611 0 : state->opts = opts;
2612 0 : state->search_bases = search_bases;
2613 0 : state->timeout = timeout;
2614 :
2615 0 : state->attrs = talloc_array(state, const char *, 4);
2616 0 : if (state->attrs == NULL) {
2617 0 : ret = ENOMEM;
2618 0 : goto fail;
2619 : }
2620 0 : state->attrs[0] = "objectclass";
2621 0 : state->attrs[1] = opts->user_map[SDAP_AT_USER_UID].name;
2622 0 : state->attrs[2] = opts->group_map[SDAP_AT_GROUP_GID].name;
2623 0 : state->attrs[3] = NULL;
2624 :
2625 0 : state->filter = talloc_asprintf(state,
2626 : "(|(&(%s=*)(objectclass=%s))(&(%s=*)(objectclass=%s)))",
2627 0 : opts->user_map[SDAP_AT_USER_UID].name,
2628 0 : opts->user_map[SDAP_OC_USER].name,
2629 0 : opts->group_map[SDAP_AT_GROUP_GID].name,
2630 0 : opts->group_map[SDAP_OC_GROUP].name);
2631 0 : if (state->filter == NULL) {
2632 0 : ret = ENOMEM;
2633 0 : goto fail;
2634 : }
2635 :
2636 0 : ret = sdap_posix_check_next(req);
2637 0 : if (ret != EOK) {
2638 0 : goto fail;
2639 : }
2640 :
2641 0 : return req;
2642 :
2643 : fail:
2644 0 : tevent_req_error(req, ret);
2645 0 : tevent_req_post(req, ev);
2646 0 : return req;
2647 : }
2648 :
2649 0 : static errno_t sdap_posix_check_next(struct tevent_req *req)
2650 : {
2651 0 : struct tevent_req *subreq = NULL;
2652 0 : struct sdap_posix_check_state *state =
2653 0 : tevent_req_data(req, struct sdap_posix_check_state);
2654 :
2655 0 : DEBUG(SSSDBG_TRACE_FUNC,
2656 : "Searching for POSIX attributes with base [%s]\n",
2657 : state->search_bases[state->base_iter]->basedn);
2658 :
2659 0 : subreq = sdap_get_generic_ext_send(state, state->ev, state->opts,
2660 : state->sh,
2661 0 : state->search_bases[state->base_iter]->basedn,
2662 : LDAP_SCOPE_SUBTREE, state->filter,
2663 : state->attrs,
2664 : NULL, NULL, 1, state->timeout,
2665 : sdap_posix_check_parse, state,
2666 : SDAP_SRCH_FLG_SIZELIMIT_SILENT);
2667 0 : if (subreq == NULL) {
2668 0 : return ENOMEM;
2669 : }
2670 0 : tevent_req_set_callback(subreq, sdap_posix_check_done, req);
2671 :
2672 0 : return EOK;
2673 : }
2674 :
2675 0 : static errno_t sdap_posix_check_parse(struct sdap_handle *sh,
2676 : struct sdap_msg *msg,
2677 : void *pvt)
2678 : {
2679 0 : struct berval **vals = NULL;
2680 0 : struct sdap_posix_check_state *state =
2681 : talloc_get_type(pvt, struct sdap_posix_check_state);
2682 : char *dn;
2683 : char *endptr;
2684 :
2685 0 : dn = ldap_get_dn(sh->ldap, msg->msg);
2686 0 : if (dn == NULL) {
2687 0 : DEBUG(SSSDBG_TRACE_LIBS,
2688 : "Search did not find any entry with POSIX attributes\n");
2689 0 : goto done;
2690 : }
2691 0 : DEBUG(SSSDBG_TRACE_LIBS, "Found [%s] with POSIX attributes\n", dn);
2692 0 : ldap_memfree(dn);
2693 :
2694 0 : vals = ldap_get_values_len(sh->ldap, msg->msg,
2695 0 : state->opts->user_map[SDAP_AT_USER_UID].name);
2696 0 : if (vals == NULL) {
2697 0 : vals = ldap_get_values_len(sh->ldap, msg->msg,
2698 0 : state->opts->group_map[SDAP_AT_GROUP_GID].name);
2699 0 : if (vals == NULL) {
2700 0 : DEBUG(SSSDBG_TRACE_LIBS, "Entry does not have POSIX attrs?\n");
2701 0 : goto done;
2702 : }
2703 : }
2704 :
2705 0 : if (vals[0] == NULL) {
2706 0 : DEBUG(SSSDBG_TRACE_LIBS, "No value for POSIX attr\n");
2707 0 : goto done;
2708 : }
2709 :
2710 0 : errno = 0;
2711 0 : strtouint32(vals[0]->bv_val, &endptr, 10);
2712 0 : if (errno || *endptr || (vals[0]->bv_val == endptr)) {
2713 0 : DEBUG(SSSDBG_MINOR_FAILURE,
2714 : "POSIX attribute is not a number: %s\n", vals[0]->bv_val);
2715 : }
2716 :
2717 0 : state->has_posix = true;
2718 : done:
2719 0 : ldap_value_free_len(vals);
2720 0 : return EOK;
2721 : }
2722 :
2723 0 : static void sdap_posix_check_done(struct tevent_req *subreq)
2724 : {
2725 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
2726 : struct tevent_req);
2727 0 : struct sdap_posix_check_state *state =
2728 0 : tevent_req_data(req, struct sdap_posix_check_state);
2729 : errno_t ret;
2730 :
2731 0 : ret = sdap_get_generic_ext_recv(subreq, NULL, NULL, NULL);
2732 0 : talloc_zfree(subreq);
2733 0 : if (ret != EOK) {
2734 0 : DEBUG(SSSDBG_OP_FAILURE,
2735 : "sdap_get_generic_ext_recv failed [%d]: %s\n",
2736 : ret, strerror(ret));
2737 0 : tevent_req_error(req, ret);
2738 0 : return;
2739 : }
2740 :
2741 : /* Positive hit is definitve, no need to search other bases */
2742 0 : if (state->has_posix == true) {
2743 0 : DEBUG(SSSDBG_FUNC_DATA, "Server has POSIX attributes\n");
2744 0 : tevent_req_done(req);
2745 0 : return;
2746 : }
2747 :
2748 0 : state->base_iter++;
2749 0 : if (state->search_bases[state->base_iter]) {
2750 : /* There are more search bases to try */
2751 0 : ret = sdap_posix_check_next(req);
2752 0 : if (ret != EOK) {
2753 0 : tevent_req_error(req, ret);
2754 : }
2755 0 : return;
2756 : }
2757 :
2758 : /* All bases done! */
2759 0 : DEBUG(SSSDBG_TRACE_LIBS, "Cycled through all bases\n");
2760 0 : tevent_req_done(req);
2761 : }
2762 :
2763 0 : int sdap_posix_check_recv(struct tevent_req *req,
2764 : bool *_has_posix)
2765 : {
2766 0 : struct sdap_posix_check_state *state = tevent_req_data(req,
2767 : struct sdap_posix_check_state);
2768 :
2769 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
2770 :
2771 0 : *_has_posix = state->has_posix;
2772 0 : return EOK;
2773 : }
2774 :
2775 : /* ==Generic Deref Search============================================ */
2776 : enum sdap_deref_type {
2777 : SDAP_DEREF_OPENLDAP,
2778 : SDAP_DEREF_ASQ
2779 : };
2780 :
2781 : struct sdap_deref_search_state {
2782 : struct sdap_handle *sh;
2783 : const char *base_dn;
2784 : const char *deref_attr;
2785 :
2786 : size_t reply_count;
2787 : struct sdap_deref_attrs **reply;
2788 : enum sdap_deref_type deref_type;
2789 : unsigned flags;
2790 : };
2791 :
2792 : static void sdap_deref_search_done(struct tevent_req *subreq);
2793 : static void sdap_deref_search_with_filter_done(struct tevent_req *subreq);
2794 :
2795 : struct tevent_req *
2796 0 : sdap_deref_search_with_filter_send(TALLOC_CTX *memctx,
2797 : struct tevent_context *ev,
2798 : struct sdap_options *opts,
2799 : struct sdap_handle *sh,
2800 : const char *search_base,
2801 : const char *filter,
2802 : const char *deref_attr,
2803 : const char **attrs,
2804 : int num_maps,
2805 : struct sdap_attr_map_info *maps,
2806 : int timeout,
2807 : unsigned flags)
2808 : {
2809 0 : struct tevent_req *req = NULL;
2810 0 : struct tevent_req *subreq = NULL;
2811 : struct sdap_deref_search_state *state;
2812 :
2813 0 : req = tevent_req_create(memctx, &state, struct sdap_deref_search_state);
2814 0 : if (!req) return NULL;
2815 :
2816 0 : state->sh = sh;
2817 0 : state->reply_count = 0;
2818 0 : state->reply = NULL;
2819 0 : state->flags = flags;
2820 :
2821 0 : if (sdap_is_control_supported(sh, LDAP_CONTROL_X_DEREF)) {
2822 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Server supports OpenLDAP deref\n");
2823 0 : state->deref_type = SDAP_DEREF_OPENLDAP;
2824 :
2825 0 : subreq = sdap_x_deref_search_send(state, ev, opts, sh, search_base,
2826 : filter, deref_attr, attrs, maps,
2827 : num_maps, timeout);
2828 0 : if (!subreq) {
2829 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot start OpenLDAP deref search\n");
2830 0 : goto fail;
2831 : }
2832 : } else {
2833 0 : DEBUG(SSSDBG_OP_FAILURE,
2834 : "Server does not support any known deref method!\n");
2835 0 : goto fail;
2836 : }
2837 :
2838 0 : tevent_req_set_callback(subreq, sdap_deref_search_with_filter_done, req);
2839 0 : return req;
2840 :
2841 : fail:
2842 0 : talloc_zfree(req);
2843 0 : return NULL;
2844 : }
2845 :
2846 0 : static void sdap_deref_search_with_filter_done(struct tevent_req *subreq)
2847 : {
2848 0 : sdap_deref_search_done(subreq);
2849 0 : }
2850 :
2851 0 : int sdap_deref_search_with_filter_recv(struct tevent_req *req,
2852 : TALLOC_CTX *mem_ctx,
2853 : size_t *reply_count,
2854 : struct sdap_deref_attrs ***reply)
2855 : {
2856 0 : return sdap_deref_search_recv(req, mem_ctx, reply_count, reply);
2857 : }
2858 :
2859 : struct tevent_req *
2860 0 : sdap_deref_search_send(TALLOC_CTX *memctx,
2861 : struct tevent_context *ev,
2862 : struct sdap_options *opts,
2863 : struct sdap_handle *sh,
2864 : const char *base_dn,
2865 : const char *deref_attr,
2866 : const char **attrs,
2867 : int num_maps,
2868 : struct sdap_attr_map_info *maps,
2869 : int timeout)
2870 : {
2871 0 : struct tevent_req *req = NULL;
2872 0 : struct tevent_req *subreq = NULL;
2873 : struct sdap_deref_search_state *state;
2874 :
2875 0 : req = tevent_req_create(memctx, &state, struct sdap_deref_search_state);
2876 0 : if (!req) return NULL;
2877 :
2878 0 : state->sh = sh;
2879 0 : state->reply_count = 0;
2880 0 : state->reply = NULL;
2881 0 : state->base_dn = base_dn;
2882 0 : state->deref_attr = deref_attr;
2883 :
2884 : PROBE(SDAP_DEREF_SEARCH_SEND, state->base_dn, state->deref_attr);
2885 :
2886 0 : if (sdap_is_control_supported(sh, LDAP_SERVER_ASQ_OID)) {
2887 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Server supports ASQ\n");
2888 0 : state->deref_type = SDAP_DEREF_ASQ;
2889 :
2890 0 : subreq = sdap_asq_search_send(state, ev, opts, sh, base_dn,
2891 : deref_attr, attrs, maps, num_maps,
2892 : timeout);
2893 0 : if (!subreq) {
2894 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot start ASQ search\n");
2895 0 : goto fail;
2896 : }
2897 0 : } else if (sdap_is_control_supported(sh, LDAP_CONTROL_X_DEREF)) {
2898 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Server supports OpenLDAP deref\n");
2899 0 : state->deref_type = SDAP_DEREF_OPENLDAP;
2900 :
2901 0 : subreq = sdap_x_deref_search_send(state, ev, opts, sh, base_dn, NULL,
2902 : deref_attr, attrs, maps, num_maps,
2903 : timeout);
2904 0 : if (!subreq) {
2905 0 : DEBUG(SSSDBG_OP_FAILURE, "Cannot start OpenLDAP deref search\n");
2906 0 : goto fail;
2907 : }
2908 : } else {
2909 0 : DEBUG(SSSDBG_OP_FAILURE,
2910 : "Server does not support any known deref method!\n");
2911 0 : goto fail;
2912 : }
2913 :
2914 0 : tevent_req_set_callback(subreq, sdap_deref_search_done, req);
2915 0 : return req;
2916 :
2917 : fail:
2918 0 : talloc_zfree(req);
2919 0 : return NULL;
2920 : }
2921 :
2922 0 : static void sdap_deref_search_done(struct tevent_req *subreq)
2923 : {
2924 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
2925 : struct tevent_req);
2926 0 : struct sdap_deref_search_state *state = tevent_req_data(req,
2927 : struct sdap_deref_search_state);
2928 : int ret;
2929 :
2930 0 : switch (state->deref_type) {
2931 : case SDAP_DEREF_OPENLDAP:
2932 0 : ret = sdap_x_deref_search_recv(subreq, state,
2933 : &state->reply_count, &state->reply);
2934 0 : break;
2935 : case SDAP_DEREF_ASQ:
2936 0 : ret = sdap_asq_search_recv(subreq, state,
2937 : &state->reply_count, &state->reply);
2938 0 : break;
2939 : default:
2940 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unknown deref method\n");
2941 0 : tevent_req_error(req, EINVAL);
2942 0 : return;
2943 : }
2944 :
2945 0 : talloc_zfree(subreq);
2946 0 : if (ret != EOK) {
2947 0 : DEBUG(SSSDBG_OP_FAILURE,
2948 : "dereference processing failed [%d]: %s\n", ret, strerror(ret));
2949 0 : if (ret == ENOTSUP) {
2950 0 : state->sh->disable_deref = true;
2951 : }
2952 :
2953 0 : if (!(state->flags & SDAP_DEREF_FLG_SILENT)) {
2954 0 : if (ret == ENOTSUP) {
2955 0 : sss_log(SSS_LOG_WARNING,
2956 : "LDAP server claims to support deref, but deref search "
2957 : "failed. Disabling deref for further requests. You can "
2958 : "permanently disable deref by setting "
2959 : "ldap_deref_threshold to 0 in domain configuration.");
2960 : } else {
2961 0 : sss_log(SSS_LOG_WARNING,
2962 : "dereference processing failed : %s", strerror(ret));
2963 : }
2964 : }
2965 0 : tevent_req_error(req, ret);
2966 0 : return;
2967 : }
2968 :
2969 0 : tevent_req_done(req);
2970 : }
2971 :
2972 0 : int sdap_deref_search_recv(struct tevent_req *req,
2973 : TALLOC_CTX *mem_ctx,
2974 : size_t *reply_count,
2975 : struct sdap_deref_attrs ***reply)
2976 : {
2977 0 : struct sdap_deref_search_state *state = tevent_req_data(req,
2978 : struct sdap_deref_search_state);
2979 :
2980 : PROBE(SDAP_DEREF_SEARCH_RECV, state->base_dn, state->deref_attr);
2981 :
2982 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
2983 :
2984 0 : *reply_count = state->reply_count;
2985 0 : *reply = talloc_steal(mem_ctx, state->reply);
2986 :
2987 0 : return EOK;
2988 : }
2989 :
2990 0 : bool sdap_has_deref_support(struct sdap_handle *sh, struct sdap_options *opts)
2991 : {
2992 0 : const char *deref_oids[][2] = { { LDAP_SERVER_ASQ_OID, "ASQ" },
2993 : { LDAP_CONTROL_X_DEREF, "OpenLDAP" },
2994 : { NULL, NULL }
2995 : };
2996 : int i;
2997 : int deref_threshold;
2998 :
2999 0 : if (sh->disable_deref) {
3000 0 : return false;
3001 : }
3002 :
3003 0 : deref_threshold = dp_opt_get_int(opts->basic, SDAP_DEREF_THRESHOLD);
3004 0 : if (deref_threshold == 0) {
3005 0 : return false;
3006 : }
3007 :
3008 0 : for (i=0; deref_oids[i][0]; i++) {
3009 0 : if (sdap_is_control_supported(sh, deref_oids[i][0])) {
3010 0 : DEBUG(SSSDBG_TRACE_FUNC, "The server supports deref method %s\n",
3011 : deref_oids[i][1]);
3012 0 : return true;
3013 : }
3014 : }
3015 :
3016 0 : return false;
3017 : }
|