s3:libads/ldap.c: store the dc name in the saf cache as in all other places
[Samba.git] / source / libads / ldap.c
blob47b9f3e4d2c58ea8ccb1249dcb2bfd9bdf6e7c1c
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;
121 return result;
124 /**********************************************
125 Do client and server sitename match ?
126 **********************************************/
128 bool ads_sitename_match(ADS_STRUCT *ads)
130 if (ads->config.server_site_name == NULL &&
131 ads->config.client_site_name == NULL ) {
132 DEBUG(10,("ads_sitename_match: both null\n"));
133 return True;
135 if (ads->config.server_site_name &&
136 ads->config.client_site_name &&
137 strequal(ads->config.server_site_name,
138 ads->config.client_site_name)) {
139 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
140 return True;
142 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
143 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
144 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
145 return False;
148 /**********************************************
149 Is this the closest DC ?
150 **********************************************/
152 bool ads_closest_dc(ADS_STRUCT *ads)
154 if (ads->config.flags & NBT_SERVER_CLOSEST) {
155 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
156 return True;
159 /* not sure if this can ever happen */
160 if (ads_sitename_match(ads)) {
161 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
162 return True;
165 if (ads->config.client_site_name == NULL) {
166 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
167 return True;
170 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
171 ads->config.ldap_server_name));
173 return False;
178 try a connection to a given ldap server, returning True and setting the servers IP
179 in the ads struct if successful
181 bool ads_try_connect(ADS_STRUCT *ads, const char *server )
183 char *srv;
184 struct nbt_cldap_netlogon_5 cldap_reply;
185 TALLOC_CTX *mem_ctx = NULL;
186 bool ret = false;
188 if (!server || !*server) {
189 return False;
192 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
193 server, ads->server.realm));
195 mem_ctx = talloc_init("ads_try_connect");
196 if (!mem_ctx) {
197 DEBUG(0,("out of memory\n"));
198 return false;
201 /* this copes with inet_ntoa brokenness */
203 srv = SMB_STRDUP(server);
205 ZERO_STRUCT( cldap_reply );
207 if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
208 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
209 ret = false;
210 goto out;
213 /* Check the CLDAP reply flags */
215 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
216 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
217 srv));
218 ret = false;
219 goto out;
222 /* Fill in the ads->config values */
224 SAFE_FREE(ads->config.realm);
225 SAFE_FREE(ads->config.bind_path);
226 SAFE_FREE(ads->config.ldap_server_name);
227 SAFE_FREE(ads->config.server_site_name);
228 SAFE_FREE(ads->config.client_site_name);
229 SAFE_FREE(ads->server.workgroup);
231 ads->config.flags = cldap_reply.server_type;
232 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
233 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
234 strupper_m(ads->config.realm);
235 ads->config.bind_path = ads_build_dn(ads->config.realm);
236 if (*cldap_reply.server_site) {
237 ads->config.server_site_name =
238 SMB_STRDUP(cldap_reply.server_site);
240 if (*cldap_reply.client_site) {
241 ads->config.client_site_name =
242 SMB_STRDUP(cldap_reply.client_site);
244 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
246 ads->ldap.port = LDAP_PORT;
247 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
248 DEBUG(1,("ads_try_connect: unable to convert %s "
249 "to an address\n",
250 srv));
251 ret = false;
252 goto out;
255 /* Store our site name. */
256 sitename_store( cldap_reply.domain, cldap_reply.client_site);
257 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
259 ret = true;
260 out:
261 SAFE_FREE(srv);
262 TALLOC_FREE(mem_ctx);
264 return ret;
267 /**********************************************************************
268 Try to find an AD dc using our internal name resolution routines
269 Try the realm first and then then workgroup name if netbios is not
270 disabled
271 **********************************************************************/
273 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
275 const char *c_domain;
276 const char *c_realm;
277 int count, i=0;
278 struct ip_service *ip_list;
279 const char *realm;
280 const char *domain;
281 bool got_realm = False;
282 bool use_own_domain = False;
283 char *sitename;
284 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
286 /* if the realm and workgroup are both empty, assume they are ours */
288 /* realm */
289 c_realm = ads->server.realm;
291 if ( !c_realm || !*c_realm ) {
292 /* special case where no realm and no workgroup means our own */
293 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
294 use_own_domain = True;
295 c_realm = lp_realm();
299 if (c_realm && *c_realm)
300 got_realm = True;
302 /* we need to try once with the realm name and fallback to the
303 netbios domain name if we fail (if netbios has not been disabled */
305 if ( !got_realm && !lp_disable_netbios() ) {
306 c_realm = ads->server.workgroup;
307 if (!c_realm || !*c_realm) {
308 if ( use_own_domain )
309 c_realm = lp_workgroup();
313 if ( !c_realm || !*c_realm ) {
314 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
315 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
318 if ( use_own_domain ) {
319 c_domain = lp_workgroup();
320 } else {
321 c_domain = ads->server.workgroup;
324 realm = c_realm;
325 domain = c_domain;
328 * In case of LDAP we use get_dc_name() as that
329 * creates the custom krb5.conf file
331 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
332 fstring srv_name;
333 struct sockaddr_storage ip_out;
335 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
336 (got_realm ? "realm" : "domain"), realm));
338 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
340 * we call ads_try_connect() to fill in the
341 * ads->config details
343 if (ads_try_connect(ads, srv_name)) {
344 return NT_STATUS_OK;
348 return NT_STATUS_NO_LOGON_SERVERS;
351 sitename = sitename_fetch(realm);
353 again:
355 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
356 (got_realm ? "realm" : "domain"), realm));
358 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
359 if (!NT_STATUS_IS_OK(status)) {
360 /* fall back to netbios if we can */
361 if ( got_realm && !lp_disable_netbios() ) {
362 got_realm = False;
363 goto again;
366 SAFE_FREE(sitename);
367 return status;
370 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
371 for ( i=0; i<count; i++ ) {
372 char server[INET6_ADDRSTRLEN];
374 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
376 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
377 continue;
379 if (!got_realm) {
380 /* realm in this case is a workgroup name. We need
381 to ignore any IP addresses in the negative connection
382 cache that match ip addresses returned in the ad realm
383 case. It sucks that I have to reproduce the logic above... */
384 c_realm = ads->server.realm;
385 if ( !c_realm || !*c_realm ) {
386 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
387 c_realm = lp_realm();
390 if (c_realm && *c_realm &&
391 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
392 /* Ensure we add the workgroup name for this
393 IP address as negative too. */
394 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
395 continue;
399 if ( ads_try_connect(ads, server) ) {
400 SAFE_FREE(ip_list);
401 SAFE_FREE(sitename);
402 return NT_STATUS_OK;
405 /* keep track of failures */
406 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
409 SAFE_FREE(ip_list);
411 /* In case we failed to contact one of our closest DC on our site we
412 * need to try to find another DC, retry with a site-less SRV DNS query
413 * - Guenther */
415 if (sitename) {
416 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
417 "trying to find another DC\n", sitename));
418 SAFE_FREE(sitename);
419 namecache_delete(realm, 0x1C);
420 goto again;
423 return NT_STATUS_NO_LOGON_SERVERS;
428 * Connect to the LDAP server
429 * @param ads Pointer to an existing ADS_STRUCT
430 * @return status of connection
432 ADS_STATUS ads_connect(ADS_STRUCT *ads)
434 int version = LDAP_VERSION3;
435 ADS_STATUS status;
436 NTSTATUS ntstatus;
437 char addr[INET6_ADDRSTRLEN];
439 ZERO_STRUCT(ads->ldap);
440 ads->ldap.last_attempt = time(NULL);
441 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
443 /* try with a user specified server */
445 if (DEBUGLEVEL >= 11) {
446 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
447 DEBUG(11,("ads_connect: entering\n"));
448 DEBUGADD(11,("%s\n", s));
449 TALLOC_FREE(s);
452 if (ads->server.ldap_server &&
453 ads_try_connect(ads, ads->server.ldap_server)) {
454 goto got_connection;
457 ntstatus = ads_find_dc(ads);
458 if (NT_STATUS_IS_OK(ntstatus)) {
459 goto got_connection;
462 status = ADS_ERROR_NT(ntstatus);
463 goto out;
465 got_connection:
467 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
468 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
470 if (!ads->auth.user_name) {
471 /* Must use the userPrincipalName value here or sAMAccountName
472 and not servicePrincipalName; found by Guenther Deschner */
474 asprintf(&ads->auth.user_name, "%s$", global_myname() );
477 if (!ads->auth.realm) {
478 ads->auth.realm = SMB_STRDUP(ads->config.realm);
481 if (!ads->auth.kdc_server) {
482 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
483 ads->auth.kdc_server = SMB_STRDUP(addr);
486 #if KRB5_DNS_HACK
487 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
488 to MIT kerberos to work (tridge) */
490 char *env;
491 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
492 setenv(env, ads->auth.kdc_server, 1);
493 free(env);
495 #endif
497 /* If the caller() requested no LDAP bind, then we are done */
499 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
500 status = ADS_SUCCESS;
501 goto out;
504 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
505 if (!ads->ldap.mem_ctx) {
506 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
507 goto out;
510 /* Otherwise setup the TCP LDAP session */
512 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
513 LDAP_PORT, lp_ldap_timeout());
514 if (ads->ldap.ld == NULL) {
515 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
516 goto out;
518 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
520 /* cache the successful connection for workgroup and realm */
521 if (ads_closest_dc(ads)) {
522 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
523 saf_store( ads->server.realm, ads->config.ldap_server_name);
526 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
528 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
529 if (!ADS_ERR_OK(status)) {
530 goto out;
533 /* fill in the current time and offsets */
535 status = ads_current_time( ads );
536 if ( !ADS_ERR_OK(status) ) {
537 goto out;
540 /* Now do the bind */
542 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
543 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
544 goto out;
547 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
548 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
549 goto out;
552 status = ads_sasl_bind(ads);
554 out:
555 if (DEBUGLEVEL >= 11) {
556 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
557 DEBUG(11,("ads_connect: leaving with: %s\n",
558 ads_errstr(status)));
559 DEBUGADD(11,("%s\n", s));
560 TALLOC_FREE(s);
563 return status;
567 * Disconnect the LDAP server
568 * @param ads Pointer to an existing ADS_STRUCT
570 void ads_disconnect(ADS_STRUCT *ads)
572 if (ads->ldap.ld) {
573 ldap_unbind(ads->ldap.ld);
574 ads->ldap.ld = NULL;
576 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
577 ads->ldap.wrap_ops->disconnect(ads);
579 if (ads->ldap.mem_ctx) {
580 talloc_free(ads->ldap.mem_ctx);
582 ZERO_STRUCT(ads->ldap);
586 Duplicate a struct berval into talloc'ed memory
588 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
590 struct berval *value;
592 if (!in_val) return NULL;
594 value = TALLOC_ZERO_P(ctx, struct berval);
595 if (value == NULL)
596 return NULL;
597 if (in_val->bv_len == 0) return value;
599 value->bv_len = in_val->bv_len;
600 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
601 in_val->bv_len);
602 return value;
606 Make a values list out of an array of (struct berval *)
608 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
609 const struct berval **in_vals)
611 struct berval **values;
612 int i;
614 if (!in_vals) return NULL;
615 for (i=0; in_vals[i]; i++)
616 ; /* count values */
617 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
618 if (!values) return NULL;
620 for (i=0; in_vals[i]; i++) {
621 values[i] = dup_berval(ctx, in_vals[i]);
623 return values;
627 UTF8-encode a values list out of an array of (char *)
629 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
631 char **values;
632 int i;
634 if (!in_vals) return NULL;
635 for (i=0; in_vals[i]; i++)
636 ; /* count values */
637 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
638 if (!values) return NULL;
640 for (i=0; in_vals[i]; i++) {
641 if (push_utf8_talloc(ctx, &values[i], in_vals[i]) == (size_t) -1) {
642 TALLOC_FREE(values);
643 return NULL;
646 return values;
650 Pull a (char *) array out of a UTF8-encoded values list
652 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
654 char **values;
655 int i;
657 if (!in_vals) return NULL;
658 for (i=0; in_vals[i]; i++)
659 ; /* count values */
660 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
661 if (!values) return NULL;
663 for (i=0; in_vals[i]; i++) {
664 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
666 return values;
670 * Do a search with paged results. cookie must be null on the first
671 * call, and then returned on each subsequent call. It will be null
672 * again when the entire search is complete
673 * @param ads connection to ads server
674 * @param bind_path Base dn for the search
675 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
676 * @param expr Search expression - specified in local charset
677 * @param attrs Attributes to retrieve - specified in utf8 or ascii
678 * @param res ** which will contain results - free res* with ads_msgfree()
679 * @param count Number of entries retrieved on this page
680 * @param cookie The paged results cookie to be returned on subsequent calls
681 * @return status of search
683 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
684 const char *bind_path,
685 int scope, const char *expr,
686 const char **attrs, void *args,
687 LDAPMessage **res,
688 int *count, struct berval **cookie)
690 int rc, i, version;
691 char *utf8_expr, *utf8_path, **search_attrs;
692 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
693 BerElement *cookie_be = NULL;
694 struct berval *cookie_bv= NULL;
695 BerElement *ext_be = NULL;
696 struct berval *ext_bv= NULL;
698 TALLOC_CTX *ctx;
699 ads_control *external_control = (ads_control *) args;
701 *res = NULL;
703 if (!(ctx = talloc_init("ads_do_paged_search_args")))
704 return ADS_ERROR(LDAP_NO_MEMORY);
706 /* 0 means the conversion worked but the result was empty
707 so we only fail if it's -1. In any case, it always
708 at least nulls out the dest */
709 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
710 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
711 rc = LDAP_NO_MEMORY;
712 goto done;
715 if (!attrs || !(*attrs))
716 search_attrs = NULL;
717 else {
718 /* This would be the utf8-encoded version...*/
719 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
720 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
721 rc = LDAP_NO_MEMORY;
722 goto done;
726 /* Paged results only available on ldap v3 or later */
727 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
728 if (version < LDAP_VERSION3) {
729 rc = LDAP_NOT_SUPPORTED;
730 goto done;
733 cookie_be = ber_alloc_t(LBER_USE_DER);
734 if (*cookie) {
735 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
736 ber_bvfree(*cookie); /* don't need it from last time */
737 *cookie = NULL;
738 } else {
739 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
741 ber_flatten(cookie_be, &cookie_bv);
742 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
743 PagedResults.ldctl_iscritical = (char) 1;
744 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
745 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
747 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
748 NoReferrals.ldctl_iscritical = (char) 0;
749 NoReferrals.ldctl_value.bv_len = 0;
750 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
752 if (external_control &&
753 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
754 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
756 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
757 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
759 /* win2k does not accept a ldctl_value beeing passed in */
761 if (external_control->val != 0) {
763 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
764 rc = LDAP_NO_MEMORY;
765 goto done;
768 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
769 rc = LDAP_NO_MEMORY;
770 goto done;
772 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
773 rc = LDAP_NO_MEMORY;
774 goto done;
777 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
778 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
780 } else {
781 ExternalCtrl.ldctl_value.bv_len = 0;
782 ExternalCtrl.ldctl_value.bv_val = NULL;
785 controls[0] = &NoReferrals;
786 controls[1] = &PagedResults;
787 controls[2] = &ExternalCtrl;
788 controls[3] = NULL;
790 } else {
791 controls[0] = &NoReferrals;
792 controls[1] = &PagedResults;
793 controls[2] = NULL;
796 /* we need to disable referrals as the openldap libs don't
797 handle them and paged results at the same time. Using them
798 together results in the result record containing the server
799 page control being removed from the result list (tridge/jmcd)
801 leaving this in despite the control that says don't generate
802 referrals, in case the server doesn't support it (jmcd)
804 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
806 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
807 search_attrs, 0, controls,
808 NULL, LDAP_NO_LIMIT,
809 (LDAPMessage **)res);
811 ber_free(cookie_be, 1);
812 ber_bvfree(cookie_bv);
814 if (rc) {
815 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
816 ldap_err2string(rc)));
817 goto done;
820 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
821 NULL, &rcontrols, 0);
823 if (!rcontrols) {
824 goto done;
827 for (i=0; rcontrols[i]; i++) {
828 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
829 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
830 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
831 &cookie_bv);
832 /* the berval is the cookie, but must be freed when
833 it is all done */
834 if (cookie_bv->bv_len) /* still more to do */
835 *cookie=ber_bvdup(cookie_bv);
836 else
837 *cookie=NULL;
838 ber_bvfree(cookie_bv);
839 ber_free(cookie_be, 1);
840 break;
843 ldap_controls_free(rcontrols);
845 done:
846 talloc_destroy(ctx);
848 if (ext_be) {
849 ber_free(ext_be, 1);
852 if (ext_bv) {
853 ber_bvfree(ext_bv);
856 /* if/when we decide to utf8-encode attrs, take out this next line */
857 TALLOC_FREE(search_attrs);
859 return ADS_ERROR(rc);
862 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
863 int scope, const char *expr,
864 const char **attrs, LDAPMessage **res,
865 int *count, struct berval **cookie)
867 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
872 * Get all results for a search. This uses ads_do_paged_search() to return
873 * all entries in a large search.
874 * @param ads connection to ads server
875 * @param bind_path Base dn for the search
876 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
877 * @param expr Search expression
878 * @param attrs Attributes to retrieve
879 * @param res ** which will contain results - free res* with ads_msgfree()
880 * @return status of search
882 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
883 int scope, const char *expr,
884 const char **attrs, void *args,
885 LDAPMessage **res)
887 struct berval *cookie = NULL;
888 int count = 0;
889 ADS_STATUS status;
891 *res = NULL;
892 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
893 &count, &cookie);
895 if (!ADS_ERR_OK(status))
896 return status;
898 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
899 while (cookie) {
900 LDAPMessage *res2 = NULL;
901 ADS_STATUS status2;
902 LDAPMessage *msg, *next;
904 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
905 attrs, args, &res2, &count, &cookie);
907 if (!ADS_ERR_OK(status2)) break;
909 /* this relies on the way that ldap_add_result_entry() works internally. I hope
910 that this works on all ldap libs, but I have only tested with openldap */
911 for (msg = ads_first_message(ads, res2); msg; msg = next) {
912 next = ads_next_message(ads, msg);
913 ldap_add_result_entry((LDAPMessage **)res, msg);
915 /* note that we do not free res2, as the memory is now
916 part of the main returned list */
918 #else
919 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
920 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
921 #endif
923 return status;
926 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
927 int scope, const char *expr,
928 const char **attrs, LDAPMessage **res)
930 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
933 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
934 int scope, const char *expr,
935 const char **attrs, uint32 sd_flags,
936 LDAPMessage **res)
938 ads_control args;
940 args.control = ADS_SD_FLAGS_OID;
941 args.val = sd_flags;
942 args.critical = True;
944 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
949 * Run a function on all results for a search. Uses ads_do_paged_search() and
950 * runs the function as each page is returned, using ads_process_results()
951 * @param ads connection to ads server
952 * @param bind_path Base dn for the search
953 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
954 * @param expr Search expression - specified in local charset
955 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
956 * @param fn Function which takes attr name, values list, and data_area
957 * @param data_area Pointer which is passed to function on each call
958 * @return status of search
960 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
961 int scope, const char *expr, const char **attrs,
962 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
963 void *data_area)
965 struct berval *cookie = NULL;
966 int count = 0;
967 ADS_STATUS status;
968 LDAPMessage *res;
970 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
971 &count, &cookie);
973 if (!ADS_ERR_OK(status)) return status;
975 ads_process_results(ads, res, fn, data_area);
976 ads_msgfree(ads, res);
978 while (cookie) {
979 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
980 &res, &count, &cookie);
982 if (!ADS_ERR_OK(status)) break;
984 ads_process_results(ads, res, fn, data_area);
985 ads_msgfree(ads, res);
988 return status;
992 * Do a search with a timeout.
993 * @param ads connection to ads server
994 * @param bind_path Base dn for the search
995 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
996 * @param expr Search expression
997 * @param attrs Attributes to retrieve
998 * @param res ** which will contain results - free res* with ads_msgfree()
999 * @return status of search
1001 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1002 const char *expr,
1003 const char **attrs, LDAPMessage **res)
1005 int rc;
1006 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1007 TALLOC_CTX *ctx;
1009 *res = NULL;
1010 if (!(ctx = talloc_init("ads_do_search"))) {
1011 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1012 return ADS_ERROR(LDAP_NO_MEMORY);
1015 /* 0 means the conversion worked but the result was empty
1016 so we only fail if it's negative. In any case, it always
1017 at least nulls out the dest */
1018 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
1019 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
1020 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1021 rc = LDAP_NO_MEMORY;
1022 goto done;
1025 if (!attrs || !(*attrs))
1026 search_attrs = NULL;
1027 else {
1028 /* This would be the utf8-encoded version...*/
1029 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1030 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
1032 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1033 rc = LDAP_NO_MEMORY;
1034 goto done;
1038 /* see the note in ads_do_paged_search - we *must* disable referrals */
1039 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1041 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1042 search_attrs, 0, NULL, NULL,
1043 LDAP_NO_LIMIT,
1044 (LDAPMessage **)res);
1046 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1047 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1048 rc = 0;
1051 done:
1052 talloc_destroy(ctx);
1053 /* if/when we decide to utf8-encode attrs, take out this next line */
1054 TALLOC_FREE(search_attrs);
1055 return ADS_ERROR(rc);
1058 * Do a general ADS search
1059 * @param ads connection to ads server
1060 * @param res ** which will contain results - free res* with ads_msgfree()
1061 * @param expr Search expression
1062 * @param attrs Attributes to retrieve
1063 * @return status of search
1065 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1066 const char *expr, const char **attrs)
1068 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1069 expr, attrs, res);
1073 * Do a search on a specific DistinguishedName
1074 * @param ads connection to ads server
1075 * @param res ** which will contain results - free res* with ads_msgfree()
1076 * @param dn DistinguishName to search
1077 * @param attrs Attributes to retrieve
1078 * @return status of search
1080 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1081 const char *dn, const char **attrs)
1083 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1084 attrs, res);
1088 * Free up memory from a ads_search
1089 * @param ads connection to ads server
1090 * @param msg Search results to free
1092 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1094 if (!msg) return;
1095 ldap_msgfree(msg);
1099 * Free up memory from various ads requests
1100 * @param ads connection to ads server
1101 * @param mem Area to free
1103 void ads_memfree(ADS_STRUCT *ads, void *mem)
1105 SAFE_FREE(mem);
1109 * Get a dn from search results
1110 * @param ads connection to ads server
1111 * @param msg Search result
1112 * @return dn string
1114 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1116 char *utf8_dn, *unix_dn;
1118 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1120 if (!utf8_dn) {
1121 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1122 return NULL;
1125 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1126 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1127 utf8_dn ));
1128 return NULL;
1130 ldap_memfree(utf8_dn);
1131 return unix_dn;
1135 * Get the parent from a dn
1136 * @param dn the dn to return the parent from
1137 * @return parent dn string
1139 char *ads_parent_dn(const char *dn)
1141 char *p;
1143 if (dn == NULL) {
1144 return NULL;
1147 p = strchr(dn, ',');
1149 if (p == NULL) {
1150 return NULL;
1153 return p+1;
1157 * Find a machine account given a hostname
1158 * @param ads connection to ads server
1159 * @param res ** which will contain results - free res* with ads_msgfree()
1160 * @param host Hostname to search for
1161 * @return status of search
1163 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1164 const char *machine)
1166 ADS_STATUS status;
1167 char *expr;
1168 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1170 *res = NULL;
1172 /* the easiest way to find a machine account anywhere in the tree
1173 is to look for hostname$ */
1174 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1175 DEBUG(1, ("asprintf failed!\n"));
1176 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1179 status = ads_search(ads, res, expr, attrs);
1180 SAFE_FREE(expr);
1181 return status;
1185 * Initialize a list of mods to be used in a modify request
1186 * @param ctx An initialized TALLOC_CTX
1187 * @return allocated ADS_MODLIST
1189 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1191 #define ADS_MODLIST_ALLOC_SIZE 10
1192 LDAPMod **mods;
1194 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1195 /* -1 is safety to make sure we don't go over the end.
1196 need to reset it to NULL before doing ldap modify */
1197 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1199 return (ADS_MODLIST)mods;
1204 add an attribute to the list, with values list already constructed
1206 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1207 int mod_op, const char *name,
1208 const void *_invals)
1210 const void **invals = (const void **)_invals;
1211 int curmod;
1212 LDAPMod **modlist = (LDAPMod **) *mods;
1213 struct berval **ber_values = NULL;
1214 char **char_values = NULL;
1216 if (!invals) {
1217 mod_op = LDAP_MOD_DELETE;
1218 } else {
1219 if (mod_op & LDAP_MOD_BVALUES)
1220 ber_values = ads_dup_values(ctx,
1221 (const struct berval **)invals);
1222 else
1223 char_values = ads_push_strvals(ctx,
1224 (const char **) invals);
1227 /* find the first empty slot */
1228 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1229 curmod++);
1230 if (modlist[curmod] == (LDAPMod *) -1) {
1231 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1232 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1233 return ADS_ERROR(LDAP_NO_MEMORY);
1234 memset(&modlist[curmod], 0,
1235 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1236 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1237 *mods = (ADS_MODLIST)modlist;
1240 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1241 return ADS_ERROR(LDAP_NO_MEMORY);
1242 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1243 if (mod_op & LDAP_MOD_BVALUES) {
1244 modlist[curmod]->mod_bvalues = ber_values;
1245 } else if (mod_op & LDAP_MOD_DELETE) {
1246 modlist[curmod]->mod_values = NULL;
1247 } else {
1248 modlist[curmod]->mod_values = char_values;
1251 modlist[curmod]->mod_op = mod_op;
1252 return ADS_ERROR(LDAP_SUCCESS);
1256 * Add a single string value to a mod list
1257 * @param ctx An initialized TALLOC_CTX
1258 * @param mods An initialized ADS_MODLIST
1259 * @param name The attribute name to add
1260 * @param val The value to add - NULL means DELETE
1261 * @return ADS STATUS indicating success of add
1263 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1264 const char *name, const char *val)
1266 const char *values[2];
1268 values[0] = val;
1269 values[1] = NULL;
1271 if (!val)
1272 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1273 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1277 * Add an array of string values to a mod list
1278 * @param ctx An initialized TALLOC_CTX
1279 * @param mods An initialized ADS_MODLIST
1280 * @param name The attribute name to add
1281 * @param vals The array of string values to add - NULL means DELETE
1282 * @return ADS STATUS indicating success of add
1284 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1285 const char *name, const char **vals)
1287 if (!vals)
1288 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1289 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1290 name, (const void **) vals);
1293 #if 0
1295 * Add a single ber-encoded value to a mod list
1296 * @param ctx An initialized TALLOC_CTX
1297 * @param mods An initialized ADS_MODLIST
1298 * @param name The attribute name to add
1299 * @param val The value to add - NULL means DELETE
1300 * @return ADS STATUS indicating success of add
1302 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1303 const char *name, const struct berval *val)
1305 const struct berval *values[2];
1307 values[0] = val;
1308 values[1] = NULL;
1309 if (!val)
1310 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1311 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1312 name, (const void **) values);
1314 #endif
1317 * Perform an ldap modify
1318 * @param ads connection to ads server
1319 * @param mod_dn DistinguishedName to modify
1320 * @param mods list of modifications to perform
1321 * @return status of modify
1323 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1325 int ret,i;
1326 char *utf8_dn = NULL;
1328 this control is needed to modify that contains a currently
1329 non-existent attribute (but allowable for the object) to run
1331 LDAPControl PermitModify = {
1332 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1333 {0, NULL},
1334 (char) 1};
1335 LDAPControl *controls[2];
1337 controls[0] = &PermitModify;
1338 controls[1] = NULL;
1340 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1341 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1344 /* find the end of the list, marked by NULL or -1 */
1345 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1346 /* make sure the end of the list is NULL */
1347 mods[i] = NULL;
1348 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1349 (LDAPMod **) mods, controls, NULL);
1350 SAFE_FREE(utf8_dn);
1351 return ADS_ERROR(ret);
1355 * Perform an ldap add
1356 * @param ads connection to ads server
1357 * @param new_dn DistinguishedName to add
1358 * @param mods list of attributes and values for DN
1359 * @return status of add
1361 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1363 int ret, i;
1364 char *utf8_dn = NULL;
1366 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1367 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1368 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1371 /* find the end of the list, marked by NULL or -1 */
1372 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1373 /* make sure the end of the list is NULL */
1374 mods[i] = NULL;
1376 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1377 SAFE_FREE(utf8_dn);
1378 return ADS_ERROR(ret);
1382 * Delete a DistinguishedName
1383 * @param ads connection to ads server
1384 * @param new_dn DistinguishedName to delete
1385 * @return status of delete
1387 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1389 int ret;
1390 char *utf8_dn = NULL;
1391 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1392 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1393 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1396 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1397 SAFE_FREE(utf8_dn);
1398 return ADS_ERROR(ret);
1402 * Build an org unit string
1403 * if org unit is Computers or blank then assume a container, otherwise
1404 * assume a / separated list of organisational units.
1405 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1406 * @param ads connection to ads server
1407 * @param org_unit Organizational unit
1408 * @return org unit string - caller must free
1410 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1412 char *ret = NULL;
1414 if (!org_unit || !*org_unit) {
1416 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1418 /* samba4 might not yet respond to a wellknownobject-query */
1419 return ret ? ret : SMB_STRDUP("cn=Computers");
1422 if (strequal(org_unit, "Computers")) {
1423 return SMB_STRDUP("cn=Computers");
1426 /* jmcd: removed "\\" from the separation chars, because it is
1427 needed as an escape for chars like '#' which are valid in an
1428 OU name */
1429 return ads_build_path(org_unit, "/", "ou=", 1);
1433 * Get a org unit string for a well-known GUID
1434 * @param ads connection to ads server
1435 * @param wknguid Well known GUID
1436 * @return org unit string - caller must free
1438 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1440 ADS_STATUS status;
1441 LDAPMessage *res = NULL;
1442 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1443 **bind_dn_exp = NULL;
1444 const char *attrs[] = {"distinguishedName", NULL};
1445 int new_ln, wkn_ln, bind_ln, i;
1447 if (wknguid == NULL) {
1448 return NULL;
1451 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1452 DEBUG(1, ("asprintf failed!\n"));
1453 return NULL;
1456 status = ads_search_dn(ads, &res, base, attrs);
1457 if (!ADS_ERR_OK(status)) {
1458 DEBUG(1,("Failed while searching for: %s\n", base));
1459 goto out;
1462 if (ads_count_replies(ads, res) != 1) {
1463 goto out;
1466 /* substitute the bind-path from the well-known-guid-search result */
1467 wkn_dn = ads_get_dn(ads, res);
1468 if (!wkn_dn) {
1469 goto out;
1472 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1473 if (!wkn_dn_exp) {
1474 goto out;
1477 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1478 if (!bind_dn_exp) {
1479 goto out;
1482 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1484 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1487 new_ln = wkn_ln - bind_ln;
1489 ret = SMB_STRDUP(wkn_dn_exp[0]);
1490 if (!ret) {
1491 goto out;
1494 for (i=1; i < new_ln; i++) {
1495 char *s = NULL;
1497 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1498 SAFE_FREE(ret);
1499 goto out;
1502 SAFE_FREE(ret);
1503 ret = SMB_STRDUP(s);
1504 free(s);
1505 if (!ret) {
1506 goto out;
1510 out:
1511 SAFE_FREE(base);
1512 ads_msgfree(ads, res);
1513 ads_memfree(ads, wkn_dn);
1514 if (wkn_dn_exp) {
1515 ldap_value_free(wkn_dn_exp);
1517 if (bind_dn_exp) {
1518 ldap_value_free(bind_dn_exp);
1521 return ret;
1525 * Adds (appends) an item to an attribute array, rather then
1526 * replacing the whole list
1527 * @param ctx An initialized TALLOC_CTX
1528 * @param mods An initialized ADS_MODLIST
1529 * @param name name of the ldap attribute to append to
1530 * @param vals an array of values to add
1531 * @return status of addition
1534 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1535 const char *name, const char **vals)
1537 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1538 (const void *) vals);
1542 * Determines the computer account's current KVNO via an LDAP lookup
1543 * @param ads An initialized ADS_STRUCT
1544 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1545 * @return the kvno for the computer account, or -1 in case of a failure.
1548 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1550 LDAPMessage *res = NULL;
1551 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1552 char *filter;
1553 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1554 char *dn_string = NULL;
1555 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1557 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1558 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1559 return kvno;
1561 ret = ads_search(ads, &res, filter, attrs);
1562 SAFE_FREE(filter);
1563 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1564 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1565 ads_msgfree(ads, res);
1566 return kvno;
1569 dn_string = ads_get_dn(ads, res);
1570 if (!dn_string) {
1571 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1572 ads_msgfree(ads, res);
1573 return kvno;
1575 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1576 ads_memfree(ads, dn_string);
1578 /* ---------------------------------------------------------
1579 * 0 is returned as a default KVNO from this point on...
1580 * This is done because Windows 2000 does not support key
1581 * version numbers. Chances are that a failure in the next
1582 * step is simply due to Windows 2000 being used for a
1583 * domain controller. */
1584 kvno = 0;
1586 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1587 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1588 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1589 ads_msgfree(ads, res);
1590 return kvno;
1593 /* Success */
1594 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1595 ads_msgfree(ads, res);
1596 return kvno;
1600 * This clears out all registered spn's for a given hostname
1601 * @param ads An initilaized ADS_STRUCT
1602 * @param machine_name the NetBIOS name of the computer.
1603 * @return 0 upon success, non-zero otherwise.
1606 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1608 TALLOC_CTX *ctx;
1609 LDAPMessage *res = NULL;
1610 ADS_MODLIST mods;
1611 const char *servicePrincipalName[1] = {NULL};
1612 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1613 char *dn_string = NULL;
1615 ret = ads_find_machine_acct(ads, &res, machine_name);
1616 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1617 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1618 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1619 ads_msgfree(ads, res);
1620 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1623 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1624 ctx = talloc_init("ads_clear_service_principal_names");
1625 if (!ctx) {
1626 ads_msgfree(ads, res);
1627 return ADS_ERROR(LDAP_NO_MEMORY);
1630 if (!(mods = ads_init_mods(ctx))) {
1631 talloc_destroy(ctx);
1632 ads_msgfree(ads, res);
1633 return ADS_ERROR(LDAP_NO_MEMORY);
1635 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1636 if (!ADS_ERR_OK(ret)) {
1637 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1638 ads_msgfree(ads, res);
1639 talloc_destroy(ctx);
1640 return ret;
1642 dn_string = ads_get_dn(ads, res);
1643 if (!dn_string) {
1644 talloc_destroy(ctx);
1645 ads_msgfree(ads, res);
1646 return ADS_ERROR(LDAP_NO_MEMORY);
1648 ret = ads_gen_mod(ads, dn_string, mods);
1649 ads_memfree(ads,dn_string);
1650 if (!ADS_ERR_OK(ret)) {
1651 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1652 machine_name));
1653 ads_msgfree(ads, res);
1654 talloc_destroy(ctx);
1655 return ret;
1658 ads_msgfree(ads, res);
1659 talloc_destroy(ctx);
1660 return ret;
1664 * This adds a service principal name to an existing computer account
1665 * (found by hostname) in AD.
1666 * @param ads An initialized ADS_STRUCT
1667 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1668 * @param my_fqdn The fully qualified DNS name of the machine
1669 * @param spn A string of the service principal to add, i.e. 'host'
1670 * @return 0 upon sucess, or non-zero if a failure occurs
1673 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1674 const char *my_fqdn, const char *spn)
1676 ADS_STATUS ret;
1677 TALLOC_CTX *ctx;
1678 LDAPMessage *res = NULL;
1679 char *psp1, *psp2;
1680 ADS_MODLIST mods;
1681 char *dn_string = NULL;
1682 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1684 ret = ads_find_machine_acct(ads, &res, machine_name);
1685 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1686 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1687 machine_name));
1688 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1689 spn, machine_name, ads->config.realm));
1690 ads_msgfree(ads, res);
1691 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1694 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1695 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1696 ads_msgfree(ads, res);
1697 return ADS_ERROR(LDAP_NO_MEMORY);
1700 /* add short name spn */
1702 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1703 talloc_destroy(ctx);
1704 ads_msgfree(ads, res);
1705 return ADS_ERROR(LDAP_NO_MEMORY);
1707 strupper_m(psp1);
1708 strlower_m(&psp1[strlen(spn)]);
1709 servicePrincipalName[0] = psp1;
1711 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1712 psp1, machine_name));
1715 /* add fully qualified spn */
1717 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1718 ret = ADS_ERROR(LDAP_NO_MEMORY);
1719 goto out;
1721 strupper_m(psp2);
1722 strlower_m(&psp2[strlen(spn)]);
1723 servicePrincipalName[1] = psp2;
1725 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1726 psp2, machine_name));
1728 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1729 ret = ADS_ERROR(LDAP_NO_MEMORY);
1730 goto out;
1733 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1734 if (!ADS_ERR_OK(ret)) {
1735 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1736 goto out;
1739 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1740 ret = ADS_ERROR(LDAP_NO_MEMORY);
1741 goto out;
1744 ret = ads_gen_mod(ads, dn_string, mods);
1745 ads_memfree(ads,dn_string);
1746 if (!ADS_ERR_OK(ret)) {
1747 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1748 goto out;
1751 out:
1752 TALLOC_FREE( ctx );
1753 ads_msgfree(ads, res);
1754 return ret;
1758 * adds a machine account to the ADS server
1759 * @param ads An intialized ADS_STRUCT
1760 * @param machine_name - the NetBIOS machine name of this account.
1761 * @param account_type A number indicating the type of account to create
1762 * @param org_unit The LDAP path in which to place this account
1763 * @return 0 upon success, or non-zero otherwise
1766 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1767 const char *org_unit)
1769 ADS_STATUS ret;
1770 char *samAccountName, *controlstr;
1771 TALLOC_CTX *ctx;
1772 ADS_MODLIST mods;
1773 char *machine_escaped = NULL;
1774 char *new_dn;
1775 const char *objectClass[] = {"top", "person", "organizationalPerson",
1776 "user", "computer", NULL};
1777 LDAPMessage *res = NULL;
1778 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1779 UF_DONT_EXPIRE_PASSWD |\
1780 UF_ACCOUNTDISABLE );
1782 if (!(ctx = talloc_init("ads_add_machine_acct")))
1783 return ADS_ERROR(LDAP_NO_MEMORY);
1785 ret = ADS_ERROR(LDAP_NO_MEMORY);
1787 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1788 if (!machine_escaped) {
1789 goto done;
1792 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1793 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1795 if ( !new_dn || !samAccountName ) {
1796 goto done;
1799 #ifndef ENCTYPE_ARCFOUR_HMAC
1800 acct_control |= UF_USE_DES_KEY_ONLY;
1801 #endif
1803 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1804 goto done;
1807 if (!(mods = ads_init_mods(ctx))) {
1808 goto done;
1811 ads_mod_str(ctx, &mods, "cn", machine_name);
1812 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1813 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1814 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1816 ret = ads_gen_add(ads, new_dn, mods);
1818 done:
1819 SAFE_FREE(machine_escaped);
1820 ads_msgfree(ads, res);
1821 talloc_destroy(ctx);
1823 return ret;
1827 * move a machine account to another OU on the ADS server
1828 * @param ads - An intialized ADS_STRUCT
1829 * @param machine_name - the NetBIOS machine name of this account.
1830 * @param org_unit - The LDAP path in which to place this account
1831 * @param moved - whether we moved the machine account (optional)
1832 * @return 0 upon success, or non-zero otherwise
1835 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1836 const char *org_unit, bool *moved)
1838 ADS_STATUS rc;
1839 int ldap_status;
1840 LDAPMessage *res = NULL;
1841 char *filter = NULL;
1842 char *computer_dn = NULL;
1843 char *parent_dn;
1844 char *computer_rdn = NULL;
1845 bool need_move = False;
1847 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1848 rc = ADS_ERROR(LDAP_NO_MEMORY);
1849 goto done;
1852 /* Find pre-existing machine */
1853 rc = ads_search(ads, &res, filter, NULL);
1854 if (!ADS_ERR_OK(rc)) {
1855 goto done;
1858 computer_dn = ads_get_dn(ads, res);
1859 if (!computer_dn) {
1860 rc = ADS_ERROR(LDAP_NO_MEMORY);
1861 goto done;
1864 parent_dn = ads_parent_dn(computer_dn);
1865 if (strequal(parent_dn, org_unit)) {
1866 goto done;
1869 need_move = True;
1871 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1872 rc = ADS_ERROR(LDAP_NO_MEMORY);
1873 goto done;
1876 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1877 org_unit, 1, NULL, NULL);
1878 rc = ADS_ERROR(ldap_status);
1880 done:
1881 ads_msgfree(ads, res);
1882 SAFE_FREE(filter);
1883 SAFE_FREE(computer_dn);
1884 SAFE_FREE(computer_rdn);
1886 if (!ADS_ERR_OK(rc)) {
1887 need_move = False;
1890 if (moved) {
1891 *moved = need_move;
1894 return rc;
1898 dump a binary result from ldap
1900 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1902 int i, j;
1903 for (i=0; values[i]; i++) {
1904 printf("%s: ", field);
1905 for (j=0; j<values[i]->bv_len; j++) {
1906 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1908 printf("\n");
1912 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1914 int i;
1915 for (i=0; values[i]; i++) {
1917 UUID_FLAT guid;
1918 struct GUID tmp;
1920 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1921 smb_uuid_unpack(guid, &tmp);
1922 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1927 dump a sid result from ldap
1929 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1931 int i;
1932 for (i=0; values[i]; i++) {
1933 DOM_SID sid;
1934 fstring tmp;
1935 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1936 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1941 dump ntSecurityDescriptor
1943 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1945 TALLOC_CTX *frame = talloc_stackframe();
1946 struct security_descriptor *psd;
1947 NTSTATUS status;
1949 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1950 values[0]->bv_len, &psd);
1951 if (!NT_STATUS_IS_OK(status)) {
1952 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1953 nt_errstr(status)));
1954 TALLOC_FREE(frame);
1955 return;
1958 if (psd) {
1959 ads_disp_sd(ads, talloc_tos(), psd);
1962 TALLOC_FREE(frame);
1966 dump a string result from ldap
1968 static void dump_string(const char *field, char **values)
1970 int i;
1971 for (i=0; values[i]; i++) {
1972 printf("%s: %s\n", field, values[i]);
1977 dump a field from LDAP on stdout
1978 used for debugging
1981 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1983 const struct {
1984 const char *name;
1985 bool string;
1986 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1987 } handlers[] = {
1988 {"objectGUID", False, dump_guid},
1989 {"netbootGUID", False, dump_guid},
1990 {"nTSecurityDescriptor", False, dump_sd},
1991 {"dnsRecord", False, dump_binary},
1992 {"objectSid", False, dump_sid},
1993 {"tokenGroups", False, dump_sid},
1994 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1995 {"tokengroupsGlobalandUniversal", False, dump_sid},
1996 {"mS-DS-CreatorSID", False, dump_sid},
1997 {"msExchMailboxGuid", False, dump_guid},
1998 {NULL, True, NULL}
2000 int i;
2002 if (!field) { /* must be end of an entry */
2003 printf("\n");
2004 return False;
2007 for (i=0; handlers[i].name; i++) {
2008 if (StrCaseCmp(handlers[i].name, field) == 0) {
2009 if (!values) /* first time, indicate string or not */
2010 return handlers[i].string;
2011 handlers[i].handler(ads, field, (struct berval **) values);
2012 break;
2015 if (!handlers[i].name) {
2016 if (!values) /* first time, indicate string conversion */
2017 return True;
2018 dump_string(field, (char **)values);
2020 return False;
2024 * Dump a result from LDAP on stdout
2025 * used for debugging
2026 * @param ads connection to ads server
2027 * @param res Results to dump
2030 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2032 ads_process_results(ads, res, ads_dump_field, NULL);
2036 * Walk through results, calling a function for each entry found.
2037 * The function receives a field name, a berval * array of values,
2038 * and a data area passed through from the start. The function is
2039 * called once with null for field and values at the end of each
2040 * entry.
2041 * @param ads connection to ads server
2042 * @param res Results to process
2043 * @param fn Function for processing each result
2044 * @param data_area user-defined area to pass to function
2046 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2047 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2048 void *data_area)
2050 LDAPMessage *msg;
2051 TALLOC_CTX *ctx;
2053 if (!(ctx = talloc_init("ads_process_results")))
2054 return;
2056 for (msg = ads_first_entry(ads, res); msg;
2057 msg = ads_next_entry(ads, msg)) {
2058 char *utf8_field;
2059 BerElement *b;
2061 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2062 (LDAPMessage *)msg,&b);
2063 utf8_field;
2064 utf8_field=ldap_next_attribute(ads->ldap.ld,
2065 (LDAPMessage *)msg,b)) {
2066 struct berval **ber_vals;
2067 char **str_vals, **utf8_vals;
2068 char *field;
2069 bool string;
2071 pull_utf8_talloc(ctx, &field, utf8_field);
2072 string = fn(ads, field, NULL, data_area);
2074 if (string) {
2075 utf8_vals = ldap_get_values(ads->ldap.ld,
2076 (LDAPMessage *)msg, field);
2077 str_vals = ads_pull_strvals(ctx,
2078 (const char **) utf8_vals);
2079 fn(ads, field, (void **) str_vals, data_area);
2080 ldap_value_free(utf8_vals);
2081 } else {
2082 ber_vals = ldap_get_values_len(ads->ldap.ld,
2083 (LDAPMessage *)msg, field);
2084 fn(ads, field, (void **) ber_vals, data_area);
2086 ldap_value_free_len(ber_vals);
2088 ldap_memfree(utf8_field);
2090 ber_free(b, 0);
2091 talloc_free_children(ctx);
2092 fn(ads, NULL, NULL, data_area); /* completed an entry */
2095 talloc_destroy(ctx);
2099 * count how many replies are in a LDAPMessage
2100 * @param ads connection to ads server
2101 * @param res Results to count
2102 * @return number of replies
2104 int ads_count_replies(ADS_STRUCT *ads, void *res)
2106 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2110 * pull the first entry from a ADS result
2111 * @param ads connection to ads server
2112 * @param res Results of search
2113 * @return first entry from result
2115 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2117 return ldap_first_entry(ads->ldap.ld, res);
2121 * pull the next entry from a ADS result
2122 * @param ads connection to ads server
2123 * @param res Results of search
2124 * @return next entry from result
2126 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2128 return ldap_next_entry(ads->ldap.ld, res);
2132 * pull the first message from a ADS result
2133 * @param ads connection to ads server
2134 * @param res Results of search
2135 * @return first message from result
2137 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2139 return ldap_first_message(ads->ldap.ld, res);
2143 * pull the next message from a ADS result
2144 * @param ads connection to ads server
2145 * @param res Results of search
2146 * @return next message from result
2148 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2150 return ldap_next_message(ads->ldap.ld, res);
2154 * pull a single string from a ADS result
2155 * @param ads connection to ads server
2156 * @param mem_ctx TALLOC_CTX to use for allocating result string
2157 * @param msg Results of search
2158 * @param field Attribute to retrieve
2159 * @return Result string in talloc context
2161 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2162 const char *field)
2164 char **values;
2165 char *ret = NULL;
2166 char *ux_string;
2167 size_t rc;
2169 values = ldap_get_values(ads->ldap.ld, msg, field);
2170 if (!values)
2171 return NULL;
2173 if (values[0]) {
2174 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2175 values[0]);
2176 if (rc != (size_t)-1)
2177 ret = ux_string;
2180 ldap_value_free(values);
2181 return ret;
2185 * pull an array of strings from a ADS result
2186 * @param ads connection to ads server
2187 * @param mem_ctx TALLOC_CTX to use for allocating result string
2188 * @param msg Results of search
2189 * @param field Attribute to retrieve
2190 * @return Result strings in talloc context
2192 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2193 LDAPMessage *msg, const char *field,
2194 size_t *num_values)
2196 char **values;
2197 char **ret = NULL;
2198 int i;
2200 values = ldap_get_values(ads->ldap.ld, msg, field);
2201 if (!values)
2202 return NULL;
2204 *num_values = ldap_count_values(values);
2206 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2207 if (!ret) {
2208 ldap_value_free(values);
2209 return NULL;
2212 for (i=0;i<*num_values;i++) {
2213 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2214 ldap_value_free(values);
2215 return NULL;
2218 ret[i] = NULL;
2220 ldap_value_free(values);
2221 return ret;
2225 * pull an array of strings from a ADS result
2226 * (handle large multivalue attributes with range retrieval)
2227 * @param ads connection to ads server
2228 * @param mem_ctx TALLOC_CTX to use for allocating result string
2229 * @param msg Results of search
2230 * @param field Attribute to retrieve
2231 * @param current_strings strings returned by a previous call to this function
2232 * @param next_attribute The next query should ask for this attribute
2233 * @param num_values How many values did we get this time?
2234 * @param more_values Are there more values to get?
2235 * @return Result strings in talloc context
2237 char **ads_pull_strings_range(ADS_STRUCT *ads,
2238 TALLOC_CTX *mem_ctx,
2239 LDAPMessage *msg, const char *field,
2240 char **current_strings,
2241 const char **next_attribute,
2242 size_t *num_strings,
2243 bool *more_strings)
2245 char *attr;
2246 char *expected_range_attrib, *range_attr;
2247 BerElement *ptr = NULL;
2248 char **strings;
2249 char **new_strings;
2250 size_t num_new_strings;
2251 unsigned long int range_start;
2252 unsigned long int range_end;
2254 /* we might have been given the whole lot anyway */
2255 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2256 *more_strings = False;
2257 return strings;
2260 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2262 /* look for Range result */
2263 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2264 attr;
2265 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2266 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2267 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2268 range_attr = attr;
2269 break;
2271 ldap_memfree(attr);
2273 if (!attr) {
2274 ber_free(ptr, 0);
2275 /* nothing here - this field is just empty */
2276 *more_strings = False;
2277 return NULL;
2280 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2281 &range_start, &range_end) == 2) {
2282 *more_strings = True;
2283 } else {
2284 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2285 &range_start) == 1) {
2286 *more_strings = False;
2287 } else {
2288 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2289 range_attr));
2290 ldap_memfree(range_attr);
2291 *more_strings = False;
2292 return NULL;
2296 if ((*num_strings) != range_start) {
2297 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2298 " - aborting range retreival\n",
2299 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2300 ldap_memfree(range_attr);
2301 *more_strings = False;
2302 return NULL;
2305 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2307 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2308 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2309 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2310 range_attr, (unsigned long int)range_end - range_start + 1,
2311 (unsigned long int)num_new_strings));
2312 ldap_memfree(range_attr);
2313 *more_strings = False;
2314 return NULL;
2317 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2318 *num_strings + num_new_strings);
2320 if (strings == NULL) {
2321 ldap_memfree(range_attr);
2322 *more_strings = False;
2323 return NULL;
2326 if (new_strings && num_new_strings) {
2327 memcpy(&strings[*num_strings], new_strings,
2328 sizeof(*new_strings) * num_new_strings);
2331 (*num_strings) += num_new_strings;
2333 if (*more_strings) {
2334 *next_attribute = talloc_asprintf(mem_ctx,
2335 "%s;range=%d-*",
2336 field,
2337 (int)*num_strings);
2339 if (!*next_attribute) {
2340 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2341 ldap_memfree(range_attr);
2342 *more_strings = False;
2343 return NULL;
2347 ldap_memfree(range_attr);
2349 return strings;
2353 * pull a single uint32 from a ADS result
2354 * @param ads connection to ads server
2355 * @param msg Results of search
2356 * @param field Attribute to retrieve
2357 * @param v Pointer to int to store result
2358 * @return boolean inidicating success
2360 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2361 uint32 *v)
2363 char **values;
2365 values = ldap_get_values(ads->ldap.ld, msg, field);
2366 if (!values)
2367 return False;
2368 if (!values[0]) {
2369 ldap_value_free(values);
2370 return False;
2373 *v = atoi(values[0]);
2374 ldap_value_free(values);
2375 return True;
2379 * pull a single objectGUID from an ADS result
2380 * @param ads connection to ADS server
2381 * @param msg results of search
2382 * @param guid 37-byte area to receive text guid
2383 * @return boolean indicating success
2385 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2387 char **values;
2388 UUID_FLAT flat_guid;
2390 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2391 if (!values)
2392 return False;
2394 if (values[0]) {
2395 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2396 smb_uuid_unpack(flat_guid, guid);
2397 ldap_value_free(values);
2398 return True;
2400 ldap_value_free(values);
2401 return False;
2407 * pull a single DOM_SID from a ADS result
2408 * @param ads connection to ads server
2409 * @param msg Results of search
2410 * @param field Attribute to retrieve
2411 * @param sid Pointer to sid to store result
2412 * @return boolean inidicating success
2414 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2415 DOM_SID *sid)
2417 struct berval **values;
2418 bool ret = False;
2420 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2422 if (!values)
2423 return False;
2425 if (values[0])
2426 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2428 ldap_value_free_len(values);
2429 return ret;
2433 * pull an array of DOM_SIDs from a ADS result
2434 * @param ads connection to ads server
2435 * @param mem_ctx TALLOC_CTX for allocating sid array
2436 * @param msg Results of search
2437 * @param field Attribute to retrieve
2438 * @param sids pointer to sid array to allocate
2439 * @return the count of SIDs pulled
2441 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2442 LDAPMessage *msg, const char *field, DOM_SID **sids)
2444 struct berval **values;
2445 bool ret;
2446 int count, i;
2448 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2450 if (!values)
2451 return 0;
2453 for (i=0; values[i]; i++)
2454 /* nop */ ;
2456 if (i) {
2457 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2458 if (!(*sids)) {
2459 ldap_value_free_len(values);
2460 return 0;
2462 } else {
2463 (*sids) = NULL;
2466 count = 0;
2467 for (i=0; values[i]; i++) {
2468 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2469 if (ret) {
2470 DEBUG(10, ("pulling SID: %s\n",
2471 sid_string_dbg(&(*sids)[count])));
2472 count++;
2476 ldap_value_free_len(values);
2477 return count;
2481 * pull a SEC_DESC from a ADS result
2482 * @param ads connection to ads server
2483 * @param mem_ctx TALLOC_CTX for allocating sid array
2484 * @param msg Results of search
2485 * @param field Attribute to retrieve
2486 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2487 * @return boolean inidicating success
2489 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2490 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2492 struct berval **values;
2493 bool ret = true;
2495 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2497 if (!values) return false;
2499 if (values[0]) {
2500 NTSTATUS status;
2501 status = unmarshall_sec_desc(mem_ctx,
2502 (uint8 *)values[0]->bv_val,
2503 values[0]->bv_len, sd);
2504 if (!NT_STATUS_IS_OK(status)) {
2505 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2506 nt_errstr(status)));
2507 ret = false;
2511 ldap_value_free_len(values);
2512 return ret;
2516 * in order to support usernames longer than 21 characters we need to
2517 * use both the sAMAccountName and the userPrincipalName attributes
2518 * It seems that not all users have the userPrincipalName attribute set
2520 * @param ads connection to ads server
2521 * @param mem_ctx TALLOC_CTX for allocating sid array
2522 * @param msg Results of search
2523 * @return the username
2525 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2526 LDAPMessage *msg)
2528 #if 0 /* JERRY */
2529 char *ret, *p;
2531 /* lookup_name() only works on the sAMAccountName to
2532 returning the username portion of userPrincipalName
2533 breaks winbindd_getpwnam() */
2535 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2536 if (ret && (p = strchr_m(ret, '@'))) {
2537 *p = 0;
2538 return ret;
2540 #endif
2541 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2546 * find the update serial number - this is the core of the ldap cache
2547 * @param ads connection to ads server
2548 * @param ads connection to ADS server
2549 * @param usn Pointer to retrieved update serial number
2550 * @return status of search
2552 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2554 const char *attrs[] = {"highestCommittedUSN", NULL};
2555 ADS_STATUS status;
2556 LDAPMessage *res;
2558 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2559 if (!ADS_ERR_OK(status))
2560 return status;
2562 if (ads_count_replies(ads, res) != 1) {
2563 ads_msgfree(ads, res);
2564 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2567 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2568 ads_msgfree(ads, res);
2569 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2572 ads_msgfree(ads, res);
2573 return ADS_SUCCESS;
2576 /* parse a ADS timestring - typical string is
2577 '20020917091222.0Z0' which means 09:12.22 17th September
2578 2002, timezone 0 */
2579 static time_t ads_parse_time(const char *str)
2581 struct tm tm;
2583 ZERO_STRUCT(tm);
2585 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2586 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2587 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2588 return 0;
2590 tm.tm_year -= 1900;
2591 tm.tm_mon -= 1;
2593 return timegm(&tm);
2596 /********************************************************************
2597 ********************************************************************/
2599 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2601 const char *attrs[] = {"currentTime", NULL};
2602 ADS_STATUS status;
2603 LDAPMessage *res;
2604 char *timestr;
2605 TALLOC_CTX *ctx;
2606 ADS_STRUCT *ads_s = ads;
2608 if (!(ctx = talloc_init("ads_current_time"))) {
2609 return ADS_ERROR(LDAP_NO_MEMORY);
2612 /* establish a new ldap tcp session if necessary */
2614 if ( !ads->ldap.ld ) {
2615 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2616 ads->server.ldap_server )) == NULL )
2618 goto done;
2620 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2621 status = ads_connect( ads_s );
2622 if ( !ADS_ERR_OK(status))
2623 goto done;
2626 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2627 if (!ADS_ERR_OK(status)) {
2628 goto done;
2631 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2632 if (!timestr) {
2633 ads_msgfree(ads_s, res);
2634 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2635 goto done;
2638 /* but save the time and offset in the original ADS_STRUCT */
2640 ads->config.current_time = ads_parse_time(timestr);
2642 if (ads->config.current_time != 0) {
2643 ads->auth.time_offset = ads->config.current_time - time(NULL);
2644 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2647 ads_msgfree(ads, res);
2649 status = ADS_SUCCESS;
2651 done:
2652 /* free any temporary ads connections */
2653 if ( ads_s != ads ) {
2654 ads_destroy( &ads_s );
2656 talloc_destroy(ctx);
2658 return status;
2661 /********************************************************************
2662 ********************************************************************/
2664 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2666 const char *attrs[] = {"domainFunctionality", NULL};
2667 ADS_STATUS status;
2668 LDAPMessage *res;
2669 ADS_STRUCT *ads_s = ads;
2671 *val = DS_DOMAIN_FUNCTION_2000;
2673 /* establish a new ldap tcp session if necessary */
2675 if ( !ads->ldap.ld ) {
2676 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2677 ads->server.ldap_server )) == NULL )
2679 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2680 goto done;
2682 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2683 status = ads_connect( ads_s );
2684 if ( !ADS_ERR_OK(status))
2685 goto done;
2688 /* If the attribute does not exist assume it is a Windows 2000
2689 functional domain */
2691 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2692 if (!ADS_ERR_OK(status)) {
2693 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2694 status = ADS_SUCCESS;
2696 goto done;
2699 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2700 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2702 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2705 ads_msgfree(ads, res);
2707 done:
2708 /* free any temporary ads connections */
2709 if ( ads_s != ads ) {
2710 ads_destroy( &ads_s );
2713 return status;
2717 * find the domain sid for our domain
2718 * @param ads connection to ads server
2719 * @param sid Pointer to domain sid
2720 * @return status of search
2722 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2724 const char *attrs[] = {"objectSid", NULL};
2725 LDAPMessage *res;
2726 ADS_STATUS rc;
2728 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2729 attrs, &res);
2730 if (!ADS_ERR_OK(rc)) return rc;
2731 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2732 ads_msgfree(ads, res);
2733 return ADS_ERROR_SYSTEM(ENOENT);
2735 ads_msgfree(ads, res);
2737 return ADS_SUCCESS;
2741 * find our site name
2742 * @param ads connection to ads server
2743 * @param mem_ctx Pointer to talloc context
2744 * @param site_name Pointer to the sitename
2745 * @return status of search
2747 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2749 ADS_STATUS status;
2750 LDAPMessage *res;
2751 const char *dn, *service_name;
2752 const char *attrs[] = { "dsServiceName", NULL };
2754 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2755 if (!ADS_ERR_OK(status)) {
2756 return status;
2759 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2760 if (service_name == NULL) {
2761 ads_msgfree(ads, res);
2762 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2765 ads_msgfree(ads, res);
2767 /* go up three levels */
2768 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2769 if (dn == NULL) {
2770 return ADS_ERROR(LDAP_NO_MEMORY);
2773 *site_name = talloc_strdup(mem_ctx, dn);
2774 if (*site_name == NULL) {
2775 return ADS_ERROR(LDAP_NO_MEMORY);
2778 return status;
2780 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2785 * find the site dn where a machine resides
2786 * @param ads connection to ads server
2787 * @param mem_ctx Pointer to talloc context
2788 * @param computer_name name of the machine
2789 * @param site_name Pointer to the sitename
2790 * @return status of search
2792 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2794 ADS_STATUS status;
2795 LDAPMessage *res;
2796 const char *parent, *filter;
2797 char *config_context = NULL;
2798 char *dn;
2800 /* shortcut a query */
2801 if (strequal(computer_name, ads->config.ldap_server_name)) {
2802 return ads_site_dn(ads, mem_ctx, site_dn);
2805 status = ads_config_path(ads, mem_ctx, &config_context);
2806 if (!ADS_ERR_OK(status)) {
2807 return status;
2810 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2811 if (filter == NULL) {
2812 return ADS_ERROR(LDAP_NO_MEMORY);
2815 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2816 filter, NULL, &res);
2817 if (!ADS_ERR_OK(status)) {
2818 return status;
2821 if (ads_count_replies(ads, res) != 1) {
2822 ads_msgfree(ads, res);
2823 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2826 dn = ads_get_dn(ads, res);
2827 if (dn == NULL) {
2828 ads_msgfree(ads, res);
2829 return ADS_ERROR(LDAP_NO_MEMORY);
2832 /* go up three levels */
2833 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2834 if (parent == NULL) {
2835 ads_msgfree(ads, res);
2836 ads_memfree(ads, dn);
2837 return ADS_ERROR(LDAP_NO_MEMORY);
2840 *site_dn = talloc_strdup(mem_ctx, parent);
2841 if (*site_dn == NULL) {
2842 ads_msgfree(ads, res);
2843 ads_memfree(ads, dn);
2844 return ADS_ERROR(LDAP_NO_MEMORY);
2847 ads_memfree(ads, dn);
2848 ads_msgfree(ads, res);
2850 return status;
2854 * get the upn suffixes for a domain
2855 * @param ads connection to ads server
2856 * @param mem_ctx Pointer to talloc context
2857 * @param suffixes Pointer to an array of suffixes
2858 * @param num_suffixes Pointer to the number of suffixes
2859 * @return status of search
2861 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2863 ADS_STATUS status;
2864 LDAPMessage *res;
2865 const char *base;
2866 char *config_context = NULL;
2867 const char *attrs[] = { "uPNSuffixes", NULL };
2869 status = ads_config_path(ads, mem_ctx, &config_context);
2870 if (!ADS_ERR_OK(status)) {
2871 return status;
2874 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2875 if (base == NULL) {
2876 return ADS_ERROR(LDAP_NO_MEMORY);
2879 status = ads_search_dn(ads, &res, base, attrs);
2880 if (!ADS_ERR_OK(status)) {
2881 return status;
2884 if (ads_count_replies(ads, res) != 1) {
2885 ads_msgfree(ads, res);
2886 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2889 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2890 if ((*suffixes) == NULL) {
2891 ads_msgfree(ads, res);
2892 return ADS_ERROR(LDAP_NO_MEMORY);
2895 ads_msgfree(ads, res);
2897 return status;
2901 * get the joinable ous for a domain
2902 * @param ads connection to ads server
2903 * @param mem_ctx Pointer to talloc context
2904 * @param ous Pointer to an array of ous
2905 * @param num_ous Pointer to the number of ous
2906 * @return status of search
2908 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2909 TALLOC_CTX *mem_ctx,
2910 char ***ous,
2911 size_t *num_ous)
2913 ADS_STATUS status;
2914 LDAPMessage *res = NULL;
2915 LDAPMessage *msg = NULL;
2916 const char *attrs[] = { "dn", NULL };
2917 int count = 0;
2919 status = ads_search(ads, &res,
2920 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2921 attrs);
2922 if (!ADS_ERR_OK(status)) {
2923 return status;
2926 count = ads_count_replies(ads, res);
2927 if (count < 1) {
2928 ads_msgfree(ads, res);
2929 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2932 for (msg = ads_first_entry(ads, res); msg;
2933 msg = ads_next_entry(ads, msg)) {
2935 char *dn = NULL;
2937 dn = ads_get_dn(ads, msg);
2938 if (!dn) {
2939 ads_msgfree(ads, res);
2940 return ADS_ERROR(LDAP_NO_MEMORY);
2943 if (!add_string_to_array(mem_ctx, dn,
2944 (const char ***)ous,
2945 (int *)num_ous)) {
2946 ads_memfree(ads, dn);
2947 ads_msgfree(ads, res);
2948 return ADS_ERROR(LDAP_NO_MEMORY);
2951 ads_memfree(ads, dn);
2954 ads_msgfree(ads, res);
2956 return status;
2961 * pull a DOM_SID from an extended dn string
2962 * @param mem_ctx TALLOC_CTX
2963 * @param extended_dn string
2964 * @param flags string type of extended_dn
2965 * @param sid pointer to a DOM_SID
2966 * @return NT_STATUS_OK on success,
2967 * NT_INVALID_PARAMETER on error,
2968 * NT_STATUS_NOT_FOUND if no SID present
2970 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2971 const char *extended_dn,
2972 enum ads_extended_dn_flags flags,
2973 DOM_SID *sid)
2975 char *p, *q, *dn;
2977 if (!extended_dn) {
2978 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2981 /* otherwise extended_dn gets stripped off */
2982 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2983 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2986 * ADS_EXTENDED_DN_HEX_STRING:
2987 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2989 * ADS_EXTENDED_DN_STRING (only with w2k3):
2990 * <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
2992 * Object with no SID, such as an Exchange Public Folder
2993 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
2996 p = strchr(dn, ';');
2997 if (!p) {
2998 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3001 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3002 DEBUG(5,("No SID present in extended dn\n"));
3003 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3006 p += strlen(";<SID=");
3008 q = strchr(p, '>');
3009 if (!q) {
3010 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3013 *q = '\0';
3015 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3017 switch (flags) {
3019 case ADS_EXTENDED_DN_STRING:
3020 if (!string_to_sid(sid, p)) {
3021 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3023 break;
3024 case ADS_EXTENDED_DN_HEX_STRING: {
3025 fstring buf;
3026 size_t buf_len;
3028 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3029 if (buf_len == 0) {
3030 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3033 if (!sid_parse(buf, buf_len, sid)) {
3034 DEBUG(10,("failed to parse sid\n"));
3035 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3037 break;
3039 default:
3040 DEBUG(10,("unknown extended dn format\n"));
3041 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3044 return ADS_ERROR_NT(NT_STATUS_OK);
3048 * pull an array of DOM_SIDs from a ADS result
3049 * @param ads connection to ads server
3050 * @param mem_ctx TALLOC_CTX for allocating sid array
3051 * @param msg Results of search
3052 * @param field Attribute to retrieve
3053 * @param flags string type of extended_dn
3054 * @param sids pointer to sid array to allocate
3055 * @return the count of SIDs pulled
3057 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3058 TALLOC_CTX *mem_ctx,
3059 LDAPMessage *msg,
3060 const char *field,
3061 enum ads_extended_dn_flags flags,
3062 DOM_SID **sids)
3064 int i;
3065 ADS_STATUS rc;
3066 size_t dn_count, ret_count = 0;
3067 char **dn_strings;
3069 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3070 &dn_count)) == NULL) {
3071 return 0;
3074 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3075 if (!(*sids)) {
3076 TALLOC_FREE(dn_strings);
3077 return 0;
3080 for (i=0; i<dn_count; i++) {
3081 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3082 flags, &(*sids)[i]);
3083 if (!ADS_ERR_OK(rc)) {
3084 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3085 NT_STATUS_NOT_FOUND)) {
3086 continue;
3088 else {
3089 TALLOC_FREE(*sids);
3090 TALLOC_FREE(dn_strings);
3091 return 0;
3094 ret_count++;
3097 TALLOC_FREE(dn_strings);
3099 return ret_count;
3102 /********************************************************************
3103 ********************************************************************/
3105 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3107 LDAPMessage *res = NULL;
3108 ADS_STATUS status;
3109 int count = 0;
3110 char *name = NULL;
3112 status = ads_find_machine_acct(ads, &res, global_myname());
3113 if (!ADS_ERR_OK(status)) {
3114 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3115 global_myname()));
3116 goto out;
3119 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3120 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3121 goto out;
3124 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3125 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3128 out:
3129 ads_msgfree(ads, res);
3131 return name;
3134 /********************************************************************
3135 ********************************************************************/
3137 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3139 LDAPMessage *res = NULL;
3140 ADS_STATUS status;
3141 int count = 0;
3142 char *name = NULL;
3144 status = ads_find_machine_acct(ads, &res, machine_name);
3145 if (!ADS_ERR_OK(status)) {
3146 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3147 global_myname()));
3148 goto out;
3151 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3152 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3153 goto out;
3156 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3157 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3160 out:
3161 ads_msgfree(ads, res);
3163 return name;
3166 /********************************************************************
3167 ********************************************************************/
3169 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3171 LDAPMessage *res = NULL;
3172 ADS_STATUS status;
3173 int count = 0;
3174 char *name = NULL;
3176 status = ads_find_machine_acct(ads, &res, global_myname());
3177 if (!ADS_ERR_OK(status)) {
3178 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3179 global_myname()));
3180 goto out;
3183 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3184 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3185 goto out;
3188 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3189 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3192 out:
3193 ads_msgfree(ads, res);
3195 return name;
3198 #if 0
3200 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3203 * Join a machine to a realm
3204 * Creates the machine account and sets the machine password
3205 * @param ads connection to ads server
3206 * @param machine name of host to add
3207 * @param org_unit Organizational unit to place machine in
3208 * @return status of join
3210 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3211 uint32 account_type, const char *org_unit)
3213 ADS_STATUS status;
3214 LDAPMessage *res = NULL;
3215 char *machine;
3217 /* machine name must be lowercase */
3218 machine = SMB_STRDUP(machine_name);
3219 strlower_m(machine);
3222 status = ads_find_machine_acct(ads, (void **)&res, machine);
3223 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3224 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3225 status = ads_leave_realm(ads, machine);
3226 if (!ADS_ERR_OK(status)) {
3227 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3228 machine, ads->config.realm));
3229 return status;
3233 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3234 if (!ADS_ERR_OK(status)) {
3235 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3236 SAFE_FREE(machine);
3237 return status;
3240 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3241 if (!ADS_ERR_OK(status)) {
3242 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3243 SAFE_FREE(machine);
3244 return status;
3247 SAFE_FREE(machine);
3248 ads_msgfree(ads, res);
3250 return status;
3252 #endif
3255 * Delete a machine from the realm
3256 * @param ads connection to ads server
3257 * @param hostname Machine to remove
3258 * @return status of delete
3260 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3262 ADS_STATUS status;
3263 void *msg;
3264 LDAPMessage *res;
3265 char *hostnameDN, *host;
3266 int rc;
3267 LDAPControl ldap_control;
3268 LDAPControl * pldap_control[2] = {NULL, NULL};
3270 pldap_control[0] = &ldap_control;
3271 memset(&ldap_control, 0, sizeof(LDAPControl));
3272 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3274 /* hostname must be lowercase */
3275 host = SMB_STRDUP(hostname);
3276 strlower_m(host);
3278 status = ads_find_machine_acct(ads, &res, host);
3279 if (!ADS_ERR_OK(status)) {
3280 DEBUG(0, ("Host account for %s does not exist.\n", host));
3281 SAFE_FREE(host);
3282 return status;
3285 msg = ads_first_entry(ads, res);
3286 if (!msg) {
3287 SAFE_FREE(host);
3288 return ADS_ERROR_SYSTEM(ENOENT);
3291 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3293 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3294 if (rc) {
3295 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3296 }else {
3297 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3300 if (rc != LDAP_SUCCESS) {
3301 const char *attrs[] = { "cn", NULL };
3302 LDAPMessage *msg_sub;
3304 /* we only search with scope ONE, we do not expect any further
3305 * objects to be created deeper */
3307 status = ads_do_search_retry(ads, hostnameDN,
3308 LDAP_SCOPE_ONELEVEL,
3309 "(objectclass=*)", attrs, &res);
3311 if (!ADS_ERR_OK(status)) {
3312 SAFE_FREE(host);
3313 ads_memfree(ads, hostnameDN);
3314 return status;
3317 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3318 msg_sub = ads_next_entry(ads, msg_sub)) {
3320 char *dn = NULL;
3322 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3323 SAFE_FREE(host);
3324 ads_memfree(ads, hostnameDN);
3325 return ADS_ERROR(LDAP_NO_MEMORY);
3328 status = ads_del_dn(ads, dn);
3329 if (!ADS_ERR_OK(status)) {
3330 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3331 SAFE_FREE(host);
3332 ads_memfree(ads, dn);
3333 ads_memfree(ads, hostnameDN);
3334 return status;
3337 ads_memfree(ads, dn);
3340 /* there should be no subordinate objects anymore */
3341 status = ads_do_search_retry(ads, hostnameDN,
3342 LDAP_SCOPE_ONELEVEL,
3343 "(objectclass=*)", attrs, &res);
3345 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3346 SAFE_FREE(host);
3347 ads_memfree(ads, hostnameDN);
3348 return status;
3351 /* delete hostnameDN now */
3352 status = ads_del_dn(ads, hostnameDN);
3353 if (!ADS_ERR_OK(status)) {
3354 SAFE_FREE(host);
3355 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3356 ads_memfree(ads, hostnameDN);
3357 return status;
3361 ads_memfree(ads, hostnameDN);
3363 status = ads_find_machine_acct(ads, &res, host);
3364 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3365 DEBUG(3, ("Failed to remove host account.\n"));
3366 SAFE_FREE(host);
3367 return status;
3370 SAFE_FREE(host);
3371 return status;
3375 * pull all token-sids from an LDAP dn
3376 * @param ads connection to ads server
3377 * @param mem_ctx TALLOC_CTX for allocating sid array
3378 * @param dn of LDAP object
3379 * @param user_sid pointer to DOM_SID (objectSid)
3380 * @param primary_group_sid pointer to DOM_SID (self composed)
3381 * @param sids pointer to sid array to allocate
3382 * @param num_sids counter of SIDs pulled
3383 * @return status of token query
3385 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3386 TALLOC_CTX *mem_ctx,
3387 const char *dn,
3388 DOM_SID *user_sid,
3389 DOM_SID *primary_group_sid,
3390 DOM_SID **sids,
3391 size_t *num_sids)
3393 ADS_STATUS status;
3394 LDAPMessage *res = NULL;
3395 int count = 0;
3396 size_t tmp_num_sids;
3397 DOM_SID *tmp_sids;
3398 DOM_SID tmp_user_sid;
3399 DOM_SID tmp_primary_group_sid;
3400 uint32 pgid;
3401 const char *attrs[] = {
3402 "objectSid",
3403 "tokenGroups",
3404 "primaryGroupID",
3405 NULL
3408 status = ads_search_retry_dn(ads, &res, dn, attrs);
3409 if (!ADS_ERR_OK(status)) {
3410 return status;
3413 count = ads_count_replies(ads, res);
3414 if (count != 1) {
3415 ads_msgfree(ads, res);
3416 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3419 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3420 ads_msgfree(ads, res);
3421 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3424 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3425 ads_msgfree(ads, res);
3426 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3430 /* hack to compose the primary group sid without knowing the
3431 * domsid */
3433 DOM_SID domsid;
3434 uint32 dummy_rid;
3436 sid_copy(&domsid, &tmp_user_sid);
3438 if (!sid_split_rid(&domsid, &dummy_rid)) {
3439 ads_msgfree(ads, res);
3440 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3443 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3444 ads_msgfree(ads, res);
3445 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3449 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3451 if (tmp_num_sids == 0 || !tmp_sids) {
3452 ads_msgfree(ads, res);
3453 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3456 if (num_sids) {
3457 *num_sids = tmp_num_sids;
3460 if (sids) {
3461 *sids = tmp_sids;
3464 if (user_sid) {
3465 *user_sid = tmp_user_sid;
3468 if (primary_group_sid) {
3469 *primary_group_sid = tmp_primary_group_sid;
3472 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3474 ads_msgfree(ads, res);
3475 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3479 * Find a sAMAccoutName in LDAP
3480 * @param ads connection to ads server
3481 * @param mem_ctx TALLOC_CTX for allocating sid array
3482 * @param samaccountname to search
3483 * @param uac_ret uint32 pointer userAccountControl attribute value
3484 * @param dn_ret pointer to dn
3485 * @return status of token query
3487 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3488 TALLOC_CTX *mem_ctx,
3489 const char *samaccountname,
3490 uint32 *uac_ret,
3491 const char **dn_ret)
3493 ADS_STATUS status;
3494 const char *attrs[] = { "userAccountControl", NULL };
3495 const char *filter;
3496 LDAPMessage *res = NULL;
3497 char *dn = NULL;
3498 uint32 uac = 0;
3500 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3501 samaccountname);
3502 if (filter == NULL) {
3503 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3504 goto out;
3507 status = ads_do_search_all(ads, ads->config.bind_path,
3508 LDAP_SCOPE_SUBTREE,
3509 filter, attrs, &res);
3511 if (!ADS_ERR_OK(status)) {
3512 goto out;
3515 if (ads_count_replies(ads, res) != 1) {
3516 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3517 goto out;
3520 dn = ads_get_dn(ads, res);
3521 if (dn == NULL) {
3522 status = ADS_ERROR(LDAP_NO_MEMORY);
3523 goto out;
3526 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3527 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3528 goto out;
3531 if (uac_ret) {
3532 *uac_ret = uac;
3535 if (dn_ret) {
3536 *dn_ret = talloc_strdup(mem_ctx, dn);
3537 if (!*dn_ret) {
3538 status = ADS_ERROR(LDAP_NO_MEMORY);
3539 goto out;
3542 out:
3543 ads_memfree(ads, dn);
3544 ads_msgfree(ads, res);
3546 return status;
3550 * find our configuration path
3551 * @param ads connection to ads server
3552 * @param mem_ctx Pointer to talloc context
3553 * @param config_path Pointer to the config path
3554 * @return status of search
3556 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3557 TALLOC_CTX *mem_ctx,
3558 char **config_path)
3560 ADS_STATUS status;
3561 LDAPMessage *res = NULL;
3562 const char *config_context = NULL;
3563 const char *attrs[] = { "configurationNamingContext", NULL };
3565 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3566 "(objectclass=*)", attrs, &res);
3567 if (!ADS_ERR_OK(status)) {
3568 return status;
3571 config_context = ads_pull_string(ads, mem_ctx, res,
3572 "configurationNamingContext");
3573 ads_msgfree(ads, res);
3574 if (!config_context) {
3575 return ADS_ERROR(LDAP_NO_MEMORY);
3578 if (config_path) {
3579 *config_path = talloc_strdup(mem_ctx, config_context);
3580 if (!*config_path) {
3581 return ADS_ERROR(LDAP_NO_MEMORY);
3585 return ADS_ERROR(LDAP_SUCCESS);
3589 * find the displayName of an extended right
3590 * @param ads connection to ads server
3591 * @param config_path The config path
3592 * @param mem_ctx Pointer to talloc context
3593 * @param GUID struct of the rightsGUID
3594 * @return status of search
3596 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3597 const char *config_path,
3598 TALLOC_CTX *mem_ctx,
3599 const struct GUID *rights_guid)
3601 ADS_STATUS rc;
3602 LDAPMessage *res = NULL;
3603 char *expr = NULL;
3604 const char *attrs[] = { "displayName", NULL };
3605 const char *result = NULL;
3606 const char *path;
3608 if (!ads || !mem_ctx || !rights_guid) {
3609 goto done;
3612 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3613 smb_uuid_string(mem_ctx, *rights_guid));
3614 if (!expr) {
3615 goto done;
3618 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3619 if (!path) {
3620 goto done;
3623 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3624 expr, attrs, &res);
3625 if (!ADS_ERR_OK(rc)) {
3626 goto done;
3629 if (ads_count_replies(ads, res) != 1) {
3630 goto done;
3633 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3635 done:
3636 ads_msgfree(ads, res);
3637 return result;
3642 * verify or build and verify an account ou
3643 * @param mem_ctx Pointer to talloc context
3644 * @param ads connection to ads server
3645 * @param account_ou
3646 * @return status of search
3649 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3650 ADS_STRUCT *ads,
3651 const char **account_ou)
3653 struct ldb_dn *name_dn = NULL;
3654 const char *name = NULL;
3655 char *ou_string = NULL;
3657 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3658 if (name_dn) {
3659 return ADS_SUCCESS;
3662 ou_string = ads_ou_string(ads, *account_ou);
3663 if (!ou_string) {
3664 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3667 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3668 ads->config.bind_path);
3669 SAFE_FREE(ou_string);
3670 if (!name) {
3671 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3674 name_dn = ldb_dn_explode(mem_ctx, name);
3675 if (!name_dn) {
3676 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3679 *account_ou = talloc_strdup(mem_ctx, name);
3680 if (!*account_ou) {
3681 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3684 return ADS_SUCCESS;
3687 #endif