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 "tldap_util.h"
23 #include "../libcli/security/security.h"
24 #include "../lib/util/asn1.h"
25 #include "lib/util/smb_strtox.h"
27 bool tldap_entry_values(struct tldap_message
*msg
, const char *attribute
,
28 DATA_BLOB
**values
, int *num_values
)
30 struct tldap_attribute
*attributes
;
31 int i
, num_attributes
;
33 if (!tldap_entry_attributes(msg
, &attributes
, &num_attributes
)) {
37 for (i
=0; i
<num_attributes
; i
++) {
38 if (strequal(attribute
, attributes
[i
].name
)) {
42 if (i
== num_attributes
) {
45 *num_values
= attributes
[i
].num_values
;
46 *values
= attributes
[i
].values
;
50 bool tldap_get_single_valueblob(struct tldap_message
*msg
,
51 const char *attribute
, DATA_BLOB
*blob
)
56 if (attribute
== NULL
) {
59 if (!tldap_entry_values(msg
, attribute
, &values
, &num_values
)) {
62 if (num_values
!= 1) {
69 char *tldap_talloc_single_attribute(struct tldap_message
*msg
,
70 const char *attribute
,
77 if (!tldap_get_single_valueblob(msg
, attribute
, &val
)) {
80 if (!convert_string_talloc(mem_ctx
, CH_UTF8
, CH_UNIX
,
88 bool tldap_pull_binsid(struct tldap_message
*msg
, const char *attribute
,
94 if (!tldap_get_single_valueblob(msg
, attribute
, &val
)) {
97 ret
= sid_parse(val
.data
, val
.length
, sid
);
101 bool tldap_pull_guid(struct tldap_message
*msg
, const char *attribute
,
106 if (!tldap_get_single_valueblob(msg
, attribute
, &val
)) {
109 return NT_STATUS_IS_OK(GUID_from_data_blob(&val
, guid
));
112 static bool tldap_add_blob_vals(TALLOC_CTX
*mem_ctx
, struct tldap_mod
*mod
,
113 DATA_BLOB
*newvals
, int num_newvals
)
115 int num_values
= talloc_array_length(mod
->values
);
119 tmp
= talloc_realloc(mem_ctx
, mod
->values
, DATA_BLOB
,
120 num_values
+ num_newvals
);
126 for (i
=0; i
<num_newvals
; i
++) {
127 mod
->values
[i
+num_values
].data
= (uint8_t *)talloc_memdup(
128 mod
->values
, newvals
[i
].data
, newvals
[i
].length
);
129 if (mod
->values
[i
+num_values
].data
== NULL
) {
132 mod
->values
[i
+num_values
].length
= newvals
[i
].length
;
134 mod
->num_values
= num_values
+ num_newvals
;
138 bool tldap_add_mod_blobs(TALLOC_CTX
*mem_ctx
,
139 struct tldap_mod
**pmods
, int *pnum_mods
,
140 int mod_op
, const char *attrib
,
141 DATA_BLOB
*newvals
, int num_newvals
)
143 struct tldap_mod new_mod
;
144 struct tldap_mod
*mods
= *pmods
;
145 struct tldap_mod
*mod
= NULL
;
149 mods
= talloc_array(mem_ctx
, struct tldap_mod
, 0);
155 num_mods
= *pnum_mods
;
157 for (i
=0; i
<num_mods
; i
++) {
158 if ((mods
[i
].mod_op
== mod_op
)
159 && strequal(mods
[i
].attribute
, attrib
)) {
166 new_mod
.mod_op
= mod_op
;
167 new_mod
.attribute
= talloc_strdup(mods
, attrib
);
168 if (new_mod
.attribute
== NULL
) {
171 new_mod
.num_values
= 0;
172 new_mod
.values
= NULL
;
176 if ((num_newvals
!= 0)
177 && !tldap_add_blob_vals(mods
, mod
, newvals
, num_newvals
)) {
181 if ((i
== num_mods
) && (talloc_array_length(mods
) < num_mods
+ 1)) {
182 mods
= talloc_realloc(talloc_tos(), mods
, struct tldap_mod
,
187 mods
[num_mods
] = *mod
;
195 bool tldap_add_mod_str(TALLOC_CTX
*mem_ctx
,
196 struct tldap_mod
**pmods
, int *pnum_mods
,
197 int mod_op
, const char *attrib
, const char *str
)
202 if (!convert_string_talloc(talloc_tos(), CH_UNIX
, CH_UTF8
, str
,
203 strlen(str
), &utf8
.data
, &utf8
.length
)) {
207 ret
= tldap_add_mod_blobs(mem_ctx
, pmods
, pnum_mods
, mod_op
, attrib
,
209 TALLOC_FREE(utf8
.data
);
213 static bool tldap_make_mod_blob_int(struct tldap_message
*existing
,
215 struct tldap_mod
**pmods
, int *pnum_mods
,
216 const char *attrib
, DATA_BLOB newval
,
217 int (*comparison
)(const DATA_BLOB
*d1
,
218 const DATA_BLOB
*d2
))
221 DATA_BLOB
*values
= NULL
;
222 DATA_BLOB oldval
= data_blob_null
;
224 if ((existing
!= NULL
)
225 && tldap_entry_values(existing
, attrib
, &values
, &num_values
)) {
227 if (num_values
> 1) {
228 /* can't change multivalue attributes atm */
231 if (num_values
== 1) {
236 if ((oldval
.data
!= NULL
) && (newval
.data
!= NULL
)
237 && (comparison(&oldval
, &newval
) == 0)) {
238 /* Believe it or not, but LDAP will deny a delete and
239 an add at the same time if the values are the
241 DEBUG(10,("tldap_make_mod_blob_int: attribute |%s| not "
242 "changed.\n", attrib
));
246 if (oldval
.data
!= NULL
) {
247 /* By deleting exactly the value we found in the entry this
248 * should be race-free in the sense that the LDAP-Server will
249 * deny the complete operation if somebody changed the
250 * attribute behind our back. */
251 /* This will also allow modifying single valued attributes in
252 * Novell NDS. In NDS you have to first remove attribute and
253 * then you could add new value */
255 DEBUG(10, ("tldap_make_mod_blob_int: deleting attribute |%s|\n",
257 if (!tldap_add_mod_blobs(mem_ctx
, pmods
, pnum_mods
,
259 attrib
, &oldval
, 1)) {
264 /* Regardless of the real operation (add or modify)
265 we add the new value here. We rely on deleting
266 the old value, should it exist. */
268 if (newval
.data
!= NULL
) {
269 DEBUG(10, ("tldap_make_mod_blob_int: adding attribute |%s| value len "
270 "%d\n", attrib
, (int)newval
.length
));
271 if (!tldap_add_mod_blobs(mem_ctx
, pmods
, pnum_mods
,
273 attrib
, &newval
, 1)) {
280 bool tldap_make_mod_blob(struct tldap_message
*existing
, TALLOC_CTX
*mem_ctx
,
281 struct tldap_mod
**pmods
, int *pnum_mods
,
282 const char *attrib
, DATA_BLOB newval
)
284 return tldap_make_mod_blob_int(existing
, mem_ctx
, pmods
, pnum_mods
,
285 attrib
, newval
, data_blob_cmp
);
288 static int compare_utf8_blobs(const DATA_BLOB
*d1
, const DATA_BLOB
*d2
)
294 if (!convert_string_talloc(talloc_tos(), CH_UTF8
, CH_UNIX
, d1
->data
,
295 d1
->length
, &s1
, &s1len
)) {
296 /* can't do much here */
299 if (!convert_string_talloc(talloc_tos(), CH_UTF8
, CH_UNIX
, d2
->data
,
300 d2
->length
, &s2
, &s2len
)) {
301 /* can't do much here */
305 ret
= strcasecmp_m(s1
, s2
);
311 bool tldap_make_mod_fmt(struct tldap_message
*existing
, TALLOC_CTX
*mem_ctx
,
312 struct tldap_mod
**pmods
, int *pnum_mods
,
313 const char *attrib
, const char *fmt
, ...)
318 DATA_BLOB blob
= data_blob_null
;
321 newval
= talloc_vasprintf(talloc_tos(), fmt
, ap
);
324 if (newval
== NULL
) {
328 blob
.length
= strlen(newval
);
329 if (blob
.length
!= 0) {
330 blob
.data
= discard_const_p(uint8_t, newval
);
332 ret
= tldap_make_mod_blob_int(existing
, mem_ctx
, pmods
, pnum_mods
,
333 attrib
, blob
, compare_utf8_blobs
);
338 const char *tldap_errstr(TALLOC_CTX
*mem_ctx
, struct tldap_context
*ld
,
341 const char *ld_error
= NULL
;
345 ld_error
= tldap_msg_diagnosticmessage(tldap_ctx_lastmsg(ld
));
347 res
= talloc_asprintf(mem_ctx
, "LDAP error %d (%s), %s",
348 (int)TLDAP_RC_V(rc
), tldap_rc2string(rc
),
349 ld_error
? ld_error
: "unknown");
353 TLDAPRC
tldap_search_va(struct tldap_context
*ld
, const char *base
, int scope
,
354 const char *attrs
[], int num_attrs
, int attrsonly
,
355 TALLOC_CTX
*mem_ctx
, struct tldap_message
***res
,
356 const char *fmt
, va_list ap
)
361 filter
= talloc_vasprintf(talloc_tos(), fmt
, ap
);
362 if (filter
== NULL
) {
363 return TLDAP_NO_MEMORY
;
366 rc
= tldap_search(ld
, base
, scope
, filter
,
367 attrs
, num_attrs
, attrsonly
,
368 NULL
/*sctrls*/, 0, NULL
/*cctrls*/, 0,
369 0 /*timelimit*/, 0 /*sizelimit*/, 0 /*deref*/,
375 TLDAPRC
tldap_search_fmt(struct tldap_context
*ld
, const char *base
, int scope
,
376 const char *attrs
[], int num_attrs
, int attrsonly
,
377 TALLOC_CTX
*mem_ctx
, struct tldap_message
***res
,
378 const char *fmt
, ...)
384 rc
= tldap_search_va(ld
, base
, scope
, attrs
, num_attrs
, attrsonly
,
385 mem_ctx
, res
, fmt
, ap
);
390 bool tldap_pull_uint64(struct tldap_message
*msg
, const char *attr
,
397 str
= tldap_talloc_single_attribute(msg
, attr
, talloc_tos());
399 DEBUG(10, ("Could not find attribute %s\n", attr
));
403 result
= smb_strtoull(str
, NULL
, 10, &error
, SMB_STR_STANDARD
);
405 DBG_DEBUG("Attribute conversion failed (%s)\n",
416 bool tldap_pull_uint32(struct tldap_message
*msg
, const char *attr
,
421 if (!tldap_pull_uint64(msg
, attr
, &result
)) {
424 *presult
= (uint32_t)result
;
428 struct tldap_fetch_rootdse_state
{
429 struct tldap_context
*ld
;
430 struct tldap_message
*rootdse
;
433 static void tldap_fetch_rootdse_done(struct tevent_req
*subreq
);
435 struct tevent_req
*tldap_fetch_rootdse_send(TALLOC_CTX
*mem_ctx
,
436 struct tevent_context
*ev
,
437 struct tldap_context
*ld
)
439 struct tevent_req
*req
, *subreq
;
440 struct tldap_fetch_rootdse_state
*state
;
441 static const char *attrs
[2] = { "*", "+" };
443 req
= tevent_req_create(mem_ctx
, &state
,
444 struct tldap_fetch_rootdse_state
);
449 state
->rootdse
= NULL
;
451 subreq
= tldap_search_send(
452 mem_ctx
, ev
, ld
, "", TLDAP_SCOPE_BASE
, "(objectclass=*)",
453 attrs
, ARRAY_SIZE(attrs
), 0, NULL
, 0, NULL
, 0, 0, 0, 0);
454 if (tevent_req_nomem(subreq
, req
)) {
455 return tevent_req_post(req
, ev
);
457 tevent_req_set_callback(subreq
, tldap_fetch_rootdse_done
, req
);
461 static void tldap_fetch_rootdse_done(struct tevent_req
*subreq
)
463 struct tevent_req
*req
= tevent_req_callback_data(
464 subreq
, struct tevent_req
);
465 struct tldap_fetch_rootdse_state
*state
= tevent_req_data(
466 req
, struct tldap_fetch_rootdse_state
);
467 struct tldap_message
*msg
;
470 rc
= tldap_search_recv(subreq
, state
, &msg
);
471 if (tevent_req_ldap_error(req
, rc
)) {
475 switch (tldap_msg_type(msg
)) {
476 case TLDAP_RES_SEARCH_ENTRY
:
477 if (state
->rootdse
!= NULL
) {
480 state
->rootdse
= msg
;
482 case TLDAP_RES_SEARCH_RESULT
:
484 if (state
->rootdse
== NULL
) {
487 tevent_req_done(req
);
495 tevent_req_ldap_error(req
, TLDAP_PROTOCOL_ERROR
);
499 TLDAPRC
tldap_fetch_rootdse_recv(struct tevent_req
*req
)
501 struct tldap_fetch_rootdse_state
*state
= tevent_req_data(
502 req
, struct tldap_fetch_rootdse_state
);
506 if (tevent_req_is_ldap_error(req
, &rc
)) {
509 /* Trigger parsing the dn, just to make sure it's ok */
510 if (!tldap_entry_dn(state
->rootdse
, &dn
)) {
511 return TLDAP_DECODING_ERROR
;
513 if (!tldap_context_setattr(state
->ld
, "tldap:rootdse",
515 return TLDAP_NO_MEMORY
;
517 return TLDAP_SUCCESS
;
520 TLDAPRC
tldap_fetch_rootdse(struct tldap_context
*ld
)
522 TALLOC_CTX
*frame
= talloc_stackframe();
523 struct tevent_context
*ev
;
524 struct tevent_req
*req
;
525 TLDAPRC rc
= TLDAP_NO_MEMORY
;
527 ev
= samba_tevent_context_init(frame
);
531 req
= tldap_fetch_rootdse_send(frame
, ev
, ld
);
535 if (!tevent_req_poll(req
, ev
)) {
536 rc
= TLDAP_OPERATIONS_ERROR
;
540 rc
= tldap_fetch_rootdse_recv(req
);
546 struct tldap_message
*tldap_rootdse(struct tldap_context
*ld
)
548 return talloc_get_type(tldap_context_getattr(ld
, "tldap:rootdse"),
549 struct tldap_message
);
552 bool tldap_entry_has_attrvalue(struct tldap_message
*msg
,
553 const char *attribute
,
554 const DATA_BLOB blob
)
559 if (!tldap_entry_values(msg
, attribute
, &values
, &num_values
)) {
562 for (i
=0; i
<num_values
; i
++) {
563 if (data_blob_cmp(&values
[i
], &blob
) == 0) {
570 bool tldap_supports_control(struct tldap_context
*ld
, const char *oid
)
572 struct tldap_message
*rootdse
= tldap_rootdse(ld
);
574 if (rootdse
== NULL
) {
577 return tldap_entry_has_attrvalue(rootdse
, "supportedControl",
578 data_blob_const(oid
, strlen(oid
)));
581 struct tldap_control
*tldap_add_control(TALLOC_CTX
*mem_ctx
,
582 struct tldap_control
*ctrls
,
584 struct tldap_control
*ctrl
)
586 struct tldap_control
*result
;
588 result
= talloc_array(mem_ctx
, struct tldap_control
, num_ctrls
+1);
589 if (result
== NULL
) {
593 memcpy(result
, ctrls
, sizeof(struct tldap_control
) * num_ctrls
);
595 result
[num_ctrls
] = *ctrl
;
600 * Find a control returned by the server
602 struct tldap_control
*tldap_msg_findcontrol(struct tldap_message
*msg
,
605 struct tldap_control
*controls
;
608 tldap_msg_sctrls(msg
, &num_controls
, &controls
);
610 for (i
=0; i
<num_controls
; i
++) {
611 if (strcmp(controls
[i
].oid
, oid
) == 0) {
618 struct tldap_search_paged_state
{
619 struct tevent_context
*ev
;
620 struct tldap_context
*ld
;
627 struct tldap_control
*sctrls
;
629 struct tldap_control
*cctrls
;
636 struct asn1_data
*asn1
;
638 struct tldap_message
*result
;
641 static struct tevent_req
*tldap_ship_paged_search(
643 struct tldap_search_paged_state
*state
)
645 struct tldap_control
*pgctrl
;
646 struct asn1_data
*asn1
= NULL
;
648 asn1
= asn1_init(state
, ASN1_MAX_TREE_DEPTH
);
652 if (!asn1_push_tag(asn1
, ASN1_SEQUENCE(0))) goto err
;
653 if (!asn1_write_Integer(asn1
, state
->page_size
)) goto err
;
654 if (!asn1_write_OctetString(asn1
, state
->cookie
.data
, state
->cookie
.length
)) goto err
;
655 if (!asn1_pop_tag(asn1
)) goto err
;
658 pgctrl
= &state
->sctrls
[state
->num_sctrls
-1];
659 pgctrl
->oid
= TLDAP_CONTROL_PAGEDRESULTS
;
660 pgctrl
->critical
= true;
661 if (!asn1_blob(state
->asn1
, &pgctrl
->value
)) {
664 return tldap_search_send(mem_ctx
, state
->ev
, state
->ld
, state
->base
,
665 state
->scope
, state
->filter
, state
->attrs
,
666 state
->num_attrs
, state
->attrsonly
,
667 state
->sctrls
, state
->num_sctrls
,
668 state
->cctrls
, state
->num_cctrls
,
669 state
->timelimit
, state
->sizelimit
,
678 static void tldap_search_paged_done(struct tevent_req
*subreq
);
680 struct tevent_req
*tldap_search_paged_send(TALLOC_CTX
*mem_ctx
,
681 struct tevent_context
*ev
,
682 struct tldap_context
*ld
,
683 const char *base
, int scope
,
688 struct tldap_control
*sctrls
,
690 struct tldap_control
*cctrls
,
697 struct tevent_req
*req
, *subreq
;
698 struct tldap_search_paged_state
*state
;
699 struct tldap_control empty_control
;
701 req
= tevent_req_create(mem_ctx
, &state
,
702 struct tldap_search_paged_state
);
709 state
->filter
= filter
;
710 state
->scope
= scope
;
711 state
->attrs
= attrs
;
712 state
->num_attrs
= num_attrs
;
713 state
->attrsonly
= attrsonly
;
714 state
->cctrls
= cctrls
;
715 state
->num_cctrls
= num_cctrls
;
716 state
->timelimit
= timelimit
;
717 state
->sizelimit
= sizelimit
;
718 state
->deref
= deref
;
720 state
->page_size
= page_size
;
722 state
->cookie
= data_blob_null
;
724 ZERO_STRUCT(empty_control
);
726 state
->sctrls
= tldap_add_control(state
, sctrls
, num_sctrls
,
728 if (tevent_req_nomem(state
->sctrls
, req
)) {
729 return tevent_req_post(req
, ev
);
731 state
->num_sctrls
= num_sctrls
+1;
733 subreq
= tldap_ship_paged_search(state
, state
);
734 if (tevent_req_nomem(subreq
, req
)) {
735 return tevent_req_post(req
, ev
);
737 tevent_req_set_callback(subreq
, tldap_search_paged_done
, req
);
742 static void tldap_search_paged_done(struct tevent_req
*subreq
)
744 struct tevent_req
*req
= tevent_req_callback_data(
745 subreq
, struct tevent_req
);
746 struct tldap_search_paged_state
*state
= tevent_req_data(
747 req
, struct tldap_search_paged_state
);
748 struct asn1_data
*asn1
= NULL
;
749 struct tldap_control
*pgctrl
;
753 rc
= tldap_search_recv(subreq
, state
, &state
->result
);
754 if (tevent_req_ldap_error(req
, rc
)) {
758 TALLOC_FREE(state
->asn1
);
760 switch (tldap_msg_type(state
->result
)) {
761 case TLDAP_RES_SEARCH_ENTRY
:
762 case TLDAP_RES_SEARCH_REFERENCE
:
763 tevent_req_notify_callback(req
);
765 case TLDAP_RES_SEARCH_RESULT
:
769 tevent_req_ldap_error(req
, TLDAP_PROTOCOL_ERROR
);
775 /* We've finished one paged search, fire the next */
777 pgctrl
= tldap_msg_findcontrol(state
->result
,
778 TLDAP_CONTROL_PAGEDRESULTS
);
779 if (pgctrl
== NULL
) {
780 /* RFC2696 requires the server to return the control */
781 tevent_req_ldap_error(req
, TLDAP_PROTOCOL_ERROR
);
785 TALLOC_FREE(state
->cookie
.data
);
787 asn1
= asn1_init(talloc_tos(), ASN1_MAX_TREE_DEPTH
);
788 if (tevent_req_nomem(asn1
, req
)) {
792 asn1_load_nocopy(asn1
, pgctrl
->value
.data
, pgctrl
->value
.length
);
793 if (!asn1_start_tag(asn1
, ASN1_SEQUENCE(0))) goto err
;
794 if (!asn1_read_Integer(asn1
, &size
)) goto err
;
795 if (!asn1_read_OctetString(asn1
, state
, &state
->cookie
)) goto err
;
796 if (!asn1_end_tag(asn1
)) goto err
;
800 if (state
->cookie
.length
== 0) {
801 /* We're done, no cookie anymore */
802 tevent_req_done(req
);
806 TALLOC_FREE(state
->result
);
808 subreq
= tldap_ship_paged_search(state
, state
);
809 if (tevent_req_nomem(subreq
, req
)) {
812 tevent_req_set_callback(subreq
, tldap_search_paged_done
, req
);
818 tevent_req_ldap_error(req
, TLDAP_DECODING_ERROR
);
821 TLDAPRC
tldap_search_paged_recv(struct tevent_req
*req
, TALLOC_CTX
*mem_ctx
,
822 struct tldap_message
**pmsg
)
824 struct tldap_search_paged_state
*state
= tevent_req_data(
825 req
, struct tldap_search_paged_state
);
828 if (!tevent_req_is_in_progress(req
)
829 && tevent_req_is_ldap_error(req
, &rc
)) {
832 if (tevent_req_is_in_progress(req
)) {
833 switch (tldap_msg_type(state
->result
)) {
834 case TLDAP_RES_SEARCH_ENTRY
:
835 case TLDAP_RES_SEARCH_REFERENCE
:
838 return TLDAP_PROTOCOL_ERROR
;
841 *pmsg
= talloc_move(mem_ctx
, &state
->result
);
842 return TLDAP_SUCCESS
;