s3/docs: Fix typos.
[Samba.git] / source / libads / ldap.c
blobd8a117888eaefe4a0cce045f74f034f183a5b6e2
1 /*
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/>.
24 #include "includes.h"
25 #include "lib/ldb/include/includes.h"
27 #ifdef HAVE_LDAP
29 /**
30 * @file ldap.c
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
34 * ads setups.
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
40 **/
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
53 gotalarm = 1;
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
58 LDAP *ldp = NULL;
61 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62 "%u seconds\n", server, port, to));
64 /* Setup timeout */
65 gotalarm = 0;
66 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
67 alarm(to);
68 /* End setup timeout. */
70 ldp = ldap_open(server, port);
72 if (ldp == NULL) {
73 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
74 server, port, strerror(errno)));
75 } else {
76 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
79 /* Teardown timeout. */
80 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
81 alarm(0);
83 return ldp;
86 static int ldap_search_with_timeout(LDAP *ld,
87 LDAP_CONST char *base,
88 int scope,
89 LDAP_CONST char *filter,
90 char **attrs,
91 int attrsonly,
92 LDAPControl **sctrls,
93 LDAPControl **cctrls,
94 int sizelimit,
95 LDAPMessage **res )
97 struct timeval timeout;
98 int result;
100 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
101 timeout.tv_sec = lp_ldap_timeout();
102 timeout.tv_usec = 0;
104 /* Setup alarm timeout.... Do we need both of these ? JRA. */
105 gotalarm = 0;
106 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
107 alarm(lp_ldap_timeout());
108 /* End setup timeout. */
110 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
111 attrsonly, sctrls, cctrls, &timeout,
112 sizelimit, res);
114 /* Teardown timeout. */
115 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
116 alarm(0);
118 if (gotalarm != 0)
119 return LDAP_TIMELIMIT_EXCEEDED;
122 * A bug in OpenLDAP means ldap_search_ext_s can return
123 * LDAP_SUCCESS but with a NULL res pointer. Cope with
124 * this. See bug #6279 for details. JRA.
127 if (*res == NULL) {
128 return LDAP_TIMELIMIT_EXCEEDED;
131 return result;
134 /**********************************************
135 Do client and server sitename match ?
136 **********************************************/
138 bool ads_sitename_match(ADS_STRUCT *ads)
140 if (ads->config.server_site_name == NULL &&
141 ads->config.client_site_name == NULL ) {
142 DEBUG(10,("ads_sitename_match: both null\n"));
143 return True;
145 if (ads->config.server_site_name &&
146 ads->config.client_site_name &&
147 strequal(ads->config.server_site_name,
148 ads->config.client_site_name)) {
149 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
150 return True;
152 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
153 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
154 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
155 return False;
158 /**********************************************
159 Is this the closest DC ?
160 **********************************************/
162 bool ads_closest_dc(ADS_STRUCT *ads)
164 if (ads->config.flags & NBT_SERVER_CLOSEST) {
165 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
166 return True;
169 /* not sure if this can ever happen */
170 if (ads_sitename_match(ads)) {
171 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
172 return True;
175 if (ads->config.client_site_name == NULL) {
176 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
177 return True;
180 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
181 ads->config.ldap_server_name));
183 return False;
188 try a connection to a given ldap server, returning True and setting the servers IP
189 in the ads struct if successful
191 bool ads_try_connect(ADS_STRUCT *ads, const char *server )
193 char *srv;
194 struct nbt_cldap_netlogon_5 cldap_reply;
195 TALLOC_CTX *mem_ctx = NULL;
196 bool ret = false;
198 if (!server || !*server) {
199 return False;
202 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
203 server, ads->server.realm));
205 mem_ctx = talloc_init("ads_try_connect");
206 if (!mem_ctx) {
207 DEBUG(0,("out of memory\n"));
208 return false;
211 /* this copes with inet_ntoa brokenness */
213 srv = SMB_STRDUP(server);
215 ZERO_STRUCT( cldap_reply );
217 if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
218 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
219 ret = false;
220 goto out;
223 /* Check the CLDAP reply flags */
225 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
226 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
227 srv));
228 ret = false;
229 goto out;
232 /* Fill in the ads->config values */
234 SAFE_FREE(ads->config.realm);
235 SAFE_FREE(ads->config.bind_path);
236 SAFE_FREE(ads->config.ldap_server_name);
237 SAFE_FREE(ads->config.server_site_name);
238 SAFE_FREE(ads->config.client_site_name);
239 SAFE_FREE(ads->server.workgroup);
241 ads->config.flags = cldap_reply.server_type;
242 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
243 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
244 strupper_m(ads->config.realm);
245 ads->config.bind_path = ads_build_dn(ads->config.realm);
246 if (*cldap_reply.server_site) {
247 ads->config.server_site_name =
248 SMB_STRDUP(cldap_reply.server_site);
250 if (*cldap_reply.client_site) {
251 ads->config.client_site_name =
252 SMB_STRDUP(cldap_reply.client_site);
254 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
256 ads->ldap.port = LDAP_PORT;
257 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
258 DEBUG(1,("ads_try_connect: unable to convert %s "
259 "to an address\n",
260 srv));
261 ret = false;
262 goto out;
265 /* Store our site name. */
266 sitename_store( cldap_reply.domain, cldap_reply.client_site);
267 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
269 ret = true;
270 out:
271 SAFE_FREE(srv);
272 TALLOC_FREE(mem_ctx);
274 return ret;
277 /**********************************************************************
278 Try to find an AD dc using our internal name resolution routines
279 Try the realm first and then then workgroup name if netbios is not
280 disabled
281 **********************************************************************/
283 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
285 const char *c_domain;
286 const char *c_realm;
287 int count, i=0;
288 struct ip_service *ip_list;
289 const char *realm;
290 const char *domain;
291 bool got_realm = False;
292 bool use_own_domain = False;
293 char *sitename;
294 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
296 /* if the realm and workgroup are both empty, assume they are ours */
298 /* realm */
299 c_realm = ads->server.realm;
301 if ( !c_realm || !*c_realm ) {
302 /* special case where no realm and no workgroup means our own */
303 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
304 use_own_domain = True;
305 c_realm = lp_realm();
309 if (c_realm && *c_realm)
310 got_realm = True;
312 again:
314 /* we need to try once with the realm name and fallback to the
315 netbios domain name if we fail (if netbios has not been disabled */
317 if ( !got_realm && !lp_disable_netbios() ) {
318 c_realm = ads->server.workgroup;
319 if (!c_realm || !*c_realm) {
320 if ( use_own_domain )
321 c_realm = lp_workgroup();
325 if ( !c_realm || !*c_realm ) {
326 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
327 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
330 if ( use_own_domain ) {
331 c_domain = lp_workgroup();
332 } else {
333 c_domain = ads->server.workgroup;
336 realm = c_realm;
337 domain = c_domain;
340 * In case of LDAP we use get_dc_name() as that
341 * creates the custom krb5.conf file
343 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
344 fstring srv_name;
345 struct sockaddr_storage ip_out;
347 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
348 (got_realm ? "realm" : "domain"), realm));
350 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
352 * we call ads_try_connect() to fill in the
353 * ads->config details
355 if (ads_try_connect(ads, srv_name)) {
356 return NT_STATUS_OK;
360 return NT_STATUS_NO_LOGON_SERVERS;
363 sitename = sitename_fetch(realm);
365 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
366 (got_realm ? "realm" : "domain"), realm));
368 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
369 if (!NT_STATUS_IS_OK(status)) {
370 /* fall back to netbios if we can */
371 if ( got_realm && !lp_disable_netbios() ) {
372 got_realm = False;
373 goto again;
376 SAFE_FREE(sitename);
377 return status;
380 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
381 for ( i=0; i<count; i++ ) {
382 char server[INET6_ADDRSTRLEN];
384 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
386 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
387 continue;
389 if (!got_realm) {
390 /* realm in this case is a workgroup name. We need
391 to ignore any IP addresses in the negative connection
392 cache that match ip addresses returned in the ad realm
393 case. It sucks that I have to reproduce the logic above... */
394 c_realm = ads->server.realm;
395 if ( !c_realm || !*c_realm ) {
396 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
397 c_realm = lp_realm();
400 if (c_realm && *c_realm &&
401 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
402 /* Ensure we add the workgroup name for this
403 IP address as negative too. */
404 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
405 continue;
409 if ( ads_try_connect(ads, server) ) {
410 SAFE_FREE(ip_list);
411 SAFE_FREE(sitename);
412 return NT_STATUS_OK;
415 /* keep track of failures */
416 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
419 SAFE_FREE(ip_list);
421 /* In case we failed to contact one of our closest DC on our site we
422 * need to try to find another DC, retry with a site-less SRV DNS query
423 * - Guenther */
425 if (sitename) {
426 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
427 "trying to find another DC\n", sitename));
428 SAFE_FREE(sitename);
429 namecache_delete(realm, 0x1C);
430 goto again;
433 return NT_STATUS_NO_LOGON_SERVERS;
438 * Connect to the LDAP server
439 * @param ads Pointer to an existing ADS_STRUCT
440 * @return status of connection
442 ADS_STATUS ads_connect(ADS_STRUCT *ads)
444 int version = LDAP_VERSION3;
445 ADS_STATUS status;
446 NTSTATUS ntstatus;
447 char addr[INET6_ADDRSTRLEN];
449 ZERO_STRUCT(ads->ldap);
450 ads->ldap.last_attempt = time(NULL);
451 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
453 /* try with a user specified server */
455 if (DEBUGLEVEL >= 11) {
456 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
457 DEBUG(11,("ads_connect: entering\n"));
458 DEBUGADD(11,("%s\n", s));
459 TALLOC_FREE(s);
462 if (ads->server.ldap_server &&
463 ads_try_connect(ads, ads->server.ldap_server)) {
464 goto got_connection;
467 ntstatus = ads_find_dc(ads);
468 if (NT_STATUS_IS_OK(ntstatus)) {
469 goto got_connection;
472 status = ADS_ERROR_NT(ntstatus);
473 goto out;
475 got_connection:
477 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
478 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
480 if (!ads->auth.user_name) {
481 /* Must use the userPrincipalName value here or sAMAccountName
482 and not servicePrincipalName; found by Guenther Deschner */
484 asprintf(&ads->auth.user_name, "%s$", global_myname() );
487 if (!ads->auth.realm) {
488 ads->auth.realm = SMB_STRDUP(ads->config.realm);
491 if (!ads->auth.kdc_server) {
492 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
493 ads->auth.kdc_server = SMB_STRDUP(addr);
496 #if KRB5_DNS_HACK
497 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
498 to MIT kerberos to work (tridge) */
500 char *env;
501 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
502 setenv(env, ads->auth.kdc_server, 1);
503 free(env);
505 #endif
507 /* If the caller() requested no LDAP bind, then we are done */
509 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
510 status = ADS_SUCCESS;
511 goto out;
514 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
515 if (!ads->ldap.mem_ctx) {
516 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
517 goto out;
520 /* Otherwise setup the TCP LDAP session */
522 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
523 LDAP_PORT, lp_ldap_timeout());
524 if (ads->ldap.ld == NULL) {
525 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
526 goto out;
528 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
530 /* cache the successful connection for workgroup and realm */
531 if (ads_closest_dc(ads)) {
532 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
533 saf_store( ads->server.realm, ads->config.ldap_server_name);
536 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
538 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
539 if (!ADS_ERR_OK(status)) {
540 goto out;
543 /* fill in the current time and offsets */
545 status = ads_current_time( ads );
546 if ( !ADS_ERR_OK(status) ) {
547 goto out;
550 /* Now do the bind */
552 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
553 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
554 goto out;
557 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
558 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
559 goto out;
562 status = ads_sasl_bind(ads);
564 out:
565 if (DEBUGLEVEL >= 11) {
566 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
567 DEBUG(11,("ads_connect: leaving with: %s\n",
568 ads_errstr(status)));
569 DEBUGADD(11,("%s\n", s));
570 TALLOC_FREE(s);
573 return status;
577 * Disconnect the LDAP server
578 * @param ads Pointer to an existing ADS_STRUCT
580 void ads_disconnect(ADS_STRUCT *ads)
582 if (ads->ldap.ld) {
583 ldap_unbind(ads->ldap.ld);
584 ads->ldap.ld = NULL;
586 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
587 ads->ldap.wrap_ops->disconnect(ads);
589 if (ads->ldap.mem_ctx) {
590 talloc_free(ads->ldap.mem_ctx);
592 ZERO_STRUCT(ads->ldap);
596 Duplicate a struct berval into talloc'ed memory
598 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
600 struct berval *value;
602 if (!in_val) return NULL;
604 value = TALLOC_ZERO_P(ctx, struct berval);
605 if (value == NULL)
606 return NULL;
607 if (in_val->bv_len == 0) return value;
609 value->bv_len = in_val->bv_len;
610 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
611 in_val->bv_len);
612 return value;
616 Make a values list out of an array of (struct berval *)
618 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
619 const struct berval **in_vals)
621 struct berval **values;
622 int i;
624 if (!in_vals) return NULL;
625 for (i=0; in_vals[i]; i++)
626 ; /* count values */
627 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
628 if (!values) return NULL;
630 for (i=0; in_vals[i]; i++) {
631 values[i] = dup_berval(ctx, in_vals[i]);
633 return values;
637 UTF8-encode a values list out of an array of (char *)
639 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
641 char **values;
642 int i;
644 if (!in_vals) return NULL;
645 for (i=0; in_vals[i]; i++)
646 ; /* count values */
647 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
648 if (!values) return NULL;
650 for (i=0; in_vals[i]; i++) {
651 if (push_utf8_talloc(ctx, &values[i], in_vals[i]) == (size_t) -1) {
652 TALLOC_FREE(values);
653 return NULL;
656 return values;
660 Pull a (char *) array out of a UTF8-encoded values list
662 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
664 char **values;
665 int i;
667 if (!in_vals) return NULL;
668 for (i=0; in_vals[i]; i++)
669 ; /* count values */
670 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
671 if (!values) return NULL;
673 for (i=0; in_vals[i]; i++) {
674 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
676 return values;
680 * Do a search with paged results. cookie must be null on the first
681 * call, and then returned on each subsequent call. It will be null
682 * again when the entire search is complete
683 * @param ads connection to ads server
684 * @param bind_path Base dn for the search
685 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
686 * @param expr Search expression - specified in local charset
687 * @param attrs Attributes to retrieve - specified in utf8 or ascii
688 * @param res ** which will contain results - free res* with ads_msgfree()
689 * @param count Number of entries retrieved on this page
690 * @param cookie The paged results cookie to be returned on subsequent calls
691 * @return status of search
693 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
694 const char *bind_path,
695 int scope, const char *expr,
696 const char **attrs, void *args,
697 LDAPMessage **res,
698 int *count, struct berval **cookie)
700 int rc, i, version;
701 char *utf8_expr, *utf8_path, **search_attrs;
702 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
703 BerElement *cookie_be = NULL;
704 struct berval *cookie_bv= NULL;
705 BerElement *ext_be = NULL;
706 struct berval *ext_bv= NULL;
708 TALLOC_CTX *ctx;
709 ads_control *external_control = (ads_control *) args;
711 *res = NULL;
713 if (!(ctx = talloc_init("ads_do_paged_search_args")))
714 return ADS_ERROR(LDAP_NO_MEMORY);
716 /* 0 means the conversion worked but the result was empty
717 so we only fail if it's -1. In any case, it always
718 at least nulls out the dest */
719 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
720 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
721 rc = LDAP_NO_MEMORY;
722 goto done;
725 if (!attrs || !(*attrs))
726 search_attrs = NULL;
727 else {
728 /* This would be the utf8-encoded version...*/
729 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
730 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
731 rc = LDAP_NO_MEMORY;
732 goto done;
736 /* Paged results only available on ldap v3 or later */
737 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
738 if (version < LDAP_VERSION3) {
739 rc = LDAP_NOT_SUPPORTED;
740 goto done;
743 cookie_be = ber_alloc_t(LBER_USE_DER);
744 if (*cookie) {
745 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
746 ber_bvfree(*cookie); /* don't need it from last time */
747 *cookie = NULL;
748 } else {
749 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
751 ber_flatten(cookie_be, &cookie_bv);
752 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
753 PagedResults.ldctl_iscritical = (char) 1;
754 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
755 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
757 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
758 NoReferrals.ldctl_iscritical = (char) 0;
759 NoReferrals.ldctl_value.bv_len = 0;
760 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
762 if (external_control &&
763 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
764 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
766 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
767 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
769 /* win2k does not accept a ldctl_value beeing passed in */
771 if (external_control->val != 0) {
773 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
774 rc = LDAP_NO_MEMORY;
775 goto done;
778 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
779 rc = LDAP_NO_MEMORY;
780 goto done;
782 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
783 rc = LDAP_NO_MEMORY;
784 goto done;
787 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
788 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
790 } else {
791 ExternalCtrl.ldctl_value.bv_len = 0;
792 ExternalCtrl.ldctl_value.bv_val = NULL;
795 controls[0] = &NoReferrals;
796 controls[1] = &PagedResults;
797 controls[2] = &ExternalCtrl;
798 controls[3] = NULL;
800 } else {
801 controls[0] = &NoReferrals;
802 controls[1] = &PagedResults;
803 controls[2] = NULL;
806 /* we need to disable referrals as the openldap libs don't
807 handle them and paged results at the same time. Using them
808 together results in the result record containing the server
809 page control being removed from the result list (tridge/jmcd)
811 leaving this in despite the control that says don't generate
812 referrals, in case the server doesn't support it (jmcd)
814 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
816 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
817 search_attrs, 0, controls,
818 NULL, LDAP_NO_LIMIT,
819 (LDAPMessage **)res);
821 ber_free(cookie_be, 1);
822 ber_bvfree(cookie_bv);
824 if (rc) {
825 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
826 ldap_err2string(rc)));
827 goto done;
830 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
831 NULL, &rcontrols, 0);
833 if (!rcontrols) {
834 goto done;
837 for (i=0; rcontrols[i]; i++) {
838 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
839 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
840 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
841 &cookie_bv);
842 /* the berval is the cookie, but must be freed when
843 it is all done */
844 if (cookie_bv->bv_len) /* still more to do */
845 *cookie=ber_bvdup(cookie_bv);
846 else
847 *cookie=NULL;
848 ber_bvfree(cookie_bv);
849 ber_free(cookie_be, 1);
850 break;
853 ldap_controls_free(rcontrols);
855 done:
856 talloc_destroy(ctx);
858 if (ext_be) {
859 ber_free(ext_be, 1);
862 if (ext_bv) {
863 ber_bvfree(ext_bv);
866 /* if/when we decide to utf8-encode attrs, take out this next line */
867 TALLOC_FREE(search_attrs);
869 return ADS_ERROR(rc);
872 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
873 int scope, const char *expr,
874 const char **attrs, LDAPMessage **res,
875 int *count, struct berval **cookie)
877 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
882 * Get all results for a search. This uses ads_do_paged_search() to return
883 * all entries in a large search.
884 * @param ads connection to ads server
885 * @param bind_path Base dn for the search
886 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
887 * @param expr Search expression
888 * @param attrs Attributes to retrieve
889 * @param res ** which will contain results - free res* with ads_msgfree()
890 * @return status of search
892 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
893 int scope, const char *expr,
894 const char **attrs, void *args,
895 LDAPMessage **res)
897 struct berval *cookie = NULL;
898 int count = 0;
899 ADS_STATUS status;
901 *res = NULL;
902 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
903 &count, &cookie);
905 if (!ADS_ERR_OK(status))
906 return status;
908 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
909 while (cookie) {
910 LDAPMessage *res2 = NULL;
911 ADS_STATUS status2;
912 LDAPMessage *msg, *next;
914 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
915 attrs, args, &res2, &count, &cookie);
917 if (!ADS_ERR_OK(status2)) break;
919 /* this relies on the way that ldap_add_result_entry() works internally. I hope
920 that this works on all ldap libs, but I have only tested with openldap */
921 for (msg = ads_first_message(ads, res2); msg; msg = next) {
922 next = ads_next_message(ads, msg);
923 ldap_add_result_entry((LDAPMessage **)res, msg);
925 /* note that we do not free res2, as the memory is now
926 part of the main returned list */
928 #else
929 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
930 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
931 #endif
933 return status;
936 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
937 int scope, const char *expr,
938 const char **attrs, LDAPMessage **res)
940 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
943 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
944 int scope, const char *expr,
945 const char **attrs, uint32 sd_flags,
946 LDAPMessage **res)
948 ads_control args;
950 args.control = ADS_SD_FLAGS_OID;
951 args.val = sd_flags;
952 args.critical = True;
954 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
959 * Run a function on all results for a search. Uses ads_do_paged_search() and
960 * runs the function as each page is returned, using ads_process_results()
961 * @param ads connection to ads server
962 * @param bind_path Base dn for the search
963 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
964 * @param expr Search expression - specified in local charset
965 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
966 * @param fn Function which takes attr name, values list, and data_area
967 * @param data_area Pointer which is passed to function on each call
968 * @return status of search
970 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
971 int scope, const char *expr, const char **attrs,
972 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
973 void *data_area)
975 struct berval *cookie = NULL;
976 int count = 0;
977 ADS_STATUS status;
978 LDAPMessage *res;
980 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
981 &count, &cookie);
983 if (!ADS_ERR_OK(status)) return status;
985 ads_process_results(ads, res, fn, data_area);
986 ads_msgfree(ads, res);
988 while (cookie) {
989 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
990 &res, &count, &cookie);
992 if (!ADS_ERR_OK(status)) break;
994 ads_process_results(ads, res, fn, data_area);
995 ads_msgfree(ads, res);
998 return status;
1002 * Do a search with a timeout.
1003 * @param ads connection to ads server
1004 * @param bind_path Base dn for the search
1005 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1006 * @param expr Search expression
1007 * @param attrs Attributes to retrieve
1008 * @param res ** which will contain results - free res* with ads_msgfree()
1009 * @return status of search
1011 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1012 const char *expr,
1013 const char **attrs, LDAPMessage **res)
1015 int rc;
1016 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1017 TALLOC_CTX *ctx;
1019 *res = NULL;
1020 if (!(ctx = talloc_init("ads_do_search"))) {
1021 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1022 return ADS_ERROR(LDAP_NO_MEMORY);
1025 /* 0 means the conversion worked but the result was empty
1026 so we only fail if it's negative. In any case, it always
1027 at least nulls out the dest */
1028 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
1029 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
1030 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1031 rc = LDAP_NO_MEMORY;
1032 goto done;
1035 if (!attrs || !(*attrs))
1036 search_attrs = NULL;
1037 else {
1038 /* This would be the utf8-encoded version...*/
1039 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1040 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
1042 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1043 rc = LDAP_NO_MEMORY;
1044 goto done;
1048 /* see the note in ads_do_paged_search - we *must* disable referrals */
1049 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1051 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1052 search_attrs, 0, NULL, NULL,
1053 LDAP_NO_LIMIT,
1054 (LDAPMessage **)res);
1056 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1057 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1058 rc = 0;
1061 done:
1062 talloc_destroy(ctx);
1063 /* if/when we decide to utf8-encode attrs, take out this next line */
1064 TALLOC_FREE(search_attrs);
1065 return ADS_ERROR(rc);
1068 * Do a general ADS search
1069 * @param ads connection to ads server
1070 * @param res ** which will contain results - free res* with ads_msgfree()
1071 * @param expr Search expression
1072 * @param attrs Attributes to retrieve
1073 * @return status of search
1075 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1076 const char *expr, const char **attrs)
1078 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1079 expr, attrs, res);
1083 * Do a search on a specific DistinguishedName
1084 * @param ads connection to ads server
1085 * @param res ** which will contain results - free res* with ads_msgfree()
1086 * @param dn DistinguishName to search
1087 * @param attrs Attributes to retrieve
1088 * @return status of search
1090 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1091 const char *dn, const char **attrs)
1093 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1094 attrs, res);
1098 * Free up memory from a ads_search
1099 * @param ads connection to ads server
1100 * @param msg Search results to free
1102 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1104 if (!msg) return;
1105 ldap_msgfree(msg);
1109 * Free up memory from various ads requests
1110 * @param ads connection to ads server
1111 * @param mem Area to free
1113 void ads_memfree(ADS_STRUCT *ads, void *mem)
1115 SAFE_FREE(mem);
1119 * Get a dn from search results
1120 * @param ads connection to ads server
1121 * @param msg Search result
1122 * @return dn string
1124 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1126 char *utf8_dn, *unix_dn;
1128 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1130 if (!utf8_dn) {
1131 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1132 return NULL;
1135 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1136 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1137 utf8_dn ));
1138 return NULL;
1140 ldap_memfree(utf8_dn);
1141 return unix_dn;
1145 * Get the parent from a dn
1146 * @param dn the dn to return the parent from
1147 * @return parent dn string
1149 char *ads_parent_dn(const char *dn)
1151 char *p;
1153 if (dn == NULL) {
1154 return NULL;
1157 p = strchr(dn, ',');
1159 if (p == NULL) {
1160 return NULL;
1163 return p+1;
1167 * Find a machine account given a hostname
1168 * @param ads connection to ads server
1169 * @param res ** which will contain results - free res* with ads_msgfree()
1170 * @param host Hostname to search for
1171 * @return status of search
1173 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1174 const char *machine)
1176 ADS_STATUS status;
1177 char *expr;
1178 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1180 *res = NULL;
1182 /* the easiest way to find a machine account anywhere in the tree
1183 is to look for hostname$ */
1184 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1185 DEBUG(1, ("asprintf failed!\n"));
1186 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1189 status = ads_search(ads, res, expr, attrs);
1190 SAFE_FREE(expr);
1191 return status;
1195 * Initialize a list of mods to be used in a modify request
1196 * @param ctx An initialized TALLOC_CTX
1197 * @return allocated ADS_MODLIST
1199 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1201 #define ADS_MODLIST_ALLOC_SIZE 10
1202 LDAPMod **mods;
1204 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1205 /* -1 is safety to make sure we don't go over the end.
1206 need to reset it to NULL before doing ldap modify */
1207 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1209 return (ADS_MODLIST)mods;
1214 add an attribute to the list, with values list already constructed
1216 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1217 int mod_op, const char *name,
1218 const void *_invals)
1220 const void **invals = (const void **)_invals;
1221 int curmod;
1222 LDAPMod **modlist = (LDAPMod **) *mods;
1223 struct berval **ber_values = NULL;
1224 char **char_values = NULL;
1226 if (!invals) {
1227 mod_op = LDAP_MOD_DELETE;
1228 } else {
1229 if (mod_op & LDAP_MOD_BVALUES)
1230 ber_values = ads_dup_values(ctx,
1231 (const struct berval **)invals);
1232 else
1233 char_values = ads_push_strvals(ctx,
1234 (const char **) invals);
1237 /* find the first empty slot */
1238 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1239 curmod++);
1240 if (modlist[curmod] == (LDAPMod *) -1) {
1241 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1242 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1243 return ADS_ERROR(LDAP_NO_MEMORY);
1244 memset(&modlist[curmod], 0,
1245 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1246 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1247 *mods = (ADS_MODLIST)modlist;
1250 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1251 return ADS_ERROR(LDAP_NO_MEMORY);
1252 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1253 if (mod_op & LDAP_MOD_BVALUES) {
1254 modlist[curmod]->mod_bvalues = ber_values;
1255 } else if (mod_op & LDAP_MOD_DELETE) {
1256 modlist[curmod]->mod_values = NULL;
1257 } else {
1258 modlist[curmod]->mod_values = char_values;
1261 modlist[curmod]->mod_op = mod_op;
1262 return ADS_ERROR(LDAP_SUCCESS);
1266 * Add a single string value to a mod list
1267 * @param ctx An initialized TALLOC_CTX
1268 * @param mods An initialized ADS_MODLIST
1269 * @param name The attribute name to add
1270 * @param val The value to add - NULL means DELETE
1271 * @return ADS STATUS indicating success of add
1273 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1274 const char *name, const char *val)
1276 const char *values[2];
1278 values[0] = val;
1279 values[1] = NULL;
1281 if (!val)
1282 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1283 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1287 * Add an array of string values to a mod list
1288 * @param ctx An initialized TALLOC_CTX
1289 * @param mods An initialized ADS_MODLIST
1290 * @param name The attribute name to add
1291 * @param vals The array of string values to add - NULL means DELETE
1292 * @return ADS STATUS indicating success of add
1294 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1295 const char *name, const char **vals)
1297 if (!vals)
1298 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1299 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1300 name, (const void **) vals);
1303 #if 0
1305 * Add a single ber-encoded value to a mod list
1306 * @param ctx An initialized TALLOC_CTX
1307 * @param mods An initialized ADS_MODLIST
1308 * @param name The attribute name to add
1309 * @param val The value to add - NULL means DELETE
1310 * @return ADS STATUS indicating success of add
1312 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1313 const char *name, const struct berval *val)
1315 const struct berval *values[2];
1317 values[0] = val;
1318 values[1] = NULL;
1319 if (!val)
1320 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1321 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1322 name, (const void **) values);
1324 #endif
1327 * Perform an ldap modify
1328 * @param ads connection to ads server
1329 * @param mod_dn DistinguishedName to modify
1330 * @param mods list of modifications to perform
1331 * @return status of modify
1333 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1335 int ret,i;
1336 char *utf8_dn = NULL;
1338 this control is needed to modify that contains a currently
1339 non-existent attribute (but allowable for the object) to run
1341 LDAPControl PermitModify = {
1342 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1343 {0, NULL},
1344 (char) 1};
1345 LDAPControl *controls[2];
1347 controls[0] = &PermitModify;
1348 controls[1] = NULL;
1350 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1351 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1354 /* find the end of the list, marked by NULL or -1 */
1355 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1356 /* make sure the end of the list is NULL */
1357 mods[i] = NULL;
1358 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1359 (LDAPMod **) mods, controls, NULL);
1360 SAFE_FREE(utf8_dn);
1361 return ADS_ERROR(ret);
1365 * Perform an ldap add
1366 * @param ads connection to ads server
1367 * @param new_dn DistinguishedName to add
1368 * @param mods list of attributes and values for DN
1369 * @return status of add
1371 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1373 int ret, i;
1374 char *utf8_dn = NULL;
1376 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1377 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1378 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1381 /* find the end of the list, marked by NULL or -1 */
1382 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1383 /* make sure the end of the list is NULL */
1384 mods[i] = NULL;
1386 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1387 SAFE_FREE(utf8_dn);
1388 return ADS_ERROR(ret);
1392 * Delete a DistinguishedName
1393 * @param ads connection to ads server
1394 * @param new_dn DistinguishedName to delete
1395 * @return status of delete
1397 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1399 int ret;
1400 char *utf8_dn = NULL;
1401 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1402 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1403 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1406 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1407 SAFE_FREE(utf8_dn);
1408 return ADS_ERROR(ret);
1412 * Build an org unit string
1413 * if org unit is Computers or blank then assume a container, otherwise
1414 * assume a / separated list of organisational units.
1415 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1416 * @param ads connection to ads server
1417 * @param org_unit Organizational unit
1418 * @return org unit string - caller must free
1420 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1422 char *ret = NULL;
1424 if (!org_unit || !*org_unit) {
1426 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1428 /* samba4 might not yet respond to a wellknownobject-query */
1429 return ret ? ret : SMB_STRDUP("cn=Computers");
1432 if (strequal(org_unit, "Computers")) {
1433 return SMB_STRDUP("cn=Computers");
1436 /* jmcd: removed "\\" from the separation chars, because it is
1437 needed as an escape for chars like '#' which are valid in an
1438 OU name */
1439 return ads_build_path(org_unit, "/", "ou=", 1);
1443 * Get a org unit string for a well-known GUID
1444 * @param ads connection to ads server
1445 * @param wknguid Well known GUID
1446 * @return org unit string - caller must free
1448 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1450 ADS_STATUS status;
1451 LDAPMessage *res = NULL;
1452 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1453 **bind_dn_exp = NULL;
1454 const char *attrs[] = {"distinguishedName", NULL};
1455 int new_ln, wkn_ln, bind_ln, i;
1457 if (wknguid == NULL) {
1458 return NULL;
1461 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1462 DEBUG(1, ("asprintf failed!\n"));
1463 return NULL;
1466 status = ads_search_dn(ads, &res, base, attrs);
1467 if (!ADS_ERR_OK(status)) {
1468 DEBUG(1,("Failed while searching for: %s\n", base));
1469 goto out;
1472 if (ads_count_replies(ads, res) != 1) {
1473 goto out;
1476 /* substitute the bind-path from the well-known-guid-search result */
1477 wkn_dn = ads_get_dn(ads, res);
1478 if (!wkn_dn) {
1479 goto out;
1482 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1483 if (!wkn_dn_exp) {
1484 goto out;
1487 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1488 if (!bind_dn_exp) {
1489 goto out;
1492 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1494 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1497 new_ln = wkn_ln - bind_ln;
1499 ret = SMB_STRDUP(wkn_dn_exp[0]);
1500 if (!ret) {
1501 goto out;
1504 for (i=1; i < new_ln; i++) {
1505 char *s = NULL;
1507 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1508 SAFE_FREE(ret);
1509 goto out;
1512 SAFE_FREE(ret);
1513 ret = SMB_STRDUP(s);
1514 free(s);
1515 if (!ret) {
1516 goto out;
1520 out:
1521 SAFE_FREE(base);
1522 ads_msgfree(ads, res);
1523 ads_memfree(ads, wkn_dn);
1524 if (wkn_dn_exp) {
1525 ldap_value_free(wkn_dn_exp);
1527 if (bind_dn_exp) {
1528 ldap_value_free(bind_dn_exp);
1531 return ret;
1535 * Adds (appends) an item to an attribute array, rather then
1536 * replacing the whole list
1537 * @param ctx An initialized TALLOC_CTX
1538 * @param mods An initialized ADS_MODLIST
1539 * @param name name of the ldap attribute to append to
1540 * @param vals an array of values to add
1541 * @return status of addition
1544 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1545 const char *name, const char **vals)
1547 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1548 (const void *) vals);
1552 * Determines the computer account's current KVNO via an LDAP lookup
1553 * @param ads An initialized ADS_STRUCT
1554 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1555 * @return the kvno for the computer account, or -1 in case of a failure.
1558 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1560 LDAPMessage *res = NULL;
1561 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1562 char *filter;
1563 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1564 char *dn_string = NULL;
1565 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1567 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1568 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1569 return kvno;
1571 ret = ads_search(ads, &res, filter, attrs);
1572 SAFE_FREE(filter);
1573 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1574 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1575 ads_msgfree(ads, res);
1576 return kvno;
1579 dn_string = ads_get_dn(ads, res);
1580 if (!dn_string) {
1581 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1582 ads_msgfree(ads, res);
1583 return kvno;
1585 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1586 ads_memfree(ads, dn_string);
1588 /* ---------------------------------------------------------
1589 * 0 is returned as a default KVNO from this point on...
1590 * This is done because Windows 2000 does not support key
1591 * version numbers. Chances are that a failure in the next
1592 * step is simply due to Windows 2000 being used for a
1593 * domain controller. */
1594 kvno = 0;
1596 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1597 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1598 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1599 ads_msgfree(ads, res);
1600 return kvno;
1603 /* Success */
1604 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1605 ads_msgfree(ads, res);
1606 return kvno;
1610 * This clears out all registered spn's for a given hostname
1611 * @param ads An initilaized ADS_STRUCT
1612 * @param machine_name the NetBIOS name of the computer.
1613 * @return 0 upon success, non-zero otherwise.
1616 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1618 TALLOC_CTX *ctx;
1619 LDAPMessage *res = NULL;
1620 ADS_MODLIST mods;
1621 const char *servicePrincipalName[1] = {NULL};
1622 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1623 char *dn_string = NULL;
1625 ret = ads_find_machine_acct(ads, &res, machine_name);
1626 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1627 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1628 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1629 ads_msgfree(ads, res);
1630 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1633 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1634 ctx = talloc_init("ads_clear_service_principal_names");
1635 if (!ctx) {
1636 ads_msgfree(ads, res);
1637 return ADS_ERROR(LDAP_NO_MEMORY);
1640 if (!(mods = ads_init_mods(ctx))) {
1641 talloc_destroy(ctx);
1642 ads_msgfree(ads, res);
1643 return ADS_ERROR(LDAP_NO_MEMORY);
1645 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1646 if (!ADS_ERR_OK(ret)) {
1647 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1648 ads_msgfree(ads, res);
1649 talloc_destroy(ctx);
1650 return ret;
1652 dn_string = ads_get_dn(ads, res);
1653 if (!dn_string) {
1654 talloc_destroy(ctx);
1655 ads_msgfree(ads, res);
1656 return ADS_ERROR(LDAP_NO_MEMORY);
1658 ret = ads_gen_mod(ads, dn_string, mods);
1659 ads_memfree(ads,dn_string);
1660 if (!ADS_ERR_OK(ret)) {
1661 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1662 machine_name));
1663 ads_msgfree(ads, res);
1664 talloc_destroy(ctx);
1665 return ret;
1668 ads_msgfree(ads, res);
1669 talloc_destroy(ctx);
1670 return ret;
1674 * This adds a service principal name to an existing computer account
1675 * (found by hostname) in AD.
1676 * @param ads An initialized ADS_STRUCT
1677 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1678 * @param my_fqdn The fully qualified DNS name of the machine
1679 * @param spn A string of the service principal to add, i.e. 'host'
1680 * @return 0 upon sucess, or non-zero if a failure occurs
1683 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1684 const char *my_fqdn, const char *spn)
1686 ADS_STATUS ret;
1687 TALLOC_CTX *ctx;
1688 LDAPMessage *res = NULL;
1689 char *psp1, *psp2;
1690 ADS_MODLIST mods;
1691 char *dn_string = NULL;
1692 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1694 ret = ads_find_machine_acct(ads, &res, machine_name);
1695 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1696 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1697 machine_name));
1698 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1699 spn, machine_name, ads->config.realm));
1700 ads_msgfree(ads, res);
1701 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1704 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1705 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1706 ads_msgfree(ads, res);
1707 return ADS_ERROR(LDAP_NO_MEMORY);
1710 /* add short name spn */
1712 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1713 talloc_destroy(ctx);
1714 ads_msgfree(ads, res);
1715 return ADS_ERROR(LDAP_NO_MEMORY);
1717 strupper_m(psp1);
1718 strlower_m(&psp1[strlen(spn)]);
1719 servicePrincipalName[0] = psp1;
1721 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1722 psp1, machine_name));
1725 /* add fully qualified spn */
1727 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1728 ret = ADS_ERROR(LDAP_NO_MEMORY);
1729 goto out;
1731 strupper_m(psp2);
1732 strlower_m(&psp2[strlen(spn)]);
1733 servicePrincipalName[1] = psp2;
1735 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1736 psp2, machine_name));
1738 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1739 ret = ADS_ERROR(LDAP_NO_MEMORY);
1740 goto out;
1743 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1744 if (!ADS_ERR_OK(ret)) {
1745 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1746 goto out;
1749 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1750 ret = ADS_ERROR(LDAP_NO_MEMORY);
1751 goto out;
1754 ret = ads_gen_mod(ads, dn_string, mods);
1755 ads_memfree(ads,dn_string);
1756 if (!ADS_ERR_OK(ret)) {
1757 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1758 goto out;
1761 out:
1762 TALLOC_FREE( ctx );
1763 ads_msgfree(ads, res);
1764 return ret;
1768 * adds a machine account to the ADS server
1769 * @param ads An intialized ADS_STRUCT
1770 * @param machine_name - the NetBIOS machine name of this account.
1771 * @param account_type A number indicating the type of account to create
1772 * @param org_unit The LDAP path in which to place this account
1773 * @return 0 upon success, or non-zero otherwise
1776 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1777 const char *org_unit)
1779 ADS_STATUS ret;
1780 char *samAccountName, *controlstr;
1781 TALLOC_CTX *ctx;
1782 ADS_MODLIST mods;
1783 char *machine_escaped = NULL;
1784 char *new_dn;
1785 const char *objectClass[] = {"top", "person", "organizationalPerson",
1786 "user", "computer", NULL};
1787 LDAPMessage *res = NULL;
1788 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1789 UF_DONT_EXPIRE_PASSWD |\
1790 UF_ACCOUNTDISABLE );
1792 if (!(ctx = talloc_init("ads_add_machine_acct")))
1793 return ADS_ERROR(LDAP_NO_MEMORY);
1795 ret = ADS_ERROR(LDAP_NO_MEMORY);
1797 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1798 if (!machine_escaped) {
1799 goto done;
1802 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1803 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1805 if ( !new_dn || !samAccountName ) {
1806 goto done;
1809 #ifndef ENCTYPE_ARCFOUR_HMAC
1810 acct_control |= UF_USE_DES_KEY_ONLY;
1811 #endif
1813 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1814 goto done;
1817 if (!(mods = ads_init_mods(ctx))) {
1818 goto done;
1821 ads_mod_str(ctx, &mods, "cn", machine_name);
1822 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1823 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1824 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1826 ret = ads_gen_add(ads, new_dn, mods);
1828 done:
1829 SAFE_FREE(machine_escaped);
1830 ads_msgfree(ads, res);
1831 talloc_destroy(ctx);
1833 return ret;
1837 * move a machine account to another OU on the ADS server
1838 * @param ads - An intialized ADS_STRUCT
1839 * @param machine_name - the NetBIOS machine name of this account.
1840 * @param org_unit - The LDAP path in which to place this account
1841 * @param moved - whether we moved the machine account (optional)
1842 * @return 0 upon success, or non-zero otherwise
1845 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1846 const char *org_unit, bool *moved)
1848 ADS_STATUS rc;
1849 int ldap_status;
1850 LDAPMessage *res = NULL;
1851 char *filter = NULL;
1852 char *computer_dn = NULL;
1853 char *parent_dn;
1854 char *computer_rdn = NULL;
1855 bool need_move = False;
1857 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1858 rc = ADS_ERROR(LDAP_NO_MEMORY);
1859 goto done;
1862 /* Find pre-existing machine */
1863 rc = ads_search(ads, &res, filter, NULL);
1864 if (!ADS_ERR_OK(rc)) {
1865 goto done;
1868 computer_dn = ads_get_dn(ads, res);
1869 if (!computer_dn) {
1870 rc = ADS_ERROR(LDAP_NO_MEMORY);
1871 goto done;
1874 parent_dn = ads_parent_dn(computer_dn);
1875 if (strequal(parent_dn, org_unit)) {
1876 goto done;
1879 need_move = True;
1881 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1882 rc = ADS_ERROR(LDAP_NO_MEMORY);
1883 goto done;
1886 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1887 org_unit, 1, NULL, NULL);
1888 rc = ADS_ERROR(ldap_status);
1890 done:
1891 ads_msgfree(ads, res);
1892 SAFE_FREE(filter);
1893 SAFE_FREE(computer_dn);
1894 SAFE_FREE(computer_rdn);
1896 if (!ADS_ERR_OK(rc)) {
1897 need_move = False;
1900 if (moved) {
1901 *moved = need_move;
1904 return rc;
1908 dump a binary result from ldap
1910 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1912 int i, j;
1913 for (i=0; values[i]; i++) {
1914 printf("%s: ", field);
1915 for (j=0; j<values[i]->bv_len; j++) {
1916 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1918 printf("\n");
1922 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1924 int i;
1925 for (i=0; values[i]; i++) {
1927 UUID_FLAT guid;
1928 struct GUID tmp;
1930 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1931 smb_uuid_unpack(guid, &tmp);
1932 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1937 dump a sid result from ldap
1939 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1941 int i;
1942 for (i=0; values[i]; i++) {
1943 DOM_SID sid;
1944 fstring tmp;
1945 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1946 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1951 dump ntSecurityDescriptor
1953 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1955 TALLOC_CTX *frame = talloc_stackframe();
1956 struct security_descriptor *psd;
1957 NTSTATUS status;
1959 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1960 values[0]->bv_len, &psd);
1961 if (!NT_STATUS_IS_OK(status)) {
1962 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1963 nt_errstr(status)));
1964 TALLOC_FREE(frame);
1965 return;
1968 if (psd) {
1969 ads_disp_sd(ads, talloc_tos(), psd);
1972 TALLOC_FREE(frame);
1976 dump a string result from ldap
1978 static void dump_string(const char *field, char **values)
1980 int i;
1981 for (i=0; values[i]; i++) {
1982 printf("%s: %s\n", field, values[i]);
1987 dump a field from LDAP on stdout
1988 used for debugging
1991 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1993 const struct {
1994 const char *name;
1995 bool string;
1996 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1997 } handlers[] = {
1998 {"objectGUID", False, dump_guid},
1999 {"netbootGUID", False, dump_guid},
2000 {"nTSecurityDescriptor", False, dump_sd},
2001 {"dnsRecord", False, dump_binary},
2002 {"objectSid", False, dump_sid},
2003 {"tokenGroups", False, dump_sid},
2004 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2005 {"tokengroupsGlobalandUniversal", False, dump_sid},
2006 {"mS-DS-CreatorSID", False, dump_sid},
2007 {"msExchMailboxGuid", False, dump_guid},
2008 {NULL, True, NULL}
2010 int i;
2012 if (!field) { /* must be end of an entry */
2013 printf("\n");
2014 return False;
2017 for (i=0; handlers[i].name; i++) {
2018 if (StrCaseCmp(handlers[i].name, field) == 0) {
2019 if (!values) /* first time, indicate string or not */
2020 return handlers[i].string;
2021 handlers[i].handler(ads, field, (struct berval **) values);
2022 break;
2025 if (!handlers[i].name) {
2026 if (!values) /* first time, indicate string conversion */
2027 return True;
2028 dump_string(field, (char **)values);
2030 return False;
2034 * Dump a result from LDAP on stdout
2035 * used for debugging
2036 * @param ads connection to ads server
2037 * @param res Results to dump
2040 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2042 ads_process_results(ads, res, ads_dump_field, NULL);
2046 * Walk through results, calling a function for each entry found.
2047 * The function receives a field name, a berval * array of values,
2048 * and a data area passed through from the start. The function is
2049 * called once with null for field and values at the end of each
2050 * entry.
2051 * @param ads connection to ads server
2052 * @param res Results to process
2053 * @param fn Function for processing each result
2054 * @param data_area user-defined area to pass to function
2056 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2057 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2058 void *data_area)
2060 LDAPMessage *msg;
2061 TALLOC_CTX *ctx;
2063 if (!(ctx = talloc_init("ads_process_results")))
2064 return;
2066 for (msg = ads_first_entry(ads, res); msg;
2067 msg = ads_next_entry(ads, msg)) {
2068 char *utf8_field;
2069 BerElement *b;
2071 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2072 (LDAPMessage *)msg,&b);
2073 utf8_field;
2074 utf8_field=ldap_next_attribute(ads->ldap.ld,
2075 (LDAPMessage *)msg,b)) {
2076 struct berval **ber_vals;
2077 char **str_vals, **utf8_vals;
2078 char *field;
2079 bool string;
2081 pull_utf8_talloc(ctx, &field, utf8_field);
2082 string = fn(ads, field, NULL, data_area);
2084 if (string) {
2085 utf8_vals = ldap_get_values(ads->ldap.ld,
2086 (LDAPMessage *)msg, field);
2087 str_vals = ads_pull_strvals(ctx,
2088 (const char **) utf8_vals);
2089 fn(ads, field, (void **) str_vals, data_area);
2090 ldap_value_free(utf8_vals);
2091 } else {
2092 ber_vals = ldap_get_values_len(ads->ldap.ld,
2093 (LDAPMessage *)msg, field);
2094 fn(ads, field, (void **) ber_vals, data_area);
2096 ldap_value_free_len(ber_vals);
2098 ldap_memfree(utf8_field);
2100 ber_free(b, 0);
2101 talloc_free_children(ctx);
2102 fn(ads, NULL, NULL, data_area); /* completed an entry */
2105 talloc_destroy(ctx);
2109 * count how many replies are in a LDAPMessage
2110 * @param ads connection to ads server
2111 * @param res Results to count
2112 * @return number of replies
2114 int ads_count_replies(ADS_STRUCT *ads, void *res)
2116 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2120 * pull the first entry from a ADS result
2121 * @param ads connection to ads server
2122 * @param res Results of search
2123 * @return first entry from result
2125 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2127 return ldap_first_entry(ads->ldap.ld, res);
2131 * pull the next entry from a ADS result
2132 * @param ads connection to ads server
2133 * @param res Results of search
2134 * @return next entry from result
2136 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2138 return ldap_next_entry(ads->ldap.ld, res);
2142 * pull the first message from a ADS result
2143 * @param ads connection to ads server
2144 * @param res Results of search
2145 * @return first message from result
2147 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2149 return ldap_first_message(ads->ldap.ld, res);
2153 * pull the next message from a ADS result
2154 * @param ads connection to ads server
2155 * @param res Results of search
2156 * @return next message from result
2158 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2160 return ldap_next_message(ads->ldap.ld, res);
2164 * pull a single string from a ADS result
2165 * @param ads connection to ads server
2166 * @param mem_ctx TALLOC_CTX to use for allocating result string
2167 * @param msg Results of search
2168 * @param field Attribute to retrieve
2169 * @return Result string in talloc context
2171 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2172 const char *field)
2174 char **values;
2175 char *ret = NULL;
2176 char *ux_string;
2177 size_t rc;
2179 values = ldap_get_values(ads->ldap.ld, msg, field);
2180 if (!values)
2181 return NULL;
2183 if (values[0]) {
2184 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2185 values[0]);
2186 if (rc != (size_t)-1)
2187 ret = ux_string;
2190 ldap_value_free(values);
2191 return ret;
2195 * pull an array of strings from a ADS result
2196 * @param ads connection to ads server
2197 * @param mem_ctx TALLOC_CTX to use for allocating result string
2198 * @param msg Results of search
2199 * @param field Attribute to retrieve
2200 * @return Result strings in talloc context
2202 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2203 LDAPMessage *msg, const char *field,
2204 size_t *num_values)
2206 char **values;
2207 char **ret = NULL;
2208 int i;
2210 values = ldap_get_values(ads->ldap.ld, msg, field);
2211 if (!values)
2212 return NULL;
2214 *num_values = ldap_count_values(values);
2216 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2217 if (!ret) {
2218 ldap_value_free(values);
2219 return NULL;
2222 for (i=0;i<*num_values;i++) {
2223 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2224 ldap_value_free(values);
2225 return NULL;
2228 ret[i] = NULL;
2230 ldap_value_free(values);
2231 return ret;
2235 * pull an array of strings from a ADS result
2236 * (handle large multivalue attributes with range retrieval)
2237 * @param ads connection to ads server
2238 * @param mem_ctx TALLOC_CTX to use for allocating result string
2239 * @param msg Results of search
2240 * @param field Attribute to retrieve
2241 * @param current_strings strings returned by a previous call to this function
2242 * @param next_attribute The next query should ask for this attribute
2243 * @param num_values How many values did we get this time?
2244 * @param more_values Are there more values to get?
2245 * @return Result strings in talloc context
2247 char **ads_pull_strings_range(ADS_STRUCT *ads,
2248 TALLOC_CTX *mem_ctx,
2249 LDAPMessage *msg, const char *field,
2250 char **current_strings,
2251 const char **next_attribute,
2252 size_t *num_strings,
2253 bool *more_strings)
2255 char *attr;
2256 char *expected_range_attrib, *range_attr;
2257 BerElement *ptr = NULL;
2258 char **strings;
2259 char **new_strings;
2260 size_t num_new_strings;
2261 unsigned long int range_start;
2262 unsigned long int range_end;
2264 /* we might have been given the whole lot anyway */
2265 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2266 *more_strings = False;
2267 return strings;
2270 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2272 /* look for Range result */
2273 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2274 attr;
2275 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2276 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2277 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2278 range_attr = attr;
2279 break;
2281 ldap_memfree(attr);
2283 if (!attr) {
2284 ber_free(ptr, 0);
2285 /* nothing here - this field is just empty */
2286 *more_strings = False;
2287 return NULL;
2290 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2291 &range_start, &range_end) == 2) {
2292 *more_strings = True;
2293 } else {
2294 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2295 &range_start) == 1) {
2296 *more_strings = False;
2297 } else {
2298 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2299 range_attr));
2300 ldap_memfree(range_attr);
2301 *more_strings = False;
2302 return NULL;
2306 if ((*num_strings) != range_start) {
2307 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2308 " - aborting range retreival\n",
2309 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2310 ldap_memfree(range_attr);
2311 *more_strings = False;
2312 return NULL;
2315 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2317 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2318 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2319 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2320 range_attr, (unsigned long int)range_end - range_start + 1,
2321 (unsigned long int)num_new_strings));
2322 ldap_memfree(range_attr);
2323 *more_strings = False;
2324 return NULL;
2327 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2328 *num_strings + num_new_strings);
2330 if (strings == NULL) {
2331 ldap_memfree(range_attr);
2332 *more_strings = False;
2333 return NULL;
2336 if (new_strings && num_new_strings) {
2337 memcpy(&strings[*num_strings], new_strings,
2338 sizeof(*new_strings) * num_new_strings);
2341 (*num_strings) += num_new_strings;
2343 if (*more_strings) {
2344 *next_attribute = talloc_asprintf(mem_ctx,
2345 "%s;range=%d-*",
2346 field,
2347 (int)*num_strings);
2349 if (!*next_attribute) {
2350 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2351 ldap_memfree(range_attr);
2352 *more_strings = False;
2353 return NULL;
2357 ldap_memfree(range_attr);
2359 return strings;
2363 * pull a single uint32 from a ADS result
2364 * @param ads connection to ads server
2365 * @param msg Results of search
2366 * @param field Attribute to retrieve
2367 * @param v Pointer to int to store result
2368 * @return boolean inidicating success
2370 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2371 uint32 *v)
2373 char **values;
2375 values = ldap_get_values(ads->ldap.ld, msg, field);
2376 if (!values)
2377 return False;
2378 if (!values[0]) {
2379 ldap_value_free(values);
2380 return False;
2383 *v = atoi(values[0]);
2384 ldap_value_free(values);
2385 return True;
2389 * pull a single objectGUID from an ADS result
2390 * @param ads connection to ADS server
2391 * @param msg results of search
2392 * @param guid 37-byte area to receive text guid
2393 * @return boolean indicating success
2395 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2397 char **values;
2398 UUID_FLAT flat_guid;
2400 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2401 if (!values)
2402 return False;
2404 if (values[0]) {
2405 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2406 smb_uuid_unpack(flat_guid, guid);
2407 ldap_value_free(values);
2408 return True;
2410 ldap_value_free(values);
2411 return False;
2417 * pull a single DOM_SID from a ADS result
2418 * @param ads connection to ads server
2419 * @param msg Results of search
2420 * @param field Attribute to retrieve
2421 * @param sid Pointer to sid to store result
2422 * @return boolean inidicating success
2424 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2425 DOM_SID *sid)
2427 struct berval **values;
2428 bool ret = False;
2430 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2432 if (!values)
2433 return False;
2435 if (values[0])
2436 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2438 ldap_value_free_len(values);
2439 return ret;
2443 * pull an array of DOM_SIDs from a ADS result
2444 * @param ads connection to ads server
2445 * @param mem_ctx TALLOC_CTX for allocating sid array
2446 * @param msg Results of search
2447 * @param field Attribute to retrieve
2448 * @param sids pointer to sid array to allocate
2449 * @return the count of SIDs pulled
2451 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2452 LDAPMessage *msg, const char *field, DOM_SID **sids)
2454 struct berval **values;
2455 bool ret;
2456 int count, i;
2458 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2460 if (!values)
2461 return 0;
2463 for (i=0; values[i]; i++)
2464 /* nop */ ;
2466 if (i) {
2467 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2468 if (!(*sids)) {
2469 ldap_value_free_len(values);
2470 return 0;
2472 } else {
2473 (*sids) = NULL;
2476 count = 0;
2477 for (i=0; values[i]; i++) {
2478 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2479 if (ret) {
2480 DEBUG(10, ("pulling SID: %s\n",
2481 sid_string_dbg(&(*sids)[count])));
2482 count++;
2486 ldap_value_free_len(values);
2487 return count;
2491 * pull a SEC_DESC from a ADS result
2492 * @param ads connection to ads server
2493 * @param mem_ctx TALLOC_CTX for allocating sid array
2494 * @param msg Results of search
2495 * @param field Attribute to retrieve
2496 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2497 * @return boolean inidicating success
2499 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2500 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2502 struct berval **values;
2503 bool ret = true;
2505 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2507 if (!values) return false;
2509 if (values[0]) {
2510 NTSTATUS status;
2511 status = unmarshall_sec_desc(mem_ctx,
2512 (uint8 *)values[0]->bv_val,
2513 values[0]->bv_len, sd);
2514 if (!NT_STATUS_IS_OK(status)) {
2515 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2516 nt_errstr(status)));
2517 ret = false;
2521 ldap_value_free_len(values);
2522 return ret;
2526 * in order to support usernames longer than 21 characters we need to
2527 * use both the sAMAccountName and the userPrincipalName attributes
2528 * It seems that not all users have the userPrincipalName attribute set
2530 * @param ads connection to ads server
2531 * @param mem_ctx TALLOC_CTX for allocating sid array
2532 * @param msg Results of search
2533 * @return the username
2535 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2536 LDAPMessage *msg)
2538 #if 0 /* JERRY */
2539 char *ret, *p;
2541 /* lookup_name() only works on the sAMAccountName to
2542 returning the username portion of userPrincipalName
2543 breaks winbindd_getpwnam() */
2545 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2546 if (ret && (p = strchr_m(ret, '@'))) {
2547 *p = 0;
2548 return ret;
2550 #endif
2551 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2556 * find the update serial number - this is the core of the ldap cache
2557 * @param ads connection to ads server
2558 * @param ads connection to ADS server
2559 * @param usn Pointer to retrieved update serial number
2560 * @return status of search
2562 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2564 const char *attrs[] = {"highestCommittedUSN", NULL};
2565 ADS_STATUS status;
2566 LDAPMessage *res;
2568 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2569 if (!ADS_ERR_OK(status))
2570 return status;
2572 if (ads_count_replies(ads, res) != 1) {
2573 ads_msgfree(ads, res);
2574 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2577 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2578 ads_msgfree(ads, res);
2579 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2582 ads_msgfree(ads, res);
2583 return ADS_SUCCESS;
2586 /* parse a ADS timestring - typical string is
2587 '20020917091222.0Z0' which means 09:12.22 17th September
2588 2002, timezone 0 */
2589 static time_t ads_parse_time(const char *str)
2591 struct tm tm;
2593 ZERO_STRUCT(tm);
2595 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2596 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2597 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2598 return 0;
2600 tm.tm_year -= 1900;
2601 tm.tm_mon -= 1;
2603 return timegm(&tm);
2606 /********************************************************************
2607 ********************************************************************/
2609 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2611 const char *attrs[] = {"currentTime", NULL};
2612 ADS_STATUS status;
2613 LDAPMessage *res;
2614 char *timestr;
2615 TALLOC_CTX *ctx;
2616 ADS_STRUCT *ads_s = ads;
2618 if (!(ctx = talloc_init("ads_current_time"))) {
2619 return ADS_ERROR(LDAP_NO_MEMORY);
2622 /* establish a new ldap tcp session if necessary */
2624 if ( !ads->ldap.ld ) {
2625 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2626 ads->server.ldap_server )) == NULL )
2628 goto done;
2630 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2631 status = ads_connect( ads_s );
2632 if ( !ADS_ERR_OK(status))
2633 goto done;
2636 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2637 if (!ADS_ERR_OK(status)) {
2638 goto done;
2641 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2642 if (!timestr) {
2643 ads_msgfree(ads_s, res);
2644 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2645 goto done;
2648 /* but save the time and offset in the original ADS_STRUCT */
2650 ads->config.current_time = ads_parse_time(timestr);
2652 if (ads->config.current_time != 0) {
2653 ads->auth.time_offset = ads->config.current_time - time(NULL);
2654 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2657 ads_msgfree(ads, res);
2659 status = ADS_SUCCESS;
2661 done:
2662 /* free any temporary ads connections */
2663 if ( ads_s != ads ) {
2664 ads_destroy( &ads_s );
2666 talloc_destroy(ctx);
2668 return status;
2671 /********************************************************************
2672 ********************************************************************/
2674 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2676 const char *attrs[] = {"domainFunctionality", NULL};
2677 ADS_STATUS status;
2678 LDAPMessage *res;
2679 ADS_STRUCT *ads_s = ads;
2681 *val = DS_DOMAIN_FUNCTION_2000;
2683 /* establish a new ldap tcp session if necessary */
2685 if ( !ads->ldap.ld ) {
2686 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2687 ads->server.ldap_server )) == NULL )
2689 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2690 goto done;
2692 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2693 status = ads_connect( ads_s );
2694 if ( !ADS_ERR_OK(status))
2695 goto done;
2698 /* If the attribute does not exist assume it is a Windows 2000
2699 functional domain */
2701 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2702 if (!ADS_ERR_OK(status)) {
2703 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2704 status = ADS_SUCCESS;
2706 goto done;
2709 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2710 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2712 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2715 ads_msgfree(ads, res);
2717 done:
2718 /* free any temporary ads connections */
2719 if ( ads_s != ads ) {
2720 ads_destroy( &ads_s );
2723 return status;
2727 * find the domain sid for our domain
2728 * @param ads connection to ads server
2729 * @param sid Pointer to domain sid
2730 * @return status of search
2732 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2734 const char *attrs[] = {"objectSid", NULL};
2735 LDAPMessage *res;
2736 ADS_STATUS rc;
2738 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2739 attrs, &res);
2740 if (!ADS_ERR_OK(rc)) return rc;
2741 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2742 ads_msgfree(ads, res);
2743 return ADS_ERROR_SYSTEM(ENOENT);
2745 ads_msgfree(ads, res);
2747 return ADS_SUCCESS;
2751 * find our site name
2752 * @param ads connection to ads server
2753 * @param mem_ctx Pointer to talloc context
2754 * @param site_name Pointer to the sitename
2755 * @return status of search
2757 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2759 ADS_STATUS status;
2760 LDAPMessage *res;
2761 const char *dn, *service_name;
2762 const char *attrs[] = { "dsServiceName", NULL };
2764 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2765 if (!ADS_ERR_OK(status)) {
2766 return status;
2769 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2770 if (service_name == NULL) {
2771 ads_msgfree(ads, res);
2772 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2775 ads_msgfree(ads, res);
2777 /* go up three levels */
2778 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2779 if (dn == NULL) {
2780 return ADS_ERROR(LDAP_NO_MEMORY);
2783 *site_name = talloc_strdup(mem_ctx, dn);
2784 if (*site_name == NULL) {
2785 return ADS_ERROR(LDAP_NO_MEMORY);
2788 return status;
2790 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2795 * find the site dn where a machine resides
2796 * @param ads connection to ads server
2797 * @param mem_ctx Pointer to talloc context
2798 * @param computer_name name of the machine
2799 * @param site_name Pointer to the sitename
2800 * @return status of search
2802 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2804 ADS_STATUS status;
2805 LDAPMessage *res;
2806 const char *parent, *filter;
2807 char *config_context = NULL;
2808 char *dn;
2810 /* shortcut a query */
2811 if (strequal(computer_name, ads->config.ldap_server_name)) {
2812 return ads_site_dn(ads, mem_ctx, site_dn);
2815 status = ads_config_path(ads, mem_ctx, &config_context);
2816 if (!ADS_ERR_OK(status)) {
2817 return status;
2820 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2821 if (filter == NULL) {
2822 return ADS_ERROR(LDAP_NO_MEMORY);
2825 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2826 filter, NULL, &res);
2827 if (!ADS_ERR_OK(status)) {
2828 return status;
2831 if (ads_count_replies(ads, res) != 1) {
2832 ads_msgfree(ads, res);
2833 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2836 dn = ads_get_dn(ads, res);
2837 if (dn == NULL) {
2838 ads_msgfree(ads, res);
2839 return ADS_ERROR(LDAP_NO_MEMORY);
2842 /* go up three levels */
2843 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2844 if (parent == NULL) {
2845 ads_msgfree(ads, res);
2846 ads_memfree(ads, dn);
2847 return ADS_ERROR(LDAP_NO_MEMORY);
2850 *site_dn = talloc_strdup(mem_ctx, parent);
2851 if (*site_dn == NULL) {
2852 ads_msgfree(ads, res);
2853 ads_memfree(ads, dn);
2854 return ADS_ERROR(LDAP_NO_MEMORY);
2857 ads_memfree(ads, dn);
2858 ads_msgfree(ads, res);
2860 return status;
2864 * get the upn suffixes for a domain
2865 * @param ads connection to ads server
2866 * @param mem_ctx Pointer to talloc context
2867 * @param suffixes Pointer to an array of suffixes
2868 * @param num_suffixes Pointer to the number of suffixes
2869 * @return status of search
2871 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2873 ADS_STATUS status;
2874 LDAPMessage *res;
2875 const char *base;
2876 char *config_context = NULL;
2877 const char *attrs[] = { "uPNSuffixes", NULL };
2879 status = ads_config_path(ads, mem_ctx, &config_context);
2880 if (!ADS_ERR_OK(status)) {
2881 return status;
2884 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2885 if (base == NULL) {
2886 return ADS_ERROR(LDAP_NO_MEMORY);
2889 status = ads_search_dn(ads, &res, base, attrs);
2890 if (!ADS_ERR_OK(status)) {
2891 return status;
2894 if (ads_count_replies(ads, res) != 1) {
2895 ads_msgfree(ads, res);
2896 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2899 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2900 if ((*suffixes) == NULL) {
2901 ads_msgfree(ads, res);
2902 return ADS_ERROR(LDAP_NO_MEMORY);
2905 ads_msgfree(ads, res);
2907 return status;
2911 * get the joinable ous for a domain
2912 * @param ads connection to ads server
2913 * @param mem_ctx Pointer to talloc context
2914 * @param ous Pointer to an array of ous
2915 * @param num_ous Pointer to the number of ous
2916 * @return status of search
2918 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2919 TALLOC_CTX *mem_ctx,
2920 char ***ous,
2921 size_t *num_ous)
2923 ADS_STATUS status;
2924 LDAPMessage *res = NULL;
2925 LDAPMessage *msg = NULL;
2926 const char *attrs[] = { "dn", NULL };
2927 int count = 0;
2929 status = ads_search(ads, &res,
2930 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2931 attrs);
2932 if (!ADS_ERR_OK(status)) {
2933 return status;
2936 count = ads_count_replies(ads, res);
2937 if (count < 1) {
2938 ads_msgfree(ads, res);
2939 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2942 for (msg = ads_first_entry(ads, res); msg;
2943 msg = ads_next_entry(ads, msg)) {
2945 char *dn = NULL;
2947 dn = ads_get_dn(ads, msg);
2948 if (!dn) {
2949 ads_msgfree(ads, res);
2950 return ADS_ERROR(LDAP_NO_MEMORY);
2953 if (!add_string_to_array(mem_ctx, dn,
2954 (const char ***)ous,
2955 (int *)num_ous)) {
2956 ads_memfree(ads, dn);
2957 ads_msgfree(ads, res);
2958 return ADS_ERROR(LDAP_NO_MEMORY);
2961 ads_memfree(ads, dn);
2964 ads_msgfree(ads, res);
2966 return status;
2971 * pull a DOM_SID from an extended dn string
2972 * @param mem_ctx TALLOC_CTX
2973 * @param extended_dn string
2974 * @param flags string type of extended_dn
2975 * @param sid pointer to a DOM_SID
2976 * @return NT_STATUS_OK on success,
2977 * NT_INVALID_PARAMETER on error,
2978 * NT_STATUS_NOT_FOUND if no SID present
2980 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2981 const char *extended_dn,
2982 enum ads_extended_dn_flags flags,
2983 DOM_SID *sid)
2985 char *p, *q, *dn;
2987 if (!extended_dn) {
2988 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2991 /* otherwise extended_dn gets stripped off */
2992 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2993 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2996 * ADS_EXTENDED_DN_HEX_STRING:
2997 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2999 * ADS_EXTENDED_DN_STRING (only with w2k3):
3000 * <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
3002 * Object with no SID, such as an Exchange Public Folder
3003 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3006 p = strchr(dn, ';');
3007 if (!p) {
3008 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3011 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3012 DEBUG(5,("No SID present in extended dn\n"));
3013 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3016 p += strlen(";<SID=");
3018 q = strchr(p, '>');
3019 if (!q) {
3020 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3023 *q = '\0';
3025 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3027 switch (flags) {
3029 case ADS_EXTENDED_DN_STRING:
3030 if (!string_to_sid(sid, p)) {
3031 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3033 break;
3034 case ADS_EXTENDED_DN_HEX_STRING: {
3035 fstring buf;
3036 size_t buf_len;
3038 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3039 if (buf_len == 0) {
3040 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3043 if (!sid_parse(buf, buf_len, sid)) {
3044 DEBUG(10,("failed to parse sid\n"));
3045 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3047 break;
3049 default:
3050 DEBUG(10,("unknown extended dn format\n"));
3051 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3054 return ADS_ERROR_NT(NT_STATUS_OK);
3058 * pull an array of DOM_SIDs from a ADS result
3059 * @param ads connection to ads server
3060 * @param mem_ctx TALLOC_CTX for allocating sid array
3061 * @param msg Results of search
3062 * @param field Attribute to retrieve
3063 * @param flags string type of extended_dn
3064 * @param sids pointer to sid array to allocate
3065 * @return the count of SIDs pulled
3067 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3068 TALLOC_CTX *mem_ctx,
3069 LDAPMessage *msg,
3070 const char *field,
3071 enum ads_extended_dn_flags flags,
3072 DOM_SID **sids)
3074 int i;
3075 ADS_STATUS rc;
3076 size_t dn_count, ret_count = 0;
3077 char **dn_strings;
3079 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3080 &dn_count)) == NULL) {
3081 return 0;
3084 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3085 if (!(*sids)) {
3086 TALLOC_FREE(dn_strings);
3087 return 0;
3090 for (i=0; i<dn_count; i++) {
3091 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3092 flags, &(*sids)[i]);
3093 if (!ADS_ERR_OK(rc)) {
3094 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3095 NT_STATUS_NOT_FOUND)) {
3096 continue;
3098 else {
3099 TALLOC_FREE(*sids);
3100 TALLOC_FREE(dn_strings);
3101 return 0;
3104 ret_count++;
3107 TALLOC_FREE(dn_strings);
3109 return ret_count;
3112 /********************************************************************
3113 ********************************************************************/
3115 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3117 LDAPMessage *res = NULL;
3118 ADS_STATUS status;
3119 int count = 0;
3120 char *name = NULL;
3122 status = ads_find_machine_acct(ads, &res, global_myname());
3123 if (!ADS_ERR_OK(status)) {
3124 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3125 global_myname()));
3126 goto out;
3129 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3130 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3131 goto out;
3134 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3135 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3138 out:
3139 ads_msgfree(ads, res);
3141 return name;
3144 /********************************************************************
3145 ********************************************************************/
3147 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3149 LDAPMessage *res = NULL;
3150 ADS_STATUS status;
3151 int count = 0;
3152 char *name = NULL;
3154 status = ads_find_machine_acct(ads, &res, machine_name);
3155 if (!ADS_ERR_OK(status)) {
3156 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3157 global_myname()));
3158 goto out;
3161 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3162 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3163 goto out;
3166 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3167 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3170 out:
3171 ads_msgfree(ads, res);
3173 return name;
3176 /********************************************************************
3177 ********************************************************************/
3179 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3181 LDAPMessage *res = NULL;
3182 ADS_STATUS status;
3183 int count = 0;
3184 char *name = NULL;
3186 status = ads_find_machine_acct(ads, &res, global_myname());
3187 if (!ADS_ERR_OK(status)) {
3188 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3189 global_myname()));
3190 goto out;
3193 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3194 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3195 goto out;
3198 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3199 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3202 out:
3203 ads_msgfree(ads, res);
3205 return name;
3208 #if 0
3210 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3213 * Join a machine to a realm
3214 * Creates the machine account and sets the machine password
3215 * @param ads connection to ads server
3216 * @param machine name of host to add
3217 * @param org_unit Organizational unit to place machine in
3218 * @return status of join
3220 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3221 uint32 account_type, const char *org_unit)
3223 ADS_STATUS status;
3224 LDAPMessage *res = NULL;
3225 char *machine;
3227 /* machine name must be lowercase */
3228 machine = SMB_STRDUP(machine_name);
3229 strlower_m(machine);
3232 status = ads_find_machine_acct(ads, (void **)&res, machine);
3233 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3234 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3235 status = ads_leave_realm(ads, machine);
3236 if (!ADS_ERR_OK(status)) {
3237 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3238 machine, ads->config.realm));
3239 return status;
3243 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3244 if (!ADS_ERR_OK(status)) {
3245 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3246 SAFE_FREE(machine);
3247 return status;
3250 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3251 if (!ADS_ERR_OK(status)) {
3252 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3253 SAFE_FREE(machine);
3254 return status;
3257 SAFE_FREE(machine);
3258 ads_msgfree(ads, res);
3260 return status;
3262 #endif
3265 * Delete a machine from the realm
3266 * @param ads connection to ads server
3267 * @param hostname Machine to remove
3268 * @return status of delete
3270 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3272 ADS_STATUS status;
3273 void *msg;
3274 LDAPMessage *res;
3275 char *hostnameDN, *host;
3276 int rc;
3277 LDAPControl ldap_control;
3278 LDAPControl * pldap_control[2] = {NULL, NULL};
3280 pldap_control[0] = &ldap_control;
3281 memset(&ldap_control, 0, sizeof(LDAPControl));
3282 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3284 /* hostname must be lowercase */
3285 host = SMB_STRDUP(hostname);
3286 strlower_m(host);
3288 status = ads_find_machine_acct(ads, &res, host);
3289 if (!ADS_ERR_OK(status)) {
3290 DEBUG(0, ("Host account for %s does not exist.\n", host));
3291 SAFE_FREE(host);
3292 return status;
3295 msg = ads_first_entry(ads, res);
3296 if (!msg) {
3297 SAFE_FREE(host);
3298 return ADS_ERROR_SYSTEM(ENOENT);
3301 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3303 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3304 if (rc) {
3305 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3306 }else {
3307 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3310 if (rc != LDAP_SUCCESS) {
3311 const char *attrs[] = { "cn", NULL };
3312 LDAPMessage *msg_sub;
3314 /* we only search with scope ONE, we do not expect any further
3315 * objects to be created deeper */
3317 status = ads_do_search_retry(ads, hostnameDN,
3318 LDAP_SCOPE_ONELEVEL,
3319 "(objectclass=*)", attrs, &res);
3321 if (!ADS_ERR_OK(status)) {
3322 SAFE_FREE(host);
3323 ads_memfree(ads, hostnameDN);
3324 return status;
3327 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3328 msg_sub = ads_next_entry(ads, msg_sub)) {
3330 char *dn = NULL;
3332 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3333 SAFE_FREE(host);
3334 ads_memfree(ads, hostnameDN);
3335 return ADS_ERROR(LDAP_NO_MEMORY);
3338 status = ads_del_dn(ads, dn);
3339 if (!ADS_ERR_OK(status)) {
3340 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3341 SAFE_FREE(host);
3342 ads_memfree(ads, dn);
3343 ads_memfree(ads, hostnameDN);
3344 return status;
3347 ads_memfree(ads, dn);
3350 /* there should be no subordinate objects anymore */
3351 status = ads_do_search_retry(ads, hostnameDN,
3352 LDAP_SCOPE_ONELEVEL,
3353 "(objectclass=*)", attrs, &res);
3355 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3356 SAFE_FREE(host);
3357 ads_memfree(ads, hostnameDN);
3358 return status;
3361 /* delete hostnameDN now */
3362 status = ads_del_dn(ads, hostnameDN);
3363 if (!ADS_ERR_OK(status)) {
3364 SAFE_FREE(host);
3365 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3366 ads_memfree(ads, hostnameDN);
3367 return status;
3371 ads_memfree(ads, hostnameDN);
3373 status = ads_find_machine_acct(ads, &res, host);
3374 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3375 DEBUG(3, ("Failed to remove host account.\n"));
3376 SAFE_FREE(host);
3377 return status;
3380 SAFE_FREE(host);
3381 return status;
3385 * pull all token-sids from an LDAP dn
3386 * @param ads connection to ads server
3387 * @param mem_ctx TALLOC_CTX for allocating sid array
3388 * @param dn of LDAP object
3389 * @param user_sid pointer to DOM_SID (objectSid)
3390 * @param primary_group_sid pointer to DOM_SID (self composed)
3391 * @param sids pointer to sid array to allocate
3392 * @param num_sids counter of SIDs pulled
3393 * @return status of token query
3395 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3396 TALLOC_CTX *mem_ctx,
3397 const char *dn,
3398 DOM_SID *user_sid,
3399 DOM_SID *primary_group_sid,
3400 DOM_SID **sids,
3401 size_t *num_sids)
3403 ADS_STATUS status;
3404 LDAPMessage *res = NULL;
3405 int count = 0;
3406 size_t tmp_num_sids;
3407 DOM_SID *tmp_sids;
3408 DOM_SID tmp_user_sid;
3409 DOM_SID tmp_primary_group_sid;
3410 uint32 pgid;
3411 const char *attrs[] = {
3412 "objectSid",
3413 "tokenGroups",
3414 "primaryGroupID",
3415 NULL
3418 status = ads_search_retry_dn(ads, &res, dn, attrs);
3419 if (!ADS_ERR_OK(status)) {
3420 return status;
3423 count = ads_count_replies(ads, res);
3424 if (count != 1) {
3425 ads_msgfree(ads, res);
3426 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3429 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3430 ads_msgfree(ads, res);
3431 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3434 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3435 ads_msgfree(ads, res);
3436 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3440 /* hack to compose the primary group sid without knowing the
3441 * domsid */
3443 DOM_SID domsid;
3444 uint32 dummy_rid;
3446 sid_copy(&domsid, &tmp_user_sid);
3448 if (!sid_split_rid(&domsid, &dummy_rid)) {
3449 ads_msgfree(ads, res);
3450 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3453 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3454 ads_msgfree(ads, res);
3455 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3459 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3461 if (tmp_num_sids == 0 || !tmp_sids) {
3462 ads_msgfree(ads, res);
3463 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3466 if (num_sids) {
3467 *num_sids = tmp_num_sids;
3470 if (sids) {
3471 *sids = tmp_sids;
3474 if (user_sid) {
3475 *user_sid = tmp_user_sid;
3478 if (primary_group_sid) {
3479 *primary_group_sid = tmp_primary_group_sid;
3482 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3484 ads_msgfree(ads, res);
3485 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3489 * Find a sAMAccoutName in LDAP
3490 * @param ads connection to ads server
3491 * @param mem_ctx TALLOC_CTX for allocating sid array
3492 * @param samaccountname to search
3493 * @param uac_ret uint32 pointer userAccountControl attribute value
3494 * @param dn_ret pointer to dn
3495 * @return status of token query
3497 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3498 TALLOC_CTX *mem_ctx,
3499 const char *samaccountname,
3500 uint32 *uac_ret,
3501 const char **dn_ret)
3503 ADS_STATUS status;
3504 const char *attrs[] = { "userAccountControl", NULL };
3505 const char *filter;
3506 LDAPMessage *res = NULL;
3507 char *dn = NULL;
3508 uint32 uac = 0;
3510 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3511 samaccountname);
3512 if (filter == NULL) {
3513 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3514 goto out;
3517 status = ads_do_search_all(ads, ads->config.bind_path,
3518 LDAP_SCOPE_SUBTREE,
3519 filter, attrs, &res);
3521 if (!ADS_ERR_OK(status)) {
3522 goto out;
3525 if (ads_count_replies(ads, res) != 1) {
3526 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3527 goto out;
3530 dn = ads_get_dn(ads, res);
3531 if (dn == NULL) {
3532 status = ADS_ERROR(LDAP_NO_MEMORY);
3533 goto out;
3536 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3537 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3538 goto out;
3541 if (uac_ret) {
3542 *uac_ret = uac;
3545 if (dn_ret) {
3546 *dn_ret = talloc_strdup(mem_ctx, dn);
3547 if (!*dn_ret) {
3548 status = ADS_ERROR(LDAP_NO_MEMORY);
3549 goto out;
3552 out:
3553 ads_memfree(ads, dn);
3554 ads_msgfree(ads, res);
3556 return status;
3560 * find our configuration path
3561 * @param ads connection to ads server
3562 * @param mem_ctx Pointer to talloc context
3563 * @param config_path Pointer to the config path
3564 * @return status of search
3566 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3567 TALLOC_CTX *mem_ctx,
3568 char **config_path)
3570 ADS_STATUS status;
3571 LDAPMessage *res = NULL;
3572 const char *config_context = NULL;
3573 const char *attrs[] = { "configurationNamingContext", NULL };
3575 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3576 "(objectclass=*)", attrs, &res);
3577 if (!ADS_ERR_OK(status)) {
3578 return status;
3581 config_context = ads_pull_string(ads, mem_ctx, res,
3582 "configurationNamingContext");
3583 ads_msgfree(ads, res);
3584 if (!config_context) {
3585 return ADS_ERROR(LDAP_NO_MEMORY);
3588 if (config_path) {
3589 *config_path = talloc_strdup(mem_ctx, config_context);
3590 if (!*config_path) {
3591 return ADS_ERROR(LDAP_NO_MEMORY);
3595 return ADS_ERROR(LDAP_SUCCESS);
3599 * find the displayName of an extended right
3600 * @param ads connection to ads server
3601 * @param config_path The config path
3602 * @param mem_ctx Pointer to talloc context
3603 * @param GUID struct of the rightsGUID
3604 * @return status of search
3606 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3607 const char *config_path,
3608 TALLOC_CTX *mem_ctx,
3609 const struct GUID *rights_guid)
3611 ADS_STATUS rc;
3612 LDAPMessage *res = NULL;
3613 char *expr = NULL;
3614 const char *attrs[] = { "displayName", NULL };
3615 const char *result = NULL;
3616 const char *path;
3618 if (!ads || !mem_ctx || !rights_guid) {
3619 goto done;
3622 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3623 smb_uuid_string(mem_ctx, *rights_guid));
3624 if (!expr) {
3625 goto done;
3628 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3629 if (!path) {
3630 goto done;
3633 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3634 expr, attrs, &res);
3635 if (!ADS_ERR_OK(rc)) {
3636 goto done;
3639 if (ads_count_replies(ads, res) != 1) {
3640 goto done;
3643 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3645 done:
3646 ads_msgfree(ads, res);
3647 return result;
3652 * verify or build and verify an account ou
3653 * @param mem_ctx Pointer to talloc context
3654 * @param ads connection to ads server
3655 * @param account_ou
3656 * @return status of search
3659 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3660 ADS_STRUCT *ads,
3661 const char **account_ou)
3663 struct ldb_dn *name_dn = NULL;
3664 const char *name = NULL;
3665 char *ou_string = NULL;
3667 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3668 if (name_dn) {
3669 return ADS_SUCCESS;
3672 ou_string = ads_ou_string(ads, *account_ou);
3673 if (!ou_string) {
3674 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3677 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3678 ads->config.bind_path);
3679 SAFE_FREE(ou_string);
3680 if (!name) {
3681 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3684 name_dn = ldb_dn_explode(mem_ctx, name);
3685 if (!name_dn) {
3686 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3689 *account_ou = talloc_strdup(mem_ctx, name);
3690 if (!*account_ou) {
3691 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3694 return ADS_SUCCESS;
3697 #endif