Document how to enable the name aliasing support in Winbind.
[Samba.git] / source / libads / ldap.c
blobeb45e3a0dd7050fbdc7609330838218031bcf024
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "lib/ldb/include/includes.h"
27 #ifdef HAVE_LDAP
29 /**
30 * @file ldap.c
31 * @brief basic ldap client-side routines for ads server communications
33 * The routines contained here should do the necessary ldap calls for
34 * ads setups.
36 * Important note: attribute names passed into ads_ routines must
37 * already be in UTF-8 format. We do not convert them because in almost
38 * all cases, they are just ascii (which is represented with the same
39 * codepoints in UTF-8). This may have to change at some point
40 **/
43 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
45 static SIG_ATOMIC_T gotalarm;
47 /***************************************************************
48 Signal function to tell us we timed out.
49 ****************************************************************/
51 static void gotalarm_sig(void)
53 gotalarm = 1;
56 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
58 LDAP *ldp = NULL;
61 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
62 "%u seconds\n", server, port, to));
64 /* Setup timeout */
65 gotalarm = 0;
66 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
67 alarm(to);
68 /* End setup timeout. */
70 ldp = ldap_open(server, port);
72 if (ldp == NULL) {
73 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
74 server, port, strerror(errno)));
75 } else {
76 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
79 /* Teardown timeout. */
80 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
81 alarm(0);
83 return ldp;
86 static int ldap_search_with_timeout(LDAP *ld,
87 LDAP_CONST char *base,
88 int scope,
89 LDAP_CONST char *filter,
90 char **attrs,
91 int attrsonly,
92 LDAPControl **sctrls,
93 LDAPControl **cctrls,
94 int sizelimit,
95 LDAPMessage **res )
97 struct timeval timeout;
98 int result;
100 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
101 timeout.tv_sec = lp_ldap_timeout();
102 timeout.tv_usec = 0;
104 /* Setup alarm timeout.... Do we need both of these ? JRA. */
105 gotalarm = 0;
106 CatchSignal(SIGALRM, SIGNAL_CAST gotalarm_sig);
107 alarm(lp_ldap_timeout());
108 /* End setup timeout. */
110 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
111 attrsonly, sctrls, cctrls, &timeout,
112 sizelimit, res);
114 /* Teardown timeout. */
115 CatchSignal(SIGALRM, SIGNAL_CAST SIG_IGN);
116 alarm(0);
118 if (gotalarm != 0)
119 return LDAP_TIMELIMIT_EXCEEDED;
121 return result;
124 /**********************************************
125 Do client and server sitename match ?
126 **********************************************/
128 bool ads_sitename_match(ADS_STRUCT *ads)
130 if (ads->config.server_site_name == NULL &&
131 ads->config.client_site_name == NULL ) {
132 DEBUG(10,("ads_sitename_match: both null\n"));
133 return True;
135 if (ads->config.server_site_name &&
136 ads->config.client_site_name &&
137 strequal(ads->config.server_site_name,
138 ads->config.client_site_name)) {
139 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
140 return True;
142 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
143 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
144 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
145 return False;
148 /**********************************************
149 Is this the closest DC ?
150 **********************************************/
152 bool ads_closest_dc(ADS_STRUCT *ads)
154 if (ads->config.flags & NBT_SERVER_CLOSEST) {
155 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
156 return True;
159 /* not sure if this can ever happen */
160 if (ads_sitename_match(ads)) {
161 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
162 return True;
165 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
166 ads->config.ldap_server_name));
168 return False;
173 try a connection to a given ldap server, returning True and setting the servers IP
174 in the ads struct if successful
176 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
178 char *srv;
179 struct nbt_cldap_netlogon_5 cldap_reply;
180 TALLOC_CTX *mem_ctx = NULL;
181 bool ret = false;
183 if (!server || !*server) {
184 return False;
187 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
188 server, ads->server.realm));
190 mem_ctx = talloc_init("ads_try_connect");
191 if (!mem_ctx) {
192 DEBUG(0,("out of memory\n"));
193 return false;
196 /* this copes with inet_ntoa brokenness */
198 srv = SMB_STRDUP(server);
200 ZERO_STRUCT( cldap_reply );
202 if ( !ads_cldap_netlogon_5(mem_ctx, srv, ads->server.realm, &cldap_reply ) ) {
203 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
204 ret = false;
205 goto out;
208 /* Check the CLDAP reply flags */
210 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
211 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
212 srv));
213 ret = false;
214 goto out;
217 /* Fill in the ads->config values */
219 SAFE_FREE(ads->config.realm);
220 SAFE_FREE(ads->config.bind_path);
221 SAFE_FREE(ads->config.ldap_server_name);
222 SAFE_FREE(ads->config.server_site_name);
223 SAFE_FREE(ads->config.client_site_name);
224 SAFE_FREE(ads->server.workgroup);
226 ads->config.flags = cldap_reply.server_type;
227 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
228 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
229 strupper_m(ads->config.realm);
230 ads->config.bind_path = ads_build_dn(ads->config.realm);
231 if (*cldap_reply.server_site) {
232 ads->config.server_site_name =
233 SMB_STRDUP(cldap_reply.server_site);
235 if (*cldap_reply.client_site) {
236 ads->config.client_site_name =
237 SMB_STRDUP(cldap_reply.client_site);
239 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain);
241 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
242 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
243 DEBUG(1,("ads_try_connect: unable to convert %s "
244 "to an address\n",
245 srv));
246 ret = false;
247 goto out;
250 /* Store our site name. */
251 sitename_store( cldap_reply.domain, cldap_reply.client_site);
252 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
254 ret = true;
255 out:
256 SAFE_FREE(srv);
257 TALLOC_FREE(mem_ctx);
259 return ret;
262 /**********************************************************************
263 Try to find an AD dc using our internal name resolution routines
264 Try the realm first and then then workgroup name if netbios is not
265 disabled
266 **********************************************************************/
268 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
270 const char *c_realm;
271 int count, i=0;
272 struct ip_service *ip_list;
273 const char *realm;
274 bool got_realm = False;
275 bool use_own_domain = False;
276 char *sitename;
277 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
279 /* if the realm and workgroup are both empty, assume they are ours */
281 /* realm */
282 c_realm = ads->server.realm;
284 if ( !c_realm || !*c_realm ) {
285 /* special case where no realm and no workgroup means our own */
286 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
287 use_own_domain = True;
288 c_realm = lp_realm();
292 if (c_realm && *c_realm)
293 got_realm = True;
295 /* we need to try once with the realm name and fallback to the
296 netbios domain name if we fail (if netbios has not been disabled */
298 if ( !got_realm && !lp_disable_netbios() ) {
299 c_realm = ads->server.workgroup;
300 if (!c_realm || !*c_realm) {
301 if ( use_own_domain )
302 c_realm = lp_workgroup();
305 if ( !c_realm || !*c_realm ) {
306 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
307 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
311 realm = c_realm;
313 sitename = sitename_fetch(realm);
315 again:
317 DEBUG(6,("ads_find_dc: looking for %s '%s'\n",
318 (got_realm ? "realm" : "domain"), realm));
320 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
321 if (!NT_STATUS_IS_OK(status)) {
322 /* fall back to netbios if we can */
323 if ( got_realm && !lp_disable_netbios() ) {
324 got_realm = False;
325 goto again;
328 SAFE_FREE(sitename);
329 return status;
332 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
333 for ( i=0; i<count; i++ ) {
334 char server[INET6_ADDRSTRLEN];
336 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
338 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
339 continue;
341 if (!got_realm) {
342 /* realm in this case is a workgroup name. We need
343 to ignore any IP addresses in the negative connection
344 cache that match ip addresses returned in the ad realm
345 case. It sucks that I have to reproduce the logic above... */
346 c_realm = ads->server.realm;
347 if ( !c_realm || !*c_realm ) {
348 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
349 c_realm = lp_realm();
352 if (c_realm && *c_realm &&
353 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
354 /* Ensure we add the workgroup name for this
355 IP address as negative too. */
356 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
357 continue;
361 if ( ads_try_connect(ads, server, false) ) {
362 SAFE_FREE(ip_list);
363 SAFE_FREE(sitename);
364 return NT_STATUS_OK;
367 /* keep track of failures */
368 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
371 SAFE_FREE(ip_list);
373 /* In case we failed to contact one of our closest DC on our site we
374 * need to try to find another DC, retry with a site-less SRV DNS query
375 * - Guenther */
377 if (sitename) {
378 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
379 "trying to find another DC\n", sitename));
380 SAFE_FREE(sitename);
381 namecache_delete(realm, 0x1C);
382 goto again;
385 return NT_STATUS_NO_LOGON_SERVERS;
388 /*********************************************************************
389 *********************************************************************/
391 static NTSTATUS ads_lookup_site(void)
393 ADS_STRUCT *ads = NULL;
394 ADS_STATUS ads_status;
395 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
397 ads = ads_init(lp_realm(), NULL, NULL);
398 if (!ads) {
399 return NT_STATUS_NO_MEMORY;
402 /* The NO_BIND here will find a DC and set the client site
403 but not establish the TCP connection */
405 ads->auth.flags = ADS_AUTH_NO_BIND;
406 ads_status = ads_connect(ads);
407 if (!ADS_ERR_OK(ads_status)) {
408 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
409 ads_errstr(ads_status)));
411 nt_status = ads_ntstatus(ads_status);
413 if (ads) {
414 ads_destroy(&ads);
417 return nt_status;
420 /*********************************************************************
421 *********************************************************************/
423 static const char* host_dns_domain(const char *fqdn)
425 const char *p = fqdn;
427 /* go to next char following '.' */
429 if ((p = strchr_m(fqdn, '.')) != NULL) {
430 p++;
433 return p;
438 * Connect to the Global Catalog server
439 * @param ads Pointer to an existing ADS_STRUCT
440 * @return status of connection
442 * Simple wrapper around ads_connect() that fills in the
443 * GC ldap server information
446 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
448 TALLOC_CTX *frame = talloc_stackframe();
449 struct dns_rr_srv *gcs_list;
450 int num_gcs;
451 char *realm = ads->server.realm;
452 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
453 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
454 int i;
455 bool done = false;
456 char *sitename = NULL;
458 if (!realm)
459 realm = lp_realm();
461 if ((sitename = sitename_fetch(realm)) == NULL) {
462 ads_lookup_site();
463 sitename = sitename_fetch(realm);
466 do {
467 /* We try once with a sitename and once without
468 (unless we don't have a sitename and then we're
469 done */
471 if (sitename == NULL)
472 done = true;
474 nt_status = ads_dns_query_gcs(frame, realm, sitename,
475 &gcs_list, &num_gcs);
477 SAFE_FREE(sitename);
479 if (!NT_STATUS_IS_OK(nt_status)) {
480 ads_status = ADS_ERROR_NT(nt_status);
481 goto done;
484 /* Loop until we get a successful connection or have gone
485 through them all. When connecting a GC server, make sure that
486 the realm is the server's DNS name and not the forest root */
488 for (i=0; i<num_gcs; i++) {
489 ads->server.gc = true;
490 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
491 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
492 ads_status = ads_connect(ads);
493 if (ADS_ERR_OK(ads_status)) {
494 /* Reset the bind_dn to "". A Global Catalog server
495 may host multiple domain trees in a forest.
496 Windows 2003 GC server will accept "" as the search
497 path to imply search all domain trees in the forest */
499 SAFE_FREE(ads->config.bind_path);
500 ads->config.bind_path = SMB_STRDUP("");
503 goto done;
505 SAFE_FREE(ads->server.ldap_server);
506 SAFE_FREE(ads->server.realm);
509 TALLOC_FREE(gcs_list);
510 num_gcs = 0;
511 } while (!done);
513 done:
514 SAFE_FREE(sitename);
515 talloc_destroy(frame);
517 return ads_status;
522 * Connect to the LDAP server
523 * @param ads Pointer to an existing ADS_STRUCT
524 * @return status of connection
526 ADS_STATUS ads_connect(ADS_STRUCT *ads)
528 int version = LDAP_VERSION3;
529 ADS_STATUS status;
530 NTSTATUS ntstatus;
531 char addr[INET6_ADDRSTRLEN];
533 ZERO_STRUCT(ads->ldap);
534 ads->ldap.last_attempt = time(NULL);
535 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
537 /* try with a user specified server */
539 if (DEBUGLEVEL >= 11) {
540 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
541 DEBUG(11,("ads_connect: entering\n"));
542 DEBUGADD(11,("%s\n", s));
543 TALLOC_FREE(s);
546 if (ads->server.ldap_server &&
547 ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
548 goto got_connection;
551 ntstatus = ads_find_dc(ads);
552 if (NT_STATUS_IS_OK(ntstatus)) {
553 goto got_connection;
556 status = ADS_ERROR_NT(ntstatus);
557 goto out;
559 got_connection:
561 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
562 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
564 if (!ads->auth.user_name) {
565 /* Must use the userPrincipalName value here or sAMAccountName
566 and not servicePrincipalName; found by Guenther Deschner */
568 asprintf(&ads->auth.user_name, "%s$", global_myname() );
571 if (!ads->auth.realm) {
572 ads->auth.realm = SMB_STRDUP(ads->config.realm);
575 if (!ads->auth.kdc_server) {
576 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
577 ads->auth.kdc_server = SMB_STRDUP(addr);
580 #if KRB5_DNS_HACK
581 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
582 to MIT kerberos to work (tridge) */
584 char *env;
585 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
586 setenv(env, ads->auth.kdc_server, 1);
587 free(env);
589 #endif
591 /* If the caller() requested no LDAP bind, then we are done */
593 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
594 status = ADS_SUCCESS;
595 goto out;
598 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
599 if (!ads->ldap.mem_ctx) {
600 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
601 goto out;
604 /* Otherwise setup the TCP LDAP session */
606 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
607 ads->ldap.port, lp_ldap_timeout());
608 if (ads->ldap.ld == NULL) {
609 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
610 goto out;
612 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
614 /* cache the successful connection for workgroup and realm */
615 if (ads_closest_dc(ads)) {
616 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
617 saf_store( ads->server.workgroup, addr);
618 saf_store( ads->server.realm, addr);
621 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
623 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
624 if (!ADS_ERR_OK(status)) {
625 goto out;
628 /* fill in the current time and offsets */
630 status = ads_current_time( ads );
631 if ( !ADS_ERR_OK(status) ) {
632 goto out;
635 /* Now do the bind */
637 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
638 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
639 goto out;
642 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
643 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
644 goto out;
647 status = ads_sasl_bind(ads);
649 out:
650 if (DEBUGLEVEL >= 11) {
651 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
652 DEBUG(11,("ads_connect: leaving with: %s\n",
653 ads_errstr(status)));
654 DEBUGADD(11,("%s\n", s));
655 TALLOC_FREE(s);
658 return status;
662 * Connect to the LDAP server using given credentials
663 * @param ads Pointer to an existing ADS_STRUCT
664 * @return status of connection
666 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
668 ads->auth.flags |= ADS_AUTH_USER_CREDS;
670 return ads_connect(ads);
674 * Disconnect the LDAP server
675 * @param ads Pointer to an existing ADS_STRUCT
677 void ads_disconnect(ADS_STRUCT *ads)
679 if (ads->ldap.ld) {
680 ldap_unbind(ads->ldap.ld);
681 ads->ldap.ld = NULL;
683 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
684 ads->ldap.wrap_ops->disconnect(ads);
686 if (ads->ldap.mem_ctx) {
687 talloc_free(ads->ldap.mem_ctx);
689 ZERO_STRUCT(ads->ldap);
693 Duplicate a struct berval into talloc'ed memory
695 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
697 struct berval *value;
699 if (!in_val) return NULL;
701 value = TALLOC_ZERO_P(ctx, struct berval);
702 if (value == NULL)
703 return NULL;
704 if (in_val->bv_len == 0) return value;
706 value->bv_len = in_val->bv_len;
707 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
708 in_val->bv_len);
709 return value;
713 Make a values list out of an array of (struct berval *)
715 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
716 const struct berval **in_vals)
718 struct berval **values;
719 int i;
721 if (!in_vals) return NULL;
722 for (i=0; in_vals[i]; i++)
723 ; /* count values */
724 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
725 if (!values) return NULL;
727 for (i=0; in_vals[i]; i++) {
728 values[i] = dup_berval(ctx, in_vals[i]);
730 return values;
734 UTF8-encode a values list out of an array of (char *)
736 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
738 char **values;
739 int i;
740 size_t size;
742 if (!in_vals) return NULL;
743 for (i=0; in_vals[i]; i++)
744 ; /* count values */
745 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
746 if (!values) return NULL;
748 for (i=0; in_vals[i]; i++) {
749 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
750 TALLOC_FREE(values);
751 return NULL;
754 return values;
758 Pull a (char *) array out of a UTF8-encoded values list
760 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
762 char **values;
763 int i;
764 size_t converted_size;
766 if (!in_vals) return NULL;
767 for (i=0; in_vals[i]; i++)
768 ; /* count values */
769 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
770 if (!values) return NULL;
772 for (i=0; in_vals[i]; i++) {
773 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
774 &converted_size)) {
775 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
776 "%s", strerror(errno)));
779 return values;
783 * Do a search with paged results. cookie must be null on the first
784 * call, and then returned on each subsequent call. It will be null
785 * again when the entire search is complete
786 * @param ads connection to ads server
787 * @param bind_path Base dn for the search
788 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
789 * @param expr Search expression - specified in local charset
790 * @param attrs Attributes to retrieve - specified in utf8 or ascii
791 * @param res ** which will contain results - free res* with ads_msgfree()
792 * @param count Number of entries retrieved on this page
793 * @param cookie The paged results cookie to be returned on subsequent calls
794 * @return status of search
796 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
797 const char *bind_path,
798 int scope, const char *expr,
799 const char **attrs, void *args,
800 LDAPMessage **res,
801 int *count, struct berval **cookie)
803 int rc, i, version;
804 char *utf8_expr, *utf8_path, **search_attrs;
805 size_t converted_size;
806 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
807 BerElement *cookie_be = NULL;
808 struct berval *cookie_bv= NULL;
809 BerElement *ext_be = NULL;
810 struct berval *ext_bv= NULL;
812 TALLOC_CTX *ctx;
813 ads_control *external_control = (ads_control *) args;
815 *res = NULL;
817 if (!(ctx = talloc_init("ads_do_paged_search_args")))
818 return ADS_ERROR(LDAP_NO_MEMORY);
820 /* 0 means the conversion worked but the result was empty
821 so we only fail if it's -1. In any case, it always
822 at least nulls out the dest */
823 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
824 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
826 rc = LDAP_NO_MEMORY;
827 goto done;
830 if (!attrs || !(*attrs))
831 search_attrs = NULL;
832 else {
833 /* This would be the utf8-encoded version...*/
834 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
835 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
836 rc = LDAP_NO_MEMORY;
837 goto done;
841 /* Paged results only available on ldap v3 or later */
842 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
843 if (version < LDAP_VERSION3) {
844 rc = LDAP_NOT_SUPPORTED;
845 goto done;
848 cookie_be = ber_alloc_t(LBER_USE_DER);
849 if (*cookie) {
850 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
851 ber_bvfree(*cookie); /* don't need it from last time */
852 *cookie = NULL;
853 } else {
854 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
856 ber_flatten(cookie_be, &cookie_bv);
857 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
858 PagedResults.ldctl_iscritical = (char) 1;
859 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
860 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
862 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
863 NoReferrals.ldctl_iscritical = (char) 0;
864 NoReferrals.ldctl_value.bv_len = 0;
865 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
867 if (external_control &&
868 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
869 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
871 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
872 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
874 /* win2k does not accept a ldctl_value beeing passed in */
876 if (external_control->val != 0) {
878 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
879 rc = LDAP_NO_MEMORY;
880 goto done;
883 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
884 rc = LDAP_NO_MEMORY;
885 goto done;
887 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
888 rc = LDAP_NO_MEMORY;
889 goto done;
892 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
893 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
895 } else {
896 ExternalCtrl.ldctl_value.bv_len = 0;
897 ExternalCtrl.ldctl_value.bv_val = NULL;
900 controls[0] = &NoReferrals;
901 controls[1] = &PagedResults;
902 controls[2] = &ExternalCtrl;
903 controls[3] = NULL;
905 } else {
906 controls[0] = &NoReferrals;
907 controls[1] = &PagedResults;
908 controls[2] = NULL;
911 /* we need to disable referrals as the openldap libs don't
912 handle them and paged results at the same time. Using them
913 together results in the result record containing the server
914 page control being removed from the result list (tridge/jmcd)
916 leaving this in despite the control that says don't generate
917 referrals, in case the server doesn't support it (jmcd)
919 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
921 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
922 search_attrs, 0, controls,
923 NULL, LDAP_NO_LIMIT,
924 (LDAPMessage **)res);
926 ber_free(cookie_be, 1);
927 ber_bvfree(cookie_bv);
929 if (rc) {
930 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
931 ldap_err2string(rc)));
932 goto done;
935 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
936 NULL, &rcontrols, 0);
938 if (!rcontrols) {
939 goto done;
942 for (i=0; rcontrols[i]; i++) {
943 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
944 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
945 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
946 &cookie_bv);
947 /* the berval is the cookie, but must be freed when
948 it is all done */
949 if (cookie_bv->bv_len) /* still more to do */
950 *cookie=ber_bvdup(cookie_bv);
951 else
952 *cookie=NULL;
953 ber_bvfree(cookie_bv);
954 ber_free(cookie_be, 1);
955 break;
958 ldap_controls_free(rcontrols);
960 done:
961 talloc_destroy(ctx);
963 if (ext_be) {
964 ber_free(ext_be, 1);
967 if (ext_bv) {
968 ber_bvfree(ext_bv);
971 /* if/when we decide to utf8-encode attrs, take out this next line */
972 TALLOC_FREE(search_attrs);
974 return ADS_ERROR(rc);
977 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
978 int scope, const char *expr,
979 const char **attrs, LDAPMessage **res,
980 int *count, struct berval **cookie)
982 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
987 * Get all results for a search. This uses ads_do_paged_search() to return
988 * all entries in a large search.
989 * @param ads connection to ads server
990 * @param bind_path Base dn for the search
991 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
992 * @param expr Search expression
993 * @param attrs Attributes to retrieve
994 * @param res ** which will contain results - free res* with ads_msgfree()
995 * @return status of search
997 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
998 int scope, const char *expr,
999 const char **attrs, void *args,
1000 LDAPMessage **res)
1002 struct berval *cookie = NULL;
1003 int count = 0;
1004 ADS_STATUS status;
1006 *res = NULL;
1007 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1008 &count, &cookie);
1010 if (!ADS_ERR_OK(status))
1011 return status;
1013 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1014 while (cookie) {
1015 LDAPMessage *res2 = NULL;
1016 ADS_STATUS status2;
1017 LDAPMessage *msg, *next;
1019 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1020 attrs, args, &res2, &count, &cookie);
1022 if (!ADS_ERR_OK(status2)) break;
1024 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1025 that this works on all ldap libs, but I have only tested with openldap */
1026 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1027 next = ads_next_message(ads, msg);
1028 ldap_add_result_entry((LDAPMessage **)res, msg);
1030 /* note that we do not free res2, as the memory is now
1031 part of the main returned list */
1033 #else
1034 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1035 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1036 #endif
1038 return status;
1041 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1042 int scope, const char *expr,
1043 const char **attrs, LDAPMessage **res)
1045 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1048 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1049 int scope, const char *expr,
1050 const char **attrs, uint32 sd_flags,
1051 LDAPMessage **res)
1053 ads_control args;
1055 args.control = ADS_SD_FLAGS_OID;
1056 args.val = sd_flags;
1057 args.critical = True;
1059 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1064 * Run a function on all results for a search. Uses ads_do_paged_search() and
1065 * runs the function as each page is returned, using ads_process_results()
1066 * @param ads connection to ads server
1067 * @param bind_path Base dn for the search
1068 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1069 * @param expr Search expression - specified in local charset
1070 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1071 * @param fn Function which takes attr name, values list, and data_area
1072 * @param data_area Pointer which is passed to function on each call
1073 * @return status of search
1075 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1076 int scope, const char *expr, const char **attrs,
1077 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1078 void *data_area)
1080 struct berval *cookie = NULL;
1081 int count = 0;
1082 ADS_STATUS status;
1083 LDAPMessage *res;
1085 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1086 &count, &cookie);
1088 if (!ADS_ERR_OK(status)) return status;
1090 ads_process_results(ads, res, fn, data_area);
1091 ads_msgfree(ads, res);
1093 while (cookie) {
1094 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1095 &res, &count, &cookie);
1097 if (!ADS_ERR_OK(status)) break;
1099 ads_process_results(ads, res, fn, data_area);
1100 ads_msgfree(ads, res);
1103 return status;
1107 * Do a search with a timeout.
1108 * @param ads connection to ads server
1109 * @param bind_path Base dn for the search
1110 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1111 * @param expr Search expression
1112 * @param attrs Attributes to retrieve
1113 * @param res ** which will contain results - free res* with ads_msgfree()
1114 * @return status of search
1116 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1117 const char *expr,
1118 const char **attrs, LDAPMessage **res)
1120 int rc;
1121 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1122 size_t converted_size;
1123 TALLOC_CTX *ctx;
1125 *res = NULL;
1126 if (!(ctx = talloc_init("ads_do_search"))) {
1127 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1128 return ADS_ERROR(LDAP_NO_MEMORY);
1131 /* 0 means the conversion worked but the result was empty
1132 so we only fail if it's negative. In any case, it always
1133 at least nulls out the dest */
1134 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1135 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1137 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1138 rc = LDAP_NO_MEMORY;
1139 goto done;
1142 if (!attrs || !(*attrs))
1143 search_attrs = NULL;
1144 else {
1145 /* This would be the utf8-encoded version...*/
1146 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1147 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
1149 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1150 rc = LDAP_NO_MEMORY;
1151 goto done;
1155 /* see the note in ads_do_paged_search - we *must* disable referrals */
1156 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1158 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1159 search_attrs, 0, NULL, NULL,
1160 LDAP_NO_LIMIT,
1161 (LDAPMessage **)res);
1163 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1164 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1165 rc = 0;
1168 done:
1169 talloc_destroy(ctx);
1170 /* if/when we decide to utf8-encode attrs, take out this next line */
1171 TALLOC_FREE(search_attrs);
1172 return ADS_ERROR(rc);
1175 * Do a general ADS search
1176 * @param ads connection to ads server
1177 * @param res ** which will contain results - free res* with ads_msgfree()
1178 * @param expr Search expression
1179 * @param attrs Attributes to retrieve
1180 * @return status of search
1182 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1183 const char *expr, const char **attrs)
1185 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1186 expr, attrs, res);
1190 * Do a search on a specific DistinguishedName
1191 * @param ads connection to ads server
1192 * @param res ** which will contain results - free res* with ads_msgfree()
1193 * @param dn DistinguishName to search
1194 * @param attrs Attributes to retrieve
1195 * @return status of search
1197 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1198 const char *dn, const char **attrs)
1200 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1201 attrs, res);
1205 * Free up memory from a ads_search
1206 * @param ads connection to ads server
1207 * @param msg Search results to free
1209 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1211 if (!msg) return;
1212 ldap_msgfree(msg);
1216 * Free up memory from various ads requests
1217 * @param ads connection to ads server
1218 * @param mem Area to free
1220 void ads_memfree(ADS_STRUCT *ads, void *mem)
1222 SAFE_FREE(mem);
1226 * Get a dn from search results
1227 * @param ads connection to ads server
1228 * @param msg Search result
1229 * @return dn string
1231 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1233 char *utf8_dn, *unix_dn;
1234 size_t converted_size;
1236 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1238 if (!utf8_dn) {
1239 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1240 return NULL;
1243 if (!pull_utf8_allocate(&unix_dn, utf8_dn, &converted_size)) {
1244 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1245 utf8_dn ));
1246 return NULL;
1248 ldap_memfree(utf8_dn);
1249 return unix_dn;
1253 * Get the parent from a dn
1254 * @param dn the dn to return the parent from
1255 * @return parent dn string
1257 char *ads_parent_dn(const char *dn)
1259 char *p;
1261 if (dn == NULL) {
1262 return NULL;
1265 p = strchr(dn, ',');
1267 if (p == NULL) {
1268 return NULL;
1271 return p+1;
1275 * Find a machine account given a hostname
1276 * @param ads connection to ads server
1277 * @param res ** which will contain results - free res* with ads_msgfree()
1278 * @param host Hostname to search for
1279 * @return status of search
1281 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1282 const char *machine)
1284 ADS_STATUS status;
1285 char *expr;
1286 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1288 *res = NULL;
1290 /* the easiest way to find a machine account anywhere in the tree
1291 is to look for hostname$ */
1292 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1293 DEBUG(1, ("asprintf failed!\n"));
1294 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1297 status = ads_search(ads, res, expr, attrs);
1298 SAFE_FREE(expr);
1299 return status;
1303 * Initialize a list of mods to be used in a modify request
1304 * @param ctx An initialized TALLOC_CTX
1305 * @return allocated ADS_MODLIST
1307 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1309 #define ADS_MODLIST_ALLOC_SIZE 10
1310 LDAPMod **mods;
1312 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1313 /* -1 is safety to make sure we don't go over the end.
1314 need to reset it to NULL before doing ldap modify */
1315 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1317 return (ADS_MODLIST)mods;
1322 add an attribute to the list, with values list already constructed
1324 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1325 int mod_op, const char *name,
1326 const void *_invals)
1328 const void **invals = (const void **)_invals;
1329 int curmod;
1330 LDAPMod **modlist = (LDAPMod **) *mods;
1331 struct berval **ber_values = NULL;
1332 char **char_values = NULL;
1334 if (!invals) {
1335 mod_op = LDAP_MOD_DELETE;
1336 } else {
1337 if (mod_op & LDAP_MOD_BVALUES)
1338 ber_values = ads_dup_values(ctx,
1339 (const struct berval **)invals);
1340 else
1341 char_values = ads_push_strvals(ctx,
1342 (const char **) invals);
1345 /* find the first empty slot */
1346 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1347 curmod++);
1348 if (modlist[curmod] == (LDAPMod *) -1) {
1349 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1350 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1351 return ADS_ERROR(LDAP_NO_MEMORY);
1352 memset(&modlist[curmod], 0,
1353 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1354 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1355 *mods = (ADS_MODLIST)modlist;
1358 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1359 return ADS_ERROR(LDAP_NO_MEMORY);
1360 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1361 if (mod_op & LDAP_MOD_BVALUES) {
1362 modlist[curmod]->mod_bvalues = ber_values;
1363 } else if (mod_op & LDAP_MOD_DELETE) {
1364 modlist[curmod]->mod_values = NULL;
1365 } else {
1366 modlist[curmod]->mod_values = char_values;
1369 modlist[curmod]->mod_op = mod_op;
1370 return ADS_ERROR(LDAP_SUCCESS);
1374 * Add a single string value to a mod list
1375 * @param ctx An initialized TALLOC_CTX
1376 * @param mods An initialized ADS_MODLIST
1377 * @param name The attribute name to add
1378 * @param val The value to add - NULL means DELETE
1379 * @return ADS STATUS indicating success of add
1381 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1382 const char *name, const char *val)
1384 const char *values[2];
1386 values[0] = val;
1387 values[1] = NULL;
1389 if (!val)
1390 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1391 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1395 * Add an array of string values to a mod list
1396 * @param ctx An initialized TALLOC_CTX
1397 * @param mods An initialized ADS_MODLIST
1398 * @param name The attribute name to add
1399 * @param vals The array of string values to add - NULL means DELETE
1400 * @return ADS STATUS indicating success of add
1402 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1403 const char *name, const char **vals)
1405 if (!vals)
1406 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1407 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1408 name, (const void **) vals);
1411 #if 0
1413 * Add a single ber-encoded value to a mod list
1414 * @param ctx An initialized TALLOC_CTX
1415 * @param mods An initialized ADS_MODLIST
1416 * @param name The attribute name to add
1417 * @param val The value to add - NULL means DELETE
1418 * @return ADS STATUS indicating success of add
1420 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1421 const char *name, const struct berval *val)
1423 const struct berval *values[2];
1425 values[0] = val;
1426 values[1] = NULL;
1427 if (!val)
1428 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1429 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1430 name, (const void **) values);
1432 #endif
1435 * Perform an ldap modify
1436 * @param ads connection to ads server
1437 * @param mod_dn DistinguishedName to modify
1438 * @param mods list of modifications to perform
1439 * @return status of modify
1441 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1443 int ret,i;
1444 char *utf8_dn = NULL;
1445 size_t converted_size;
1447 this control is needed to modify that contains a currently
1448 non-existent attribute (but allowable for the object) to run
1450 LDAPControl PermitModify = {
1451 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1452 {0, NULL},
1453 (char) 1};
1454 LDAPControl *controls[2];
1456 controls[0] = &PermitModify;
1457 controls[1] = NULL;
1459 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1460 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1463 /* find the end of the list, marked by NULL or -1 */
1464 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1465 /* make sure the end of the list is NULL */
1466 mods[i] = NULL;
1467 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1468 (LDAPMod **) mods, controls, NULL);
1469 SAFE_FREE(utf8_dn);
1470 return ADS_ERROR(ret);
1474 * Perform an ldap add
1475 * @param ads connection to ads server
1476 * @param new_dn DistinguishedName to add
1477 * @param mods list of attributes and values for DN
1478 * @return status of add
1480 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1482 int ret, i;
1483 char *utf8_dn = NULL;
1484 size_t converted_size;
1486 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1487 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1488 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1491 /* find the end of the list, marked by NULL or -1 */
1492 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1493 /* make sure the end of the list is NULL */
1494 mods[i] = NULL;
1496 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1497 SAFE_FREE(utf8_dn);
1498 return ADS_ERROR(ret);
1502 * Delete a DistinguishedName
1503 * @param ads connection to ads server
1504 * @param new_dn DistinguishedName to delete
1505 * @return status of delete
1507 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1509 int ret;
1510 char *utf8_dn = NULL;
1511 size_t converted_size;
1512 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1513 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1514 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1517 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1518 SAFE_FREE(utf8_dn);
1519 return ADS_ERROR(ret);
1523 * Build an org unit string
1524 * if org unit is Computers or blank then assume a container, otherwise
1525 * assume a / separated list of organisational units.
1526 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1527 * @param ads connection to ads server
1528 * @param org_unit Organizational unit
1529 * @return org unit string - caller must free
1531 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1533 char *ret = NULL;
1535 if (!org_unit || !*org_unit) {
1537 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1539 /* samba4 might not yet respond to a wellknownobject-query */
1540 return ret ? ret : SMB_STRDUP("cn=Computers");
1543 if (strequal(org_unit, "Computers")) {
1544 return SMB_STRDUP("cn=Computers");
1547 /* jmcd: removed "\\" from the separation chars, because it is
1548 needed as an escape for chars like '#' which are valid in an
1549 OU name */
1550 return ads_build_path(org_unit, "/", "ou=", 1);
1554 * Get a org unit string for a well-known GUID
1555 * @param ads connection to ads server
1556 * @param wknguid Well known GUID
1557 * @return org unit string - caller must free
1559 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1561 ADS_STATUS status;
1562 LDAPMessage *res = NULL;
1563 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1564 **bind_dn_exp = NULL;
1565 const char *attrs[] = {"distinguishedName", NULL};
1566 int new_ln, wkn_ln, bind_ln, i;
1568 if (wknguid == NULL) {
1569 return NULL;
1572 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1573 DEBUG(1, ("asprintf failed!\n"));
1574 return NULL;
1577 status = ads_search_dn(ads, &res, base, attrs);
1578 if (!ADS_ERR_OK(status)) {
1579 DEBUG(1,("Failed while searching for: %s\n", base));
1580 goto out;
1583 if (ads_count_replies(ads, res) != 1) {
1584 goto out;
1587 /* substitute the bind-path from the well-known-guid-search result */
1588 wkn_dn = ads_get_dn(ads, res);
1589 if (!wkn_dn) {
1590 goto out;
1593 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1594 if (!wkn_dn_exp) {
1595 goto out;
1598 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1599 if (!bind_dn_exp) {
1600 goto out;
1603 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1605 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1608 new_ln = wkn_ln - bind_ln;
1610 ret = SMB_STRDUP(wkn_dn_exp[0]);
1611 if (!ret) {
1612 goto out;
1615 for (i=1; i < new_ln; i++) {
1616 char *s = NULL;
1618 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1619 SAFE_FREE(ret);
1620 goto out;
1623 SAFE_FREE(ret);
1624 ret = SMB_STRDUP(s);
1625 free(s);
1626 if (!ret) {
1627 goto out;
1631 out:
1632 SAFE_FREE(base);
1633 ads_msgfree(ads, res);
1634 ads_memfree(ads, wkn_dn);
1635 if (wkn_dn_exp) {
1636 ldap_value_free(wkn_dn_exp);
1638 if (bind_dn_exp) {
1639 ldap_value_free(bind_dn_exp);
1642 return ret;
1646 * Adds (appends) an item to an attribute array, rather then
1647 * replacing the whole list
1648 * @param ctx An initialized TALLOC_CTX
1649 * @param mods An initialized ADS_MODLIST
1650 * @param name name of the ldap attribute to append to
1651 * @param vals an array of values to add
1652 * @return status of addition
1655 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1656 const char *name, const char **vals)
1658 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1659 (const void *) vals);
1663 * Determines the an account's current KVNO via an LDAP lookup
1664 * @param ads An initialized ADS_STRUCT
1665 * @param account_name the NT samaccountname.
1666 * @return the kvno for the account, or -1 in case of a failure.
1669 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1671 LDAPMessage *res = NULL;
1672 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1673 char *filter;
1674 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1675 char *dn_string = NULL;
1676 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1678 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1679 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1680 return kvno;
1682 ret = ads_search(ads, &res, filter, attrs);
1683 SAFE_FREE(filter);
1684 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1685 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1686 ads_msgfree(ads, res);
1687 return kvno;
1690 dn_string = ads_get_dn(ads, res);
1691 if (!dn_string) {
1692 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1693 ads_msgfree(ads, res);
1694 return kvno;
1696 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1697 ads_memfree(ads, dn_string);
1699 /* ---------------------------------------------------------
1700 * 0 is returned as a default KVNO from this point on...
1701 * This is done because Windows 2000 does not support key
1702 * version numbers. Chances are that a failure in the next
1703 * step is simply due to Windows 2000 being used for a
1704 * domain controller. */
1705 kvno = 0;
1707 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1708 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1709 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1710 ads_msgfree(ads, res);
1711 return kvno;
1714 /* Success */
1715 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1716 ads_msgfree(ads, res);
1717 return kvno;
1721 * Determines the computer account's current KVNO via an LDAP lookup
1722 * @param ads An initialized ADS_STRUCT
1723 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1724 * @return the kvno for the computer account, or -1 in case of a failure.
1727 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1729 char *computer_account = NULL;
1730 uint32_t kvno = -1;
1732 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1733 return kvno;
1736 kvno = ads_get_kvno(ads, computer_account);
1737 free(computer_account);
1739 return kvno;
1743 * This clears out all registered spn's for a given hostname
1744 * @param ads An initilaized ADS_STRUCT
1745 * @param machine_name the NetBIOS name of the computer.
1746 * @return 0 upon success, non-zero otherwise.
1749 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1751 TALLOC_CTX *ctx;
1752 LDAPMessage *res = NULL;
1753 ADS_MODLIST mods;
1754 const char *servicePrincipalName[1] = {NULL};
1755 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1756 char *dn_string = NULL;
1758 ret = ads_find_machine_acct(ads, &res, machine_name);
1759 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1760 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1761 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1762 ads_msgfree(ads, res);
1763 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1766 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1767 ctx = talloc_init("ads_clear_service_principal_names");
1768 if (!ctx) {
1769 ads_msgfree(ads, res);
1770 return ADS_ERROR(LDAP_NO_MEMORY);
1773 if (!(mods = ads_init_mods(ctx))) {
1774 talloc_destroy(ctx);
1775 ads_msgfree(ads, res);
1776 return ADS_ERROR(LDAP_NO_MEMORY);
1778 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1779 if (!ADS_ERR_OK(ret)) {
1780 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1781 ads_msgfree(ads, res);
1782 talloc_destroy(ctx);
1783 return ret;
1785 dn_string = ads_get_dn(ads, res);
1786 if (!dn_string) {
1787 talloc_destroy(ctx);
1788 ads_msgfree(ads, res);
1789 return ADS_ERROR(LDAP_NO_MEMORY);
1791 ret = ads_gen_mod(ads, dn_string, mods);
1792 ads_memfree(ads,dn_string);
1793 if (!ADS_ERR_OK(ret)) {
1794 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1795 machine_name));
1796 ads_msgfree(ads, res);
1797 talloc_destroy(ctx);
1798 return ret;
1801 ads_msgfree(ads, res);
1802 talloc_destroy(ctx);
1803 return ret;
1807 * This adds a service principal name to an existing computer account
1808 * (found by hostname) in AD.
1809 * @param ads An initialized ADS_STRUCT
1810 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1811 * @param my_fqdn The fully qualified DNS name of the machine
1812 * @param spn A string of the service principal to add, i.e. 'host'
1813 * @return 0 upon sucess, or non-zero if a failure occurs
1816 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1817 const char *my_fqdn, const char *spn)
1819 ADS_STATUS ret;
1820 TALLOC_CTX *ctx;
1821 LDAPMessage *res = NULL;
1822 char *psp1, *psp2;
1823 ADS_MODLIST mods;
1824 char *dn_string = NULL;
1825 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1827 ret = ads_find_machine_acct(ads, &res, machine_name);
1828 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1829 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1830 machine_name));
1831 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1832 spn, machine_name, ads->config.realm));
1833 ads_msgfree(ads, res);
1834 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1837 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1838 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1839 ads_msgfree(ads, res);
1840 return ADS_ERROR(LDAP_NO_MEMORY);
1843 /* add short name spn */
1845 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1846 talloc_destroy(ctx);
1847 ads_msgfree(ads, res);
1848 return ADS_ERROR(LDAP_NO_MEMORY);
1850 strupper_m(psp1);
1851 strlower_m(&psp1[strlen(spn)]);
1852 servicePrincipalName[0] = psp1;
1854 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1855 psp1, machine_name));
1858 /* add fully qualified spn */
1860 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1861 ret = ADS_ERROR(LDAP_NO_MEMORY);
1862 goto out;
1864 strupper_m(psp2);
1865 strlower_m(&psp2[strlen(spn)]);
1866 servicePrincipalName[1] = psp2;
1868 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1869 psp2, machine_name));
1871 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1872 ret = ADS_ERROR(LDAP_NO_MEMORY);
1873 goto out;
1876 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1877 if (!ADS_ERR_OK(ret)) {
1878 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1879 goto out;
1882 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1883 ret = ADS_ERROR(LDAP_NO_MEMORY);
1884 goto out;
1887 ret = ads_gen_mod(ads, dn_string, mods);
1888 ads_memfree(ads,dn_string);
1889 if (!ADS_ERR_OK(ret)) {
1890 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1891 goto out;
1894 out:
1895 TALLOC_FREE( ctx );
1896 ads_msgfree(ads, res);
1897 return ret;
1901 * adds a machine account to the ADS server
1902 * @param ads An intialized ADS_STRUCT
1903 * @param machine_name - the NetBIOS machine name of this account.
1904 * @param account_type A number indicating the type of account to create
1905 * @param org_unit The LDAP path in which to place this account
1906 * @return 0 upon success, or non-zero otherwise
1909 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1910 const char *org_unit)
1912 ADS_STATUS ret;
1913 char *samAccountName, *controlstr;
1914 TALLOC_CTX *ctx;
1915 ADS_MODLIST mods;
1916 char *machine_escaped = NULL;
1917 char *new_dn;
1918 const char *objectClass[] = {"top", "person", "organizationalPerson",
1919 "user", "computer", NULL};
1920 LDAPMessage *res = NULL;
1921 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1922 UF_DONT_EXPIRE_PASSWD |\
1923 UF_ACCOUNTDISABLE );
1925 if (!(ctx = talloc_init("ads_add_machine_acct")))
1926 return ADS_ERROR(LDAP_NO_MEMORY);
1928 ret = ADS_ERROR(LDAP_NO_MEMORY);
1930 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1931 if (!machine_escaped) {
1932 goto done;
1935 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1936 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1938 if ( !new_dn || !samAccountName ) {
1939 goto done;
1942 #ifndef ENCTYPE_ARCFOUR_HMAC
1943 acct_control |= UF_USE_DES_KEY_ONLY;
1944 #endif
1946 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1947 goto done;
1950 if (!(mods = ads_init_mods(ctx))) {
1951 goto done;
1954 ads_mod_str(ctx, &mods, "cn", machine_name);
1955 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1956 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1957 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1959 ret = ads_gen_add(ads, new_dn, mods);
1961 done:
1962 SAFE_FREE(machine_escaped);
1963 ads_msgfree(ads, res);
1964 talloc_destroy(ctx);
1966 return ret;
1970 * move a machine account to another OU on the ADS server
1971 * @param ads - An intialized ADS_STRUCT
1972 * @param machine_name - the NetBIOS machine name of this account.
1973 * @param org_unit - The LDAP path in which to place this account
1974 * @param moved - whether we moved the machine account (optional)
1975 * @return 0 upon success, or non-zero otherwise
1978 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1979 const char *org_unit, bool *moved)
1981 ADS_STATUS rc;
1982 int ldap_status;
1983 LDAPMessage *res = NULL;
1984 char *filter = NULL;
1985 char *computer_dn = NULL;
1986 char *parent_dn;
1987 char *computer_rdn = NULL;
1988 bool need_move = False;
1990 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1991 rc = ADS_ERROR(LDAP_NO_MEMORY);
1992 goto done;
1995 /* Find pre-existing machine */
1996 rc = ads_search(ads, &res, filter, NULL);
1997 if (!ADS_ERR_OK(rc)) {
1998 goto done;
2001 computer_dn = ads_get_dn(ads, res);
2002 if (!computer_dn) {
2003 rc = ADS_ERROR(LDAP_NO_MEMORY);
2004 goto done;
2007 parent_dn = ads_parent_dn(computer_dn);
2008 if (strequal(parent_dn, org_unit)) {
2009 goto done;
2012 need_move = True;
2014 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2015 rc = ADS_ERROR(LDAP_NO_MEMORY);
2016 goto done;
2019 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2020 org_unit, 1, NULL, NULL);
2021 rc = ADS_ERROR(ldap_status);
2023 done:
2024 ads_msgfree(ads, res);
2025 SAFE_FREE(filter);
2026 SAFE_FREE(computer_dn);
2027 SAFE_FREE(computer_rdn);
2029 if (!ADS_ERR_OK(rc)) {
2030 need_move = False;
2033 if (moved) {
2034 *moved = need_move;
2037 return rc;
2041 dump a binary result from ldap
2043 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2045 int i, j;
2046 for (i=0; values[i]; i++) {
2047 printf("%s: ", field);
2048 for (j=0; j<values[i]->bv_len; j++) {
2049 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2051 printf("\n");
2055 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2057 int i;
2058 for (i=0; values[i]; i++) {
2060 UUID_FLAT guid;
2061 struct GUID tmp;
2063 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2064 smb_uuid_unpack(guid, &tmp);
2065 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
2070 dump a sid result from ldap
2072 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2074 int i;
2075 for (i=0; values[i]; i++) {
2076 DOM_SID sid;
2077 fstring tmp;
2078 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2079 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2084 dump ntSecurityDescriptor
2086 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2088 TALLOC_CTX *frame = talloc_stackframe();
2089 struct security_descriptor *psd;
2090 NTSTATUS status;
2092 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2093 values[0]->bv_len, &psd);
2094 if (!NT_STATUS_IS_OK(status)) {
2095 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2096 nt_errstr(status)));
2097 TALLOC_FREE(frame);
2098 return;
2101 if (psd) {
2102 ads_disp_sd(ads, talloc_tos(), psd);
2105 TALLOC_FREE(frame);
2109 dump a string result from ldap
2111 static void dump_string(const char *field, char **values)
2113 int i;
2114 for (i=0; values[i]; i++) {
2115 printf("%s: %s\n", field, values[i]);
2120 dump a field from LDAP on stdout
2121 used for debugging
2124 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2126 const struct {
2127 const char *name;
2128 bool string;
2129 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2130 } handlers[] = {
2131 {"objectGUID", False, dump_guid},
2132 {"netbootGUID", False, dump_guid},
2133 {"nTSecurityDescriptor", False, dump_sd},
2134 {"dnsRecord", False, dump_binary},
2135 {"objectSid", False, dump_sid},
2136 {"tokenGroups", False, dump_sid},
2137 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2138 {"tokengroupsGlobalandUniversal", False, dump_sid},
2139 {"mS-DS-CreatorSID", False, dump_sid},
2140 {"msExchMailboxGuid", False, dump_guid},
2141 {NULL, True, NULL}
2143 int i;
2145 if (!field) { /* must be end of an entry */
2146 printf("\n");
2147 return False;
2150 for (i=0; handlers[i].name; i++) {
2151 if (StrCaseCmp(handlers[i].name, field) == 0) {
2152 if (!values) /* first time, indicate string or not */
2153 return handlers[i].string;
2154 handlers[i].handler(ads, field, (struct berval **) values);
2155 break;
2158 if (!handlers[i].name) {
2159 if (!values) /* first time, indicate string conversion */
2160 return True;
2161 dump_string(field, (char **)values);
2163 return False;
2167 * Dump a result from LDAP on stdout
2168 * used for debugging
2169 * @param ads connection to ads server
2170 * @param res Results to dump
2173 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2175 ads_process_results(ads, res, ads_dump_field, NULL);
2179 * Walk through results, calling a function for each entry found.
2180 * The function receives a field name, a berval * array of values,
2181 * and a data area passed through from the start. The function is
2182 * called once with null for field and values at the end of each
2183 * entry.
2184 * @param ads connection to ads server
2185 * @param res Results to process
2186 * @param fn Function for processing each result
2187 * @param data_area user-defined area to pass to function
2189 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2190 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2191 void *data_area)
2193 LDAPMessage *msg;
2194 TALLOC_CTX *ctx;
2195 size_t converted_size;
2197 if (!(ctx = talloc_init("ads_process_results")))
2198 return;
2200 for (msg = ads_first_entry(ads, res); msg;
2201 msg = ads_next_entry(ads, msg)) {
2202 char *utf8_field;
2203 BerElement *b;
2205 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2206 (LDAPMessage *)msg,&b);
2207 utf8_field;
2208 utf8_field=ldap_next_attribute(ads->ldap.ld,
2209 (LDAPMessage *)msg,b)) {
2210 struct berval **ber_vals;
2211 char **str_vals, **utf8_vals;
2212 char *field;
2213 bool string;
2215 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2216 &converted_size))
2218 DEBUG(0,("ads_process_results: "
2219 "pull_utf8_talloc failed: %s",
2220 strerror(errno)));
2223 string = fn(ads, field, NULL, data_area);
2225 if (string) {
2226 utf8_vals = ldap_get_values(ads->ldap.ld,
2227 (LDAPMessage *)msg, field);
2228 str_vals = ads_pull_strvals(ctx,
2229 (const char **) utf8_vals);
2230 fn(ads, field, (void **) str_vals, data_area);
2231 ldap_value_free(utf8_vals);
2232 } else {
2233 ber_vals = ldap_get_values_len(ads->ldap.ld,
2234 (LDAPMessage *)msg, field);
2235 fn(ads, field, (void **) ber_vals, data_area);
2237 ldap_value_free_len(ber_vals);
2239 ldap_memfree(utf8_field);
2241 ber_free(b, 0);
2242 talloc_free_children(ctx);
2243 fn(ads, NULL, NULL, data_area); /* completed an entry */
2246 talloc_destroy(ctx);
2250 * count how many replies are in a LDAPMessage
2251 * @param ads connection to ads server
2252 * @param res Results to count
2253 * @return number of replies
2255 int ads_count_replies(ADS_STRUCT *ads, void *res)
2257 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2261 * pull the first entry from a ADS result
2262 * @param ads connection to ads server
2263 * @param res Results of search
2264 * @return first entry from result
2266 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2268 return ldap_first_entry(ads->ldap.ld, res);
2272 * pull the next entry from a ADS result
2273 * @param ads connection to ads server
2274 * @param res Results of search
2275 * @return next entry from result
2277 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2279 return ldap_next_entry(ads->ldap.ld, res);
2283 * pull the first message from a ADS result
2284 * @param ads connection to ads server
2285 * @param res Results of search
2286 * @return first message from result
2288 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2290 return ldap_first_message(ads->ldap.ld, res);
2294 * pull the next message from a ADS result
2295 * @param ads connection to ads server
2296 * @param res Results of search
2297 * @return next message from result
2299 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2301 return ldap_next_message(ads->ldap.ld, res);
2305 * pull a single string from a ADS result
2306 * @param ads connection to ads server
2307 * @param mem_ctx TALLOC_CTX to use for allocating result string
2308 * @param msg Results of search
2309 * @param field Attribute to retrieve
2310 * @return Result string in talloc context
2312 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2313 const char *field)
2315 char **values;
2316 char *ret = NULL;
2317 char *ux_string;
2318 size_t converted_size;
2320 values = ldap_get_values(ads->ldap.ld, msg, field);
2321 if (!values)
2322 return NULL;
2324 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2325 &converted_size))
2327 ret = ux_string;
2329 ldap_value_free(values);
2330 return ret;
2334 * pull an array of strings from a ADS result
2335 * @param ads connection to ads server
2336 * @param mem_ctx TALLOC_CTX to use for allocating result string
2337 * @param msg Results of search
2338 * @param field Attribute to retrieve
2339 * @return Result strings in talloc context
2341 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2342 LDAPMessage *msg, const char *field,
2343 size_t *num_values)
2345 char **values;
2346 char **ret = NULL;
2347 int i;
2348 size_t converted_size;
2350 values = ldap_get_values(ads->ldap.ld, msg, field);
2351 if (!values)
2352 return NULL;
2354 *num_values = ldap_count_values(values);
2356 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2357 if (!ret) {
2358 ldap_value_free(values);
2359 return NULL;
2362 for (i=0;i<*num_values;i++) {
2363 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2364 &converted_size))
2366 ldap_value_free(values);
2367 return NULL;
2370 ret[i] = NULL;
2372 ldap_value_free(values);
2373 return ret;
2377 * pull an array of strings from a ADS result
2378 * (handle large multivalue attributes with range retrieval)
2379 * @param ads connection to ads server
2380 * @param mem_ctx TALLOC_CTX to use for allocating result string
2381 * @param msg Results of search
2382 * @param field Attribute to retrieve
2383 * @param current_strings strings returned by a previous call to this function
2384 * @param next_attribute The next query should ask for this attribute
2385 * @param num_values How many values did we get this time?
2386 * @param more_values Are there more values to get?
2387 * @return Result strings in talloc context
2389 char **ads_pull_strings_range(ADS_STRUCT *ads,
2390 TALLOC_CTX *mem_ctx,
2391 LDAPMessage *msg, const char *field,
2392 char **current_strings,
2393 const char **next_attribute,
2394 size_t *num_strings,
2395 bool *more_strings)
2397 char *attr;
2398 char *expected_range_attrib, *range_attr;
2399 BerElement *ptr = NULL;
2400 char **strings;
2401 char **new_strings;
2402 size_t num_new_strings;
2403 unsigned long int range_start;
2404 unsigned long int range_end;
2406 /* we might have been given the whole lot anyway */
2407 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2408 *more_strings = False;
2409 return strings;
2412 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2414 /* look for Range result */
2415 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2416 attr;
2417 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2418 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2419 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2420 range_attr = attr;
2421 break;
2423 ldap_memfree(attr);
2425 if (!attr) {
2426 ber_free(ptr, 0);
2427 /* nothing here - this field is just empty */
2428 *more_strings = False;
2429 return NULL;
2432 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2433 &range_start, &range_end) == 2) {
2434 *more_strings = True;
2435 } else {
2436 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2437 &range_start) == 1) {
2438 *more_strings = False;
2439 } else {
2440 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2441 range_attr));
2442 ldap_memfree(range_attr);
2443 *more_strings = False;
2444 return NULL;
2448 if ((*num_strings) != range_start) {
2449 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2450 " - aborting range retreival\n",
2451 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2452 ldap_memfree(range_attr);
2453 *more_strings = False;
2454 return NULL;
2457 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2459 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2460 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2461 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2462 range_attr, (unsigned long int)range_end - range_start + 1,
2463 (unsigned long int)num_new_strings));
2464 ldap_memfree(range_attr);
2465 *more_strings = False;
2466 return NULL;
2469 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2470 *num_strings + num_new_strings);
2472 if (strings == NULL) {
2473 ldap_memfree(range_attr);
2474 *more_strings = False;
2475 return NULL;
2478 if (new_strings && num_new_strings) {
2479 memcpy(&strings[*num_strings], new_strings,
2480 sizeof(*new_strings) * num_new_strings);
2483 (*num_strings) += num_new_strings;
2485 if (*more_strings) {
2486 *next_attribute = talloc_asprintf(mem_ctx,
2487 "%s;range=%d-*",
2488 field,
2489 (int)*num_strings);
2491 if (!*next_attribute) {
2492 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2493 ldap_memfree(range_attr);
2494 *more_strings = False;
2495 return NULL;
2499 ldap_memfree(range_attr);
2501 return strings;
2505 * pull a single uint32 from a ADS result
2506 * @param ads connection to ads server
2507 * @param msg Results of search
2508 * @param field Attribute to retrieve
2509 * @param v Pointer to int to store result
2510 * @return boolean inidicating success
2512 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2513 uint32 *v)
2515 char **values;
2517 values = ldap_get_values(ads->ldap.ld, msg, field);
2518 if (!values)
2519 return False;
2520 if (!values[0]) {
2521 ldap_value_free(values);
2522 return False;
2525 *v = atoi(values[0]);
2526 ldap_value_free(values);
2527 return True;
2531 * pull a single objectGUID from an ADS result
2532 * @param ads connection to ADS server
2533 * @param msg results of search
2534 * @param guid 37-byte area to receive text guid
2535 * @return boolean indicating success
2537 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2539 char **values;
2540 UUID_FLAT flat_guid;
2542 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2543 if (!values)
2544 return False;
2546 if (values[0]) {
2547 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2548 smb_uuid_unpack(flat_guid, guid);
2549 ldap_value_free(values);
2550 return True;
2552 ldap_value_free(values);
2553 return False;
2559 * pull a single DOM_SID from a ADS result
2560 * @param ads connection to ads server
2561 * @param msg Results of search
2562 * @param field Attribute to retrieve
2563 * @param sid Pointer to sid to store result
2564 * @return boolean inidicating success
2566 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2567 DOM_SID *sid)
2569 struct berval **values;
2570 bool ret = False;
2572 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2574 if (!values)
2575 return False;
2577 if (values[0])
2578 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2580 ldap_value_free_len(values);
2581 return ret;
2585 * pull an array of DOM_SIDs from a ADS result
2586 * @param ads connection to ads server
2587 * @param mem_ctx TALLOC_CTX for allocating sid array
2588 * @param msg Results of search
2589 * @param field Attribute to retrieve
2590 * @param sids pointer to sid array to allocate
2591 * @return the count of SIDs pulled
2593 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2594 LDAPMessage *msg, const char *field, DOM_SID **sids)
2596 struct berval **values;
2597 bool ret;
2598 int count, i;
2600 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2602 if (!values)
2603 return 0;
2605 for (i=0; values[i]; i++)
2606 /* nop */ ;
2608 if (i) {
2609 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2610 if (!(*sids)) {
2611 ldap_value_free_len(values);
2612 return 0;
2614 } else {
2615 (*sids) = NULL;
2618 count = 0;
2619 for (i=0; values[i]; i++) {
2620 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2621 if (ret) {
2622 DEBUG(10, ("pulling SID: %s\n",
2623 sid_string_dbg(&(*sids)[count])));
2624 count++;
2628 ldap_value_free_len(values);
2629 return count;
2633 * pull a SEC_DESC from a ADS result
2634 * @param ads connection to ads server
2635 * @param mem_ctx TALLOC_CTX for allocating sid array
2636 * @param msg Results of search
2637 * @param field Attribute to retrieve
2638 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2639 * @return boolean inidicating success
2641 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2642 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2644 struct berval **values;
2645 bool ret = true;
2647 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2649 if (!values) return false;
2651 if (values[0]) {
2652 NTSTATUS status;
2653 status = unmarshall_sec_desc(mem_ctx,
2654 (uint8 *)values[0]->bv_val,
2655 values[0]->bv_len, sd);
2656 if (!NT_STATUS_IS_OK(status)) {
2657 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2658 nt_errstr(status)));
2659 ret = false;
2663 ldap_value_free_len(values);
2664 return ret;
2668 * in order to support usernames longer than 21 characters we need to
2669 * use both the sAMAccountName and the userPrincipalName attributes
2670 * It seems that not all users have the userPrincipalName attribute set
2672 * @param ads connection to ads server
2673 * @param mem_ctx TALLOC_CTX for allocating sid array
2674 * @param msg Results of search
2675 * @return the username
2677 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2678 LDAPMessage *msg)
2680 #if 0 /* JERRY */
2681 char *ret, *p;
2683 /* lookup_name() only works on the sAMAccountName to
2684 returning the username portion of userPrincipalName
2685 breaks winbindd_getpwnam() */
2687 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2688 if (ret && (p = strchr_m(ret, '@'))) {
2689 *p = 0;
2690 return ret;
2692 #endif
2693 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2698 * find the update serial number - this is the core of the ldap cache
2699 * @param ads connection to ads server
2700 * @param ads connection to ADS server
2701 * @param usn Pointer to retrieved update serial number
2702 * @return status of search
2704 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2706 const char *attrs[] = {"highestCommittedUSN", NULL};
2707 ADS_STATUS status;
2708 LDAPMessage *res;
2710 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2711 if (!ADS_ERR_OK(status))
2712 return status;
2714 if (ads_count_replies(ads, res) != 1) {
2715 ads_msgfree(ads, res);
2716 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2719 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2720 ads_msgfree(ads, res);
2721 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2724 ads_msgfree(ads, res);
2725 return ADS_SUCCESS;
2728 /* parse a ADS timestring - typical string is
2729 '20020917091222.0Z0' which means 09:12.22 17th September
2730 2002, timezone 0 */
2731 static time_t ads_parse_time(const char *str)
2733 struct tm tm;
2735 ZERO_STRUCT(tm);
2737 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2738 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2739 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2740 return 0;
2742 tm.tm_year -= 1900;
2743 tm.tm_mon -= 1;
2745 return timegm(&tm);
2748 /********************************************************************
2749 ********************************************************************/
2751 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2753 const char *attrs[] = {"currentTime", NULL};
2754 ADS_STATUS status;
2755 LDAPMessage *res;
2756 char *timestr;
2757 TALLOC_CTX *ctx;
2758 ADS_STRUCT *ads_s = ads;
2760 if (!(ctx = talloc_init("ads_current_time"))) {
2761 return ADS_ERROR(LDAP_NO_MEMORY);
2764 /* establish a new ldap tcp session if necessary */
2766 if ( !ads->ldap.ld ) {
2767 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2768 ads->server.ldap_server )) == NULL )
2770 goto done;
2772 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2773 status = ads_connect( ads_s );
2774 if ( !ADS_ERR_OK(status))
2775 goto done;
2778 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2779 if (!ADS_ERR_OK(status)) {
2780 goto done;
2783 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2784 if (!timestr) {
2785 ads_msgfree(ads_s, res);
2786 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2787 goto done;
2790 /* but save the time and offset in the original ADS_STRUCT */
2792 ads->config.current_time = ads_parse_time(timestr);
2794 if (ads->config.current_time != 0) {
2795 ads->auth.time_offset = ads->config.current_time - time(NULL);
2796 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2799 ads_msgfree(ads, res);
2801 status = ADS_SUCCESS;
2803 done:
2804 /* free any temporary ads connections */
2805 if ( ads_s != ads ) {
2806 ads_destroy( &ads_s );
2808 talloc_destroy(ctx);
2810 return status;
2813 /********************************************************************
2814 ********************************************************************/
2816 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2818 const char *attrs[] = {"domainFunctionality", NULL};
2819 ADS_STATUS status;
2820 LDAPMessage *res;
2821 ADS_STRUCT *ads_s = ads;
2823 *val = DS_DOMAIN_FUNCTION_2000;
2825 /* establish a new ldap tcp session if necessary */
2827 if ( !ads->ldap.ld ) {
2828 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2829 ads->server.ldap_server )) == NULL )
2831 goto done;
2833 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2834 status = ads_connect( ads_s );
2835 if ( !ADS_ERR_OK(status))
2836 goto done;
2839 /* If the attribute does not exist assume it is a Windows 2000
2840 functional domain */
2842 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2843 if (!ADS_ERR_OK(status)) {
2844 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2845 status = ADS_SUCCESS;
2847 goto done;
2850 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2851 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2853 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2856 ads_msgfree(ads, res);
2858 done:
2859 /* free any temporary ads connections */
2860 if ( ads_s != ads ) {
2861 ads_destroy( &ads_s );
2864 return status;
2868 * find the domain sid for our domain
2869 * @param ads connection to ads server
2870 * @param sid Pointer to domain sid
2871 * @return status of search
2873 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2875 const char *attrs[] = {"objectSid", NULL};
2876 LDAPMessage *res;
2877 ADS_STATUS rc;
2879 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2880 attrs, &res);
2881 if (!ADS_ERR_OK(rc)) return rc;
2882 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2883 ads_msgfree(ads, res);
2884 return ADS_ERROR_SYSTEM(ENOENT);
2886 ads_msgfree(ads, res);
2888 return ADS_SUCCESS;
2892 * find our site name
2893 * @param ads connection to ads server
2894 * @param mem_ctx Pointer to talloc context
2895 * @param site_name Pointer to the sitename
2896 * @return status of search
2898 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2900 ADS_STATUS status;
2901 LDAPMessage *res;
2902 const char *dn, *service_name;
2903 const char *attrs[] = { "dsServiceName", NULL };
2905 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2906 if (!ADS_ERR_OK(status)) {
2907 return status;
2910 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2911 if (service_name == NULL) {
2912 ads_msgfree(ads, res);
2913 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2916 ads_msgfree(ads, res);
2918 /* go up three levels */
2919 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2920 if (dn == NULL) {
2921 return ADS_ERROR(LDAP_NO_MEMORY);
2924 *site_name = talloc_strdup(mem_ctx, dn);
2925 if (*site_name == NULL) {
2926 return ADS_ERROR(LDAP_NO_MEMORY);
2929 return status;
2931 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2936 * find the site dn where a machine resides
2937 * @param ads connection to ads server
2938 * @param mem_ctx Pointer to talloc context
2939 * @param computer_name name of the machine
2940 * @param site_name Pointer to the sitename
2941 * @return status of search
2943 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2945 ADS_STATUS status;
2946 LDAPMessage *res;
2947 const char *parent, *filter;
2948 char *config_context = NULL;
2949 char *dn;
2951 /* shortcut a query */
2952 if (strequal(computer_name, ads->config.ldap_server_name)) {
2953 return ads_site_dn(ads, mem_ctx, site_dn);
2956 status = ads_config_path(ads, mem_ctx, &config_context);
2957 if (!ADS_ERR_OK(status)) {
2958 return status;
2961 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2962 if (filter == NULL) {
2963 return ADS_ERROR(LDAP_NO_MEMORY);
2966 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2967 filter, NULL, &res);
2968 if (!ADS_ERR_OK(status)) {
2969 return status;
2972 if (ads_count_replies(ads, res) != 1) {
2973 ads_msgfree(ads, res);
2974 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2977 dn = ads_get_dn(ads, res);
2978 if (dn == NULL) {
2979 ads_msgfree(ads, res);
2980 return ADS_ERROR(LDAP_NO_MEMORY);
2983 /* go up three levels */
2984 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2985 if (parent == NULL) {
2986 ads_msgfree(ads, res);
2987 ads_memfree(ads, dn);
2988 return ADS_ERROR(LDAP_NO_MEMORY);
2991 *site_dn = talloc_strdup(mem_ctx, parent);
2992 if (*site_dn == NULL) {
2993 ads_msgfree(ads, res);
2994 ads_memfree(ads, dn);
2995 return ADS_ERROR(LDAP_NO_MEMORY);
2998 ads_memfree(ads, dn);
2999 ads_msgfree(ads, res);
3001 return status;
3005 * get the upn suffixes for a domain
3006 * @param ads connection to ads server
3007 * @param mem_ctx Pointer to talloc context
3008 * @param suffixes Pointer to an array of suffixes
3009 * @param num_suffixes Pointer to the number of suffixes
3010 * @return status of search
3012 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3014 ADS_STATUS status;
3015 LDAPMessage *res;
3016 const char *base;
3017 char *config_context = NULL;
3018 const char *attrs[] = { "uPNSuffixes", NULL };
3020 status = ads_config_path(ads, mem_ctx, &config_context);
3021 if (!ADS_ERR_OK(status)) {
3022 return status;
3025 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3026 if (base == NULL) {
3027 return ADS_ERROR(LDAP_NO_MEMORY);
3030 status = ads_search_dn(ads, &res, base, attrs);
3031 if (!ADS_ERR_OK(status)) {
3032 return status;
3035 if (ads_count_replies(ads, res) != 1) {
3036 ads_msgfree(ads, res);
3037 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3040 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3041 if ((*suffixes) == NULL) {
3042 ads_msgfree(ads, res);
3043 return ADS_ERROR(LDAP_NO_MEMORY);
3046 ads_msgfree(ads, res);
3048 return status;
3052 * get the joinable ous for a domain
3053 * @param ads connection to ads server
3054 * @param mem_ctx Pointer to talloc context
3055 * @param ous Pointer to an array of ous
3056 * @param num_ous Pointer to the number of ous
3057 * @return status of search
3059 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3060 TALLOC_CTX *mem_ctx,
3061 char ***ous,
3062 size_t *num_ous)
3064 ADS_STATUS status;
3065 LDAPMessage *res = NULL;
3066 LDAPMessage *msg = NULL;
3067 const char *attrs[] = { "dn", NULL };
3068 int count = 0;
3070 status = ads_search(ads, &res,
3071 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3072 attrs);
3073 if (!ADS_ERR_OK(status)) {
3074 return status;
3077 count = ads_count_replies(ads, res);
3078 if (count < 1) {
3079 ads_msgfree(ads, res);
3080 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3083 for (msg = ads_first_entry(ads, res); msg;
3084 msg = ads_next_entry(ads, msg)) {
3086 char *dn = NULL;
3088 dn = ads_get_dn(ads, msg);
3089 if (!dn) {
3090 ads_msgfree(ads, res);
3091 return ADS_ERROR(LDAP_NO_MEMORY);
3094 if (!add_string_to_array(mem_ctx, dn,
3095 (const char ***)ous,
3096 (int *)num_ous)) {
3097 ads_memfree(ads, dn);
3098 ads_msgfree(ads, res);
3099 return ADS_ERROR(LDAP_NO_MEMORY);
3102 ads_memfree(ads, dn);
3105 ads_msgfree(ads, res);
3107 return status;
3112 * pull a DOM_SID from an extended dn string
3113 * @param mem_ctx TALLOC_CTX
3114 * @param extended_dn string
3115 * @param flags string type of extended_dn
3116 * @param sid pointer to a DOM_SID
3117 * @return boolean inidicating success
3119 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3120 const char *extended_dn,
3121 enum ads_extended_dn_flags flags,
3122 DOM_SID *sid)
3124 char *p, *q, *dn;
3126 if (!extended_dn) {
3127 return False;
3130 /* otherwise extended_dn gets stripped off */
3131 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3132 return False;
3135 * ADS_EXTENDED_DN_HEX_STRING:
3136 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3138 * ADS_EXTENDED_DN_STRING (only with w2k3):
3139 <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
3142 p = strchr(dn, ';');
3143 if (!p) {
3144 return False;
3147 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3148 return False;
3151 p += strlen(";<SID=");
3153 q = strchr(p, '>');
3154 if (!q) {
3155 return False;
3158 *q = '\0';
3160 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3162 switch (flags) {
3164 case ADS_EXTENDED_DN_STRING:
3165 if (!string_to_sid(sid, p)) {
3166 return False;
3168 break;
3169 case ADS_EXTENDED_DN_HEX_STRING: {
3170 fstring buf;
3171 size_t buf_len;
3173 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3174 if (buf_len == 0) {
3175 return False;
3178 if (!sid_parse(buf, buf_len, sid)) {
3179 DEBUG(10,("failed to parse sid\n"));
3180 return False;
3182 break;
3184 default:
3185 DEBUG(10,("unknown extended dn format\n"));
3186 return False;
3189 return True;
3193 * pull an array of DOM_SIDs from a ADS result
3194 * @param ads connection to ads server
3195 * @param mem_ctx TALLOC_CTX for allocating sid array
3196 * @param msg Results of search
3197 * @param field Attribute to retrieve
3198 * @param flags string type of extended_dn
3199 * @param sids pointer to sid array to allocate
3200 * @return the count of SIDs pulled
3202 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3203 TALLOC_CTX *mem_ctx,
3204 LDAPMessage *msg,
3205 const char *field,
3206 enum ads_extended_dn_flags flags,
3207 DOM_SID **sids)
3209 int i;
3210 size_t dn_count;
3211 char **dn_strings;
3213 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3214 &dn_count)) == NULL) {
3215 return 0;
3218 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3219 if (!(*sids)) {
3220 TALLOC_FREE(dn_strings);
3221 return 0;
3224 for (i=0; i<dn_count; i++) {
3226 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3227 flags, &(*sids)[i])) {
3228 TALLOC_FREE(*sids);
3229 TALLOC_FREE(dn_strings);
3230 return 0;
3234 TALLOC_FREE(dn_strings);
3236 return dn_count;
3239 /********************************************************************
3240 ********************************************************************/
3242 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3244 LDAPMessage *res = NULL;
3245 ADS_STATUS status;
3246 int count = 0;
3247 char *name = NULL;
3249 status = ads_find_machine_acct(ads, &res, global_myname());
3250 if (!ADS_ERR_OK(status)) {
3251 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3252 global_myname()));
3253 goto out;
3256 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3257 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3258 goto out;
3261 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3262 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3265 out:
3266 ads_msgfree(ads, res);
3268 return name;
3271 /********************************************************************
3272 ********************************************************************/
3274 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3276 LDAPMessage *res = NULL;
3277 ADS_STATUS status;
3278 int count = 0;
3279 char *name = NULL;
3281 status = ads_find_machine_acct(ads, &res, machine_name);
3282 if (!ADS_ERR_OK(status)) {
3283 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3284 global_myname()));
3285 goto out;
3288 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3289 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3290 goto out;
3293 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3294 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3297 out:
3298 ads_msgfree(ads, res);
3300 return name;
3303 /********************************************************************
3304 ********************************************************************/
3306 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3308 LDAPMessage *res = NULL;
3309 ADS_STATUS status;
3310 int count = 0;
3311 char *name = NULL;
3313 status = ads_find_machine_acct(ads, &res, global_myname());
3314 if (!ADS_ERR_OK(status)) {
3315 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3316 global_myname()));
3317 goto out;
3320 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3321 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3322 goto out;
3325 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3326 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3329 out:
3330 ads_msgfree(ads, res);
3332 return name;
3335 #if 0
3337 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3340 * Join a machine to a realm
3341 * Creates the machine account and sets the machine password
3342 * @param ads connection to ads server
3343 * @param machine name of host to add
3344 * @param org_unit Organizational unit to place machine in
3345 * @return status of join
3347 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3348 uint32 account_type, const char *org_unit)
3350 ADS_STATUS status;
3351 LDAPMessage *res = NULL;
3352 char *machine;
3354 /* machine name must be lowercase */
3355 machine = SMB_STRDUP(machine_name);
3356 strlower_m(machine);
3359 status = ads_find_machine_acct(ads, (void **)&res, machine);
3360 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3361 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3362 status = ads_leave_realm(ads, machine);
3363 if (!ADS_ERR_OK(status)) {
3364 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3365 machine, ads->config.realm));
3366 return status;
3370 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3371 if (!ADS_ERR_OK(status)) {
3372 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3373 SAFE_FREE(machine);
3374 return status;
3377 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3378 if (!ADS_ERR_OK(status)) {
3379 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3380 SAFE_FREE(machine);
3381 return status;
3384 SAFE_FREE(machine);
3385 ads_msgfree(ads, res);
3387 return status;
3389 #endif
3392 * Delete a machine from the realm
3393 * @param ads connection to ads server
3394 * @param hostname Machine to remove
3395 * @return status of delete
3397 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3399 ADS_STATUS status;
3400 void *msg;
3401 LDAPMessage *res;
3402 char *hostnameDN, *host;
3403 int rc;
3404 LDAPControl ldap_control;
3405 LDAPControl * pldap_control[2] = {NULL, NULL};
3407 pldap_control[0] = &ldap_control;
3408 memset(&ldap_control, 0, sizeof(LDAPControl));
3409 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3411 /* hostname must be lowercase */
3412 host = SMB_STRDUP(hostname);
3413 strlower_m(host);
3415 status = ads_find_machine_acct(ads, &res, host);
3416 if (!ADS_ERR_OK(status)) {
3417 DEBUG(0, ("Host account for %s does not exist.\n", host));
3418 SAFE_FREE(host);
3419 return status;
3422 msg = ads_first_entry(ads, res);
3423 if (!msg) {
3424 SAFE_FREE(host);
3425 return ADS_ERROR_SYSTEM(ENOENT);
3428 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3430 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3431 if (rc) {
3432 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3433 }else {
3434 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3437 if (rc != LDAP_SUCCESS) {
3438 const char *attrs[] = { "cn", NULL };
3439 LDAPMessage *msg_sub;
3441 /* we only search with scope ONE, we do not expect any further
3442 * objects to be created deeper */
3444 status = ads_do_search_retry(ads, hostnameDN,
3445 LDAP_SCOPE_ONELEVEL,
3446 "(objectclass=*)", attrs, &res);
3448 if (!ADS_ERR_OK(status)) {
3449 SAFE_FREE(host);
3450 ads_memfree(ads, hostnameDN);
3451 return status;
3454 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3455 msg_sub = ads_next_entry(ads, msg_sub)) {
3457 char *dn = NULL;
3459 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3460 SAFE_FREE(host);
3461 ads_memfree(ads, hostnameDN);
3462 return ADS_ERROR(LDAP_NO_MEMORY);
3465 status = ads_del_dn(ads, dn);
3466 if (!ADS_ERR_OK(status)) {
3467 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3468 SAFE_FREE(host);
3469 ads_memfree(ads, dn);
3470 ads_memfree(ads, hostnameDN);
3471 return status;
3474 ads_memfree(ads, dn);
3477 /* there should be no subordinate objects anymore */
3478 status = ads_do_search_retry(ads, hostnameDN,
3479 LDAP_SCOPE_ONELEVEL,
3480 "(objectclass=*)", attrs, &res);
3482 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3483 SAFE_FREE(host);
3484 ads_memfree(ads, hostnameDN);
3485 return status;
3488 /* delete hostnameDN now */
3489 status = ads_del_dn(ads, hostnameDN);
3490 if (!ADS_ERR_OK(status)) {
3491 SAFE_FREE(host);
3492 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3493 ads_memfree(ads, hostnameDN);
3494 return status;
3498 ads_memfree(ads, hostnameDN);
3500 status = ads_find_machine_acct(ads, &res, host);
3501 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3502 DEBUG(3, ("Failed to remove host account.\n"));
3503 SAFE_FREE(host);
3504 return status;
3507 SAFE_FREE(host);
3508 return status;
3512 * pull all token-sids from an LDAP dn
3513 * @param ads connection to ads server
3514 * @param mem_ctx TALLOC_CTX for allocating sid array
3515 * @param dn of LDAP object
3516 * @param user_sid pointer to DOM_SID (objectSid)
3517 * @param primary_group_sid pointer to DOM_SID (self composed)
3518 * @param sids pointer to sid array to allocate
3519 * @param num_sids counter of SIDs pulled
3520 * @return status of token query
3522 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3523 TALLOC_CTX *mem_ctx,
3524 const char *dn,
3525 DOM_SID *user_sid,
3526 DOM_SID *primary_group_sid,
3527 DOM_SID **sids,
3528 size_t *num_sids)
3530 ADS_STATUS status;
3531 LDAPMessage *res = NULL;
3532 int count = 0;
3533 size_t tmp_num_sids;
3534 DOM_SID *tmp_sids;
3535 DOM_SID tmp_user_sid;
3536 DOM_SID tmp_primary_group_sid;
3537 uint32 pgid;
3538 const char *attrs[] = {
3539 "objectSid",
3540 "tokenGroups",
3541 "primaryGroupID",
3542 NULL
3545 status = ads_search_retry_dn(ads, &res, dn, attrs);
3546 if (!ADS_ERR_OK(status)) {
3547 return status;
3550 count = ads_count_replies(ads, res);
3551 if (count != 1) {
3552 ads_msgfree(ads, res);
3553 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3556 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3557 ads_msgfree(ads, res);
3558 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3561 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3562 ads_msgfree(ads, res);
3563 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3567 /* hack to compose the primary group sid without knowing the
3568 * domsid */
3570 DOM_SID domsid;
3571 uint32 dummy_rid;
3573 sid_copy(&domsid, &tmp_user_sid);
3575 if (!sid_split_rid(&domsid, &dummy_rid)) {
3576 ads_msgfree(ads, res);
3577 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3580 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3581 ads_msgfree(ads, res);
3582 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3586 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3588 if (tmp_num_sids == 0 || !tmp_sids) {
3589 ads_msgfree(ads, res);
3590 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3593 if (num_sids) {
3594 *num_sids = tmp_num_sids;
3597 if (sids) {
3598 *sids = tmp_sids;
3601 if (user_sid) {
3602 *user_sid = tmp_user_sid;
3605 if (primary_group_sid) {
3606 *primary_group_sid = tmp_primary_group_sid;
3609 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3611 ads_msgfree(ads, res);
3612 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3616 * Find a sAMAccoutName in LDAP
3617 * @param ads connection to ads server
3618 * @param mem_ctx TALLOC_CTX for allocating sid array
3619 * @param samaccountname to search
3620 * @param uac_ret uint32 pointer userAccountControl attribute value
3621 * @param dn_ret pointer to dn
3622 * @return status of token query
3624 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3625 TALLOC_CTX *mem_ctx,
3626 const char *samaccountname,
3627 uint32 *uac_ret,
3628 const char **dn_ret)
3630 ADS_STATUS status;
3631 const char *attrs[] = { "userAccountControl", NULL };
3632 const char *filter;
3633 LDAPMessage *res = NULL;
3634 char *dn = NULL;
3635 uint32 uac = 0;
3637 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3638 samaccountname);
3639 if (filter == NULL) {
3640 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3641 goto out;
3644 status = ads_do_search_all(ads, ads->config.bind_path,
3645 LDAP_SCOPE_SUBTREE,
3646 filter, attrs, &res);
3648 if (!ADS_ERR_OK(status)) {
3649 goto out;
3652 if (ads_count_replies(ads, res) != 1) {
3653 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3654 goto out;
3657 dn = ads_get_dn(ads, res);
3658 if (dn == NULL) {
3659 status = ADS_ERROR(LDAP_NO_MEMORY);
3660 goto out;
3663 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3664 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3665 goto out;
3668 if (uac_ret) {
3669 *uac_ret = uac;
3672 if (dn_ret) {
3673 *dn_ret = talloc_strdup(mem_ctx, dn);
3674 if (!*dn_ret) {
3675 status = ADS_ERROR(LDAP_NO_MEMORY);
3676 goto out;
3679 out:
3680 ads_memfree(ads, dn);
3681 ads_msgfree(ads, res);
3683 return status;
3687 * find our configuration path
3688 * @param ads connection to ads server
3689 * @param mem_ctx Pointer to talloc context
3690 * @param config_path Pointer to the config path
3691 * @return status of search
3693 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3694 TALLOC_CTX *mem_ctx,
3695 char **config_path)
3697 ADS_STATUS status;
3698 LDAPMessage *res = NULL;
3699 const char *config_context = NULL;
3700 const char *attrs[] = { "configurationNamingContext", NULL };
3702 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3703 "(objectclass=*)", attrs, &res);
3704 if (!ADS_ERR_OK(status)) {
3705 return status;
3708 config_context = ads_pull_string(ads, mem_ctx, res,
3709 "configurationNamingContext");
3710 ads_msgfree(ads, res);
3711 if (!config_context) {
3712 return ADS_ERROR(LDAP_NO_MEMORY);
3715 if (config_path) {
3716 *config_path = talloc_strdup(mem_ctx, config_context);
3717 if (!*config_path) {
3718 return ADS_ERROR(LDAP_NO_MEMORY);
3722 return ADS_ERROR(LDAP_SUCCESS);
3726 * find the displayName of an extended right
3727 * @param ads connection to ads server
3728 * @param config_path The config path
3729 * @param mem_ctx Pointer to talloc context
3730 * @param GUID struct of the rightsGUID
3731 * @return status of search
3733 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3734 const char *config_path,
3735 TALLOC_CTX *mem_ctx,
3736 const struct GUID *rights_guid)
3738 ADS_STATUS rc;
3739 LDAPMessage *res = NULL;
3740 char *expr = NULL;
3741 const char *attrs[] = { "displayName", NULL };
3742 const char *result = NULL;
3743 const char *path;
3745 if (!ads || !mem_ctx || !rights_guid) {
3746 goto done;
3749 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3750 smb_uuid_string(mem_ctx, *rights_guid));
3751 if (!expr) {
3752 goto done;
3755 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3756 if (!path) {
3757 goto done;
3760 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3761 expr, attrs, &res);
3762 if (!ADS_ERR_OK(rc)) {
3763 goto done;
3766 if (ads_count_replies(ads, res) != 1) {
3767 goto done;
3770 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3772 done:
3773 ads_msgfree(ads, res);
3774 return result;
3779 * verify or build and verify an account ou
3780 * @param mem_ctx Pointer to talloc context
3781 * @param ads connection to ads server
3782 * @param account_ou
3783 * @return status of search
3786 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3787 ADS_STRUCT *ads,
3788 const char **account_ou)
3790 struct ldb_dn *name_dn = NULL;
3791 const char *name = NULL;
3792 char *ou_string = NULL;
3794 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3795 if (name_dn) {
3796 return ADS_SUCCESS;
3799 ou_string = ads_ou_string(ads, *account_ou);
3800 if (!ou_string) {
3801 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3804 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3805 ads->config.bind_path);
3806 SAFE_FREE(ou_string);
3807 if (!name) {
3808 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3811 name_dn = ldb_dn_explode(mem_ctx, name);
3812 if (!name_dn) {
3813 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3816 *account_ou = talloc_strdup(mem_ctx, name);
3817 if (!*account_ou) {
3818 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3821 return ADS_SUCCESS;
3824 #endif