Remove a pointless CONST_DISCARD
[Samba/gebeck_regimport.git] / source3 / libads / ldap.c
blobb59dab1f1353bb6d510bdd572bfa00d3b5d4a3bb
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;
396 struct nbt_cldap_netlogon_5 cldap_reply;
398 ZERO_STRUCT(cldap_reply);
400 ads = ads_init(lp_realm(), NULL, NULL);
401 if (!ads) {
402 return NT_STATUS_NO_MEMORY;
405 /* The NO_BIND here will find a DC and set the client site
406 but not establish the TCP connection */
408 ads->auth.flags = ADS_AUTH_NO_BIND;
409 ads_status = ads_connect(ads);
410 if (!ADS_ERR_OK(ads_status)) {
411 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
412 ads_errstr(ads_status)));
414 nt_status = ads_ntstatus(ads_status);
416 if (ads) {
417 ads_destroy(&ads);
420 return nt_status;
423 /*********************************************************************
424 *********************************************************************/
426 static const char* host_dns_domain(const char *fqdn)
428 const char *p = fqdn;
430 /* go to next char following '.' */
432 if ((p = strchr_m(fqdn, '.')) != NULL) {
433 p++;
436 return p;
441 * Connect to the Global Catalog server
442 * @param ads Pointer to an existing ADS_STRUCT
443 * @return status of connection
445 * Simple wrapper around ads_connect() that fills in the
446 * GC ldap server information
449 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
451 TALLOC_CTX *frame = talloc_stackframe();
452 struct dns_rr_srv *gcs_list;
453 int num_gcs;
454 char *realm = ads->server.realm;
455 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
456 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
457 int i;
458 bool done = false;
459 char *sitename = NULL;
461 if (!realm)
462 realm = lp_realm();
464 if ((sitename = sitename_fetch(realm)) == NULL) {
465 ads_lookup_site();
466 sitename = sitename_fetch(realm);
469 do {
470 /* We try once with a sitename and once without
471 (unless we don't have a sitename and then we're
472 done */
474 if (sitename == NULL)
475 done = true;
477 nt_status = ads_dns_query_gcs(frame, realm, sitename,
478 &gcs_list, &num_gcs);
480 SAFE_FREE(sitename);
482 if (!NT_STATUS_IS_OK(nt_status)) {
483 ads_status = ADS_ERROR_NT(nt_status);
484 goto done;
487 /* Loop until we get a successful connection or have gone
488 through them all. When connecting a GC server, make sure that
489 the realm is the server's DNS name and not the forest root */
491 for (i=0; i<num_gcs; i++) {
492 ads->server.gc = true;
493 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
494 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
495 ads_status = ads_connect(ads);
496 if (ADS_ERR_OK(ads_status)) {
497 /* Reset the bind_dn to "". A Global Catalog server
498 may host multiple domain trees in a forest.
499 Windows 2003 GC server will accept "" as the search
500 path to imply search all domain trees in the forest */
502 SAFE_FREE(ads->config.bind_path);
503 ads->config.bind_path = SMB_STRDUP("");
506 goto done;
508 SAFE_FREE(ads->server.ldap_server);
509 SAFE_FREE(ads->server.realm);
512 TALLOC_FREE(gcs_list);
513 num_gcs = 0;
514 } while (!done);
516 done:
517 SAFE_FREE(sitename);
518 talloc_destroy(frame);
520 return ads_status;
525 * Connect to the LDAP server
526 * @param ads Pointer to an existing ADS_STRUCT
527 * @return status of connection
529 ADS_STATUS ads_connect(ADS_STRUCT *ads)
531 int version = LDAP_VERSION3;
532 ADS_STATUS status;
533 NTSTATUS ntstatus;
534 char addr[INET6_ADDRSTRLEN];
536 ZERO_STRUCT(ads->ldap);
537 ads->ldap.last_attempt = time(NULL);
538 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
540 /* try with a user specified server */
542 if (DEBUGLEVEL >= 11) {
543 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
544 DEBUG(11,("ads_connect: entering\n"));
545 DEBUGADD(11,("%s\n", s));
546 TALLOC_FREE(s);
549 if (ads->server.ldap_server &&
550 ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
551 goto got_connection;
554 ntstatus = ads_find_dc(ads);
555 if (NT_STATUS_IS_OK(ntstatus)) {
556 goto got_connection;
559 status = ADS_ERROR_NT(ntstatus);
560 goto out;
562 got_connection:
564 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
565 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
567 if (!ads->auth.user_name) {
568 /* Must use the userPrincipalName value here or sAMAccountName
569 and not servicePrincipalName; found by Guenther Deschner */
571 asprintf(&ads->auth.user_name, "%s$", global_myname() );
574 if (!ads->auth.realm) {
575 ads->auth.realm = SMB_STRDUP(ads->config.realm);
578 if (!ads->auth.kdc_server) {
579 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
580 ads->auth.kdc_server = SMB_STRDUP(addr);
583 #if KRB5_DNS_HACK
584 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
585 to MIT kerberos to work (tridge) */
587 char *env;
588 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
589 setenv(env, ads->auth.kdc_server, 1);
590 free(env);
592 #endif
594 /* If the caller() requested no LDAP bind, then we are done */
596 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
597 status = ADS_SUCCESS;
598 goto out;
601 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
602 if (!ads->ldap.mem_ctx) {
603 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
604 goto out;
607 /* Otherwise setup the TCP LDAP session */
609 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
610 ads->ldap.port, lp_ldap_timeout());
611 if (ads->ldap.ld == NULL) {
612 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
613 goto out;
615 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
617 /* cache the successful connection for workgroup and realm */
618 if (ads_closest_dc(ads)) {
619 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
620 saf_store( ads->server.workgroup, addr);
621 saf_store( ads->server.realm, addr);
624 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
626 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
627 if (!ADS_ERR_OK(status)) {
628 goto out;
631 /* fill in the current time and offsets */
633 status = ads_current_time( ads );
634 if ( !ADS_ERR_OK(status) ) {
635 goto out;
638 /* Now do the bind */
640 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
641 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
642 goto out;
645 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
646 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
647 goto out;
650 status = ads_sasl_bind(ads);
652 out:
653 if (DEBUGLEVEL >= 11) {
654 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
655 DEBUG(11,("ads_connect: leaving with: %s\n",
656 ads_errstr(status)));
657 DEBUGADD(11,("%s\n", s));
658 TALLOC_FREE(s);
661 return status;
665 * Connect to the LDAP server using given credentials
666 * @param ads Pointer to an existing ADS_STRUCT
667 * @return status of connection
669 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
671 ads->auth.flags |= ADS_AUTH_USER_CREDS;
673 return ads_connect(ads);
677 * Disconnect the LDAP server
678 * @param ads Pointer to an existing ADS_STRUCT
680 void ads_disconnect(ADS_STRUCT *ads)
682 if (ads->ldap.ld) {
683 ldap_unbind(ads->ldap.ld);
684 ads->ldap.ld = NULL;
686 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
687 ads->ldap.wrap_ops->disconnect(ads);
689 if (ads->ldap.mem_ctx) {
690 talloc_free(ads->ldap.mem_ctx);
692 ZERO_STRUCT(ads->ldap);
696 Duplicate a struct berval into talloc'ed memory
698 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
700 struct berval *value;
702 if (!in_val) return NULL;
704 value = TALLOC_ZERO_P(ctx, struct berval);
705 if (value == NULL)
706 return NULL;
707 if (in_val->bv_len == 0) return value;
709 value->bv_len = in_val->bv_len;
710 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
711 in_val->bv_len);
712 return value;
716 Make a values list out of an array of (struct berval *)
718 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
719 const struct berval **in_vals)
721 struct berval **values;
722 int i;
724 if (!in_vals) return NULL;
725 for (i=0; in_vals[i]; i++)
726 ; /* count values */
727 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
728 if (!values) return NULL;
730 for (i=0; in_vals[i]; i++) {
731 values[i] = dup_berval(ctx, in_vals[i]);
733 return values;
737 UTF8-encode a values list out of an array of (char *)
739 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
741 char **values;
742 int i;
743 size_t size;
745 if (!in_vals) return NULL;
746 for (i=0; in_vals[i]; i++)
747 ; /* count values */
748 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
749 if (!values) return NULL;
751 for (i=0; in_vals[i]; i++) {
752 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
753 TALLOC_FREE(values);
754 return NULL;
757 return values;
761 Pull a (char *) array out of a UTF8-encoded values list
763 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
765 char **values;
766 int i;
767 size_t converted_size;
769 if (!in_vals) return NULL;
770 for (i=0; in_vals[i]; i++)
771 ; /* count values */
772 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
773 if (!values) return NULL;
775 for (i=0; in_vals[i]; i++) {
776 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
777 &converted_size)) {
778 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
779 "%s", strerror(errno)));
782 return values;
786 * Do a search with paged results. cookie must be null on the first
787 * call, and then returned on each subsequent call. It will be null
788 * again when the entire search is complete
789 * @param ads connection to ads server
790 * @param bind_path Base dn for the search
791 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
792 * @param expr Search expression - specified in local charset
793 * @param attrs Attributes to retrieve - specified in utf8 or ascii
794 * @param res ** which will contain results - free res* with ads_msgfree()
795 * @param count Number of entries retrieved on this page
796 * @param cookie The paged results cookie to be returned on subsequent calls
797 * @return status of search
799 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
800 const char *bind_path,
801 int scope, const char *expr,
802 const char **attrs, void *args,
803 LDAPMessage **res,
804 int *count, struct berval **cookie)
806 int rc, i, version;
807 char *utf8_expr, *utf8_path, **search_attrs;
808 size_t converted_size;
809 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
810 BerElement *cookie_be = NULL;
811 struct berval *cookie_bv= NULL;
812 BerElement *ext_be = NULL;
813 struct berval *ext_bv= NULL;
815 TALLOC_CTX *ctx;
816 ads_control *external_control = (ads_control *) args;
818 *res = NULL;
820 if (!(ctx = talloc_init("ads_do_paged_search_args")))
821 return ADS_ERROR(LDAP_NO_MEMORY);
823 /* 0 means the conversion worked but the result was empty
824 so we only fail if it's -1. In any case, it always
825 at least nulls out the dest */
826 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
827 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
829 rc = LDAP_NO_MEMORY;
830 goto done;
833 if (!attrs || !(*attrs))
834 search_attrs = NULL;
835 else {
836 /* This would be the utf8-encoded version...*/
837 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
838 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
839 rc = LDAP_NO_MEMORY;
840 goto done;
844 /* Paged results only available on ldap v3 or later */
845 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
846 if (version < LDAP_VERSION3) {
847 rc = LDAP_NOT_SUPPORTED;
848 goto done;
851 cookie_be = ber_alloc_t(LBER_USE_DER);
852 if (*cookie) {
853 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
854 ber_bvfree(*cookie); /* don't need it from last time */
855 *cookie = NULL;
856 } else {
857 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
859 ber_flatten(cookie_be, &cookie_bv);
860 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
861 PagedResults.ldctl_iscritical = (char) 1;
862 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
863 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
865 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
866 NoReferrals.ldctl_iscritical = (char) 0;
867 NoReferrals.ldctl_value.bv_len = 0;
868 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
870 if (external_control &&
871 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
872 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
874 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
875 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
877 /* win2k does not accept a ldctl_value beeing passed in */
879 if (external_control->val != 0) {
881 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
882 rc = LDAP_NO_MEMORY;
883 goto done;
886 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
887 rc = LDAP_NO_MEMORY;
888 goto done;
890 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
891 rc = LDAP_NO_MEMORY;
892 goto done;
895 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
896 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
898 } else {
899 ExternalCtrl.ldctl_value.bv_len = 0;
900 ExternalCtrl.ldctl_value.bv_val = NULL;
903 controls[0] = &NoReferrals;
904 controls[1] = &PagedResults;
905 controls[2] = &ExternalCtrl;
906 controls[3] = NULL;
908 } else {
909 controls[0] = &NoReferrals;
910 controls[1] = &PagedResults;
911 controls[2] = NULL;
914 /* we need to disable referrals as the openldap libs don't
915 handle them and paged results at the same time. Using them
916 together results in the result record containing the server
917 page control being removed from the result list (tridge/jmcd)
919 leaving this in despite the control that says don't generate
920 referrals, in case the server doesn't support it (jmcd)
922 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
924 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
925 search_attrs, 0, controls,
926 NULL, LDAP_NO_LIMIT,
927 (LDAPMessage **)res);
929 ber_free(cookie_be, 1);
930 ber_bvfree(cookie_bv);
932 if (rc) {
933 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
934 ldap_err2string(rc)));
935 goto done;
938 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
939 NULL, &rcontrols, 0);
941 if (!rcontrols) {
942 goto done;
945 for (i=0; rcontrols[i]; i++) {
946 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
947 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
948 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
949 &cookie_bv);
950 /* the berval is the cookie, but must be freed when
951 it is all done */
952 if (cookie_bv->bv_len) /* still more to do */
953 *cookie=ber_bvdup(cookie_bv);
954 else
955 *cookie=NULL;
956 ber_bvfree(cookie_bv);
957 ber_free(cookie_be, 1);
958 break;
961 ldap_controls_free(rcontrols);
963 done:
964 talloc_destroy(ctx);
966 if (ext_be) {
967 ber_free(ext_be, 1);
970 if (ext_bv) {
971 ber_bvfree(ext_bv);
974 /* if/when we decide to utf8-encode attrs, take out this next line */
975 TALLOC_FREE(search_attrs);
977 return ADS_ERROR(rc);
980 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
981 int scope, const char *expr,
982 const char **attrs, LDAPMessage **res,
983 int *count, struct berval **cookie)
985 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
990 * Get all results for a search. This uses ads_do_paged_search() to return
991 * all entries in a large search.
992 * @param ads connection to ads server
993 * @param bind_path Base dn for the search
994 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
995 * @param expr Search expression
996 * @param attrs Attributes to retrieve
997 * @param res ** which will contain results - free res* with ads_msgfree()
998 * @return status of search
1000 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1001 int scope, const char *expr,
1002 const char **attrs, void *args,
1003 LDAPMessage **res)
1005 struct berval *cookie = NULL;
1006 int count = 0;
1007 ADS_STATUS status;
1009 *res = NULL;
1010 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1011 &count, &cookie);
1013 if (!ADS_ERR_OK(status))
1014 return status;
1016 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1017 while (cookie) {
1018 LDAPMessage *res2 = NULL;
1019 ADS_STATUS status2;
1020 LDAPMessage *msg, *next;
1022 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1023 attrs, args, &res2, &count, &cookie);
1025 if (!ADS_ERR_OK(status2)) break;
1027 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1028 that this works on all ldap libs, but I have only tested with openldap */
1029 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1030 next = ads_next_message(ads, msg);
1031 ldap_add_result_entry((LDAPMessage **)res, msg);
1033 /* note that we do not free res2, as the memory is now
1034 part of the main returned list */
1036 #else
1037 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1038 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1039 #endif
1041 return status;
1044 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1045 int scope, const char *expr,
1046 const char **attrs, LDAPMessage **res)
1048 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1051 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1052 int scope, const char *expr,
1053 const char **attrs, uint32 sd_flags,
1054 LDAPMessage **res)
1056 ads_control args;
1058 args.control = ADS_SD_FLAGS_OID;
1059 args.val = sd_flags;
1060 args.critical = True;
1062 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1067 * Run a function on all results for a search. Uses ads_do_paged_search() and
1068 * runs the function as each page is returned, using ads_process_results()
1069 * @param ads connection to ads server
1070 * @param bind_path Base dn for the search
1071 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1072 * @param expr Search expression - specified in local charset
1073 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1074 * @param fn Function which takes attr name, values list, and data_area
1075 * @param data_area Pointer which is passed to function on each call
1076 * @return status of search
1078 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1079 int scope, const char *expr, const char **attrs,
1080 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1081 void *data_area)
1083 struct berval *cookie = NULL;
1084 int count = 0;
1085 ADS_STATUS status;
1086 LDAPMessage *res;
1088 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1089 &count, &cookie);
1091 if (!ADS_ERR_OK(status)) return status;
1093 ads_process_results(ads, res, fn, data_area);
1094 ads_msgfree(ads, res);
1096 while (cookie) {
1097 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1098 &res, &count, &cookie);
1100 if (!ADS_ERR_OK(status)) break;
1102 ads_process_results(ads, res, fn, data_area);
1103 ads_msgfree(ads, res);
1106 return status;
1110 * Do a search with a timeout.
1111 * @param ads connection to ads server
1112 * @param bind_path Base dn for the search
1113 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1114 * @param expr Search expression
1115 * @param attrs Attributes to retrieve
1116 * @param res ** which will contain results - free res* with ads_msgfree()
1117 * @return status of search
1119 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1120 const char *expr,
1121 const char **attrs, LDAPMessage **res)
1123 int rc;
1124 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1125 size_t converted_size;
1126 TALLOC_CTX *ctx;
1128 *res = NULL;
1129 if (!(ctx = talloc_init("ads_do_search"))) {
1130 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1131 return ADS_ERROR(LDAP_NO_MEMORY);
1134 /* 0 means the conversion worked but the result was empty
1135 so we only fail if it's negative. In any case, it always
1136 at least nulls out the dest */
1137 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1138 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1140 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1141 rc = LDAP_NO_MEMORY;
1142 goto done;
1145 if (!attrs || !(*attrs))
1146 search_attrs = NULL;
1147 else {
1148 /* This would be the utf8-encoded version...*/
1149 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1150 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
1152 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1153 rc = LDAP_NO_MEMORY;
1154 goto done;
1158 /* see the note in ads_do_paged_search - we *must* disable referrals */
1159 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1161 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1162 search_attrs, 0, NULL, NULL,
1163 LDAP_NO_LIMIT,
1164 (LDAPMessage **)res);
1166 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1167 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1168 rc = 0;
1171 done:
1172 talloc_destroy(ctx);
1173 /* if/when we decide to utf8-encode attrs, take out this next line */
1174 TALLOC_FREE(search_attrs);
1175 return ADS_ERROR(rc);
1178 * Do a general ADS search
1179 * @param ads connection to ads server
1180 * @param res ** which will contain results - free res* with ads_msgfree()
1181 * @param expr Search expression
1182 * @param attrs Attributes to retrieve
1183 * @return status of search
1185 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1186 const char *expr, const char **attrs)
1188 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1189 expr, attrs, res);
1193 * Do a search on a specific DistinguishedName
1194 * @param ads connection to ads server
1195 * @param res ** which will contain results - free res* with ads_msgfree()
1196 * @param dn DistinguishName to search
1197 * @param attrs Attributes to retrieve
1198 * @return status of search
1200 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1201 const char *dn, const char **attrs)
1203 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1204 attrs, res);
1208 * Free up memory from a ads_search
1209 * @param ads connection to ads server
1210 * @param msg Search results to free
1212 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1214 if (!msg) return;
1215 ldap_msgfree(msg);
1219 * Free up memory from various ads requests
1220 * @param ads connection to ads server
1221 * @param mem Area to free
1223 void ads_memfree(ADS_STRUCT *ads, void *mem)
1225 SAFE_FREE(mem);
1229 * Get a dn from search results
1230 * @param ads connection to ads server
1231 * @param msg Search result
1232 * @return dn string
1234 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1236 char *utf8_dn, *unix_dn;
1237 size_t converted_size;
1239 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1241 if (!utf8_dn) {
1242 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1243 return NULL;
1246 if (!pull_utf8_allocate(&unix_dn, utf8_dn, &converted_size)) {
1247 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1248 utf8_dn ));
1249 return NULL;
1251 ldap_memfree(utf8_dn);
1252 return unix_dn;
1256 * Get the parent from a dn
1257 * @param dn the dn to return the parent from
1258 * @return parent dn string
1260 char *ads_parent_dn(const char *dn)
1262 char *p;
1264 if (dn == NULL) {
1265 return NULL;
1268 p = strchr(dn, ',');
1270 if (p == NULL) {
1271 return NULL;
1274 return p+1;
1278 * Find a machine account given a hostname
1279 * @param ads connection to ads server
1280 * @param res ** which will contain results - free res* with ads_msgfree()
1281 * @param host Hostname to search for
1282 * @return status of search
1284 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1285 const char *machine)
1287 ADS_STATUS status;
1288 char *expr;
1289 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1291 *res = NULL;
1293 /* the easiest way to find a machine account anywhere in the tree
1294 is to look for hostname$ */
1295 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1296 DEBUG(1, ("asprintf failed!\n"));
1297 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1300 status = ads_search(ads, res, expr, attrs);
1301 SAFE_FREE(expr);
1302 return status;
1306 * Initialize a list of mods to be used in a modify request
1307 * @param ctx An initialized TALLOC_CTX
1308 * @return allocated ADS_MODLIST
1310 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1312 #define ADS_MODLIST_ALLOC_SIZE 10
1313 LDAPMod **mods;
1315 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1316 /* -1 is safety to make sure we don't go over the end.
1317 need to reset it to NULL before doing ldap modify */
1318 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1320 return (ADS_MODLIST)mods;
1325 add an attribute to the list, with values list already constructed
1327 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1328 int mod_op, const char *name,
1329 const void *_invals)
1331 const void **invals = (const void **)_invals;
1332 int curmod;
1333 LDAPMod **modlist = (LDAPMod **) *mods;
1334 struct berval **ber_values = NULL;
1335 char **char_values = NULL;
1337 if (!invals) {
1338 mod_op = LDAP_MOD_DELETE;
1339 } else {
1340 if (mod_op & LDAP_MOD_BVALUES)
1341 ber_values = ads_dup_values(ctx,
1342 (const struct berval **)invals);
1343 else
1344 char_values = ads_push_strvals(ctx,
1345 (const char **) invals);
1348 /* find the first empty slot */
1349 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1350 curmod++);
1351 if (modlist[curmod] == (LDAPMod *) -1) {
1352 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1353 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1354 return ADS_ERROR(LDAP_NO_MEMORY);
1355 memset(&modlist[curmod], 0,
1356 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1357 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1358 *mods = (ADS_MODLIST)modlist;
1361 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1362 return ADS_ERROR(LDAP_NO_MEMORY);
1363 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1364 if (mod_op & LDAP_MOD_BVALUES) {
1365 modlist[curmod]->mod_bvalues = ber_values;
1366 } else if (mod_op & LDAP_MOD_DELETE) {
1367 modlist[curmod]->mod_values = NULL;
1368 } else {
1369 modlist[curmod]->mod_values = char_values;
1372 modlist[curmod]->mod_op = mod_op;
1373 return ADS_ERROR(LDAP_SUCCESS);
1377 * Add a single string value to a mod list
1378 * @param ctx An initialized TALLOC_CTX
1379 * @param mods An initialized ADS_MODLIST
1380 * @param name The attribute name to add
1381 * @param val The value to add - NULL means DELETE
1382 * @return ADS STATUS indicating success of add
1384 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1385 const char *name, const char *val)
1387 const char *values[2];
1389 values[0] = val;
1390 values[1] = NULL;
1392 if (!val)
1393 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1394 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1398 * Add an array of string values to a mod list
1399 * @param ctx An initialized TALLOC_CTX
1400 * @param mods An initialized ADS_MODLIST
1401 * @param name The attribute name to add
1402 * @param vals The array of string values to add - NULL means DELETE
1403 * @return ADS STATUS indicating success of add
1405 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1406 const char *name, const char **vals)
1408 if (!vals)
1409 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1410 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1411 name, (const void **) vals);
1414 #if 0
1416 * Add a single ber-encoded value to a mod list
1417 * @param ctx An initialized TALLOC_CTX
1418 * @param mods An initialized ADS_MODLIST
1419 * @param name The attribute name to add
1420 * @param val The value to add - NULL means DELETE
1421 * @return ADS STATUS indicating success of add
1423 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1424 const char *name, const struct berval *val)
1426 const struct berval *values[2];
1428 values[0] = val;
1429 values[1] = NULL;
1430 if (!val)
1431 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1432 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1433 name, (const void **) values);
1435 #endif
1438 * Perform an ldap modify
1439 * @param ads connection to ads server
1440 * @param mod_dn DistinguishedName to modify
1441 * @param mods list of modifications to perform
1442 * @return status of modify
1444 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1446 int ret,i;
1447 char *utf8_dn = NULL;
1448 size_t converted_size;
1450 this control is needed to modify that contains a currently
1451 non-existent attribute (but allowable for the object) to run
1453 LDAPControl PermitModify = {
1454 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1455 {0, NULL},
1456 (char) 1};
1457 LDAPControl *controls[2];
1459 controls[0] = &PermitModify;
1460 controls[1] = NULL;
1462 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1463 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1466 /* find the end of the list, marked by NULL or -1 */
1467 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1468 /* make sure the end of the list is NULL */
1469 mods[i] = NULL;
1470 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1471 (LDAPMod **) mods, controls, NULL);
1472 SAFE_FREE(utf8_dn);
1473 return ADS_ERROR(ret);
1477 * Perform an ldap add
1478 * @param ads connection to ads server
1479 * @param new_dn DistinguishedName to add
1480 * @param mods list of attributes and values for DN
1481 * @return status of add
1483 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1485 int ret, i;
1486 char *utf8_dn = NULL;
1487 size_t converted_size;
1489 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1490 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1491 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1494 /* find the end of the list, marked by NULL or -1 */
1495 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1496 /* make sure the end of the list is NULL */
1497 mods[i] = NULL;
1499 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1500 SAFE_FREE(utf8_dn);
1501 return ADS_ERROR(ret);
1505 * Delete a DistinguishedName
1506 * @param ads connection to ads server
1507 * @param new_dn DistinguishedName to delete
1508 * @return status of delete
1510 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1512 int ret;
1513 char *utf8_dn = NULL;
1514 size_t converted_size;
1515 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1516 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1517 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1520 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1521 SAFE_FREE(utf8_dn);
1522 return ADS_ERROR(ret);
1526 * Build an org unit string
1527 * if org unit is Computers or blank then assume a container, otherwise
1528 * assume a / separated list of organisational units.
1529 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1530 * @param ads connection to ads server
1531 * @param org_unit Organizational unit
1532 * @return org unit string - caller must free
1534 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1536 char *ret = NULL;
1538 if (!org_unit || !*org_unit) {
1540 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1542 /* samba4 might not yet respond to a wellknownobject-query */
1543 return ret ? ret : SMB_STRDUP("cn=Computers");
1546 if (strequal(org_unit, "Computers")) {
1547 return SMB_STRDUP("cn=Computers");
1550 /* jmcd: removed "\\" from the separation chars, because it is
1551 needed as an escape for chars like '#' which are valid in an
1552 OU name */
1553 return ads_build_path(org_unit, "/", "ou=", 1);
1557 * Get a org unit string for a well-known GUID
1558 * @param ads connection to ads server
1559 * @param wknguid Well known GUID
1560 * @return org unit string - caller must free
1562 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1564 ADS_STATUS status;
1565 LDAPMessage *res = NULL;
1566 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1567 **bind_dn_exp = NULL;
1568 const char *attrs[] = {"distinguishedName", NULL};
1569 int new_ln, wkn_ln, bind_ln, i;
1571 if (wknguid == NULL) {
1572 return NULL;
1575 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1576 DEBUG(1, ("asprintf failed!\n"));
1577 return NULL;
1580 status = ads_search_dn(ads, &res, base, attrs);
1581 if (!ADS_ERR_OK(status)) {
1582 DEBUG(1,("Failed while searching for: %s\n", base));
1583 goto out;
1586 if (ads_count_replies(ads, res) != 1) {
1587 goto out;
1590 /* substitute the bind-path from the well-known-guid-search result */
1591 wkn_dn = ads_get_dn(ads, res);
1592 if (!wkn_dn) {
1593 goto out;
1596 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1597 if (!wkn_dn_exp) {
1598 goto out;
1601 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1602 if (!bind_dn_exp) {
1603 goto out;
1606 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1608 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1611 new_ln = wkn_ln - bind_ln;
1613 ret = SMB_STRDUP(wkn_dn_exp[0]);
1614 if (!ret) {
1615 goto out;
1618 for (i=1; i < new_ln; i++) {
1619 char *s = NULL;
1621 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1622 SAFE_FREE(ret);
1623 goto out;
1626 SAFE_FREE(ret);
1627 ret = SMB_STRDUP(s);
1628 free(s);
1629 if (!ret) {
1630 goto out;
1634 out:
1635 SAFE_FREE(base);
1636 ads_msgfree(ads, res);
1637 ads_memfree(ads, wkn_dn);
1638 if (wkn_dn_exp) {
1639 ldap_value_free(wkn_dn_exp);
1641 if (bind_dn_exp) {
1642 ldap_value_free(bind_dn_exp);
1645 return ret;
1649 * Adds (appends) an item to an attribute array, rather then
1650 * replacing the whole list
1651 * @param ctx An initialized TALLOC_CTX
1652 * @param mods An initialized ADS_MODLIST
1653 * @param name name of the ldap attribute to append to
1654 * @param vals an array of values to add
1655 * @return status of addition
1658 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1659 const char *name, const char **vals)
1661 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1662 (const void *) vals);
1666 * Determines the an account's current KVNO via an LDAP lookup
1667 * @param ads An initialized ADS_STRUCT
1668 * @param account_name the NT samaccountname.
1669 * @return the kvno for the account, or -1 in case of a failure.
1672 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1674 LDAPMessage *res = NULL;
1675 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1676 char *filter;
1677 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1678 char *dn_string = NULL;
1679 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1681 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1682 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1683 return kvno;
1685 ret = ads_search(ads, &res, filter, attrs);
1686 SAFE_FREE(filter);
1687 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1688 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1689 ads_msgfree(ads, res);
1690 return kvno;
1693 dn_string = ads_get_dn(ads, res);
1694 if (!dn_string) {
1695 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1696 ads_msgfree(ads, res);
1697 return kvno;
1699 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1700 ads_memfree(ads, dn_string);
1702 /* ---------------------------------------------------------
1703 * 0 is returned as a default KVNO from this point on...
1704 * This is done because Windows 2000 does not support key
1705 * version numbers. Chances are that a failure in the next
1706 * step is simply due to Windows 2000 being used for a
1707 * domain controller. */
1708 kvno = 0;
1710 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1711 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1712 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1713 ads_msgfree(ads, res);
1714 return kvno;
1717 /* Success */
1718 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1719 ads_msgfree(ads, res);
1720 return kvno;
1724 * Determines the computer account's current KVNO via an LDAP lookup
1725 * @param ads An initialized ADS_STRUCT
1726 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1727 * @return the kvno for the computer account, or -1 in case of a failure.
1730 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1732 char *computer_account = NULL;
1733 uint32_t kvno = -1;
1735 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1736 return kvno;
1739 kvno = ads_get_kvno(ads, computer_account);
1740 free(computer_account);
1742 return kvno;
1746 * This clears out all registered spn's for a given hostname
1747 * @param ads An initilaized ADS_STRUCT
1748 * @param machine_name the NetBIOS name of the computer.
1749 * @return 0 upon success, non-zero otherwise.
1752 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1754 TALLOC_CTX *ctx;
1755 LDAPMessage *res = NULL;
1756 ADS_MODLIST mods;
1757 const char *servicePrincipalName[1] = {NULL};
1758 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1759 char *dn_string = NULL;
1761 ret = ads_find_machine_acct(ads, &res, machine_name);
1762 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1763 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1764 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1765 ads_msgfree(ads, res);
1766 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1769 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1770 ctx = talloc_init("ads_clear_service_principal_names");
1771 if (!ctx) {
1772 ads_msgfree(ads, res);
1773 return ADS_ERROR(LDAP_NO_MEMORY);
1776 if (!(mods = ads_init_mods(ctx))) {
1777 talloc_destroy(ctx);
1778 ads_msgfree(ads, res);
1779 return ADS_ERROR(LDAP_NO_MEMORY);
1781 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1782 if (!ADS_ERR_OK(ret)) {
1783 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1784 ads_msgfree(ads, res);
1785 talloc_destroy(ctx);
1786 return ret;
1788 dn_string = ads_get_dn(ads, res);
1789 if (!dn_string) {
1790 talloc_destroy(ctx);
1791 ads_msgfree(ads, res);
1792 return ADS_ERROR(LDAP_NO_MEMORY);
1794 ret = ads_gen_mod(ads, dn_string, mods);
1795 ads_memfree(ads,dn_string);
1796 if (!ADS_ERR_OK(ret)) {
1797 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1798 machine_name));
1799 ads_msgfree(ads, res);
1800 talloc_destroy(ctx);
1801 return ret;
1804 ads_msgfree(ads, res);
1805 talloc_destroy(ctx);
1806 return ret;
1810 * This adds a service principal name to an existing computer account
1811 * (found by hostname) in AD.
1812 * @param ads An initialized ADS_STRUCT
1813 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1814 * @param my_fqdn The fully qualified DNS name of the machine
1815 * @param spn A string of the service principal to add, i.e. 'host'
1816 * @return 0 upon sucess, or non-zero if a failure occurs
1819 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1820 const char *my_fqdn, const char *spn)
1822 ADS_STATUS ret;
1823 TALLOC_CTX *ctx;
1824 LDAPMessage *res = NULL;
1825 char *psp1, *psp2;
1826 ADS_MODLIST mods;
1827 char *dn_string = NULL;
1828 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1830 ret = ads_find_machine_acct(ads, &res, machine_name);
1831 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1832 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1833 machine_name));
1834 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1835 spn, machine_name, ads->config.realm));
1836 ads_msgfree(ads, res);
1837 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1840 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1841 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1842 ads_msgfree(ads, res);
1843 return ADS_ERROR(LDAP_NO_MEMORY);
1846 /* add short name spn */
1848 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1849 talloc_destroy(ctx);
1850 ads_msgfree(ads, res);
1851 return ADS_ERROR(LDAP_NO_MEMORY);
1853 strupper_m(psp1);
1854 strlower_m(&psp1[strlen(spn)]);
1855 servicePrincipalName[0] = psp1;
1857 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1858 psp1, machine_name));
1861 /* add fully qualified spn */
1863 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1864 ret = ADS_ERROR(LDAP_NO_MEMORY);
1865 goto out;
1867 strupper_m(psp2);
1868 strlower_m(&psp2[strlen(spn)]);
1869 servicePrincipalName[1] = psp2;
1871 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1872 psp2, machine_name));
1874 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1875 ret = ADS_ERROR(LDAP_NO_MEMORY);
1876 goto out;
1879 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1880 if (!ADS_ERR_OK(ret)) {
1881 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1882 goto out;
1885 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1886 ret = ADS_ERROR(LDAP_NO_MEMORY);
1887 goto out;
1890 ret = ads_gen_mod(ads, dn_string, mods);
1891 ads_memfree(ads,dn_string);
1892 if (!ADS_ERR_OK(ret)) {
1893 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1894 goto out;
1897 out:
1898 TALLOC_FREE( ctx );
1899 ads_msgfree(ads, res);
1900 return ret;
1904 * adds a machine account to the ADS server
1905 * @param ads An intialized ADS_STRUCT
1906 * @param machine_name - the NetBIOS machine name of this account.
1907 * @param account_type A number indicating the type of account to create
1908 * @param org_unit The LDAP path in which to place this account
1909 * @return 0 upon success, or non-zero otherwise
1912 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1913 const char *org_unit)
1915 ADS_STATUS ret;
1916 char *samAccountName, *controlstr;
1917 TALLOC_CTX *ctx;
1918 ADS_MODLIST mods;
1919 char *machine_escaped = NULL;
1920 char *new_dn;
1921 const char *objectClass[] = {"top", "person", "organizationalPerson",
1922 "user", "computer", NULL};
1923 LDAPMessage *res = NULL;
1924 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1925 UF_DONT_EXPIRE_PASSWD |\
1926 UF_ACCOUNTDISABLE );
1928 if (!(ctx = talloc_init("ads_add_machine_acct")))
1929 return ADS_ERROR(LDAP_NO_MEMORY);
1931 ret = ADS_ERROR(LDAP_NO_MEMORY);
1933 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1934 if (!machine_escaped) {
1935 goto done;
1938 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1939 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1941 if ( !new_dn || !samAccountName ) {
1942 goto done;
1945 #ifndef ENCTYPE_ARCFOUR_HMAC
1946 acct_control |= UF_USE_DES_KEY_ONLY;
1947 #endif
1949 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1950 goto done;
1953 if (!(mods = ads_init_mods(ctx))) {
1954 goto done;
1957 ads_mod_str(ctx, &mods, "cn", machine_name);
1958 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1959 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1960 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1962 ret = ads_gen_add(ads, new_dn, mods);
1964 done:
1965 SAFE_FREE(machine_escaped);
1966 ads_msgfree(ads, res);
1967 talloc_destroy(ctx);
1969 return ret;
1973 * move a machine account to another OU on the ADS server
1974 * @param ads - An intialized ADS_STRUCT
1975 * @param machine_name - the NetBIOS machine name of this account.
1976 * @param org_unit - The LDAP path in which to place this account
1977 * @param moved - whether we moved the machine account (optional)
1978 * @return 0 upon success, or non-zero otherwise
1981 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1982 const char *org_unit, bool *moved)
1984 ADS_STATUS rc;
1985 int ldap_status;
1986 LDAPMessage *res = NULL;
1987 char *filter = NULL;
1988 char *computer_dn = NULL;
1989 char *parent_dn;
1990 char *computer_rdn = NULL;
1991 bool need_move = False;
1993 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
1994 rc = ADS_ERROR(LDAP_NO_MEMORY);
1995 goto done;
1998 /* Find pre-existing machine */
1999 rc = ads_search(ads, &res, filter, NULL);
2000 if (!ADS_ERR_OK(rc)) {
2001 goto done;
2004 computer_dn = ads_get_dn(ads, res);
2005 if (!computer_dn) {
2006 rc = ADS_ERROR(LDAP_NO_MEMORY);
2007 goto done;
2010 parent_dn = ads_parent_dn(computer_dn);
2011 if (strequal(parent_dn, org_unit)) {
2012 goto done;
2015 need_move = True;
2017 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2018 rc = ADS_ERROR(LDAP_NO_MEMORY);
2019 goto done;
2022 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2023 org_unit, 1, NULL, NULL);
2024 rc = ADS_ERROR(ldap_status);
2026 done:
2027 ads_msgfree(ads, res);
2028 SAFE_FREE(filter);
2029 SAFE_FREE(computer_dn);
2030 SAFE_FREE(computer_rdn);
2032 if (!ADS_ERR_OK(rc)) {
2033 need_move = False;
2036 if (moved) {
2037 *moved = need_move;
2040 return rc;
2044 dump a binary result from ldap
2046 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2048 int i, j;
2049 for (i=0; values[i]; i++) {
2050 printf("%s: ", field);
2051 for (j=0; j<values[i]->bv_len; j++) {
2052 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2054 printf("\n");
2058 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2060 int i;
2061 for (i=0; values[i]; i++) {
2063 UUID_FLAT guid;
2064 struct GUID tmp;
2066 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2067 smb_uuid_unpack(guid, &tmp);
2068 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
2073 dump a sid result from ldap
2075 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2077 int i;
2078 for (i=0; values[i]; i++) {
2079 DOM_SID sid;
2080 fstring tmp;
2081 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2082 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2087 dump ntSecurityDescriptor
2089 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2091 TALLOC_CTX *frame = talloc_stackframe();
2092 struct security_descriptor *psd;
2093 NTSTATUS status;
2095 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2096 values[0]->bv_len, &psd);
2097 if (!NT_STATUS_IS_OK(status)) {
2098 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2099 nt_errstr(status)));
2100 TALLOC_FREE(frame);
2101 return;
2104 if (psd) {
2105 ads_disp_sd(ads, talloc_tos(), psd);
2108 TALLOC_FREE(frame);
2112 dump a string result from ldap
2114 static void dump_string(const char *field, char **values)
2116 int i;
2117 for (i=0; values[i]; i++) {
2118 printf("%s: %s\n", field, values[i]);
2123 dump a field from LDAP on stdout
2124 used for debugging
2127 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2129 const struct {
2130 const char *name;
2131 bool string;
2132 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2133 } handlers[] = {
2134 {"objectGUID", False, dump_guid},
2135 {"netbootGUID", False, dump_guid},
2136 {"nTSecurityDescriptor", False, dump_sd},
2137 {"dnsRecord", False, dump_binary},
2138 {"objectSid", False, dump_sid},
2139 {"tokenGroups", False, dump_sid},
2140 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2141 {"tokengroupsGlobalandUniversal", False, dump_sid},
2142 {"mS-DS-CreatorSID", False, dump_sid},
2143 {"msExchMailboxGuid", False, dump_guid},
2144 {NULL, True, NULL}
2146 int i;
2148 if (!field) { /* must be end of an entry */
2149 printf("\n");
2150 return False;
2153 for (i=0; handlers[i].name; i++) {
2154 if (StrCaseCmp(handlers[i].name, field) == 0) {
2155 if (!values) /* first time, indicate string or not */
2156 return handlers[i].string;
2157 handlers[i].handler(ads, field, (struct berval **) values);
2158 break;
2161 if (!handlers[i].name) {
2162 if (!values) /* first time, indicate string conversion */
2163 return True;
2164 dump_string(field, (char **)values);
2166 return False;
2170 * Dump a result from LDAP on stdout
2171 * used for debugging
2172 * @param ads connection to ads server
2173 * @param res Results to dump
2176 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2178 ads_process_results(ads, res, ads_dump_field, NULL);
2182 * Walk through results, calling a function for each entry found.
2183 * The function receives a field name, a berval * array of values,
2184 * and a data area passed through from the start. The function is
2185 * called once with null for field and values at the end of each
2186 * entry.
2187 * @param ads connection to ads server
2188 * @param res Results to process
2189 * @param fn Function for processing each result
2190 * @param data_area user-defined area to pass to function
2192 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2193 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2194 void *data_area)
2196 LDAPMessage *msg;
2197 TALLOC_CTX *ctx;
2198 size_t converted_size;
2200 if (!(ctx = talloc_init("ads_process_results")))
2201 return;
2203 for (msg = ads_first_entry(ads, res); msg;
2204 msg = ads_next_entry(ads, msg)) {
2205 char *utf8_field;
2206 BerElement *b;
2208 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2209 (LDAPMessage *)msg,&b);
2210 utf8_field;
2211 utf8_field=ldap_next_attribute(ads->ldap.ld,
2212 (LDAPMessage *)msg,b)) {
2213 struct berval **ber_vals;
2214 char **str_vals, **utf8_vals;
2215 char *field;
2216 bool string;
2218 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2219 &converted_size))
2221 DEBUG(0,("ads_process_results: "
2222 "pull_utf8_talloc failed: %s",
2223 strerror(errno)));
2226 string = fn(ads, field, NULL, data_area);
2228 if (string) {
2229 utf8_vals = ldap_get_values(ads->ldap.ld,
2230 (LDAPMessage *)msg, field);
2231 str_vals = ads_pull_strvals(ctx,
2232 (const char **) utf8_vals);
2233 fn(ads, field, (void **) str_vals, data_area);
2234 ldap_value_free(utf8_vals);
2235 } else {
2236 ber_vals = ldap_get_values_len(ads->ldap.ld,
2237 (LDAPMessage *)msg, field);
2238 fn(ads, field, (void **) ber_vals, data_area);
2240 ldap_value_free_len(ber_vals);
2242 ldap_memfree(utf8_field);
2244 ber_free(b, 0);
2245 talloc_free_children(ctx);
2246 fn(ads, NULL, NULL, data_area); /* completed an entry */
2249 talloc_destroy(ctx);
2253 * count how many replies are in a LDAPMessage
2254 * @param ads connection to ads server
2255 * @param res Results to count
2256 * @return number of replies
2258 int ads_count_replies(ADS_STRUCT *ads, void *res)
2260 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2264 * pull the first entry from a ADS result
2265 * @param ads connection to ads server
2266 * @param res Results of search
2267 * @return first entry from result
2269 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2271 return ldap_first_entry(ads->ldap.ld, res);
2275 * pull the next entry from a ADS result
2276 * @param ads connection to ads server
2277 * @param res Results of search
2278 * @return next entry from result
2280 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2282 return ldap_next_entry(ads->ldap.ld, res);
2286 * pull the first message from a ADS result
2287 * @param ads connection to ads server
2288 * @param res Results of search
2289 * @return first message from result
2291 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2293 return ldap_first_message(ads->ldap.ld, res);
2297 * pull the next message from a ADS result
2298 * @param ads connection to ads server
2299 * @param res Results of search
2300 * @return next message from result
2302 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2304 return ldap_next_message(ads->ldap.ld, res);
2308 * pull a single string from a ADS result
2309 * @param ads connection to ads server
2310 * @param mem_ctx TALLOC_CTX to use for allocating result string
2311 * @param msg Results of search
2312 * @param field Attribute to retrieve
2313 * @return Result string in talloc context
2315 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2316 const char *field)
2318 char **values;
2319 char *ret = NULL;
2320 char *ux_string;
2321 size_t converted_size;
2323 values = ldap_get_values(ads->ldap.ld, msg, field);
2324 if (!values)
2325 return NULL;
2327 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2328 &converted_size))
2330 ret = ux_string;
2332 ldap_value_free(values);
2333 return ret;
2337 * pull an array of strings from a ADS result
2338 * @param ads connection to ads server
2339 * @param mem_ctx TALLOC_CTX to use for allocating result string
2340 * @param msg Results of search
2341 * @param field Attribute to retrieve
2342 * @return Result strings in talloc context
2344 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2345 LDAPMessage *msg, const char *field,
2346 size_t *num_values)
2348 char **values;
2349 char **ret = NULL;
2350 int i;
2351 size_t converted_size;
2353 values = ldap_get_values(ads->ldap.ld, msg, field);
2354 if (!values)
2355 return NULL;
2357 *num_values = ldap_count_values(values);
2359 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2360 if (!ret) {
2361 ldap_value_free(values);
2362 return NULL;
2365 for (i=0;i<*num_values;i++) {
2366 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2367 &converted_size))
2369 ldap_value_free(values);
2370 return NULL;
2373 ret[i] = NULL;
2375 ldap_value_free(values);
2376 return ret;
2380 * pull an array of strings from a ADS result
2381 * (handle large multivalue attributes with range retrieval)
2382 * @param ads connection to ads server
2383 * @param mem_ctx TALLOC_CTX to use for allocating result string
2384 * @param msg Results of search
2385 * @param field Attribute to retrieve
2386 * @param current_strings strings returned by a previous call to this function
2387 * @param next_attribute The next query should ask for this attribute
2388 * @param num_values How many values did we get this time?
2389 * @param more_values Are there more values to get?
2390 * @return Result strings in talloc context
2392 char **ads_pull_strings_range(ADS_STRUCT *ads,
2393 TALLOC_CTX *mem_ctx,
2394 LDAPMessage *msg, const char *field,
2395 char **current_strings,
2396 const char **next_attribute,
2397 size_t *num_strings,
2398 bool *more_strings)
2400 char *attr;
2401 char *expected_range_attrib, *range_attr;
2402 BerElement *ptr = NULL;
2403 char **strings;
2404 char **new_strings;
2405 size_t num_new_strings;
2406 unsigned long int range_start;
2407 unsigned long int range_end;
2409 /* we might have been given the whole lot anyway */
2410 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2411 *more_strings = False;
2412 return strings;
2415 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2417 /* look for Range result */
2418 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2419 attr;
2420 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2421 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2422 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2423 range_attr = attr;
2424 break;
2426 ldap_memfree(attr);
2428 if (!attr) {
2429 ber_free(ptr, 0);
2430 /* nothing here - this field is just empty */
2431 *more_strings = False;
2432 return NULL;
2435 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2436 &range_start, &range_end) == 2) {
2437 *more_strings = True;
2438 } else {
2439 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2440 &range_start) == 1) {
2441 *more_strings = False;
2442 } else {
2443 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2444 range_attr));
2445 ldap_memfree(range_attr);
2446 *more_strings = False;
2447 return NULL;
2451 if ((*num_strings) != range_start) {
2452 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2453 " - aborting range retreival\n",
2454 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2455 ldap_memfree(range_attr);
2456 *more_strings = False;
2457 return NULL;
2460 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2462 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2463 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2464 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2465 range_attr, (unsigned long int)range_end - range_start + 1,
2466 (unsigned long int)num_new_strings));
2467 ldap_memfree(range_attr);
2468 *more_strings = False;
2469 return NULL;
2472 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2473 *num_strings + num_new_strings);
2475 if (strings == NULL) {
2476 ldap_memfree(range_attr);
2477 *more_strings = False;
2478 return NULL;
2481 if (new_strings && num_new_strings) {
2482 memcpy(&strings[*num_strings], new_strings,
2483 sizeof(*new_strings) * num_new_strings);
2486 (*num_strings) += num_new_strings;
2488 if (*more_strings) {
2489 *next_attribute = talloc_asprintf(mem_ctx,
2490 "%s;range=%d-*",
2491 field,
2492 (int)*num_strings);
2494 if (!*next_attribute) {
2495 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2496 ldap_memfree(range_attr);
2497 *more_strings = False;
2498 return NULL;
2502 ldap_memfree(range_attr);
2504 return strings;
2508 * pull a single uint32 from a ADS result
2509 * @param ads connection to ads server
2510 * @param msg Results of search
2511 * @param field Attribute to retrieve
2512 * @param v Pointer to int to store result
2513 * @return boolean inidicating success
2515 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2516 uint32 *v)
2518 char **values;
2520 values = ldap_get_values(ads->ldap.ld, msg, field);
2521 if (!values)
2522 return False;
2523 if (!values[0]) {
2524 ldap_value_free(values);
2525 return False;
2528 *v = atoi(values[0]);
2529 ldap_value_free(values);
2530 return True;
2534 * pull a single objectGUID from an ADS result
2535 * @param ads connection to ADS server
2536 * @param msg results of search
2537 * @param guid 37-byte area to receive text guid
2538 * @return boolean indicating success
2540 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2542 char **values;
2543 UUID_FLAT flat_guid;
2545 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2546 if (!values)
2547 return False;
2549 if (values[0]) {
2550 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2551 smb_uuid_unpack(flat_guid, guid);
2552 ldap_value_free(values);
2553 return True;
2555 ldap_value_free(values);
2556 return False;
2562 * pull a single DOM_SID from a ADS result
2563 * @param ads connection to ads server
2564 * @param msg Results of search
2565 * @param field Attribute to retrieve
2566 * @param sid Pointer to sid to store result
2567 * @return boolean inidicating success
2569 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2570 DOM_SID *sid)
2572 struct berval **values;
2573 bool ret = False;
2575 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2577 if (!values)
2578 return False;
2580 if (values[0])
2581 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2583 ldap_value_free_len(values);
2584 return ret;
2588 * pull an array of DOM_SIDs from a ADS result
2589 * @param ads connection to ads server
2590 * @param mem_ctx TALLOC_CTX for allocating sid array
2591 * @param msg Results of search
2592 * @param field Attribute to retrieve
2593 * @param sids pointer to sid array to allocate
2594 * @return the count of SIDs pulled
2596 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2597 LDAPMessage *msg, const char *field, DOM_SID **sids)
2599 struct berval **values;
2600 bool ret;
2601 int count, i;
2603 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2605 if (!values)
2606 return 0;
2608 for (i=0; values[i]; i++)
2609 /* nop */ ;
2611 if (i) {
2612 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2613 if (!(*sids)) {
2614 ldap_value_free_len(values);
2615 return 0;
2617 } else {
2618 (*sids) = NULL;
2621 count = 0;
2622 for (i=0; values[i]; i++) {
2623 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2624 if (ret) {
2625 DEBUG(10, ("pulling SID: %s\n",
2626 sid_string_dbg(&(*sids)[count])));
2627 count++;
2631 ldap_value_free_len(values);
2632 return count;
2636 * pull a SEC_DESC from a ADS result
2637 * @param ads connection to ads server
2638 * @param mem_ctx TALLOC_CTX for allocating sid array
2639 * @param msg Results of search
2640 * @param field Attribute to retrieve
2641 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2642 * @return boolean inidicating success
2644 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2645 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2647 struct berval **values;
2648 bool ret = true;
2650 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2652 if (!values) return false;
2654 if (values[0]) {
2655 NTSTATUS status;
2656 status = unmarshall_sec_desc(mem_ctx,
2657 (uint8 *)values[0]->bv_val,
2658 values[0]->bv_len, sd);
2659 if (!NT_STATUS_IS_OK(status)) {
2660 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2661 nt_errstr(status)));
2662 ret = false;
2666 ldap_value_free_len(values);
2667 return ret;
2671 * in order to support usernames longer than 21 characters we need to
2672 * use both the sAMAccountName and the userPrincipalName attributes
2673 * It seems that not all users have the userPrincipalName attribute set
2675 * @param ads connection to ads server
2676 * @param mem_ctx TALLOC_CTX for allocating sid array
2677 * @param msg Results of search
2678 * @return the username
2680 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2681 LDAPMessage *msg)
2683 #if 0 /* JERRY */
2684 char *ret, *p;
2686 /* lookup_name() only works on the sAMAccountName to
2687 returning the username portion of userPrincipalName
2688 breaks winbindd_getpwnam() */
2690 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2691 if (ret && (p = strchr_m(ret, '@'))) {
2692 *p = 0;
2693 return ret;
2695 #endif
2696 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2701 * find the update serial number - this is the core of the ldap cache
2702 * @param ads connection to ads server
2703 * @param ads connection to ADS server
2704 * @param usn Pointer to retrieved update serial number
2705 * @return status of search
2707 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2709 const char *attrs[] = {"highestCommittedUSN", NULL};
2710 ADS_STATUS status;
2711 LDAPMessage *res;
2713 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2714 if (!ADS_ERR_OK(status))
2715 return status;
2717 if (ads_count_replies(ads, res) != 1) {
2718 ads_msgfree(ads, res);
2719 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2722 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2723 ads_msgfree(ads, res);
2724 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2727 ads_msgfree(ads, res);
2728 return ADS_SUCCESS;
2731 /* parse a ADS timestring - typical string is
2732 '20020917091222.0Z0' which means 09:12.22 17th September
2733 2002, timezone 0 */
2734 static time_t ads_parse_time(const char *str)
2736 struct tm tm;
2738 ZERO_STRUCT(tm);
2740 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2741 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2742 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2743 return 0;
2745 tm.tm_year -= 1900;
2746 tm.tm_mon -= 1;
2748 return timegm(&tm);
2751 /********************************************************************
2752 ********************************************************************/
2754 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2756 const char *attrs[] = {"currentTime", NULL};
2757 ADS_STATUS status;
2758 LDAPMessage *res;
2759 char *timestr;
2760 TALLOC_CTX *ctx;
2761 ADS_STRUCT *ads_s = ads;
2763 if (!(ctx = talloc_init("ads_current_time"))) {
2764 return ADS_ERROR(LDAP_NO_MEMORY);
2767 /* establish a new ldap tcp session if necessary */
2769 if ( !ads->ldap.ld ) {
2770 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2771 ads->server.ldap_server )) == NULL )
2773 goto done;
2775 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2776 status = ads_connect( ads_s );
2777 if ( !ADS_ERR_OK(status))
2778 goto done;
2781 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2782 if (!ADS_ERR_OK(status)) {
2783 goto done;
2786 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2787 if (!timestr) {
2788 ads_msgfree(ads_s, res);
2789 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2790 goto done;
2793 /* but save the time and offset in the original ADS_STRUCT */
2795 ads->config.current_time = ads_parse_time(timestr);
2797 if (ads->config.current_time != 0) {
2798 ads->auth.time_offset = ads->config.current_time - time(NULL);
2799 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2802 ads_msgfree(ads, res);
2804 status = ADS_SUCCESS;
2806 done:
2807 /* free any temporary ads connections */
2808 if ( ads_s != ads ) {
2809 ads_destroy( &ads_s );
2811 talloc_destroy(ctx);
2813 return status;
2816 /********************************************************************
2817 ********************************************************************/
2819 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2821 const char *attrs[] = {"domainFunctionality", NULL};
2822 ADS_STATUS status;
2823 LDAPMessage *res;
2824 ADS_STRUCT *ads_s = ads;
2826 *val = DS_DOMAIN_FUNCTION_2000;
2828 /* establish a new ldap tcp session if necessary */
2830 if ( !ads->ldap.ld ) {
2831 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2832 ads->server.ldap_server )) == NULL )
2834 goto done;
2836 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2837 status = ads_connect( ads_s );
2838 if ( !ADS_ERR_OK(status))
2839 goto done;
2842 /* If the attribute does not exist assume it is a Windows 2000
2843 functional domain */
2845 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2846 if (!ADS_ERR_OK(status)) {
2847 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2848 status = ADS_SUCCESS;
2850 goto done;
2853 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2854 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2856 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2859 ads_msgfree(ads, res);
2861 done:
2862 /* free any temporary ads connections */
2863 if ( ads_s != ads ) {
2864 ads_destroy( &ads_s );
2867 return status;
2871 * find the domain sid for our domain
2872 * @param ads connection to ads server
2873 * @param sid Pointer to domain sid
2874 * @return status of search
2876 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2878 const char *attrs[] = {"objectSid", NULL};
2879 LDAPMessage *res;
2880 ADS_STATUS rc;
2882 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2883 attrs, &res);
2884 if (!ADS_ERR_OK(rc)) return rc;
2885 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2886 ads_msgfree(ads, res);
2887 return ADS_ERROR_SYSTEM(ENOENT);
2889 ads_msgfree(ads, res);
2891 return ADS_SUCCESS;
2895 * find our site name
2896 * @param ads connection to ads server
2897 * @param mem_ctx Pointer to talloc context
2898 * @param site_name Pointer to the sitename
2899 * @return status of search
2901 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2903 ADS_STATUS status;
2904 LDAPMessage *res;
2905 const char *dn, *service_name;
2906 const char *attrs[] = { "dsServiceName", NULL };
2908 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2909 if (!ADS_ERR_OK(status)) {
2910 return status;
2913 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2914 if (service_name == NULL) {
2915 ads_msgfree(ads, res);
2916 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2919 ads_msgfree(ads, res);
2921 /* go up three levels */
2922 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2923 if (dn == NULL) {
2924 return ADS_ERROR(LDAP_NO_MEMORY);
2927 *site_name = talloc_strdup(mem_ctx, dn);
2928 if (*site_name == NULL) {
2929 return ADS_ERROR(LDAP_NO_MEMORY);
2932 return status;
2934 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2939 * find the site dn where a machine resides
2940 * @param ads connection to ads server
2941 * @param mem_ctx Pointer to talloc context
2942 * @param computer_name name of the machine
2943 * @param site_name Pointer to the sitename
2944 * @return status of search
2946 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2948 ADS_STATUS status;
2949 LDAPMessage *res;
2950 const char *parent, *filter;
2951 char *config_context = NULL;
2952 char *dn;
2954 /* shortcut a query */
2955 if (strequal(computer_name, ads->config.ldap_server_name)) {
2956 return ads_site_dn(ads, mem_ctx, site_dn);
2959 status = ads_config_path(ads, mem_ctx, &config_context);
2960 if (!ADS_ERR_OK(status)) {
2961 return status;
2964 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2965 if (filter == NULL) {
2966 return ADS_ERROR(LDAP_NO_MEMORY);
2969 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2970 filter, NULL, &res);
2971 if (!ADS_ERR_OK(status)) {
2972 return status;
2975 if (ads_count_replies(ads, res) != 1) {
2976 ads_msgfree(ads, res);
2977 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2980 dn = ads_get_dn(ads, res);
2981 if (dn == NULL) {
2982 ads_msgfree(ads, res);
2983 return ADS_ERROR(LDAP_NO_MEMORY);
2986 /* go up three levels */
2987 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
2988 if (parent == NULL) {
2989 ads_msgfree(ads, res);
2990 ads_memfree(ads, dn);
2991 return ADS_ERROR(LDAP_NO_MEMORY);
2994 *site_dn = talloc_strdup(mem_ctx, parent);
2995 if (*site_dn == NULL) {
2996 ads_msgfree(ads, res);
2997 ads_memfree(ads, dn);
2998 return ADS_ERROR(LDAP_NO_MEMORY);
3001 ads_memfree(ads, dn);
3002 ads_msgfree(ads, res);
3004 return status;
3008 * get the upn suffixes for a domain
3009 * @param ads connection to ads server
3010 * @param mem_ctx Pointer to talloc context
3011 * @param suffixes Pointer to an array of suffixes
3012 * @param num_suffixes Pointer to the number of suffixes
3013 * @return status of search
3015 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3017 ADS_STATUS status;
3018 LDAPMessage *res;
3019 const char *base;
3020 char *config_context = NULL;
3021 const char *attrs[] = { "uPNSuffixes", NULL };
3023 status = ads_config_path(ads, mem_ctx, &config_context);
3024 if (!ADS_ERR_OK(status)) {
3025 return status;
3028 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3029 if (base == NULL) {
3030 return ADS_ERROR(LDAP_NO_MEMORY);
3033 status = ads_search_dn(ads, &res, base, attrs);
3034 if (!ADS_ERR_OK(status)) {
3035 return status;
3038 if (ads_count_replies(ads, res) != 1) {
3039 ads_msgfree(ads, res);
3040 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3043 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3044 if ((*suffixes) == NULL) {
3045 ads_msgfree(ads, res);
3046 return ADS_ERROR(LDAP_NO_MEMORY);
3049 ads_msgfree(ads, res);
3051 return status;
3055 * get the joinable ous for a domain
3056 * @param ads connection to ads server
3057 * @param mem_ctx Pointer to talloc context
3058 * @param ous Pointer to an array of ous
3059 * @param num_ous Pointer to the number of ous
3060 * @return status of search
3062 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3063 TALLOC_CTX *mem_ctx,
3064 char ***ous,
3065 size_t *num_ous)
3067 ADS_STATUS status;
3068 LDAPMessage *res = NULL;
3069 LDAPMessage *msg = NULL;
3070 const char *attrs[] = { "dn", NULL };
3071 int count = 0;
3073 status = ads_search(ads, &res,
3074 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3075 attrs);
3076 if (!ADS_ERR_OK(status)) {
3077 return status;
3080 count = ads_count_replies(ads, res);
3081 if (count < 1) {
3082 ads_msgfree(ads, res);
3083 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3086 for (msg = ads_first_entry(ads, res); msg;
3087 msg = ads_next_entry(ads, msg)) {
3089 char *dn = NULL;
3091 dn = ads_get_dn(ads, msg);
3092 if (!dn) {
3093 ads_msgfree(ads, res);
3094 return ADS_ERROR(LDAP_NO_MEMORY);
3097 if (!add_string_to_array(mem_ctx, dn,
3098 (const char ***)ous,
3099 (int *)num_ous)) {
3100 ads_memfree(ads, dn);
3101 ads_msgfree(ads, res);
3102 return ADS_ERROR(LDAP_NO_MEMORY);
3105 ads_memfree(ads, dn);
3108 ads_msgfree(ads, res);
3110 return status;
3115 * pull a DOM_SID from an extended dn string
3116 * @param mem_ctx TALLOC_CTX
3117 * @param extended_dn string
3118 * @param flags string type of extended_dn
3119 * @param sid pointer to a DOM_SID
3120 * @return boolean inidicating success
3122 bool ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3123 const char *extended_dn,
3124 enum ads_extended_dn_flags flags,
3125 DOM_SID *sid)
3127 char *p, *q, *dn;
3129 if (!extended_dn) {
3130 return False;
3133 /* otherwise extended_dn gets stripped off */
3134 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3135 return False;
3138 * ADS_EXTENDED_DN_HEX_STRING:
3139 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3141 * ADS_EXTENDED_DN_STRING (only with w2k3):
3142 <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
3145 p = strchr(dn, ';');
3146 if (!p) {
3147 return False;
3150 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3151 return False;
3154 p += strlen(";<SID=");
3156 q = strchr(p, '>');
3157 if (!q) {
3158 return False;
3161 *q = '\0';
3163 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3165 switch (flags) {
3167 case ADS_EXTENDED_DN_STRING:
3168 if (!string_to_sid(sid, p)) {
3169 return False;
3171 break;
3172 case ADS_EXTENDED_DN_HEX_STRING: {
3173 fstring buf;
3174 size_t buf_len;
3176 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3177 if (buf_len == 0) {
3178 return False;
3181 if (!sid_parse(buf, buf_len, sid)) {
3182 DEBUG(10,("failed to parse sid\n"));
3183 return False;
3185 break;
3187 default:
3188 DEBUG(10,("unknown extended dn format\n"));
3189 return False;
3192 return True;
3196 * pull an array of DOM_SIDs from a ADS result
3197 * @param ads connection to ads server
3198 * @param mem_ctx TALLOC_CTX for allocating sid array
3199 * @param msg Results of search
3200 * @param field Attribute to retrieve
3201 * @param flags string type of extended_dn
3202 * @param sids pointer to sid array to allocate
3203 * @return the count of SIDs pulled
3205 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3206 TALLOC_CTX *mem_ctx,
3207 LDAPMessage *msg,
3208 const char *field,
3209 enum ads_extended_dn_flags flags,
3210 DOM_SID **sids)
3212 int i;
3213 size_t dn_count;
3214 char **dn_strings;
3216 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3217 &dn_count)) == NULL) {
3218 return 0;
3221 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3222 if (!(*sids)) {
3223 TALLOC_FREE(dn_strings);
3224 return 0;
3227 for (i=0; i<dn_count; i++) {
3229 if (!ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3230 flags, &(*sids)[i])) {
3231 TALLOC_FREE(*sids);
3232 TALLOC_FREE(dn_strings);
3233 return 0;
3237 TALLOC_FREE(dn_strings);
3239 return dn_count;
3242 /********************************************************************
3243 ********************************************************************/
3245 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3247 LDAPMessage *res = NULL;
3248 ADS_STATUS status;
3249 int count = 0;
3250 char *name = NULL;
3252 status = ads_find_machine_acct(ads, &res, global_myname());
3253 if (!ADS_ERR_OK(status)) {
3254 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3255 global_myname()));
3256 goto out;
3259 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3260 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3261 goto out;
3264 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3265 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3268 out:
3269 ads_msgfree(ads, res);
3271 return name;
3274 /********************************************************************
3275 ********************************************************************/
3277 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3279 LDAPMessage *res = NULL;
3280 ADS_STATUS status;
3281 int count = 0;
3282 char *name = NULL;
3284 status = ads_find_machine_acct(ads, &res, machine_name);
3285 if (!ADS_ERR_OK(status)) {
3286 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3287 global_myname()));
3288 goto out;
3291 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3292 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3293 goto out;
3296 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3297 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3300 out:
3301 ads_msgfree(ads, res);
3303 return name;
3306 /********************************************************************
3307 ********************************************************************/
3309 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3311 LDAPMessage *res = NULL;
3312 ADS_STATUS status;
3313 int count = 0;
3314 char *name = NULL;
3316 status = ads_find_machine_acct(ads, &res, global_myname());
3317 if (!ADS_ERR_OK(status)) {
3318 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3319 global_myname()));
3320 goto out;
3323 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3324 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3325 goto out;
3328 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3329 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3332 out:
3333 ads_msgfree(ads, res);
3335 return name;
3338 #if 0
3340 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3343 * Join a machine to a realm
3344 * Creates the machine account and sets the machine password
3345 * @param ads connection to ads server
3346 * @param machine name of host to add
3347 * @param org_unit Organizational unit to place machine in
3348 * @return status of join
3350 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3351 uint32 account_type, const char *org_unit)
3353 ADS_STATUS status;
3354 LDAPMessage *res = NULL;
3355 char *machine;
3357 /* machine name must be lowercase */
3358 machine = SMB_STRDUP(machine_name);
3359 strlower_m(machine);
3362 status = ads_find_machine_acct(ads, (void **)&res, machine);
3363 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3364 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3365 status = ads_leave_realm(ads, machine);
3366 if (!ADS_ERR_OK(status)) {
3367 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3368 machine, ads->config.realm));
3369 return status;
3373 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3374 if (!ADS_ERR_OK(status)) {
3375 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3376 SAFE_FREE(machine);
3377 return status;
3380 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3381 if (!ADS_ERR_OK(status)) {
3382 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3383 SAFE_FREE(machine);
3384 return status;
3387 SAFE_FREE(machine);
3388 ads_msgfree(ads, res);
3390 return status;
3392 #endif
3395 * Delete a machine from the realm
3396 * @param ads connection to ads server
3397 * @param hostname Machine to remove
3398 * @return status of delete
3400 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3402 ADS_STATUS status;
3403 void *msg;
3404 LDAPMessage *res;
3405 char *hostnameDN, *host;
3406 int rc;
3407 LDAPControl ldap_control;
3408 LDAPControl * pldap_control[2] = {NULL, NULL};
3410 pldap_control[0] = &ldap_control;
3411 memset(&ldap_control, 0, sizeof(LDAPControl));
3412 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3414 /* hostname must be lowercase */
3415 host = SMB_STRDUP(hostname);
3416 strlower_m(host);
3418 status = ads_find_machine_acct(ads, &res, host);
3419 if (!ADS_ERR_OK(status)) {
3420 DEBUG(0, ("Host account for %s does not exist.\n", host));
3421 SAFE_FREE(host);
3422 return status;
3425 msg = ads_first_entry(ads, res);
3426 if (!msg) {
3427 SAFE_FREE(host);
3428 return ADS_ERROR_SYSTEM(ENOENT);
3431 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3433 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3434 if (rc) {
3435 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3436 }else {
3437 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3440 if (rc != LDAP_SUCCESS) {
3441 const char *attrs[] = { "cn", NULL };
3442 LDAPMessage *msg_sub;
3444 /* we only search with scope ONE, we do not expect any further
3445 * objects to be created deeper */
3447 status = ads_do_search_retry(ads, hostnameDN,
3448 LDAP_SCOPE_ONELEVEL,
3449 "(objectclass=*)", attrs, &res);
3451 if (!ADS_ERR_OK(status)) {
3452 SAFE_FREE(host);
3453 ads_memfree(ads, hostnameDN);
3454 return status;
3457 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3458 msg_sub = ads_next_entry(ads, msg_sub)) {
3460 char *dn = NULL;
3462 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3463 SAFE_FREE(host);
3464 ads_memfree(ads, hostnameDN);
3465 return ADS_ERROR(LDAP_NO_MEMORY);
3468 status = ads_del_dn(ads, dn);
3469 if (!ADS_ERR_OK(status)) {
3470 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3471 SAFE_FREE(host);
3472 ads_memfree(ads, dn);
3473 ads_memfree(ads, hostnameDN);
3474 return status;
3477 ads_memfree(ads, dn);
3480 /* there should be no subordinate objects anymore */
3481 status = ads_do_search_retry(ads, hostnameDN,
3482 LDAP_SCOPE_ONELEVEL,
3483 "(objectclass=*)", attrs, &res);
3485 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3486 SAFE_FREE(host);
3487 ads_memfree(ads, hostnameDN);
3488 return status;
3491 /* delete hostnameDN now */
3492 status = ads_del_dn(ads, hostnameDN);
3493 if (!ADS_ERR_OK(status)) {
3494 SAFE_FREE(host);
3495 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3496 ads_memfree(ads, hostnameDN);
3497 return status;
3501 ads_memfree(ads, hostnameDN);
3503 status = ads_find_machine_acct(ads, &res, host);
3504 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3505 DEBUG(3, ("Failed to remove host account.\n"));
3506 SAFE_FREE(host);
3507 return status;
3510 SAFE_FREE(host);
3511 return status;
3515 * pull all token-sids from an LDAP dn
3516 * @param ads connection to ads server
3517 * @param mem_ctx TALLOC_CTX for allocating sid array
3518 * @param dn of LDAP object
3519 * @param user_sid pointer to DOM_SID (objectSid)
3520 * @param primary_group_sid pointer to DOM_SID (self composed)
3521 * @param sids pointer to sid array to allocate
3522 * @param num_sids counter of SIDs pulled
3523 * @return status of token query
3525 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3526 TALLOC_CTX *mem_ctx,
3527 const char *dn,
3528 DOM_SID *user_sid,
3529 DOM_SID *primary_group_sid,
3530 DOM_SID **sids,
3531 size_t *num_sids)
3533 ADS_STATUS status;
3534 LDAPMessage *res = NULL;
3535 int count = 0;
3536 size_t tmp_num_sids;
3537 DOM_SID *tmp_sids;
3538 DOM_SID tmp_user_sid;
3539 DOM_SID tmp_primary_group_sid;
3540 uint32 pgid;
3541 const char *attrs[] = {
3542 "objectSid",
3543 "tokenGroups",
3544 "primaryGroupID",
3545 NULL
3548 status = ads_search_retry_dn(ads, &res, dn, attrs);
3549 if (!ADS_ERR_OK(status)) {
3550 return status;
3553 count = ads_count_replies(ads, res);
3554 if (count != 1) {
3555 ads_msgfree(ads, res);
3556 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3559 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3560 ads_msgfree(ads, res);
3561 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3564 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3565 ads_msgfree(ads, res);
3566 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3570 /* hack to compose the primary group sid without knowing the
3571 * domsid */
3573 DOM_SID domsid;
3574 uint32 dummy_rid;
3576 sid_copy(&domsid, &tmp_user_sid);
3578 if (!sid_split_rid(&domsid, &dummy_rid)) {
3579 ads_msgfree(ads, res);
3580 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3583 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3584 ads_msgfree(ads, res);
3585 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3589 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3591 if (tmp_num_sids == 0 || !tmp_sids) {
3592 ads_msgfree(ads, res);
3593 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3596 if (num_sids) {
3597 *num_sids = tmp_num_sids;
3600 if (sids) {
3601 *sids = tmp_sids;
3604 if (user_sid) {
3605 *user_sid = tmp_user_sid;
3608 if (primary_group_sid) {
3609 *primary_group_sid = tmp_primary_group_sid;
3612 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3614 ads_msgfree(ads, res);
3615 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3619 * Find a sAMAccoutName in LDAP
3620 * @param ads connection to ads server
3621 * @param mem_ctx TALLOC_CTX for allocating sid array
3622 * @param samaccountname to search
3623 * @param uac_ret uint32 pointer userAccountControl attribute value
3624 * @param dn_ret pointer to dn
3625 * @return status of token query
3627 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3628 TALLOC_CTX *mem_ctx,
3629 const char *samaccountname,
3630 uint32 *uac_ret,
3631 const char **dn_ret)
3633 ADS_STATUS status;
3634 const char *attrs[] = { "userAccountControl", NULL };
3635 const char *filter;
3636 LDAPMessage *res = NULL;
3637 char *dn = NULL;
3638 uint32 uac = 0;
3640 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3641 samaccountname);
3642 if (filter == NULL) {
3643 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3644 goto out;
3647 status = ads_do_search_all(ads, ads->config.bind_path,
3648 LDAP_SCOPE_SUBTREE,
3649 filter, attrs, &res);
3651 if (!ADS_ERR_OK(status)) {
3652 goto out;
3655 if (ads_count_replies(ads, res) != 1) {
3656 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3657 goto out;
3660 dn = ads_get_dn(ads, res);
3661 if (dn == NULL) {
3662 status = ADS_ERROR(LDAP_NO_MEMORY);
3663 goto out;
3666 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3667 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3668 goto out;
3671 if (uac_ret) {
3672 *uac_ret = uac;
3675 if (dn_ret) {
3676 *dn_ret = talloc_strdup(mem_ctx, dn);
3677 if (!*dn_ret) {
3678 status = ADS_ERROR(LDAP_NO_MEMORY);
3679 goto out;
3682 out:
3683 ads_memfree(ads, dn);
3684 ads_msgfree(ads, res);
3686 return status;
3690 * find our configuration path
3691 * @param ads connection to ads server
3692 * @param mem_ctx Pointer to talloc context
3693 * @param config_path Pointer to the config path
3694 * @return status of search
3696 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3697 TALLOC_CTX *mem_ctx,
3698 char **config_path)
3700 ADS_STATUS status;
3701 LDAPMessage *res = NULL;
3702 const char *config_context = NULL;
3703 const char *attrs[] = { "configurationNamingContext", NULL };
3705 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3706 "(objectclass=*)", attrs, &res);
3707 if (!ADS_ERR_OK(status)) {
3708 return status;
3711 config_context = ads_pull_string(ads, mem_ctx, res,
3712 "configurationNamingContext");
3713 ads_msgfree(ads, res);
3714 if (!config_context) {
3715 return ADS_ERROR(LDAP_NO_MEMORY);
3718 if (config_path) {
3719 *config_path = talloc_strdup(mem_ctx, config_context);
3720 if (!*config_path) {
3721 return ADS_ERROR(LDAP_NO_MEMORY);
3725 return ADS_ERROR(LDAP_SUCCESS);
3729 * find the displayName of an extended right
3730 * @param ads connection to ads server
3731 * @param config_path The config path
3732 * @param mem_ctx Pointer to talloc context
3733 * @param GUID struct of the rightsGUID
3734 * @return status of search
3736 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3737 const char *config_path,
3738 TALLOC_CTX *mem_ctx,
3739 const struct GUID *rights_guid)
3741 ADS_STATUS rc;
3742 LDAPMessage *res = NULL;
3743 char *expr = NULL;
3744 const char *attrs[] = { "displayName", NULL };
3745 const char *result = NULL;
3746 const char *path;
3748 if (!ads || !mem_ctx || !rights_guid) {
3749 goto done;
3752 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3753 smb_uuid_string(mem_ctx, *rights_guid));
3754 if (!expr) {
3755 goto done;
3758 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3759 if (!path) {
3760 goto done;
3763 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3764 expr, attrs, &res);
3765 if (!ADS_ERR_OK(rc)) {
3766 goto done;
3769 if (ads_count_replies(ads, res) != 1) {
3770 goto done;
3773 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3775 done:
3776 ads_msgfree(ads, res);
3777 return result;
3782 * verify or build and verify an account ou
3783 * @param mem_ctx Pointer to talloc context
3784 * @param ads connection to ads server
3785 * @param account_ou
3786 * @return status of search
3789 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3790 ADS_STRUCT *ads,
3791 const char **account_ou)
3793 struct ldb_dn *name_dn = NULL;
3794 const char *name = NULL;
3795 char *ou_string = NULL;
3797 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3798 if (name_dn) {
3799 return ADS_SUCCESS;
3802 ou_string = ads_ou_string(ads, *account_ou);
3803 if (!ou_string) {
3804 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3807 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3808 ads->config.bind_path);
3809 SAFE_FREE(ou_string);
3810 if (!name) {
3811 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3814 name_dn = ldb_dn_explode(mem_ctx, name);
3815 if (!name_dn) {
3816 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3819 *account_ou = talloc_strdup(mem_ctx, name);
3820 if (!*account_ou) {
3821 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3824 return ADS_SUCCESS;
3827 #endif