s3-samr-server: unify callback convention: _samr_QueryDomainInfo.
[Samba.git] / source / libads / ldap.c
blob03d02fc3440d488f4c810355bd6d41d2255ebaa4
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 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2832 goto done;
2834 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2835 status = ads_connect( ads_s );
2836 if ( !ADS_ERR_OK(status))
2837 goto done;
2840 /* If the attribute does not exist assume it is a Windows 2000
2841 functional domain */
2843 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2844 if (!ADS_ERR_OK(status)) {
2845 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2846 status = ADS_SUCCESS;
2848 goto done;
2851 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2852 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2854 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2857 ads_msgfree(ads, res);
2859 done:
2860 /* free any temporary ads connections */
2861 if ( ads_s != ads ) {
2862 ads_destroy( &ads_s );
2865 return status;
2869 * find the domain sid for our domain
2870 * @param ads connection to ads server
2871 * @param sid Pointer to domain sid
2872 * @return status of search
2874 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2876 const char *attrs[] = {"objectSid", NULL};
2877 LDAPMessage *res;
2878 ADS_STATUS rc;
2880 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2881 attrs, &res);
2882 if (!ADS_ERR_OK(rc)) return rc;
2883 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2884 ads_msgfree(ads, res);
2885 return ADS_ERROR_SYSTEM(ENOENT);
2887 ads_msgfree(ads, res);
2889 return ADS_SUCCESS;
2893 * find our site name
2894 * @param ads connection to ads server
2895 * @param mem_ctx Pointer to talloc context
2896 * @param site_name Pointer to the sitename
2897 * @return status of search
2899 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2901 ADS_STATUS status;
2902 LDAPMessage *res;
2903 const char *dn, *service_name;
2904 const char *attrs[] = { "dsServiceName", NULL };
2906 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2907 if (!ADS_ERR_OK(status)) {
2908 return status;
2911 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2912 if (service_name == NULL) {
2913 ads_msgfree(ads, res);
2914 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2917 ads_msgfree(ads, res);
2919 /* go up three levels */
2920 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2921 if (dn == NULL) {
2922 return ADS_ERROR(LDAP_NO_MEMORY);
2925 *site_name = talloc_strdup(mem_ctx, dn);
2926 if (*site_name == NULL) {
2927 return ADS_ERROR(LDAP_NO_MEMORY);
2930 return status;
2932 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2937 * find the site dn where a machine resides
2938 * @param ads connection to ads server
2939 * @param mem_ctx Pointer to talloc context
2940 * @param computer_name name of the machine
2941 * @param site_name Pointer to the sitename
2942 * @return status of search
2944 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2946 ADS_STATUS status;
2947 LDAPMessage *res;
2948 const char *parent, *filter;
2949 char *config_context = NULL;
2950 char *dn;
2952 /* shortcut a query */
2953 if (strequal(computer_name, ads->config.ldap_server_name)) {
2954 return ads_site_dn(ads, mem_ctx, site_dn);
2957 status = ads_config_path(ads, mem_ctx, &config_context);
2958 if (!ADS_ERR_OK(status)) {
2959 return status;
2962 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2963 if (filter == NULL) {
2964 return ADS_ERROR(LDAP_NO_MEMORY);
2967 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2968 filter, NULL, &res);
2969 if (!ADS_ERR_OK(status)) {
2970 return status;
2973 if (ads_count_replies(ads, res) != 1) {
2974 ads_msgfree(ads, res);
2975 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2978 dn = ads_get_dn(ads, res);
2979 if (dn == NULL) {
2980 ads_msgfree(ads, res);
2981 return ADS_ERROR(LDAP_NO_MEMORY);
2984 /* go up three levels */
2985 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2986 if (parent == NULL) {
2987 ads_msgfree(ads, res);
2988 ads_memfree(ads, dn);
2989 return ADS_ERROR(LDAP_NO_MEMORY);
2992 *site_dn = talloc_strdup(mem_ctx, parent);
2993 if (*site_dn == NULL) {
2994 ads_msgfree(ads, res);
2995 ads_memfree(ads, dn);
2996 return ADS_ERROR(LDAP_NO_MEMORY);
2999 ads_memfree(ads, dn);
3000 ads_msgfree(ads, res);
3002 return status;
3006 * get the upn suffixes for a domain
3007 * @param ads connection to ads server
3008 * @param mem_ctx Pointer to talloc context
3009 * @param suffixes Pointer to an array of suffixes
3010 * @param num_suffixes Pointer to the number of suffixes
3011 * @return status of search
3013 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3015 ADS_STATUS status;
3016 LDAPMessage *res;
3017 const char *base;
3018 char *config_context = NULL;
3019 const char *attrs[] = { "uPNSuffixes", NULL };
3021 status = ads_config_path(ads, mem_ctx, &config_context);
3022 if (!ADS_ERR_OK(status)) {
3023 return status;
3026 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3027 if (base == NULL) {
3028 return ADS_ERROR(LDAP_NO_MEMORY);
3031 status = ads_search_dn(ads, &res, base, attrs);
3032 if (!ADS_ERR_OK(status)) {
3033 return status;
3036 if (ads_count_replies(ads, res) != 1) {
3037 ads_msgfree(ads, res);
3038 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3041 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3042 if ((*suffixes) == NULL) {
3043 ads_msgfree(ads, res);
3044 return ADS_ERROR(LDAP_NO_MEMORY);
3047 ads_msgfree(ads, res);
3049 return status;
3053 * get the joinable ous for a domain
3054 * @param ads connection to ads server
3055 * @param mem_ctx Pointer to talloc context
3056 * @param ous Pointer to an array of ous
3057 * @param num_ous Pointer to the number of ous
3058 * @return status of search
3060 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3061 TALLOC_CTX *mem_ctx,
3062 char ***ous,
3063 size_t *num_ous)
3065 ADS_STATUS status;
3066 LDAPMessage *res = NULL;
3067 LDAPMessage *msg = NULL;
3068 const char *attrs[] = { "dn", NULL };
3069 int count = 0;
3071 status = ads_search(ads, &res,
3072 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3073 attrs);
3074 if (!ADS_ERR_OK(status)) {
3075 return status;
3078 count = ads_count_replies(ads, res);
3079 if (count < 1) {
3080 ads_msgfree(ads, res);
3081 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3084 for (msg = ads_first_entry(ads, res); msg;
3085 msg = ads_next_entry(ads, msg)) {
3087 char *dn = NULL;
3089 dn = ads_get_dn(ads, msg);
3090 if (!dn) {
3091 ads_msgfree(ads, res);
3092 return ADS_ERROR(LDAP_NO_MEMORY);
3095 if (!add_string_to_array(mem_ctx, dn,
3096 (const char ***)ous,
3097 (int *)num_ous)) {
3098 ads_memfree(ads, dn);
3099 ads_msgfree(ads, res);
3100 return ADS_ERROR(LDAP_NO_MEMORY);
3103 ads_memfree(ads, dn);
3106 ads_msgfree(ads, res);
3108 return status;
3113 * pull a DOM_SID from an extended dn string
3114 * @param mem_ctx TALLOC_CTX
3115 * @param extended_dn string
3116 * @param flags string type of extended_dn
3117 * @param sid pointer to a DOM_SID
3118 * @return boolean inidicating success
3120 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3121 const char *extended_dn,
3122 enum ads_extended_dn_flags flags,
3123 DOM_SID *sid)
3125 char *p, *q, *dn;
3127 if (!extended_dn) {
3128 return False;
3131 /* otherwise extended_dn gets stripped off */
3132 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3133 return False;
3136 * ADS_EXTENDED_DN_HEX_STRING:
3137 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3139 * ADS_EXTENDED_DN_STRING (only with w2k3):
3140 <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
3143 p = strchr(dn, ';');
3144 if (!p) {
3145 return False;
3148 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3149 return False;
3152 p += strlen(";<SID=");
3154 q = strchr(p, '>');
3155 if (!q) {
3156 return False;
3159 *q = '\0';
3161 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3163 switch (flags) {
3165 case ADS_EXTENDED_DN_STRING:
3166 if (!string_to_sid(sid, p)) {
3167 return False;
3169 break;
3170 case ADS_EXTENDED_DN_HEX_STRING: {
3171 fstring buf;
3172 size_t buf_len;
3174 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3175 if (buf_len == 0) {
3176 return False;
3179 if (!sid_parse(buf, buf_len, sid)) {
3180 DEBUG(10,("failed to parse sid\n"));
3181 return False;
3183 break;
3185 default:
3186 DEBUG(10,("unknown extended dn format\n"));
3187 return False;
3190 return True;
3194 * pull an array of DOM_SIDs from a ADS result
3195 * @param ads connection to ads server
3196 * @param mem_ctx TALLOC_CTX for allocating sid array
3197 * @param msg Results of search
3198 * @param field Attribute to retrieve
3199 * @param flags string type of extended_dn
3200 * @param sids pointer to sid array to allocate
3201 * @return the count of SIDs pulled
3203 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3204 TALLOC_CTX *mem_ctx,
3205 LDAPMessage *msg,
3206 const char *field,
3207 enum ads_extended_dn_flags flags,
3208 DOM_SID **sids)
3210 int i;
3211 size_t dn_count;
3212 char **dn_strings;
3214 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3215 &dn_count)) == NULL) {
3216 return 0;
3219 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3220 if (!(*sids)) {
3221 TALLOC_FREE(dn_strings);
3222 return 0;
3225 for (i=0; i<dn_count; i++) {
3227 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3228 flags, &(*sids)[i])) {
3229 TALLOC_FREE(*sids);
3230 TALLOC_FREE(dn_strings);
3231 return 0;
3235 TALLOC_FREE(dn_strings);
3237 return dn_count;
3240 /********************************************************************
3241 ********************************************************************/
3243 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3245 LDAPMessage *res = NULL;
3246 ADS_STATUS status;
3247 int count = 0;
3248 char *name = NULL;
3250 status = ads_find_machine_acct(ads, &res, global_myname());
3251 if (!ADS_ERR_OK(status)) {
3252 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3253 global_myname()));
3254 goto out;
3257 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3258 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3259 goto out;
3262 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3263 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3266 out:
3267 ads_msgfree(ads, res);
3269 return name;
3272 /********************************************************************
3273 ********************************************************************/
3275 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3277 LDAPMessage *res = NULL;
3278 ADS_STATUS status;
3279 int count = 0;
3280 char *name = NULL;
3282 status = ads_find_machine_acct(ads, &res, machine_name);
3283 if (!ADS_ERR_OK(status)) {
3284 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3285 global_myname()));
3286 goto out;
3289 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3290 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3291 goto out;
3294 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3295 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3298 out:
3299 ads_msgfree(ads, res);
3301 return name;
3304 /********************************************************************
3305 ********************************************************************/
3307 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3309 LDAPMessage *res = NULL;
3310 ADS_STATUS status;
3311 int count = 0;
3312 char *name = NULL;
3314 status = ads_find_machine_acct(ads, &res, global_myname());
3315 if (!ADS_ERR_OK(status)) {
3316 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3317 global_myname()));
3318 goto out;
3321 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3322 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3323 goto out;
3326 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3327 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3330 out:
3331 ads_msgfree(ads, res);
3333 return name;
3336 #if 0
3338 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3341 * Join a machine to a realm
3342 * Creates the machine account and sets the machine password
3343 * @param ads connection to ads server
3344 * @param machine name of host to add
3345 * @param org_unit Organizational unit to place machine in
3346 * @return status of join
3348 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3349 uint32 account_type, const char *org_unit)
3351 ADS_STATUS status;
3352 LDAPMessage *res = NULL;
3353 char *machine;
3355 /* machine name must be lowercase */
3356 machine = SMB_STRDUP(machine_name);
3357 strlower_m(machine);
3360 status = ads_find_machine_acct(ads, (void **)&res, machine);
3361 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3362 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3363 status = ads_leave_realm(ads, machine);
3364 if (!ADS_ERR_OK(status)) {
3365 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3366 machine, ads->config.realm));
3367 return status;
3371 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3372 if (!ADS_ERR_OK(status)) {
3373 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3374 SAFE_FREE(machine);
3375 return status;
3378 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3379 if (!ADS_ERR_OK(status)) {
3380 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3381 SAFE_FREE(machine);
3382 return status;
3385 SAFE_FREE(machine);
3386 ads_msgfree(ads, res);
3388 return status;
3390 #endif
3393 * Delete a machine from the realm
3394 * @param ads connection to ads server
3395 * @param hostname Machine to remove
3396 * @return status of delete
3398 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3400 ADS_STATUS status;
3401 void *msg;
3402 LDAPMessage *res;
3403 char *hostnameDN, *host;
3404 int rc;
3405 LDAPControl ldap_control;
3406 LDAPControl * pldap_control[2] = {NULL, NULL};
3408 pldap_control[0] = &ldap_control;
3409 memset(&ldap_control, 0, sizeof(LDAPControl));
3410 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3412 /* hostname must be lowercase */
3413 host = SMB_STRDUP(hostname);
3414 strlower_m(host);
3416 status = ads_find_machine_acct(ads, &res, host);
3417 if (!ADS_ERR_OK(status)) {
3418 DEBUG(0, ("Host account for %s does not exist.\n", host));
3419 SAFE_FREE(host);
3420 return status;
3423 msg = ads_first_entry(ads, res);
3424 if (!msg) {
3425 SAFE_FREE(host);
3426 return ADS_ERROR_SYSTEM(ENOENT);
3429 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3431 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3432 if (rc) {
3433 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3434 }else {
3435 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3438 if (rc != LDAP_SUCCESS) {
3439 const char *attrs[] = { "cn", NULL };
3440 LDAPMessage *msg_sub;
3442 /* we only search with scope ONE, we do not expect any further
3443 * objects to be created deeper */
3445 status = ads_do_search_retry(ads, hostnameDN,
3446 LDAP_SCOPE_ONELEVEL,
3447 "(objectclass=*)", attrs, &res);
3449 if (!ADS_ERR_OK(status)) {
3450 SAFE_FREE(host);
3451 ads_memfree(ads, hostnameDN);
3452 return status;
3455 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3456 msg_sub = ads_next_entry(ads, msg_sub)) {
3458 char *dn = NULL;
3460 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3461 SAFE_FREE(host);
3462 ads_memfree(ads, hostnameDN);
3463 return ADS_ERROR(LDAP_NO_MEMORY);
3466 status = ads_del_dn(ads, dn);
3467 if (!ADS_ERR_OK(status)) {
3468 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3469 SAFE_FREE(host);
3470 ads_memfree(ads, dn);
3471 ads_memfree(ads, hostnameDN);
3472 return status;
3475 ads_memfree(ads, dn);
3478 /* there should be no subordinate objects anymore */
3479 status = ads_do_search_retry(ads, hostnameDN,
3480 LDAP_SCOPE_ONELEVEL,
3481 "(objectclass=*)", attrs, &res);
3483 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3484 SAFE_FREE(host);
3485 ads_memfree(ads, hostnameDN);
3486 return status;
3489 /* delete hostnameDN now */
3490 status = ads_del_dn(ads, hostnameDN);
3491 if (!ADS_ERR_OK(status)) {
3492 SAFE_FREE(host);
3493 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3494 ads_memfree(ads, hostnameDN);
3495 return status;
3499 ads_memfree(ads, hostnameDN);
3501 status = ads_find_machine_acct(ads, &res, host);
3502 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3503 DEBUG(3, ("Failed to remove host account.\n"));
3504 SAFE_FREE(host);
3505 return status;
3508 SAFE_FREE(host);
3509 return status;
3513 * pull all token-sids from an LDAP dn
3514 * @param ads connection to ads server
3515 * @param mem_ctx TALLOC_CTX for allocating sid array
3516 * @param dn of LDAP object
3517 * @param user_sid pointer to DOM_SID (objectSid)
3518 * @param primary_group_sid pointer to DOM_SID (self composed)
3519 * @param sids pointer to sid array to allocate
3520 * @param num_sids counter of SIDs pulled
3521 * @return status of token query
3523 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3524 TALLOC_CTX *mem_ctx,
3525 const char *dn,
3526 DOM_SID *user_sid,
3527 DOM_SID *primary_group_sid,
3528 DOM_SID **sids,
3529 size_t *num_sids)
3531 ADS_STATUS status;
3532 LDAPMessage *res = NULL;
3533 int count = 0;
3534 size_t tmp_num_sids;
3535 DOM_SID *tmp_sids;
3536 DOM_SID tmp_user_sid;
3537 DOM_SID tmp_primary_group_sid;
3538 uint32 pgid;
3539 const char *attrs[] = {
3540 "objectSid",
3541 "tokenGroups",
3542 "primaryGroupID",
3543 NULL
3546 status = ads_search_retry_dn(ads, &res, dn, attrs);
3547 if (!ADS_ERR_OK(status)) {
3548 return status;
3551 count = ads_count_replies(ads, res);
3552 if (count != 1) {
3553 ads_msgfree(ads, res);
3554 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3557 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3558 ads_msgfree(ads, res);
3559 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3562 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3563 ads_msgfree(ads, res);
3564 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3568 /* hack to compose the primary group sid without knowing the
3569 * domsid */
3571 DOM_SID domsid;
3572 uint32 dummy_rid;
3574 sid_copy(&domsid, &tmp_user_sid);
3576 if (!sid_split_rid(&domsid, &dummy_rid)) {
3577 ads_msgfree(ads, res);
3578 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3581 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3582 ads_msgfree(ads, res);
3583 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3587 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3589 if (tmp_num_sids == 0 || !tmp_sids) {
3590 ads_msgfree(ads, res);
3591 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3594 if (num_sids) {
3595 *num_sids = tmp_num_sids;
3598 if (sids) {
3599 *sids = tmp_sids;
3602 if (user_sid) {
3603 *user_sid = tmp_user_sid;
3606 if (primary_group_sid) {
3607 *primary_group_sid = tmp_primary_group_sid;
3610 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3612 ads_msgfree(ads, res);
3613 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3617 * Find a sAMAccoutName in LDAP
3618 * @param ads connection to ads server
3619 * @param mem_ctx TALLOC_CTX for allocating sid array
3620 * @param samaccountname to search
3621 * @param uac_ret uint32 pointer userAccountControl attribute value
3622 * @param dn_ret pointer to dn
3623 * @return status of token query
3625 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3626 TALLOC_CTX *mem_ctx,
3627 const char *samaccountname,
3628 uint32 *uac_ret,
3629 const char **dn_ret)
3631 ADS_STATUS status;
3632 const char *attrs[] = { "userAccountControl", NULL };
3633 const char *filter;
3634 LDAPMessage *res = NULL;
3635 char *dn = NULL;
3636 uint32 uac = 0;
3638 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3639 samaccountname);
3640 if (filter == NULL) {
3641 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3642 goto out;
3645 status = ads_do_search_all(ads, ads->config.bind_path,
3646 LDAP_SCOPE_SUBTREE,
3647 filter, attrs, &res);
3649 if (!ADS_ERR_OK(status)) {
3650 goto out;
3653 if (ads_count_replies(ads, res) != 1) {
3654 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3655 goto out;
3658 dn = ads_get_dn(ads, res);
3659 if (dn == NULL) {
3660 status = ADS_ERROR(LDAP_NO_MEMORY);
3661 goto out;
3664 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3665 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3666 goto out;
3669 if (uac_ret) {
3670 *uac_ret = uac;
3673 if (dn_ret) {
3674 *dn_ret = talloc_strdup(mem_ctx, dn);
3675 if (!*dn_ret) {
3676 status = ADS_ERROR(LDAP_NO_MEMORY);
3677 goto out;
3680 out:
3681 ads_memfree(ads, dn);
3682 ads_msgfree(ads, res);
3684 return status;
3688 * find our configuration path
3689 * @param ads connection to ads server
3690 * @param mem_ctx Pointer to talloc context
3691 * @param config_path Pointer to the config path
3692 * @return status of search
3694 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3695 TALLOC_CTX *mem_ctx,
3696 char **config_path)
3698 ADS_STATUS status;
3699 LDAPMessage *res = NULL;
3700 const char *config_context = NULL;
3701 const char *attrs[] = { "configurationNamingContext", NULL };
3703 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3704 "(objectclass=*)", attrs, &res);
3705 if (!ADS_ERR_OK(status)) {
3706 return status;
3709 config_context = ads_pull_string(ads, mem_ctx, res,
3710 "configurationNamingContext");
3711 ads_msgfree(ads, res);
3712 if (!config_context) {
3713 return ADS_ERROR(LDAP_NO_MEMORY);
3716 if (config_path) {
3717 *config_path = talloc_strdup(mem_ctx, config_context);
3718 if (!*config_path) {
3719 return ADS_ERROR(LDAP_NO_MEMORY);
3723 return ADS_ERROR(LDAP_SUCCESS);
3727 * find the displayName of an extended right
3728 * @param ads connection to ads server
3729 * @param config_path The config path
3730 * @param mem_ctx Pointer to talloc context
3731 * @param GUID struct of the rightsGUID
3732 * @return status of search
3734 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3735 const char *config_path,
3736 TALLOC_CTX *mem_ctx,
3737 const struct GUID *rights_guid)
3739 ADS_STATUS rc;
3740 LDAPMessage *res = NULL;
3741 char *expr = NULL;
3742 const char *attrs[] = { "displayName", NULL };
3743 const char *result = NULL;
3744 const char *path;
3746 if (!ads || !mem_ctx || !rights_guid) {
3747 goto done;
3750 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3751 smb_uuid_string(mem_ctx, *rights_guid));
3752 if (!expr) {
3753 goto done;
3756 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3757 if (!path) {
3758 goto done;
3761 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3762 expr, attrs, &res);
3763 if (!ADS_ERR_OK(rc)) {
3764 goto done;
3767 if (ads_count_replies(ads, res) != 1) {
3768 goto done;
3771 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3773 done:
3774 ads_msgfree(ads, res);
3775 return result;
3780 * verify or build and verify an account ou
3781 * @param mem_ctx Pointer to talloc context
3782 * @param ads connection to ads server
3783 * @param account_ou
3784 * @return status of search
3787 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3788 ADS_STRUCT *ads,
3789 const char **account_ou)
3791 struct ldb_dn *name_dn = NULL;
3792 const char *name = NULL;
3793 char *ou_string = NULL;
3795 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3796 if (name_dn) {
3797 return ADS_SUCCESS;
3800 ou_string = ads_ou_string(ads, *account_ou);
3801 if (!ou_string) {
3802 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3805 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3806 ads->config.bind_path);
3807 SAFE_FREE(ou_string);
3808 if (!name) {
3809 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3812 name_dn = ldb_dn_explode(mem_ctx, name);
3813 if (!name_dn) {
3814 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3817 *account_ou = talloc_strdup(mem_ctx, name);
3818 if (!*account_ou) {
3819 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3822 return ADS_SUCCESS;
3825 #endif