2 Unix SMB/CIFS implementation.
3 Infrastructure for async ldap client requests
4 Copyright (C) Volker Lendecke 2009
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
22 #include "system/network.h"
23 #include "system/locale.h"
24 #include "lib/util/talloc_stack.h"
25 #include "lib/util/samba_util.h"
26 #include "lib/util_tsock.h"
27 #include "../lib/util/asn1.h"
28 #include "../lib/tsocket/tsocket.h"
29 #include "../lib/util/tevent_unix.h"
31 static TLDAPRC
tldap_simple_recv(struct tevent_req
*req
);
33 #define TEVENT_TLDAP_RC_MAGIC (0x87bcd26e)
35 bool tevent_req_ldap_error(struct tevent_req
*req
, TLDAPRC rc
)
39 if (TLDAP_RC_IS_SUCCESS(rc
)) {
43 err
= TEVENT_TLDAP_RC_MAGIC
;
45 err
|= TLDAP_RC_V(rc
);
47 return tevent_req_error(req
, err
);
50 bool tevent_req_is_ldap_error(struct tevent_req
*req
, TLDAPRC
*perr
)
52 enum tevent_req_state state
;
55 if (!tevent_req_is_error(req
, &state
, &err
)) {
59 case TEVENT_REQ_TIMED_OUT
:
60 *perr
= TLDAP_TIMEOUT
;
62 case TEVENT_REQ_NO_MEMORY
:
63 *perr
= TLDAP_NO_MEMORY
;
65 case TEVENT_REQ_USER_ERROR
:
66 if ((err
>> 32) != TEVENT_TLDAP_RC_MAGIC
) {
69 *perr
= TLDAP_RC(err
& 0xffffffff);
72 *perr
= TLDAP_OPERATIONS_ERROR
;
78 struct tldap_ctx_attribute
{
83 struct tldap_context
{
85 struct tstream_context
*conn
;
88 struct tevent_queue
*outgoing
;
89 struct tevent_req
**pending
;
91 /* For the sync wrappers we need something like get_last_error... */
92 struct tldap_message
*last_msg
;
95 void (*log_fn
)(void *context
, enum tldap_debug_level level
,
96 const char *fmt
, va_list ap
);
99 struct tldap_ctx_attribute
*ctx_attrs
;
102 struct tldap_message
{
103 struct asn1_data
*data
;
110 struct tldap_attribute
*attribs
;
112 /* Error data sent by the server */
115 char *res_diagnosticmessage
;
117 DATA_BLOB res_serverSaslCreds
;
118 struct tldap_control
*res_sctrls
;
120 /* Controls sent by the server */
121 struct tldap_control
*ctrls
;
124 void tldap_set_debug(struct tldap_context
*ld
,
125 void (*log_fn
)(void *log_private
,
126 enum tldap_debug_level level
,
128 va_list ap
) PRINTF_ATTRIBUTE(3,0),
132 ld
->log_private
= log_private
;
135 static void tldap_debug(struct tldap_context
*ld
,
136 enum tldap_debug_level level
,
137 const char *fmt
, ...)
143 if (ld
->log_fn
== NULL
) {
147 ld
->log_fn(ld
->log_private
, level
, fmt
, ap
);
151 static int tldap_next_msgid(struct tldap_context
*ld
)
155 result
= ld
->msgid
++;
156 if (ld
->msgid
== 2147483647) {
162 struct tldap_context
*tldap_context_create(TALLOC_CTX
*mem_ctx
, int fd
)
164 struct tldap_context
*ctx
;
167 ctx
= talloc_zero(mem_ctx
, struct tldap_context
);
171 ret
= tstream_bsd_existing_socket(ctx
, fd
, &ctx
->conn
);
178 ctx
->outgoing
= tevent_queue_create(ctx
, "tldap_outgoing");
179 if (ctx
->outgoing
== NULL
) {
186 bool tldap_connection_ok(struct tldap_context
*ld
)
191 return !ld
->server_down
;
194 static size_t tldap_pending_reqs(struct tldap_context
*ld
)
196 return talloc_array_length(ld
->pending
);
199 struct tstream_context
*tldap_get_tstream(struct tldap_context
*ld
)
204 void tldap_set_tstream(struct tldap_context
*ld
,
205 struct tstream_context
*stream
)
210 static struct tldap_ctx_attribute
*tldap_context_findattr(
211 struct tldap_context
*ld
, const char *name
)
215 num_attrs
= talloc_array_length(ld
->ctx_attrs
);
217 for (i
=0; i
<num_attrs
; i
++) {
218 if (strcmp(ld
->ctx_attrs
[i
].name
, name
) == 0) {
219 return &ld
->ctx_attrs
[i
];
225 bool tldap_context_setattr(struct tldap_context
*ld
,
226 const char *name
, const void *_pptr
)
228 struct tldap_ctx_attribute
*tmp
, *attr
;
231 void **pptr
= (void **)discard_const_p(void,_pptr
);
233 attr
= tldap_context_findattr(ld
, name
);
236 * We don't actually delete attrs, we don't expect tons of
237 * attributes being shuffled around.
239 TALLOC_FREE(attr
->ptr
);
241 attr
->ptr
= talloc_move(ld
->ctx_attrs
, pptr
);
247 tmpname
= talloc_strdup(ld
, name
);
248 if (tmpname
== NULL
) {
252 num_attrs
= talloc_array_length(ld
->ctx_attrs
);
254 tmp
= talloc_realloc(ld
, ld
->ctx_attrs
, struct tldap_ctx_attribute
,
257 TALLOC_FREE(tmpname
);
260 tmp
[num_attrs
].name
= talloc_move(tmp
, &tmpname
);
262 tmp
[num_attrs
].ptr
= talloc_move(tmp
, pptr
);
264 tmp
[num_attrs
].ptr
= NULL
;
271 void *tldap_context_getattr(struct tldap_context
*ld
, const char *name
)
273 struct tldap_ctx_attribute
*attr
= tldap_context_findattr(ld
, name
);
281 struct read_ldap_state
{
286 static ssize_t
read_ldap_more(uint8_t *buf
, size_t buflen
, void *private_data
);
287 static void read_ldap_done(struct tevent_req
*subreq
);
289 static struct tevent_req
*read_ldap_send(TALLOC_CTX
*mem_ctx
,
290 struct tevent_context
*ev
,
291 struct tstream_context
*conn
)
293 struct tevent_req
*req
, *subreq
;
294 struct read_ldap_state
*state
;
296 req
= tevent_req_create(mem_ctx
, &state
, struct read_ldap_state
);
302 subreq
= tstream_read_packet_send(state
, ev
, conn
, 2, read_ldap_more
,
304 if (tevent_req_nomem(subreq
, req
)) {
305 return tevent_req_post(req
, ev
);
307 tevent_req_set_callback(subreq
, read_ldap_done
, req
);
311 static ssize_t
read_ldap_more(uint8_t *buf
, size_t buflen
, void *private_data
)
313 struct read_ldap_state
*state
= talloc_get_type_abort(
314 private_data
, struct read_ldap_state
);
319 /* We've been here, we're done */
324 * From ldap.h: LDAP_TAG_MESSAGE is 0x30
326 if (buf
[0] != 0x30) {
331 if ((len
& 0x80) == 0) {
336 lensize
= (len
& 0x7f);
340 /* Please get us the full length */
343 if (buflen
> 2 + lensize
) {
347 if (buflen
!= 2 + lensize
) {
351 for (i
=0; i
<lensize
; i
++) {
352 len
= (len
<< 8) | buf
[2+i
];
357 static void read_ldap_done(struct tevent_req
*subreq
)
359 struct tevent_req
*req
= tevent_req_callback_data(
360 subreq
, struct tevent_req
);
361 struct read_ldap_state
*state
= tevent_req_data(
362 req
, struct read_ldap_state
);
366 nread
= tstream_read_packet_recv(subreq
, state
, &state
->buf
, &err
);
369 tevent_req_error(req
, err
);
372 tevent_req_done(req
);
375 static ssize_t
read_ldap_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
376 uint8_t **pbuf
, int *perrno
)
378 struct read_ldap_state
*state
= tevent_req_data(
379 req
, struct read_ldap_state
);
381 if (tevent_req_is_unix_error(req
, perrno
)) {
384 *pbuf
= talloc_move(mem_ctx
, &state
->buf
);
385 return talloc_get_size(*pbuf
);
388 struct tldap_msg_state
{
389 struct tldap_context
*ld
;
390 struct tevent_context
*ev
;
394 struct asn1_data
*data
;
398 static bool tldap_push_controls(struct asn1_data
*data
,
399 struct tldap_control
*sctrls
,
404 if ((sctrls
== NULL
) || (num_sctrls
== 0)) {
408 if (!asn1_push_tag(data
, ASN1_CONTEXT(0))) return false;
410 for (i
=0; i
<num_sctrls
; i
++) {
411 struct tldap_control
*c
= &sctrls
[i
];
412 if (!asn1_push_tag(data
, ASN1_SEQUENCE(0))) return false;
413 if (!asn1_write_OctetString(data
, c
->oid
, strlen(c
->oid
))) return false;
415 if (!asn1_write_BOOLEAN(data
, true)) return false;
417 if (c
->value
.data
!= NULL
) {
418 if (!asn1_write_OctetString(data
, c
->value
.data
,
419 c
->value
.length
)) return false;
421 if (!asn1_pop_tag(data
)) return false; /* ASN1_SEQUENCE(0) */
424 return asn1_pop_tag(data
); /* ASN1_CONTEXT(0) */
427 static void tldap_msg_sent(struct tevent_req
*subreq
);
428 static void tldap_msg_received(struct tevent_req
*subreq
);
430 static struct tevent_req
*tldap_msg_send(TALLOC_CTX
*mem_ctx
,
431 struct tevent_context
*ev
,
432 struct tldap_context
*ld
,
433 int id
, struct asn1_data
*data
,
434 struct tldap_control
*sctrls
,
437 struct tevent_req
*req
, *subreq
;
438 struct tldap_msg_state
*state
;
441 tldap_debug(ld
, TLDAP_DEBUG_TRACE
, "tldap_msg_send: sending msg %d\n",
444 req
= tevent_req_create(mem_ctx
, &state
, struct tldap_msg_state
);
452 if (state
->ld
->server_down
) {
453 tevent_req_ldap_error(req
, TLDAP_SERVER_DOWN
);
454 return tevent_req_post(req
, ev
);
457 if (!tldap_push_controls(data
, sctrls
, num_sctrls
)) {
458 tevent_req_ldap_error(req
, TLDAP_ENCODING_ERROR
);
459 return tevent_req_post(req
, ev
);
463 if (!asn1_pop_tag(data
)) {
464 tevent_req_ldap_error(req
, TLDAP_ENCODING_ERROR
);
465 return tevent_req_post(req
, ev
);
468 if (!asn1_blob(data
, &blob
)) {
469 tevent_req_ldap_error(req
, TLDAP_ENCODING_ERROR
);
470 return tevent_req_post(req
, ev
);
473 state
->iov
.iov_base
= (void *)blob
.data
;
474 state
->iov
.iov_len
= blob
.length
;
476 subreq
= tstream_writev_queue_send(state
, ev
, ld
->conn
, ld
->outgoing
,
478 if (tevent_req_nomem(subreq
, req
)) {
479 return tevent_req_post(req
, ev
);
481 tevent_req_set_callback(subreq
, tldap_msg_sent
, req
);
485 static void tldap_msg_unset_pending(struct tevent_req
*req
)
487 struct tldap_msg_state
*state
= tevent_req_data(
488 req
, struct tldap_msg_state
);
489 struct tldap_context
*ld
= state
->ld
;
490 int num_pending
= tldap_pending_reqs(ld
);
493 tevent_req_set_cleanup_fn(req
, NULL
);
495 if (num_pending
== 1) {
496 TALLOC_FREE(ld
->pending
);
500 for (i
=0; i
<num_pending
; i
++) {
501 if (req
== ld
->pending
[i
]) {
505 if (i
== num_pending
) {
507 * Something's seriously broken. Just returning here is the
508 * right thing nevertheless, the point of this routine is to
509 * remove ourselves from cli->pending.
515 * Remove ourselves from the cli->pending array
517 if (num_pending
> 1) {
518 ld
->pending
[i
] = ld
->pending
[num_pending
-1];
522 * No NULL check here, we're shrinking by sizeof(void *), and
523 * talloc_realloc just adjusts the size for this.
525 ld
->pending
= talloc_realloc(NULL
, ld
->pending
, struct tevent_req
*,
529 static void tldap_msg_cleanup(struct tevent_req
*req
,
530 enum tevent_req_state req_state
)
533 case TEVENT_REQ_USER_ERROR
:
534 case TEVENT_REQ_RECEIVED
:
535 tldap_msg_unset_pending(req
);
542 static bool tldap_msg_set_pending(struct tevent_req
*req
)
544 struct tldap_msg_state
*state
= tevent_req_data(
545 req
, struct tldap_msg_state
);
546 struct tldap_context
*ld
;
547 struct tevent_req
**pending
;
549 struct tevent_req
*subreq
;
552 num_pending
= tldap_pending_reqs(ld
);
554 pending
= talloc_realloc(ld
, ld
->pending
, struct tevent_req
*,
556 if (pending
== NULL
) {
559 pending
[num_pending
] = req
;
560 ld
->pending
= pending
;
561 tevent_req_set_cleanup_fn(req
, tldap_msg_cleanup
);
563 if (num_pending
> 0) {
568 * We're the first one, add the read_ldap request that waits for the
569 * answer from the server
571 subreq
= read_ldap_send(ld
->pending
, state
->ev
, ld
->conn
);
572 if (subreq
== NULL
) {
573 tldap_msg_unset_pending(req
);
576 tevent_req_set_callback(subreq
, tldap_msg_received
, ld
);
580 static void tldap_msg_sent(struct tevent_req
*subreq
)
582 struct tevent_req
*req
= tevent_req_callback_data(
583 subreq
, struct tevent_req
);
584 struct tldap_msg_state
*state
= tevent_req_data(
585 req
, struct tldap_msg_state
);
589 nwritten
= tstream_writev_queue_recv(subreq
, &err
);
591 if (nwritten
== -1) {
592 state
->ld
->server_down
= true;
593 tevent_req_ldap_error(req
, TLDAP_SERVER_DOWN
);
597 if (!tldap_msg_set_pending(req
)) {
603 static int tldap_msg_msgid(struct tevent_req
*req
)
605 struct tldap_msg_state
*state
= tevent_req_data(
606 req
, struct tldap_msg_state
);
611 static void tldap_msg_received(struct tevent_req
*subreq
)
613 struct tldap_context
*ld
= tevent_req_callback_data(
614 subreq
, struct tldap_context
);
615 struct tevent_req
*req
;
616 struct tldap_msg_state
*state
;
617 struct asn1_data
*data
;
627 received
= read_ldap_recv(subreq
, talloc_tos(), &inbuf
, &err
);
629 if (received
== -1) {
630 ld
->server_down
= true;
631 status
= TLDAP_SERVER_DOWN
;
635 data
= asn1_init(talloc_tos());
637 status
= TLDAP_NO_MEMORY
;
640 asn1_load_nocopy(data
, inbuf
, received
);
643 ok
&= asn1_start_tag(data
, ASN1_SEQUENCE(0));
644 ok
&= asn1_read_Integer(data
, &id
);
645 ok
&= asn1_peek_uint8(data
, &type
);
648 status
= TLDAP_PROTOCOL_ERROR
;
652 tldap_debug(ld
, TLDAP_DEBUG_TRACE
, "tldap_msg_received: got msg %d "
653 "type %d\n", id
, (int)type
);
655 num_pending
= talloc_array_length(ld
->pending
);
657 for (i
=0; i
<num_pending
; i
++) {
658 if (id
== tldap_msg_msgid(ld
->pending
[i
])) {
662 if (i
== num_pending
) {
663 /* Dump unexpected reply */
664 tldap_debug(ld
, TLDAP_DEBUG_WARNING
, "tldap_msg_received: "
665 "No request pending for msg %d\n", id
);
671 req
= ld
->pending
[i
];
672 state
= tevent_req_data(req
, struct tldap_msg_state
);
674 state
->inbuf
= talloc_move(state
, &inbuf
);
675 state
->data
= talloc_move(state
, &data
);
677 tldap_msg_unset_pending(req
);
678 num_pending
= talloc_array_length(ld
->pending
);
680 tevent_req_done(req
);
683 if (num_pending
== 0) {
686 if (talloc_array_length(ld
->pending
) > num_pending
) {
688 * The callback functions called from tevent_req_done() above
689 * have put something on the pending queue. We don't have to
690 * trigger the read_ldap_send(), tldap_msg_set_pending() has
691 * done it for us already.
696 state
= tevent_req_data(ld
->pending
[0], struct tldap_msg_state
);
697 subreq
= read_ldap_send(ld
->pending
, state
->ev
, ld
->conn
);
698 if (subreq
== NULL
) {
699 status
= TLDAP_NO_MEMORY
;
702 tevent_req_set_callback(subreq
, tldap_msg_received
, ld
);
706 while (talloc_array_length(ld
->pending
) > 0) {
707 req
= ld
->pending
[0];
708 state
= tevent_req_data(req
, struct tldap_msg_state
);
709 tevent_req_defer_callback(req
, state
->ev
);
710 tevent_req_ldap_error(req
, status
);
714 static TLDAPRC
tldap_msg_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
715 struct tldap_message
**pmsg
)
717 struct tldap_msg_state
*state
= tevent_req_data(
718 req
, struct tldap_msg_state
);
719 struct tldap_message
*msg
;
723 if (tevent_req_is_ldap_error(req
, &err
)) {
727 if (!asn1_peek_uint8(state
->data
, &msgtype
)) {
728 return TLDAP_PROTOCOL_ERROR
;
732 return TLDAP_SUCCESS
;
735 msg
= talloc_zero(mem_ctx
, struct tldap_message
);
737 return TLDAP_NO_MEMORY
;
741 msg
->inbuf
= talloc_move(msg
, &state
->inbuf
);
742 msg
->data
= talloc_move(msg
, &state
->data
);
746 return TLDAP_SUCCESS
;
749 struct tldap_req_state
{
751 struct asn1_data
*out
;
752 struct tldap_message
*result
;
755 static struct tevent_req
*tldap_req_create(TALLOC_CTX
*mem_ctx
,
756 struct tldap_context
*ld
,
757 struct tldap_req_state
**pstate
)
759 struct tevent_req
*req
;
760 struct tldap_req_state
*state
;
762 req
= tevent_req_create(mem_ctx
, &state
, struct tldap_req_state
);
766 state
->out
= asn1_init(state
);
767 if (state
->out
== NULL
) {
770 state
->id
= tldap_next_msgid(ld
);
772 if (!asn1_push_tag(state
->out
, ASN1_SEQUENCE(0))) goto err
;
773 if (!asn1_write_Integer(state
->out
, state
->id
)) goto err
;
784 static void tldap_save_msg(struct tldap_context
*ld
, struct tevent_req
*req
)
786 struct tldap_req_state
*state
= tevent_req_data(
787 req
, struct tldap_req_state
);
789 TALLOC_FREE(ld
->last_msg
);
790 ld
->last_msg
= talloc_move(ld
, &state
->result
);
793 static char *blob2string_talloc(TALLOC_CTX
*mem_ctx
, DATA_BLOB blob
)
795 char *result
= talloc_array(mem_ctx
, char, blob
.length
+1);
797 if (result
== NULL
) {
801 memcpy(result
, blob
.data
, blob
.length
);
802 result
[blob
.length
] = '\0';
806 static bool asn1_read_OctetString_talloc(TALLOC_CTX
*mem_ctx
,
807 struct asn1_data
*data
,
812 if (!asn1_read_OctetString(data
, mem_ctx
, &string
))
815 result
= blob2string_talloc(mem_ctx
, string
);
817 data_blob_free(&string
);
819 if (result
== NULL
) {
826 static bool tldap_decode_controls(struct tldap_req_state
*state
);
828 static bool tldap_decode_response(struct tldap_req_state
*state
)
830 struct asn1_data
*data
= state
->result
->data
;
831 struct tldap_message
*msg
= state
->result
;
835 ok
&= asn1_read_enumerated(data
, &rc
);
837 msg
->lderr
= TLDAP_RC(rc
);
840 ok
&= asn1_read_OctetString_talloc(msg
, data
, &msg
->res_matcheddn
);
841 ok
&= asn1_read_OctetString_talloc(msg
, data
,
842 &msg
->res_diagnosticmessage
);
844 if (asn1_peek_tag(data
, ASN1_CONTEXT(3))) {
845 ok
&= asn1_start_tag(data
, ASN1_CONTEXT(3));
846 ok
&= asn1_read_OctetString_talloc(msg
, data
,
848 ok
&= asn1_end_tag(data
);
850 msg
->res_referral
= NULL
;
856 static void tldap_sasl_bind_done(struct tevent_req
*subreq
);
858 struct tevent_req
*tldap_sasl_bind_send(TALLOC_CTX
*mem_ctx
,
859 struct tevent_context
*ev
,
860 struct tldap_context
*ld
,
862 const char *mechanism
,
864 struct tldap_control
*sctrls
,
866 struct tldap_control
*cctrls
,
869 struct tevent_req
*req
, *subreq
;
870 struct tldap_req_state
*state
;
872 req
= tldap_req_create(mem_ctx
, ld
, &state
);
881 if (!asn1_push_tag(state
->out
, TLDAP_REQ_BIND
)) goto err
;
882 if (!asn1_write_Integer(state
->out
, ld
->ld_version
)) goto err
;
883 if (!asn1_write_OctetString(state
->out
, dn
, strlen(dn
))) goto err
;
885 if (mechanism
== NULL
) {
886 if (!asn1_push_tag(state
->out
, ASN1_CONTEXT_SIMPLE(0))) goto err
;
887 if (!asn1_write(state
->out
, creds
->data
, creds
->length
)) goto err
;
888 if (!asn1_pop_tag(state
->out
)) goto err
;
890 if (!asn1_push_tag(state
->out
, ASN1_CONTEXT(3))) goto err
;
891 if (!asn1_write_OctetString(state
->out
, mechanism
,
892 strlen(mechanism
))) goto err
;
893 if ((creds
!= NULL
) && (creds
->data
!= NULL
)) {
894 if (!asn1_write_OctetString(state
->out
, creds
->data
,
895 creds
->length
)) goto err
;
897 if (!asn1_pop_tag(state
->out
)) goto err
;
900 if (!asn1_pop_tag(state
->out
)) goto err
;
902 subreq
= tldap_msg_send(state
, ev
, ld
, state
->id
, state
->out
,
904 if (tevent_req_nomem(subreq
, req
)) {
905 return tevent_req_post(req
, ev
);
907 tevent_req_set_callback(subreq
, tldap_sasl_bind_done
, req
);
912 tevent_req_ldap_error(req
, TLDAP_ENCODING_ERROR
);
913 return tevent_req_post(req
, ev
);
916 static void tldap_sasl_bind_done(struct tevent_req
*subreq
)
918 struct tevent_req
*req
= tevent_req_callback_data(
919 subreq
, struct tevent_req
);
920 struct tldap_req_state
*state
= tevent_req_data(
921 req
, struct tldap_req_state
);
925 rc
= tldap_msg_recv(subreq
, state
, &state
->result
);
927 if (tevent_req_ldap_error(req
, rc
)) {
930 if (state
->result
->type
!= TLDAP_RES_BIND
) {
931 tevent_req_ldap_error(req
, TLDAP_PROTOCOL_ERROR
);
935 ok
= asn1_start_tag(state
->result
->data
, TLDAP_RES_BIND
);
936 ok
&= tldap_decode_response(state
);
938 if (asn1_peek_tag(state
->result
->data
, ASN1_CONTEXT_SIMPLE(7))) {
941 ok
&= asn1_start_tag(state
->result
->data
,
942 ASN1_CONTEXT_SIMPLE(7));
947 len
= asn1_tag_remaining(state
->result
->data
);
952 state
->result
->res_serverSaslCreds
=
953 data_blob_talloc(state
->result
, NULL
, len
);
954 if (state
->result
->res_serverSaslCreds
.data
== NULL
) {
958 ok
= asn1_read(state
->result
->data
,
959 state
->result
->res_serverSaslCreds
.data
,
960 state
->result
->res_serverSaslCreds
.length
);
962 ok
&= asn1_end_tag(state
->result
->data
);
965 ok
&= asn1_end_tag(state
->result
->data
);
971 if (!TLDAP_RC_IS_SUCCESS(state
->result
->lderr
) &&
972 !TLDAP_RC_EQUAL(state
->result
->lderr
,
973 TLDAP_SASL_BIND_IN_PROGRESS
)) {
974 tevent_req_ldap_error(req
, state
->result
->lderr
);
977 tevent_req_done(req
);
981 tevent_req_ldap_error(req
, TLDAP_DECODING_ERROR
);
985 TLDAPRC
tldap_sasl_bind_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
986 DATA_BLOB
*serverSaslCreds
)
988 struct tldap_req_state
*state
= tevent_req_data(
989 req
, struct tldap_req_state
);
992 if (tevent_req_is_ldap_error(req
, &rc
)) {
996 if (serverSaslCreds
!= NULL
) {
997 serverSaslCreds
->data
= talloc_move(
998 mem_ctx
, &state
->result
->res_serverSaslCreds
.data
);
999 serverSaslCreds
->length
=
1000 state
->result
->res_serverSaslCreds
.length
;
1003 return state
->result
->lderr
;
1006 TLDAPRC
tldap_sasl_bind(struct tldap_context
*ld
,
1008 const char *mechanism
,
1010 struct tldap_control
*sctrls
,
1012 struct tldap_control
*cctrls
,
1014 TALLOC_CTX
*mem_ctx
,
1015 DATA_BLOB
*serverSaslCreds
)
1017 TALLOC_CTX
*frame
= talloc_stackframe();
1018 struct tevent_context
*ev
;
1019 struct tevent_req
*req
;
1020 TLDAPRC rc
= TLDAP_NO_MEMORY
;
1022 ev
= samba_tevent_context_init(frame
);
1026 req
= tldap_sasl_bind_send(frame
, ev
, ld
, dn
, mechanism
, creds
,
1027 sctrls
, num_sctrls
, cctrls
, num_cctrls
);
1031 if (!tevent_req_poll(req
, ev
)) {
1032 rc
= TLDAP_OPERATIONS_ERROR
;
1035 rc
= tldap_sasl_bind_recv(req
, mem_ctx
, serverSaslCreds
);
1036 tldap_save_msg(ld
, req
);
1042 struct tevent_req
*tldap_simple_bind_send(TALLOC_CTX
*mem_ctx
,
1043 struct tevent_context
*ev
,
1044 struct tldap_context
*ld
,
1050 if (passwd
!= NULL
) {
1051 cred
.data
= discard_const_p(uint8_t, passwd
);
1052 cred
.length
= strlen(passwd
);
1054 cred
.data
= discard_const_p(uint8_t, "");
1057 return tldap_sasl_bind_send(mem_ctx
, ev
, ld
, dn
, NULL
, &cred
, NULL
, 0,
1061 TLDAPRC
tldap_simple_bind_recv(struct tevent_req
*req
)
1063 return tldap_sasl_bind_recv(req
, NULL
, NULL
);
1066 TLDAPRC
tldap_simple_bind(struct tldap_context
*ld
, const char *dn
,
1071 if (passwd
!= NULL
) {
1072 cred
.data
= discard_const_p(uint8_t, passwd
);
1073 cred
.length
= strlen(passwd
);
1075 cred
.data
= discard_const_p(uint8_t, "");
1078 return tldap_sasl_bind(ld
, dn
, NULL
, &cred
, NULL
, 0, NULL
, 0,
1082 /*****************************************************************************/
1084 /* can't use isalpha() as only a strict set is valid for LDAP */
1086 static bool tldap_is_alpha(char c
)
1088 return (((c
>= 'a') && (c
<= 'z')) || \
1089 ((c
>= 'A') && (c
<= 'Z')));
1092 static bool tldap_is_adh(char c
)
1094 return tldap_is_alpha(c
) || isdigit(c
) || (c
== '-');
1097 #define TLDAP_FILTER_AND ASN1_CONTEXT(0)
1098 #define TLDAP_FILTER_OR ASN1_CONTEXT(1)
1099 #define TLDAP_FILTER_NOT ASN1_CONTEXT(2)
1100 #define TLDAP_FILTER_EQ ASN1_CONTEXT(3)
1101 #define TLDAP_FILTER_SUB ASN1_CONTEXT(4)
1102 #define TLDAP_FILTER_LE ASN1_CONTEXT(5)
1103 #define TLDAP_FILTER_GE ASN1_CONTEXT(6)
1104 #define TLDAP_FILTER_PRES ASN1_CONTEXT_SIMPLE(7)
1105 #define TLDAP_FILTER_APX ASN1_CONTEXT(8)
1106 #define TLDAP_FILTER_EXT ASN1_CONTEXT(9)
1108 #define TLDAP_SUB_INI ASN1_CONTEXT_SIMPLE(0)
1109 #define TLDAP_SUB_ANY ASN1_CONTEXT_SIMPLE(1)
1110 #define TLDAP_SUB_FIN ASN1_CONTEXT_SIMPLE(2)
1113 /* oid's should be numerical only in theory,
1114 * but apparently some broken servers may have alphanum aliases instead.
1115 * Do like openldap libraries and allow alphanum aliases for oids, but
1116 * do not allow Tagging options in that case.
1118 static bool tldap_is_attrdesc(const char *s
, int len
, bool no_tagopts
)
1120 bool is_oid
= false;
1124 /* first char has stricter rules */
1127 } else if (!tldap_is_alpha(*s
)) {
1128 /* bad first char */
1132 for (i
= 1; i
< len
; i
++) {
1135 if (isdigit(s
[i
])) {
1148 if (tldap_is_adh(s
[i
])) {
1155 /* no tagging options */
1162 if ((i
+ 1) == len
) {
1180 /* this function copies the value until the closing parenthesis is found. */
1181 static char *tldap_get_val(TALLOC_CTX
*memctx
,
1182 const char *value
, const char **_s
)
1184 const char *s
= value
;
1186 /* find terminator */
1189 if (s
&& (*(s
- 1) == '\\')) {
1194 if (!s
|| !(*s
== ')')) {
1195 /* malformed filter */
1201 return talloc_strndup(memctx
, value
, s
- value
);
1204 static int tldap_hex2char(const char *x
)
1206 if (isxdigit(x
[0]) && isxdigit(x
[1])) {
1207 const char h1
= x
[0], h2
= x
[1];
1210 if (h1
>= 'a') c
= h1
- (int)'a' + 10;
1211 else if (h1
>= 'A') c
= h1
- (int)'A' + 10;
1212 else if (h1
>= '0') c
= h1
- (int)'0';
1214 if (h2
>= 'a') c
+= h2
- (int)'a' + 10;
1215 else if (h2
>= 'A') c
+= h2
- (int)'A' + 10;
1216 else if (h2
>= '0') c
+= h2
- (int)'0';
1224 static bool tldap_find_first_star(const char *val
, const char **star
)
1228 for (s
= val
; *s
; s
++) {
1231 if (isxdigit(s
[1]) && isxdigit(s
[2])) {
1235 /* not hex based escape, check older syntax */
1244 /* invalid escape sequence */
1249 /* end of val, nothing found */
1259 /* string ended without closing parenthesis, filter is malformed */
1263 static bool tldap_unescape_inplace(char *value
, size_t *val_len
)
1268 for (i
= 0,p
= 0; i
< *val_len
; i
++) {
1274 /* these must be escaped */
1278 if (!value
[i
+ 1]) {
1284 /* LDAPv3 escaped */
1285 c
= tldap_hex2char(&value
[i
]);
1286 if (c
>= 0 && c
< 256) {
1293 /* LDAPv2 escaped */
1299 value
[p
] = value
[i
];
1310 value
[p
] = value
[i
];
1319 static bool tldap_push_filter_basic(struct tldap_context
*ld
,
1320 struct asn1_data
*data
,
1322 static bool tldap_push_filter_substring(struct tldap_context
*ld
,
1323 struct asn1_data
*data
,
1326 static bool tldap_push_filter_int(struct tldap_context
*ld
,
1327 struct asn1_data
*data
,
1330 const char *s
= *_s
;
1334 tldap_debug(ld
, TLDAP_DEBUG_ERROR
,
1335 "Incomplete or malformed filter\n");
1340 /* we are right after a parenthesis,
1341 * find out what op we have at hand */
1344 tldap_debug(ld
, TLDAP_DEBUG_TRACE
, "Filter op: AND\n");
1345 if (!asn1_push_tag(data
, TLDAP_FILTER_AND
)) return false;
1350 tldap_debug(ld
, TLDAP_DEBUG_TRACE
, "Filter op: OR\n");
1351 if (!asn1_push_tag(data
, TLDAP_FILTER_OR
)) return false;
1356 tldap_debug(ld
, TLDAP_DEBUG_TRACE
, "Filter op: NOT\n");
1357 if (!asn1_push_tag(data
, TLDAP_FILTER_NOT
)) return false;
1359 ret
= tldap_push_filter_int(ld
, data
, &s
);
1363 if (!asn1_pop_tag(data
)) return false;
1368 tldap_debug(ld
, TLDAP_DEBUG_ERROR
,
1369 "Invalid parenthesis '%c'\n", *s
);
1373 tldap_debug(ld
, TLDAP_DEBUG_ERROR
,
1374 "Invalid filter termination\n");
1378 ret
= tldap_push_filter_basic(ld
, data
, &s
);
1385 /* only and/or filters get here.
1386 * go through the list of filters */
1389 /* RFC 4526: empty and/or */
1390 if (!asn1_pop_tag(data
)) return false;
1395 ret
= tldap_push_filter_int(ld
, data
, &s
);
1401 /* end of list, return */
1402 if (!asn1_pop_tag(data
)) return false;
1409 tldap_debug(ld
, TLDAP_DEBUG_ERROR
,
1410 "Incomplete or malformed filter\n");
1415 if (asn1_has_error(data
)) {
1424 static bool tldap_push_filter_basic(struct tldap_context
*ld
,
1425 struct asn1_data
*data
,
1428 TALLOC_CTX
*tmpctx
= talloc_tos();
1429 const char *s
= *_s
;
1437 size_t type_len
= 0;
1440 bool write_octect
= true;
1443 eq
= strchr(s
, '=');
1445 tldap_debug(ld
, TLDAP_DEBUG_ERROR
,
1446 "Invalid filter, missing equal sign\n");
1455 if (!asn1_push_tag(data
, TLDAP_FILTER_LE
)) return false;
1459 if (!asn1_push_tag(data
, TLDAP_FILTER_GE
)) return false;
1463 if (!asn1_push_tag(data
, TLDAP_FILTER_APX
)) return false;
1467 if (!asn1_push_tag(data
, TLDAP_FILTER_EXT
)) return false;
1468 write_octect
= false;
1474 if (*s
== ':') { /* [:dn]:rule:= value */
1476 /* malformed filter */
1480 } else { /* type[:dn][:rule]:= value */
1482 dn
= strchr(s
, ':');
1483 type_len
= dn
- type
;
1484 if (dn
== e
) { /* type:= value */
1491 rule
= strchr(dn
, ':');
1495 if ((rule
== dn
+ 1) || rule
+ 1 == e
) {
1496 /* malformed filter, contains "::" */
1500 if (strncasecmp_m(dn
, "dn:", 3) != 0) {
1505 /* malformed filter. With two
1506 * optionals, the first must be "dn"
1519 if (!type
&& !dn
&& !rule
) {
1520 /* malformed filter, there must be at least one */
1525 MatchingRuleAssertion ::= SEQUENCE {
1526 matchingRule [1] MatchingRuleID OPTIONAL,
1527 type [2] AttributeDescription OPTIONAL,
1528 matchValue [3] AssertionValue,
1529 dnAttributes [4] BOOLEAN DEFAULT FALSE
1533 /* check and add rule */
1535 ret
= tldap_is_attrdesc(rule
, e
- rule
, true);
1539 if (!asn1_push_tag(data
, ASN1_CONTEXT_SIMPLE(1))) return false;
1540 if (!asn1_write(data
, rule
, e
- rule
)) return false;
1541 if (!asn1_pop_tag(data
)) return false;
1544 /* check and add type */
1546 ret
= tldap_is_attrdesc(type
, type_len
, false);
1550 if (!asn1_push_tag(data
, ASN1_CONTEXT_SIMPLE(2))) return false;
1551 if (!asn1_write(data
, type
, type_len
)) return false;
1552 if (!asn1_pop_tag(data
)) return false;
1555 uval
= tldap_get_val(tmpctx
, val
, _s
);
1559 uval_len
= *_s
- val
;
1560 ret
= tldap_unescape_inplace(uval
, &uval_len
);
1565 if (!asn1_push_tag(data
, ASN1_CONTEXT_SIMPLE(3))) return false;
1566 if (!asn1_write(data
, uval
, uval_len
)) return false;
1567 if (!asn1_pop_tag(data
)) return false;
1569 if (!asn1_push_tag(data
, ASN1_CONTEXT_SIMPLE(4))) return false;
1570 if (!asn1_write_uint8(data
, dn
?1:0)) return false;
1571 if (!asn1_pop_tag(data
)) return false;
1577 ret
= tldap_is_attrdesc(s
, e
- s
, false);
1582 if (strncmp(val
, "*)", 2) == 0) {
1584 if (!asn1_push_tag(data
, TLDAP_FILTER_PRES
)) return false;
1585 if (!asn1_write(data
, s
, e
- s
)) return false;
1587 write_octect
= false;
1591 ret
= tldap_find_first_star(val
, &star
);
1597 if (!asn1_push_tag(data
, TLDAP_FILTER_SUB
)) return false;
1598 if (!asn1_write_OctetString(data
, s
, e
- s
)) return false;
1599 ret
= tldap_push_filter_substring(ld
, data
, val
, &s
);
1604 write_octect
= false;
1608 /* if nothing else, then it is just equality */
1609 if (!asn1_push_tag(data
, TLDAP_FILTER_EQ
)) return false;
1610 write_octect
= true;
1615 uval
= tldap_get_val(tmpctx
, val
, _s
);
1619 uval_len
= *_s
- val
;
1620 ret
= tldap_unescape_inplace(uval
, &uval_len
);
1625 if (!asn1_write_OctetString(data
, s
, e
- s
)) return false;
1626 if (!asn1_write_OctetString(data
, uval
, uval_len
)) return false;
1629 if (asn1_has_error(data
)) {
1632 return asn1_pop_tag(data
);
1635 static bool tldap_push_filter_substring(struct tldap_context
*ld
,
1636 struct asn1_data
*data
,
1640 TALLOC_CTX
*tmpctx
= talloc_tos();
1641 bool initial
= true;
1648 SubstringFilter ::= SEQUENCE {
1649 type AttributeDescription,
1650 -- at least one must be present
1651 substrings SEQUENCE OF CHOICE {
1652 initial [0] LDAPString,
1654 final [2] LDAPString } }
1656 if (!asn1_push_tag(data
, ASN1_SEQUENCE(0))) return false;
1659 ret
= tldap_find_first_star(val
, &star
);
1663 chunk_len
= star
- val
;
1667 if (!initial
&& chunk_len
== 0) {
1668 /* found '**', which is illegal */
1684 if (initial
&& chunk_len
== 0) {
1690 chunk
= talloc_strndup(tmpctx
, val
, chunk_len
);
1694 ret
= tldap_unescape_inplace(chunk
, &chunk_len
);
1701 if (!asn1_push_tag(data
, TLDAP_SUB_INI
)) return false;
1704 if (!asn1_push_tag(data
, TLDAP_SUB_ANY
)) return false;
1708 if (!asn1_push_tag(data
, TLDAP_SUB_FIN
)) return false;
1714 if (!asn1_write(data
, chunk
, chunk_len
)) return false;
1715 if (!asn1_pop_tag(data
)) return false;
1719 } while (*star
== '*');
1723 /* end of sequence */
1724 return asn1_pop_tag(data
);
1727 /* NOTE: although openldap libraries allow for spaces in some places, mosly
1728 * around parenthesis, we do not allow any spaces (except in values of
1729 * course) as I couldn't fine any place in RFC 4512 or RFC 4515 where
1730 * leading or trailing spaces where allowed.
1732 static bool tldap_push_filter(struct tldap_context
*ld
,
1733 struct asn1_data
*data
,
1736 const char *s
= filter
;
1739 ret
= tldap_push_filter_int(ld
, data
, &s
);
1741 tldap_debug(ld
, TLDAP_DEBUG_ERROR
,
1742 "Incomplete or malformed filter\n");
1748 /*****************************************************************************/
1750 static void tldap_search_done(struct tevent_req
*subreq
);
1752 struct tevent_req
*tldap_search_send(TALLOC_CTX
*mem_ctx
,
1753 struct tevent_context
*ev
,
1754 struct tldap_context
*ld
,
1755 const char *base
, int scope
,
1760 struct tldap_control
*sctrls
,
1762 struct tldap_control
*cctrls
,
1768 struct tevent_req
*req
, *subreq
;
1769 struct tldap_req_state
*state
;
1772 req
= tldap_req_create(mem_ctx
, ld
, &state
);
1777 if (!asn1_push_tag(state
->out
, TLDAP_REQ_SEARCH
)) goto encoding_error
;
1778 if (!asn1_write_OctetString(state
->out
, base
, strlen(base
))) goto encoding_error
;
1779 if (!asn1_write_enumerated(state
->out
, scope
)) goto encoding_error
;
1780 if (!asn1_write_enumerated(state
->out
, deref
)) goto encoding_error
;
1781 if (!asn1_write_Integer(state
->out
, sizelimit
)) goto encoding_error
;
1782 if (!asn1_write_Integer(state
->out
, timelimit
)) goto encoding_error
;
1783 if (!asn1_write_BOOLEAN(state
->out
, attrsonly
)) goto encoding_error
;
1785 if (!tldap_push_filter(ld
, state
->out
, filter
)) {
1786 goto encoding_error
;
1789 if (!asn1_push_tag(state
->out
, ASN1_SEQUENCE(0))) goto encoding_error
;
1790 for (i
=0; i
<num_attrs
; i
++) {
1791 if (!asn1_write_OctetString(state
->out
, attrs
[i
], strlen(attrs
[i
]))) goto encoding_error
;
1793 if (!asn1_pop_tag(state
->out
)) goto encoding_error
;
1794 if (!asn1_pop_tag(state
->out
)) goto encoding_error
;
1796 subreq
= tldap_msg_send(state
, ev
, ld
, state
->id
, state
->out
,
1797 sctrls
, num_sctrls
);
1798 if (tevent_req_nomem(subreq
, req
)) {
1799 return tevent_req_post(req
, ev
);
1801 tevent_req_set_callback(subreq
, tldap_search_done
, req
);
1805 tevent_req_ldap_error(req
, TLDAP_ENCODING_ERROR
);
1806 return tevent_req_post(req
, ev
);
1809 static void tldap_search_done(struct tevent_req
*subreq
)
1811 struct tevent_req
*req
= tevent_req_callback_data(
1812 subreq
, struct tevent_req
);
1813 struct tldap_req_state
*state
= tevent_req_data(
1814 req
, struct tldap_req_state
);
1817 rc
= tldap_msg_recv(subreq
, state
, &state
->result
);
1818 if (tevent_req_ldap_error(req
, rc
)) {
1821 switch (state
->result
->type
) {
1822 case TLDAP_RES_SEARCH_ENTRY
:
1823 case TLDAP_RES_SEARCH_REFERENCE
:
1824 if (!tldap_msg_set_pending(subreq
)) {
1825 tevent_req_oom(req
);
1828 tevent_req_notify_callback(req
);
1830 case TLDAP_RES_SEARCH_RESULT
:
1831 TALLOC_FREE(subreq
);
1832 if (!asn1_start_tag(state
->result
->data
,
1833 state
->result
->type
) ||
1834 !tldap_decode_response(state
) ||
1835 !asn1_end_tag(state
->result
->data
) ||
1836 !tldap_decode_controls(state
)) {
1837 tevent_req_ldap_error(req
, TLDAP_DECODING_ERROR
);
1840 tevent_req_done(req
);
1843 tevent_req_ldap_error(req
, TLDAP_PROTOCOL_ERROR
);
1848 TLDAPRC
tldap_search_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
1849 struct tldap_message
**pmsg
)
1851 struct tldap_req_state
*state
= tevent_req_data(
1852 req
, struct tldap_req_state
);
1855 if (!tevent_req_is_in_progress(req
)
1856 && tevent_req_is_ldap_error(req
, &rc
)) {
1860 if (tevent_req_is_in_progress(req
)) {
1861 switch (state
->result
->type
) {
1862 case TLDAP_RES_SEARCH_ENTRY
:
1863 case TLDAP_RES_SEARCH_REFERENCE
:
1866 return TLDAP_OPERATIONS_ERROR
;
1870 *pmsg
= talloc_move(mem_ctx
, &state
->result
);
1871 return TLDAP_SUCCESS
;
1874 struct tldap_search_all_state
{
1875 struct tldap_message
**msgs
;
1876 struct tldap_message
*result
;
1879 static void tldap_search_all_done(struct tevent_req
*subreq
);
1881 struct tevent_req
*tldap_search_all_send(
1882 TALLOC_CTX
*mem_ctx
, struct tevent_context
*ev
,
1883 struct tldap_context
*ld
, const char *base
, int scope
,
1884 const char *filter
, const char **attrs
, int num_attrs
, int attrsonly
,
1885 struct tldap_control
*sctrls
, int num_sctrls
,
1886 struct tldap_control
*cctrls
, int num_cctrls
,
1887 int timelimit
, int sizelimit
, int deref
)
1889 struct tevent_req
*req
, *subreq
;
1890 struct tldap_search_all_state
*state
;
1892 req
= tevent_req_create(mem_ctx
, &state
,
1893 struct tldap_search_all_state
);
1898 subreq
= tldap_search_send(state
, ev
, ld
, base
, scope
, filter
,
1899 attrs
, num_attrs
, attrsonly
,
1900 sctrls
, num_sctrls
, cctrls
, num_cctrls
,
1901 timelimit
, sizelimit
, deref
);
1902 if (tevent_req_nomem(subreq
, req
)) {
1903 return tevent_req_post(req
, ev
);
1905 tevent_req_set_callback(subreq
, tldap_search_all_done
, req
);
1909 static void tldap_search_all_done(struct tevent_req
*subreq
)
1911 struct tevent_req
*req
= tevent_req_callback_data(
1912 subreq
, struct tevent_req
);
1913 struct tldap_search_all_state
*state
= tevent_req_data(
1914 req
, struct tldap_search_all_state
);
1915 struct tldap_message
*msg
, **tmp
;
1920 rc
= tldap_search_recv(subreq
, state
, &msg
);
1921 /* No TALLOC_FREE(subreq), this is multi-step */
1922 if (tevent_req_ldap_error(req
, rc
)) {
1926 msgtype
= tldap_msg_type(msg
);
1927 if (msgtype
== TLDAP_RES_SEARCH_RESULT
) {
1928 state
->result
= msg
;
1929 tevent_req_done(req
);
1933 num_msgs
= talloc_array_length(state
->msgs
);
1935 tmp
= talloc_realloc(state
, state
->msgs
, struct tldap_message
*,
1937 if (tevent_req_nomem(tmp
, req
)) {
1941 state
->msgs
[num_msgs
] = talloc_move(state
->msgs
, &msg
);
1944 TLDAPRC
tldap_search_all_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
1945 struct tldap_message
***msgs
,
1946 struct tldap_message
**result
)
1948 struct tldap_search_all_state
*state
= tevent_req_data(
1949 req
, struct tldap_search_all_state
);
1952 if (tevent_req_is_ldap_error(req
, &rc
)) {
1957 *msgs
= talloc_move(mem_ctx
, &state
->msgs
);
1959 if (result
!= NULL
) {
1960 *result
= talloc_move(mem_ctx
, &state
->result
);
1963 return TLDAP_SUCCESS
;
1966 TLDAPRC
tldap_search(struct tldap_context
*ld
,
1967 const char *base
, int scope
, const char *filter
,
1968 const char **attrs
, int num_attrs
, int attrsonly
,
1969 struct tldap_control
*sctrls
, int num_sctrls
,
1970 struct tldap_control
*cctrls
, int num_cctrls
,
1971 int timelimit
, int sizelimit
, int deref
,
1972 TALLOC_CTX
*mem_ctx
, struct tldap_message
***pmsgs
)
1975 struct tevent_context
*ev
;
1976 struct tevent_req
*req
;
1977 TLDAPRC rc
= TLDAP_NO_MEMORY
;
1978 struct tldap_message
**msgs
;
1979 struct tldap_message
*result
;
1981 if (tldap_pending_reqs(ld
)) {
1985 frame
= talloc_stackframe();
1987 ev
= samba_tevent_context_init(frame
);
1991 req
= tldap_search_all_send(frame
, ev
, ld
, base
, scope
, filter
,
1992 attrs
, num_attrs
, attrsonly
,
1993 sctrls
, num_sctrls
, cctrls
, num_cctrls
,
1994 timelimit
, sizelimit
, deref
);
1998 if (!tevent_req_poll(req
, ev
)) {
1999 rc
= TLDAP_OPERATIONS_ERROR
;
2002 rc
= tldap_search_all_recv(req
, frame
, &msgs
, &result
);
2004 if (!TLDAP_RC_IS_SUCCESS(rc
)) {
2008 TALLOC_FREE(ld
->last_msg
);
2009 ld
->last_msg
= talloc_move(ld
, &result
);
2011 if (pmsgs
!= NULL
) {
2012 *pmsgs
= talloc_move(mem_ctx
, &msgs
);
2019 static bool tldap_parse_search_entry(struct tldap_message
*msg
)
2021 int num_attribs
= 0;
2023 if (msg
->type
!= TLDAP_RES_SEARCH_ENTRY
) {
2026 if (!asn1_start_tag(msg
->data
, TLDAP_RES_SEARCH_ENTRY
)) {
2032 if (!asn1_read_OctetString_talloc(msg
, msg
->data
, &msg
->dn
)) return false;
2034 if (msg
->dn
== NULL
) {
2039 * Attributes: We overallocate msg->attribs by one, so that while
2040 * looping over the attributes we can directly parse into the last
2041 * array element. Same for the values in the inner loop.
2044 msg
->attribs
= talloc_array(msg
, struct tldap_attribute
, 1);
2045 if (msg
->attribs
== NULL
) {
2049 if (!asn1_start_tag(msg
->data
, ASN1_SEQUENCE(0))) return false;
2050 while (asn1_peek_tag(msg
->data
, ASN1_SEQUENCE(0))) {
2051 struct tldap_attribute
*attrib
;
2054 attrib
= &msg
->attribs
[num_attribs
];
2055 attrib
->values
= talloc_array(msg
->attribs
, DATA_BLOB
, 1);
2056 if (attrib
->values
== NULL
) {
2059 if (!asn1_start_tag(msg
->data
, ASN1_SEQUENCE(0))) return false;
2060 if (!asn1_read_OctetString_talloc(msg
->attribs
, msg
->data
,
2061 &attrib
->name
)) return false;
2062 if (!asn1_start_tag(msg
->data
, ASN1_SET
)) return false;
2064 while (asn1_peek_tag(msg
->data
, ASN1_OCTET_STRING
)) {
2065 if (!asn1_read_OctetString(msg
->data
, msg
,
2066 &attrib
->values
[num_values
])) return false;
2068 attrib
->values
= talloc_realloc(
2069 msg
->attribs
, attrib
->values
, DATA_BLOB
,
2071 if (attrib
->values
== NULL
) {
2076 attrib
->values
= talloc_realloc(msg
->attribs
, attrib
->values
,
2077 DATA_BLOB
, num_values
);
2078 attrib
->num_values
= num_values
;
2080 if (!asn1_end_tag(msg
->data
)) return false; /* ASN1_SET */
2081 if (!asn1_end_tag(msg
->data
)) return false; /* ASN1_SEQUENCE(0) */
2082 msg
->attribs
= talloc_realloc(
2083 msg
, msg
->attribs
, struct tldap_attribute
,
2085 if (msg
->attribs
== NULL
) {
2090 msg
->attribs
= talloc_realloc(
2091 msg
, msg
->attribs
, struct tldap_attribute
, num_attribs
);
2092 return asn1_end_tag(msg
->data
);
2095 bool tldap_entry_dn(struct tldap_message
*msg
, char **dn
)
2097 if ((msg
->dn
== NULL
) && (!tldap_parse_search_entry(msg
))) {
2104 bool tldap_entry_attributes(struct tldap_message
*msg
,
2105 struct tldap_attribute
**attributes
,
2106 int *num_attributes
)
2108 if ((msg
->dn
== NULL
) && (!tldap_parse_search_entry(msg
))) {
2111 *attributes
= msg
->attribs
;
2112 *num_attributes
= talloc_array_length(msg
->attribs
);
2116 static bool tldap_decode_controls(struct tldap_req_state
*state
)
2118 struct tldap_message
*msg
= state
->result
;
2119 struct asn1_data
*data
= msg
->data
;
2120 struct tldap_control
*sctrls
= NULL
;
2121 int num_controls
= 0;
2124 msg
->res_sctrls
= NULL
;
2126 if (!asn1_peek_tag(data
, ASN1_CONTEXT(0))) {
2130 if (!asn1_start_tag(data
, ASN1_CONTEXT(0))) goto out
;
2132 while (asn1_peek_tag(data
, ASN1_SEQUENCE(0))) {
2133 struct tldap_control
*c
;
2136 sctrls
= talloc_realloc(msg
, sctrls
, struct tldap_control
,
2138 if (sctrls
== NULL
) {
2141 c
= &sctrls
[num_controls
];
2143 if (!asn1_start_tag(data
, ASN1_SEQUENCE(0))) goto out
;
2144 if (!asn1_read_OctetString_talloc(msg
, data
, &oid
)) goto out
;
2145 if (asn1_has_error(data
) || (oid
== NULL
)) {
2149 if (asn1_peek_tag(data
, ASN1_BOOLEAN
)) {
2150 if (!asn1_read_BOOLEAN(data
, &c
->critical
)) goto out
;
2152 c
->critical
= false;
2154 c
->value
= data_blob_null
;
2155 if (asn1_peek_tag(data
, ASN1_OCTET_STRING
) &&
2156 !asn1_read_OctetString(data
, msg
, &c
->value
)) {
2159 if (!asn1_end_tag(data
)) goto out
; /* ASN1_SEQUENCE(0) */
2164 if (!asn1_end_tag(data
)) goto out
; /* ASN1_CONTEXT(0) */
2171 msg
->res_sctrls
= sctrls
;
2173 TALLOC_FREE(sctrls
);
2178 static void tldap_simple_done(struct tevent_req
*subreq
, int type
)
2180 struct tevent_req
*req
= tevent_req_callback_data(
2181 subreq
, struct tevent_req
);
2182 struct tldap_req_state
*state
= tevent_req_data(
2183 req
, struct tldap_req_state
);
2186 rc
= tldap_msg_recv(subreq
, state
, &state
->result
);
2187 TALLOC_FREE(subreq
);
2188 if (tevent_req_ldap_error(req
, rc
)) {
2191 if (state
->result
->type
!= type
) {
2192 tevent_req_ldap_error(req
, TLDAP_PROTOCOL_ERROR
);
2195 if (!asn1_start_tag(state
->result
->data
, state
->result
->type
) ||
2196 !tldap_decode_response(state
) ||
2197 !asn1_end_tag(state
->result
->data
) ||
2198 !tldap_decode_controls(state
)) {
2199 tevent_req_ldap_error(req
, TLDAP_DECODING_ERROR
);
2202 if (!TLDAP_RC_IS_SUCCESS(state
->result
->lderr
)) {
2203 tevent_req_ldap_error(req
, state
->result
->lderr
);
2206 tevent_req_done(req
);
2209 static TLDAPRC
tldap_simple_recv(struct tevent_req
*req
)
2212 if (tevent_req_is_ldap_error(req
, &rc
)) {
2215 return TLDAP_SUCCESS
;
2218 static void tldap_add_done(struct tevent_req
*subreq
);
2220 struct tevent_req
*tldap_add_send(TALLOC_CTX
*mem_ctx
,
2221 struct tevent_context
*ev
,
2222 struct tldap_context
*ld
,
2224 struct tldap_mod
*attributes
,
2226 struct tldap_control
*sctrls
,
2228 struct tldap_control
*cctrls
,
2231 struct tevent_req
*req
, *subreq
;
2232 struct tldap_req_state
*state
;
2235 req
= tldap_req_create(mem_ctx
, ld
, &state
);
2240 if (!asn1_push_tag(state
->out
, TLDAP_REQ_ADD
)) goto err
;
2241 if (!asn1_write_OctetString(state
->out
, dn
, strlen(dn
))) goto err
;
2242 if (!asn1_push_tag(state
->out
, ASN1_SEQUENCE(0))) goto err
;
2244 for (i
=0; i
<num_attributes
; i
++) {
2245 struct tldap_mod
*attrib
= &attributes
[i
];
2246 if (!asn1_push_tag(state
->out
, ASN1_SEQUENCE(0))) goto err
;
2247 if (!asn1_write_OctetString(state
->out
, attrib
->attribute
,
2248 strlen(attrib
->attribute
))) goto err
;
2249 if (!asn1_push_tag(state
->out
, ASN1_SET
)) goto err
;
2250 for (j
=0; j
<attrib
->num_values
; j
++) {
2251 if (!asn1_write_OctetString(state
->out
,
2252 attrib
->values
[j
].data
,
2253 attrib
->values
[j
].length
)) goto err
;
2255 if (!asn1_pop_tag(state
->out
)) goto err
;
2256 if (!asn1_pop_tag(state
->out
)) goto err
;
2259 if (!asn1_pop_tag(state
->out
)) goto err
;
2260 if (!asn1_pop_tag(state
->out
)) goto err
;
2262 subreq
= tldap_msg_send(state
, ev
, ld
, state
->id
, state
->out
,
2263 sctrls
, num_sctrls
);
2264 if (tevent_req_nomem(subreq
, req
)) {
2265 return tevent_req_post(req
, ev
);
2267 tevent_req_set_callback(subreq
, tldap_add_done
, req
);
2272 tevent_req_ldap_error(req
, TLDAP_ENCODING_ERROR
);
2273 return tevent_req_post(req
, ev
);
2276 static void tldap_add_done(struct tevent_req
*subreq
)
2278 tldap_simple_done(subreq
, TLDAP_RES_ADD
);
2281 TLDAPRC
tldap_add_recv(struct tevent_req
*req
)
2283 return tldap_simple_recv(req
);
2286 TLDAPRC
tldap_add(struct tldap_context
*ld
, const char *dn
,
2287 struct tldap_mod
*attributes
, int num_attributes
,
2288 struct tldap_control
*sctrls
, int num_sctrls
,
2289 struct tldap_control
*cctrls
, int num_cctrls
)
2291 TALLOC_CTX
*frame
= talloc_stackframe();
2292 struct tevent_context
*ev
;
2293 struct tevent_req
*req
;
2294 TLDAPRC rc
= TLDAP_NO_MEMORY
;
2296 ev
= samba_tevent_context_init(frame
);
2300 req
= tldap_add_send(frame
, ev
, ld
, dn
, attributes
, num_attributes
,
2301 sctrls
, num_sctrls
, cctrls
, num_cctrls
);
2305 if (!tevent_req_poll(req
, ev
)) {
2306 rc
= TLDAP_OPERATIONS_ERROR
;
2309 rc
= tldap_add_recv(req
);
2310 tldap_save_msg(ld
, req
);
2316 static void tldap_modify_done(struct tevent_req
*subreq
);
2318 struct tevent_req
*tldap_modify_send(TALLOC_CTX
*mem_ctx
,
2319 struct tevent_context
*ev
,
2320 struct tldap_context
*ld
,
2322 struct tldap_mod
*mods
, int num_mods
,
2323 struct tldap_control
*sctrls
,
2325 struct tldap_control
*cctrls
,
2328 struct tevent_req
*req
, *subreq
;
2329 struct tldap_req_state
*state
;
2332 req
= tldap_req_create(mem_ctx
, ld
, &state
);
2337 if (!asn1_push_tag(state
->out
, TLDAP_REQ_MODIFY
)) goto err
;
2338 if (!asn1_write_OctetString(state
->out
, dn
, strlen(dn
))) goto err
;
2339 if (!asn1_push_tag(state
->out
, ASN1_SEQUENCE(0))) goto err
;
2341 for (i
=0; i
<num_mods
; i
++) {
2342 struct tldap_mod
*mod
= &mods
[i
];
2343 if (!asn1_push_tag(state
->out
, ASN1_SEQUENCE(0))) goto err
;
2344 if (!asn1_write_enumerated(state
->out
, mod
->mod_op
)) goto err
;
2345 if (!asn1_push_tag(state
->out
, ASN1_SEQUENCE(0))) goto err
;
2346 if (!asn1_write_OctetString(state
->out
, mod
->attribute
,
2347 strlen(mod
->attribute
))) goto err
;
2348 if (!asn1_push_tag(state
->out
, ASN1_SET
)) goto err
;
2349 for (j
=0; j
<mod
->num_values
; j
++) {
2350 if (!asn1_write_OctetString(state
->out
,
2351 mod
->values
[j
].data
,
2352 mod
->values
[j
].length
)) goto err
;
2354 if (!asn1_pop_tag(state
->out
)) goto err
;
2355 if (!asn1_pop_tag(state
->out
)) goto err
;
2356 if (!asn1_pop_tag(state
->out
)) goto err
;
2359 if (!asn1_pop_tag(state
->out
)) goto err
;
2360 if (!asn1_pop_tag(state
->out
)) goto err
;
2362 subreq
= tldap_msg_send(state
, ev
, ld
, state
->id
, state
->out
,
2363 sctrls
, num_sctrls
);
2364 if (tevent_req_nomem(subreq
, req
)) {
2365 return tevent_req_post(req
, ev
);
2367 tevent_req_set_callback(subreq
, tldap_modify_done
, req
);
2372 tevent_req_ldap_error(req
, TLDAP_ENCODING_ERROR
);
2373 return tevent_req_post(req
, ev
);
2376 static void tldap_modify_done(struct tevent_req
*subreq
)
2378 tldap_simple_done(subreq
, TLDAP_RES_MODIFY
);
2381 TLDAPRC
tldap_modify_recv(struct tevent_req
*req
)
2383 return tldap_simple_recv(req
);
2386 TLDAPRC
tldap_modify(struct tldap_context
*ld
, const char *dn
,
2387 struct tldap_mod
*mods
, int num_mods
,
2388 struct tldap_control
*sctrls
, int num_sctrls
,
2389 struct tldap_control
*cctrls
, int num_cctrls
)
2391 TALLOC_CTX
*frame
= talloc_stackframe();
2392 struct tevent_context
*ev
;
2393 struct tevent_req
*req
;
2394 TLDAPRC rc
= TLDAP_NO_MEMORY
;
2396 ev
= samba_tevent_context_init(frame
);
2400 req
= tldap_modify_send(frame
, ev
, ld
, dn
, mods
, num_mods
,
2401 sctrls
, num_sctrls
, cctrls
, num_cctrls
);
2405 if (!tevent_req_poll(req
, ev
)) {
2406 rc
= TLDAP_OPERATIONS_ERROR
;
2409 rc
= tldap_modify_recv(req
);
2410 tldap_save_msg(ld
, req
);
2416 static void tldap_delete_done(struct tevent_req
*subreq
);
2418 struct tevent_req
*tldap_delete_send(TALLOC_CTX
*mem_ctx
,
2419 struct tevent_context
*ev
,
2420 struct tldap_context
*ld
,
2422 struct tldap_control
*sctrls
,
2424 struct tldap_control
*cctrls
,
2427 struct tevent_req
*req
, *subreq
;
2428 struct tldap_req_state
*state
;
2430 req
= tldap_req_create(mem_ctx
, ld
, &state
);
2435 if (!asn1_push_tag(state
->out
, TLDAP_REQ_DELETE
)) goto err
;
2436 if (!asn1_write(state
->out
, dn
, strlen(dn
))) goto err
;
2437 if (!asn1_pop_tag(state
->out
)) goto err
;
2439 subreq
= tldap_msg_send(state
, ev
, ld
, state
->id
, state
->out
,
2440 sctrls
, num_sctrls
);
2441 if (tevent_req_nomem(subreq
, req
)) {
2442 return tevent_req_post(req
, ev
);
2444 tevent_req_set_callback(subreq
, tldap_delete_done
, req
);
2449 tevent_req_ldap_error(req
, TLDAP_ENCODING_ERROR
);
2450 return tevent_req_post(req
, ev
);
2453 static void tldap_delete_done(struct tevent_req
*subreq
)
2455 tldap_simple_done(subreq
, TLDAP_RES_DELETE
);
2458 TLDAPRC
tldap_delete_recv(struct tevent_req
*req
)
2460 return tldap_simple_recv(req
);
2463 TLDAPRC
tldap_delete(struct tldap_context
*ld
, const char *dn
,
2464 struct tldap_control
*sctrls
, int num_sctrls
,
2465 struct tldap_control
*cctrls
, int num_cctrls
)
2467 TALLOC_CTX
*frame
= talloc_stackframe();
2468 struct tevent_context
*ev
;
2469 struct tevent_req
*req
;
2470 TLDAPRC rc
= TLDAP_NO_MEMORY
;
2472 ev
= samba_tevent_context_init(frame
);
2476 req
= tldap_delete_send(frame
, ev
, ld
, dn
, sctrls
, num_sctrls
,
2477 cctrls
, num_cctrls
);
2481 if (!tevent_req_poll(req
, ev
)) {
2482 rc
= TLDAP_OPERATIONS_ERROR
;
2485 rc
= tldap_delete_recv(req
);
2486 tldap_save_msg(ld
, req
);
2492 int tldap_msg_id(const struct tldap_message
*msg
)
2497 int tldap_msg_type(const struct tldap_message
*msg
)
2502 const char *tldap_msg_matcheddn(struct tldap_message
*msg
)
2507 return msg
->res_matcheddn
;
2510 const char *tldap_msg_diagnosticmessage(struct tldap_message
*msg
)
2515 return msg
->res_diagnosticmessage
;
2518 const char *tldap_msg_referral(struct tldap_message
*msg
)
2523 return msg
->res_referral
;
2526 void tldap_msg_sctrls(struct tldap_message
*msg
, int *num_sctrls
,
2527 struct tldap_control
**sctrls
)
2534 *sctrls
= msg
->res_sctrls
;
2535 *num_sctrls
= talloc_array_length(msg
->res_sctrls
);
2538 struct tldap_message
*tldap_ctx_lastmsg(struct tldap_context
*ld
)
2540 return ld
->last_msg
;
2543 static const struct { TLDAPRC rc
; const char *string
; } tldaprc_errmap
[] =
2547 { TLDAP_OPERATIONS_ERROR
,
2548 "TLDAP_OPERATIONS_ERROR" },
2549 { TLDAP_PROTOCOL_ERROR
,
2550 "TLDAP_PROTOCOL_ERROR" },
2551 { TLDAP_TIMELIMIT_EXCEEDED
,
2552 "TLDAP_TIMELIMIT_EXCEEDED" },
2553 { TLDAP_SIZELIMIT_EXCEEDED
,
2554 "TLDAP_SIZELIMIT_EXCEEDED" },
2555 { TLDAP_COMPARE_FALSE
,
2556 "TLDAP_COMPARE_FALSE" },
2557 { TLDAP_COMPARE_TRUE
,
2558 "TLDAP_COMPARE_TRUE" },
2559 { TLDAP_STRONG_AUTH_NOT_SUPPORTED
,
2560 "TLDAP_STRONG_AUTH_NOT_SUPPORTED" },
2561 { TLDAP_STRONG_AUTH_REQUIRED
,
2562 "TLDAP_STRONG_AUTH_REQUIRED" },
2565 { TLDAP_ADMINLIMIT_EXCEEDED
,
2566 "TLDAP_ADMINLIMIT_EXCEEDED" },
2567 { TLDAP_UNAVAILABLE_CRITICAL_EXTENSION
,
2568 "TLDAP_UNAVAILABLE_CRITICAL_EXTENSION" },
2569 { TLDAP_CONFIDENTIALITY_REQUIRED
,
2570 "TLDAP_CONFIDENTIALITY_REQUIRED" },
2571 { TLDAP_SASL_BIND_IN_PROGRESS
,
2572 "TLDAP_SASL_BIND_IN_PROGRESS" },
2573 { TLDAP_NO_SUCH_ATTRIBUTE
,
2574 "TLDAP_NO_SUCH_ATTRIBUTE" },
2575 { TLDAP_UNDEFINED_TYPE
,
2576 "TLDAP_UNDEFINED_TYPE" },
2577 { TLDAP_INAPPROPRIATE_MATCHING
,
2578 "TLDAP_INAPPROPRIATE_MATCHING" },
2579 { TLDAP_CONSTRAINT_VIOLATION
,
2580 "TLDAP_CONSTRAINT_VIOLATION" },
2581 { TLDAP_TYPE_OR_VALUE_EXISTS
,
2582 "TLDAP_TYPE_OR_VALUE_EXISTS" },
2583 { TLDAP_INVALID_SYNTAX
,
2584 "TLDAP_INVALID_SYNTAX" },
2585 { TLDAP_NO_SUCH_OBJECT
,
2586 "TLDAP_NO_SUCH_OBJECT" },
2587 { TLDAP_ALIAS_PROBLEM
,
2588 "TLDAP_ALIAS_PROBLEM" },
2589 { TLDAP_INVALID_DN_SYNTAX
,
2590 "TLDAP_INVALID_DN_SYNTAX" },
2593 { TLDAP_ALIAS_DEREF_PROBLEM
,
2594 "TLDAP_ALIAS_DEREF_PROBLEM" },
2595 { TLDAP_INAPPROPRIATE_AUTH
,
2596 "TLDAP_INAPPROPRIATE_AUTH" },
2597 { TLDAP_INVALID_CREDENTIALS
,
2598 "TLDAP_INVALID_CREDENTIALS" },
2599 { TLDAP_INSUFFICIENT_ACCESS
,
2600 "TLDAP_INSUFFICIENT_ACCESS" },
2603 { TLDAP_UNAVAILABLE
,
2604 "TLDAP_UNAVAILABLE" },
2605 { TLDAP_UNWILLING_TO_PERFORM
,
2606 "TLDAP_UNWILLING_TO_PERFORM" },
2607 { TLDAP_LOOP_DETECT
,
2608 "TLDAP_LOOP_DETECT" },
2609 { TLDAP_NAMING_VIOLATION
,
2610 "TLDAP_NAMING_VIOLATION" },
2611 { TLDAP_OBJECT_CLASS_VIOLATION
,
2612 "TLDAP_OBJECT_CLASS_VIOLATION" },
2613 { TLDAP_NOT_ALLOWED_ON_NONLEAF
,
2614 "TLDAP_NOT_ALLOWED_ON_NONLEAF" },
2615 { TLDAP_NOT_ALLOWED_ON_RDN
,
2616 "TLDAP_NOT_ALLOWED_ON_RDN" },
2617 { TLDAP_ALREADY_EXISTS
,
2618 "TLDAP_ALREADY_EXISTS" },
2619 { TLDAP_NO_OBJECT_CLASS_MODS
,
2620 "TLDAP_NO_OBJECT_CLASS_MODS" },
2621 { TLDAP_RESULTS_TOO_LARGE
,
2622 "TLDAP_RESULTS_TOO_LARGE" },
2623 { TLDAP_AFFECTS_MULTIPLE_DSAS
,
2624 "TLDAP_AFFECTS_MULTIPLE_DSAS" },
2627 { TLDAP_SERVER_DOWN
,
2628 "TLDAP_SERVER_DOWN" },
2629 { TLDAP_LOCAL_ERROR
,
2630 "TLDAP_LOCAL_ERROR" },
2631 { TLDAP_ENCODING_ERROR
,
2632 "TLDAP_ENCODING_ERROR" },
2633 { TLDAP_DECODING_ERROR
,
2634 "TLDAP_DECODING_ERROR" },
2637 { TLDAP_AUTH_UNKNOWN
,
2638 "TLDAP_AUTH_UNKNOWN" },
2639 { TLDAP_FILTER_ERROR
,
2640 "TLDAP_FILTER_ERROR" },
2641 { TLDAP_USER_CANCELLED
,
2642 "TLDAP_USER_CANCELLED" },
2643 { TLDAP_PARAM_ERROR
,
2644 "TLDAP_PARAM_ERROR" },
2646 "TLDAP_NO_MEMORY" },
2647 { TLDAP_CONNECT_ERROR
,
2648 "TLDAP_CONNECT_ERROR" },
2649 { TLDAP_NOT_SUPPORTED
,
2650 "TLDAP_NOT_SUPPORTED" },
2651 { TLDAP_CONTROL_NOT_FOUND
,
2652 "TLDAP_CONTROL_NOT_FOUND" },
2653 { TLDAP_NO_RESULTS_RETURNED
,
2654 "TLDAP_NO_RESULTS_RETURNED" },
2655 { TLDAP_MORE_RESULTS_TO_RETURN
,
2656 "TLDAP_MORE_RESULTS_TO_RETURN" },
2657 { TLDAP_CLIENT_LOOP
,
2658 "TLDAP_CLIENT_LOOP" },
2659 { TLDAP_REFERRAL_LIMIT_EXCEEDED
,
2660 "TLDAP_REFERRAL_LIMIT_EXCEEDED" },
2663 const char *tldap_rc2string(TLDAPRC rc
)
2667 for (i
=0; i
<ARRAY_SIZE(tldaprc_errmap
); i
++) {
2668 if (TLDAP_RC_EQUAL(rc
, tldaprc_errmap
[i
].rc
)) {
2669 return tldaprc_errmap
[i
].string
;
2673 return "Unknown LDAP Error";