Fix bug #6279 - winbindd crash. Cope with LDAP libraries returning LDAP_SUCCESS but...
[Samba.git] / source / libads / ldap.c
blob8c37bfb69e00ab56c09876a0721eb85e53bbe78f
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 if (*res == NULL) {
122 return LDAP_TIMELIMIT_EXCEEDED;
125 return result;
128 /**********************************************
129 Do client and server sitename match ?
130 **********************************************/
132 bool ads_sitename_match(ADS_STRUCT *ads)
134 if (ads->config.server_site_name == NULL &&
135 ads->config.client_site_name == NULL ) {
136 DEBUG(10,("ads_sitename_match: both null\n"));
137 return True;
139 if (ads->config.server_site_name &&
140 ads->config.client_site_name &&
141 strequal(ads->config.server_site_name,
142 ads->config.client_site_name)) {
143 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
144 return True;
146 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
147 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
148 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
149 return False;
152 /**********************************************
153 Is this the closest DC ?
154 **********************************************/
156 bool ads_closest_dc(ADS_STRUCT *ads)
158 if (ads->config.flags & NBT_SERVER_CLOSEST) {
159 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
160 return True;
163 /* not sure if this can ever happen */
164 if (ads_sitename_match(ads)) {
165 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
166 return True;
169 if (ads->config.client_site_name == NULL) {
170 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
171 return True;
174 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
175 ads->config.ldap_server_name));
177 return False;
182 try a connection to a given ldap server, returning True and setting the servers IP
183 in the ads struct if successful
185 bool ads_try_connect(ADS_STRUCT *ads, const char *server )
187 char *srv;
188 struct nbt_cldap_netlogon_5 cldap_reply;
189 TALLOC_CTX *mem_ctx = NULL;
190 bool ret = false;
192 if (!server || !*server) {
193 return False;
196 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
197 server, ads->server.realm));
199 mem_ctx = talloc_init("ads_try_connect");
200 if (!mem_ctx) {
201 DEBUG(0,("out of memory\n"));
202 return false;
205 /* this copes with inet_ntoa brokenness */
207 srv = SMB_STRDUP(server);
209 ZERO_STRUCT( cldap_reply );
211 if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
212 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
213 ret = false;
214 goto out;
217 /* Check the CLDAP reply flags */
219 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
220 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
221 srv));
222 ret = false;
223 goto out;
226 /* Fill in the ads->config values */
228 SAFE_FREE(ads->config.realm);
229 SAFE_FREE(ads->config.bind_path);
230 SAFE_FREE(ads->config.ldap_server_name);
231 SAFE_FREE(ads->config.server_site_name);
232 SAFE_FREE(ads->config.client_site_name);
233 SAFE_FREE(ads->server.workgroup);
235 ads->config.flags = cldap_reply.server_type;
236 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
237 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
238 strupper_m(ads->config.realm);
239 ads->config.bind_path = ads_build_dn(ads->config.realm);
240 if (*cldap_reply.server_site) {
241 ads->config.server_site_name =
242 SMB_STRDUP(cldap_reply.server_site);
244 if (*cldap_reply.client_site) {
245 ads->config.client_site_name =
246 SMB_STRDUP(cldap_reply.client_site);
248 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
250 ads->ldap.port = LDAP_PORT;
251 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
252 DEBUG(1,("ads_try_connect: unable to convert %s "
253 "to an address\n",
254 srv));
255 ret = false;
256 goto out;
259 /* Store our site name. */
260 sitename_store( cldap_reply.domain, cldap_reply.client_site);
261 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
263 ret = true;
264 out:
265 SAFE_FREE(srv);
266 TALLOC_FREE(mem_ctx);
268 return ret;
271 /**********************************************************************
272 Try to find an AD dc using our internal name resolution routines
273 Try the realm first and then then workgroup name if netbios is not
274 disabled
275 **********************************************************************/
277 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
279 const char *c_domain;
280 const char *c_realm;
281 int count, i=0;
282 struct ip_service *ip_list;
283 const char *realm;
284 const char *domain;
285 bool got_realm = False;
286 bool use_own_domain = False;
287 char *sitename;
288 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
290 /* if the realm and workgroup are both empty, assume they are ours */
292 /* realm */
293 c_realm = ads->server.realm;
295 if ( !c_realm || !*c_realm ) {
296 /* special case where no realm and no workgroup means our own */
297 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
298 use_own_domain = True;
299 c_realm = lp_realm();
303 if (c_realm && *c_realm)
304 got_realm = True;
306 again:
308 /* we need to try once with the realm name and fallback to the
309 netbios domain name if we fail (if netbios has not been disabled */
311 if ( !got_realm && !lp_disable_netbios() ) {
312 c_realm = ads->server.workgroup;
313 if (!c_realm || !*c_realm) {
314 if ( use_own_domain )
315 c_realm = lp_workgroup();
319 if ( !c_realm || !*c_realm ) {
320 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
321 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
324 if ( use_own_domain ) {
325 c_domain = lp_workgroup();
326 } else {
327 c_domain = ads->server.workgroup;
330 realm = c_realm;
331 domain = c_domain;
334 * In case of LDAP we use get_dc_name() as that
335 * creates the custom krb5.conf file
337 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
338 fstring srv_name;
339 struct sockaddr_storage ip_out;
341 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
342 (got_realm ? "realm" : "domain"), realm));
344 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
346 * we call ads_try_connect() to fill in the
347 * ads->config details
349 if (ads_try_connect(ads, srv_name)) {
350 return NT_STATUS_OK;
354 return NT_STATUS_NO_LOGON_SERVERS;
357 sitename = sitename_fetch(realm);
359 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
360 (got_realm ? "realm" : "domain"), realm));
362 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
363 if (!NT_STATUS_IS_OK(status)) {
364 /* fall back to netbios if we can */
365 if ( got_realm && !lp_disable_netbios() ) {
366 got_realm = False;
367 goto again;
370 SAFE_FREE(sitename);
371 return status;
374 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
375 for ( i=0; i<count; i++ ) {
376 char server[INET6_ADDRSTRLEN];
378 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
380 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
381 continue;
383 if (!got_realm) {
384 /* realm in this case is a workgroup name. We need
385 to ignore any IP addresses in the negative connection
386 cache that match ip addresses returned in the ad realm
387 case. It sucks that I have to reproduce the logic above... */
388 c_realm = ads->server.realm;
389 if ( !c_realm || !*c_realm ) {
390 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
391 c_realm = lp_realm();
394 if (c_realm && *c_realm &&
395 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
396 /* Ensure we add the workgroup name for this
397 IP address as negative too. */
398 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
399 continue;
403 if ( ads_try_connect(ads, server) ) {
404 SAFE_FREE(ip_list);
405 SAFE_FREE(sitename);
406 return NT_STATUS_OK;
409 /* keep track of failures */
410 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
413 SAFE_FREE(ip_list);
415 /* In case we failed to contact one of our closest DC on our site we
416 * need to try to find another DC, retry with a site-less SRV DNS query
417 * - Guenther */
419 if (sitename) {
420 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
421 "trying to find another DC\n", sitename));
422 SAFE_FREE(sitename);
423 namecache_delete(realm, 0x1C);
424 goto again;
427 return NT_STATUS_NO_LOGON_SERVERS;
432 * Connect to the LDAP server
433 * @param ads Pointer to an existing ADS_STRUCT
434 * @return status of connection
436 ADS_STATUS ads_connect(ADS_STRUCT *ads)
438 int version = LDAP_VERSION3;
439 ADS_STATUS status;
440 NTSTATUS ntstatus;
441 char addr[INET6_ADDRSTRLEN];
443 ZERO_STRUCT(ads->ldap);
444 ads->ldap.last_attempt = time(NULL);
445 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
447 /* try with a user specified server */
449 if (DEBUGLEVEL >= 11) {
450 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
451 DEBUG(11,("ads_connect: entering\n"));
452 DEBUGADD(11,("%s\n", s));
453 TALLOC_FREE(s);
456 if (ads->server.ldap_server &&
457 ads_try_connect(ads, ads->server.ldap_server)) {
458 goto got_connection;
461 ntstatus = ads_find_dc(ads);
462 if (NT_STATUS_IS_OK(ntstatus)) {
463 goto got_connection;
466 status = ADS_ERROR_NT(ntstatus);
467 goto out;
469 got_connection:
471 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
472 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
474 if (!ads->auth.user_name) {
475 /* Must use the userPrincipalName value here or sAMAccountName
476 and not servicePrincipalName; found by Guenther Deschner */
478 asprintf(&ads->auth.user_name, "%s$", global_myname() );
481 if (!ads->auth.realm) {
482 ads->auth.realm = SMB_STRDUP(ads->config.realm);
485 if (!ads->auth.kdc_server) {
486 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
487 ads->auth.kdc_server = SMB_STRDUP(addr);
490 #if KRB5_DNS_HACK
491 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
492 to MIT kerberos to work (tridge) */
494 char *env;
495 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
496 setenv(env, ads->auth.kdc_server, 1);
497 free(env);
499 #endif
501 /* If the caller() requested no LDAP bind, then we are done */
503 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
504 status = ADS_SUCCESS;
505 goto out;
508 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
509 if (!ads->ldap.mem_ctx) {
510 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
511 goto out;
514 /* Otherwise setup the TCP LDAP session */
516 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
517 LDAP_PORT, lp_ldap_timeout());
518 if (ads->ldap.ld == NULL) {
519 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
520 goto out;
522 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
524 /* cache the successful connection for workgroup and realm */
525 if (ads_closest_dc(ads)) {
526 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
527 saf_store( ads->server.realm, ads->config.ldap_server_name);
530 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
532 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
533 if (!ADS_ERR_OK(status)) {
534 goto out;
537 /* fill in the current time and offsets */
539 status = ads_current_time( ads );
540 if ( !ADS_ERR_OK(status) ) {
541 goto out;
544 /* Now do the bind */
546 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
547 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
548 goto out;
551 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
552 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
553 goto out;
556 status = ads_sasl_bind(ads);
558 out:
559 if (DEBUGLEVEL >= 11) {
560 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
561 DEBUG(11,("ads_connect: leaving with: %s\n",
562 ads_errstr(status)));
563 DEBUGADD(11,("%s\n", s));
564 TALLOC_FREE(s);
567 return status;
571 * Disconnect the LDAP server
572 * @param ads Pointer to an existing ADS_STRUCT
574 void ads_disconnect(ADS_STRUCT *ads)
576 if (ads->ldap.ld) {
577 ldap_unbind(ads->ldap.ld);
578 ads->ldap.ld = NULL;
580 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
581 ads->ldap.wrap_ops->disconnect(ads);
583 if (ads->ldap.mem_ctx) {
584 talloc_free(ads->ldap.mem_ctx);
586 ZERO_STRUCT(ads->ldap);
590 Duplicate a struct berval into talloc'ed memory
592 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
594 struct berval *value;
596 if (!in_val) return NULL;
598 value = TALLOC_ZERO_P(ctx, struct berval);
599 if (value == NULL)
600 return NULL;
601 if (in_val->bv_len == 0) return value;
603 value->bv_len = in_val->bv_len;
604 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
605 in_val->bv_len);
606 return value;
610 Make a values list out of an array of (struct berval *)
612 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
613 const struct berval **in_vals)
615 struct berval **values;
616 int i;
618 if (!in_vals) return NULL;
619 for (i=0; in_vals[i]; i++)
620 ; /* count values */
621 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
622 if (!values) return NULL;
624 for (i=0; in_vals[i]; i++) {
625 values[i] = dup_berval(ctx, in_vals[i]);
627 return values;
631 UTF8-encode a values list out of an array of (char *)
633 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
635 char **values;
636 int i;
638 if (!in_vals) return NULL;
639 for (i=0; in_vals[i]; i++)
640 ; /* count values */
641 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
642 if (!values) return NULL;
644 for (i=0; in_vals[i]; i++) {
645 if (push_utf8_talloc(ctx, &values[i], in_vals[i]) == (size_t) -1) {
646 TALLOC_FREE(values);
647 return NULL;
650 return values;
654 Pull a (char *) array out of a UTF8-encoded values list
656 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
658 char **values;
659 int i;
661 if (!in_vals) return NULL;
662 for (i=0; in_vals[i]; i++)
663 ; /* count values */
664 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
665 if (!values) return NULL;
667 for (i=0; in_vals[i]; i++) {
668 pull_utf8_talloc(ctx, &values[i], in_vals[i]);
670 return values;
674 * Do a search with paged results. cookie must be null on the first
675 * call, and then returned on each subsequent call. It will be null
676 * again when the entire search is complete
677 * @param ads connection to ads server
678 * @param bind_path Base dn for the search
679 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
680 * @param expr Search expression - specified in local charset
681 * @param attrs Attributes to retrieve - specified in utf8 or ascii
682 * @param res ** which will contain results - free res* with ads_msgfree()
683 * @param count Number of entries retrieved on this page
684 * @param cookie The paged results cookie to be returned on subsequent calls
685 * @return status of search
687 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
688 const char *bind_path,
689 int scope, const char *expr,
690 const char **attrs, void *args,
691 LDAPMessage **res,
692 int *count, struct berval **cookie)
694 int rc, i, version;
695 char *utf8_expr, *utf8_path, **search_attrs;
696 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
697 BerElement *cookie_be = NULL;
698 struct berval *cookie_bv= NULL;
699 BerElement *ext_be = NULL;
700 struct berval *ext_bv= NULL;
702 TALLOC_CTX *ctx;
703 ads_control *external_control = (ads_control *) args;
705 *res = NULL;
707 if (!(ctx = talloc_init("ads_do_paged_search_args")))
708 return ADS_ERROR(LDAP_NO_MEMORY);
710 /* 0 means the conversion worked but the result was empty
711 so we only fail if it's -1. In any case, it always
712 at least nulls out the dest */
713 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
714 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
715 rc = LDAP_NO_MEMORY;
716 goto done;
719 if (!attrs || !(*attrs))
720 search_attrs = NULL;
721 else {
722 /* This would be the utf8-encoded version...*/
723 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
724 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
725 rc = LDAP_NO_MEMORY;
726 goto done;
730 /* Paged results only available on ldap v3 or later */
731 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
732 if (version < LDAP_VERSION3) {
733 rc = LDAP_NOT_SUPPORTED;
734 goto done;
737 cookie_be = ber_alloc_t(LBER_USE_DER);
738 if (*cookie) {
739 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
740 ber_bvfree(*cookie); /* don't need it from last time */
741 *cookie = NULL;
742 } else {
743 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
745 ber_flatten(cookie_be, &cookie_bv);
746 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
747 PagedResults.ldctl_iscritical = (char) 1;
748 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
749 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
751 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
752 NoReferrals.ldctl_iscritical = (char) 0;
753 NoReferrals.ldctl_value.bv_len = 0;
754 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
756 if (external_control &&
757 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
758 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
760 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
761 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
763 /* win2k does not accept a ldctl_value beeing passed in */
765 if (external_control->val != 0) {
767 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
768 rc = LDAP_NO_MEMORY;
769 goto done;
772 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
773 rc = LDAP_NO_MEMORY;
774 goto done;
776 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
777 rc = LDAP_NO_MEMORY;
778 goto done;
781 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
782 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
784 } else {
785 ExternalCtrl.ldctl_value.bv_len = 0;
786 ExternalCtrl.ldctl_value.bv_val = NULL;
789 controls[0] = &NoReferrals;
790 controls[1] = &PagedResults;
791 controls[2] = &ExternalCtrl;
792 controls[3] = NULL;
794 } else {
795 controls[0] = &NoReferrals;
796 controls[1] = &PagedResults;
797 controls[2] = NULL;
800 /* we need to disable referrals as the openldap libs don't
801 handle them and paged results at the same time. Using them
802 together results in the result record containing the server
803 page control being removed from the result list (tridge/jmcd)
805 leaving this in despite the control that says don't generate
806 referrals, in case the server doesn't support it (jmcd)
808 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
810 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
811 search_attrs, 0, controls,
812 NULL, LDAP_NO_LIMIT,
813 (LDAPMessage **)res);
815 ber_free(cookie_be, 1);
816 ber_bvfree(cookie_bv);
818 if (rc) {
819 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
820 ldap_err2string(rc)));
821 goto done;
824 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
825 NULL, &rcontrols, 0);
827 if (!rcontrols) {
828 goto done;
831 for (i=0; rcontrols[i]; i++) {
832 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
833 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
834 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
835 &cookie_bv);
836 /* the berval is the cookie, but must be freed when
837 it is all done */
838 if (cookie_bv->bv_len) /* still more to do */
839 *cookie=ber_bvdup(cookie_bv);
840 else
841 *cookie=NULL;
842 ber_bvfree(cookie_bv);
843 ber_free(cookie_be, 1);
844 break;
847 ldap_controls_free(rcontrols);
849 done:
850 talloc_destroy(ctx);
852 if (ext_be) {
853 ber_free(ext_be, 1);
856 if (ext_bv) {
857 ber_bvfree(ext_bv);
860 /* if/when we decide to utf8-encode attrs, take out this next line */
861 TALLOC_FREE(search_attrs);
863 return ADS_ERROR(rc);
866 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
867 int scope, const char *expr,
868 const char **attrs, LDAPMessage **res,
869 int *count, struct berval **cookie)
871 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
876 * Get all results for a search. This uses ads_do_paged_search() to return
877 * all entries in a large search.
878 * @param ads connection to ads server
879 * @param bind_path Base dn for the search
880 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
881 * @param expr Search expression
882 * @param attrs Attributes to retrieve
883 * @param res ** which will contain results - free res* with ads_msgfree()
884 * @return status of search
886 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
887 int scope, const char *expr,
888 const char **attrs, void *args,
889 LDAPMessage **res)
891 struct berval *cookie = NULL;
892 int count = 0;
893 ADS_STATUS status;
895 *res = NULL;
896 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
897 &count, &cookie);
899 if (!ADS_ERR_OK(status))
900 return status;
902 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
903 while (cookie) {
904 LDAPMessage *res2 = NULL;
905 ADS_STATUS status2;
906 LDAPMessage *msg, *next;
908 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
909 attrs, args, &res2, &count, &cookie);
911 if (!ADS_ERR_OK(status2)) break;
913 /* this relies on the way that ldap_add_result_entry() works internally. I hope
914 that this works on all ldap libs, but I have only tested with openldap */
915 for (msg = ads_first_message(ads, res2); msg; msg = next) {
916 next = ads_next_message(ads, msg);
917 ldap_add_result_entry((LDAPMessage **)res, msg);
919 /* note that we do not free res2, as the memory is now
920 part of the main returned list */
922 #else
923 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
924 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
925 #endif
927 return status;
930 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
931 int scope, const char *expr,
932 const char **attrs, LDAPMessage **res)
934 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
937 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
938 int scope, const char *expr,
939 const char **attrs, uint32 sd_flags,
940 LDAPMessage **res)
942 ads_control args;
944 args.control = ADS_SD_FLAGS_OID;
945 args.val = sd_flags;
946 args.critical = True;
948 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
953 * Run a function on all results for a search. Uses ads_do_paged_search() and
954 * runs the function as each page is returned, using ads_process_results()
955 * @param ads connection to ads server
956 * @param bind_path Base dn for the search
957 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
958 * @param expr Search expression - specified in local charset
959 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
960 * @param fn Function which takes attr name, values list, and data_area
961 * @param data_area Pointer which is passed to function on each call
962 * @return status of search
964 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
965 int scope, const char *expr, const char **attrs,
966 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
967 void *data_area)
969 struct berval *cookie = NULL;
970 int count = 0;
971 ADS_STATUS status;
972 LDAPMessage *res;
974 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
975 &count, &cookie);
977 if (!ADS_ERR_OK(status)) return status;
979 ads_process_results(ads, res, fn, data_area);
980 ads_msgfree(ads, res);
982 while (cookie) {
983 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
984 &res, &count, &cookie);
986 if (!ADS_ERR_OK(status)) break;
988 ads_process_results(ads, res, fn, data_area);
989 ads_msgfree(ads, res);
992 return status;
996 * Do a search with a timeout.
997 * @param ads connection to ads server
998 * @param bind_path Base dn for the search
999 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1000 * @param expr Search expression
1001 * @param attrs Attributes to retrieve
1002 * @param res ** which will contain results - free res* with ads_msgfree()
1003 * @return status of search
1005 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1006 const char *expr,
1007 const char **attrs, LDAPMessage **res)
1009 int rc;
1010 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1011 TALLOC_CTX *ctx;
1013 *res = NULL;
1014 if (!(ctx = talloc_init("ads_do_search"))) {
1015 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1016 return ADS_ERROR(LDAP_NO_MEMORY);
1019 /* 0 means the conversion worked but the result was empty
1020 so we only fail if it's negative. In any case, it always
1021 at least nulls out the dest */
1022 if ((push_utf8_talloc(ctx, &utf8_expr, expr) == (size_t)-1) ||
1023 (push_utf8_talloc(ctx, &utf8_path, bind_path) == (size_t)-1)) {
1024 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1025 rc = LDAP_NO_MEMORY;
1026 goto done;
1029 if (!attrs || !(*attrs))
1030 search_attrs = NULL;
1031 else {
1032 /* This would be the utf8-encoded version...*/
1033 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1034 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
1036 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1037 rc = LDAP_NO_MEMORY;
1038 goto done;
1042 /* see the note in ads_do_paged_search - we *must* disable referrals */
1043 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1045 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1046 search_attrs, 0, NULL, NULL,
1047 LDAP_NO_LIMIT,
1048 (LDAPMessage **)res);
1050 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1051 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1052 rc = 0;
1055 done:
1056 talloc_destroy(ctx);
1057 /* if/when we decide to utf8-encode attrs, take out this next line */
1058 TALLOC_FREE(search_attrs);
1059 return ADS_ERROR(rc);
1062 * Do a general ADS search
1063 * @param ads connection to ads server
1064 * @param res ** which will contain results - free res* with ads_msgfree()
1065 * @param expr Search expression
1066 * @param attrs Attributes to retrieve
1067 * @return status of search
1069 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1070 const char *expr, const char **attrs)
1072 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1073 expr, attrs, res);
1077 * Do a search on a specific DistinguishedName
1078 * @param ads connection to ads server
1079 * @param res ** which will contain results - free res* with ads_msgfree()
1080 * @param dn DistinguishName to search
1081 * @param attrs Attributes to retrieve
1082 * @return status of search
1084 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1085 const char *dn, const char **attrs)
1087 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1088 attrs, res);
1092 * Free up memory from a ads_search
1093 * @param ads connection to ads server
1094 * @param msg Search results to free
1096 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1098 if (!msg) return;
1099 ldap_msgfree(msg);
1103 * Free up memory from various ads requests
1104 * @param ads connection to ads server
1105 * @param mem Area to free
1107 void ads_memfree(ADS_STRUCT *ads, void *mem)
1109 SAFE_FREE(mem);
1113 * Get a dn from search results
1114 * @param ads connection to ads server
1115 * @param msg Search result
1116 * @return dn string
1118 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1120 char *utf8_dn, *unix_dn;
1122 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1124 if (!utf8_dn) {
1125 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1126 return NULL;
1129 if (pull_utf8_allocate(&unix_dn, utf8_dn) == (size_t)-1) {
1130 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1131 utf8_dn ));
1132 return NULL;
1134 ldap_memfree(utf8_dn);
1135 return unix_dn;
1139 * Get the parent from a dn
1140 * @param dn the dn to return the parent from
1141 * @return parent dn string
1143 char *ads_parent_dn(const char *dn)
1145 char *p;
1147 if (dn == NULL) {
1148 return NULL;
1151 p = strchr(dn, ',');
1153 if (p == NULL) {
1154 return NULL;
1157 return p+1;
1161 * Find a machine account given a hostname
1162 * @param ads connection to ads server
1163 * @param res ** which will contain results - free res* with ads_msgfree()
1164 * @param host Hostname to search for
1165 * @return status of search
1167 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1168 const char *machine)
1170 ADS_STATUS status;
1171 char *expr;
1172 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1174 *res = NULL;
1176 /* the easiest way to find a machine account anywhere in the tree
1177 is to look for hostname$ */
1178 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1179 DEBUG(1, ("asprintf failed!\n"));
1180 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1183 status = ads_search(ads, res, expr, attrs);
1184 SAFE_FREE(expr);
1185 return status;
1189 * Initialize a list of mods to be used in a modify request
1190 * @param ctx An initialized TALLOC_CTX
1191 * @return allocated ADS_MODLIST
1193 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1195 #define ADS_MODLIST_ALLOC_SIZE 10
1196 LDAPMod **mods;
1198 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1199 /* -1 is safety to make sure we don't go over the end.
1200 need to reset it to NULL before doing ldap modify */
1201 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1203 return (ADS_MODLIST)mods;
1208 add an attribute to the list, with values list already constructed
1210 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1211 int mod_op, const char *name,
1212 const void *_invals)
1214 const void **invals = (const void **)_invals;
1215 int curmod;
1216 LDAPMod **modlist = (LDAPMod **) *mods;
1217 struct berval **ber_values = NULL;
1218 char **char_values = NULL;
1220 if (!invals) {
1221 mod_op = LDAP_MOD_DELETE;
1222 } else {
1223 if (mod_op & LDAP_MOD_BVALUES)
1224 ber_values = ads_dup_values(ctx,
1225 (const struct berval **)invals);
1226 else
1227 char_values = ads_push_strvals(ctx,
1228 (const char **) invals);
1231 /* find the first empty slot */
1232 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1233 curmod++);
1234 if (modlist[curmod] == (LDAPMod *) -1) {
1235 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1236 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1237 return ADS_ERROR(LDAP_NO_MEMORY);
1238 memset(&modlist[curmod], 0,
1239 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1240 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1241 *mods = (ADS_MODLIST)modlist;
1244 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1245 return ADS_ERROR(LDAP_NO_MEMORY);
1246 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1247 if (mod_op & LDAP_MOD_BVALUES) {
1248 modlist[curmod]->mod_bvalues = ber_values;
1249 } else if (mod_op & LDAP_MOD_DELETE) {
1250 modlist[curmod]->mod_values = NULL;
1251 } else {
1252 modlist[curmod]->mod_values = char_values;
1255 modlist[curmod]->mod_op = mod_op;
1256 return ADS_ERROR(LDAP_SUCCESS);
1260 * Add a single string value to a mod list
1261 * @param ctx An initialized TALLOC_CTX
1262 * @param mods An initialized ADS_MODLIST
1263 * @param name The attribute name to add
1264 * @param val The value to add - NULL means DELETE
1265 * @return ADS STATUS indicating success of add
1267 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1268 const char *name, const char *val)
1270 const char *values[2];
1272 values[0] = val;
1273 values[1] = NULL;
1275 if (!val)
1276 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1277 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1281 * Add an array of string values to a mod list
1282 * @param ctx An initialized TALLOC_CTX
1283 * @param mods An initialized ADS_MODLIST
1284 * @param name The attribute name to add
1285 * @param vals The array of string values to add - NULL means DELETE
1286 * @return ADS STATUS indicating success of add
1288 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1289 const char *name, const char **vals)
1291 if (!vals)
1292 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1293 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1294 name, (const void **) vals);
1297 #if 0
1299 * Add a single ber-encoded value to a mod list
1300 * @param ctx An initialized TALLOC_CTX
1301 * @param mods An initialized ADS_MODLIST
1302 * @param name The attribute name to add
1303 * @param val The value to add - NULL means DELETE
1304 * @return ADS STATUS indicating success of add
1306 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1307 const char *name, const struct berval *val)
1309 const struct berval *values[2];
1311 values[0] = val;
1312 values[1] = NULL;
1313 if (!val)
1314 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1315 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1316 name, (const void **) values);
1318 #endif
1321 * Perform an ldap modify
1322 * @param ads connection to ads server
1323 * @param mod_dn DistinguishedName to modify
1324 * @param mods list of modifications to perform
1325 * @return status of modify
1327 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1329 int ret,i;
1330 char *utf8_dn = NULL;
1332 this control is needed to modify that contains a currently
1333 non-existent attribute (but allowable for the object) to run
1335 LDAPControl PermitModify = {
1336 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1337 {0, NULL},
1338 (char) 1};
1339 LDAPControl *controls[2];
1341 controls[0] = &PermitModify;
1342 controls[1] = NULL;
1344 if (push_utf8_allocate(&utf8_dn, mod_dn) == -1) {
1345 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1348 /* find the end of the list, marked by NULL or -1 */
1349 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1350 /* make sure the end of the list is NULL */
1351 mods[i] = NULL;
1352 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1353 (LDAPMod **) mods, controls, NULL);
1354 SAFE_FREE(utf8_dn);
1355 return ADS_ERROR(ret);
1359 * Perform an ldap add
1360 * @param ads connection to ads server
1361 * @param new_dn DistinguishedName to add
1362 * @param mods list of attributes and values for DN
1363 * @return status of add
1365 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1367 int ret, i;
1368 char *utf8_dn = NULL;
1370 if (push_utf8_allocate(&utf8_dn, new_dn) == -1) {
1371 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1372 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1375 /* find the end of the list, marked by NULL or -1 */
1376 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1377 /* make sure the end of the list is NULL */
1378 mods[i] = NULL;
1380 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1381 SAFE_FREE(utf8_dn);
1382 return ADS_ERROR(ret);
1386 * Delete a DistinguishedName
1387 * @param ads connection to ads server
1388 * @param new_dn DistinguishedName to delete
1389 * @return status of delete
1391 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1393 int ret;
1394 char *utf8_dn = NULL;
1395 if (push_utf8_allocate(&utf8_dn, del_dn) == -1) {
1396 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1397 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1400 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1401 SAFE_FREE(utf8_dn);
1402 return ADS_ERROR(ret);
1406 * Build an org unit string
1407 * if org unit is Computers or blank then assume a container, otherwise
1408 * assume a / separated list of organisational units.
1409 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1410 * @param ads connection to ads server
1411 * @param org_unit Organizational unit
1412 * @return org unit string - caller must free
1414 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1416 char *ret = NULL;
1418 if (!org_unit || !*org_unit) {
1420 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1422 /* samba4 might not yet respond to a wellknownobject-query */
1423 return ret ? ret : SMB_STRDUP("cn=Computers");
1426 if (strequal(org_unit, "Computers")) {
1427 return SMB_STRDUP("cn=Computers");
1430 /* jmcd: removed "\\" from the separation chars, because it is
1431 needed as an escape for chars like '#' which are valid in an
1432 OU name */
1433 return ads_build_path(org_unit, "/", "ou=", 1);
1437 * Get a org unit string for a well-known GUID
1438 * @param ads connection to ads server
1439 * @param wknguid Well known GUID
1440 * @return org unit string - caller must free
1442 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1444 ADS_STATUS status;
1445 LDAPMessage *res = NULL;
1446 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1447 **bind_dn_exp = NULL;
1448 const char *attrs[] = {"distinguishedName", NULL};
1449 int new_ln, wkn_ln, bind_ln, i;
1451 if (wknguid == NULL) {
1452 return NULL;
1455 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1456 DEBUG(1, ("asprintf failed!\n"));
1457 return NULL;
1460 status = ads_search_dn(ads, &res, base, attrs);
1461 if (!ADS_ERR_OK(status)) {
1462 DEBUG(1,("Failed while searching for: %s\n", base));
1463 goto out;
1466 if (ads_count_replies(ads, res) != 1) {
1467 goto out;
1470 /* substitute the bind-path from the well-known-guid-search result */
1471 wkn_dn = ads_get_dn(ads, res);
1472 if (!wkn_dn) {
1473 goto out;
1476 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1477 if (!wkn_dn_exp) {
1478 goto out;
1481 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1482 if (!bind_dn_exp) {
1483 goto out;
1486 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1488 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1491 new_ln = wkn_ln - bind_ln;
1493 ret = SMB_STRDUP(wkn_dn_exp[0]);
1494 if (!ret) {
1495 goto out;
1498 for (i=1; i < new_ln; i++) {
1499 char *s = NULL;
1501 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1502 SAFE_FREE(ret);
1503 goto out;
1506 SAFE_FREE(ret);
1507 ret = SMB_STRDUP(s);
1508 free(s);
1509 if (!ret) {
1510 goto out;
1514 out:
1515 SAFE_FREE(base);
1516 ads_msgfree(ads, res);
1517 ads_memfree(ads, wkn_dn);
1518 if (wkn_dn_exp) {
1519 ldap_value_free(wkn_dn_exp);
1521 if (bind_dn_exp) {
1522 ldap_value_free(bind_dn_exp);
1525 return ret;
1529 * Adds (appends) an item to an attribute array, rather then
1530 * replacing the whole list
1531 * @param ctx An initialized TALLOC_CTX
1532 * @param mods An initialized ADS_MODLIST
1533 * @param name name of the ldap attribute to append to
1534 * @param vals an array of values to add
1535 * @return status of addition
1538 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1539 const char *name, const char **vals)
1541 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1542 (const void *) vals);
1546 * Determines the computer account's current KVNO via an LDAP lookup
1547 * @param ads An initialized ADS_STRUCT
1548 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1549 * @return the kvno for the computer account, or -1 in case of a failure.
1552 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *machine_name)
1554 LDAPMessage *res = NULL;
1555 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1556 char *filter;
1557 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1558 char *dn_string = NULL;
1559 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1561 DEBUG(5,("ads_get_kvno: Searching for host %s\n", machine_name));
1562 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1563 return kvno;
1565 ret = ads_search(ads, &res, filter, attrs);
1566 SAFE_FREE(filter);
1567 if (!ADS_ERR_OK(ret) && ads_count_replies(ads, res)) {
1568 DEBUG(1,("ads_get_kvno: Computer Account For %s not found.\n", machine_name));
1569 ads_msgfree(ads, res);
1570 return kvno;
1573 dn_string = ads_get_dn(ads, res);
1574 if (!dn_string) {
1575 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1576 ads_msgfree(ads, res);
1577 return kvno;
1579 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1580 ads_memfree(ads, dn_string);
1582 /* ---------------------------------------------------------
1583 * 0 is returned as a default KVNO from this point on...
1584 * This is done because Windows 2000 does not support key
1585 * version numbers. Chances are that a failure in the next
1586 * step is simply due to Windows 2000 being used for a
1587 * domain controller. */
1588 kvno = 0;
1590 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1591 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1592 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1593 ads_msgfree(ads, res);
1594 return kvno;
1597 /* Success */
1598 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1599 ads_msgfree(ads, res);
1600 return kvno;
1604 * This clears out all registered spn's for a given hostname
1605 * @param ads An initilaized ADS_STRUCT
1606 * @param machine_name the NetBIOS name of the computer.
1607 * @return 0 upon success, non-zero otherwise.
1610 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1612 TALLOC_CTX *ctx;
1613 LDAPMessage *res = NULL;
1614 ADS_MODLIST mods;
1615 const char *servicePrincipalName[1] = {NULL};
1616 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1617 char *dn_string = NULL;
1619 ret = ads_find_machine_acct(ads, &res, machine_name);
1620 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1621 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1622 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1623 ads_msgfree(ads, res);
1624 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1627 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1628 ctx = talloc_init("ads_clear_service_principal_names");
1629 if (!ctx) {
1630 ads_msgfree(ads, res);
1631 return ADS_ERROR(LDAP_NO_MEMORY);
1634 if (!(mods = ads_init_mods(ctx))) {
1635 talloc_destroy(ctx);
1636 ads_msgfree(ads, res);
1637 return ADS_ERROR(LDAP_NO_MEMORY);
1639 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1640 if (!ADS_ERR_OK(ret)) {
1641 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1642 ads_msgfree(ads, res);
1643 talloc_destroy(ctx);
1644 return ret;
1646 dn_string = ads_get_dn(ads, res);
1647 if (!dn_string) {
1648 talloc_destroy(ctx);
1649 ads_msgfree(ads, res);
1650 return ADS_ERROR(LDAP_NO_MEMORY);
1652 ret = ads_gen_mod(ads, dn_string, mods);
1653 ads_memfree(ads,dn_string);
1654 if (!ADS_ERR_OK(ret)) {
1655 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1656 machine_name));
1657 ads_msgfree(ads, res);
1658 talloc_destroy(ctx);
1659 return ret;
1662 ads_msgfree(ads, res);
1663 talloc_destroy(ctx);
1664 return ret;
1668 * This adds a service principal name to an existing computer account
1669 * (found by hostname) in AD.
1670 * @param ads An initialized ADS_STRUCT
1671 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1672 * @param my_fqdn The fully qualified DNS name of the machine
1673 * @param spn A string of the service principal to add, i.e. 'host'
1674 * @return 0 upon sucess, or non-zero if a failure occurs
1677 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1678 const char *my_fqdn, const char *spn)
1680 ADS_STATUS ret;
1681 TALLOC_CTX *ctx;
1682 LDAPMessage *res = NULL;
1683 char *psp1, *psp2;
1684 ADS_MODLIST mods;
1685 char *dn_string = NULL;
1686 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1688 ret = ads_find_machine_acct(ads, &res, machine_name);
1689 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1690 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1691 machine_name));
1692 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1693 spn, machine_name, ads->config.realm));
1694 ads_msgfree(ads, res);
1695 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1698 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1699 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1700 ads_msgfree(ads, res);
1701 return ADS_ERROR(LDAP_NO_MEMORY);
1704 /* add short name spn */
1706 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1707 talloc_destroy(ctx);
1708 ads_msgfree(ads, res);
1709 return ADS_ERROR(LDAP_NO_MEMORY);
1711 strupper_m(psp1);
1712 strlower_m(&psp1[strlen(spn)]);
1713 servicePrincipalName[0] = psp1;
1715 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1716 psp1, machine_name));
1719 /* add fully qualified spn */
1721 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1722 ret = ADS_ERROR(LDAP_NO_MEMORY);
1723 goto out;
1725 strupper_m(psp2);
1726 strlower_m(&psp2[strlen(spn)]);
1727 servicePrincipalName[1] = psp2;
1729 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1730 psp2, machine_name));
1732 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1733 ret = ADS_ERROR(LDAP_NO_MEMORY);
1734 goto out;
1737 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1738 if (!ADS_ERR_OK(ret)) {
1739 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1740 goto out;
1743 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1744 ret = ADS_ERROR(LDAP_NO_MEMORY);
1745 goto out;
1748 ret = ads_gen_mod(ads, dn_string, mods);
1749 ads_memfree(ads,dn_string);
1750 if (!ADS_ERR_OK(ret)) {
1751 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1752 goto out;
1755 out:
1756 TALLOC_FREE( ctx );
1757 ads_msgfree(ads, res);
1758 return ret;
1762 * adds a machine account to the ADS server
1763 * @param ads An intialized ADS_STRUCT
1764 * @param machine_name - the NetBIOS machine name of this account.
1765 * @param account_type A number indicating the type of account to create
1766 * @param org_unit The LDAP path in which to place this account
1767 * @return 0 upon success, or non-zero otherwise
1770 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1771 const char *org_unit)
1773 ADS_STATUS ret;
1774 char *samAccountName, *controlstr;
1775 TALLOC_CTX *ctx;
1776 ADS_MODLIST mods;
1777 char *machine_escaped = NULL;
1778 char *new_dn;
1779 const char *objectClass[] = {"top", "person", "organizationalPerson",
1780 "user", "computer", NULL};
1781 LDAPMessage *res = NULL;
1782 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1783 UF_DONT_EXPIRE_PASSWD |\
1784 UF_ACCOUNTDISABLE );
1786 if (!(ctx = talloc_init("ads_add_machine_acct")))
1787 return ADS_ERROR(LDAP_NO_MEMORY);
1789 ret = ADS_ERROR(LDAP_NO_MEMORY);
1791 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1792 if (!machine_escaped) {
1793 goto done;
1796 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1797 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1799 if ( !new_dn || !samAccountName ) {
1800 goto done;
1803 #ifndef ENCTYPE_ARCFOUR_HMAC
1804 acct_control |= UF_USE_DES_KEY_ONLY;
1805 #endif
1807 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1808 goto done;
1811 if (!(mods = ads_init_mods(ctx))) {
1812 goto done;
1815 ads_mod_str(ctx, &mods, "cn", machine_name);
1816 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1817 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1818 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1820 ret = ads_gen_add(ads, new_dn, mods);
1822 done:
1823 SAFE_FREE(machine_escaped);
1824 ads_msgfree(ads, res);
1825 talloc_destroy(ctx);
1827 return ret;
1831 * move a machine account to another OU on the ADS server
1832 * @param ads - An intialized ADS_STRUCT
1833 * @param machine_name - the NetBIOS machine name of this account.
1834 * @param org_unit - The LDAP path in which to place this account
1835 * @param moved - whether we moved the machine account (optional)
1836 * @return 0 upon success, or non-zero otherwise
1839 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1840 const char *org_unit, bool *moved)
1842 ADS_STATUS rc;
1843 int ldap_status;
1844 LDAPMessage *res = NULL;
1845 char *filter = NULL;
1846 char *computer_dn = NULL;
1847 char *parent_dn;
1848 char *computer_rdn = NULL;
1849 bool need_move = False;
1851 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1852 rc = ADS_ERROR(LDAP_NO_MEMORY);
1853 goto done;
1856 /* Find pre-existing machine */
1857 rc = ads_search(ads, &res, filter, NULL);
1858 if (!ADS_ERR_OK(rc)) {
1859 goto done;
1862 computer_dn = ads_get_dn(ads, res);
1863 if (!computer_dn) {
1864 rc = ADS_ERROR(LDAP_NO_MEMORY);
1865 goto done;
1868 parent_dn = ads_parent_dn(computer_dn);
1869 if (strequal(parent_dn, org_unit)) {
1870 goto done;
1873 need_move = True;
1875 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
1876 rc = ADS_ERROR(LDAP_NO_MEMORY);
1877 goto done;
1880 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
1881 org_unit, 1, NULL, NULL);
1882 rc = ADS_ERROR(ldap_status);
1884 done:
1885 ads_msgfree(ads, res);
1886 SAFE_FREE(filter);
1887 SAFE_FREE(computer_dn);
1888 SAFE_FREE(computer_rdn);
1890 if (!ADS_ERR_OK(rc)) {
1891 need_move = False;
1894 if (moved) {
1895 *moved = need_move;
1898 return rc;
1902 dump a binary result from ldap
1904 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
1906 int i, j;
1907 for (i=0; values[i]; i++) {
1908 printf("%s: ", field);
1909 for (j=0; j<values[i]->bv_len; j++) {
1910 printf("%02X", (unsigned char)values[i]->bv_val[j]);
1912 printf("\n");
1916 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
1918 int i;
1919 for (i=0; values[i]; i++) {
1921 UUID_FLAT guid;
1922 struct GUID tmp;
1924 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
1925 smb_uuid_unpack(guid, &tmp);
1926 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
1931 dump a sid result from ldap
1933 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
1935 int i;
1936 for (i=0; values[i]; i++) {
1937 DOM_SID sid;
1938 fstring tmp;
1939 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
1940 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
1945 dump ntSecurityDescriptor
1947 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
1949 TALLOC_CTX *frame = talloc_stackframe();
1950 struct security_descriptor *psd;
1951 NTSTATUS status;
1953 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
1954 values[0]->bv_len, &psd);
1955 if (!NT_STATUS_IS_OK(status)) {
1956 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
1957 nt_errstr(status)));
1958 TALLOC_FREE(frame);
1959 return;
1962 if (psd) {
1963 ads_disp_sd(ads, talloc_tos(), psd);
1966 TALLOC_FREE(frame);
1970 dump a string result from ldap
1972 static void dump_string(const char *field, char **values)
1974 int i;
1975 for (i=0; values[i]; i++) {
1976 printf("%s: %s\n", field, values[i]);
1981 dump a field from LDAP on stdout
1982 used for debugging
1985 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
1987 const struct {
1988 const char *name;
1989 bool string;
1990 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
1991 } handlers[] = {
1992 {"objectGUID", False, dump_guid},
1993 {"netbootGUID", False, dump_guid},
1994 {"nTSecurityDescriptor", False, dump_sd},
1995 {"dnsRecord", False, dump_binary},
1996 {"objectSid", False, dump_sid},
1997 {"tokenGroups", False, dump_sid},
1998 {"tokenGroupsNoGCAcceptable", False, dump_sid},
1999 {"tokengroupsGlobalandUniversal", False, dump_sid},
2000 {"mS-DS-CreatorSID", False, dump_sid},
2001 {"msExchMailboxGuid", False, dump_guid},
2002 {NULL, True, NULL}
2004 int i;
2006 if (!field) { /* must be end of an entry */
2007 printf("\n");
2008 return False;
2011 for (i=0; handlers[i].name; i++) {
2012 if (StrCaseCmp(handlers[i].name, field) == 0) {
2013 if (!values) /* first time, indicate string or not */
2014 return handlers[i].string;
2015 handlers[i].handler(ads, field, (struct berval **) values);
2016 break;
2019 if (!handlers[i].name) {
2020 if (!values) /* first time, indicate string conversion */
2021 return True;
2022 dump_string(field, (char **)values);
2024 return False;
2028 * Dump a result from LDAP on stdout
2029 * used for debugging
2030 * @param ads connection to ads server
2031 * @param res Results to dump
2034 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2036 ads_process_results(ads, res, ads_dump_field, NULL);
2040 * Walk through results, calling a function for each entry found.
2041 * The function receives a field name, a berval * array of values,
2042 * and a data area passed through from the start. The function is
2043 * called once with null for field and values at the end of each
2044 * entry.
2045 * @param ads connection to ads server
2046 * @param res Results to process
2047 * @param fn Function for processing each result
2048 * @param data_area user-defined area to pass to function
2050 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2051 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2052 void *data_area)
2054 LDAPMessage *msg;
2055 TALLOC_CTX *ctx;
2057 if (!(ctx = talloc_init("ads_process_results")))
2058 return;
2060 for (msg = ads_first_entry(ads, res); msg;
2061 msg = ads_next_entry(ads, msg)) {
2062 char *utf8_field;
2063 BerElement *b;
2065 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2066 (LDAPMessage *)msg,&b);
2067 utf8_field;
2068 utf8_field=ldap_next_attribute(ads->ldap.ld,
2069 (LDAPMessage *)msg,b)) {
2070 struct berval **ber_vals;
2071 char **str_vals, **utf8_vals;
2072 char *field;
2073 bool string;
2075 pull_utf8_talloc(ctx, &field, utf8_field);
2076 string = fn(ads, field, NULL, data_area);
2078 if (string) {
2079 utf8_vals = ldap_get_values(ads->ldap.ld,
2080 (LDAPMessage *)msg, field);
2081 str_vals = ads_pull_strvals(ctx,
2082 (const char **) utf8_vals);
2083 fn(ads, field, (void **) str_vals, data_area);
2084 ldap_value_free(utf8_vals);
2085 } else {
2086 ber_vals = ldap_get_values_len(ads->ldap.ld,
2087 (LDAPMessage *)msg, field);
2088 fn(ads, field, (void **) ber_vals, data_area);
2090 ldap_value_free_len(ber_vals);
2092 ldap_memfree(utf8_field);
2094 ber_free(b, 0);
2095 talloc_free_children(ctx);
2096 fn(ads, NULL, NULL, data_area); /* completed an entry */
2099 talloc_destroy(ctx);
2103 * count how many replies are in a LDAPMessage
2104 * @param ads connection to ads server
2105 * @param res Results to count
2106 * @return number of replies
2108 int ads_count_replies(ADS_STRUCT *ads, void *res)
2110 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2114 * pull the first entry from a ADS result
2115 * @param ads connection to ads server
2116 * @param res Results of search
2117 * @return first entry from result
2119 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2121 return ldap_first_entry(ads->ldap.ld, res);
2125 * pull the next entry from a ADS result
2126 * @param ads connection to ads server
2127 * @param res Results of search
2128 * @return next entry from result
2130 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2132 return ldap_next_entry(ads->ldap.ld, res);
2136 * pull the first message from a ADS result
2137 * @param ads connection to ads server
2138 * @param res Results of search
2139 * @return first message from result
2141 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2143 return ldap_first_message(ads->ldap.ld, res);
2147 * pull the next message from a ADS result
2148 * @param ads connection to ads server
2149 * @param res Results of search
2150 * @return next message from result
2152 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2154 return ldap_next_message(ads->ldap.ld, res);
2158 * pull a single string from a ADS result
2159 * @param ads connection to ads server
2160 * @param mem_ctx TALLOC_CTX to use for allocating result string
2161 * @param msg Results of search
2162 * @param field Attribute to retrieve
2163 * @return Result string in talloc context
2165 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2166 const char *field)
2168 char **values;
2169 char *ret = NULL;
2170 char *ux_string;
2171 size_t rc;
2173 values = ldap_get_values(ads->ldap.ld, msg, field);
2174 if (!values)
2175 return NULL;
2177 if (values[0]) {
2178 rc = pull_utf8_talloc(mem_ctx, &ux_string,
2179 values[0]);
2180 if (rc != (size_t)-1)
2181 ret = ux_string;
2184 ldap_value_free(values);
2185 return ret;
2189 * pull an array of strings from a ADS result
2190 * @param ads connection to ads server
2191 * @param mem_ctx TALLOC_CTX to use for allocating result string
2192 * @param msg Results of search
2193 * @param field Attribute to retrieve
2194 * @return Result strings in talloc context
2196 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2197 LDAPMessage *msg, const char *field,
2198 size_t *num_values)
2200 char **values;
2201 char **ret = NULL;
2202 int i;
2204 values = ldap_get_values(ads->ldap.ld, msg, field);
2205 if (!values)
2206 return NULL;
2208 *num_values = ldap_count_values(values);
2210 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2211 if (!ret) {
2212 ldap_value_free(values);
2213 return NULL;
2216 for (i=0;i<*num_values;i++) {
2217 if (pull_utf8_talloc(mem_ctx, &ret[i], values[i]) == -1) {
2218 ldap_value_free(values);
2219 return NULL;
2222 ret[i] = NULL;
2224 ldap_value_free(values);
2225 return ret;
2229 * pull an array of strings from a ADS result
2230 * (handle large multivalue attributes with range retrieval)
2231 * @param ads connection to ads server
2232 * @param mem_ctx TALLOC_CTX to use for allocating result string
2233 * @param msg Results of search
2234 * @param field Attribute to retrieve
2235 * @param current_strings strings returned by a previous call to this function
2236 * @param next_attribute The next query should ask for this attribute
2237 * @param num_values How many values did we get this time?
2238 * @param more_values Are there more values to get?
2239 * @return Result strings in talloc context
2241 char **ads_pull_strings_range(ADS_STRUCT *ads,
2242 TALLOC_CTX *mem_ctx,
2243 LDAPMessage *msg, const char *field,
2244 char **current_strings,
2245 const char **next_attribute,
2246 size_t *num_strings,
2247 bool *more_strings)
2249 char *attr;
2250 char *expected_range_attrib, *range_attr;
2251 BerElement *ptr = NULL;
2252 char **strings;
2253 char **new_strings;
2254 size_t num_new_strings;
2255 unsigned long int range_start;
2256 unsigned long int range_end;
2258 /* we might have been given the whole lot anyway */
2259 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2260 *more_strings = False;
2261 return strings;
2264 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2266 /* look for Range result */
2267 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2268 attr;
2269 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2270 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2271 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2272 range_attr = attr;
2273 break;
2275 ldap_memfree(attr);
2277 if (!attr) {
2278 ber_free(ptr, 0);
2279 /* nothing here - this field is just empty */
2280 *more_strings = False;
2281 return NULL;
2284 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2285 &range_start, &range_end) == 2) {
2286 *more_strings = True;
2287 } else {
2288 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2289 &range_start) == 1) {
2290 *more_strings = False;
2291 } else {
2292 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2293 range_attr));
2294 ldap_memfree(range_attr);
2295 *more_strings = False;
2296 return NULL;
2300 if ((*num_strings) != range_start) {
2301 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2302 " - aborting range retreival\n",
2303 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2304 ldap_memfree(range_attr);
2305 *more_strings = False;
2306 return NULL;
2309 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2311 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2312 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2313 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2314 range_attr, (unsigned long int)range_end - range_start + 1,
2315 (unsigned long int)num_new_strings));
2316 ldap_memfree(range_attr);
2317 *more_strings = False;
2318 return NULL;
2321 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2322 *num_strings + num_new_strings);
2324 if (strings == NULL) {
2325 ldap_memfree(range_attr);
2326 *more_strings = False;
2327 return NULL;
2330 if (new_strings && num_new_strings) {
2331 memcpy(&strings[*num_strings], new_strings,
2332 sizeof(*new_strings) * num_new_strings);
2335 (*num_strings) += num_new_strings;
2337 if (*more_strings) {
2338 *next_attribute = talloc_asprintf(mem_ctx,
2339 "%s;range=%d-*",
2340 field,
2341 (int)*num_strings);
2343 if (!*next_attribute) {
2344 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2345 ldap_memfree(range_attr);
2346 *more_strings = False;
2347 return NULL;
2351 ldap_memfree(range_attr);
2353 return strings;
2357 * pull a single uint32 from a ADS result
2358 * @param ads connection to ads server
2359 * @param msg Results of search
2360 * @param field Attribute to retrieve
2361 * @param v Pointer to int to store result
2362 * @return boolean inidicating success
2364 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2365 uint32 *v)
2367 char **values;
2369 values = ldap_get_values(ads->ldap.ld, msg, field);
2370 if (!values)
2371 return False;
2372 if (!values[0]) {
2373 ldap_value_free(values);
2374 return False;
2377 *v = atoi(values[0]);
2378 ldap_value_free(values);
2379 return True;
2383 * pull a single objectGUID from an ADS result
2384 * @param ads connection to ADS server
2385 * @param msg results of search
2386 * @param guid 37-byte area to receive text guid
2387 * @return boolean indicating success
2389 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2391 char **values;
2392 UUID_FLAT flat_guid;
2394 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2395 if (!values)
2396 return False;
2398 if (values[0]) {
2399 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2400 smb_uuid_unpack(flat_guid, guid);
2401 ldap_value_free(values);
2402 return True;
2404 ldap_value_free(values);
2405 return False;
2411 * pull a single DOM_SID from a ADS result
2412 * @param ads connection to ads server
2413 * @param msg Results of search
2414 * @param field Attribute to retrieve
2415 * @param sid Pointer to sid to store result
2416 * @return boolean inidicating success
2418 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2419 DOM_SID *sid)
2421 struct berval **values;
2422 bool ret = False;
2424 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2426 if (!values)
2427 return False;
2429 if (values[0])
2430 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2432 ldap_value_free_len(values);
2433 return ret;
2437 * pull an array of DOM_SIDs from a ADS result
2438 * @param ads connection to ads server
2439 * @param mem_ctx TALLOC_CTX for allocating sid array
2440 * @param msg Results of search
2441 * @param field Attribute to retrieve
2442 * @param sids pointer to sid array to allocate
2443 * @return the count of SIDs pulled
2445 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2446 LDAPMessage *msg, const char *field, DOM_SID **sids)
2448 struct berval **values;
2449 bool ret;
2450 int count, i;
2452 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2454 if (!values)
2455 return 0;
2457 for (i=0; values[i]; i++)
2458 /* nop */ ;
2460 if (i) {
2461 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2462 if (!(*sids)) {
2463 ldap_value_free_len(values);
2464 return 0;
2466 } else {
2467 (*sids) = NULL;
2470 count = 0;
2471 for (i=0; values[i]; i++) {
2472 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2473 if (ret) {
2474 DEBUG(10, ("pulling SID: %s\n",
2475 sid_string_dbg(&(*sids)[count])));
2476 count++;
2480 ldap_value_free_len(values);
2481 return count;
2485 * pull a SEC_DESC from a ADS result
2486 * @param ads connection to ads server
2487 * @param mem_ctx TALLOC_CTX for allocating sid array
2488 * @param msg Results of search
2489 * @param field Attribute to retrieve
2490 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2491 * @return boolean inidicating success
2493 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2494 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2496 struct berval **values;
2497 bool ret = true;
2499 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2501 if (!values) return false;
2503 if (values[0]) {
2504 NTSTATUS status;
2505 status = unmarshall_sec_desc(mem_ctx,
2506 (uint8 *)values[0]->bv_val,
2507 values[0]->bv_len, sd);
2508 if (!NT_STATUS_IS_OK(status)) {
2509 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2510 nt_errstr(status)));
2511 ret = false;
2515 ldap_value_free_len(values);
2516 return ret;
2520 * in order to support usernames longer than 21 characters we need to
2521 * use both the sAMAccountName and the userPrincipalName attributes
2522 * It seems that not all users have the userPrincipalName attribute set
2524 * @param ads connection to ads server
2525 * @param mem_ctx TALLOC_CTX for allocating sid array
2526 * @param msg Results of search
2527 * @return the username
2529 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2530 LDAPMessage *msg)
2532 #if 0 /* JERRY */
2533 char *ret, *p;
2535 /* lookup_name() only works on the sAMAccountName to
2536 returning the username portion of userPrincipalName
2537 breaks winbindd_getpwnam() */
2539 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2540 if (ret && (p = strchr_m(ret, '@'))) {
2541 *p = 0;
2542 return ret;
2544 #endif
2545 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2550 * find the update serial number - this is the core of the ldap cache
2551 * @param ads connection to ads server
2552 * @param ads connection to ADS server
2553 * @param usn Pointer to retrieved update serial number
2554 * @return status of search
2556 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2558 const char *attrs[] = {"highestCommittedUSN", NULL};
2559 ADS_STATUS status;
2560 LDAPMessage *res;
2562 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2563 if (!ADS_ERR_OK(status))
2564 return status;
2566 if (ads_count_replies(ads, res) != 1) {
2567 ads_msgfree(ads, res);
2568 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2571 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2572 ads_msgfree(ads, res);
2573 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2576 ads_msgfree(ads, res);
2577 return ADS_SUCCESS;
2580 /* parse a ADS timestring - typical string is
2581 '20020917091222.0Z0' which means 09:12.22 17th September
2582 2002, timezone 0 */
2583 static time_t ads_parse_time(const char *str)
2585 struct tm tm;
2587 ZERO_STRUCT(tm);
2589 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2590 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2591 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2592 return 0;
2594 tm.tm_year -= 1900;
2595 tm.tm_mon -= 1;
2597 return timegm(&tm);
2600 /********************************************************************
2601 ********************************************************************/
2603 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2605 const char *attrs[] = {"currentTime", NULL};
2606 ADS_STATUS status;
2607 LDAPMessage *res;
2608 char *timestr;
2609 TALLOC_CTX *ctx;
2610 ADS_STRUCT *ads_s = ads;
2612 if (!(ctx = talloc_init("ads_current_time"))) {
2613 return ADS_ERROR(LDAP_NO_MEMORY);
2616 /* establish a new ldap tcp session if necessary */
2618 if ( !ads->ldap.ld ) {
2619 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2620 ads->server.ldap_server )) == NULL )
2622 goto done;
2624 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2625 status = ads_connect( ads_s );
2626 if ( !ADS_ERR_OK(status))
2627 goto done;
2630 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2631 if (!ADS_ERR_OK(status)) {
2632 goto done;
2635 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2636 if (!timestr) {
2637 ads_msgfree(ads_s, res);
2638 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2639 goto done;
2642 /* but save the time and offset in the original ADS_STRUCT */
2644 ads->config.current_time = ads_parse_time(timestr);
2646 if (ads->config.current_time != 0) {
2647 ads->auth.time_offset = ads->config.current_time - time(NULL);
2648 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2651 ads_msgfree(ads, res);
2653 status = ADS_SUCCESS;
2655 done:
2656 /* free any temporary ads connections */
2657 if ( ads_s != ads ) {
2658 ads_destroy( &ads_s );
2660 talloc_destroy(ctx);
2662 return status;
2665 /********************************************************************
2666 ********************************************************************/
2668 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2670 const char *attrs[] = {"domainFunctionality", NULL};
2671 ADS_STATUS status;
2672 LDAPMessage *res;
2673 ADS_STRUCT *ads_s = ads;
2675 *val = DS_DOMAIN_FUNCTION_2000;
2677 /* establish a new ldap tcp session if necessary */
2679 if ( !ads->ldap.ld ) {
2680 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2681 ads->server.ldap_server )) == NULL )
2683 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2684 goto done;
2686 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2687 status = ads_connect( ads_s );
2688 if ( !ADS_ERR_OK(status))
2689 goto done;
2692 /* If the attribute does not exist assume it is a Windows 2000
2693 functional domain */
2695 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2696 if (!ADS_ERR_OK(status)) {
2697 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2698 status = ADS_SUCCESS;
2700 goto done;
2703 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2704 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2706 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2709 ads_msgfree(ads, res);
2711 done:
2712 /* free any temporary ads connections */
2713 if ( ads_s != ads ) {
2714 ads_destroy( &ads_s );
2717 return status;
2721 * find the domain sid for our domain
2722 * @param ads connection to ads server
2723 * @param sid Pointer to domain sid
2724 * @return status of search
2726 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2728 const char *attrs[] = {"objectSid", NULL};
2729 LDAPMessage *res;
2730 ADS_STATUS rc;
2732 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2733 attrs, &res);
2734 if (!ADS_ERR_OK(rc)) return rc;
2735 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2736 ads_msgfree(ads, res);
2737 return ADS_ERROR_SYSTEM(ENOENT);
2739 ads_msgfree(ads, res);
2741 return ADS_SUCCESS;
2745 * find our site name
2746 * @param ads connection to ads server
2747 * @param mem_ctx Pointer to talloc context
2748 * @param site_name Pointer to the sitename
2749 * @return status of search
2751 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2753 ADS_STATUS status;
2754 LDAPMessage *res;
2755 const char *dn, *service_name;
2756 const char *attrs[] = { "dsServiceName", NULL };
2758 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2759 if (!ADS_ERR_OK(status)) {
2760 return status;
2763 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2764 if (service_name == NULL) {
2765 ads_msgfree(ads, res);
2766 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2769 ads_msgfree(ads, res);
2771 /* go up three levels */
2772 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2773 if (dn == NULL) {
2774 return ADS_ERROR(LDAP_NO_MEMORY);
2777 *site_name = talloc_strdup(mem_ctx, dn);
2778 if (*site_name == NULL) {
2779 return ADS_ERROR(LDAP_NO_MEMORY);
2782 return status;
2784 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2789 * find the site dn where a machine resides
2790 * @param ads connection to ads server
2791 * @param mem_ctx Pointer to talloc context
2792 * @param computer_name name of the machine
2793 * @param site_name Pointer to the sitename
2794 * @return status of search
2796 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2798 ADS_STATUS status;
2799 LDAPMessage *res;
2800 const char *parent, *filter;
2801 char *config_context = NULL;
2802 char *dn;
2804 /* shortcut a query */
2805 if (strequal(computer_name, ads->config.ldap_server_name)) {
2806 return ads_site_dn(ads, mem_ctx, site_dn);
2809 status = ads_config_path(ads, mem_ctx, &config_context);
2810 if (!ADS_ERR_OK(status)) {
2811 return status;
2814 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2815 if (filter == NULL) {
2816 return ADS_ERROR(LDAP_NO_MEMORY);
2819 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2820 filter, NULL, &res);
2821 if (!ADS_ERR_OK(status)) {
2822 return status;
2825 if (ads_count_replies(ads, res) != 1) {
2826 ads_msgfree(ads, res);
2827 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2830 dn = ads_get_dn(ads, res);
2831 if (dn == NULL) {
2832 ads_msgfree(ads, res);
2833 return ADS_ERROR(LDAP_NO_MEMORY);
2836 /* go up three levels */
2837 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2838 if (parent == NULL) {
2839 ads_msgfree(ads, res);
2840 ads_memfree(ads, dn);
2841 return ADS_ERROR(LDAP_NO_MEMORY);
2844 *site_dn = talloc_strdup(mem_ctx, parent);
2845 if (*site_dn == NULL) {
2846 ads_msgfree(ads, res);
2847 ads_memfree(ads, dn);
2848 return ADS_ERROR(LDAP_NO_MEMORY);
2851 ads_memfree(ads, dn);
2852 ads_msgfree(ads, res);
2854 return status;
2858 * get the upn suffixes for a domain
2859 * @param ads connection to ads server
2860 * @param mem_ctx Pointer to talloc context
2861 * @param suffixes Pointer to an array of suffixes
2862 * @param num_suffixes Pointer to the number of suffixes
2863 * @return status of search
2865 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
2867 ADS_STATUS status;
2868 LDAPMessage *res;
2869 const char *base;
2870 char *config_context = NULL;
2871 const char *attrs[] = { "uPNSuffixes", NULL };
2873 status = ads_config_path(ads, mem_ctx, &config_context);
2874 if (!ADS_ERR_OK(status)) {
2875 return status;
2878 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
2879 if (base == NULL) {
2880 return ADS_ERROR(LDAP_NO_MEMORY);
2883 status = ads_search_dn(ads, &res, base, attrs);
2884 if (!ADS_ERR_OK(status)) {
2885 return status;
2888 if (ads_count_replies(ads, res) != 1) {
2889 ads_msgfree(ads, res);
2890 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2893 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
2894 if ((*suffixes) == NULL) {
2895 ads_msgfree(ads, res);
2896 return ADS_ERROR(LDAP_NO_MEMORY);
2899 ads_msgfree(ads, res);
2901 return status;
2905 * get the joinable ous for a domain
2906 * @param ads connection to ads server
2907 * @param mem_ctx Pointer to talloc context
2908 * @param ous Pointer to an array of ous
2909 * @param num_ous Pointer to the number of ous
2910 * @return status of search
2912 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
2913 TALLOC_CTX *mem_ctx,
2914 char ***ous,
2915 size_t *num_ous)
2917 ADS_STATUS status;
2918 LDAPMessage *res = NULL;
2919 LDAPMessage *msg = NULL;
2920 const char *attrs[] = { "dn", NULL };
2921 int count = 0;
2923 status = ads_search(ads, &res,
2924 "(|(objectClass=domain)(objectclass=organizationalUnit))",
2925 attrs);
2926 if (!ADS_ERR_OK(status)) {
2927 return status;
2930 count = ads_count_replies(ads, res);
2931 if (count < 1) {
2932 ads_msgfree(ads, res);
2933 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2936 for (msg = ads_first_entry(ads, res); msg;
2937 msg = ads_next_entry(ads, msg)) {
2939 char *dn = NULL;
2941 dn = ads_get_dn(ads, msg);
2942 if (!dn) {
2943 ads_msgfree(ads, res);
2944 return ADS_ERROR(LDAP_NO_MEMORY);
2947 if (!add_string_to_array(mem_ctx, dn,
2948 (const char ***)ous,
2949 (int *)num_ous)) {
2950 ads_memfree(ads, dn);
2951 ads_msgfree(ads, res);
2952 return ADS_ERROR(LDAP_NO_MEMORY);
2955 ads_memfree(ads, dn);
2958 ads_msgfree(ads, res);
2960 return status;
2965 * pull a DOM_SID from an extended dn string
2966 * @param mem_ctx TALLOC_CTX
2967 * @param extended_dn string
2968 * @param flags string type of extended_dn
2969 * @param sid pointer to a DOM_SID
2970 * @return NT_STATUS_OK on success,
2971 * NT_INVALID_PARAMETER on error,
2972 * NT_STATUS_NOT_FOUND if no SID present
2974 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
2975 const char *extended_dn,
2976 enum ads_extended_dn_flags flags,
2977 DOM_SID *sid)
2979 char *p, *q, *dn;
2981 if (!extended_dn) {
2982 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2985 /* otherwise extended_dn gets stripped off */
2986 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
2987 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
2990 * ADS_EXTENDED_DN_HEX_STRING:
2991 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
2993 * ADS_EXTENDED_DN_STRING (only with w2k3):
2994 * <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
2996 * Object with no SID, such as an Exchange Public Folder
2997 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3000 p = strchr(dn, ';');
3001 if (!p) {
3002 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3005 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3006 DEBUG(5,("No SID present in extended dn\n"));
3007 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3010 p += strlen(";<SID=");
3012 q = strchr(p, '>');
3013 if (!q) {
3014 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3017 *q = '\0';
3019 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3021 switch (flags) {
3023 case ADS_EXTENDED_DN_STRING:
3024 if (!string_to_sid(sid, p)) {
3025 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3027 break;
3028 case ADS_EXTENDED_DN_HEX_STRING: {
3029 fstring buf;
3030 size_t buf_len;
3032 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3033 if (buf_len == 0) {
3034 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3037 if (!sid_parse(buf, buf_len, sid)) {
3038 DEBUG(10,("failed to parse sid\n"));
3039 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3041 break;
3043 default:
3044 DEBUG(10,("unknown extended dn format\n"));
3045 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3048 return ADS_ERROR_NT(NT_STATUS_OK);
3052 * pull an array of DOM_SIDs from a ADS result
3053 * @param ads connection to ads server
3054 * @param mem_ctx TALLOC_CTX for allocating sid array
3055 * @param msg Results of search
3056 * @param field Attribute to retrieve
3057 * @param flags string type of extended_dn
3058 * @param sids pointer to sid array to allocate
3059 * @return the count of SIDs pulled
3061 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3062 TALLOC_CTX *mem_ctx,
3063 LDAPMessage *msg,
3064 const char *field,
3065 enum ads_extended_dn_flags flags,
3066 DOM_SID **sids)
3068 int i;
3069 ADS_STATUS rc;
3070 size_t dn_count, ret_count = 0;
3071 char **dn_strings;
3073 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3074 &dn_count)) == NULL) {
3075 return 0;
3078 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3079 if (!(*sids)) {
3080 TALLOC_FREE(dn_strings);
3081 return 0;
3084 for (i=0; i<dn_count; i++) {
3085 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3086 flags, &(*sids)[i]);
3087 if (!ADS_ERR_OK(rc)) {
3088 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3089 NT_STATUS_NOT_FOUND)) {
3090 continue;
3092 else {
3093 TALLOC_FREE(*sids);
3094 TALLOC_FREE(dn_strings);
3095 return 0;
3098 ret_count++;
3101 TALLOC_FREE(dn_strings);
3103 return ret_count;
3106 /********************************************************************
3107 ********************************************************************/
3109 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3111 LDAPMessage *res = NULL;
3112 ADS_STATUS status;
3113 int count = 0;
3114 char *name = NULL;
3116 status = ads_find_machine_acct(ads, &res, global_myname());
3117 if (!ADS_ERR_OK(status)) {
3118 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3119 global_myname()));
3120 goto out;
3123 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3124 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3125 goto out;
3128 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3129 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3132 out:
3133 ads_msgfree(ads, res);
3135 return name;
3138 /********************************************************************
3139 ********************************************************************/
3141 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3143 LDAPMessage *res = NULL;
3144 ADS_STATUS status;
3145 int count = 0;
3146 char *name = NULL;
3148 status = ads_find_machine_acct(ads, &res, machine_name);
3149 if (!ADS_ERR_OK(status)) {
3150 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3151 global_myname()));
3152 goto out;
3155 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3156 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3157 goto out;
3160 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3161 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3164 out:
3165 ads_msgfree(ads, res);
3167 return name;
3170 /********************************************************************
3171 ********************************************************************/
3173 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3175 LDAPMessage *res = NULL;
3176 ADS_STATUS status;
3177 int count = 0;
3178 char *name = NULL;
3180 status = ads_find_machine_acct(ads, &res, global_myname());
3181 if (!ADS_ERR_OK(status)) {
3182 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3183 global_myname()));
3184 goto out;
3187 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3188 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3189 goto out;
3192 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3193 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3196 out:
3197 ads_msgfree(ads, res);
3199 return name;
3202 #if 0
3204 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3207 * Join a machine to a realm
3208 * Creates the machine account and sets the machine password
3209 * @param ads connection to ads server
3210 * @param machine name of host to add
3211 * @param org_unit Organizational unit to place machine in
3212 * @return status of join
3214 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3215 uint32 account_type, const char *org_unit)
3217 ADS_STATUS status;
3218 LDAPMessage *res = NULL;
3219 char *machine;
3221 /* machine name must be lowercase */
3222 machine = SMB_STRDUP(machine_name);
3223 strlower_m(machine);
3226 status = ads_find_machine_acct(ads, (void **)&res, machine);
3227 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3228 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3229 status = ads_leave_realm(ads, machine);
3230 if (!ADS_ERR_OK(status)) {
3231 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3232 machine, ads->config.realm));
3233 return status;
3237 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3238 if (!ADS_ERR_OK(status)) {
3239 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3240 SAFE_FREE(machine);
3241 return status;
3244 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3245 if (!ADS_ERR_OK(status)) {
3246 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3247 SAFE_FREE(machine);
3248 return status;
3251 SAFE_FREE(machine);
3252 ads_msgfree(ads, res);
3254 return status;
3256 #endif
3259 * Delete a machine from the realm
3260 * @param ads connection to ads server
3261 * @param hostname Machine to remove
3262 * @return status of delete
3264 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3266 ADS_STATUS status;
3267 void *msg;
3268 LDAPMessage *res;
3269 char *hostnameDN, *host;
3270 int rc;
3271 LDAPControl ldap_control;
3272 LDAPControl * pldap_control[2] = {NULL, NULL};
3274 pldap_control[0] = &ldap_control;
3275 memset(&ldap_control, 0, sizeof(LDAPControl));
3276 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3278 /* hostname must be lowercase */
3279 host = SMB_STRDUP(hostname);
3280 strlower_m(host);
3282 status = ads_find_machine_acct(ads, &res, host);
3283 if (!ADS_ERR_OK(status)) {
3284 DEBUG(0, ("Host account for %s does not exist.\n", host));
3285 SAFE_FREE(host);
3286 return status;
3289 msg = ads_first_entry(ads, res);
3290 if (!msg) {
3291 SAFE_FREE(host);
3292 return ADS_ERROR_SYSTEM(ENOENT);
3295 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3297 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3298 if (rc) {
3299 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3300 }else {
3301 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3304 if (rc != LDAP_SUCCESS) {
3305 const char *attrs[] = { "cn", NULL };
3306 LDAPMessage *msg_sub;
3308 /* we only search with scope ONE, we do not expect any further
3309 * objects to be created deeper */
3311 status = ads_do_search_retry(ads, hostnameDN,
3312 LDAP_SCOPE_ONELEVEL,
3313 "(objectclass=*)", attrs, &res);
3315 if (!ADS_ERR_OK(status)) {
3316 SAFE_FREE(host);
3317 ads_memfree(ads, hostnameDN);
3318 return status;
3321 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3322 msg_sub = ads_next_entry(ads, msg_sub)) {
3324 char *dn = NULL;
3326 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3327 SAFE_FREE(host);
3328 ads_memfree(ads, hostnameDN);
3329 return ADS_ERROR(LDAP_NO_MEMORY);
3332 status = ads_del_dn(ads, dn);
3333 if (!ADS_ERR_OK(status)) {
3334 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3335 SAFE_FREE(host);
3336 ads_memfree(ads, dn);
3337 ads_memfree(ads, hostnameDN);
3338 return status;
3341 ads_memfree(ads, dn);
3344 /* there should be no subordinate objects anymore */
3345 status = ads_do_search_retry(ads, hostnameDN,
3346 LDAP_SCOPE_ONELEVEL,
3347 "(objectclass=*)", attrs, &res);
3349 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3350 SAFE_FREE(host);
3351 ads_memfree(ads, hostnameDN);
3352 return status;
3355 /* delete hostnameDN now */
3356 status = ads_del_dn(ads, hostnameDN);
3357 if (!ADS_ERR_OK(status)) {
3358 SAFE_FREE(host);
3359 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3360 ads_memfree(ads, hostnameDN);
3361 return status;
3365 ads_memfree(ads, hostnameDN);
3367 status = ads_find_machine_acct(ads, &res, host);
3368 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3369 DEBUG(3, ("Failed to remove host account.\n"));
3370 SAFE_FREE(host);
3371 return status;
3374 SAFE_FREE(host);
3375 return status;
3379 * pull all token-sids from an LDAP dn
3380 * @param ads connection to ads server
3381 * @param mem_ctx TALLOC_CTX for allocating sid array
3382 * @param dn of LDAP object
3383 * @param user_sid pointer to DOM_SID (objectSid)
3384 * @param primary_group_sid pointer to DOM_SID (self composed)
3385 * @param sids pointer to sid array to allocate
3386 * @param num_sids counter of SIDs pulled
3387 * @return status of token query
3389 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3390 TALLOC_CTX *mem_ctx,
3391 const char *dn,
3392 DOM_SID *user_sid,
3393 DOM_SID *primary_group_sid,
3394 DOM_SID **sids,
3395 size_t *num_sids)
3397 ADS_STATUS status;
3398 LDAPMessage *res = NULL;
3399 int count = 0;
3400 size_t tmp_num_sids;
3401 DOM_SID *tmp_sids;
3402 DOM_SID tmp_user_sid;
3403 DOM_SID tmp_primary_group_sid;
3404 uint32 pgid;
3405 const char *attrs[] = {
3406 "objectSid",
3407 "tokenGroups",
3408 "primaryGroupID",
3409 NULL
3412 status = ads_search_retry_dn(ads, &res, dn, attrs);
3413 if (!ADS_ERR_OK(status)) {
3414 return status;
3417 count = ads_count_replies(ads, res);
3418 if (count != 1) {
3419 ads_msgfree(ads, res);
3420 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3423 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3424 ads_msgfree(ads, res);
3425 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3428 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3429 ads_msgfree(ads, res);
3430 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3434 /* hack to compose the primary group sid without knowing the
3435 * domsid */
3437 DOM_SID domsid;
3438 uint32 dummy_rid;
3440 sid_copy(&domsid, &tmp_user_sid);
3442 if (!sid_split_rid(&domsid, &dummy_rid)) {
3443 ads_msgfree(ads, res);
3444 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3447 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3448 ads_msgfree(ads, res);
3449 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3453 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3455 if (tmp_num_sids == 0 || !tmp_sids) {
3456 ads_msgfree(ads, res);
3457 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3460 if (num_sids) {
3461 *num_sids = tmp_num_sids;
3464 if (sids) {
3465 *sids = tmp_sids;
3468 if (user_sid) {
3469 *user_sid = tmp_user_sid;
3472 if (primary_group_sid) {
3473 *primary_group_sid = tmp_primary_group_sid;
3476 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3478 ads_msgfree(ads, res);
3479 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3483 * Find a sAMAccoutName in LDAP
3484 * @param ads connection to ads server
3485 * @param mem_ctx TALLOC_CTX for allocating sid array
3486 * @param samaccountname to search
3487 * @param uac_ret uint32 pointer userAccountControl attribute value
3488 * @param dn_ret pointer to dn
3489 * @return status of token query
3491 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3492 TALLOC_CTX *mem_ctx,
3493 const char *samaccountname,
3494 uint32 *uac_ret,
3495 const char **dn_ret)
3497 ADS_STATUS status;
3498 const char *attrs[] = { "userAccountControl", NULL };
3499 const char *filter;
3500 LDAPMessage *res = NULL;
3501 char *dn = NULL;
3502 uint32 uac = 0;
3504 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3505 samaccountname);
3506 if (filter == NULL) {
3507 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3508 goto out;
3511 status = ads_do_search_all(ads, ads->config.bind_path,
3512 LDAP_SCOPE_SUBTREE,
3513 filter, attrs, &res);
3515 if (!ADS_ERR_OK(status)) {
3516 goto out;
3519 if (ads_count_replies(ads, res) != 1) {
3520 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3521 goto out;
3524 dn = ads_get_dn(ads, res);
3525 if (dn == NULL) {
3526 status = ADS_ERROR(LDAP_NO_MEMORY);
3527 goto out;
3530 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3531 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3532 goto out;
3535 if (uac_ret) {
3536 *uac_ret = uac;
3539 if (dn_ret) {
3540 *dn_ret = talloc_strdup(mem_ctx, dn);
3541 if (!*dn_ret) {
3542 status = ADS_ERROR(LDAP_NO_MEMORY);
3543 goto out;
3546 out:
3547 ads_memfree(ads, dn);
3548 ads_msgfree(ads, res);
3550 return status;
3554 * find our configuration path
3555 * @param ads connection to ads server
3556 * @param mem_ctx Pointer to talloc context
3557 * @param config_path Pointer to the config path
3558 * @return status of search
3560 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3561 TALLOC_CTX *mem_ctx,
3562 char **config_path)
3564 ADS_STATUS status;
3565 LDAPMessage *res = NULL;
3566 const char *config_context = NULL;
3567 const char *attrs[] = { "configurationNamingContext", NULL };
3569 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3570 "(objectclass=*)", attrs, &res);
3571 if (!ADS_ERR_OK(status)) {
3572 return status;
3575 config_context = ads_pull_string(ads, mem_ctx, res,
3576 "configurationNamingContext");
3577 ads_msgfree(ads, res);
3578 if (!config_context) {
3579 return ADS_ERROR(LDAP_NO_MEMORY);
3582 if (config_path) {
3583 *config_path = talloc_strdup(mem_ctx, config_context);
3584 if (!*config_path) {
3585 return ADS_ERROR(LDAP_NO_MEMORY);
3589 return ADS_ERROR(LDAP_SUCCESS);
3593 * find the displayName of an extended right
3594 * @param ads connection to ads server
3595 * @param config_path The config path
3596 * @param mem_ctx Pointer to talloc context
3597 * @param GUID struct of the rightsGUID
3598 * @return status of search
3600 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3601 const char *config_path,
3602 TALLOC_CTX *mem_ctx,
3603 const struct GUID *rights_guid)
3605 ADS_STATUS rc;
3606 LDAPMessage *res = NULL;
3607 char *expr = NULL;
3608 const char *attrs[] = { "displayName", NULL };
3609 const char *result = NULL;
3610 const char *path;
3612 if (!ads || !mem_ctx || !rights_guid) {
3613 goto done;
3616 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3617 smb_uuid_string(mem_ctx, *rights_guid));
3618 if (!expr) {
3619 goto done;
3622 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3623 if (!path) {
3624 goto done;
3627 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3628 expr, attrs, &res);
3629 if (!ADS_ERR_OK(rc)) {
3630 goto done;
3633 if (ads_count_replies(ads, res) != 1) {
3634 goto done;
3637 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3639 done:
3640 ads_msgfree(ads, res);
3641 return result;
3646 * verify or build and verify an account ou
3647 * @param mem_ctx Pointer to talloc context
3648 * @param ads connection to ads server
3649 * @param account_ou
3650 * @return status of search
3653 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3654 ADS_STRUCT *ads,
3655 const char **account_ou)
3657 struct ldb_dn *name_dn = NULL;
3658 const char *name = NULL;
3659 char *ou_string = NULL;
3661 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3662 if (name_dn) {
3663 return ADS_SUCCESS;
3666 ou_string = ads_ou_string(ads, *account_ou);
3667 if (!ou_string) {
3668 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3671 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3672 ads->config.bind_path);
3673 SAFE_FREE(ou_string);
3674 if (!name) {
3675 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3678 name_dn = ldb_dn_explode(mem_ctx, name);
3679 if (!name_dn) {
3680 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3683 *account_ou = talloc_strdup(mem_ctx, name);
3684 if (!*account_ou) {
3685 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3688 return ADS_SUCCESS;
3691 #endif