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 "lib/param/loadparm.h"
38 * @brief basic ldap client-side routines for ads server communications
40 * The routines contained here should do the necessary ldap calls for
43 * Important note: attribute names passed into ads_ routines must
44 * already be in UTF-8 format. We do not convert them because in almost
45 * all cases, they are just ascii (which is represented with the same
46 * codepoints in UTF-8). This may have to change at some point
50 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
52 static SIG_ATOMIC_T gotalarm
;
54 /***************************************************************
55 Signal function to tell us we timed out.
56 ****************************************************************/
58 static void gotalarm_sig(int signum
)
63 LDAP
*ldap_open_with_timeout(const char *server
,
64 struct sockaddr_storage
*ss
,
65 int port
, unsigned int to
)
71 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
72 "%u seconds\n", server
, port
, to
));
77 CatchSignal(SIGALRM
, gotalarm_sig
);
79 /* End setup timeout. */
82 if ( strchr_m(server
, ':') ) {
84 uri
= talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server
, port
);
87 uri
= talloc_asprintf(talloc_tos(), "ldap://%s:%u", server
, port
);
93 #ifdef HAVE_LDAP_INITIALIZE
94 ldap_err
= ldap_initialize(&ldp
, uri
);
96 ldp
= ldap_open(server
, port
);
98 ldap_err
= LDAP_SUCCESS
;
100 ldap_err
= LDAP_OTHER
;
103 if (ldap_err
!= LDAP_SUCCESS
) {
104 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
105 uri
, ldap_err2string(ldap_err
)));
107 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri
));
111 /* Teardown timeout. */
113 CatchSignal(SIGALRM
, SIG_IGN
);
119 static int ldap_search_with_timeout(LDAP
*ld
,
120 LDAP_CONST
char *base
,
122 LDAP_CONST
char *filter
,
125 LDAPControl
**sctrls
,
126 LDAPControl
**cctrls
,
130 int to
= lp_ldap_timeout();
131 struct timeval timeout
;
132 struct timeval
*timeout_ptr
= NULL
;
135 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
141 timeout_ptr
= &timeout
;
143 /* Setup alarm timeout. */
144 CatchSignal(SIGALRM
, gotalarm_sig
);
145 /* Make the alarm time one second beyond
146 the timout we're setting for the
147 remote search timeout, to allow that
148 to fire in preference. */
150 /* End setup timeout. */
154 result
= ldap_search_ext_s(ld
, base
, scope
, filter
, attrs
,
155 attrsonly
, sctrls
, cctrls
, timeout_ptr
,
159 /* Teardown alarm timeout. */
160 CatchSignal(SIGALRM
, SIG_IGN
);
165 return LDAP_TIMELIMIT_EXCEEDED
;
168 * A bug in OpenLDAP means ldap_search_ext_s can return
169 * LDAP_SUCCESS but with a NULL res pointer. Cope with
170 * this. See bug #6279 for details. JRA.
174 return LDAP_TIMELIMIT_EXCEEDED
;
180 /**********************************************
181 Do client and server sitename match ?
182 **********************************************/
184 bool ads_sitename_match(ADS_STRUCT
*ads
)
186 if (ads
->config
.server_site_name
== NULL
&&
187 ads
->config
.client_site_name
== NULL
) {
188 DEBUG(10,("ads_sitename_match: both null\n"));
191 if (ads
->config
.server_site_name
&&
192 ads
->config
.client_site_name
&&
193 strequal(ads
->config
.server_site_name
,
194 ads
->config
.client_site_name
)) {
195 DEBUG(10,("ads_sitename_match: name %s match\n", ads
->config
.server_site_name
));
198 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
199 ads
->config
.server_site_name
? ads
->config
.server_site_name
: "NULL",
200 ads
->config
.client_site_name
? ads
->config
.client_site_name
: "NULL"));
204 /**********************************************
205 Is this the closest DC ?
206 **********************************************/
208 bool ads_closest_dc(ADS_STRUCT
*ads
)
210 if (ads
->config
.flags
& NBT_SERVER_CLOSEST
) {
211 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
215 /* not sure if this can ever happen */
216 if (ads_sitename_match(ads
)) {
217 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
221 if (ads
->config
.client_site_name
== NULL
) {
222 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
226 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
227 ads
->config
.ldap_server_name
));
234 try a connection to a given ldap server, returning True and setting the servers IP
235 in the ads struct if successful
237 static bool ads_try_connect(ADS_STRUCT
*ads
, bool gc
,
238 struct sockaddr_storage
*ss
)
240 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply
;
241 TALLOC_CTX
*frame
= talloc_stackframe();
243 char addr
[INET6_ADDRSTRLEN
];
250 print_sockaddr(addr
, sizeof(addr
), ss
);
252 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
253 addr
, ads
->server
.realm
));
255 ZERO_STRUCT( cldap_reply
);
257 if ( !ads_cldap_netlogon_5(frame
, ss
, ads
->server
.realm
, &cldap_reply
) ) {
258 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr
));
263 /* Check the CLDAP reply flags */
265 if ( !(cldap_reply
.server_type
& NBT_SERVER_LDAP
) ) {
266 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
272 /* Fill in the ads->config values */
274 SAFE_FREE(ads
->config
.realm
);
275 SAFE_FREE(ads
->config
.bind_path
);
276 SAFE_FREE(ads
->config
.ldap_server_name
);
277 SAFE_FREE(ads
->config
.server_site_name
);
278 SAFE_FREE(ads
->config
.client_site_name
);
279 SAFE_FREE(ads
->server
.workgroup
);
281 ads
->config
.flags
= cldap_reply
.server_type
;
282 ads
->config
.ldap_server_name
= SMB_STRDUP(cldap_reply
.pdc_dns_name
);
283 ads
->config
.realm
= SMB_STRDUP(cldap_reply
.dns_domain
);
284 if (!strupper_m(ads
->config
.realm
)) {
289 ads
->config
.bind_path
= ads_build_dn(ads
->config
.realm
);
290 if (*cldap_reply
.server_site
) {
291 ads
->config
.server_site_name
=
292 SMB_STRDUP(cldap_reply
.server_site
);
294 if (*cldap_reply
.client_site
) {
295 ads
->config
.client_site_name
=
296 SMB_STRDUP(cldap_reply
.client_site
);
298 ads
->server
.workgroup
= SMB_STRDUP(cldap_reply
.domain_name
);
300 ads
->ldap
.port
= gc
? LDAP_GC_PORT
: LDAP_PORT
;
303 /* Store our site name. */
304 sitename_store( cldap_reply
.domain_name
, cldap_reply
.client_site
);
305 sitename_store( cldap_reply
.dns_domain
, cldap_reply
.client_site
);
315 /**********************************************************************
316 send a cldap ping to list of servers, one at a time, until one of
317 them answers it's an ldap server. Record success in the ADS_STRUCT.
318 Take note of and update negative connection cache.
319 **********************************************************************/
321 static NTSTATUS
cldap_ping_list(ADS_STRUCT
*ads
,const char *domain
,
322 struct ip_service
*ip_list
, int count
)
327 for (i
= 0; i
< count
; i
++) {
328 char server
[INET6_ADDRSTRLEN
];
330 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
332 if (!NT_STATUS_IS_OK(
333 check_negative_conn_cache(domain
, server
)))
336 ok
= ads_try_connect(ads
, false, &ip_list
[i
].ss
);
341 /* keep track of failures */
342 add_failed_connection_entry(domain
, server
,
343 NT_STATUS_UNSUCCESSFUL
);
346 return NT_STATUS_NO_LOGON_SERVERS
;
349 /***************************************************************************
350 resolve a name and perform an "ldap ping" using NetBIOS and related methods
351 ****************************************************************************/
353 static NTSTATUS
resolve_and_ping_netbios(ADS_STRUCT
*ads
,
354 const char *domain
, const char *realm
)
357 struct ip_service
*ip_list
;
358 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
360 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
363 status
= get_sorted_dc_list(domain
, NULL
, &ip_list
, &count
,
365 if (!NT_STATUS_IS_OK(status
)) {
369 /* remove servers which are known to be dead based on
370 the corresponding DNS method */
372 for (i
= 0; i
< count
; ++i
) {
373 char server
[INET6_ADDRSTRLEN
];
375 print_sockaddr(server
, sizeof(server
), &ip_list
[i
].ss
);
378 check_negative_conn_cache(realm
, server
))) {
379 /* Ensure we add the workgroup name for this
380 IP address as negative too. */
381 add_failed_connection_entry(
383 NT_STATUS_UNSUCCESSFUL
);
388 status
= cldap_ping_list(ads
, domain
, ip_list
, count
);
396 /**********************************************************************
397 resolve a name and perform an "ldap ping" using DNS
398 **********************************************************************/
400 static NTSTATUS
resolve_and_ping_dns(ADS_STRUCT
*ads
, const char *sitename
,
404 struct ip_service
*ip_list
;
405 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
407 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
410 status
= get_sorted_dc_list(realm
, sitename
, &ip_list
, &count
,
412 if (!NT_STATUS_IS_OK(status
)) {
416 status
= cldap_ping_list(ads
, realm
, ip_list
, count
);
423 /**********************************************************************
424 Try to find an AD dc using our internal name resolution routines
425 Try the realm first and then then workgroup name if netbios is not
427 **********************************************************************/
429 static NTSTATUS
ads_find_dc(ADS_STRUCT
*ads
)
431 const char *c_domain
= "";
433 bool use_own_domain
= False
;
434 char *sitename
= NULL
;
435 NTSTATUS status
= NT_STATUS_UNSUCCESSFUL
;
438 /* if the realm and workgroup are both empty, assume they are ours */
441 c_realm
= ads
->server
.realm
;
447 /* special case where no realm and no workgroup means our own */
448 if ( !ads
->server
.workgroup
|| !*ads
->server
.workgroup
) {
449 use_own_domain
= True
;
450 c_realm
= lp_realm();
454 if (!lp_disable_netbios()) {
455 if (use_own_domain
) {
456 c_domain
= lp_workgroup();
458 c_domain
= ads
->server
.workgroup
;
459 if (!*c_realm
&& (!c_domain
|| !*c_domain
)) {
460 c_domain
= lp_workgroup();
469 if (!*c_realm
&& !*c_domain
) {
470 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
472 return NT_STATUS_INVALID_PARAMETER
; /* rather need MISSING_PARAMETER ... */
476 * In case of LDAP we use get_dc_name() as that
477 * creates the custom krb5.conf file
479 if (!(ads
->auth
.flags
& ADS_AUTH_NO_BIND
)) {
481 struct sockaddr_storage ip_out
;
483 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
484 " and falling back to domain '%s'\n",
487 ok
= get_dc_name(c_domain
, c_realm
, srv_name
, &ip_out
);
490 * we call ads_try_connect() to fill in the
491 * ads->config details
493 ok
= ads_try_connect(ads
, false, &ip_out
);
499 return NT_STATUS_NO_LOGON_SERVERS
;
503 sitename
= sitename_fetch(talloc_tos(), c_realm
);
504 status
= resolve_and_ping_dns(ads
, sitename
, c_realm
);
506 if (NT_STATUS_IS_OK(status
)) {
507 TALLOC_FREE(sitename
);
511 /* In case we failed to contact one of our closest DC on our
513 * need to try to find another DC, retry with a site-less SRV
518 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
519 "our site (%s), Trying to find another DC "
520 "for realm '%s' (domain '%s')\n",
521 sitename
, c_realm
, c_domain
));
522 namecache_delete(c_realm
, 0x1C);
524 resolve_and_ping_dns(ads
, NULL
, c_realm
);
526 if (NT_STATUS_IS_OK(status
)) {
527 TALLOC_FREE(sitename
);
532 TALLOC_FREE(sitename
);
535 /* try netbios as fallback - if permitted,
536 or if configuration specifically requests it */
539 DEBUG(3, ("ads_find_dc: falling back to netbios "
540 "name resolution for domain '%s' (realm '%s')\n",
544 status
= resolve_and_ping_netbios(ads
, c_domain
, c_realm
);
545 if (NT_STATUS_IS_OK(status
)) {
550 DEBUG(1, ("ads_find_dc: "
551 "name resolution for realm '%s' (domain '%s') failed: %s\n",
552 c_realm
, c_domain
, nt_errstr(status
)));
556 /*********************************************************************
557 *********************************************************************/
559 static NTSTATUS
ads_lookup_site(void)
561 ADS_STRUCT
*ads
= NULL
;
562 ADS_STATUS ads_status
;
563 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
565 ads
= ads_init(lp_realm(), NULL
, NULL
);
567 return NT_STATUS_NO_MEMORY
;
570 /* The NO_BIND here will find a DC and set the client site
571 but not establish the TCP connection */
573 ads
->auth
.flags
= ADS_AUTH_NO_BIND
;
574 ads_status
= ads_connect(ads
);
575 if (!ADS_ERR_OK(ads_status
)) {
576 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
577 ads_errstr(ads_status
)));
579 nt_status
= ads_ntstatus(ads_status
);
588 /*********************************************************************
589 *********************************************************************/
591 static const char* host_dns_domain(const char *fqdn
)
593 const char *p
= fqdn
;
595 /* go to next char following '.' */
597 if ((p
= strchr_m(fqdn
, '.')) != NULL
) {
606 * Connect to the Global Catalog server
607 * @param ads Pointer to an existing ADS_STRUCT
608 * @return status of connection
610 * Simple wrapper around ads_connect() that fills in the
611 * GC ldap server information
614 ADS_STATUS
ads_connect_gc(ADS_STRUCT
*ads
)
616 TALLOC_CTX
*frame
= talloc_stackframe();
617 struct dns_rr_srv
*gcs_list
;
619 const char *realm
= ads
->server
.realm
;
620 NTSTATUS nt_status
= NT_STATUS_UNSUCCESSFUL
;
621 ADS_STATUS ads_status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
624 char *sitename
= NULL
;
629 if ((sitename
= sitename_fetch(frame
, realm
)) == NULL
) {
631 sitename
= sitename_fetch(frame
, realm
);
635 /* We try once with a sitename and once without
636 (unless we don't have a sitename and then we're
639 if (sitename
== NULL
)
642 nt_status
= ads_dns_query_gcs(frame
,
648 if (!NT_STATUS_IS_OK(nt_status
)) {
649 ads_status
= ADS_ERROR_NT(nt_status
);
653 /* Loop until we get a successful connection or have gone
654 through them all. When connecting a GC server, make sure that
655 the realm is the server's DNS name and not the forest root */
657 for (i
=0; i
<num_gcs
; i
++) {
658 ads
->server
.gc
= true;
659 ads
->server
.ldap_server
= SMB_STRDUP(gcs_list
[i
].hostname
);
660 ads
->server
.realm
= SMB_STRDUP(host_dns_domain(ads
->server
.ldap_server
));
661 ads_status
= ads_connect(ads
);
662 if (ADS_ERR_OK(ads_status
)) {
663 /* Reset the bind_dn to "". A Global Catalog server
664 may host multiple domain trees in a forest.
665 Windows 2003 GC server will accept "" as the search
666 path to imply search all domain trees in the forest */
668 SAFE_FREE(ads
->config
.bind_path
);
669 ads
->config
.bind_path
= SMB_STRDUP("");
674 SAFE_FREE(ads
->server
.ldap_server
);
675 SAFE_FREE(ads
->server
.realm
);
678 TALLOC_FREE(gcs_list
);
683 talloc_destroy(frame
);
690 * Connect to the LDAP server
691 * @param ads Pointer to an existing ADS_STRUCT
692 * @return status of connection
694 ADS_STATUS
ads_connect(ADS_STRUCT
*ads
)
696 int version
= LDAP_VERSION3
;
699 char addr
[INET6_ADDRSTRLEN
];
701 ZERO_STRUCT(ads
->ldap
);
702 ads
->ldap
.last_attempt
= time_mono(NULL
);
703 ads
->ldap
.wrap_type
= ADS_SASLWRAP_TYPE_PLAIN
;
705 /* try with a user specified server */
707 if (DEBUGLEVEL
>= 11) {
708 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
709 DEBUG(11,("ads_connect: entering\n"));
710 DEBUGADD(11,("%s\n", s
));
714 if (ads
->server
.ldap_server
) {
716 struct sockaddr_storage ss
;
718 ok
= resolve_name(ads
->server
.ldap_server
, &ss
, 0x20, true);
720 DEBUG(5,("ads_connect: unable to resolve name %s\n",
721 ads
->server
.ldap_server
));
722 status
= ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
725 ok
= ads_try_connect(ads
, ads
->server
.gc
, &ss
);
730 /* The choice of which GC use is handled one level up in
731 ads_connect_gc(). If we continue on from here with
732 ads_find_dc() we will get GC searches on port 389 which
733 doesn't work. --jerry */
735 if (ads
->server
.gc
== true) {
736 return ADS_ERROR(LDAP_OPERATIONS_ERROR
);
740 ntstatus
= ads_find_dc(ads
);
741 if (NT_STATUS_IS_OK(ntstatus
)) {
745 status
= ADS_ERROR_NT(ntstatus
);
750 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
751 DEBUG(3,("Successfully contacted LDAP server %s\n", addr
));
753 if (!ads
->auth
.user_name
) {
754 /* Must use the userPrincipalName value here or sAMAccountName
755 and not servicePrincipalName; found by Guenther Deschner */
757 if (asprintf(&ads
->auth
.user_name
, "%s$", lp_netbios_name() ) == -1) {
758 DEBUG(0,("ads_connect: asprintf fail.\n"));
759 ads
->auth
.user_name
= NULL
;
763 if (!ads
->auth
.realm
) {
764 ads
->auth
.realm
= SMB_STRDUP(ads
->config
.realm
);
767 if (!ads
->auth
.kdc_server
) {
768 print_sockaddr(addr
, sizeof(addr
), &ads
->ldap
.ss
);
769 ads
->auth
.kdc_server
= SMB_STRDUP(addr
);
772 /* If the caller() requested no LDAP bind, then we are done */
774 if (ads
->auth
.flags
& ADS_AUTH_NO_BIND
) {
775 status
= ADS_SUCCESS
;
779 ads
->ldap
.mem_ctx
= talloc_init("ads LDAP connection memory");
780 if (!ads
->ldap
.mem_ctx
) {
781 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
785 /* Otherwise setup the TCP LDAP session */
787 ads
->ldap
.ld
= ldap_open_with_timeout(addr
,
789 ads
->ldap
.port
, lp_ldap_timeout());
790 if (ads
->ldap
.ld
== NULL
) {
791 status
= ADS_ERROR(LDAP_OPERATIONS_ERROR
);
794 DEBUG(3,("Connected to LDAP server %s\n", ads
->config
.ldap_server_name
));
796 /* cache the successful connection for workgroup and realm */
797 if (ads_closest_dc(ads
)) {
798 saf_store( ads
->server
.workgroup
, ads
->config
.ldap_server_name
);
799 saf_store( ads
->server
.realm
, ads
->config
.ldap_server_name
);
802 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
804 if ( lp_ldap_ssl_ads() ) {
805 status
= ADS_ERROR(smbldap_start_tls(ads
->ldap
.ld
, version
));
806 if (!ADS_ERR_OK(status
)) {
811 /* fill in the current time and offsets */
813 status
= ads_current_time( ads
);
814 if ( !ADS_ERR_OK(status
) ) {
818 /* Now do the bind */
820 if (ads
->auth
.flags
& ADS_AUTH_ANON_BIND
) {
821 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, NULL
, NULL
));
825 if (ads
->auth
.flags
& ADS_AUTH_SIMPLE_BIND
) {
826 status
= ADS_ERROR(ldap_simple_bind_s(ads
->ldap
.ld
, ads
->auth
.user_name
, ads
->auth
.password
));
830 status
= ads_sasl_bind(ads
);
833 if (DEBUGLEVEL
>= 11) {
834 char *s
= NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct
, ads
);
835 DEBUG(11,("ads_connect: leaving with: %s\n",
836 ads_errstr(status
)));
837 DEBUGADD(11,("%s\n", s
));
845 * Connect to the LDAP server using given credentials
846 * @param ads Pointer to an existing ADS_STRUCT
847 * @return status of connection
849 ADS_STATUS
ads_connect_user_creds(ADS_STRUCT
*ads
)
851 ads
->auth
.flags
|= ADS_AUTH_USER_CREDS
;
853 return ads_connect(ads
);
857 * Disconnect the LDAP server
858 * @param ads Pointer to an existing ADS_STRUCT
860 void ads_disconnect(ADS_STRUCT
*ads
)
863 ldap_unbind(ads
->ldap
.ld
);
866 if (ads
->ldap
.wrap_ops
&& ads
->ldap
.wrap_ops
->disconnect
) {
867 ads
->ldap
.wrap_ops
->disconnect(ads
);
869 if (ads
->ldap
.mem_ctx
) {
870 talloc_free(ads
->ldap
.mem_ctx
);
872 ZERO_STRUCT(ads
->ldap
);
876 Duplicate a struct berval into talloc'ed memory
878 static struct berval
*dup_berval(TALLOC_CTX
*ctx
, const struct berval
*in_val
)
880 struct berval
*value
;
882 if (!in_val
) return NULL
;
884 value
= talloc_zero(ctx
, struct berval
);
887 if (in_val
->bv_len
== 0) return value
;
889 value
->bv_len
= in_val
->bv_len
;
890 value
->bv_val
= (char *)talloc_memdup(ctx
, in_val
->bv_val
,
896 Make a values list out of an array of (struct berval *)
898 static struct berval
**ads_dup_values(TALLOC_CTX
*ctx
,
899 const struct berval
**in_vals
)
901 struct berval
**values
;
904 if (!in_vals
) return NULL
;
905 for (i
=0; in_vals
[i
]; i
++)
907 values
= talloc_zero_array(ctx
, struct berval
*, i
+1);
908 if (!values
) return NULL
;
910 for (i
=0; in_vals
[i
]; i
++) {
911 values
[i
] = dup_berval(ctx
, in_vals
[i
]);
917 UTF8-encode a values list out of an array of (char *)
919 static char **ads_push_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
925 if (!in_vals
) return NULL
;
926 for (i
=0; in_vals
[i
]; i
++)
928 values
= talloc_zero_array(ctx
, char *, i
+1);
929 if (!values
) return NULL
;
931 for (i
=0; in_vals
[i
]; i
++) {
932 if (!push_utf8_talloc(ctx
, &values
[i
], in_vals
[i
], &size
)) {
941 Pull a (char *) array out of a UTF8-encoded values list
943 static char **ads_pull_strvals(TALLOC_CTX
*ctx
, const char **in_vals
)
947 size_t converted_size
;
949 if (!in_vals
) return NULL
;
950 for (i
=0; in_vals
[i
]; i
++)
952 values
= talloc_zero_array(ctx
, char *, i
+1);
953 if (!values
) return NULL
;
955 for (i
=0; in_vals
[i
]; i
++) {
956 if (!pull_utf8_talloc(ctx
, &values
[i
], in_vals
[i
],
958 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
959 "%s", strerror(errno
)));
966 * Do a search with paged results. cookie must be null on the first
967 * call, and then returned on each subsequent call. It will be null
968 * again when the entire search is complete
969 * @param ads connection to ads server
970 * @param bind_path Base dn for the search
971 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
972 * @param expr Search expression - specified in local charset
973 * @param attrs Attributes to retrieve - specified in utf8 or ascii
974 * @param res ** which will contain results - free res* with ads_msgfree()
975 * @param count Number of entries retrieved on this page
976 * @param cookie The paged results cookie to be returned on subsequent calls
977 * @return status of search
979 static ADS_STATUS
ads_do_paged_search_args(ADS_STRUCT
*ads
,
980 const char *bind_path
,
981 int scope
, const char *expr
,
982 const char **attrs
, void *args
,
984 int *count
, struct berval
**cookie
)
987 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
988 size_t converted_size
;
989 LDAPControl PagedResults
, NoReferrals
, ExternalCtrl
, *controls
[4], **rcontrols
;
990 BerElement
*cookie_be
= NULL
;
991 struct berval
*cookie_bv
= NULL
;
992 BerElement
*ext_be
= NULL
;
993 struct berval
*ext_bv
= NULL
;
996 ads_control
*external_control
= (ads_control
*) args
;
1000 if (!(ctx
= talloc_init("ads_do_paged_search_args")))
1001 return ADS_ERROR(LDAP_NO_MEMORY
);
1003 /* 0 means the conversion worked but the result was empty
1004 so we only fail if it's -1. In any case, it always
1005 at least nulls out the dest */
1006 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1007 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1009 rc
= LDAP_NO_MEMORY
;
1013 if (!attrs
|| !(*attrs
))
1014 search_attrs
= NULL
;
1016 /* This would be the utf8-encoded version...*/
1017 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1018 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
))) {
1019 rc
= LDAP_NO_MEMORY
;
1024 /* Paged results only available on ldap v3 or later */
1025 ldap_get_option(ads
->ldap
.ld
, LDAP_OPT_PROTOCOL_VERSION
, &version
);
1026 if (version
< LDAP_VERSION3
) {
1027 rc
= LDAP_NOT_SUPPORTED
;
1031 cookie_be
= ber_alloc_t(LBER_USE_DER
);
1033 ber_printf(cookie_be
, "{iO}", (ber_int_t
) ads
->config
.ldap_page_size
, *cookie
);
1034 ber_bvfree(*cookie
); /* don't need it from last time */
1037 ber_printf(cookie_be
, "{io}", (ber_int_t
) ads
->config
.ldap_page_size
, "", 0);
1039 ber_flatten(cookie_be
, &cookie_bv
);
1040 PagedResults
.ldctl_oid
= discard_const_p(char, ADS_PAGE_CTL_OID
);
1041 PagedResults
.ldctl_iscritical
= (char) 1;
1042 PagedResults
.ldctl_value
.bv_len
= cookie_bv
->bv_len
;
1043 PagedResults
.ldctl_value
.bv_val
= cookie_bv
->bv_val
;
1045 NoReferrals
.ldctl_oid
= discard_const_p(char, ADS_NO_REFERRALS_OID
);
1046 NoReferrals
.ldctl_iscritical
= (char) 0;
1047 NoReferrals
.ldctl_value
.bv_len
= 0;
1048 NoReferrals
.ldctl_value
.bv_val
= discard_const_p(char, "");
1050 if (external_control
&&
1051 (strequal(external_control
->control
, ADS_EXTENDED_DN_OID
) ||
1052 strequal(external_control
->control
, ADS_SD_FLAGS_OID
))) {
1054 ExternalCtrl
.ldctl_oid
= discard_const_p(char, external_control
->control
);
1055 ExternalCtrl
.ldctl_iscritical
= (char) external_control
->critical
;
1057 /* win2k does not accept a ldctl_value beeing passed in */
1059 if (external_control
->val
!= 0) {
1061 if ((ext_be
= ber_alloc_t(LBER_USE_DER
)) == NULL
) {
1062 rc
= LDAP_NO_MEMORY
;
1066 if ((ber_printf(ext_be
, "{i}", (ber_int_t
) external_control
->val
)) == -1) {
1067 rc
= LDAP_NO_MEMORY
;
1070 if ((ber_flatten(ext_be
, &ext_bv
)) == -1) {
1071 rc
= LDAP_NO_MEMORY
;
1075 ExternalCtrl
.ldctl_value
.bv_len
= ext_bv
->bv_len
;
1076 ExternalCtrl
.ldctl_value
.bv_val
= ext_bv
->bv_val
;
1079 ExternalCtrl
.ldctl_value
.bv_len
= 0;
1080 ExternalCtrl
.ldctl_value
.bv_val
= NULL
;
1083 controls
[0] = &NoReferrals
;
1084 controls
[1] = &PagedResults
;
1085 controls
[2] = &ExternalCtrl
;
1089 controls
[0] = &NoReferrals
;
1090 controls
[1] = &PagedResults
;
1094 /* we need to disable referrals as the openldap libs don't
1095 handle them and paged results at the same time. Using them
1096 together results in the result record containing the server
1097 page control being removed from the result list (tridge/jmcd)
1099 leaving this in despite the control that says don't generate
1100 referrals, in case the server doesn't support it (jmcd)
1102 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1104 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1105 search_attrs
, 0, controls
,
1106 NULL
, LDAP_NO_LIMIT
,
1107 (LDAPMessage
**)res
);
1109 ber_free(cookie_be
, 1);
1110 ber_bvfree(cookie_bv
);
1113 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr
,
1114 ldap_err2string(rc
)));
1115 if (rc
== LDAP_OTHER
) {
1119 ret
= ldap_parse_result(ads
->ldap
.ld
,
1127 if (ret
== LDAP_SUCCESS
) {
1128 DEBUG(3, ("ldap_search_with_timeout(%s) "
1129 "error: %s\n", expr
, ldap_errmsg
));
1130 ldap_memfree(ldap_errmsg
);
1136 rc
= ldap_parse_result(ads
->ldap
.ld
, *res
, NULL
, NULL
, NULL
,
1137 NULL
, &rcontrols
, 0);
1143 for (i
=0; rcontrols
[i
]; i
++) {
1144 if (strcmp(ADS_PAGE_CTL_OID
, rcontrols
[i
]->ldctl_oid
) == 0) {
1145 cookie_be
= ber_init(&rcontrols
[i
]->ldctl_value
);
1146 ber_scanf(cookie_be
,"{iO}", (ber_int_t
*) count
,
1148 /* the berval is the cookie, but must be freed when
1150 if (cookie_bv
->bv_len
) /* still more to do */
1151 *cookie
=ber_bvdup(cookie_bv
);
1154 ber_bvfree(cookie_bv
);
1155 ber_free(cookie_be
, 1);
1159 ldap_controls_free(rcontrols
);
1162 talloc_destroy(ctx
);
1165 ber_free(ext_be
, 1);
1172 /* if/when we decide to utf8-encode attrs, take out this next line */
1173 TALLOC_FREE(search_attrs
);
1175 return ADS_ERROR(rc
);
1178 static ADS_STATUS
ads_do_paged_search(ADS_STRUCT
*ads
, const char *bind_path
,
1179 int scope
, const char *expr
,
1180 const char **attrs
, LDAPMessage
**res
,
1181 int *count
, struct berval
**cookie
)
1183 return ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
, count
, cookie
);
1188 * Get all results for a search. This uses ads_do_paged_search() to return
1189 * all entries in a large search.
1190 * @param ads connection to ads server
1191 * @param bind_path Base dn for the search
1192 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1193 * @param expr Search expression
1194 * @param attrs Attributes to retrieve
1195 * @param res ** which will contain results - free res* with ads_msgfree()
1196 * @return status of search
1198 ADS_STATUS
ads_do_search_all_args(ADS_STRUCT
*ads
, const char *bind_path
,
1199 int scope
, const char *expr
,
1200 const char **attrs
, void *args
,
1203 struct berval
*cookie
= NULL
;
1208 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
, attrs
, args
, res
,
1211 if (!ADS_ERR_OK(status
))
1214 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1216 LDAPMessage
*res2
= NULL
;
1217 LDAPMessage
*msg
, *next
;
1219 status
= ads_do_paged_search_args(ads
, bind_path
, scope
, expr
,
1220 attrs
, args
, &res2
, &count
, &cookie
);
1221 if (!ADS_ERR_OK(status
)) {
1222 /* Ensure we free all collected results */
1223 ads_msgfree(ads
, *res
);
1228 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1229 that this works on all ldap libs, but I have only tested with openldap */
1230 for (msg
= ads_first_message(ads
, res2
); msg
; msg
= next
) {
1231 next
= ads_next_message(ads
, msg
);
1232 ldap_add_result_entry((LDAPMessage
**)res
, msg
);
1234 /* note that we do not free res2, as the memory is now
1235 part of the main returned list */
1238 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1239 status
= ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL
);
1245 ADS_STATUS
ads_do_search_all(ADS_STRUCT
*ads
, const char *bind_path
,
1246 int scope
, const char *expr
,
1247 const char **attrs
, LDAPMessage
**res
)
1249 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, NULL
, res
);
1252 ADS_STATUS
ads_do_search_all_sd_flags(ADS_STRUCT
*ads
, const char *bind_path
,
1253 int scope
, const char *expr
,
1254 const char **attrs
, uint32_t sd_flags
,
1259 args
.control
= ADS_SD_FLAGS_OID
;
1260 args
.val
= sd_flags
;
1261 args
.critical
= True
;
1263 return ads_do_search_all_args(ads
, bind_path
, scope
, expr
, attrs
, &args
, res
);
1268 * Run a function on all results for a search. Uses ads_do_paged_search() and
1269 * runs the function as each page is returned, using ads_process_results()
1270 * @param ads connection to ads server
1271 * @param bind_path Base dn for the search
1272 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1273 * @param expr Search expression - specified in local charset
1274 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1275 * @param fn Function which takes attr name, values list, and data_area
1276 * @param data_area Pointer which is passed to function on each call
1277 * @return status of search
1279 ADS_STATUS
ads_do_search_all_fn(ADS_STRUCT
*ads
, const char *bind_path
,
1280 int scope
, const char *expr
, const char **attrs
,
1281 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
1284 struct berval
*cookie
= NULL
;
1289 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
, &res
,
1292 if (!ADS_ERR_OK(status
)) return status
;
1294 ads_process_results(ads
, res
, fn
, data_area
);
1295 ads_msgfree(ads
, res
);
1298 status
= ads_do_paged_search(ads
, bind_path
, scope
, expr
, attrs
,
1299 &res
, &count
, &cookie
);
1301 if (!ADS_ERR_OK(status
)) break;
1303 ads_process_results(ads
, res
, fn
, data_area
);
1304 ads_msgfree(ads
, res
);
1311 * Do a search with a timeout.
1312 * @param ads connection to ads server
1313 * @param bind_path Base dn for the search
1314 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1315 * @param expr Search expression
1316 * @param attrs Attributes to retrieve
1317 * @param res ** which will contain results - free res* with ads_msgfree()
1318 * @return status of search
1320 ADS_STATUS
ads_do_search(ADS_STRUCT
*ads
, const char *bind_path
, int scope
,
1322 const char **attrs
, LDAPMessage
**res
)
1325 char *utf8_expr
, *utf8_path
, **search_attrs
= NULL
;
1326 size_t converted_size
;
1330 if (!(ctx
= talloc_init("ads_do_search"))) {
1331 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1332 return ADS_ERROR(LDAP_NO_MEMORY
);
1335 /* 0 means the conversion worked but the result was empty
1336 so we only fail if it's negative. In any case, it always
1337 at least nulls out the dest */
1338 if (!push_utf8_talloc(ctx
, &utf8_expr
, expr
, &converted_size
) ||
1339 !push_utf8_talloc(ctx
, &utf8_path
, bind_path
, &converted_size
))
1341 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1342 rc
= LDAP_NO_MEMORY
;
1346 if (!attrs
|| !(*attrs
))
1347 search_attrs
= NULL
;
1349 /* This would be the utf8-encoded version...*/
1350 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1351 if (!(search_attrs
= str_list_copy(talloc_tos(), attrs
)))
1353 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1354 rc
= LDAP_NO_MEMORY
;
1359 /* see the note in ads_do_paged_search - we *must* disable referrals */
1360 ldap_set_option(ads
->ldap
.ld
, LDAP_OPT_REFERRALS
, LDAP_OPT_OFF
);
1362 rc
= ldap_search_with_timeout(ads
->ldap
.ld
, utf8_path
, scope
, utf8_expr
,
1363 search_attrs
, 0, NULL
, NULL
,
1365 (LDAPMessage
**)res
);
1367 if (rc
== LDAP_SIZELIMIT_EXCEEDED
) {
1368 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1373 talloc_destroy(ctx
);
1374 /* if/when we decide to utf8-encode attrs, take out this next line */
1375 TALLOC_FREE(search_attrs
);
1376 return ADS_ERROR(rc
);
1379 * Do a general ADS search
1380 * @param ads connection to ads server
1381 * @param res ** which will contain results - free res* with ads_msgfree()
1382 * @param expr Search expression
1383 * @param attrs Attributes to retrieve
1384 * @return status of search
1386 ADS_STATUS
ads_search(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1387 const char *expr
, const char **attrs
)
1389 return ads_do_search(ads
, ads
->config
.bind_path
, LDAP_SCOPE_SUBTREE
,
1394 * Do a search on a specific DistinguishedName
1395 * @param ads connection to ads server
1396 * @param res ** which will contain results - free res* with ads_msgfree()
1397 * @param dn DistinguishName to search
1398 * @param attrs Attributes to retrieve
1399 * @return status of search
1401 ADS_STATUS
ads_search_dn(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1402 const char *dn
, const char **attrs
)
1404 return ads_do_search(ads
, dn
, LDAP_SCOPE_BASE
, "(objectclass=*)",
1409 * Free up memory from a ads_search
1410 * @param ads connection to ads server
1411 * @param msg Search results to free
1413 void ads_msgfree(ADS_STRUCT
*ads
, LDAPMessage
*msg
)
1420 * Get a dn from search results
1421 * @param ads connection to ads server
1422 * @param msg Search result
1425 char *ads_get_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
)
1427 char *utf8_dn
, *unix_dn
;
1428 size_t converted_size
;
1430 utf8_dn
= ldap_get_dn(ads
->ldap
.ld
, msg
);
1433 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1437 if (!pull_utf8_talloc(mem_ctx
, &unix_dn
, utf8_dn
, &converted_size
)) {
1438 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1442 ldap_memfree(utf8_dn
);
1447 * Get the parent from a dn
1448 * @param dn the dn to return the parent from
1449 * @return parent dn string
1451 char *ads_parent_dn(const char *dn
)
1459 p
= strchr(dn
, ',');
1469 * Find a machine account given a hostname
1470 * @param ads connection to ads server
1471 * @param res ** which will contain results - free res* with ads_msgfree()
1472 * @param host Hostname to search for
1473 * @return status of search
1475 ADS_STATUS
ads_find_machine_acct(ADS_STRUCT
*ads
, LDAPMessage
**res
,
1476 const char *machine
)
1480 const char *attrs
[] = {"*", "nTSecurityDescriptor", NULL
};
1484 /* the easiest way to find a machine account anywhere in the tree
1485 is to look for hostname$ */
1486 if (asprintf(&expr
, "(samAccountName=%s$)", machine
) == -1) {
1487 DEBUG(1, ("asprintf failed!\n"));
1488 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1491 status
= ads_search(ads
, res
, expr
, attrs
);
1497 * Initialize a list of mods to be used in a modify request
1498 * @param ctx An initialized TALLOC_CTX
1499 * @return allocated ADS_MODLIST
1501 ADS_MODLIST
ads_init_mods(TALLOC_CTX
*ctx
)
1503 #define ADS_MODLIST_ALLOC_SIZE 10
1506 if ((mods
= talloc_zero_array(ctx
, LDAPMod
*, ADS_MODLIST_ALLOC_SIZE
+ 1)))
1507 /* -1 is safety to make sure we don't go over the end.
1508 need to reset it to NULL before doing ldap modify */
1509 mods
[ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1511 return (ADS_MODLIST
)mods
;
1516 add an attribute to the list, with values list already constructed
1518 static ADS_STATUS
ads_modlist_add(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1519 int mod_op
, const char *name
,
1520 const void *_invals
)
1523 LDAPMod
**modlist
= (LDAPMod
**) *mods
;
1524 struct berval
**ber_values
= NULL
;
1525 char **char_values
= NULL
;
1528 mod_op
= LDAP_MOD_DELETE
;
1530 if (mod_op
& LDAP_MOD_BVALUES
) {
1531 const struct berval
**b
;
1532 b
= discard_const_p(const struct berval
*, _invals
);
1533 ber_values
= ads_dup_values(ctx
, b
);
1536 c
= discard_const_p(const char *, _invals
);
1537 char_values
= ads_push_strvals(ctx
, c
);
1541 /* find the first empty slot */
1542 for (curmod
=0; modlist
[curmod
] && modlist
[curmod
] != (LDAPMod
*) -1;
1544 if (modlist
[curmod
] == (LDAPMod
*) -1) {
1545 if (!(modlist
= talloc_realloc(ctx
, modlist
, LDAPMod
*,
1546 curmod
+ADS_MODLIST_ALLOC_SIZE
+1)))
1547 return ADS_ERROR(LDAP_NO_MEMORY
);
1548 memset(&modlist
[curmod
], 0,
1549 ADS_MODLIST_ALLOC_SIZE
*sizeof(LDAPMod
*));
1550 modlist
[curmod
+ADS_MODLIST_ALLOC_SIZE
] = (LDAPMod
*) -1;
1551 *mods
= (ADS_MODLIST
)modlist
;
1554 if (!(modlist
[curmod
] = talloc_zero(ctx
, LDAPMod
)))
1555 return ADS_ERROR(LDAP_NO_MEMORY
);
1556 modlist
[curmod
]->mod_type
= talloc_strdup(ctx
, name
);
1557 if (mod_op
& LDAP_MOD_BVALUES
) {
1558 modlist
[curmod
]->mod_bvalues
= ber_values
;
1559 } else if (mod_op
& LDAP_MOD_DELETE
) {
1560 modlist
[curmod
]->mod_values
= NULL
;
1562 modlist
[curmod
]->mod_values
= char_values
;
1565 modlist
[curmod
]->mod_op
= mod_op
;
1566 return ADS_ERROR(LDAP_SUCCESS
);
1570 * Add a single string value to a mod list
1571 * @param ctx An initialized TALLOC_CTX
1572 * @param mods An initialized ADS_MODLIST
1573 * @param name The attribute name to add
1574 * @param val The value to add - NULL means DELETE
1575 * @return ADS STATUS indicating success of add
1577 ADS_STATUS
ads_mod_str(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1578 const char *name
, const char *val
)
1580 const char *values
[2];
1586 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1587 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
, name
, values
);
1591 * Add an array of string values to a mod list
1592 * @param ctx An initialized TALLOC_CTX
1593 * @param mods An initialized ADS_MODLIST
1594 * @param name The attribute name to add
1595 * @param vals The array of string values to add - NULL means DELETE
1596 * @return ADS STATUS indicating success of add
1598 ADS_STATUS
ads_mod_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1599 const char *name
, const char **vals
)
1602 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1603 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
,
1604 name
, (const void **) vals
);
1609 * Add a single ber-encoded value to a mod list
1610 * @param ctx An initialized TALLOC_CTX
1611 * @param mods An initialized ADS_MODLIST
1612 * @param name The attribute name to add
1613 * @param val The value to add - NULL means DELETE
1614 * @return ADS STATUS indicating success of add
1616 static ADS_STATUS
ads_mod_ber(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1617 const char *name
, const struct berval
*val
)
1619 const struct berval
*values
[2];
1624 return ads_modlist_add(ctx
, mods
, LDAP_MOD_DELETE
, name
, NULL
);
1625 return ads_modlist_add(ctx
, mods
, LDAP_MOD_REPLACE
|LDAP_MOD_BVALUES
,
1626 name
, (const void **) values
);
1631 * Perform an ldap modify
1632 * @param ads connection to ads server
1633 * @param mod_dn DistinguishedName to modify
1634 * @param mods list of modifications to perform
1635 * @return status of modify
1637 ADS_STATUS
ads_gen_mod(ADS_STRUCT
*ads
, const char *mod_dn
, ADS_MODLIST mods
)
1640 char *utf8_dn
= NULL
;
1641 size_t converted_size
;
1643 this control is needed to modify that contains a currently
1644 non-existent attribute (but allowable for the object) to run
1646 LDAPControl PermitModify
= {
1647 discard_const_p(char, ADS_PERMIT_MODIFY_OID
),
1650 LDAPControl
*controls
[2];
1652 controls
[0] = &PermitModify
;
1655 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, mod_dn
, &converted_size
)) {
1656 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1659 /* find the end of the list, marked by NULL or -1 */
1660 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1661 /* make sure the end of the list is NULL */
1663 ret
= ldap_modify_ext_s(ads
->ldap
.ld
, utf8_dn
,
1664 (LDAPMod
**) mods
, controls
, NULL
);
1665 TALLOC_FREE(utf8_dn
);
1666 return ADS_ERROR(ret
);
1670 * Perform an ldap add
1671 * @param ads connection to ads server
1672 * @param new_dn DistinguishedName to add
1673 * @param mods list of attributes and values for DN
1674 * @return status of add
1676 ADS_STATUS
ads_gen_add(ADS_STRUCT
*ads
, const char *new_dn
, ADS_MODLIST mods
)
1679 char *utf8_dn
= NULL
;
1680 size_t converted_size
;
1682 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, new_dn
, &converted_size
)) {
1683 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1684 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1687 /* find the end of the list, marked by NULL or -1 */
1688 for(i
=0;(mods
[i
]!=0)&&(mods
[i
]!=(LDAPMod
*) -1);i
++);
1689 /* make sure the end of the list is NULL */
1692 ret
= ldap_add_s(ads
->ldap
.ld
, utf8_dn
, (LDAPMod
**)mods
);
1693 TALLOC_FREE(utf8_dn
);
1694 return ADS_ERROR(ret
);
1698 * Delete a DistinguishedName
1699 * @param ads connection to ads server
1700 * @param new_dn DistinguishedName to delete
1701 * @return status of delete
1703 ADS_STATUS
ads_del_dn(ADS_STRUCT
*ads
, char *del_dn
)
1706 char *utf8_dn
= NULL
;
1707 size_t converted_size
;
1708 if (!push_utf8_talloc(talloc_tos(), &utf8_dn
, del_dn
, &converted_size
)) {
1709 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1710 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
1713 ret
= ldap_delete_s(ads
->ldap
.ld
, utf8_dn
);
1714 TALLOC_FREE(utf8_dn
);
1715 return ADS_ERROR(ret
);
1719 * Build an org unit string
1720 * if org unit is Computers or blank then assume a container, otherwise
1721 * assume a / separated list of organisational units.
1722 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1723 * @param ads connection to ads server
1724 * @param org_unit Organizational unit
1725 * @return org unit string - caller must free
1727 char *ads_ou_string(ADS_STRUCT
*ads
, const char *org_unit
)
1731 if (!org_unit
|| !*org_unit
) {
1733 ret
= ads_default_ou_string(ads
, DS_GUID_COMPUTERS_CONTAINER
);
1735 /* samba4 might not yet respond to a wellknownobject-query */
1736 return ret
? ret
: SMB_STRDUP("cn=Computers");
1739 if (strequal(org_unit
, "Computers")) {
1740 return SMB_STRDUP("cn=Computers");
1743 /* jmcd: removed "\\" from the separation chars, because it is
1744 needed as an escape for chars like '#' which are valid in an
1746 return ads_build_path(org_unit
, "/", "ou=", 1);
1750 * Get a org unit string for a well-known GUID
1751 * @param ads connection to ads server
1752 * @param wknguid Well known GUID
1753 * @return org unit string - caller must free
1755 char *ads_default_ou_string(ADS_STRUCT
*ads
, const char *wknguid
)
1758 LDAPMessage
*res
= NULL
;
1759 char *base
, *wkn_dn
= NULL
, *ret
= NULL
, **wkn_dn_exp
= NULL
,
1760 **bind_dn_exp
= NULL
;
1761 const char *attrs
[] = {"distinguishedName", NULL
};
1762 int new_ln
, wkn_ln
, bind_ln
, i
;
1764 if (wknguid
== NULL
) {
1768 if (asprintf(&base
, "<WKGUID=%s,%s>", wknguid
, ads
->config
.bind_path
) == -1) {
1769 DEBUG(1, ("asprintf failed!\n"));
1773 status
= ads_search_dn(ads
, &res
, base
, attrs
);
1774 if (!ADS_ERR_OK(status
)) {
1775 DEBUG(1,("Failed while searching for: %s\n", base
));
1779 if (ads_count_replies(ads
, res
) != 1) {
1783 /* substitute the bind-path from the well-known-guid-search result */
1784 wkn_dn
= ads_get_dn(ads
, talloc_tos(), res
);
1789 wkn_dn_exp
= ldap_explode_dn(wkn_dn
, 0);
1794 bind_dn_exp
= ldap_explode_dn(ads
->config
.bind_path
, 0);
1799 for (wkn_ln
=0; wkn_dn_exp
[wkn_ln
]; wkn_ln
++)
1801 for (bind_ln
=0; bind_dn_exp
[bind_ln
]; bind_ln
++)
1804 new_ln
= wkn_ln
- bind_ln
;
1806 ret
= SMB_STRDUP(wkn_dn_exp
[0]);
1811 for (i
=1; i
< new_ln
; i
++) {
1814 if (asprintf(&s
, "%s,%s", ret
, wkn_dn_exp
[i
]) == -1) {
1820 ret
= SMB_STRDUP(s
);
1829 ads_msgfree(ads
, res
);
1830 TALLOC_FREE(wkn_dn
);
1832 ldap_value_free(wkn_dn_exp
);
1835 ldap_value_free(bind_dn_exp
);
1842 * Adds (appends) an item to an attribute array, rather then
1843 * replacing the whole list
1844 * @param ctx An initialized TALLOC_CTX
1845 * @param mods An initialized ADS_MODLIST
1846 * @param name name of the ldap attribute to append to
1847 * @param vals an array of values to add
1848 * @return status of addition
1851 ADS_STATUS
ads_add_strlist(TALLOC_CTX
*ctx
, ADS_MODLIST
*mods
,
1852 const char *name
, const char **vals
)
1854 return ads_modlist_add(ctx
, mods
, LDAP_MOD_ADD
, name
,
1855 (const void *) vals
);
1859 * Determines the an account's current KVNO via an LDAP lookup
1860 * @param ads An initialized ADS_STRUCT
1861 * @param account_name the NT samaccountname.
1862 * @return the kvno for the account, or -1 in case of a failure.
1865 uint32_t ads_get_kvno(ADS_STRUCT
*ads
, const char *account_name
)
1867 LDAPMessage
*res
= NULL
;
1868 uint32_t kvno
= (uint32_t)-1; /* -1 indicates a failure */
1870 const char *attrs
[] = {"msDS-KeyVersionNumber", NULL
};
1871 char *dn_string
= NULL
;
1872 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1874 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name
));
1875 if (asprintf(&filter
, "(samAccountName=%s)", account_name
) == -1) {
1878 ret
= ads_search(ads
, &res
, filter
, attrs
);
1880 if (!ADS_ERR_OK(ret
) || (ads_count_replies(ads
, res
) != 1)) {
1881 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name
));
1882 ads_msgfree(ads
, res
);
1886 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1888 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1889 ads_msgfree(ads
, res
);
1892 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string
));
1893 TALLOC_FREE(dn_string
);
1895 /* ---------------------------------------------------------
1896 * 0 is returned as a default KVNO from this point on...
1897 * This is done because Windows 2000 does not support key
1898 * version numbers. Chances are that a failure in the next
1899 * step is simply due to Windows 2000 being used for a
1900 * domain controller. */
1903 if (!ads_pull_uint32(ads
, res
, "msDS-KeyVersionNumber", &kvno
)) {
1904 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1905 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1906 ads_msgfree(ads
, res
);
1911 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno
));
1912 ads_msgfree(ads
, res
);
1917 * Determines the computer account's current KVNO via an LDAP lookup
1918 * @param ads An initialized ADS_STRUCT
1919 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1920 * @return the kvno for the computer account, or -1 in case of a failure.
1923 uint32_t ads_get_machine_kvno(ADS_STRUCT
*ads
, const char *machine_name
)
1925 char *computer_account
= NULL
;
1928 if (asprintf(&computer_account
, "%s$", machine_name
) < 0) {
1932 kvno
= ads_get_kvno(ads
, computer_account
);
1933 free(computer_account
);
1939 * This clears out all registered spn's for a given hostname
1940 * @param ads An initilaized ADS_STRUCT
1941 * @param machine_name the NetBIOS name of the computer.
1942 * @return 0 upon success, non-zero otherwise.
1945 ADS_STATUS
ads_clear_service_principal_names(ADS_STRUCT
*ads
, const char *machine_name
)
1948 LDAPMessage
*res
= NULL
;
1950 const char *servicePrincipalName
[1] = {NULL
};
1951 ADS_STATUS ret
= ADS_ERROR(LDAP_SUCCESS
);
1952 char *dn_string
= NULL
;
1954 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
1955 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
1956 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name
));
1957 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name
));
1958 ads_msgfree(ads
, res
);
1959 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
1962 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name
));
1963 ctx
= talloc_init("ads_clear_service_principal_names");
1965 ads_msgfree(ads
, res
);
1966 return ADS_ERROR(LDAP_NO_MEMORY
);
1969 if (!(mods
= ads_init_mods(ctx
))) {
1970 talloc_destroy(ctx
);
1971 ads_msgfree(ads
, res
);
1972 return ADS_ERROR(LDAP_NO_MEMORY
);
1974 ret
= ads_mod_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
1975 if (!ADS_ERR_OK(ret
)) {
1976 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1977 ads_msgfree(ads
, res
);
1978 talloc_destroy(ctx
);
1981 dn_string
= ads_get_dn(ads
, talloc_tos(), res
);
1983 talloc_destroy(ctx
);
1984 ads_msgfree(ads
, res
);
1985 return ADS_ERROR(LDAP_NO_MEMORY
);
1987 ret
= ads_gen_mod(ads
, dn_string
, mods
);
1988 TALLOC_FREE(dn_string
);
1989 if (!ADS_ERR_OK(ret
)) {
1990 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1992 ads_msgfree(ads
, res
);
1993 talloc_destroy(ctx
);
1997 ads_msgfree(ads
, res
);
1998 talloc_destroy(ctx
);
2003 * @brief Search for an element in a string array.
2005 * @param[in] el_array The string array to search.
2007 * @param[in] num_el The number of elements in the string array.
2009 * @param[in] el The string to search.
2011 * @return True if found, false if not.
2013 bool ads_element_in_array(const char **el_array
, size_t num_el
, const char *el
)
2017 if (el_array
== NULL
|| num_el
== 0 || el
== NULL
) {
2021 for (i
= 0; i
< num_el
&& el_array
[i
] != NULL
; i
++) {
2024 cmp
= strcasecmp_m(el_array
[i
], el
);
2034 * @brief This gets the service principal names of an existing computer account.
2036 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2038 * @param[in] ads The ADS context to use.
2040 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2041 * identify the computer account.
2043 * @param[in] spn_array A pointer to store the array for SPNs.
2045 * @param[in] num_spns The number of principals stored in the array.
2047 * @return 0 on success, or a ADS error if a failure occured.
2049 ADS_STATUS
ads_get_service_principal_names(TALLOC_CTX
*mem_ctx
,
2051 const char *machine_name
,
2056 LDAPMessage
*res
= NULL
;
2059 status
= ads_find_machine_acct(ads
,
2062 if (!ADS_ERR_OK(status
)) {
2063 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2068 count
= ads_count_replies(ads
, res
);
2070 status
= ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2074 *spn_array
= ads_pull_strings(ads
,
2077 "servicePrincipalName",
2081 ads_msgfree(ads
, res
);
2087 * This adds a service principal name to an existing computer account
2088 * (found by hostname) in AD.
2089 * @param ads An initialized ADS_STRUCT
2090 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2091 * @param my_fqdn The fully qualified DNS name of the machine
2092 * @param spn A string of the service principal to add, i.e. 'host'
2093 * @return 0 upon sucess, or non-zero if a failure occurs
2096 ADS_STATUS
ads_add_service_principal_name(ADS_STRUCT
*ads
, const char *machine_name
,
2097 const char *my_fqdn
, const char *spn
)
2101 LDAPMessage
*res
= NULL
;
2104 char *dn_string
= NULL
;
2105 const char *servicePrincipalName
[3] = {NULL
, NULL
, NULL
};
2107 ret
= ads_find_machine_acct(ads
, &res
, machine_name
);
2108 if (!ADS_ERR_OK(ret
) || ads_count_replies(ads
, res
) != 1) {
2109 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2111 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2112 spn
, machine_name
, ads
->config
.realm
));
2113 ads_msgfree(ads
, res
);
2114 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
2117 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name
));
2118 if (!(ctx
= talloc_init("ads_add_service_principal_name"))) {
2119 ads_msgfree(ads
, res
);
2120 return ADS_ERROR(LDAP_NO_MEMORY
);
2123 /* add short name spn */
2125 if ( (psp1
= talloc_asprintf(ctx
, "%s/%s", spn
, machine_name
)) == NULL
) {
2126 talloc_destroy(ctx
);
2127 ads_msgfree(ads
, res
);
2128 return ADS_ERROR(LDAP_NO_MEMORY
);
2130 if (!strlower_m(&psp1
[strlen(spn
) + 1])) {
2131 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2134 servicePrincipalName
[0] = psp1
;
2136 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2137 psp1
, machine_name
));
2140 /* add fully qualified spn */
2142 if ( (psp2
= talloc_asprintf(ctx
, "%s/%s", spn
, my_fqdn
)) == NULL
) {
2143 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2146 if (!strlower_m(&psp2
[strlen(spn
) + 1])) {
2147 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2150 servicePrincipalName
[1] = psp2
;
2152 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2153 psp2
, machine_name
));
2155 if ( (mods
= ads_init_mods(ctx
)) == NULL
) {
2156 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2160 ret
= ads_add_strlist(ctx
, &mods
, "servicePrincipalName", servicePrincipalName
);
2161 if (!ADS_ERR_OK(ret
)) {
2162 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2166 if ( (dn_string
= ads_get_dn(ads
, ctx
, res
)) == NULL
) {
2167 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2171 ret
= ads_gen_mod(ads
, dn_string
, mods
);
2172 if (!ADS_ERR_OK(ret
)) {
2173 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2179 ads_msgfree(ads
, res
);
2184 * adds a machine account to the ADS server
2185 * @param ads An intialized ADS_STRUCT
2186 * @param machine_name - the NetBIOS machine name of this account.
2187 * @param account_type A number indicating the type of account to create
2188 * @param org_unit The LDAP path in which to place this account
2189 * @return 0 upon success, or non-zero otherwise
2192 ADS_STATUS
ads_create_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2193 const char *org_unit
)
2196 char *samAccountName
, *controlstr
;
2199 char *machine_escaped
= NULL
;
2201 const char *objectClass
[] = {"top", "person", "organizationalPerson",
2202 "user", "computer", NULL
};
2203 LDAPMessage
*res
= NULL
;
2204 uint32_t acct_control
= ( UF_WORKSTATION_TRUST_ACCOUNT
|\
2205 UF_DONT_EXPIRE_PASSWD
|\
2206 UF_ACCOUNTDISABLE
);
2208 if (!(ctx
= talloc_init("ads_add_machine_acct")))
2209 return ADS_ERROR(LDAP_NO_MEMORY
);
2211 ret
= ADS_ERROR(LDAP_NO_MEMORY
);
2213 machine_escaped
= escape_rdn_val_string_alloc(machine_name
);
2214 if (!machine_escaped
) {
2218 new_dn
= talloc_asprintf(ctx
, "cn=%s,%s", machine_escaped
, org_unit
);
2219 samAccountName
= talloc_asprintf(ctx
, "%s$", machine_name
);
2221 if ( !new_dn
|| !samAccountName
) {
2225 #ifndef ENCTYPE_ARCFOUR_HMAC
2226 acct_control
|= UF_USE_DES_KEY_ONLY
;
2229 if (!(controlstr
= talloc_asprintf(ctx
, "%u", acct_control
))) {
2233 if (!(mods
= ads_init_mods(ctx
))) {
2237 ads_mod_str(ctx
, &mods
, "cn", machine_name
);
2238 ads_mod_str(ctx
, &mods
, "sAMAccountName", samAccountName
);
2239 ads_mod_strlist(ctx
, &mods
, "objectClass", objectClass
);
2240 ads_mod_str(ctx
, &mods
, "userAccountControl", controlstr
);
2242 ret
= ads_gen_add(ads
, new_dn
, mods
);
2245 SAFE_FREE(machine_escaped
);
2246 ads_msgfree(ads
, res
);
2247 talloc_destroy(ctx
);
2253 * move a machine account to another OU on the ADS server
2254 * @param ads - An intialized ADS_STRUCT
2255 * @param machine_name - the NetBIOS machine name of this account.
2256 * @param org_unit - The LDAP path in which to place this account
2257 * @param moved - whether we moved the machine account (optional)
2258 * @return 0 upon success, or non-zero otherwise
2261 ADS_STATUS
ads_move_machine_acct(ADS_STRUCT
*ads
, const char *machine_name
,
2262 const char *org_unit
, bool *moved
)
2266 LDAPMessage
*res
= NULL
;
2267 char *filter
= NULL
;
2268 char *computer_dn
= NULL
;
2270 char *computer_rdn
= NULL
;
2271 bool need_move
= False
;
2273 if (asprintf(&filter
, "(samAccountName=%s$)", machine_name
) == -1) {
2274 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2278 /* Find pre-existing machine */
2279 rc
= ads_search(ads
, &res
, filter
, NULL
);
2280 if (!ADS_ERR_OK(rc
)) {
2284 computer_dn
= ads_get_dn(ads
, talloc_tos(), res
);
2286 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2290 parent_dn
= ads_parent_dn(computer_dn
);
2291 if (strequal(parent_dn
, org_unit
)) {
2297 if (asprintf(&computer_rdn
, "CN=%s", machine_name
) == -1) {
2298 rc
= ADS_ERROR(LDAP_NO_MEMORY
);
2302 ldap_status
= ldap_rename_s(ads
->ldap
.ld
, computer_dn
, computer_rdn
,
2303 org_unit
, 1, NULL
, NULL
);
2304 rc
= ADS_ERROR(ldap_status
);
2307 ads_msgfree(ads
, res
);
2309 TALLOC_FREE(computer_dn
);
2310 SAFE_FREE(computer_rdn
);
2312 if (!ADS_ERR_OK(rc
)) {
2324 dump a binary result from ldap
2326 static void dump_binary(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2329 for (i
=0; values
[i
]; i
++) {
2330 printf("%s: ", field
);
2331 for (j
=0; j
<values
[i
]->bv_len
; j
++) {
2332 printf("%02X", (unsigned char)values
[i
]->bv_val
[j
]);
2338 static void dump_guid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2341 for (i
=0; values
[i
]; i
++) {
2343 DATA_BLOB in
= data_blob_const(values
[i
]->bv_val
, values
[i
]->bv_len
);
2346 status
= GUID_from_ndr_blob(&in
, &guid
);
2347 if (NT_STATUS_IS_OK(status
)) {
2348 printf("%s: %s\n", field
, GUID_string(talloc_tos(), &guid
));
2350 printf("%s: INVALID GUID\n", field
);
2356 dump a sid result from ldap
2358 static void dump_sid(ADS_STRUCT
*ads
, const char *field
, struct berval
**values
)
2361 for (i
=0; values
[i
]; i
++) {
2364 if (!sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &sid
)) {
2367 printf("%s: %s\n", field
, sid_to_fstring(tmp
, &sid
));
2372 dump ntSecurityDescriptor
2374 static void dump_sd(ADS_STRUCT
*ads
, const char *filed
, struct berval
**values
)
2376 TALLOC_CTX
*frame
= talloc_stackframe();
2377 struct security_descriptor
*psd
;
2380 status
= unmarshall_sec_desc(talloc_tos(), (uint8_t *)values
[0]->bv_val
,
2381 values
[0]->bv_len
, &psd
);
2382 if (!NT_STATUS_IS_OK(status
)) {
2383 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2384 nt_errstr(status
)));
2390 ads_disp_sd(ads
, talloc_tos(), psd
);
2397 dump a string result from ldap
2399 static void dump_string(const char *field
, char **values
)
2402 for (i
=0; values
[i
]; i
++) {
2403 printf("%s: %s\n", field
, values
[i
]);
2408 dump a field from LDAP on stdout
2412 static bool ads_dump_field(ADS_STRUCT
*ads
, char *field
, void **values
, void *data_area
)
2417 void (*handler
)(ADS_STRUCT
*, const char *, struct berval
**);
2419 {"objectGUID", False
, dump_guid
},
2420 {"netbootGUID", False
, dump_guid
},
2421 {"nTSecurityDescriptor", False
, dump_sd
},
2422 {"dnsRecord", False
, dump_binary
},
2423 {"objectSid", False
, dump_sid
},
2424 {"tokenGroups", False
, dump_sid
},
2425 {"tokenGroupsNoGCAcceptable", False
, dump_sid
},
2426 {"tokengroupsGlobalandUniversal", False
, dump_sid
},
2427 {"mS-DS-CreatorSID", False
, dump_sid
},
2428 {"msExchMailboxGuid", False
, dump_guid
},
2433 if (!field
) { /* must be end of an entry */
2438 for (i
=0; handlers
[i
].name
; i
++) {
2439 if (strcasecmp_m(handlers
[i
].name
, field
) == 0) {
2440 if (!values
) /* first time, indicate string or not */
2441 return handlers
[i
].string
;
2442 handlers
[i
].handler(ads
, field
, (struct berval
**) values
);
2446 if (!handlers
[i
].name
) {
2447 if (!values
) /* first time, indicate string conversion */
2449 dump_string(field
, (char **)values
);
2455 * Dump a result from LDAP on stdout
2456 * used for debugging
2457 * @param ads connection to ads server
2458 * @param res Results to dump
2461 void ads_dump(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2463 ads_process_results(ads
, res
, ads_dump_field
, NULL
);
2467 * Walk through results, calling a function for each entry found.
2468 * The function receives a field name, a berval * array of values,
2469 * and a data area passed through from the start. The function is
2470 * called once with null for field and values at the end of each
2472 * @param ads connection to ads server
2473 * @param res Results to process
2474 * @param fn Function for processing each result
2475 * @param data_area user-defined area to pass to function
2477 void ads_process_results(ADS_STRUCT
*ads
, LDAPMessage
*res
,
2478 bool (*fn
)(ADS_STRUCT
*, char *, void **, void *),
2483 size_t converted_size
;
2485 if (!(ctx
= talloc_init("ads_process_results")))
2488 for (msg
= ads_first_entry(ads
, res
); msg
;
2489 msg
= ads_next_entry(ads
, msg
)) {
2493 for (utf8_field
=ldap_first_attribute(ads
->ldap
.ld
,
2494 (LDAPMessage
*)msg
,&b
);
2496 utf8_field
=ldap_next_attribute(ads
->ldap
.ld
,
2497 (LDAPMessage
*)msg
,b
)) {
2498 struct berval
**ber_vals
;
2504 if (!pull_utf8_talloc(ctx
, &field
, utf8_field
,
2507 DEBUG(0,("ads_process_results: "
2508 "pull_utf8_talloc failed: %s",
2512 string
= fn(ads
, field
, NULL
, data_area
);
2517 utf8_vals
= ldap_get_values(ads
->ldap
.ld
,
2518 (LDAPMessage
*)msg
, field
);
2519 p
= discard_const_p(const char *, utf8_vals
);
2520 str_vals
= ads_pull_strvals(ctx
, p
);
2521 fn(ads
, field
, (void **) str_vals
, data_area
);
2522 ldap_value_free(utf8_vals
);
2524 ber_vals
= ldap_get_values_len(ads
->ldap
.ld
,
2525 (LDAPMessage
*)msg
, field
);
2526 fn(ads
, field
, (void **) ber_vals
, data_area
);
2528 ldap_value_free_len(ber_vals
);
2530 ldap_memfree(utf8_field
);
2533 talloc_free_children(ctx
);
2534 fn(ads
, NULL
, NULL
, data_area
); /* completed an entry */
2537 talloc_destroy(ctx
);
2541 * count how many replies are in a LDAPMessage
2542 * @param ads connection to ads server
2543 * @param res Results to count
2544 * @return number of replies
2546 int ads_count_replies(ADS_STRUCT
*ads
, void *res
)
2548 return ldap_count_entries(ads
->ldap
.ld
, (LDAPMessage
*)res
);
2552 * pull the first entry from a ADS result
2553 * @param ads connection to ads server
2554 * @param res Results of search
2555 * @return first entry from result
2557 LDAPMessage
*ads_first_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2559 return ldap_first_entry(ads
->ldap
.ld
, res
);
2563 * pull the next entry from a ADS result
2564 * @param ads connection to ads server
2565 * @param res Results of search
2566 * @return next entry from result
2568 LDAPMessage
*ads_next_entry(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2570 return ldap_next_entry(ads
->ldap
.ld
, res
);
2574 * pull the first message from a ADS result
2575 * @param ads connection to ads server
2576 * @param res Results of search
2577 * @return first message from result
2579 LDAPMessage
*ads_first_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2581 return ldap_first_message(ads
->ldap
.ld
, res
);
2585 * pull the next message from a ADS result
2586 * @param ads connection to ads server
2587 * @param res Results of search
2588 * @return next message from result
2590 LDAPMessage
*ads_next_message(ADS_STRUCT
*ads
, LDAPMessage
*res
)
2592 return ldap_next_message(ads
->ldap
.ld
, res
);
2596 * pull a single string from a ADS result
2597 * @param ads connection to ads server
2598 * @param mem_ctx TALLOC_CTX to use for allocating result string
2599 * @param msg Results of search
2600 * @param field Attribute to retrieve
2601 * @return Result string in talloc context
2603 char *ads_pull_string(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, LDAPMessage
*msg
,
2609 size_t converted_size
;
2611 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2615 if (values
[0] && pull_utf8_talloc(mem_ctx
, &ux_string
, values
[0],
2620 ldap_value_free(values
);
2625 * pull an array of strings from a ADS result
2626 * @param ads connection to ads server
2627 * @param mem_ctx TALLOC_CTX to use for allocating result string
2628 * @param msg Results of search
2629 * @param field Attribute to retrieve
2630 * @return Result strings in talloc context
2632 char **ads_pull_strings(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2633 LDAPMessage
*msg
, const char *field
,
2639 size_t converted_size
;
2641 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2645 *num_values
= ldap_count_values(values
);
2647 ret
= talloc_array(mem_ctx
, char *, *num_values
+ 1);
2649 ldap_value_free(values
);
2653 for (i
=0;i
<*num_values
;i
++) {
2654 if (!pull_utf8_talloc(mem_ctx
, &ret
[i
], values
[i
],
2657 ldap_value_free(values
);
2663 ldap_value_free(values
);
2668 * pull an array of strings from a ADS result
2669 * (handle large multivalue attributes with range retrieval)
2670 * @param ads connection to ads server
2671 * @param mem_ctx TALLOC_CTX to use for allocating result string
2672 * @param msg Results of search
2673 * @param field Attribute to retrieve
2674 * @param current_strings strings returned by a previous call to this function
2675 * @param next_attribute The next query should ask for this attribute
2676 * @param num_values How many values did we get this time?
2677 * @param more_values Are there more values to get?
2678 * @return Result strings in talloc context
2680 char **ads_pull_strings_range(ADS_STRUCT
*ads
,
2681 TALLOC_CTX
*mem_ctx
,
2682 LDAPMessage
*msg
, const char *field
,
2683 char **current_strings
,
2684 const char **next_attribute
,
2685 size_t *num_strings
,
2689 char *expected_range_attrib
, *range_attr
;
2690 BerElement
*ptr
= NULL
;
2693 size_t num_new_strings
;
2694 unsigned long int range_start
;
2695 unsigned long int range_end
;
2697 /* we might have been given the whole lot anyway */
2698 if ((strings
= ads_pull_strings(ads
, mem_ctx
, msg
, field
, num_strings
))) {
2699 *more_strings
= False
;
2703 expected_range_attrib
= talloc_asprintf(mem_ctx
, "%s;Range=", field
);
2705 /* look for Range result */
2706 for (attr
= ldap_first_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, &ptr
);
2708 attr
= ldap_next_attribute(ads
->ldap
.ld
, (LDAPMessage
*)msg
, ptr
)) {
2709 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2710 if (strnequal(attr
, expected_range_attrib
, strlen(expected_range_attrib
))) {
2718 /* nothing here - this field is just empty */
2719 *more_strings
= False
;
2723 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-%lu",
2724 &range_start
, &range_end
) == 2) {
2725 *more_strings
= True
;
2727 if (sscanf(&range_attr
[strlen(expected_range_attrib
)], "%lu-*",
2728 &range_start
) == 1) {
2729 *more_strings
= False
;
2731 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2733 ldap_memfree(range_attr
);
2734 *more_strings
= False
;
2739 if ((*num_strings
) != range_start
) {
2740 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2741 " - aborting range retreival\n",
2742 range_attr
, (unsigned int)(*num_strings
) + 1, range_start
));
2743 ldap_memfree(range_attr
);
2744 *more_strings
= False
;
2748 new_strings
= ads_pull_strings(ads
, mem_ctx
, msg
, range_attr
, &num_new_strings
);
2750 if (*more_strings
&& ((*num_strings
+ num_new_strings
) != (range_end
+ 1))) {
2751 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2752 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2753 range_attr
, (unsigned long int)range_end
- range_start
+ 1,
2754 (unsigned long int)num_new_strings
));
2755 ldap_memfree(range_attr
);
2756 *more_strings
= False
;
2760 strings
= talloc_realloc(mem_ctx
, current_strings
, char *,
2761 *num_strings
+ num_new_strings
);
2763 if (strings
== NULL
) {
2764 ldap_memfree(range_attr
);
2765 *more_strings
= False
;
2769 if (new_strings
&& num_new_strings
) {
2770 memcpy(&strings
[*num_strings
], new_strings
,
2771 sizeof(*new_strings
) * num_new_strings
);
2774 (*num_strings
) += num_new_strings
;
2776 if (*more_strings
) {
2777 *next_attribute
= talloc_asprintf(mem_ctx
,
2782 if (!*next_attribute
) {
2783 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2784 ldap_memfree(range_attr
);
2785 *more_strings
= False
;
2790 ldap_memfree(range_attr
);
2796 * pull a single uint32_t from a ADS result
2797 * @param ads connection to ads server
2798 * @param msg Results of search
2799 * @param field Attribute to retrieve
2800 * @param v Pointer to int to store result
2801 * @return boolean inidicating success
2803 bool ads_pull_uint32(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2808 values
= ldap_get_values(ads
->ldap
.ld
, msg
, field
);
2812 ldap_value_free(values
);
2816 *v
= atoi(values
[0]);
2817 ldap_value_free(values
);
2822 * pull a single objectGUID from an ADS result
2823 * @param ads connection to ADS server
2824 * @param msg results of search
2825 * @param guid 37-byte area to receive text guid
2826 * @return boolean indicating success
2828 bool ads_pull_guid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, struct GUID
*guid
)
2833 if (!smbldap_talloc_single_blob(talloc_tos(), ads
->ldap
.ld
, msg
, "objectGUID",
2838 status
= GUID_from_ndr_blob(&blob
, guid
);
2839 talloc_free(blob
.data
);
2840 return NT_STATUS_IS_OK(status
);
2845 * pull a single struct dom_sid from a ADS result
2846 * @param ads connection to ads server
2847 * @param msg Results of search
2848 * @param field Attribute to retrieve
2849 * @param sid Pointer to sid to store result
2850 * @return boolean inidicating success
2852 bool ads_pull_sid(ADS_STRUCT
*ads
, LDAPMessage
*msg
, const char *field
,
2853 struct dom_sid
*sid
)
2855 return smbldap_pull_sid(ads
->ldap
.ld
, msg
, field
, sid
);
2859 * pull an array of struct dom_sids from a ADS result
2860 * @param ads connection to ads server
2861 * @param mem_ctx TALLOC_CTX for allocating sid array
2862 * @param msg Results of search
2863 * @param field Attribute to retrieve
2864 * @param sids pointer to sid array to allocate
2865 * @return the count of SIDs pulled
2867 int ads_pull_sids(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2868 LDAPMessage
*msg
, const char *field
, struct dom_sid
**sids
)
2870 struct berval
**values
;
2874 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2879 for (i
=0; values
[i
]; i
++)
2883 (*sids
) = talloc_array(mem_ctx
, struct dom_sid
, i
);
2885 ldap_value_free_len(values
);
2893 for (i
=0; values
[i
]; i
++) {
2894 ret
= sid_parse(values
[i
]->bv_val
, values
[i
]->bv_len
, &(*sids
)[count
]);
2896 DEBUG(10, ("pulling SID: %s\n",
2897 sid_string_dbg(&(*sids
)[count
])));
2902 ldap_value_free_len(values
);
2907 * pull a struct security_descriptor from a ADS result
2908 * @param ads connection to ads server
2909 * @param mem_ctx TALLOC_CTX for allocating sid array
2910 * @param msg Results of search
2911 * @param field Attribute to retrieve
2912 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2913 * @return boolean inidicating success
2915 bool ads_pull_sd(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2916 LDAPMessage
*msg
, const char *field
,
2917 struct security_descriptor
**sd
)
2919 struct berval
**values
;
2922 values
= ldap_get_values_len(ads
->ldap
.ld
, msg
, field
);
2924 if (!values
) return false;
2928 status
= unmarshall_sec_desc(mem_ctx
,
2929 (uint8_t *)values
[0]->bv_val
,
2930 values
[0]->bv_len
, sd
);
2931 if (!NT_STATUS_IS_OK(status
)) {
2932 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2933 nt_errstr(status
)));
2938 ldap_value_free_len(values
);
2943 * in order to support usernames longer than 21 characters we need to
2944 * use both the sAMAccountName and the userPrincipalName attributes
2945 * It seems that not all users have the userPrincipalName attribute set
2947 * @param ads connection to ads server
2948 * @param mem_ctx TALLOC_CTX for allocating sid array
2949 * @param msg Results of search
2950 * @return the username
2952 char *ads_pull_username(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
,
2958 /* lookup_name() only works on the sAMAccountName to
2959 returning the username portion of userPrincipalName
2960 breaks winbindd_getpwnam() */
2962 ret
= ads_pull_string(ads
, mem_ctx
, msg
, "userPrincipalName");
2963 if (ret
&& (p
= strchr_m(ret
, '@'))) {
2968 return ads_pull_string(ads
, mem_ctx
, msg
, "sAMAccountName");
2973 * find the update serial number - this is the core of the ldap cache
2974 * @param ads connection to ads server
2975 * @param ads connection to ADS server
2976 * @param usn Pointer to retrieved update serial number
2977 * @return status of search
2979 ADS_STATUS
ads_USN(ADS_STRUCT
*ads
, uint32_t *usn
)
2981 const char *attrs
[] = {"highestCommittedUSN", NULL
};
2985 status
= ads_do_search_retry(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
2986 if (!ADS_ERR_OK(status
))
2989 if (ads_count_replies(ads
, res
) != 1) {
2990 ads_msgfree(ads
, res
);
2991 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
2994 if (!ads_pull_uint32(ads
, res
, "highestCommittedUSN", usn
)) {
2995 ads_msgfree(ads
, res
);
2996 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
2999 ads_msgfree(ads
, res
);
3003 /* parse a ADS timestring - typical string is
3004 '20020917091222.0Z0' which means 09:12.22 17th September
3006 static time_t ads_parse_time(const char *str
)
3012 if (sscanf(str
, "%4d%2d%2d%2d%2d%2d",
3013 &tm
.tm_year
, &tm
.tm_mon
, &tm
.tm_mday
,
3014 &tm
.tm_hour
, &tm
.tm_min
, &tm
.tm_sec
) != 6) {
3023 /********************************************************************
3024 ********************************************************************/
3026 ADS_STATUS
ads_current_time(ADS_STRUCT
*ads
)
3028 const char *attrs
[] = {"currentTime", NULL
};
3033 ADS_STRUCT
*ads_s
= ads
;
3035 if (!(ctx
= talloc_init("ads_current_time"))) {
3036 return ADS_ERROR(LDAP_NO_MEMORY
);
3039 /* establish a new ldap tcp session if necessary */
3041 if ( !ads
->ldap
.ld
) {
3042 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
3043 ads
->server
.ldap_server
)) == NULL
)
3047 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
3048 status
= ads_connect( ads_s
);
3049 if ( !ADS_ERR_OK(status
))
3053 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3054 if (!ADS_ERR_OK(status
)) {
3058 timestr
= ads_pull_string(ads_s
, ctx
, res
, "currentTime");
3060 ads_msgfree(ads_s
, res
);
3061 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3065 /* but save the time and offset in the original ADS_STRUCT */
3067 ads
->config
.current_time
= ads_parse_time(timestr
);
3069 if (ads
->config
.current_time
!= 0) {
3070 ads
->auth
.time_offset
= ads
->config
.current_time
- time(NULL
);
3071 DEBUG(4,("KDC time offset is %d seconds\n", ads
->auth
.time_offset
));
3074 ads_msgfree(ads
, res
);
3076 status
= ADS_SUCCESS
;
3079 /* free any temporary ads connections */
3080 if ( ads_s
!= ads
) {
3081 ads_destroy( &ads_s
);
3083 talloc_destroy(ctx
);
3088 /********************************************************************
3089 ********************************************************************/
3091 ADS_STATUS
ads_domain_func_level(ADS_STRUCT
*ads
, uint32_t *val
)
3093 const char *attrs
[] = {"domainFunctionality", NULL
};
3096 ADS_STRUCT
*ads_s
= ads
;
3098 *val
= DS_DOMAIN_FUNCTION_2000
;
3100 /* establish a new ldap tcp session if necessary */
3102 if ( !ads
->ldap
.ld
) {
3103 if ( (ads_s
= ads_init( ads
->server
.realm
, ads
->server
.workgroup
,
3104 ads
->server
.ldap_server
)) == NULL
)
3106 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3109 ads_s
->auth
.flags
= ADS_AUTH_ANON_BIND
;
3110 status
= ads_connect( ads_s
);
3111 if ( !ADS_ERR_OK(status
))
3115 /* If the attribute does not exist assume it is a Windows 2000
3116 functional domain */
3118 status
= ads_do_search(ads_s
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3119 if (!ADS_ERR_OK(status
)) {
3120 if ( status
.err
.rc
== LDAP_NO_SUCH_ATTRIBUTE
) {
3121 status
= ADS_SUCCESS
;
3126 if ( !ads_pull_uint32(ads_s
, res
, "domainFunctionality", val
) ) {
3127 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3129 DEBUG(3,("ads_domain_func_level: %d\n", *val
));
3132 ads_msgfree(ads
, res
);
3135 /* free any temporary ads connections */
3136 if ( ads_s
!= ads
) {
3137 ads_destroy( &ads_s
);
3144 * find the domain sid for our domain
3145 * @param ads connection to ads server
3146 * @param sid Pointer to domain sid
3147 * @return status of search
3149 ADS_STATUS
ads_domain_sid(ADS_STRUCT
*ads
, struct dom_sid
*sid
)
3151 const char *attrs
[] = {"objectSid", NULL
};
3155 rc
= ads_do_search_retry(ads
, ads
->config
.bind_path
, LDAP_SCOPE_BASE
, "(objectclass=*)",
3157 if (!ADS_ERR_OK(rc
)) return rc
;
3158 if (!ads_pull_sid(ads
, res
, "objectSid", sid
)) {
3159 ads_msgfree(ads
, res
);
3160 return ADS_ERROR_SYSTEM(ENOENT
);
3162 ads_msgfree(ads
, res
);
3168 * find our site name
3169 * @param ads connection to ads server
3170 * @param mem_ctx Pointer to talloc context
3171 * @param site_name Pointer to the sitename
3172 * @return status of search
3174 ADS_STATUS
ads_site_dn(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char **site_name
)
3178 const char *dn
, *service_name
;
3179 const char *attrs
[] = { "dsServiceName", NULL
};
3181 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
, "(objectclass=*)", attrs
, &res
);
3182 if (!ADS_ERR_OK(status
)) {
3186 service_name
= ads_pull_string(ads
, mem_ctx
, res
, "dsServiceName");
3187 if (service_name
== NULL
) {
3188 ads_msgfree(ads
, res
);
3189 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3192 ads_msgfree(ads
, res
);
3194 /* go up three levels */
3195 dn
= ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name
)));
3197 return ADS_ERROR(LDAP_NO_MEMORY
);
3200 *site_name
= talloc_strdup(mem_ctx
, dn
);
3201 if (*site_name
== NULL
) {
3202 return ADS_ERROR(LDAP_NO_MEMORY
);
3207 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3212 * find the site dn where a machine resides
3213 * @param ads connection to ads server
3214 * @param mem_ctx Pointer to talloc context
3215 * @param computer_name name of the machine
3216 * @param site_name Pointer to the sitename
3217 * @return status of search
3219 ADS_STATUS
ads_site_dn_for_machine(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, const char *computer_name
, const char **site_dn
)
3223 const char *parent
, *filter
;
3224 char *config_context
= NULL
;
3227 /* shortcut a query */
3228 if (strequal(computer_name
, ads
->config
.ldap_server_name
)) {
3229 return ads_site_dn(ads
, mem_ctx
, site_dn
);
3232 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3233 if (!ADS_ERR_OK(status
)) {
3237 filter
= talloc_asprintf(mem_ctx
, "(cn=%s)", computer_name
);
3238 if (filter
== NULL
) {
3239 return ADS_ERROR(LDAP_NO_MEMORY
);
3242 status
= ads_do_search(ads
, config_context
, LDAP_SCOPE_SUBTREE
,
3243 filter
, NULL
, &res
);
3244 if (!ADS_ERR_OK(status
)) {
3248 if (ads_count_replies(ads
, res
) != 1) {
3249 ads_msgfree(ads
, res
);
3250 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3253 dn
= ads_get_dn(ads
, mem_ctx
, res
);
3255 ads_msgfree(ads
, res
);
3256 return ADS_ERROR(LDAP_NO_MEMORY
);
3259 /* go up three levels */
3260 parent
= ads_parent_dn(ads_parent_dn(ads_parent_dn(dn
)));
3261 if (parent
== NULL
) {
3262 ads_msgfree(ads
, res
);
3264 return ADS_ERROR(LDAP_NO_MEMORY
);
3267 *site_dn
= talloc_strdup(mem_ctx
, parent
);
3268 if (*site_dn
== NULL
) {
3269 ads_msgfree(ads
, res
);
3271 return ADS_ERROR(LDAP_NO_MEMORY
);
3275 ads_msgfree(ads
, res
);
3281 * get the upn suffixes for a domain
3282 * @param ads connection to ads server
3283 * @param mem_ctx Pointer to talloc context
3284 * @param suffixes Pointer to an array of suffixes
3285 * @param num_suffixes Pointer to the number of suffixes
3286 * @return status of search
3288 ADS_STATUS
ads_upn_suffixes(ADS_STRUCT
*ads
, TALLOC_CTX
*mem_ctx
, char ***suffixes
, size_t *num_suffixes
)
3293 char *config_context
= NULL
;
3294 const char *attrs
[] = { "uPNSuffixes", NULL
};
3296 status
= ads_config_path(ads
, mem_ctx
, &config_context
);
3297 if (!ADS_ERR_OK(status
)) {
3301 base
= talloc_asprintf(mem_ctx
, "cn=Partitions,%s", config_context
);
3303 return ADS_ERROR(LDAP_NO_MEMORY
);
3306 status
= ads_search_dn(ads
, &res
, base
, attrs
);
3307 if (!ADS_ERR_OK(status
)) {
3311 if (ads_count_replies(ads
, res
) != 1) {
3312 ads_msgfree(ads
, res
);
3313 return ADS_ERROR(LDAP_NO_SUCH_OBJECT
);
3316 (*suffixes
) = ads_pull_strings(ads
, mem_ctx
, res
, "uPNSuffixes", num_suffixes
);
3317 if ((*suffixes
) == NULL
) {
3318 ads_msgfree(ads
, res
);
3319 return ADS_ERROR(LDAP_NO_MEMORY
);
3322 ads_msgfree(ads
, res
);
3328 * get the joinable ous for a domain
3329 * @param ads connection to ads server
3330 * @param mem_ctx Pointer to talloc context
3331 * @param ous Pointer to an array of ous
3332 * @param num_ous Pointer to the number of ous
3333 * @return status of search
3335 ADS_STATUS
ads_get_joinable_ous(ADS_STRUCT
*ads
,
3336 TALLOC_CTX
*mem_ctx
,
3341 LDAPMessage
*res
= NULL
;
3342 LDAPMessage
*msg
= NULL
;
3343 const char *attrs
[] = { "dn", NULL
};
3346 status
= ads_search(ads
, &res
,
3347 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3349 if (!ADS_ERR_OK(status
)) {
3353 count
= ads_count_replies(ads
, res
);
3355 ads_msgfree(ads
, res
);
3356 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3359 for (msg
= ads_first_entry(ads
, res
); msg
;
3360 msg
= ads_next_entry(ads
, msg
)) {
3361 const char **p
= discard_const_p(const char *, *ous
);
3364 dn
= ads_get_dn(ads
, talloc_tos(), msg
);
3366 ads_msgfree(ads
, res
);
3367 return ADS_ERROR(LDAP_NO_MEMORY
);
3370 if (!add_string_to_array(mem_ctx
, dn
, &p
, num_ous
)) {
3372 ads_msgfree(ads
, res
);
3373 return ADS_ERROR(LDAP_NO_MEMORY
);
3377 *ous
= discard_const_p(char *, p
);
3380 ads_msgfree(ads
, res
);
3387 * pull a struct dom_sid from an extended dn string
3388 * @param mem_ctx TALLOC_CTX
3389 * @param extended_dn string
3390 * @param flags string type of extended_dn
3391 * @param sid pointer to a struct dom_sid
3392 * @return NT_STATUS_OK on success,
3393 * NT_INVALID_PARAMETER on error,
3394 * NT_STATUS_NOT_FOUND if no SID present
3396 ADS_STATUS
ads_get_sid_from_extended_dn(TALLOC_CTX
*mem_ctx
,
3397 const char *extended_dn
,
3398 enum ads_extended_dn_flags flags
,
3399 struct dom_sid
*sid
)
3404 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3407 /* otherwise extended_dn gets stripped off */
3408 if ((dn
= talloc_strdup(mem_ctx
, extended_dn
)) == NULL
) {
3409 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3412 * ADS_EXTENDED_DN_HEX_STRING:
3413 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3415 * ADS_EXTENDED_DN_STRING (only with w2k3):
3416 * <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
3418 * Object with no SID, such as an Exchange Public Folder
3419 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3422 p
= strchr(dn
, ';');
3424 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3427 if (strncmp(p
, ";<SID=", strlen(";<SID=")) != 0) {
3428 DEBUG(5,("No SID present in extended dn\n"));
3429 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND
);
3432 p
+= strlen(";<SID=");
3436 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3441 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p
));
3445 case ADS_EXTENDED_DN_STRING
:
3446 if (!string_to_sid(sid
, p
)) {
3447 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3450 case ADS_EXTENDED_DN_HEX_STRING
: {
3454 buf_len
= strhex_to_str(buf
, sizeof(buf
), p
, strlen(p
));
3456 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3459 if (!sid_parse(buf
, buf_len
, sid
)) {
3460 DEBUG(10,("failed to parse sid\n"));
3461 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3466 DEBUG(10,("unknown extended dn format\n"));
3467 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER
);
3470 return ADS_ERROR_NT(NT_STATUS_OK
);
3473 /********************************************************************
3474 ********************************************************************/
3476 char* ads_get_dnshostname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3478 LDAPMessage
*res
= NULL
;
3483 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3484 if (!ADS_ERR_OK(status
)) {
3485 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3486 lp_netbios_name()));
3490 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3491 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3495 if ( (name
= ads_pull_string(ads
, ctx
, res
, "dNSHostName")) == NULL
) {
3496 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3500 ads_msgfree(ads
, res
);
3505 /********************************************************************
3506 ********************************************************************/
3508 char* ads_get_upn( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3510 LDAPMessage
*res
= NULL
;
3515 status
= ads_find_machine_acct(ads
, &res
, machine_name
);
3516 if (!ADS_ERR_OK(status
)) {
3517 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3518 lp_netbios_name()));
3522 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3523 DEBUG(1,("ads_get_upn: %d entries returned!\n", count
));
3527 if ( (name
= ads_pull_string(ads
, ctx
, res
, "userPrincipalName")) == NULL
) {
3528 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3532 ads_msgfree(ads
, res
);
3537 /********************************************************************
3538 ********************************************************************/
3540 char* ads_get_samaccountname( ADS_STRUCT
*ads
, TALLOC_CTX
*ctx
, const char *machine_name
)
3542 LDAPMessage
*res
= NULL
;
3547 status
= ads_find_machine_acct(ads
, &res
, lp_netbios_name());
3548 if (!ADS_ERR_OK(status
)) {
3549 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3550 lp_netbios_name()));
3554 if ( (count
= ads_count_replies(ads
, res
)) != 1 ) {
3555 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count
));
3559 if ( (name
= ads_pull_string(ads
, ctx
, res
, "sAMAccountName")) == NULL
) {
3560 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3564 ads_msgfree(ads
, res
);
3571 SAVED CODE
- we used to join via ldap
- remember how we did
this. JRA
.
3574 * Join a machine to a realm
3575 * Creates the machine account and sets the machine password
3576 * @param ads connection to ads server
3577 * @param machine name of host to add
3578 * @param org_unit Organizational unit to place machine in
3579 * @return status of join
3581 ADS_STATUS
ads_join_realm(ADS_STRUCT
*ads
, const char *machine_name
,
3582 uint32_t account_type
, const char *org_unit
)
3585 LDAPMessage
*res
= NULL
;
3588 /* machine name must be lowercase */
3589 machine
= SMB_STRDUP(machine_name
);
3590 strlower_m(machine
);
3593 status = ads_find_machine_acct(ads, (void **)&res, machine);
3594 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3595 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3596 status = ads_leave_realm(ads, machine);
3597 if (!ADS_ERR_OK(status)) {
3598 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3599 machine, ads->config.realm));
3604 status
= ads_add_machine_acct(ads
, machine
, account_type
, org_unit
);
3605 if (!ADS_ERR_OK(status
)) {
3606 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine
, ads_errstr(status
)));
3611 status
= ads_find_machine_acct(ads
, (void **)(void *)&res
, machine
);
3612 if (!ADS_ERR_OK(status
)) {
3613 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine
));
3619 ads_msgfree(ads
, res
);
3626 * Delete a machine from the realm
3627 * @param ads connection to ads server
3628 * @param hostname Machine to remove
3629 * @return status of delete
3631 ADS_STATUS
ads_leave_realm(ADS_STRUCT
*ads
, const char *hostname
)
3636 char *hostnameDN
, *host
;
3638 LDAPControl ldap_control
;
3639 LDAPControl
* pldap_control
[2] = {NULL
, NULL
};
3641 pldap_control
[0] = &ldap_control
;
3642 memset(&ldap_control
, 0, sizeof(LDAPControl
));
3643 ldap_control
.ldctl_oid
= discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID
);
3645 /* hostname must be lowercase */
3646 host
= SMB_STRDUP(hostname
);
3647 if (!strlower_m(host
)) {
3649 return ADS_ERROR_SYSTEM(EINVAL
);
3652 status
= ads_find_machine_acct(ads
, &res
, host
);
3653 if (!ADS_ERR_OK(status
)) {
3654 DEBUG(0, ("Host account for %s does not exist.\n", host
));
3659 msg
= ads_first_entry(ads
, res
);
3662 return ADS_ERROR_SYSTEM(ENOENT
);
3665 hostnameDN
= ads_get_dn(ads
, talloc_tos(), (LDAPMessage
*)msg
);
3666 if (hostnameDN
== NULL
) {
3668 return ADS_ERROR_SYSTEM(ENOENT
);
3671 rc
= ldap_delete_ext_s(ads
->ldap
.ld
, hostnameDN
, pldap_control
, NULL
);
3673 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc
));
3675 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc
));
3678 if (rc
!= LDAP_SUCCESS
) {
3679 const char *attrs
[] = { "cn", NULL
};
3680 LDAPMessage
*msg_sub
;
3682 /* we only search with scope ONE, we do not expect any further
3683 * objects to be created deeper */
3685 status
= ads_do_search_retry(ads
, hostnameDN
,
3686 LDAP_SCOPE_ONELEVEL
,
3687 "(objectclass=*)", attrs
, &res
);
3689 if (!ADS_ERR_OK(status
)) {
3691 TALLOC_FREE(hostnameDN
);
3695 for (msg_sub
= ads_first_entry(ads
, res
); msg_sub
;
3696 msg_sub
= ads_next_entry(ads
, msg_sub
)) {
3700 if ((dn
= ads_get_dn(ads
, talloc_tos(), msg_sub
)) == NULL
) {
3702 TALLOC_FREE(hostnameDN
);
3703 return ADS_ERROR(LDAP_NO_MEMORY
);
3706 status
= ads_del_dn(ads
, dn
);
3707 if (!ADS_ERR_OK(status
)) {
3708 DEBUG(3,("failed to delete dn %s: %s\n", dn
, ads_errstr(status
)));
3711 TALLOC_FREE(hostnameDN
);
3718 /* there should be no subordinate objects anymore */
3719 status
= ads_do_search_retry(ads
, hostnameDN
,
3720 LDAP_SCOPE_ONELEVEL
,
3721 "(objectclass=*)", attrs
, &res
);
3723 if (!ADS_ERR_OK(status
) || ( (ads_count_replies(ads
, res
)) > 0 ) ) {
3725 TALLOC_FREE(hostnameDN
);
3729 /* delete hostnameDN now */
3730 status
= ads_del_dn(ads
, hostnameDN
);
3731 if (!ADS_ERR_OK(status
)) {
3733 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN
, ads_errstr(status
)));
3734 TALLOC_FREE(hostnameDN
);
3739 TALLOC_FREE(hostnameDN
);
3741 status
= ads_find_machine_acct(ads
, &res
, host
);
3742 if (ADS_ERR_OK(status
) && ads_count_replies(ads
, res
) == 1) {
3743 DEBUG(3, ("Failed to remove host account.\n"));
3753 * pull all token-sids from an LDAP dn
3754 * @param ads connection to ads server
3755 * @param mem_ctx TALLOC_CTX for allocating sid array
3756 * @param dn of LDAP object
3757 * @param user_sid pointer to struct dom_sid (objectSid)
3758 * @param primary_group_sid pointer to struct dom_sid (self composed)
3759 * @param sids pointer to sid array to allocate
3760 * @param num_sids counter of SIDs pulled
3761 * @return status of token query
3763 ADS_STATUS
ads_get_tokensids(ADS_STRUCT
*ads
,
3764 TALLOC_CTX
*mem_ctx
,
3766 struct dom_sid
*user_sid
,
3767 struct dom_sid
*primary_group_sid
,
3768 struct dom_sid
**sids
,
3772 LDAPMessage
*res
= NULL
;
3774 size_t tmp_num_sids
;
3775 struct dom_sid
*tmp_sids
;
3776 struct dom_sid tmp_user_sid
;
3777 struct dom_sid tmp_primary_group_sid
;
3779 const char *attrs
[] = {
3786 status
= ads_search_retry_dn(ads
, &res
, dn
, attrs
);
3787 if (!ADS_ERR_OK(status
)) {
3791 count
= ads_count_replies(ads
, res
);
3793 ads_msgfree(ads
, res
);
3794 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT
);
3797 if (!ads_pull_sid(ads
, res
, "objectSid", &tmp_user_sid
)) {
3798 ads_msgfree(ads
, res
);
3799 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3802 if (!ads_pull_uint32(ads
, res
, "primaryGroupID", &pgid
)) {
3803 ads_msgfree(ads
, res
);
3804 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3808 /* hack to compose the primary group sid without knowing the
3811 struct dom_sid domsid
;
3813 sid_copy(&domsid
, &tmp_user_sid
);
3815 if (!sid_split_rid(&domsid
, NULL
)) {
3816 ads_msgfree(ads
, res
);
3817 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3820 if (!sid_compose(&tmp_primary_group_sid
, &domsid
, pgid
)) {
3821 ads_msgfree(ads
, res
);
3822 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3826 tmp_num_sids
= ads_pull_sids(ads
, mem_ctx
, res
, "tokenGroups", &tmp_sids
);
3828 if (tmp_num_sids
== 0 || !tmp_sids
) {
3829 ads_msgfree(ads
, res
);
3830 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
3834 *num_sids
= tmp_num_sids
;
3842 *user_sid
= tmp_user_sid
;
3845 if (primary_group_sid
) {
3846 *primary_group_sid
= tmp_primary_group_sid
;
3849 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids
+ 2));
3851 ads_msgfree(ads
, res
);
3852 return ADS_ERROR_LDAP(LDAP_SUCCESS
);
3856 * Find a sAMAccoutName in LDAP
3857 * @param ads connection to ads server
3858 * @param mem_ctx TALLOC_CTX for allocating sid array
3859 * @param samaccountname to search
3860 * @param uac_ret uint32_t pointer userAccountControl attribute value
3861 * @param dn_ret pointer to dn
3862 * @return status of token query
3864 ADS_STATUS
ads_find_samaccount(ADS_STRUCT
*ads
,
3865 TALLOC_CTX
*mem_ctx
,
3866 const char *samaccountname
,
3868 const char **dn_ret
)
3871 const char *attrs
[] = { "userAccountControl", NULL
};
3873 LDAPMessage
*res
= NULL
;
3877 filter
= talloc_asprintf(mem_ctx
, "(&(objectclass=user)(sAMAccountName=%s))",
3879 if (filter
== NULL
) {
3880 status
= ADS_ERROR_NT(NT_STATUS_NO_MEMORY
);
3884 status
= ads_do_search_all(ads
, ads
->config
.bind_path
,
3886 filter
, attrs
, &res
);
3888 if (!ADS_ERR_OK(status
)) {
3892 if (ads_count_replies(ads
, res
) != 1) {
3893 status
= ADS_ERROR(LDAP_NO_RESULTS_RETURNED
);
3897 dn
= ads_get_dn(ads
, talloc_tos(), res
);
3899 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3903 if (!ads_pull_uint32(ads
, res
, "userAccountControl", &uac
)) {
3904 status
= ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE
);
3913 *dn_ret
= talloc_strdup(mem_ctx
, dn
);
3915 status
= ADS_ERROR(LDAP_NO_MEMORY
);
3921 ads_msgfree(ads
, res
);
3927 * find our configuration path
3928 * @param ads connection to ads server
3929 * @param mem_ctx Pointer to talloc context
3930 * @param config_path Pointer to the config path
3931 * @return status of search
3933 ADS_STATUS
ads_config_path(ADS_STRUCT
*ads
,
3934 TALLOC_CTX
*mem_ctx
,
3938 LDAPMessage
*res
= NULL
;
3939 const char *config_context
= NULL
;
3940 const char *attrs
[] = { "configurationNamingContext", NULL
};
3942 status
= ads_do_search(ads
, "", LDAP_SCOPE_BASE
,
3943 "(objectclass=*)", attrs
, &res
);
3944 if (!ADS_ERR_OK(status
)) {
3948 config_context
= ads_pull_string(ads
, mem_ctx
, res
,
3949 "configurationNamingContext");
3950 ads_msgfree(ads
, res
);
3951 if (!config_context
) {
3952 return ADS_ERROR(LDAP_NO_MEMORY
);
3956 *config_path
= talloc_strdup(mem_ctx
, config_context
);
3957 if (!*config_path
) {
3958 return ADS_ERROR(LDAP_NO_MEMORY
);
3962 return ADS_ERROR(LDAP_SUCCESS
);
3966 * find the displayName of an extended right
3967 * @param ads connection to ads server
3968 * @param config_path The config path
3969 * @param mem_ctx Pointer to talloc context
3970 * @param GUID struct of the rightsGUID
3971 * @return status of search
3973 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT
*ads
,
3974 const char *config_path
,
3975 TALLOC_CTX
*mem_ctx
,
3976 const struct GUID
*rights_guid
)
3979 LDAPMessage
*res
= NULL
;
3981 const char *attrs
[] = { "displayName", NULL
};
3982 const char *result
= NULL
;
3985 if (!ads
|| !mem_ctx
|| !rights_guid
) {
3989 expr
= talloc_asprintf(mem_ctx
, "(rightsGuid=%s)",
3990 GUID_string(mem_ctx
, rights_guid
));
3995 path
= talloc_asprintf(mem_ctx
, "cn=Extended-Rights,%s", config_path
);
4000 rc
= ads_do_search_retry(ads
, path
, LDAP_SCOPE_SUBTREE
,
4002 if (!ADS_ERR_OK(rc
)) {
4006 if (ads_count_replies(ads
, res
) != 1) {
4010 result
= ads_pull_string(ads
, mem_ctx
, res
, "displayName");
4013 ads_msgfree(ads
, res
);
4018 * verify or build and verify an account ou
4019 * @param mem_ctx Pointer to talloc context
4020 * @param ads connection to ads server
4022 * @return status of search
4025 ADS_STATUS
ads_check_ou_dn(TALLOC_CTX
*mem_ctx
,
4027 const char **account_ou
)
4033 exploded_dn
= ldap_explode_dn(*account_ou
, 0);
4035 ldap_value_free(exploded_dn
);
4039 ou_string
= ads_ou_string(ads
, *account_ou
);
4041 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4044 name
= talloc_asprintf(mem_ctx
, "%s,%s", ou_string
,
4045 ads
->config
.bind_path
);
4046 SAFE_FREE(ou_string
);
4049 return ADS_ERROR_LDAP(LDAP_NO_MEMORY
);
4052 exploded_dn
= ldap_explode_dn(name
, 0);
4054 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX
);
4056 ldap_value_free(exploded_dn
);