2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/addns/dnsquery.h"
29 #include "../libds/common/flags.h"
31 #include "../libcli/security/security.h"
32 #include "../librpc/gen_ndr/netlogon.h"
33 #include "lib/param/loadparm.h"
34 #include "libsmb/namequery.h"
40 * @brief basic ldap client-side routines for ads server communications
42 * The routines contained here should do the necessary ldap calls for
45 * Important note: attribute names passed into ads_ routines must
46 * already be in UTF-8 format. We do not convert them because in almost
47 * all cases, they are just ascii (which is represented with the same
48 * codepoints in UTF-8). This may have to change at some point
52 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
54 static SIG_ATOMIC_T gotalarm;
56 /***************************************************************
57 Signal function to tell us we timed out.
58 ****************************************************************/
60 static void gotalarm_sig(int signum)
65 LDAP *ldap_open_with_timeout(const char *server,
66 struct sockaddr_storage *ss,
67 int port, unsigned int to)
73 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
74 "%u seconds\n", server, port, to));
79 CatchSignal(SIGALRM, gotalarm_sig);
81 /* End setup timeout. */
84 if ( strchr_m(server, ':') ) {
86 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
89 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
95 #ifdef HAVE_LDAP_INIT_FD
98 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
99 unsigned timeout_ms = 1000 * to;
101 status = open_socket_out(ss, port, timeout_ms, &fd);
102 if (!NT_STATUS_IS_OK(status)) {
103 DEBUG(3, ("open_socket_out: failed to open socket\n"));
107 /* define LDAP_PROTO_TCP from openldap.h if required */
108 #ifndef LDAP_PROTO_TCP
109 #define LDAP_PROTO_TCP 1
111 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
113 #elif defined(HAVE_LDAP_INITIALIZE)
114 ldap_err = ldap_initialize(&ldp, uri);
116 ldp = ldap_open(server, port);
118 ldap_err = LDAP_SUCCESS;
120 ldap_err = LDAP_OTHER;
123 if (ldap_err != LDAP_SUCCESS) {
124 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
125 uri, ldap_err2string(ldap_err)));
127 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
131 /* Teardown timeout. */
133 CatchSignal(SIGALRM, SIG_IGN);
139 static int ldap_search_with_timeout(LDAP *ld,
140 LDAP_CONST char *base,
142 LDAP_CONST char *filter,
145 LDAPControl **sctrls,
146 LDAPControl **cctrls,
150 int to = lp_ldap_timeout();
151 struct timeval timeout;
152 struct timeval *timeout_ptr = NULL;
155 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
161 timeout_ptr = &timeout;
163 /* Setup alarm timeout. */
164 CatchSignal(SIGALRM, gotalarm_sig);
165 /* Make the alarm time one second beyond
166 the timout we're setting for the
167 remote search timeout, to allow that
168 to fire in preference. */
170 /* End setup timeout. */
174 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
175 attrsonly, sctrls, cctrls, timeout_ptr,
179 /* Teardown alarm timeout. */
180 CatchSignal(SIGALRM, SIG_IGN);
185 return LDAP_TIMELIMIT_EXCEEDED;
188 * A bug in OpenLDAP means ldap_search_ext_s can return
189 * LDAP_SUCCESS but with a NULL res pointer. Cope with
190 * this. See bug #6279 for details. JRA.
194 return LDAP_TIMELIMIT_EXCEEDED;
200 /**********************************************
201 Do client and server sitename match ?
202 **********************************************/
204 bool ads_sitename_match(ADS_STRUCT *ads)
206 if (ads->config.server_site_name == NULL &&
207 ads->config.client_site_name == NULL ) {
208 DEBUG(10,("ads_sitename_match: both null\n"));
211 if (ads->config.server_site_name &&
212 ads->config.client_site_name &&
213 strequal(ads->config.server_site_name,
214 ads->config.client_site_name)) {
215 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
218 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
219 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
220 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
224 /**********************************************
225 Is this the closest DC ?
226 **********************************************/
228 bool ads_closest_dc(ADS_STRUCT *ads)
230 if (ads->config.flags & NBT_SERVER_CLOSEST) {
231 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
235 /* not sure if this can ever happen */
236 if (ads_sitename_match(ads)) {
237 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
241 if (ads->config.client_site_name == NULL) {
242 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
246 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
247 ads->config.ldap_server_name));
254 try a connection to a given ldap server, returning True and setting the servers IP
255 in the ads struct if successful
257 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
258 struct sockaddr_storage *ss)
260 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
261 TALLOC_CTX *frame = talloc_stackframe();
263 char addr[INET6_ADDRSTRLEN];
270 print_sockaddr(addr, sizeof(addr), ss);
272 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
273 addr, ads->server.realm));
275 ZERO_STRUCT( cldap_reply );
277 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
278 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
283 /* Check the CLDAP reply flags */
285 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
286 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
292 /* Fill in the ads->config values */
294 SAFE_FREE(ads->config.realm);
295 SAFE_FREE(ads->config.bind_path);
296 SAFE_FREE(ads->config.ldap_server_name);
297 SAFE_FREE(ads->config.server_site_name);
298 SAFE_FREE(ads->config.client_site_name);
299 SAFE_FREE(ads->server.workgroup);
301 if (!check_cldap_reply_required_flags(cldap_reply.server_type,
302 ads->config.flags)) {
307 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
308 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
309 if (!strupper_m(ads->config.realm)) {
314 ads->config.bind_path = ads_build_dn(ads->config.realm);
315 if (*cldap_reply.server_site) {
316 ads->config.server_site_name =
317 SMB_STRDUP(cldap_reply.server_site);
319 if (*cldap_reply.client_site) {
320 ads->config.client_site_name =
321 SMB_STRDUP(cldap_reply.client_site);
323 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
325 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
328 /* Store our site name. */
329 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
330 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
332 /* Leave this until last so that the flags are not clobbered */
333 ads->config.flags = cldap_reply.server_type;
343 /**********************************************************************
344 send a cldap ping to list of servers, one at a time, until one of
345 them answers it's an ldap server. Record success in the ADS_STRUCT.
346 Take note of and update negative connection cache.
347 **********************************************************************/
349 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
351 struct samba_sockaddr *sa_list,
357 for (i = 0; i < count; i++) {
358 char server[INET6_ADDRSTRLEN];
360 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
362 if (!NT_STATUS_IS_OK(
363 check_negative_conn_cache(domain, server)))
366 /* Returns ok only if it matches the correct server type */
367 ok = ads_try_connect(ads, false, &sa_list[i].u.ss);
373 /* keep track of failures */
374 add_failed_connection_entry(domain, server,
375 NT_STATUS_UNSUCCESSFUL);
378 return NT_STATUS_NO_LOGON_SERVERS;
381 /***************************************************************************
382 resolve a name and perform an "ldap ping" using NetBIOS and related methods
383 ****************************************************************************/
385 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
386 const char *domain, const char *realm)
390 struct samba_sockaddr *sa_list = NULL;
393 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
396 status = get_sorted_dc_list(talloc_tos(),
402 if (!NT_STATUS_IS_OK(status)) {
406 /* remove servers which are known to be dead based on
407 the corresponding DNS method */
409 for (i = 0; i < count; ++i) {
410 char server[INET6_ADDRSTRLEN];
412 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
415 check_negative_conn_cache(realm, server))) {
416 /* Ensure we add the workgroup name for this
417 IP address as negative too. */
418 add_failed_connection_entry(
420 NT_STATUS_UNSUCCESSFUL);
425 status = cldap_ping_list(ads, domain, sa_list, count);
427 TALLOC_FREE(sa_list);
433 /**********************************************************************
434 resolve a name and perform an "ldap ping" using DNS
435 **********************************************************************/
437 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
441 struct samba_sockaddr *sa_list = NULL;
444 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
447 status = get_sorted_dc_list(talloc_tos(),
453 if (!NT_STATUS_IS_OK(status)) {
454 TALLOC_FREE(sa_list);
458 status = cldap_ping_list(ads, realm, sa_list, count);
460 TALLOC_FREE(sa_list);
465 /**********************************************************************
466 Try to find an AD dc using our internal name resolution routines
467 Try the realm first and then then workgroup name if netbios is not
469 **********************************************************************/
471 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
473 const char *c_domain = "";
475 bool use_own_domain = False;
476 char *sitename = NULL;
477 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
480 /* if the realm and workgroup are both empty, assume they are ours */
483 c_realm = ads->server.realm;
489 /* special case where no realm and no workgroup means our own */
490 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
491 use_own_domain = True;
492 c_realm = lp_realm();
496 if (!lp_disable_netbios()) {
497 if (use_own_domain) {
498 c_domain = lp_workgroup();
500 c_domain = ads->server.workgroup;
501 if (!*c_realm && (!c_domain || !*c_domain)) {
502 c_domain = lp_workgroup();
511 if (!*c_realm && !*c_domain) {
512 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
514 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
518 * In case of LDAP we use get_dc_name() as that
519 * creates the custom krb5.conf file
521 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
523 struct sockaddr_storage ip_out;
525 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
526 " and falling back to domain '%s'\n",
529 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
532 * we call ads_try_connect() to fill in the
533 * ads->config details
535 ok = ads_try_connect(ads, false, &ip_out);
541 return NT_STATUS_NO_LOGON_SERVERS;
545 sitename = sitename_fetch(talloc_tos(), c_realm);
546 status = resolve_and_ping_dns(ads, sitename, c_realm);
548 if (NT_STATUS_IS_OK(status)) {
549 TALLOC_FREE(sitename);
553 /* In case we failed to contact one of our closest DC on our
555 * need to try to find another DC, retry with a site-less SRV
560 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
561 "our site (%s), Trying to find another DC "
562 "for realm '%s' (domain '%s')\n",
563 sitename, c_realm, c_domain));
564 namecache_delete(c_realm, 0x1C);
566 resolve_and_ping_dns(ads, NULL, c_realm);
568 if (NT_STATUS_IS_OK(status)) {
569 TALLOC_FREE(sitename);
574 TALLOC_FREE(sitename);
577 /* try netbios as fallback - if permitted,
578 or if configuration specifically requests it */
581 DEBUG(3, ("ads_find_dc: falling back to netbios "
582 "name resolution for domain '%s' (realm '%s')\n",
586 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
587 if (NT_STATUS_IS_OK(status)) {
592 DEBUG(1, ("ads_find_dc: "
593 "name resolution for realm '%s' (domain '%s') failed: %s\n",
594 c_realm, c_domain, nt_errstr(status)));
598 * Connect to the LDAP server
599 * @param ads Pointer to an existing ADS_STRUCT
600 * @return status of connection
602 ADS_STATUS ads_connect(ADS_STRUCT *ads)
604 int version = LDAP_VERSION3;
607 char addr[INET6_ADDRSTRLEN];
608 struct sockaddr_storage existing_ss;
610 zero_sockaddr(&existing_ss);
613 * ads_connect can be passed in a reused ADS_STRUCT
614 * with an existing non-zero ads->ldap.ss IP address
615 * that was stored by going through ads_find_dc()
616 * if ads->server.ldap_server was NULL.
618 * If ads->server.ldap_server is still NULL but
619 * the target address isn't the zero address, then
620 * store that address off off before zeroing out
621 * ads->ldap so we don't keep doing multiple calls
622 * to ads_find_dc() in the reuse case.
624 * If a caller wants a clean ADS_STRUCT they
625 * will re-initialize by calling ads_init(), or
626 * call ads_destroy() both of which ensures
627 * ads->ldap.ss is a properly zero'ed out valid IP
630 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
631 /* Save off the address we previously found by ads_find_dc(). */
632 existing_ss = ads->ldap.ss;
636 ZERO_STRUCT(ads->ldap_wrap_data);
637 ads->ldap.last_attempt = time_mono(NULL);
638 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
640 /* try with a user specified server */
642 if (DEBUGLEVEL >= 11) {
643 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
644 DEBUG(11,("ads_connect: entering\n"));
645 DEBUGADD(11,("%s\n", s));
649 if (ads->server.ldap_server) {
651 struct sockaddr_storage ss;
653 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
655 DEBUG(5,("ads_connect: unable to resolve name %s\n",
656 ads->server.ldap_server));
657 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
660 ok = ads_try_connect(ads, ads->server.gc, &ss);
665 /* The choice of which GC use is handled one level up in
666 ads_connect_gc(). If we continue on from here with
667 ads_find_dc() we will get GC searches on port 389 which
668 doesn't work. --jerry */
670 if (ads->server.gc == true) {
671 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
674 if (ads->server.no_fallback) {
675 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
680 if (!is_zero_addr(&existing_ss)) {
681 /* We saved off who we should talk to. */
682 bool ok = ads_try_connect(ads,
689 * Keep trying to find a server and fall through
690 * into ads_find_dc() again.
694 ntstatus = ads_find_dc(ads);
695 if (NT_STATUS_IS_OK(ntstatus)) {
699 status = ADS_ERROR_NT(ntstatus);
704 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
705 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
707 if (!ads->auth.user_name) {
708 /* Must use the userPrincipalName value here or sAMAccountName
709 and not servicePrincipalName; found by Guenther Deschner */
711 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
712 DEBUG(0,("ads_connect: asprintf fail.\n"));
713 ads->auth.user_name = NULL;
717 if (!ads->auth.realm) {
718 ads->auth.realm = SMB_STRDUP(ads->config.realm);
721 if (!ads->auth.kdc_server) {
722 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
723 ads->auth.kdc_server = SMB_STRDUP(addr);
726 /* If the caller() requested no LDAP bind, then we are done */
728 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
729 status = ADS_SUCCESS;
733 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
734 if (!ads->ldap_wrap_data.mem_ctx) {
735 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
739 /* Otherwise setup the TCP LDAP session */
741 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
743 ads->ldap.port, lp_ldap_timeout());
744 if (ads->ldap.ld == NULL) {
745 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
748 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
750 /* cache the successful connection for workgroup and realm */
751 if (ads_closest_dc(ads)) {
752 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
753 saf_store( ads->server.realm, ads->config.ldap_server_name);
756 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
758 /* fill in the current time and offsets */
760 status = ads_current_time( ads );
761 if ( !ADS_ERR_OK(status) ) {
765 /* Now do the bind */
767 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
768 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
772 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
773 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
777 status = ads_sasl_bind(ads);
780 if (DEBUGLEVEL >= 11) {
781 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
782 DEBUG(11,("ads_connect: leaving with: %s\n",
783 ads_errstr(status)));
784 DEBUGADD(11,("%s\n", s));
792 * Connect to the LDAP server using given credentials
793 * @param ads Pointer to an existing ADS_STRUCT
794 * @return status of connection
796 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
798 ads->auth.flags |= ADS_AUTH_USER_CREDS;
800 return ads_connect(ads);
804 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
805 * @param ads Pointer to an existing ADS_STRUCT
807 * Sets the ads->ldap.ss to a valid
808 * zero ip address that can be detected by
809 * our is_zero_addr() function. Otherwise
810 * it is left as AF_UNSPEC (0).
812 void ads_zero_ldap(ADS_STRUCT *ads)
814 ZERO_STRUCT(ads->ldap);
816 * Initialize the sockaddr_storage so we can use
817 * sockaddr test functions against it.
819 zero_sockaddr(&ads->ldap.ss);
823 * Disconnect the LDAP server
824 * @param ads Pointer to an existing ADS_STRUCT
826 void ads_disconnect(ADS_STRUCT *ads)
829 ldap_unbind(ads->ldap.ld);
832 if (ads->ldap_wrap_data.wrap_ops &&
833 ads->ldap_wrap_data.wrap_ops->disconnect) {
834 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
836 if (ads->ldap_wrap_data.mem_ctx) {
837 talloc_free(ads->ldap_wrap_data.mem_ctx);
840 ZERO_STRUCT(ads->ldap_wrap_data);
844 Duplicate a struct berval into talloc'ed memory
846 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
848 struct berval *value;
850 if (!in_val) return NULL;
852 value = talloc_zero(ctx, struct berval);
855 if (in_val->bv_len == 0) return value;
857 value->bv_len = in_val->bv_len;
858 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
864 Make a values list out of an array of (struct berval *)
866 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
867 const struct berval **in_vals)
869 struct berval **values;
872 if (!in_vals) return NULL;
873 for (i=0; in_vals[i]; i++)
875 values = talloc_zero_array(ctx, struct berval *, i+1);
876 if (!values) return NULL;
878 for (i=0; in_vals[i]; i++) {
879 values[i] = dup_berval(ctx, in_vals[i]);
885 UTF8-encode a values list out of an array of (char *)
887 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
893 if (!in_vals) return NULL;
894 for (i=0; in_vals[i]; i++)
896 values = talloc_zero_array(ctx, char *, i+1);
897 if (!values) return NULL;
899 for (i=0; in_vals[i]; i++) {
900 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
909 Pull a (char *) array out of a UTF8-encoded values list
911 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
915 size_t converted_size;
917 if (!in_vals) return NULL;
918 for (i=0; in_vals[i]; i++)
920 values = talloc_zero_array(ctx, char *, i+1);
921 if (!values) return NULL;
923 for (i=0; in_vals[i]; i++) {
924 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
926 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
927 "%s", strerror(errno)));
934 * Do a search with paged results. cookie must be null on the first
935 * call, and then returned on each subsequent call. It will be null
936 * again when the entire search is complete
937 * @param ads connection to ads server
938 * @param bind_path Base dn for the search
939 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
940 * @param expr Search expression - specified in local charset
941 * @param attrs Attributes to retrieve - specified in utf8 or ascii
942 * @param res ** which will contain results - free res* with ads_msgfree()
943 * @param count Number of entries retrieved on this page
944 * @param cookie The paged results cookie to be returned on subsequent calls
945 * @return status of search
947 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
948 const char *bind_path,
949 int scope, const char *expr,
950 const char **attrs, void *args,
952 int *count, struct berval **cookie)
955 char *utf8_expr, *utf8_path, **search_attrs = NULL;
956 size_t converted_size;
957 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
958 BerElement *cookie_be = NULL;
959 struct berval *cookie_bv= NULL;
960 BerElement *ext_be = NULL;
961 struct berval *ext_bv= NULL;
964 ads_control *external_control = (ads_control *) args;
968 if (!(ctx = talloc_init("ads_do_paged_search_args")))
969 return ADS_ERROR(LDAP_NO_MEMORY);
971 /* 0 means the conversion worked but the result was empty
972 so we only fail if it's -1. In any case, it always
973 at least nulls out the dest */
974 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
975 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
981 if (!attrs || !(*attrs))
984 /* This would be the utf8-encoded version...*/
985 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
986 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
992 /* Paged results only available on ldap v3 or later */
993 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
994 if (version < LDAP_VERSION3) {
995 rc = LDAP_NOT_SUPPORTED;
999 cookie_be = ber_alloc_t(LBER_USE_DER);
1001 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1002 ber_bvfree(*cookie); /* don't need it from last time */
1005 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1007 ber_flatten(cookie_be, &cookie_bv);
1008 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1009 PagedResults.ldctl_iscritical = (char) 1;
1010 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1011 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1013 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1014 NoReferrals.ldctl_iscritical = (char) 0;
1015 NoReferrals.ldctl_value.bv_len = 0;
1016 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1018 if (external_control &&
1019 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1020 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1022 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1023 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1025 /* win2k does not accept a ldctl_value beeing passed in */
1027 if (external_control->val != 0) {
1029 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1030 rc = LDAP_NO_MEMORY;
1034 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1035 rc = LDAP_NO_MEMORY;
1038 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1039 rc = LDAP_NO_MEMORY;
1043 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1044 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1047 ExternalCtrl.ldctl_value.bv_len = 0;
1048 ExternalCtrl.ldctl_value.bv_val = NULL;
1051 controls[0] = &NoReferrals;
1052 controls[1] = &PagedResults;
1053 controls[2] = &ExternalCtrl;
1057 controls[0] = &NoReferrals;
1058 controls[1] = &PagedResults;
1062 /* we need to disable referrals as the openldap libs don't
1063 handle them and paged results at the same time. Using them
1064 together results in the result record containing the server
1065 page control being removed from the result list (tridge/jmcd)
1067 leaving this in despite the control that says don't generate
1068 referrals, in case the server doesn't support it (jmcd)
1070 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1072 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1073 search_attrs, 0, controls,
1074 NULL, LDAP_NO_LIMIT,
1075 (LDAPMessage **)res);
1077 ber_free(cookie_be, 1);
1078 ber_bvfree(cookie_bv);
1081 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1082 ldap_err2string(rc)));
1083 if (rc == LDAP_OTHER) {
1087 ret = ldap_parse_result(ads->ldap.ld,
1095 if (ret == LDAP_SUCCESS) {
1096 DEBUG(3, ("ldap_search_with_timeout(%s) "
1097 "error: %s\n", expr, ldap_errmsg));
1098 ldap_memfree(ldap_errmsg);
1104 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1105 NULL, &rcontrols, 0);
1111 for (i=0; rcontrols[i]; i++) {
1112 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1113 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1114 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1116 /* the berval is the cookie, but must be freed when
1118 if (cookie_bv->bv_len) /* still more to do */
1119 *cookie=ber_bvdup(cookie_bv);
1122 ber_bvfree(cookie_bv);
1123 ber_free(cookie_be, 1);
1127 ldap_controls_free(rcontrols);
1130 talloc_destroy(ctx);
1133 ber_free(ext_be, 1);
1140 if (rc != LDAP_SUCCESS && *res != NULL) {
1141 ads_msgfree(ads, *res);
1145 /* if/when we decide to utf8-encode attrs, take out this next line */
1146 TALLOC_FREE(search_attrs);
1148 return ADS_ERROR(rc);
1151 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1152 int scope, const char *expr,
1153 const char **attrs, LDAPMessage **res,
1154 int *count, struct berval **cookie)
1156 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1161 * Get all results for a search. This uses ads_do_paged_search() to return
1162 * all entries in a large search.
1163 * @param ads connection to ads server
1164 * @param bind_path Base dn for the search
1165 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1166 * @param expr Search expression
1167 * @param attrs Attributes to retrieve
1168 * @param res ** which will contain results - free res* with ads_msgfree()
1169 * @return status of search
1171 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1172 int scope, const char *expr,
1173 const char **attrs, void *args,
1176 struct berval *cookie = NULL;
1181 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1184 if (!ADS_ERR_OK(status))
1187 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1189 LDAPMessage *res2 = NULL;
1190 LDAPMessage *msg, *next;
1192 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1193 attrs, args, &res2, &count, &cookie);
1194 if (!ADS_ERR_OK(status)) {
1198 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1199 that this works on all ldap libs, but I have only tested with openldap */
1200 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1201 next = ads_next_message(ads, msg);
1202 ldap_add_result_entry((LDAPMessage **)res, msg);
1204 /* note that we do not free res2, as the memory is now
1205 part of the main returned list */
1208 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1209 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1215 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1216 int scope, const char *expr,
1217 const char **attrs, LDAPMessage **res)
1219 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1222 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1223 int scope, const char *expr,
1224 const char **attrs, uint32_t sd_flags,
1229 args.control = ADS_SD_FLAGS_OID;
1230 args.val = sd_flags;
1231 args.critical = True;
1233 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1238 * Run a function on all results for a search. Uses ads_do_paged_search() and
1239 * runs the function as each page is returned, using ads_process_results()
1240 * @param ads connection to ads server
1241 * @param bind_path Base dn for the search
1242 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1243 * @param expr Search expression - specified in local charset
1244 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1245 * @param fn Function which takes attr name, values list, and data_area
1246 * @param data_area Pointer which is passed to function on each call
1247 * @return status of search
1249 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1250 int scope, const char *expr, const char **attrs,
1251 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1254 struct berval *cookie = NULL;
1259 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1262 if (!ADS_ERR_OK(status)) return status;
1264 ads_process_results(ads, res, fn, data_area);
1265 ads_msgfree(ads, res);
1268 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1269 &res, &count, &cookie);
1271 if (!ADS_ERR_OK(status)) break;
1273 ads_process_results(ads, res, fn, data_area);
1274 ads_msgfree(ads, res);
1281 * Do a search with a timeout.
1282 * @param ads connection to ads server
1283 * @param bind_path Base dn for the search
1284 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1285 * @param expr Search expression
1286 * @param attrs Attributes to retrieve
1287 * @param res ** which will contain results - free res* with ads_msgfree()
1288 * @return status of search
1290 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1292 const char **attrs, LDAPMessage **res)
1295 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1296 size_t converted_size;
1300 if (!(ctx = talloc_init("ads_do_search"))) {
1301 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1302 return ADS_ERROR(LDAP_NO_MEMORY);
1305 /* 0 means the conversion worked but the result was empty
1306 so we only fail if it's negative. In any case, it always
1307 at least nulls out the dest */
1308 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1309 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1311 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1312 rc = LDAP_NO_MEMORY;
1316 if (!attrs || !(*attrs))
1317 search_attrs = NULL;
1319 /* This would be the utf8-encoded version...*/
1320 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1321 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1323 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1324 rc = LDAP_NO_MEMORY;
1329 /* see the note in ads_do_paged_search - we *must* disable referrals */
1330 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1332 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1333 search_attrs, 0, NULL, NULL,
1335 (LDAPMessage **)res);
1337 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1338 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1343 talloc_destroy(ctx);
1344 /* if/when we decide to utf8-encode attrs, take out this next line */
1345 TALLOC_FREE(search_attrs);
1346 return ADS_ERROR(rc);
1349 * Do a general ADS search
1350 * @param ads connection to ads server
1351 * @param res ** which will contain results - free res* with ads_msgfree()
1352 * @param expr Search expression
1353 * @param attrs Attributes to retrieve
1354 * @return status of search
1356 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1357 const char *expr, const char **attrs)
1359 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1364 * Do a search on a specific DistinguishedName
1365 * @param ads connection to ads server
1366 * @param res ** which will contain results - free res* with ads_msgfree()
1367 * @param dn DistinguishName to search
1368 * @param attrs Attributes to retrieve
1369 * @return status of search
1371 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1372 const char *dn, const char **attrs)
1374 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1379 * Free up memory from a ads_search
1380 * @param ads connection to ads server
1381 * @param msg Search results to free
1383 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1390 * Get a dn from search results
1391 * @param ads connection to ads server
1392 * @param msg Search result
1395 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1397 char *utf8_dn, *unix_dn;
1398 size_t converted_size;
1400 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1403 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1407 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1408 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1412 ldap_memfree(utf8_dn);
1417 * Get the parent from a dn
1418 * @param dn the dn to return the parent from
1419 * @return parent dn string
1421 char *ads_parent_dn(const char *dn)
1429 p = strchr(dn, ',');
1439 * Find a machine account given a hostname
1440 * @param ads connection to ads server
1441 * @param res ** which will contain results - free res* with ads_msgfree()
1442 * @param host Hostname to search for
1443 * @return status of search
1445 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1446 const char *machine)
1450 const char *attrs[] = {
1451 /* This is how Windows checks for machine accounts */
1454 "userAccountControl",
1456 "ServicePrincipalName",
1457 "userPrincipalName",
1460 /* Additional attributes Samba checks */
1461 "msDS-AdditionalDnsHostName",
1462 "msDS-SupportedEncryptionTypes",
1463 "nTSecurityDescriptor",
1468 TALLOC_CTX *frame = talloc_stackframe();
1472 /* the easiest way to find a machine account anywhere in the tree
1473 is to look for hostname$ */
1474 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1476 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1480 status = ads_search(ads, res, expr, attrs);
1481 if (ADS_ERR_OK(status)) {
1482 if (ads_count_replies(ads, *res) != 1) {
1483 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1493 * Initialize a list of mods to be used in a modify request
1494 * @param ctx An initialized TALLOC_CTX
1495 * @return allocated ADS_MODLIST
1497 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1499 #define ADS_MODLIST_ALLOC_SIZE 10
1502 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1503 /* -1 is safety to make sure we don't go over the end.
1504 need to reset it to NULL before doing ldap modify */
1505 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1507 return (ADS_MODLIST)mods;
1512 add an attribute to the list, with values list already constructed
1514 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1515 int mod_op, const char *name,
1516 const void *_invals)
1519 LDAPMod **modlist = (LDAPMod **) *mods;
1520 struct berval **ber_values = NULL;
1521 char **char_values = NULL;
1524 mod_op = LDAP_MOD_DELETE;
1526 if (mod_op & LDAP_MOD_BVALUES) {
1527 const struct berval **b;
1528 b = discard_const_p(const struct berval *, _invals);
1529 ber_values = ads_dup_values(ctx, b);
1532 c = discard_const_p(const char *, _invals);
1533 char_values = ads_push_strvals(ctx, c);
1537 /* find the first empty slot */
1538 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1540 if (modlist[curmod] == (LDAPMod *) -1) {
1541 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1542 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1543 return ADS_ERROR(LDAP_NO_MEMORY);
1544 memset(&modlist[curmod], 0,
1545 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1546 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1547 *mods = (ADS_MODLIST)modlist;
1550 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1551 return ADS_ERROR(LDAP_NO_MEMORY);
1552 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1553 if (mod_op & LDAP_MOD_BVALUES) {
1554 modlist[curmod]->mod_bvalues = ber_values;
1555 } else if (mod_op & LDAP_MOD_DELETE) {
1556 modlist[curmod]->mod_values = NULL;
1558 modlist[curmod]->mod_values = char_values;
1561 modlist[curmod]->mod_op = mod_op;
1562 return ADS_ERROR(LDAP_SUCCESS);
1566 * Add a single string value to a mod list
1567 * @param ctx An initialized TALLOC_CTX
1568 * @param mods An initialized ADS_MODLIST
1569 * @param name The attribute name to add
1570 * @param val The value to add - NULL means DELETE
1571 * @return ADS STATUS indicating success of add
1573 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1574 const char *name, const char *val)
1576 const char *values[2];
1582 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1583 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1587 * Add an array of string values to a mod list
1588 * @param ctx An initialized TALLOC_CTX
1589 * @param mods An initialized ADS_MODLIST
1590 * @param name The attribute name to add
1591 * @param vals The array of string values to add - NULL means DELETE
1592 * @return ADS STATUS indicating success of add
1594 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1595 const char *name, const char **vals)
1598 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1599 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1600 name, (const void **) vals);
1604 * Add a single ber-encoded value to a mod list
1605 * @param ctx An initialized TALLOC_CTX
1606 * @param mods An initialized ADS_MODLIST
1607 * @param name The attribute name to add
1608 * @param val The value to add - NULL means DELETE
1609 * @return ADS STATUS indicating success of add
1611 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1612 const char *name, const struct berval *val)
1614 const struct berval *values[2];
1619 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1620 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1621 name, (const void **) values);
1624 static void ads_print_error(int ret, LDAP *ld)
1627 char *ld_error = NULL;
1628 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1629 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1631 ldap_err2string(ret),
1633 SAFE_FREE(ld_error);
1638 * Perform an ldap modify
1639 * @param ads connection to ads server
1640 * @param mod_dn DistinguishedName to modify
1641 * @param mods list of modifications to perform
1642 * @return status of modify
1644 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1647 char *utf8_dn = NULL;
1648 size_t converted_size;
1650 this control is needed to modify that contains a currently
1651 non-existent attribute (but allowable for the object) to run
1653 LDAPControl PermitModify = {
1654 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1657 LDAPControl *controls[2];
1659 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1661 controls[0] = &PermitModify;
1664 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1665 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1668 /* find the end of the list, marked by NULL or -1 */
1669 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1670 /* make sure the end of the list is NULL */
1672 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1673 (LDAPMod **) mods, controls, NULL);
1674 ads_print_error(ret, ads->ldap.ld);
1675 TALLOC_FREE(utf8_dn);
1676 return ADS_ERROR(ret);
1680 * Perform an ldap add
1681 * @param ads connection to ads server
1682 * @param new_dn DistinguishedName to add
1683 * @param mods list of attributes and values for DN
1684 * @return status of add
1686 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1689 char *utf8_dn = NULL;
1690 size_t converted_size;
1692 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1694 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1695 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1696 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1699 /* find the end of the list, marked by NULL or -1 */
1700 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1701 /* make sure the end of the list is NULL */
1704 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1705 ads_print_error(ret, ads->ldap.ld);
1706 TALLOC_FREE(utf8_dn);
1707 return ADS_ERROR(ret);
1711 * Delete a DistinguishedName
1712 * @param ads connection to ads server
1713 * @param new_dn DistinguishedName to delete
1714 * @return status of delete
1716 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1719 char *utf8_dn = NULL;
1720 size_t converted_size;
1721 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1722 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1723 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1726 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1728 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1729 ads_print_error(ret, ads->ldap.ld);
1730 TALLOC_FREE(utf8_dn);
1731 return ADS_ERROR(ret);
1735 * Build an org unit string
1736 * if org unit is Computers or blank then assume a container, otherwise
1737 * assume a / separated list of organisational units.
1738 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1739 * @param ads connection to ads server
1740 * @param org_unit Organizational unit
1741 * @return org unit string - caller must free
1743 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1747 if (!org_unit || !*org_unit) {
1749 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1751 /* samba4 might not yet respond to a wellknownobject-query */
1752 return ret ? ret : SMB_STRDUP("cn=Computers");
1755 if (strequal(org_unit, "Computers")) {
1756 return SMB_STRDUP("cn=Computers");
1759 /* jmcd: removed "\\" from the separation chars, because it is
1760 needed as an escape for chars like '#' which are valid in an
1762 return ads_build_path(org_unit, "/", "ou=", 1);
1766 * Get a org unit string for a well-known GUID
1767 * @param ads connection to ads server
1768 * @param wknguid Well known GUID
1769 * @return org unit string - caller must free
1771 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1774 LDAPMessage *res = NULL;
1775 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1776 **bind_dn_exp = NULL;
1777 const char *attrs[] = {"distinguishedName", NULL};
1778 int new_ln, wkn_ln, bind_ln, i;
1780 if (wknguid == NULL) {
1784 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1785 DEBUG(1, ("asprintf failed!\n"));
1789 status = ads_search_dn(ads, &res, base, attrs);
1790 if (!ADS_ERR_OK(status)) {
1791 DEBUG(1,("Failed while searching for: %s\n", base));
1795 if (ads_count_replies(ads, res) != 1) {
1799 /* substitute the bind-path from the well-known-guid-search result */
1800 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1805 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1810 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1815 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1817 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1820 new_ln = wkn_ln - bind_ln;
1822 ret = SMB_STRDUP(wkn_dn_exp[0]);
1827 for (i=1; i < new_ln; i++) {
1830 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1836 ret = SMB_STRDUP(s);
1845 ads_msgfree(ads, res);
1846 TALLOC_FREE(wkn_dn);
1848 ldap_value_free(wkn_dn_exp);
1851 ldap_value_free(bind_dn_exp);
1858 * Adds (appends) an item to an attribute array, rather then
1859 * replacing the whole list
1860 * @param ctx An initialized TALLOC_CTX
1861 * @param mods An initialized ADS_MODLIST
1862 * @param name name of the ldap attribute to append to
1863 * @param vals an array of values to add
1864 * @return status of addition
1867 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1868 const char *name, const char **vals)
1870 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1871 (const void *) vals);
1875 * Determines the an account's current KVNO via an LDAP lookup
1876 * @param ads An initialized ADS_STRUCT
1877 * @param account_name the NT samaccountname.
1878 * @return the kvno for the account, or -1 in case of a failure.
1881 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1883 LDAPMessage *res = NULL;
1884 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1886 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1887 char *dn_string = NULL;
1890 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1891 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1894 ret = ads_search(ads, &res, filter, attrs);
1896 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1897 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1898 ads_msgfree(ads, res);
1902 dn_string = ads_get_dn(ads, talloc_tos(), res);
1904 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1905 ads_msgfree(ads, res);
1908 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1909 TALLOC_FREE(dn_string);
1911 /* ---------------------------------------------------------
1912 * 0 is returned as a default KVNO from this point on...
1913 * This is done because Windows 2000 does not support key
1914 * version numbers. Chances are that a failure in the next
1915 * step is simply due to Windows 2000 being used for a
1916 * domain controller. */
1919 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1920 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1921 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1922 ads_msgfree(ads, res);
1927 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1928 ads_msgfree(ads, res);
1933 * Determines the computer account's current KVNO via an LDAP lookup
1934 * @param ads An initialized ADS_STRUCT
1935 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1936 * @return the kvno for the computer account, or -1 in case of a failure.
1939 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1941 char *computer_account = NULL;
1944 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1948 kvno = ads_get_kvno(ads, computer_account);
1949 free(computer_account);
1955 * This clears out all registered spn's for a given hostname
1956 * @param ads An initilaized ADS_STRUCT
1957 * @param machine_name the NetBIOS name of the computer.
1958 * @return 0 upon success, non-zero otherwise.
1961 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1964 LDAPMessage *res = NULL;
1966 const char *servicePrincipalName[1] = {NULL};
1968 char *dn_string = NULL;
1970 ret = ads_find_machine_acct(ads, &res, machine_name);
1971 if (!ADS_ERR_OK(ret)) {
1972 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1973 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1974 ads_msgfree(ads, res);
1978 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1979 ctx = talloc_init("ads_clear_service_principal_names");
1981 ads_msgfree(ads, res);
1982 return ADS_ERROR(LDAP_NO_MEMORY);
1985 if (!(mods = ads_init_mods(ctx))) {
1986 talloc_destroy(ctx);
1987 ads_msgfree(ads, res);
1988 return ADS_ERROR(LDAP_NO_MEMORY);
1990 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1991 if (!ADS_ERR_OK(ret)) {
1992 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1993 ads_msgfree(ads, res);
1994 talloc_destroy(ctx);
1997 dn_string = ads_get_dn(ads, talloc_tos(), res);
1999 talloc_destroy(ctx);
2000 ads_msgfree(ads, res);
2001 return ADS_ERROR(LDAP_NO_MEMORY);
2003 ret = ads_gen_mod(ads, dn_string, mods);
2004 TALLOC_FREE(dn_string);
2005 if (!ADS_ERR_OK(ret)) {
2006 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2008 ads_msgfree(ads, res);
2009 talloc_destroy(ctx);
2013 ads_msgfree(ads, res);
2014 talloc_destroy(ctx);
2019 * @brief Search for an element in a string array.
2021 * @param[in] el_array The string array to search.
2023 * @param[in] num_el The number of elements in the string array.
2025 * @param[in] el The string to search.
2027 * @return True if found, false if not.
2029 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2033 if (el_array == NULL || num_el == 0 || el == NULL) {
2037 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2040 cmp = strcasecmp_m(el_array[i], el);
2050 * @brief This gets the service principal names of an existing computer account.
2052 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2054 * @param[in] ads The ADS context to use.
2056 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2057 * identify the computer account.
2059 * @param[in] spn_array A pointer to store the array for SPNs.
2061 * @param[in] num_spns The number of principals stored in the array.
2063 * @return 0 on success, or a ADS error if a failure occurred.
2065 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2067 const char *machine_name,
2072 LDAPMessage *res = NULL;
2075 status = ads_find_machine_acct(ads,
2078 if (!ADS_ERR_OK(status)) {
2079 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2084 count = ads_count_replies(ads, res);
2086 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2090 *spn_array = ads_pull_strings(ads,
2093 "servicePrincipalName",
2095 if (*spn_array == NULL) {
2096 DEBUG(1, ("Host account for %s does not have service principal "
2099 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2104 ads_msgfree(ads, res);
2110 * This adds a service principal name to an existing computer account
2111 * (found by hostname) in AD.
2112 * @param ads An initialized ADS_STRUCT
2113 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2114 * @param spns An array or strings for the service principals to add,
2115 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2116 * @return 0 upon sucess, or non-zero if a failure occurs
2119 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2120 const char *machine_name,
2125 LDAPMessage *res = NULL;
2127 char *dn_string = NULL;
2128 const char **servicePrincipalName = spns;
2130 ret = ads_find_machine_acct(ads, &res, machine_name);
2131 if (!ADS_ERR_OK(ret)) {
2132 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2134 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2135 ads_msgfree(ads, res);
2139 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2140 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2141 ads_msgfree(ads, res);
2142 return ADS_ERROR(LDAP_NO_MEMORY);
2145 DEBUG(5,("ads_add_service_principal_name: INFO: "
2146 "Adding %s to host %s\n",
2147 spns[0] ? "N/A" : spns[0], machine_name));
2150 DEBUG(5,("ads_add_service_principal_name: INFO: "
2151 "Adding %s to host %s\n",
2152 spns[1] ? "N/A" : spns[1], machine_name));
2154 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2155 ret = ADS_ERROR(LDAP_NO_MEMORY);
2159 ret = ads_add_strlist(ctx,
2161 "servicePrincipalName",
2162 servicePrincipalName);
2163 if (!ADS_ERR_OK(ret)) {
2164 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2168 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2169 ret = ADS_ERROR(LDAP_NO_MEMORY);
2173 ret = ads_gen_mod(ads, dn_string, mods);
2174 if (!ADS_ERR_OK(ret)) {
2175 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2181 ads_msgfree(ads, res);
2185 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2188 uint32_t acct_ctrl = 0;
2191 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2199 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2201 const struct berval *machine_pw_val)
2205 TALLOC_CTX *frame = talloc_stackframe();
2206 uint32_t acct_control;
2207 char *control_str = NULL;
2208 const char *attrs[] = {
2212 LDAPMessage *res = NULL;
2215 dn = ads_get_dn(ads, frame, msg);
2217 ret = ADS_ERROR(LDAP_NO_MEMORY);
2221 acct_control = ads_get_acct_ctrl(ads, msg);
2222 if (acct_control == 0) {
2223 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2228 * Changing the password, disables the account. So we need to change the
2229 * userAccountControl flags to enable it again.
2231 mods = ads_init_mods(frame);
2233 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2237 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2239 ret = ads_gen_mod(ads, dn, mods);
2240 if (!ADS_ERR_OK(ret)) {
2246 * To activate the account, we need to disable and enable it.
2248 acct_control |= UF_ACCOUNTDISABLE;
2250 control_str = talloc_asprintf(frame, "%u", acct_control);
2251 if (control_str == NULL) {
2252 ret = ADS_ERROR(LDAP_NO_MEMORY);
2256 mods = ads_init_mods(frame);
2258 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2262 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2264 ret = ads_gen_mod(ads, dn, mods);
2265 if (!ADS_ERR_OK(ret)) {
2269 TALLOC_FREE(control_str);
2272 * Enable the account again.
2274 acct_control &= ~UF_ACCOUNTDISABLE;
2276 control_str = talloc_asprintf(frame, "%u", acct_control);
2277 if (control_str == NULL) {
2278 ret = ADS_ERROR(LDAP_NO_MEMORY);
2282 mods = ads_init_mods(frame);
2284 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2288 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2290 ret = ads_gen_mod(ads, dn, mods);
2291 if (!ADS_ERR_OK(ret)) {
2295 TALLOC_FREE(control_str);
2297 ret = ads_search_dn(ads, &res, dn, attrs);
2298 ads_msgfree(ads, res);
2307 * adds a machine account to the ADS server
2308 * @param ads An intialized ADS_STRUCT
2309 * @param machine_name - the NetBIOS machine name of this account.
2310 * @param account_type A number indicating the type of account to create
2311 * @param org_unit The LDAP path in which to place this account
2312 * @return 0 upon success, or non-zero otherwise
2315 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2316 const char *machine_name,
2317 const char *machine_password,
2318 const char *org_unit,
2319 uint32_t etype_list,
2320 const char *dns_domain_name)
2323 char *samAccountName = NULL;
2324 char *controlstr = NULL;
2325 TALLOC_CTX *ctx = NULL;
2327 char *machine_escaped = NULL;
2328 char *dns_hostname = NULL;
2329 char *new_dn = NULL;
2330 char *utf8_pw = NULL;
2331 size_t utf8_pw_len = 0;
2332 char *utf16_pw = NULL;
2333 size_t utf16_pw_len = 0;
2334 struct berval machine_pw_val;
2336 const char **spn_array = NULL;
2337 size_t num_spns = 0;
2338 const char *spn_prefix[] = {
2340 "RestrictedKrbHost",
2343 LDAPMessage *res = NULL;
2344 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2346 ctx = talloc_init("ads_add_machine_acct");
2348 return ADS_ERROR(LDAP_NO_MEMORY);
2351 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2352 if (machine_escaped == NULL) {
2353 ret = ADS_ERROR(LDAP_NO_MEMORY);
2357 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2358 if (utf8_pw == NULL) {
2359 ret = ADS_ERROR(LDAP_NO_MEMORY);
2362 utf8_pw_len = strlen(utf8_pw);
2364 ok = convert_string_talloc(ctx,
2365 CH_UTF8, CH_UTF16MUNGED,
2366 utf8_pw, utf8_pw_len,
2367 (void *)&utf16_pw, &utf16_pw_len);
2369 ret = ADS_ERROR(LDAP_NO_MEMORY);
2373 machine_pw_val = (struct berval) {
2375 .bv_len = utf16_pw_len,
2378 /* Check if the machine account already exists. */
2379 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2380 if (ADS_ERR_OK(ret)) {
2381 /* Change the machine account password */
2382 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2383 ads_msgfree(ads, res);
2387 ads_msgfree(ads, res);
2389 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2390 if (new_dn == NULL) {
2391 ret = ADS_ERROR(LDAP_NO_MEMORY);
2395 /* Create machine account */
2397 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2398 if (samAccountName == NULL) {
2399 ret = ADS_ERROR(LDAP_NO_MEMORY);
2403 dns_hostname = talloc_asprintf(ctx,
2407 if (dns_hostname == NULL) {
2408 ret = ADS_ERROR(LDAP_NO_MEMORY);
2412 /* Add dns_hostname SPNs */
2413 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2414 char *spn = talloc_asprintf(ctx,
2419 ret = ADS_ERROR(LDAP_NO_MEMORY);
2423 ok = add_string_to_array(spn_array,
2428 ret = ADS_ERROR(LDAP_NO_MEMORY);
2433 /* Add machine_name SPNs */
2434 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2435 char *spn = talloc_asprintf(ctx,
2440 ret = ADS_ERROR(LDAP_NO_MEMORY);
2444 ok = add_string_to_array(spn_array,
2449 ret = ADS_ERROR(LDAP_NO_MEMORY);
2454 /* Make sure to NULL terminate the array */
2455 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2456 if (spn_array == NULL) {
2457 ret = ADS_ERROR(LDAP_NO_MEMORY);
2460 spn_array[num_spns] = NULL;
2462 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2463 if (controlstr == NULL) {
2464 ret = ADS_ERROR(LDAP_NO_MEMORY);
2468 mods = ads_init_mods(ctx);
2470 ret = ADS_ERROR(LDAP_NO_MEMORY);
2474 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2475 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2476 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2477 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2478 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2479 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2481 ret = ads_gen_add(ads, new_dn, mods);
2484 SAFE_FREE(machine_escaped);
2485 talloc_destroy(ctx);
2491 * move a machine account to another OU on the ADS server
2492 * @param ads - An intialized ADS_STRUCT
2493 * @param machine_name - the NetBIOS machine name of this account.
2494 * @param org_unit - The LDAP path in which to place this account
2495 * @param moved - whether we moved the machine account (optional)
2496 * @return 0 upon success, or non-zero otherwise
2499 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2500 const char *org_unit, bool *moved)
2504 LDAPMessage *res = NULL;
2505 char *filter = NULL;
2506 char *computer_dn = NULL;
2508 char *computer_rdn = NULL;
2509 bool need_move = False;
2511 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2512 rc = ADS_ERROR(LDAP_NO_MEMORY);
2516 /* Find pre-existing machine */
2517 rc = ads_search(ads, &res, filter, NULL);
2518 if (!ADS_ERR_OK(rc)) {
2522 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2524 rc = ADS_ERROR(LDAP_NO_MEMORY);
2528 parent_dn = ads_parent_dn(computer_dn);
2529 if (strequal(parent_dn, org_unit)) {
2535 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2536 rc = ADS_ERROR(LDAP_NO_MEMORY);
2540 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2541 org_unit, 1, NULL, NULL);
2542 rc = ADS_ERROR(ldap_status);
2545 ads_msgfree(ads, res);
2547 TALLOC_FREE(computer_dn);
2548 SAFE_FREE(computer_rdn);
2550 if (!ADS_ERR_OK(rc)) {
2562 dump a binary result from ldap
2564 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2567 for (i=0; values[i]; i++) {
2569 printf("%s: ", field);
2570 for (j=0; j<values[i]->bv_len; j++) {
2571 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2577 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2580 for (i=0; values[i]; i++) {
2582 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2585 status = GUID_from_ndr_blob(&in, &guid);
2586 if (NT_STATUS_IS_OK(status)) {
2587 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2589 printf("%s: INVALID GUID\n", field);
2595 dump a sid result from ldap
2597 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2600 for (i=0; values[i]; i++) {
2603 struct dom_sid_buf tmp;
2604 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2605 values[i]->bv_len, &sid);
2609 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2614 dump ntSecurityDescriptor
2616 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2618 TALLOC_CTX *frame = talloc_stackframe();
2619 struct security_descriptor *psd;
2622 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2623 values[0]->bv_len, &psd);
2624 if (!NT_STATUS_IS_OK(status)) {
2625 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2626 nt_errstr(status)));
2632 ads_disp_sd(ads, talloc_tos(), psd);
2639 dump a string result from ldap
2641 static void dump_string(const char *field, char **values)
2644 for (i=0; values[i]; i++) {
2645 printf("%s: %s\n", field, values[i]);
2650 dump a field from LDAP on stdout
2654 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2659 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2661 {"objectGUID", False, dump_guid},
2662 {"netbootGUID", False, dump_guid},
2663 {"nTSecurityDescriptor", False, dump_sd},
2664 {"dnsRecord", False, dump_binary},
2665 {"objectSid", False, dump_sid},
2666 {"tokenGroups", False, dump_sid},
2667 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2668 {"tokengroupsGlobalandUniversal", False, dump_sid},
2669 {"mS-DS-CreatorSID", False, dump_sid},
2670 {"msExchMailboxGuid", False, dump_guid},
2675 if (!field) { /* must be end of an entry */
2680 for (i=0; handlers[i].name; i++) {
2681 if (strcasecmp_m(handlers[i].name, field) == 0) {
2682 if (!values) /* first time, indicate string or not */
2683 return handlers[i].string;
2684 handlers[i].handler(ads, field, (struct berval **) values);
2688 if (!handlers[i].name) {
2689 if (!values) /* first time, indicate string conversion */
2691 dump_string(field, (char **)values);
2697 * Dump a result from LDAP on stdout
2698 * used for debugging
2699 * @param ads connection to ads server
2700 * @param res Results to dump
2703 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2705 ads_process_results(ads, res, ads_dump_field, NULL);
2709 * Walk through results, calling a function for each entry found.
2710 * The function receives a field name, a berval * array of values,
2711 * and a data area passed through from the start. The function is
2712 * called once with null for field and values at the end of each
2714 * @param ads connection to ads server
2715 * @param res Results to process
2716 * @param fn Function for processing each result
2717 * @param data_area user-defined area to pass to function
2719 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2720 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2725 size_t converted_size;
2727 if (!(ctx = talloc_init("ads_process_results")))
2730 for (msg = ads_first_entry(ads, res); msg;
2731 msg = ads_next_entry(ads, msg)) {
2735 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2736 (LDAPMessage *)msg,&b);
2738 utf8_field=ldap_next_attribute(ads->ldap.ld,
2739 (LDAPMessage *)msg,b)) {
2740 struct berval **ber_vals;
2746 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2749 DEBUG(0,("ads_process_results: "
2750 "pull_utf8_talloc failed: %s",
2754 string = fn(ads, field, NULL, data_area);
2759 utf8_vals = ldap_get_values(ads->ldap.ld,
2760 (LDAPMessage *)msg, field);
2761 p = discard_const_p(const char *, utf8_vals);
2762 str_vals = ads_pull_strvals(ctx, p);
2763 fn(ads, field, (void **) str_vals, data_area);
2764 ldap_value_free(utf8_vals);
2766 ber_vals = ldap_get_values_len(ads->ldap.ld,
2767 (LDAPMessage *)msg, field);
2768 fn(ads, field, (void **) ber_vals, data_area);
2770 ldap_value_free_len(ber_vals);
2772 ldap_memfree(utf8_field);
2775 talloc_free_children(ctx);
2776 fn(ads, NULL, NULL, data_area); /* completed an entry */
2779 talloc_destroy(ctx);
2783 * count how many replies are in a LDAPMessage
2784 * @param ads connection to ads server
2785 * @param res Results to count
2786 * @return number of replies
2788 int ads_count_replies(ADS_STRUCT *ads, void *res)
2790 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2794 * pull the first entry from a ADS result
2795 * @param ads connection to ads server
2796 * @param res Results of search
2797 * @return first entry from result
2799 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2801 return ldap_first_entry(ads->ldap.ld, res);
2805 * pull the next entry from a ADS result
2806 * @param ads connection to ads server
2807 * @param res Results of search
2808 * @return next entry from result
2810 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2812 return ldap_next_entry(ads->ldap.ld, res);
2816 * pull the first message from a ADS result
2817 * @param ads connection to ads server
2818 * @param res Results of search
2819 * @return first message from result
2821 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2823 return ldap_first_message(ads->ldap.ld, res);
2827 * pull the next message from a ADS result
2828 * @param ads connection to ads server
2829 * @param res Results of search
2830 * @return next message from result
2832 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2834 return ldap_next_message(ads->ldap.ld, res);
2838 * pull a single string from a ADS result
2839 * @param ads connection to ads server
2840 * @param mem_ctx TALLOC_CTX to use for allocating result string
2841 * @param msg Results of search
2842 * @param field Attribute to retrieve
2843 * @return Result string in talloc context
2845 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2851 size_t converted_size;
2853 values = ldap_get_values(ads->ldap.ld, msg, field);
2857 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2862 ldap_value_free(values);
2867 * pull an array of strings from a ADS result
2868 * @param ads connection to ads server
2869 * @param mem_ctx TALLOC_CTX to use for allocating result string
2870 * @param msg Results of search
2871 * @param field Attribute to retrieve
2872 * @return Result strings in talloc context
2874 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2875 LDAPMessage *msg, const char *field,
2880 size_t i, converted_size;
2882 values = ldap_get_values(ads->ldap.ld, msg, field);
2886 *num_values = ldap_count_values(values);
2888 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2890 ldap_value_free(values);
2894 for (i=0;i<*num_values;i++) {
2895 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2898 ldap_value_free(values);
2904 ldap_value_free(values);
2909 * pull an array of strings from a ADS result
2910 * (handle large multivalue attributes with range retrieval)
2911 * @param ads connection to ads server
2912 * @param mem_ctx TALLOC_CTX to use for allocating result string
2913 * @param msg Results of search
2914 * @param field Attribute to retrieve
2915 * @param current_strings strings returned by a previous call to this function
2916 * @param next_attribute The next query should ask for this attribute
2917 * @param num_values How many values did we get this time?
2918 * @param more_values Are there more values to get?
2919 * @return Result strings in talloc context
2921 char **ads_pull_strings_range(ADS_STRUCT *ads,
2922 TALLOC_CTX *mem_ctx,
2923 LDAPMessage *msg, const char *field,
2924 char **current_strings,
2925 const char **next_attribute,
2926 size_t *num_strings,
2930 char *expected_range_attrib, *range_attr;
2931 BerElement *ptr = NULL;
2934 size_t num_new_strings;
2935 unsigned long int range_start;
2936 unsigned long int range_end;
2938 /* we might have been given the whole lot anyway */
2939 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2940 *more_strings = False;
2944 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2946 /* look for Range result */
2947 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2949 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2950 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2951 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2959 /* nothing here - this field is just empty */
2960 *more_strings = False;
2964 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2965 &range_start, &range_end) == 2) {
2966 *more_strings = True;
2968 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2969 &range_start) == 1) {
2970 *more_strings = False;
2972 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2974 ldap_memfree(range_attr);
2975 *more_strings = False;
2980 if ((*num_strings) != range_start) {
2981 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2982 " - aborting range retreival\n",
2983 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2984 ldap_memfree(range_attr);
2985 *more_strings = False;
2989 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2991 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2992 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2993 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2994 range_attr, (unsigned long int)range_end - range_start + 1,
2995 (unsigned long int)num_new_strings));
2996 ldap_memfree(range_attr);
2997 *more_strings = False;
3001 strings = talloc_realloc(mem_ctx, current_strings, char *,
3002 *num_strings + num_new_strings);
3004 if (strings == NULL) {
3005 ldap_memfree(range_attr);
3006 *more_strings = False;
3010 if (new_strings && num_new_strings) {
3011 memcpy(&strings[*num_strings], new_strings,
3012 sizeof(*new_strings) * num_new_strings);
3015 (*num_strings) += num_new_strings;
3017 if (*more_strings) {
3018 *next_attribute = talloc_asprintf(mem_ctx,
3023 if (!*next_attribute) {
3024 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3025 ldap_memfree(range_attr);
3026 *more_strings = False;
3031 ldap_memfree(range_attr);
3037 * pull a single uint32_t from a ADS result
3038 * @param ads connection to ads server
3039 * @param msg Results of search
3040 * @param field Attribute to retrieve
3041 * @param v Pointer to int to store result
3042 * @return boolean inidicating success
3044 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3049 values = ldap_get_values(ads->ldap.ld, msg, field);
3053 ldap_value_free(values);
3057 *v = atoi(values[0]);
3058 ldap_value_free(values);
3063 * pull a single objectGUID from an ADS result
3064 * @param ads connection to ADS server
3065 * @param msg results of search
3066 * @param guid 37-byte area to receive text guid
3067 * @return boolean indicating success
3069 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3074 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3079 status = GUID_from_ndr_blob(&blob, guid);
3080 talloc_free(blob.data);
3081 return NT_STATUS_IS_OK(status);
3086 * pull a single struct dom_sid from a ADS result
3087 * @param ads connection to ads server
3088 * @param msg Results of search
3089 * @param field Attribute to retrieve
3090 * @param sid Pointer to sid to store result
3091 * @return boolean inidicating success
3093 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3094 struct dom_sid *sid)
3096 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3100 * pull an array of struct dom_sids from a ADS result
3101 * @param ads connection to ads server
3102 * @param mem_ctx TALLOC_CTX for allocating sid array
3103 * @param msg Results of search
3104 * @param field Attribute to retrieve
3105 * @param sids pointer to sid array to allocate
3106 * @return the count of SIDs pulled
3108 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3109 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3111 struct berval **values;
3114 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3119 for (i=0; values[i]; i++)
3123 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3125 ldap_value_free_len(values);
3133 for (i=0; values[i]; i++) {
3135 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3136 values[i]->bv_len, &(*sids)[count]);
3138 struct dom_sid_buf buf;
3139 DBG_DEBUG("pulling SID: %s\n",
3140 dom_sid_str_buf(&(*sids)[count], &buf));
3145 ldap_value_free_len(values);
3150 * pull a struct security_descriptor from a ADS result
3151 * @param ads connection to ads server
3152 * @param mem_ctx TALLOC_CTX for allocating sid array
3153 * @param msg Results of search
3154 * @param field Attribute to retrieve
3155 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3156 * @return boolean inidicating success
3158 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3159 LDAPMessage *msg, const char *field,
3160 struct security_descriptor **sd)
3162 struct berval **values;
3165 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3167 if (!values) return false;
3171 status = unmarshall_sec_desc(mem_ctx,
3172 (uint8_t *)values[0]->bv_val,
3173 values[0]->bv_len, sd);
3174 if (!NT_STATUS_IS_OK(status)) {
3175 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3176 nt_errstr(status)));
3181 ldap_value_free_len(values);
3186 * in order to support usernames longer than 21 characters we need to
3187 * use both the sAMAccountName and the userPrincipalName attributes
3188 * It seems that not all users have the userPrincipalName attribute set
3190 * @param ads connection to ads server
3191 * @param mem_ctx TALLOC_CTX for allocating sid array
3192 * @param msg Results of search
3193 * @return the username
3195 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3201 /* lookup_name() only works on the sAMAccountName to
3202 returning the username portion of userPrincipalName
3203 breaks winbindd_getpwnam() */
3205 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3206 if (ret && (p = strchr_m(ret, '@'))) {
3211 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3216 * find the update serial number - this is the core of the ldap cache
3217 * @param ads connection to ads server
3218 * @param ads connection to ADS server
3219 * @param usn Pointer to retrieved update serial number
3220 * @return status of search
3222 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3224 const char *attrs[] = {"highestCommittedUSN", NULL};
3228 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3229 if (!ADS_ERR_OK(status))
3232 if (ads_count_replies(ads, res) != 1) {
3233 ads_msgfree(ads, res);
3234 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3237 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3238 ads_msgfree(ads, res);
3239 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3242 ads_msgfree(ads, res);
3246 /* parse a ADS timestring - typical string is
3247 '20020917091222.0Z0' which means 09:12.22 17th September
3249 static time_t ads_parse_time(const char *str)
3255 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3256 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3257 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3266 /********************************************************************
3267 ********************************************************************/
3269 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3271 const char *attrs[] = {"currentTime", NULL};
3276 ADS_STRUCT *ads_s = ads;
3278 if (!(ctx = talloc_init("ads_current_time"))) {
3279 return ADS_ERROR(LDAP_NO_MEMORY);
3282 /* establish a new ldap tcp session if necessary */
3284 if ( !ads->ldap.ld ) {
3286 * ADS_STRUCT may be being reused after a
3287 * DC lookup, so ads->ldap.ss may already have a
3288 * good address. If not, re-initialize the passed-in
3289 * ADS_STRUCT with the given server.XXXX parameters.
3291 * Note that this doesn't depend on
3292 * ads->server.ldap_server != NULL,
3293 * as the case where ads->server.ldap_server==NULL and
3294 * ads->ldap.ss != zero_address is precisely the DC
3295 * lookup case where ads->ldap.ss was found by going
3296 * through ads_find_dc() again we want to avoid repeating.
3298 if (is_zero_addr(&ads->ldap.ss)) {
3299 ads_s = ads_init(ads->server.realm,
3300 ads->server.workgroup,
3301 ads->server.ldap_server,
3303 if (ads_s == NULL) {
3304 status = ADS_ERROR(LDAP_NO_MEMORY);
3308 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3309 status = ads_connect( ads_s );
3310 if ( !ADS_ERR_OK(status))
3314 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3315 if (!ADS_ERR_OK(status)) {
3319 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
3321 ads_msgfree(ads_s, res);
3322 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3326 /* but save the time and offset in the original ADS_STRUCT */
3328 ads->config.current_time = ads_parse_time(timestr);
3330 if (ads->config.current_time != 0) {
3331 ads->auth.time_offset = ads->config.current_time - time(NULL);
3332 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3335 ads_msgfree(ads, res);
3337 status = ADS_SUCCESS;
3340 /* free any temporary ads connections */
3341 if ( ads_s != ads ) {
3342 ads_destroy( &ads_s );
3344 talloc_destroy(ctx);
3349 /********************************************************************
3350 ********************************************************************/
3352 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3354 const char *attrs[] = {"domainFunctionality", NULL};
3357 ADS_STRUCT *ads_s = ads;
3359 *val = DS_DOMAIN_FUNCTION_2000;
3361 /* establish a new ldap tcp session if necessary */
3363 if ( !ads->ldap.ld ) {
3365 * ADS_STRUCT may be being reused after a
3366 * DC lookup, so ads->ldap.ss may already have a
3367 * good address. If not, re-initialize the passed-in
3368 * ADS_STRUCT with the given server.XXXX parameters.
3370 * Note that this doesn't depend on
3371 * ads->server.ldap_server != NULL,
3372 * as the case where ads->server.ldap_server==NULL and
3373 * ads->ldap.ss != zero_address is precisely the DC
3374 * lookup case where ads->ldap.ss was found by going
3375 * through ads_find_dc() again we want to avoid repeating.
3377 if (is_zero_addr(&ads->ldap.ss)) {
3378 ads_s = ads_init(ads->server.realm,
3379 ads->server.workgroup,
3380 ads->server.ldap_server,
3382 if (ads_s == NULL ) {
3383 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3387 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3388 status = ads_connect( ads_s );
3389 if ( !ADS_ERR_OK(status))
3393 /* If the attribute does not exist assume it is a Windows 2000
3394 functional domain */
3396 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3397 if (!ADS_ERR_OK(status)) {
3398 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3399 status = ADS_SUCCESS;
3404 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3405 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3407 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3410 ads_msgfree(ads, res);
3413 /* free any temporary ads connections */
3414 if ( ads_s != ads ) {
3415 ads_destroy( &ads_s );
3422 * find the domain sid for our domain
3423 * @param ads connection to ads server
3424 * @param sid Pointer to domain sid
3425 * @return status of search
3427 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3429 const char *attrs[] = {"objectSid", NULL};
3433 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3435 if (!ADS_ERR_OK(rc)) return rc;
3436 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3437 ads_msgfree(ads, res);
3438 return ADS_ERROR_SYSTEM(ENOENT);
3440 ads_msgfree(ads, res);
3446 * find our site name
3447 * @param ads connection to ads server
3448 * @param mem_ctx Pointer to talloc context
3449 * @param site_name Pointer to the sitename
3450 * @return status of search
3452 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3456 const char *dn, *service_name;
3457 const char *attrs[] = { "dsServiceName", NULL };
3459 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3460 if (!ADS_ERR_OK(status)) {
3464 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3465 if (service_name == NULL) {
3466 ads_msgfree(ads, res);
3467 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3470 ads_msgfree(ads, res);
3472 /* go up three levels */
3473 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3475 return ADS_ERROR(LDAP_NO_MEMORY);
3478 *site_name = talloc_strdup(mem_ctx, dn);
3479 if (*site_name == NULL) {
3480 return ADS_ERROR(LDAP_NO_MEMORY);
3485 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3490 * find the site dn where a machine resides
3491 * @param ads connection to ads server
3492 * @param mem_ctx Pointer to talloc context
3493 * @param computer_name name of the machine
3494 * @param site_name Pointer to the sitename
3495 * @return status of search
3497 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3501 const char *parent, *filter;
3502 char *config_context = NULL;
3505 /* shortcut a query */
3506 if (strequal(computer_name, ads->config.ldap_server_name)) {
3507 return ads_site_dn(ads, mem_ctx, site_dn);
3510 status = ads_config_path(ads, mem_ctx, &config_context);
3511 if (!ADS_ERR_OK(status)) {
3515 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3516 if (filter == NULL) {
3517 return ADS_ERROR(LDAP_NO_MEMORY);
3520 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3521 filter, NULL, &res);
3522 if (!ADS_ERR_OK(status)) {
3526 if (ads_count_replies(ads, res) != 1) {
3527 ads_msgfree(ads, res);
3528 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3531 dn = ads_get_dn(ads, mem_ctx, res);
3533 ads_msgfree(ads, res);
3534 return ADS_ERROR(LDAP_NO_MEMORY);
3537 /* go up three levels */
3538 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3539 if (parent == NULL) {
3540 ads_msgfree(ads, res);
3542 return ADS_ERROR(LDAP_NO_MEMORY);
3545 *site_dn = talloc_strdup(mem_ctx, parent);
3546 if (*site_dn == NULL) {
3547 ads_msgfree(ads, res);
3549 return ADS_ERROR(LDAP_NO_MEMORY);
3553 ads_msgfree(ads, res);
3559 * get the upn suffixes for a domain
3560 * @param ads connection to ads server
3561 * @param mem_ctx Pointer to talloc context
3562 * @param suffixes Pointer to an array of suffixes
3563 * @param num_suffixes Pointer to the number of suffixes
3564 * @return status of search
3566 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3571 char *config_context = NULL;
3572 const char *attrs[] = { "uPNSuffixes", NULL };
3574 status = ads_config_path(ads, mem_ctx, &config_context);
3575 if (!ADS_ERR_OK(status)) {
3579 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3581 return ADS_ERROR(LDAP_NO_MEMORY);
3584 status = ads_search_dn(ads, &res, base, attrs);
3585 if (!ADS_ERR_OK(status)) {
3589 if (ads_count_replies(ads, res) != 1) {
3590 ads_msgfree(ads, res);
3591 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3594 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3595 if ((*suffixes) == NULL) {
3596 ads_msgfree(ads, res);
3597 return ADS_ERROR(LDAP_NO_MEMORY);
3600 ads_msgfree(ads, res);
3606 * get the joinable ous for a domain
3607 * @param ads connection to ads server
3608 * @param mem_ctx Pointer to talloc context
3609 * @param ous Pointer to an array of ous
3610 * @param num_ous Pointer to the number of ous
3611 * @return status of search
3613 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3614 TALLOC_CTX *mem_ctx,
3619 LDAPMessage *res = NULL;
3620 LDAPMessage *msg = NULL;
3621 const char *attrs[] = { "dn", NULL };
3624 status = ads_search(ads, &res,
3625 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3627 if (!ADS_ERR_OK(status)) {
3631 count = ads_count_replies(ads, res);
3633 ads_msgfree(ads, res);
3634 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3637 for (msg = ads_first_entry(ads, res); msg;
3638 msg = ads_next_entry(ads, msg)) {
3639 const char **p = discard_const_p(const char *, *ous);
3642 dn = ads_get_dn(ads, talloc_tos(), msg);
3644 ads_msgfree(ads, res);
3645 return ADS_ERROR(LDAP_NO_MEMORY);
3648 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3650 ads_msgfree(ads, res);
3651 return ADS_ERROR(LDAP_NO_MEMORY);
3655 *ous = discard_const_p(char *, p);
3658 ads_msgfree(ads, res);
3665 * pull a struct dom_sid from an extended dn string
3666 * @param mem_ctx TALLOC_CTX
3667 * @param extended_dn string
3668 * @param flags string type of extended_dn
3669 * @param sid pointer to a struct dom_sid
3670 * @return NT_STATUS_OK on success,
3671 * NT_INVALID_PARAMETER on error,
3672 * NT_STATUS_NOT_FOUND if no SID present
3674 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3675 const char *extended_dn,
3676 enum ads_extended_dn_flags flags,
3677 struct dom_sid *sid)
3682 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3685 /* otherwise extended_dn gets stripped off */
3686 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3687 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3690 * ADS_EXTENDED_DN_HEX_STRING:
3691 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3693 * ADS_EXTENDED_DN_STRING (only with w2k3):
3694 * <GUID=63198e23-39cb-4b0f-b032-ba0105525a29>;<SID=S-1-5-21-4257769659-666132843-1169174103-1110>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3696 * Object with no SID, such as an Exchange Public Folder
3697 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3700 p = strchr(dn, ';');
3702 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3705 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3706 DEBUG(5,("No SID present in extended dn\n"));
3707 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3710 p += strlen(";<SID=");
3714 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3719 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3723 case ADS_EXTENDED_DN_STRING:
3724 if (!string_to_sid(sid, p)) {
3725 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3728 case ADS_EXTENDED_DN_HEX_STRING: {
3733 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3735 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3738 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3740 DEBUG(10,("failed to parse sid\n"));
3741 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3746 DEBUG(10,("unknown extended dn format\n"));
3747 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3750 return ADS_ERROR_NT(NT_STATUS_OK);
3753 /********************************************************************
3754 ********************************************************************/
3756 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3758 LDAPMessage *res = NULL;
3763 status = ads_find_machine_acct(ads, &res, machine_name);
3764 if (!ADS_ERR_OK(status)) {
3765 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3766 lp_netbios_name()));
3770 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3771 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3775 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3776 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3780 ads_msgfree(ads, res);
3785 /********************************************************************
3786 ********************************************************************/
3788 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3789 LDAPMessage *msg, size_t *num_values)
3791 const char *field = "msDS-AdditionalDnsHostName";
3792 struct berval **values = NULL;
3794 size_t i, converted_size;
3797 * Windows DC implicitly adds a short name for each FQDN added to
3798 * msDS-AdditionalDnsHostName, but it comes with a strage binary
3799 * suffix "\0$" which we should ignore (see bug #14406).
3802 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3803 if (values == NULL) {
3807 *num_values = ldap_count_values_len(values);
3809 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3811 ldap_value_free_len(values);
3815 for (i = 0; i < *num_values; i++) {
3817 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
3819 strnlen(values[i]->bv_val,
3821 &ret[i], &converted_size)) {
3822 ldap_value_free_len(values);
3828 ldap_value_free_len(values);
3832 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
3834 const char *machine_name,
3835 char ***hostnames_array,
3836 size_t *num_hostnames)
3839 LDAPMessage *res = NULL;
3842 status = ads_find_machine_acct(ads,
3845 if (!ADS_ERR_OK(status)) {
3846 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
3851 count = ads_count_replies(ads, res);
3853 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3857 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
3858 if (*hostnames_array == NULL) {
3859 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
3861 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3866 ads_msgfree(ads, res);
3871 /********************************************************************
3872 ********************************************************************/
3874 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3876 LDAPMessage *res = NULL;
3881 status = ads_find_machine_acct(ads, &res, machine_name);
3882 if (!ADS_ERR_OK(status)) {
3883 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3884 lp_netbios_name()));
3888 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3889 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3893 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3894 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3898 ads_msgfree(ads, res);
3903 /********************************************************************
3904 ********************************************************************/
3906 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3908 LDAPMessage *res = NULL;
3914 status = ads_find_machine_acct(ads, &res, machine_name);
3915 if (!ADS_ERR_OK(status)) {
3916 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3917 lp_netbios_name()));
3921 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3922 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3926 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3927 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3931 ads_msgfree(ads, res);
3933 ok = (strlen(name) > 0);
3941 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3944 * Join a machine to a realm
3945 * Creates the machine account and sets the machine password
3946 * @param ads connection to ads server
3947 * @param machine name of host to add
3948 * @param org_unit Organizational unit to place machine in
3949 * @return status of join
3951 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3952 uint32_t account_type, const char *org_unit)
3955 LDAPMessage *res = NULL;
3958 /* machine name must be lowercase */
3959 machine = SMB_STRDUP(machine_name);
3960 strlower_m(machine);
3963 status = ads_find_machine_acct(ads, (void **)&res, machine);
3964 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3965 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3966 status = ads_leave_realm(ads, machine);
3967 if (!ADS_ERR_OK(status)) {
3968 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3969 machine, ads->config.realm));
3974 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3975 if (!ADS_ERR_OK(status)) {
3976 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3981 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3982 if (!ADS_ERR_OK(status)) {
3983 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3989 ads_msgfree(ads, res);
3996 * Delete a machine from the realm
3997 * @param ads connection to ads server
3998 * @param hostname Machine to remove
3999 * @return status of delete
4001 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4006 char *hostnameDN, *host;
4008 LDAPControl ldap_control;
4009 LDAPControl * pldap_control[2] = {NULL, NULL};
4011 pldap_control[0] = &ldap_control;
4012 memset(&ldap_control, 0, sizeof(LDAPControl));
4013 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4015 /* hostname must be lowercase */
4016 host = SMB_STRDUP(hostname);
4017 if (!strlower_m(host)) {
4019 return ADS_ERROR_SYSTEM(EINVAL);
4022 status = ads_find_machine_acct(ads, &res, host);
4023 if (!ADS_ERR_OK(status)) {
4024 DEBUG(0, ("Host account for %s does not exist.\n", host));
4029 msg = ads_first_entry(ads, res);
4032 return ADS_ERROR_SYSTEM(ENOENT);
4035 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4036 if (hostnameDN == NULL) {
4038 return ADS_ERROR_SYSTEM(ENOENT);
4041 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4043 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4045 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4048 if (rc != LDAP_SUCCESS) {
4049 const char *attrs[] = { "cn", NULL };
4050 LDAPMessage *msg_sub;
4052 /* we only search with scope ONE, we do not expect any further
4053 * objects to be created deeper */
4055 status = ads_do_search_retry(ads, hostnameDN,
4056 LDAP_SCOPE_ONELEVEL,
4057 "(objectclass=*)", attrs, &res);
4059 if (!ADS_ERR_OK(status)) {
4061 TALLOC_FREE(hostnameDN);
4065 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4066 msg_sub = ads_next_entry(ads, msg_sub)) {
4070 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4072 TALLOC_FREE(hostnameDN);
4073 return ADS_ERROR(LDAP_NO_MEMORY);
4076 status = ads_del_dn(ads, dn);
4077 if (!ADS_ERR_OK(status)) {
4078 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4081 TALLOC_FREE(hostnameDN);
4088 /* there should be no subordinate objects anymore */
4089 status = ads_do_search_retry(ads, hostnameDN,
4090 LDAP_SCOPE_ONELEVEL,
4091 "(objectclass=*)", attrs, &res);
4093 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4095 TALLOC_FREE(hostnameDN);
4099 /* delete hostnameDN now */
4100 status = ads_del_dn(ads, hostnameDN);
4101 if (!ADS_ERR_OK(status)) {
4103 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4104 TALLOC_FREE(hostnameDN);
4109 TALLOC_FREE(hostnameDN);
4111 status = ads_find_machine_acct(ads, &res, host);
4112 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4113 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4114 DEBUG(3, ("Failed to remove host account.\n"));
4124 * pull all token-sids from an LDAP dn
4125 * @param ads connection to ads server
4126 * @param mem_ctx TALLOC_CTX for allocating sid array
4127 * @param dn of LDAP object
4128 * @param user_sid pointer to struct dom_sid (objectSid)
4129 * @param primary_group_sid pointer to struct dom_sid (self composed)
4130 * @param sids pointer to sid array to allocate
4131 * @param num_sids counter of SIDs pulled
4132 * @return status of token query
4134 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4135 TALLOC_CTX *mem_ctx,
4137 struct dom_sid *user_sid,
4138 struct dom_sid *primary_group_sid,
4139 struct dom_sid **sids,
4143 LDAPMessage *res = NULL;
4145 size_t tmp_num_sids;
4146 struct dom_sid *tmp_sids;
4147 struct dom_sid tmp_user_sid;
4148 struct dom_sid tmp_primary_group_sid;
4150 const char *attrs[] = {
4157 status = ads_search_retry_dn(ads, &res, dn, attrs);
4158 if (!ADS_ERR_OK(status)) {
4162 count = ads_count_replies(ads, res);
4164 ads_msgfree(ads, res);
4165 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4168 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4169 ads_msgfree(ads, res);
4170 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4173 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4174 ads_msgfree(ads, res);
4175 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4179 /* hack to compose the primary group sid without knowing the
4182 struct dom_sid domsid;
4184 sid_copy(&domsid, &tmp_user_sid);
4186 if (!sid_split_rid(&domsid, NULL)) {
4187 ads_msgfree(ads, res);
4188 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4191 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4192 ads_msgfree(ads, res);
4193 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4197 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4199 if (tmp_num_sids == 0 || !tmp_sids) {
4200 ads_msgfree(ads, res);
4201 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4205 *num_sids = tmp_num_sids;
4213 *user_sid = tmp_user_sid;
4216 if (primary_group_sid) {
4217 *primary_group_sid = tmp_primary_group_sid;
4220 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4222 ads_msgfree(ads, res);
4223 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4227 * Find a sAMAccoutName in LDAP
4228 * @param ads connection to ads server
4229 * @param mem_ctx TALLOC_CTX for allocating sid array
4230 * @param samaccountname to search
4231 * @param uac_ret uint32_t pointer userAccountControl attribute value
4232 * @param dn_ret pointer to dn
4233 * @return status of token query
4235 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4236 TALLOC_CTX *mem_ctx,
4237 const char *samaccountname,
4239 const char **dn_ret)
4242 const char *attrs[] = { "userAccountControl", NULL };
4244 LDAPMessage *res = NULL;
4248 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4250 if (filter == NULL) {
4251 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4255 status = ads_do_search_all(ads, ads->config.bind_path,
4257 filter, attrs, &res);
4259 if (!ADS_ERR_OK(status)) {
4263 if (ads_count_replies(ads, res) != 1) {
4264 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4268 dn = ads_get_dn(ads, talloc_tos(), res);
4270 status = ADS_ERROR(LDAP_NO_MEMORY);
4274 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4275 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4284 *dn_ret = talloc_strdup(mem_ctx, dn);
4286 status = ADS_ERROR(LDAP_NO_MEMORY);
4292 ads_msgfree(ads, res);
4298 * find our configuration path
4299 * @param ads connection to ads server
4300 * @param mem_ctx Pointer to talloc context
4301 * @param config_path Pointer to the config path
4302 * @return status of search
4304 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4305 TALLOC_CTX *mem_ctx,
4309 LDAPMessage *res = NULL;
4310 const char *config_context = NULL;
4311 const char *attrs[] = { "configurationNamingContext", NULL };
4313 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4314 "(objectclass=*)", attrs, &res);
4315 if (!ADS_ERR_OK(status)) {
4319 config_context = ads_pull_string(ads, mem_ctx, res,
4320 "configurationNamingContext");
4321 ads_msgfree(ads, res);
4322 if (!config_context) {
4323 return ADS_ERROR(LDAP_NO_MEMORY);
4327 *config_path = talloc_strdup(mem_ctx, config_context);
4328 if (!*config_path) {
4329 return ADS_ERROR(LDAP_NO_MEMORY);
4333 return ADS_ERROR(LDAP_SUCCESS);
4337 * find the displayName of an extended right
4338 * @param ads connection to ads server
4339 * @param config_path The config path
4340 * @param mem_ctx Pointer to talloc context
4341 * @param GUID struct of the rightsGUID
4342 * @return status of search
4344 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4345 const char *config_path,
4346 TALLOC_CTX *mem_ctx,
4347 const struct GUID *rights_guid)
4350 LDAPMessage *res = NULL;
4352 const char *attrs[] = { "displayName", NULL };
4353 const char *result = NULL;
4356 if (!ads || !mem_ctx || !rights_guid) {
4360 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4361 GUID_string(mem_ctx, rights_guid));
4366 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4371 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4373 if (!ADS_ERR_OK(rc)) {
4377 if (ads_count_replies(ads, res) != 1) {
4381 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4384 ads_msgfree(ads, res);
4389 * verify or build and verify an account ou
4390 * @param mem_ctx Pointer to talloc context
4391 * @param ads connection to ads server
4393 * @return status of search
4396 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4398 const char **account_ou)
4404 if (account_ou == NULL) {
4405 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4408 if (*account_ou != NULL) {
4409 exploded_dn = ldap_explode_dn(*account_ou, 0);
4411 ldap_value_free(exploded_dn);
4416 ou_string = ads_ou_string(ads, *account_ou);
4418 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4421 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4422 ads->config.bind_path);
4423 SAFE_FREE(ou_string);
4426 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4429 exploded_dn = ldap_explode_dn(name, 0);
4431 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4433 ldap_value_free(exploded_dn);