s3: libads: use get_dc_name() instead of get_sorted_dc_list() in the LDAP case
[Samba.git] / source / libads / ldap.c
blob92436070713a356a1cc83a0293b0533745c207cf
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 NETLOGON_SAM_LOGON_RESPONSE_EX 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();
306 if ( !c_realm || !*c_realm ) {
307 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
308 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
311 realm = c_realm;
314 * In case of LDAP we use get_dc_name() as that
315 * creates the custom krb5.conf file
317 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
318 fstring srv_name;
319 struct sockaddr_storage ip_out;
321 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
322 (got_realm ? "realm" : "domain"), realm));
324 if (get_dc_name(realm, realm, srv_name, &ip_out)) {
326 * we call ads_try_connect() to fill in the
327 * ads->config details
329 if (ads_try_connect(ads, srv_name, false)) {
330 return NT_STATUS_OK;
334 return NT_STATUS_NO_LOGON_SERVERS;
337 sitename = sitename_fetch(realm);
339 again:
341 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
342 (got_realm ? "realm" : "domain"), realm));
344 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
345 if (!NT_STATUS_IS_OK(status)) {
346 /* fall back to netbios if we can */
347 if ( got_realm && !lp_disable_netbios() ) {
348 got_realm = False;
349 goto again;
352 SAFE_FREE(sitename);
353 return status;
356 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
357 for ( i=0; i<count; i++ ) {
358 char server[INET6_ADDRSTRLEN];
360 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
362 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
363 continue;
365 if (!got_realm) {
366 /* realm in this case is a workgroup name. We need
367 to ignore any IP addresses in the negative connection
368 cache that match ip addresses returned in the ad realm
369 case. It sucks that I have to reproduce the logic above... */
370 c_realm = ads->server.realm;
371 if ( !c_realm || !*c_realm ) {
372 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
373 c_realm = lp_realm();
376 if (c_realm && *c_realm &&
377 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
378 /* Ensure we add the workgroup name for this
379 IP address as negative too. */
380 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
381 continue;
385 if ( ads_try_connect(ads, server, false) ) {
386 SAFE_FREE(ip_list);
387 SAFE_FREE(sitename);
388 return NT_STATUS_OK;
391 /* keep track of failures */
392 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
395 SAFE_FREE(ip_list);
397 /* In case we failed to contact one of our closest DC on our site we
398 * need to try to find another DC, retry with a site-less SRV DNS query
399 * - Guenther */
401 if (sitename) {
402 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
403 "trying to find another DC\n", sitename));
404 SAFE_FREE(sitename);
405 namecache_delete(realm, 0x1C);
406 goto again;
409 return NT_STATUS_NO_LOGON_SERVERS;
412 /*********************************************************************
413 *********************************************************************/
415 static NTSTATUS ads_lookup_site(void)
417 ADS_STRUCT *ads = NULL;
418 ADS_STATUS ads_status;
419 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
421 ads = ads_init(lp_realm(), NULL, NULL);
422 if (!ads) {
423 return NT_STATUS_NO_MEMORY;
426 /* The NO_BIND here will find a DC and set the client site
427 but not establish the TCP connection */
429 ads->auth.flags = ADS_AUTH_NO_BIND;
430 ads_status = ads_connect(ads);
431 if (!ADS_ERR_OK(ads_status)) {
432 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
433 ads_errstr(ads_status)));
435 nt_status = ads_ntstatus(ads_status);
437 if (ads) {
438 ads_destroy(&ads);
441 return nt_status;
444 /*********************************************************************
445 *********************************************************************/
447 static const char* host_dns_domain(const char *fqdn)
449 const char *p = fqdn;
451 /* go to next char following '.' */
453 if ((p = strchr_m(fqdn, '.')) != NULL) {
454 p++;
457 return p;
462 * Connect to the Global Catalog server
463 * @param ads Pointer to an existing ADS_STRUCT
464 * @return status of connection
466 * Simple wrapper around ads_connect() that fills in the
467 * GC ldap server information
470 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
472 TALLOC_CTX *frame = talloc_stackframe();
473 struct dns_rr_srv *gcs_list;
474 int num_gcs;
475 char *realm = ads->server.realm;
476 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
477 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
478 int i;
479 bool done = false;
480 char *sitename = NULL;
482 if (!realm)
483 realm = lp_realm();
485 if ((sitename = sitename_fetch(realm)) == NULL) {
486 ads_lookup_site();
487 sitename = sitename_fetch(realm);
490 do {
491 /* We try once with a sitename and once without
492 (unless we don't have a sitename and then we're
493 done */
495 if (sitename == NULL)
496 done = true;
498 nt_status = ads_dns_query_gcs(frame, realm, sitename,
499 &gcs_list, &num_gcs);
501 SAFE_FREE(sitename);
503 if (!NT_STATUS_IS_OK(nt_status)) {
504 ads_status = ADS_ERROR_NT(nt_status);
505 goto done;
508 /* Loop until we get a successful connection or have gone
509 through them all. When connecting a GC server, make sure that
510 the realm is the server's DNS name and not the forest root */
512 for (i=0; i<num_gcs; i++) {
513 ads->server.gc = true;
514 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
515 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
516 ads_status = ads_connect(ads);
517 if (ADS_ERR_OK(ads_status)) {
518 /* Reset the bind_dn to "". A Global Catalog server
519 may host multiple domain trees in a forest.
520 Windows 2003 GC server will accept "" as the search
521 path to imply search all domain trees in the forest */
523 SAFE_FREE(ads->config.bind_path);
524 ads->config.bind_path = SMB_STRDUP("");
527 goto done;
529 SAFE_FREE(ads->server.ldap_server);
530 SAFE_FREE(ads->server.realm);
533 TALLOC_FREE(gcs_list);
534 num_gcs = 0;
535 } while (!done);
537 done:
538 SAFE_FREE(sitename);
539 talloc_destroy(frame);
541 return ads_status;
546 * Connect to the LDAP server
547 * @param ads Pointer to an existing ADS_STRUCT
548 * @return status of connection
550 ADS_STATUS ads_connect(ADS_STRUCT *ads)
552 int version = LDAP_VERSION3;
553 ADS_STATUS status;
554 NTSTATUS ntstatus;
555 char addr[INET6_ADDRSTRLEN];
557 ZERO_STRUCT(ads->ldap);
558 ads->ldap.last_attempt = time(NULL);
559 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
561 /* try with a user specified server */
563 if (DEBUGLEVEL >= 11) {
564 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
565 DEBUG(11,("ads_connect: entering\n"));
566 DEBUGADD(11,("%s\n", s));
567 TALLOC_FREE(s);
570 if (ads->server.ldap_server &&
571 ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
572 goto got_connection;
575 ntstatus = ads_find_dc(ads);
576 if (NT_STATUS_IS_OK(ntstatus)) {
577 goto got_connection;
580 status = ADS_ERROR_NT(ntstatus);
581 goto out;
583 got_connection:
585 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
586 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
588 if (!ads->auth.user_name) {
589 /* Must use the userPrincipalName value here or sAMAccountName
590 and not servicePrincipalName; found by Guenther Deschner */
592 asprintf(&ads->auth.user_name, "%s$", global_myname() );
595 if (!ads->auth.realm) {
596 ads->auth.realm = SMB_STRDUP(ads->config.realm);
599 if (!ads->auth.kdc_server) {
600 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
601 ads->auth.kdc_server = SMB_STRDUP(addr);
604 #if KRB5_DNS_HACK
605 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
606 to MIT kerberos to work (tridge) */
608 char *env;
609 asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm);
610 setenv(env, ads->auth.kdc_server, 1);
611 free(env);
613 #endif
615 /* If the caller() requested no LDAP bind, then we are done */
617 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
618 status = ADS_SUCCESS;
619 goto out;
622 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
623 if (!ads->ldap.mem_ctx) {
624 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
625 goto out;
628 /* Otherwise setup the TCP LDAP session */
630 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
631 ads->ldap.port, lp_ldap_timeout());
632 if (ads->ldap.ld == NULL) {
633 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
634 goto out;
636 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
638 /* cache the successful connection for workgroup and realm */
639 if (ads_closest_dc(ads)) {
640 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
641 saf_store( ads->server.workgroup, addr);
642 saf_store( ads->server.realm, addr);
645 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
647 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
648 if (!ADS_ERR_OK(status)) {
649 goto out;
652 /* fill in the current time and offsets */
654 status = ads_current_time( ads );
655 if ( !ADS_ERR_OK(status) ) {
656 goto out;
659 /* Now do the bind */
661 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
662 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
663 goto out;
666 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
667 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
668 goto out;
671 status = ads_sasl_bind(ads);
673 out:
674 if (DEBUGLEVEL >= 11) {
675 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
676 DEBUG(11,("ads_connect: leaving with: %s\n",
677 ads_errstr(status)));
678 DEBUGADD(11,("%s\n", s));
679 TALLOC_FREE(s);
682 return status;
686 * Connect to the LDAP server using given credentials
687 * @param ads Pointer to an existing ADS_STRUCT
688 * @return status of connection
690 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
692 ads->auth.flags |= ADS_AUTH_USER_CREDS;
694 return ads_connect(ads);
698 * Disconnect the LDAP server
699 * @param ads Pointer to an existing ADS_STRUCT
701 void ads_disconnect(ADS_STRUCT *ads)
703 if (ads->ldap.ld) {
704 ldap_unbind(ads->ldap.ld);
705 ads->ldap.ld = NULL;
707 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
708 ads->ldap.wrap_ops->disconnect(ads);
710 if (ads->ldap.mem_ctx) {
711 talloc_free(ads->ldap.mem_ctx);
713 ZERO_STRUCT(ads->ldap);
717 Duplicate a struct berval into talloc'ed memory
719 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
721 struct berval *value;
723 if (!in_val) return NULL;
725 value = TALLOC_ZERO_P(ctx, struct berval);
726 if (value == NULL)
727 return NULL;
728 if (in_val->bv_len == 0) return value;
730 value->bv_len = in_val->bv_len;
731 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
732 in_val->bv_len);
733 return value;
737 Make a values list out of an array of (struct berval *)
739 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
740 const struct berval **in_vals)
742 struct berval **values;
743 int i;
745 if (!in_vals) return NULL;
746 for (i=0; in_vals[i]; i++)
747 ; /* count values */
748 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
749 if (!values) return NULL;
751 for (i=0; in_vals[i]; i++) {
752 values[i] = dup_berval(ctx, in_vals[i]);
754 return values;
758 UTF8-encode a values list out of an array of (char *)
760 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
762 char **values;
763 int i;
764 size_t size;
766 if (!in_vals) return NULL;
767 for (i=0; in_vals[i]; i++)
768 ; /* count values */
769 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
770 if (!values) return NULL;
772 for (i=0; in_vals[i]; i++) {
773 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
774 TALLOC_FREE(values);
775 return NULL;
778 return values;
782 Pull a (char *) array out of a UTF8-encoded values list
784 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
786 char **values;
787 int i;
788 size_t converted_size;
790 if (!in_vals) return NULL;
791 for (i=0; in_vals[i]; i++)
792 ; /* count values */
793 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
794 if (!values) return NULL;
796 for (i=0; in_vals[i]; i++) {
797 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
798 &converted_size)) {
799 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
800 "%s", strerror(errno)));
803 return values;
807 * Do a search with paged results. cookie must be null on the first
808 * call, and then returned on each subsequent call. It will be null
809 * again when the entire search is complete
810 * @param ads connection to ads server
811 * @param bind_path Base dn for the search
812 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
813 * @param expr Search expression - specified in local charset
814 * @param attrs Attributes to retrieve - specified in utf8 or ascii
815 * @param res ** which will contain results - free res* with ads_msgfree()
816 * @param count Number of entries retrieved on this page
817 * @param cookie The paged results cookie to be returned on subsequent calls
818 * @return status of search
820 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
821 const char *bind_path,
822 int scope, const char *expr,
823 const char **attrs, void *args,
824 LDAPMessage **res,
825 int *count, struct berval **cookie)
827 int rc, i, version;
828 char *utf8_expr, *utf8_path, **search_attrs;
829 size_t converted_size;
830 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
831 BerElement *cookie_be = NULL;
832 struct berval *cookie_bv= NULL;
833 BerElement *ext_be = NULL;
834 struct berval *ext_bv= NULL;
836 TALLOC_CTX *ctx;
837 ads_control *external_control = (ads_control *) args;
839 *res = NULL;
841 if (!(ctx = talloc_init("ads_do_paged_search_args")))
842 return ADS_ERROR(LDAP_NO_MEMORY);
844 /* 0 means the conversion worked but the result was empty
845 so we only fail if it's -1. In any case, it always
846 at least nulls out the dest */
847 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
848 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
850 rc = LDAP_NO_MEMORY;
851 goto done;
854 if (!attrs || !(*attrs))
855 search_attrs = NULL;
856 else {
857 /* This would be the utf8-encoded version...*/
858 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
859 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs))) {
860 rc = LDAP_NO_MEMORY;
861 goto done;
865 /* Paged results only available on ldap v3 or later */
866 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
867 if (version < LDAP_VERSION3) {
868 rc = LDAP_NOT_SUPPORTED;
869 goto done;
872 cookie_be = ber_alloc_t(LBER_USE_DER);
873 if (*cookie) {
874 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
875 ber_bvfree(*cookie); /* don't need it from last time */
876 *cookie = NULL;
877 } else {
878 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
880 ber_flatten(cookie_be, &cookie_bv);
881 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
882 PagedResults.ldctl_iscritical = (char) 1;
883 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
884 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
886 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
887 NoReferrals.ldctl_iscritical = (char) 0;
888 NoReferrals.ldctl_value.bv_len = 0;
889 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
891 if (external_control &&
892 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
893 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
895 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
896 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
898 /* win2k does not accept a ldctl_value beeing passed in */
900 if (external_control->val != 0) {
902 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
903 rc = LDAP_NO_MEMORY;
904 goto done;
907 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
908 rc = LDAP_NO_MEMORY;
909 goto done;
911 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
912 rc = LDAP_NO_MEMORY;
913 goto done;
916 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
917 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
919 } else {
920 ExternalCtrl.ldctl_value.bv_len = 0;
921 ExternalCtrl.ldctl_value.bv_val = NULL;
924 controls[0] = &NoReferrals;
925 controls[1] = &PagedResults;
926 controls[2] = &ExternalCtrl;
927 controls[3] = NULL;
929 } else {
930 controls[0] = &NoReferrals;
931 controls[1] = &PagedResults;
932 controls[2] = NULL;
935 /* we need to disable referrals as the openldap libs don't
936 handle them and paged results at the same time. Using them
937 together results in the result record containing the server
938 page control being removed from the result list (tridge/jmcd)
940 leaving this in despite the control that says don't generate
941 referrals, in case the server doesn't support it (jmcd)
943 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
945 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
946 search_attrs, 0, controls,
947 NULL, LDAP_NO_LIMIT,
948 (LDAPMessage **)res);
950 ber_free(cookie_be, 1);
951 ber_bvfree(cookie_bv);
953 if (rc) {
954 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
955 ldap_err2string(rc)));
956 goto done;
959 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
960 NULL, &rcontrols, 0);
962 if (!rcontrols) {
963 goto done;
966 for (i=0; rcontrols[i]; i++) {
967 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
968 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
969 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
970 &cookie_bv);
971 /* the berval is the cookie, but must be freed when
972 it is all done */
973 if (cookie_bv->bv_len) /* still more to do */
974 *cookie=ber_bvdup(cookie_bv);
975 else
976 *cookie=NULL;
977 ber_bvfree(cookie_bv);
978 ber_free(cookie_be, 1);
979 break;
982 ldap_controls_free(rcontrols);
984 done:
985 talloc_destroy(ctx);
987 if (ext_be) {
988 ber_free(ext_be, 1);
991 if (ext_bv) {
992 ber_bvfree(ext_bv);
995 /* if/when we decide to utf8-encode attrs, take out this next line */
996 TALLOC_FREE(search_attrs);
998 return ADS_ERROR(rc);
1001 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1002 int scope, const char *expr,
1003 const char **attrs, LDAPMessage **res,
1004 int *count, struct berval **cookie)
1006 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1011 * Get all results for a search. This uses ads_do_paged_search() to return
1012 * all entries in a large search.
1013 * @param ads connection to ads server
1014 * @param bind_path Base dn for the search
1015 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1016 * @param expr Search expression
1017 * @param attrs Attributes to retrieve
1018 * @param res ** which will contain results - free res* with ads_msgfree()
1019 * @return status of search
1021 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1022 int scope, const char *expr,
1023 const char **attrs, void *args,
1024 LDAPMessage **res)
1026 struct berval *cookie = NULL;
1027 int count = 0;
1028 ADS_STATUS status;
1030 *res = NULL;
1031 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1032 &count, &cookie);
1034 if (!ADS_ERR_OK(status))
1035 return status;
1037 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1038 while (cookie) {
1039 LDAPMessage *res2 = NULL;
1040 ADS_STATUS status2;
1041 LDAPMessage *msg, *next;
1043 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1044 attrs, args, &res2, &count, &cookie);
1046 if (!ADS_ERR_OK(status2)) break;
1048 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1049 that this works on all ldap libs, but I have only tested with openldap */
1050 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1051 next = ads_next_message(ads, msg);
1052 ldap_add_result_entry((LDAPMessage **)res, msg);
1054 /* note that we do not free res2, as the memory is now
1055 part of the main returned list */
1057 #else
1058 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1059 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1060 #endif
1062 return status;
1065 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1066 int scope, const char *expr,
1067 const char **attrs, LDAPMessage **res)
1069 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1072 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1073 int scope, const char *expr,
1074 const char **attrs, uint32 sd_flags,
1075 LDAPMessage **res)
1077 ads_control args;
1079 args.control = ADS_SD_FLAGS_OID;
1080 args.val = sd_flags;
1081 args.critical = True;
1083 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1088 * Run a function on all results for a search. Uses ads_do_paged_search() and
1089 * runs the function as each page is returned, using ads_process_results()
1090 * @param ads connection to ads server
1091 * @param bind_path Base dn for the search
1092 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1093 * @param expr Search expression - specified in local charset
1094 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1095 * @param fn Function which takes attr name, values list, and data_area
1096 * @param data_area Pointer which is passed to function on each call
1097 * @return status of search
1099 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1100 int scope, const char *expr, const char **attrs,
1101 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1102 void *data_area)
1104 struct berval *cookie = NULL;
1105 int count = 0;
1106 ADS_STATUS status;
1107 LDAPMessage *res;
1109 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1110 &count, &cookie);
1112 if (!ADS_ERR_OK(status)) return status;
1114 ads_process_results(ads, res, fn, data_area);
1115 ads_msgfree(ads, res);
1117 while (cookie) {
1118 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1119 &res, &count, &cookie);
1121 if (!ADS_ERR_OK(status)) break;
1123 ads_process_results(ads, res, fn, data_area);
1124 ads_msgfree(ads, res);
1127 return status;
1131 * Do a search with a timeout.
1132 * @param ads connection to ads server
1133 * @param bind_path Base dn for the search
1134 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1135 * @param expr Search expression
1136 * @param attrs Attributes to retrieve
1137 * @param res ** which will contain results - free res* with ads_msgfree()
1138 * @return status of search
1140 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1141 const char *expr,
1142 const char **attrs, LDAPMessage **res)
1144 int rc;
1145 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1146 size_t converted_size;
1147 TALLOC_CTX *ctx;
1149 *res = NULL;
1150 if (!(ctx = talloc_init("ads_do_search"))) {
1151 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1152 return ADS_ERROR(LDAP_NO_MEMORY);
1155 /* 0 means the conversion worked but the result was empty
1156 so we only fail if it's negative. In any case, it always
1157 at least nulls out the dest */
1158 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1159 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1161 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1162 rc = LDAP_NO_MEMORY;
1163 goto done;
1166 if (!attrs || !(*attrs))
1167 search_attrs = NULL;
1168 else {
1169 /* This would be the utf8-encoded version...*/
1170 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1171 if (!(str_list_copy(talloc_tos(), &search_attrs, attrs)))
1173 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1174 rc = LDAP_NO_MEMORY;
1175 goto done;
1179 /* see the note in ads_do_paged_search - we *must* disable referrals */
1180 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1182 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1183 search_attrs, 0, NULL, NULL,
1184 LDAP_NO_LIMIT,
1185 (LDAPMessage **)res);
1187 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1188 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1189 rc = 0;
1192 done:
1193 talloc_destroy(ctx);
1194 /* if/when we decide to utf8-encode attrs, take out this next line */
1195 TALLOC_FREE(search_attrs);
1196 return ADS_ERROR(rc);
1199 * Do a general ADS search
1200 * @param ads connection to ads server
1201 * @param res ** which will contain results - free res* with ads_msgfree()
1202 * @param expr Search expression
1203 * @param attrs Attributes to retrieve
1204 * @return status of search
1206 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1207 const char *expr, const char **attrs)
1209 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1210 expr, attrs, res);
1214 * Do a search on a specific DistinguishedName
1215 * @param ads connection to ads server
1216 * @param res ** which will contain results - free res* with ads_msgfree()
1217 * @param dn DistinguishName to search
1218 * @param attrs Attributes to retrieve
1219 * @return status of search
1221 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1222 const char *dn, const char **attrs)
1224 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1225 attrs, res);
1229 * Free up memory from a ads_search
1230 * @param ads connection to ads server
1231 * @param msg Search results to free
1233 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1235 if (!msg) return;
1236 ldap_msgfree(msg);
1240 * Free up memory from various ads requests
1241 * @param ads connection to ads server
1242 * @param mem Area to free
1244 void ads_memfree(ADS_STRUCT *ads, void *mem)
1246 SAFE_FREE(mem);
1250 * Get a dn from search results
1251 * @param ads connection to ads server
1252 * @param msg Search result
1253 * @return dn string
1255 char *ads_get_dn(ADS_STRUCT *ads, LDAPMessage *msg)
1257 char *utf8_dn, *unix_dn;
1258 size_t converted_size;
1260 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1262 if (!utf8_dn) {
1263 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1264 return NULL;
1267 if (!pull_utf8_allocate(&unix_dn, utf8_dn, &converted_size)) {
1268 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1269 utf8_dn ));
1270 return NULL;
1272 ldap_memfree(utf8_dn);
1273 return unix_dn;
1277 * Get the parent from a dn
1278 * @param dn the dn to return the parent from
1279 * @return parent dn string
1281 char *ads_parent_dn(const char *dn)
1283 char *p;
1285 if (dn == NULL) {
1286 return NULL;
1289 p = strchr(dn, ',');
1291 if (p == NULL) {
1292 return NULL;
1295 return p+1;
1299 * Find a machine account given a hostname
1300 * @param ads connection to ads server
1301 * @param res ** which will contain results - free res* with ads_msgfree()
1302 * @param host Hostname to search for
1303 * @return status of search
1305 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1306 const char *machine)
1308 ADS_STATUS status;
1309 char *expr;
1310 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1312 *res = NULL;
1314 /* the easiest way to find a machine account anywhere in the tree
1315 is to look for hostname$ */
1316 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1317 DEBUG(1, ("asprintf failed!\n"));
1318 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1321 status = ads_search(ads, res, expr, attrs);
1322 SAFE_FREE(expr);
1323 return status;
1327 * Initialize a list of mods to be used in a modify request
1328 * @param ctx An initialized TALLOC_CTX
1329 * @return allocated ADS_MODLIST
1331 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1333 #define ADS_MODLIST_ALLOC_SIZE 10
1334 LDAPMod **mods;
1336 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1337 /* -1 is safety to make sure we don't go over the end.
1338 need to reset it to NULL before doing ldap modify */
1339 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1341 return (ADS_MODLIST)mods;
1346 add an attribute to the list, with values list already constructed
1348 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1349 int mod_op, const char *name,
1350 const void *_invals)
1352 const void **invals = (const void **)_invals;
1353 int curmod;
1354 LDAPMod **modlist = (LDAPMod **) *mods;
1355 struct berval **ber_values = NULL;
1356 char **char_values = NULL;
1358 if (!invals) {
1359 mod_op = LDAP_MOD_DELETE;
1360 } else {
1361 if (mod_op & LDAP_MOD_BVALUES)
1362 ber_values = ads_dup_values(ctx,
1363 (const struct berval **)invals);
1364 else
1365 char_values = ads_push_strvals(ctx,
1366 (const char **) invals);
1369 /* find the first empty slot */
1370 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1371 curmod++);
1372 if (modlist[curmod] == (LDAPMod *) -1) {
1373 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1374 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1375 return ADS_ERROR(LDAP_NO_MEMORY);
1376 memset(&modlist[curmod], 0,
1377 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1378 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1379 *mods = (ADS_MODLIST)modlist;
1382 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1383 return ADS_ERROR(LDAP_NO_MEMORY);
1384 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1385 if (mod_op & LDAP_MOD_BVALUES) {
1386 modlist[curmod]->mod_bvalues = ber_values;
1387 } else if (mod_op & LDAP_MOD_DELETE) {
1388 modlist[curmod]->mod_values = NULL;
1389 } else {
1390 modlist[curmod]->mod_values = char_values;
1393 modlist[curmod]->mod_op = mod_op;
1394 return ADS_ERROR(LDAP_SUCCESS);
1398 * Add a single string value 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 val The value to add - NULL means DELETE
1403 * @return ADS STATUS indicating success of add
1405 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1406 const char *name, const char *val)
1408 const char *values[2];
1410 values[0] = val;
1411 values[1] = NULL;
1413 if (!val)
1414 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1415 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1419 * Add an array of string values to a mod list
1420 * @param ctx An initialized TALLOC_CTX
1421 * @param mods An initialized ADS_MODLIST
1422 * @param name The attribute name to add
1423 * @param vals The array of string values to add - NULL means DELETE
1424 * @return ADS STATUS indicating success of add
1426 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1427 const char *name, const char **vals)
1429 if (!vals)
1430 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1431 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1432 name, (const void **) vals);
1435 #if 0
1437 * Add a single ber-encoded value to a mod list
1438 * @param ctx An initialized TALLOC_CTX
1439 * @param mods An initialized ADS_MODLIST
1440 * @param name The attribute name to add
1441 * @param val The value to add - NULL means DELETE
1442 * @return ADS STATUS indicating success of add
1444 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1445 const char *name, const struct berval *val)
1447 const struct berval *values[2];
1449 values[0] = val;
1450 values[1] = NULL;
1451 if (!val)
1452 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1453 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1454 name, (const void **) values);
1456 #endif
1459 * Perform an ldap modify
1460 * @param ads connection to ads server
1461 * @param mod_dn DistinguishedName to modify
1462 * @param mods list of modifications to perform
1463 * @return status of modify
1465 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1467 int ret,i;
1468 char *utf8_dn = NULL;
1469 size_t converted_size;
1471 this control is needed to modify that contains a currently
1472 non-existent attribute (but allowable for the object) to run
1474 LDAPControl PermitModify = {
1475 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1476 {0, NULL},
1477 (char) 1};
1478 LDAPControl *controls[2];
1480 controls[0] = &PermitModify;
1481 controls[1] = NULL;
1483 if (!push_utf8_allocate(&utf8_dn, mod_dn, &converted_size)) {
1484 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1487 /* find the end of the list, marked by NULL or -1 */
1488 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1489 /* make sure the end of the list is NULL */
1490 mods[i] = NULL;
1491 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1492 (LDAPMod **) mods, controls, NULL);
1493 SAFE_FREE(utf8_dn);
1494 return ADS_ERROR(ret);
1498 * Perform an ldap add
1499 * @param ads connection to ads server
1500 * @param new_dn DistinguishedName to add
1501 * @param mods list of attributes and values for DN
1502 * @return status of add
1504 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1506 int ret, i;
1507 char *utf8_dn = NULL;
1508 size_t converted_size;
1510 if (!push_utf8_allocate(&utf8_dn, new_dn, &converted_size)) {
1511 DEBUG(1, ("ads_gen_add: push_utf8_allocate failed!"));
1512 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1515 /* find the end of the list, marked by NULL or -1 */
1516 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1517 /* make sure the end of the list is NULL */
1518 mods[i] = NULL;
1520 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1521 SAFE_FREE(utf8_dn);
1522 return ADS_ERROR(ret);
1526 * Delete a DistinguishedName
1527 * @param ads connection to ads server
1528 * @param new_dn DistinguishedName to delete
1529 * @return status of delete
1531 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1533 int ret;
1534 char *utf8_dn = NULL;
1535 size_t converted_size;
1536 if (!push_utf8_allocate(&utf8_dn, del_dn, &converted_size)) {
1537 DEBUG(1, ("ads_del_dn: push_utf8_allocate failed!"));
1538 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1541 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1542 SAFE_FREE(utf8_dn);
1543 return ADS_ERROR(ret);
1547 * Build an org unit string
1548 * if org unit is Computers or blank then assume a container, otherwise
1549 * assume a / separated list of organisational units.
1550 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1551 * @param ads connection to ads server
1552 * @param org_unit Organizational unit
1553 * @return org unit string - caller must free
1555 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1557 char *ret = NULL;
1559 if (!org_unit || !*org_unit) {
1561 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1563 /* samba4 might not yet respond to a wellknownobject-query */
1564 return ret ? ret : SMB_STRDUP("cn=Computers");
1567 if (strequal(org_unit, "Computers")) {
1568 return SMB_STRDUP("cn=Computers");
1571 /* jmcd: removed "\\" from the separation chars, because it is
1572 needed as an escape for chars like '#' which are valid in an
1573 OU name */
1574 return ads_build_path(org_unit, "/", "ou=", 1);
1578 * Get a org unit string for a well-known GUID
1579 * @param ads connection to ads server
1580 * @param wknguid Well known GUID
1581 * @return org unit string - caller must free
1583 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1585 ADS_STATUS status;
1586 LDAPMessage *res = NULL;
1587 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1588 **bind_dn_exp = NULL;
1589 const char *attrs[] = {"distinguishedName", NULL};
1590 int new_ln, wkn_ln, bind_ln, i;
1592 if (wknguid == NULL) {
1593 return NULL;
1596 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1597 DEBUG(1, ("asprintf failed!\n"));
1598 return NULL;
1601 status = ads_search_dn(ads, &res, base, attrs);
1602 if (!ADS_ERR_OK(status)) {
1603 DEBUG(1,("Failed while searching for: %s\n", base));
1604 goto out;
1607 if (ads_count_replies(ads, res) != 1) {
1608 goto out;
1611 /* substitute the bind-path from the well-known-guid-search result */
1612 wkn_dn = ads_get_dn(ads, res);
1613 if (!wkn_dn) {
1614 goto out;
1617 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1618 if (!wkn_dn_exp) {
1619 goto out;
1622 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1623 if (!bind_dn_exp) {
1624 goto out;
1627 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1629 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1632 new_ln = wkn_ln - bind_ln;
1634 ret = SMB_STRDUP(wkn_dn_exp[0]);
1635 if (!ret) {
1636 goto out;
1639 for (i=1; i < new_ln; i++) {
1640 char *s = NULL;
1642 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1643 SAFE_FREE(ret);
1644 goto out;
1647 SAFE_FREE(ret);
1648 ret = SMB_STRDUP(s);
1649 free(s);
1650 if (!ret) {
1651 goto out;
1655 out:
1656 SAFE_FREE(base);
1657 ads_msgfree(ads, res);
1658 ads_memfree(ads, wkn_dn);
1659 if (wkn_dn_exp) {
1660 ldap_value_free(wkn_dn_exp);
1662 if (bind_dn_exp) {
1663 ldap_value_free(bind_dn_exp);
1666 return ret;
1670 * Adds (appends) an item to an attribute array, rather then
1671 * replacing the whole list
1672 * @param ctx An initialized TALLOC_CTX
1673 * @param mods An initialized ADS_MODLIST
1674 * @param name name of the ldap attribute to append to
1675 * @param vals an array of values to add
1676 * @return status of addition
1679 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1680 const char *name, const char **vals)
1682 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1683 (const void *) vals);
1687 * Determines the an account's current KVNO via an LDAP lookup
1688 * @param ads An initialized ADS_STRUCT
1689 * @param account_name the NT samaccountname.
1690 * @return the kvno for the account, or -1 in case of a failure.
1693 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1695 LDAPMessage *res = NULL;
1696 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1697 char *filter;
1698 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1699 char *dn_string = NULL;
1700 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1702 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1703 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1704 return kvno;
1706 ret = ads_search(ads, &res, filter, attrs);
1707 SAFE_FREE(filter);
1708 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1709 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1710 ads_msgfree(ads, res);
1711 return kvno;
1714 dn_string = ads_get_dn(ads, res);
1715 if (!dn_string) {
1716 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1717 ads_msgfree(ads, res);
1718 return kvno;
1720 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1721 ads_memfree(ads, dn_string);
1723 /* ---------------------------------------------------------
1724 * 0 is returned as a default KVNO from this point on...
1725 * This is done because Windows 2000 does not support key
1726 * version numbers. Chances are that a failure in the next
1727 * step is simply due to Windows 2000 being used for a
1728 * domain controller. */
1729 kvno = 0;
1731 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1732 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1733 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1734 ads_msgfree(ads, res);
1735 return kvno;
1738 /* Success */
1739 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1740 ads_msgfree(ads, res);
1741 return kvno;
1745 * Determines the computer account's current KVNO via an LDAP lookup
1746 * @param ads An initialized ADS_STRUCT
1747 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1748 * @return the kvno for the computer account, or -1 in case of a failure.
1751 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1753 char *computer_account = NULL;
1754 uint32_t kvno = -1;
1756 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1757 return kvno;
1760 kvno = ads_get_kvno(ads, computer_account);
1761 free(computer_account);
1763 return kvno;
1767 * This clears out all registered spn's for a given hostname
1768 * @param ads An initilaized ADS_STRUCT
1769 * @param machine_name the NetBIOS name of the computer.
1770 * @return 0 upon success, non-zero otherwise.
1773 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1775 TALLOC_CTX *ctx;
1776 LDAPMessage *res = NULL;
1777 ADS_MODLIST mods;
1778 const char *servicePrincipalName[1] = {NULL};
1779 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1780 char *dn_string = NULL;
1782 ret = ads_find_machine_acct(ads, &res, machine_name);
1783 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1784 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1785 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1786 ads_msgfree(ads, res);
1787 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1790 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1791 ctx = talloc_init("ads_clear_service_principal_names");
1792 if (!ctx) {
1793 ads_msgfree(ads, res);
1794 return ADS_ERROR(LDAP_NO_MEMORY);
1797 if (!(mods = ads_init_mods(ctx))) {
1798 talloc_destroy(ctx);
1799 ads_msgfree(ads, res);
1800 return ADS_ERROR(LDAP_NO_MEMORY);
1802 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1803 if (!ADS_ERR_OK(ret)) {
1804 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1805 ads_msgfree(ads, res);
1806 talloc_destroy(ctx);
1807 return ret;
1809 dn_string = ads_get_dn(ads, res);
1810 if (!dn_string) {
1811 talloc_destroy(ctx);
1812 ads_msgfree(ads, res);
1813 return ADS_ERROR(LDAP_NO_MEMORY);
1815 ret = ads_gen_mod(ads, dn_string, mods);
1816 ads_memfree(ads,dn_string);
1817 if (!ADS_ERR_OK(ret)) {
1818 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1819 machine_name));
1820 ads_msgfree(ads, res);
1821 talloc_destroy(ctx);
1822 return ret;
1825 ads_msgfree(ads, res);
1826 talloc_destroy(ctx);
1827 return ret;
1831 * This adds a service principal name to an existing computer account
1832 * (found by hostname) in AD.
1833 * @param ads An initialized ADS_STRUCT
1834 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1835 * @param my_fqdn The fully qualified DNS name of the machine
1836 * @param spn A string of the service principal to add, i.e. 'host'
1837 * @return 0 upon sucess, or non-zero if a failure occurs
1840 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1841 const char *my_fqdn, const char *spn)
1843 ADS_STATUS ret;
1844 TALLOC_CTX *ctx;
1845 LDAPMessage *res = NULL;
1846 char *psp1, *psp2;
1847 ADS_MODLIST mods;
1848 char *dn_string = NULL;
1849 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1851 ret = ads_find_machine_acct(ads, &res, machine_name);
1852 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1853 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1854 machine_name));
1855 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1856 spn, machine_name, ads->config.realm));
1857 ads_msgfree(ads, res);
1858 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1861 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1862 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1863 ads_msgfree(ads, res);
1864 return ADS_ERROR(LDAP_NO_MEMORY);
1867 /* add short name spn */
1869 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1870 talloc_destroy(ctx);
1871 ads_msgfree(ads, res);
1872 return ADS_ERROR(LDAP_NO_MEMORY);
1874 strupper_m(psp1);
1875 strlower_m(&psp1[strlen(spn)]);
1876 servicePrincipalName[0] = psp1;
1878 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1879 psp1, machine_name));
1882 /* add fully qualified spn */
1884 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1885 ret = ADS_ERROR(LDAP_NO_MEMORY);
1886 goto out;
1888 strupper_m(psp2);
1889 strlower_m(&psp2[strlen(spn)]);
1890 servicePrincipalName[1] = psp2;
1892 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1893 psp2, machine_name));
1895 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1896 ret = ADS_ERROR(LDAP_NO_MEMORY);
1897 goto out;
1900 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1901 if (!ADS_ERR_OK(ret)) {
1902 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1903 goto out;
1906 if ( (dn_string = ads_get_dn(ads, res)) == NULL ) {
1907 ret = ADS_ERROR(LDAP_NO_MEMORY);
1908 goto out;
1911 ret = ads_gen_mod(ads, dn_string, mods);
1912 ads_memfree(ads,dn_string);
1913 if (!ADS_ERR_OK(ret)) {
1914 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1915 goto out;
1918 out:
1919 TALLOC_FREE( ctx );
1920 ads_msgfree(ads, res);
1921 return ret;
1925 * adds a machine account to the ADS server
1926 * @param ads An intialized ADS_STRUCT
1927 * @param machine_name - the NetBIOS machine name of this account.
1928 * @param account_type A number indicating the type of account to create
1929 * @param org_unit The LDAP path in which to place this account
1930 * @return 0 upon success, or non-zero otherwise
1933 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1934 const char *org_unit)
1936 ADS_STATUS ret;
1937 char *samAccountName, *controlstr;
1938 TALLOC_CTX *ctx;
1939 ADS_MODLIST mods;
1940 char *machine_escaped = NULL;
1941 char *new_dn;
1942 const char *objectClass[] = {"top", "person", "organizationalPerson",
1943 "user", "computer", NULL};
1944 LDAPMessage *res = NULL;
1945 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1946 UF_DONT_EXPIRE_PASSWD |\
1947 UF_ACCOUNTDISABLE );
1949 if (!(ctx = talloc_init("ads_add_machine_acct")))
1950 return ADS_ERROR(LDAP_NO_MEMORY);
1952 ret = ADS_ERROR(LDAP_NO_MEMORY);
1954 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1955 if (!machine_escaped) {
1956 goto done;
1959 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
1960 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
1962 if ( !new_dn || !samAccountName ) {
1963 goto done;
1966 #ifndef ENCTYPE_ARCFOUR_HMAC
1967 acct_control |= UF_USE_DES_KEY_ONLY;
1968 #endif
1970 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
1971 goto done;
1974 if (!(mods = ads_init_mods(ctx))) {
1975 goto done;
1978 ads_mod_str(ctx, &mods, "cn", machine_name);
1979 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
1980 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
1981 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
1983 ret = ads_gen_add(ads, new_dn, mods);
1985 done:
1986 SAFE_FREE(machine_escaped);
1987 ads_msgfree(ads, res);
1988 talloc_destroy(ctx);
1990 return ret;
1994 * move a machine account to another OU on the ADS server
1995 * @param ads - An intialized ADS_STRUCT
1996 * @param machine_name - the NetBIOS machine name of this account.
1997 * @param org_unit - The LDAP path in which to place this account
1998 * @param moved - whether we moved the machine account (optional)
1999 * @return 0 upon success, or non-zero otherwise
2002 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2003 const char *org_unit, bool *moved)
2005 ADS_STATUS rc;
2006 int ldap_status;
2007 LDAPMessage *res = NULL;
2008 char *filter = NULL;
2009 char *computer_dn = NULL;
2010 char *parent_dn;
2011 char *computer_rdn = NULL;
2012 bool need_move = False;
2014 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2015 rc = ADS_ERROR(LDAP_NO_MEMORY);
2016 goto done;
2019 /* Find pre-existing machine */
2020 rc = ads_search(ads, &res, filter, NULL);
2021 if (!ADS_ERR_OK(rc)) {
2022 goto done;
2025 computer_dn = ads_get_dn(ads, res);
2026 if (!computer_dn) {
2027 rc = ADS_ERROR(LDAP_NO_MEMORY);
2028 goto done;
2031 parent_dn = ads_parent_dn(computer_dn);
2032 if (strequal(parent_dn, org_unit)) {
2033 goto done;
2036 need_move = True;
2038 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2039 rc = ADS_ERROR(LDAP_NO_MEMORY);
2040 goto done;
2043 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2044 org_unit, 1, NULL, NULL);
2045 rc = ADS_ERROR(ldap_status);
2047 done:
2048 ads_msgfree(ads, res);
2049 SAFE_FREE(filter);
2050 SAFE_FREE(computer_dn);
2051 SAFE_FREE(computer_rdn);
2053 if (!ADS_ERR_OK(rc)) {
2054 need_move = False;
2057 if (moved) {
2058 *moved = need_move;
2061 return rc;
2065 dump a binary result from ldap
2067 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2069 int i, j;
2070 for (i=0; values[i]; i++) {
2071 printf("%s: ", field);
2072 for (j=0; j<values[i]->bv_len; j++) {
2073 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2075 printf("\n");
2079 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2081 int i;
2082 for (i=0; values[i]; i++) {
2084 UUID_FLAT guid;
2085 struct GUID tmp;
2087 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2088 smb_uuid_unpack(guid, &tmp);
2089 printf("%s: %s\n", field, smb_uuid_string(talloc_tos(), tmp));
2094 dump a sid result from ldap
2096 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2098 int i;
2099 for (i=0; values[i]; i++) {
2100 DOM_SID sid;
2101 fstring tmp;
2102 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2103 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2108 dump ntSecurityDescriptor
2110 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2112 TALLOC_CTX *frame = talloc_stackframe();
2113 struct security_descriptor *psd;
2114 NTSTATUS status;
2116 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2117 values[0]->bv_len, &psd);
2118 if (!NT_STATUS_IS_OK(status)) {
2119 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2120 nt_errstr(status)));
2121 TALLOC_FREE(frame);
2122 return;
2125 if (psd) {
2126 ads_disp_sd(ads, talloc_tos(), psd);
2129 TALLOC_FREE(frame);
2133 dump a string result from ldap
2135 static void dump_string(const char *field, char **values)
2137 int i;
2138 for (i=0; values[i]; i++) {
2139 printf("%s: %s\n", field, values[i]);
2144 dump a field from LDAP on stdout
2145 used for debugging
2148 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2150 const struct {
2151 const char *name;
2152 bool string;
2153 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2154 } handlers[] = {
2155 {"objectGUID", False, dump_guid},
2156 {"netbootGUID", False, dump_guid},
2157 {"nTSecurityDescriptor", False, dump_sd},
2158 {"dnsRecord", False, dump_binary},
2159 {"objectSid", False, dump_sid},
2160 {"tokenGroups", False, dump_sid},
2161 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2162 {"tokengroupsGlobalandUniversal", False, dump_sid},
2163 {"mS-DS-CreatorSID", False, dump_sid},
2164 {"msExchMailboxGuid", False, dump_guid},
2165 {NULL, True, NULL}
2167 int i;
2169 if (!field) { /* must be end of an entry */
2170 printf("\n");
2171 return False;
2174 for (i=0; handlers[i].name; i++) {
2175 if (StrCaseCmp(handlers[i].name, field) == 0) {
2176 if (!values) /* first time, indicate string or not */
2177 return handlers[i].string;
2178 handlers[i].handler(ads, field, (struct berval **) values);
2179 break;
2182 if (!handlers[i].name) {
2183 if (!values) /* first time, indicate string conversion */
2184 return True;
2185 dump_string(field, (char **)values);
2187 return False;
2191 * Dump a result from LDAP on stdout
2192 * used for debugging
2193 * @param ads connection to ads server
2194 * @param res Results to dump
2197 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2199 ads_process_results(ads, res, ads_dump_field, NULL);
2203 * Walk through results, calling a function for each entry found.
2204 * The function receives a field name, a berval * array of values,
2205 * and a data area passed through from the start. The function is
2206 * called once with null for field and values at the end of each
2207 * entry.
2208 * @param ads connection to ads server
2209 * @param res Results to process
2210 * @param fn Function for processing each result
2211 * @param data_area user-defined area to pass to function
2213 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2214 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2215 void *data_area)
2217 LDAPMessage *msg;
2218 TALLOC_CTX *ctx;
2219 size_t converted_size;
2221 if (!(ctx = talloc_init("ads_process_results")))
2222 return;
2224 for (msg = ads_first_entry(ads, res); msg;
2225 msg = ads_next_entry(ads, msg)) {
2226 char *utf8_field;
2227 BerElement *b;
2229 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2230 (LDAPMessage *)msg,&b);
2231 utf8_field;
2232 utf8_field=ldap_next_attribute(ads->ldap.ld,
2233 (LDAPMessage *)msg,b)) {
2234 struct berval **ber_vals;
2235 char **str_vals, **utf8_vals;
2236 char *field;
2237 bool string;
2239 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2240 &converted_size))
2242 DEBUG(0,("ads_process_results: "
2243 "pull_utf8_talloc failed: %s",
2244 strerror(errno)));
2247 string = fn(ads, field, NULL, data_area);
2249 if (string) {
2250 utf8_vals = ldap_get_values(ads->ldap.ld,
2251 (LDAPMessage *)msg, field);
2252 str_vals = ads_pull_strvals(ctx,
2253 (const char **) utf8_vals);
2254 fn(ads, field, (void **) str_vals, data_area);
2255 ldap_value_free(utf8_vals);
2256 } else {
2257 ber_vals = ldap_get_values_len(ads->ldap.ld,
2258 (LDAPMessage *)msg, field);
2259 fn(ads, field, (void **) ber_vals, data_area);
2261 ldap_value_free_len(ber_vals);
2263 ldap_memfree(utf8_field);
2265 ber_free(b, 0);
2266 talloc_free_children(ctx);
2267 fn(ads, NULL, NULL, data_area); /* completed an entry */
2270 talloc_destroy(ctx);
2274 * count how many replies are in a LDAPMessage
2275 * @param ads connection to ads server
2276 * @param res Results to count
2277 * @return number of replies
2279 int ads_count_replies(ADS_STRUCT *ads, void *res)
2281 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2285 * pull the first entry from a ADS result
2286 * @param ads connection to ads server
2287 * @param res Results of search
2288 * @return first entry from result
2290 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2292 return ldap_first_entry(ads->ldap.ld, res);
2296 * pull the next entry from a ADS result
2297 * @param ads connection to ads server
2298 * @param res Results of search
2299 * @return next entry from result
2301 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2303 return ldap_next_entry(ads->ldap.ld, res);
2307 * pull the first message from a ADS result
2308 * @param ads connection to ads server
2309 * @param res Results of search
2310 * @return first message from result
2312 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2314 return ldap_first_message(ads->ldap.ld, res);
2318 * pull the next message from a ADS result
2319 * @param ads connection to ads server
2320 * @param res Results of search
2321 * @return next message from result
2323 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2325 return ldap_next_message(ads->ldap.ld, res);
2329 * pull a single string from a ADS result
2330 * @param ads connection to ads server
2331 * @param mem_ctx TALLOC_CTX to use for allocating result string
2332 * @param msg Results of search
2333 * @param field Attribute to retrieve
2334 * @return Result string in talloc context
2336 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2337 const char *field)
2339 char **values;
2340 char *ret = NULL;
2341 char *ux_string;
2342 size_t converted_size;
2344 values = ldap_get_values(ads->ldap.ld, msg, field);
2345 if (!values)
2346 return NULL;
2348 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2349 &converted_size))
2351 ret = ux_string;
2353 ldap_value_free(values);
2354 return ret;
2358 * pull an array of strings from a ADS result
2359 * @param ads connection to ads server
2360 * @param mem_ctx TALLOC_CTX to use for allocating result string
2361 * @param msg Results of search
2362 * @param field Attribute to retrieve
2363 * @return Result strings in talloc context
2365 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2366 LDAPMessage *msg, const char *field,
2367 size_t *num_values)
2369 char **values;
2370 char **ret = NULL;
2371 int i;
2372 size_t converted_size;
2374 values = ldap_get_values(ads->ldap.ld, msg, field);
2375 if (!values)
2376 return NULL;
2378 *num_values = ldap_count_values(values);
2380 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2381 if (!ret) {
2382 ldap_value_free(values);
2383 return NULL;
2386 for (i=0;i<*num_values;i++) {
2387 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2388 &converted_size))
2390 ldap_value_free(values);
2391 return NULL;
2394 ret[i] = NULL;
2396 ldap_value_free(values);
2397 return ret;
2401 * pull an array of strings from a ADS result
2402 * (handle large multivalue attributes with range retrieval)
2403 * @param ads connection to ads server
2404 * @param mem_ctx TALLOC_CTX to use for allocating result string
2405 * @param msg Results of search
2406 * @param field Attribute to retrieve
2407 * @param current_strings strings returned by a previous call to this function
2408 * @param next_attribute The next query should ask for this attribute
2409 * @param num_values How many values did we get this time?
2410 * @param more_values Are there more values to get?
2411 * @return Result strings in talloc context
2413 char **ads_pull_strings_range(ADS_STRUCT *ads,
2414 TALLOC_CTX *mem_ctx,
2415 LDAPMessage *msg, const char *field,
2416 char **current_strings,
2417 const char **next_attribute,
2418 size_t *num_strings,
2419 bool *more_strings)
2421 char *attr;
2422 char *expected_range_attrib, *range_attr;
2423 BerElement *ptr = NULL;
2424 char **strings;
2425 char **new_strings;
2426 size_t num_new_strings;
2427 unsigned long int range_start;
2428 unsigned long int range_end;
2430 /* we might have been given the whole lot anyway */
2431 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2432 *more_strings = False;
2433 return strings;
2436 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2438 /* look for Range result */
2439 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2440 attr;
2441 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2442 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2443 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2444 range_attr = attr;
2445 break;
2447 ldap_memfree(attr);
2449 if (!attr) {
2450 ber_free(ptr, 0);
2451 /* nothing here - this field is just empty */
2452 *more_strings = False;
2453 return NULL;
2456 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2457 &range_start, &range_end) == 2) {
2458 *more_strings = True;
2459 } else {
2460 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2461 &range_start) == 1) {
2462 *more_strings = False;
2463 } else {
2464 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2465 range_attr));
2466 ldap_memfree(range_attr);
2467 *more_strings = False;
2468 return NULL;
2472 if ((*num_strings) != range_start) {
2473 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2474 " - aborting range retreival\n",
2475 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2476 ldap_memfree(range_attr);
2477 *more_strings = False;
2478 return NULL;
2481 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2483 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2484 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2485 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2486 range_attr, (unsigned long int)range_end - range_start + 1,
2487 (unsigned long int)num_new_strings));
2488 ldap_memfree(range_attr);
2489 *more_strings = False;
2490 return NULL;
2493 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2494 *num_strings + num_new_strings);
2496 if (strings == NULL) {
2497 ldap_memfree(range_attr);
2498 *more_strings = False;
2499 return NULL;
2502 if (new_strings && num_new_strings) {
2503 memcpy(&strings[*num_strings], new_strings,
2504 sizeof(*new_strings) * num_new_strings);
2507 (*num_strings) += num_new_strings;
2509 if (*more_strings) {
2510 *next_attribute = talloc_asprintf(mem_ctx,
2511 "%s;range=%d-*",
2512 field,
2513 (int)*num_strings);
2515 if (!*next_attribute) {
2516 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2517 ldap_memfree(range_attr);
2518 *more_strings = False;
2519 return NULL;
2523 ldap_memfree(range_attr);
2525 return strings;
2529 * pull a single uint32 from a ADS result
2530 * @param ads connection to ads server
2531 * @param msg Results of search
2532 * @param field Attribute to retrieve
2533 * @param v Pointer to int to store result
2534 * @return boolean inidicating success
2536 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2537 uint32 *v)
2539 char **values;
2541 values = ldap_get_values(ads->ldap.ld, msg, field);
2542 if (!values)
2543 return False;
2544 if (!values[0]) {
2545 ldap_value_free(values);
2546 return False;
2549 *v = atoi(values[0]);
2550 ldap_value_free(values);
2551 return True;
2555 * pull a single objectGUID from an ADS result
2556 * @param ads connection to ADS server
2557 * @param msg results of search
2558 * @param guid 37-byte area to receive text guid
2559 * @return boolean indicating success
2561 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2563 char **values;
2564 UUID_FLAT flat_guid;
2566 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2567 if (!values)
2568 return False;
2570 if (values[0]) {
2571 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2572 smb_uuid_unpack(flat_guid, guid);
2573 ldap_value_free(values);
2574 return True;
2576 ldap_value_free(values);
2577 return False;
2583 * pull a single DOM_SID from a ADS result
2584 * @param ads connection to ads server
2585 * @param msg Results of search
2586 * @param field Attribute to retrieve
2587 * @param sid Pointer to sid to store result
2588 * @return boolean inidicating success
2590 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2591 DOM_SID *sid)
2593 struct berval **values;
2594 bool ret = False;
2596 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2598 if (!values)
2599 return False;
2601 if (values[0])
2602 ret = sid_parse(values[0]->bv_val, values[0]->bv_len, sid);
2604 ldap_value_free_len(values);
2605 return ret;
2609 * pull an array of DOM_SIDs from a ADS result
2610 * @param ads connection to ads server
2611 * @param mem_ctx TALLOC_CTX for allocating sid array
2612 * @param msg Results of search
2613 * @param field Attribute to retrieve
2614 * @param sids pointer to sid array to allocate
2615 * @return the count of SIDs pulled
2617 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2618 LDAPMessage *msg, const char *field, DOM_SID **sids)
2620 struct berval **values;
2621 bool ret;
2622 int count, i;
2624 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2626 if (!values)
2627 return 0;
2629 for (i=0; values[i]; i++)
2630 /* nop */ ;
2632 if (i) {
2633 (*sids) = TALLOC_ARRAY(mem_ctx, DOM_SID, i);
2634 if (!(*sids)) {
2635 ldap_value_free_len(values);
2636 return 0;
2638 } else {
2639 (*sids) = NULL;
2642 count = 0;
2643 for (i=0; values[i]; i++) {
2644 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2645 if (ret) {
2646 DEBUG(10, ("pulling SID: %s\n",
2647 sid_string_dbg(&(*sids)[count])));
2648 count++;
2652 ldap_value_free_len(values);
2653 return count;
2657 * pull a SEC_DESC from a ADS result
2658 * @param ads connection to ads server
2659 * @param mem_ctx TALLOC_CTX for allocating sid array
2660 * @param msg Results of search
2661 * @param field Attribute to retrieve
2662 * @param sd Pointer to *SEC_DESC to store result (talloc()ed)
2663 * @return boolean inidicating success
2665 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2666 LDAPMessage *msg, const char *field, SEC_DESC **sd)
2668 struct berval **values;
2669 bool ret = true;
2671 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2673 if (!values) return false;
2675 if (values[0]) {
2676 NTSTATUS status;
2677 status = unmarshall_sec_desc(mem_ctx,
2678 (uint8 *)values[0]->bv_val,
2679 values[0]->bv_len, sd);
2680 if (!NT_STATUS_IS_OK(status)) {
2681 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2682 nt_errstr(status)));
2683 ret = false;
2687 ldap_value_free_len(values);
2688 return ret;
2692 * in order to support usernames longer than 21 characters we need to
2693 * use both the sAMAccountName and the userPrincipalName attributes
2694 * It seems that not all users have the userPrincipalName attribute set
2696 * @param ads connection to ads server
2697 * @param mem_ctx TALLOC_CTX for allocating sid array
2698 * @param msg Results of search
2699 * @return the username
2701 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2702 LDAPMessage *msg)
2704 #if 0 /* JERRY */
2705 char *ret, *p;
2707 /* lookup_name() only works on the sAMAccountName to
2708 returning the username portion of userPrincipalName
2709 breaks winbindd_getpwnam() */
2711 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2712 if (ret && (p = strchr_m(ret, '@'))) {
2713 *p = 0;
2714 return ret;
2716 #endif
2717 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2722 * find the update serial number - this is the core of the ldap cache
2723 * @param ads connection to ads server
2724 * @param ads connection to ADS server
2725 * @param usn Pointer to retrieved update serial number
2726 * @return status of search
2728 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2730 const char *attrs[] = {"highestCommittedUSN", NULL};
2731 ADS_STATUS status;
2732 LDAPMessage *res;
2734 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2735 if (!ADS_ERR_OK(status))
2736 return status;
2738 if (ads_count_replies(ads, res) != 1) {
2739 ads_msgfree(ads, res);
2740 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2743 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2744 ads_msgfree(ads, res);
2745 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2748 ads_msgfree(ads, res);
2749 return ADS_SUCCESS;
2752 /* parse a ADS timestring - typical string is
2753 '20020917091222.0Z0' which means 09:12.22 17th September
2754 2002, timezone 0 */
2755 static time_t ads_parse_time(const char *str)
2757 struct tm tm;
2759 ZERO_STRUCT(tm);
2761 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2762 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2763 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2764 return 0;
2766 tm.tm_year -= 1900;
2767 tm.tm_mon -= 1;
2769 return timegm(&tm);
2772 /********************************************************************
2773 ********************************************************************/
2775 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2777 const char *attrs[] = {"currentTime", NULL};
2778 ADS_STATUS status;
2779 LDAPMessage *res;
2780 char *timestr;
2781 TALLOC_CTX *ctx;
2782 ADS_STRUCT *ads_s = ads;
2784 if (!(ctx = talloc_init("ads_current_time"))) {
2785 return ADS_ERROR(LDAP_NO_MEMORY);
2788 /* establish a new ldap tcp session if necessary */
2790 if ( !ads->ldap.ld ) {
2791 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2792 ads->server.ldap_server )) == NULL )
2794 goto done;
2796 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2797 status = ads_connect( ads_s );
2798 if ( !ADS_ERR_OK(status))
2799 goto done;
2802 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2803 if (!ADS_ERR_OK(status)) {
2804 goto done;
2807 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2808 if (!timestr) {
2809 ads_msgfree(ads_s, res);
2810 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2811 goto done;
2814 /* but save the time and offset in the original ADS_STRUCT */
2816 ads->config.current_time = ads_parse_time(timestr);
2818 if (ads->config.current_time != 0) {
2819 ads->auth.time_offset = ads->config.current_time - time(NULL);
2820 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2823 ads_msgfree(ads, res);
2825 status = ADS_SUCCESS;
2827 done:
2828 /* free any temporary ads connections */
2829 if ( ads_s != ads ) {
2830 ads_destroy( &ads_s );
2832 talloc_destroy(ctx);
2834 return status;
2837 /********************************************************************
2838 ********************************************************************/
2840 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2842 const char *attrs[] = {"domainFunctionality", NULL};
2843 ADS_STATUS status;
2844 LDAPMessage *res;
2845 ADS_STRUCT *ads_s = ads;
2847 *val = DS_DOMAIN_FUNCTION_2000;
2849 /* establish a new ldap tcp session if necessary */
2851 if ( !ads->ldap.ld ) {
2852 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2853 ads->server.ldap_server )) == NULL )
2855 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2856 goto done;
2858 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2859 status = ads_connect( ads_s );
2860 if ( !ADS_ERR_OK(status))
2861 goto done;
2864 /* If the attribute does not exist assume it is a Windows 2000
2865 functional domain */
2867 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2868 if (!ADS_ERR_OK(status)) {
2869 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2870 status = ADS_SUCCESS;
2872 goto done;
2875 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2876 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2878 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2881 ads_msgfree(ads, res);
2883 done:
2884 /* free any temporary ads connections */
2885 if ( ads_s != ads ) {
2886 ads_destroy( &ads_s );
2889 return status;
2893 * find the domain sid for our domain
2894 * @param ads connection to ads server
2895 * @param sid Pointer to domain sid
2896 * @return status of search
2898 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, DOM_SID *sid)
2900 const char *attrs[] = {"objectSid", NULL};
2901 LDAPMessage *res;
2902 ADS_STATUS rc;
2904 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2905 attrs, &res);
2906 if (!ADS_ERR_OK(rc)) return rc;
2907 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2908 ads_msgfree(ads, res);
2909 return ADS_ERROR_SYSTEM(ENOENT);
2911 ads_msgfree(ads, res);
2913 return ADS_SUCCESS;
2917 * find our site name
2918 * @param ads connection to ads server
2919 * @param mem_ctx Pointer to talloc context
2920 * @param site_name Pointer to the sitename
2921 * @return status of search
2923 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2925 ADS_STATUS status;
2926 LDAPMessage *res;
2927 const char *dn, *service_name;
2928 const char *attrs[] = { "dsServiceName", NULL };
2930 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2931 if (!ADS_ERR_OK(status)) {
2932 return status;
2935 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2936 if (service_name == NULL) {
2937 ads_msgfree(ads, res);
2938 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2941 ads_msgfree(ads, res);
2943 /* go up three levels */
2944 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2945 if (dn == NULL) {
2946 return ADS_ERROR(LDAP_NO_MEMORY);
2949 *site_name = talloc_strdup(mem_ctx, dn);
2950 if (*site_name == NULL) {
2951 return ADS_ERROR(LDAP_NO_MEMORY);
2954 return status;
2956 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2961 * find the site dn where a machine resides
2962 * @param ads connection to ads server
2963 * @param mem_ctx Pointer to talloc context
2964 * @param computer_name name of the machine
2965 * @param site_name Pointer to the sitename
2966 * @return status of search
2968 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
2970 ADS_STATUS status;
2971 LDAPMessage *res;
2972 const char *parent, *filter;
2973 char *config_context = NULL;
2974 char *dn;
2976 /* shortcut a query */
2977 if (strequal(computer_name, ads->config.ldap_server_name)) {
2978 return ads_site_dn(ads, mem_ctx, site_dn);
2981 status = ads_config_path(ads, mem_ctx, &config_context);
2982 if (!ADS_ERR_OK(status)) {
2983 return status;
2986 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
2987 if (filter == NULL) {
2988 return ADS_ERROR(LDAP_NO_MEMORY);
2991 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
2992 filter, NULL, &res);
2993 if (!ADS_ERR_OK(status)) {
2994 return status;
2997 if (ads_count_replies(ads, res) != 1) {
2998 ads_msgfree(ads, res);
2999 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3002 dn = ads_get_dn(ads, res);
3003 if (dn == NULL) {
3004 ads_msgfree(ads, res);
3005 return ADS_ERROR(LDAP_NO_MEMORY);
3008 /* go up three levels */
3009 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3010 if (parent == NULL) {
3011 ads_msgfree(ads, res);
3012 ads_memfree(ads, dn);
3013 return ADS_ERROR(LDAP_NO_MEMORY);
3016 *site_dn = talloc_strdup(mem_ctx, parent);
3017 if (*site_dn == NULL) {
3018 ads_msgfree(ads, res);
3019 ads_memfree(ads, dn);
3020 return ADS_ERROR(LDAP_NO_MEMORY);
3023 ads_memfree(ads, dn);
3024 ads_msgfree(ads, res);
3026 return status;
3030 * get the upn suffixes for a domain
3031 * @param ads connection to ads server
3032 * @param mem_ctx Pointer to talloc context
3033 * @param suffixes Pointer to an array of suffixes
3034 * @param num_suffixes Pointer to the number of suffixes
3035 * @return status of search
3037 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3039 ADS_STATUS status;
3040 LDAPMessage *res;
3041 const char *base;
3042 char *config_context = NULL;
3043 const char *attrs[] = { "uPNSuffixes", NULL };
3045 status = ads_config_path(ads, mem_ctx, &config_context);
3046 if (!ADS_ERR_OK(status)) {
3047 return status;
3050 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3051 if (base == NULL) {
3052 return ADS_ERROR(LDAP_NO_MEMORY);
3055 status = ads_search_dn(ads, &res, base, attrs);
3056 if (!ADS_ERR_OK(status)) {
3057 return status;
3060 if (ads_count_replies(ads, res) != 1) {
3061 ads_msgfree(ads, res);
3062 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3065 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3066 if ((*suffixes) == NULL) {
3067 ads_msgfree(ads, res);
3068 return ADS_ERROR(LDAP_NO_MEMORY);
3071 ads_msgfree(ads, res);
3073 return status;
3077 * get the joinable ous for a domain
3078 * @param ads connection to ads server
3079 * @param mem_ctx Pointer to talloc context
3080 * @param ous Pointer to an array of ous
3081 * @param num_ous Pointer to the number of ous
3082 * @return status of search
3084 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3085 TALLOC_CTX *mem_ctx,
3086 char ***ous,
3087 size_t *num_ous)
3089 ADS_STATUS status;
3090 LDAPMessage *res = NULL;
3091 LDAPMessage *msg = NULL;
3092 const char *attrs[] = { "dn", NULL };
3093 int count = 0;
3095 status = ads_search(ads, &res,
3096 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3097 attrs);
3098 if (!ADS_ERR_OK(status)) {
3099 return status;
3102 count = ads_count_replies(ads, res);
3103 if (count < 1) {
3104 ads_msgfree(ads, res);
3105 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3108 for (msg = ads_first_entry(ads, res); msg;
3109 msg = ads_next_entry(ads, msg)) {
3111 char *dn = NULL;
3113 dn = ads_get_dn(ads, msg);
3114 if (!dn) {
3115 ads_msgfree(ads, res);
3116 return ADS_ERROR(LDAP_NO_MEMORY);
3119 if (!add_string_to_array(mem_ctx, dn,
3120 (const char ***)ous,
3121 (int *)num_ous)) {
3122 ads_memfree(ads, dn);
3123 ads_msgfree(ads, res);
3124 return ADS_ERROR(LDAP_NO_MEMORY);
3127 ads_memfree(ads, dn);
3130 ads_msgfree(ads, res);
3132 return status;
3137 * pull a DOM_SID from an extended dn string
3138 * @param mem_ctx TALLOC_CTX
3139 * @param extended_dn string
3140 * @param flags string type of extended_dn
3141 * @param sid pointer to a DOM_SID
3142 * @return NT_STATUS_OK on success,
3143 * NT_INVALID_PARAMETER on error,
3144 * NT_STATUS_NOT_FOUND if no SID present
3146 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3147 const char *extended_dn,
3148 enum ads_extended_dn_flags flags,
3149 DOM_SID *sid)
3151 char *p, *q, *dn;
3153 if (!extended_dn) {
3154 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3157 /* otherwise extended_dn gets stripped off */
3158 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3159 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3162 * ADS_EXTENDED_DN_HEX_STRING:
3163 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3165 * ADS_EXTENDED_DN_STRING (only with w2k3):
3166 * <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
3168 * Object with no SID, such as an Exchange Public Folder
3169 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3172 p = strchr(dn, ';');
3173 if (!p) {
3174 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3177 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3178 DEBUG(5,("No SID present in extended dn\n"));
3179 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3182 p += strlen(";<SID=");
3184 q = strchr(p, '>');
3185 if (!q) {
3186 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3189 *q = '\0';
3191 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3193 switch (flags) {
3195 case ADS_EXTENDED_DN_STRING:
3196 if (!string_to_sid(sid, p)) {
3197 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3199 break;
3200 case ADS_EXTENDED_DN_HEX_STRING: {
3201 fstring buf;
3202 size_t buf_len;
3204 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3205 if (buf_len == 0) {
3206 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3209 if (!sid_parse(buf, buf_len, sid)) {
3210 DEBUG(10,("failed to parse sid\n"));
3211 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3213 break;
3215 default:
3216 DEBUG(10,("unknown extended dn format\n"));
3217 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3220 return ADS_ERROR_NT(NT_STATUS_OK);
3224 * pull an array of DOM_SIDs from a ADS result
3225 * @param ads connection to ads server
3226 * @param mem_ctx TALLOC_CTX for allocating sid array
3227 * @param msg Results of search
3228 * @param field Attribute to retrieve
3229 * @param flags string type of extended_dn
3230 * @param sids pointer to sid array to allocate
3231 * @return the count of SIDs pulled
3233 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3234 TALLOC_CTX *mem_ctx,
3235 LDAPMessage *msg,
3236 const char *field,
3237 enum ads_extended_dn_flags flags,
3238 DOM_SID **sids)
3240 int i;
3241 ADS_STATUS rc;
3242 size_t dn_count, ret_count = 0;
3243 char **dn_strings;
3245 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3246 &dn_count)) == NULL) {
3247 return 0;
3250 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, DOM_SID, dn_count + 1);
3251 if (!(*sids)) {
3252 TALLOC_FREE(dn_strings);
3253 return 0;
3256 for (i=0; i<dn_count; i++) {
3257 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3258 flags, &(*sids)[i]);
3259 if (!ADS_ERR_OK(rc)) {
3260 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3261 NT_STATUS_NOT_FOUND)) {
3262 continue;
3264 else {
3265 TALLOC_FREE(*sids);
3266 TALLOC_FREE(dn_strings);
3267 return 0;
3270 ret_count++;
3273 TALLOC_FREE(dn_strings);
3275 return ret_count;
3278 /********************************************************************
3279 ********************************************************************/
3281 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3283 LDAPMessage *res = NULL;
3284 ADS_STATUS status;
3285 int count = 0;
3286 char *name = NULL;
3288 status = ads_find_machine_acct(ads, &res, global_myname());
3289 if (!ADS_ERR_OK(status)) {
3290 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3291 global_myname()));
3292 goto out;
3295 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3296 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3297 goto out;
3300 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3301 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3304 out:
3305 ads_msgfree(ads, res);
3307 return name;
3310 /********************************************************************
3311 ********************************************************************/
3313 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3315 LDAPMessage *res = NULL;
3316 ADS_STATUS status;
3317 int count = 0;
3318 char *name = NULL;
3320 status = ads_find_machine_acct(ads, &res, machine_name);
3321 if (!ADS_ERR_OK(status)) {
3322 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3323 global_myname()));
3324 goto out;
3327 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3328 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3329 goto out;
3332 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3333 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3336 out:
3337 ads_msgfree(ads, res);
3339 return name;
3342 /********************************************************************
3343 ********************************************************************/
3345 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3347 LDAPMessage *res = NULL;
3348 ADS_STATUS status;
3349 int count = 0;
3350 char *name = NULL;
3352 status = ads_find_machine_acct(ads, &res, global_myname());
3353 if (!ADS_ERR_OK(status)) {
3354 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3355 global_myname()));
3356 goto out;
3359 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3360 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3361 goto out;
3364 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3365 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3368 out:
3369 ads_msgfree(ads, res);
3371 return name;
3374 #if 0
3376 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3379 * Join a machine to a realm
3380 * Creates the machine account and sets the machine password
3381 * @param ads connection to ads server
3382 * @param machine name of host to add
3383 * @param org_unit Organizational unit to place machine in
3384 * @return status of join
3386 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3387 uint32 account_type, const char *org_unit)
3389 ADS_STATUS status;
3390 LDAPMessage *res = NULL;
3391 char *machine;
3393 /* machine name must be lowercase */
3394 machine = SMB_STRDUP(machine_name);
3395 strlower_m(machine);
3398 status = ads_find_machine_acct(ads, (void **)&res, machine);
3399 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3400 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3401 status = ads_leave_realm(ads, machine);
3402 if (!ADS_ERR_OK(status)) {
3403 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3404 machine, ads->config.realm));
3405 return status;
3409 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3410 if (!ADS_ERR_OK(status)) {
3411 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3412 SAFE_FREE(machine);
3413 return status;
3416 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3417 if (!ADS_ERR_OK(status)) {
3418 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3419 SAFE_FREE(machine);
3420 return status;
3423 SAFE_FREE(machine);
3424 ads_msgfree(ads, res);
3426 return status;
3428 #endif
3431 * Delete a machine from the realm
3432 * @param ads connection to ads server
3433 * @param hostname Machine to remove
3434 * @return status of delete
3436 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3438 ADS_STATUS status;
3439 void *msg;
3440 LDAPMessage *res;
3441 char *hostnameDN, *host;
3442 int rc;
3443 LDAPControl ldap_control;
3444 LDAPControl * pldap_control[2] = {NULL, NULL};
3446 pldap_control[0] = &ldap_control;
3447 memset(&ldap_control, 0, sizeof(LDAPControl));
3448 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3450 /* hostname must be lowercase */
3451 host = SMB_STRDUP(hostname);
3452 strlower_m(host);
3454 status = ads_find_machine_acct(ads, &res, host);
3455 if (!ADS_ERR_OK(status)) {
3456 DEBUG(0, ("Host account for %s does not exist.\n", host));
3457 SAFE_FREE(host);
3458 return status;
3461 msg = ads_first_entry(ads, res);
3462 if (!msg) {
3463 SAFE_FREE(host);
3464 return ADS_ERROR_SYSTEM(ENOENT);
3467 hostnameDN = ads_get_dn(ads, (LDAPMessage *)msg);
3469 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3470 if (rc) {
3471 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3472 }else {
3473 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3476 if (rc != LDAP_SUCCESS) {
3477 const char *attrs[] = { "cn", NULL };
3478 LDAPMessage *msg_sub;
3480 /* we only search with scope ONE, we do not expect any further
3481 * objects to be created deeper */
3483 status = ads_do_search_retry(ads, hostnameDN,
3484 LDAP_SCOPE_ONELEVEL,
3485 "(objectclass=*)", attrs, &res);
3487 if (!ADS_ERR_OK(status)) {
3488 SAFE_FREE(host);
3489 ads_memfree(ads, hostnameDN);
3490 return status;
3493 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3494 msg_sub = ads_next_entry(ads, msg_sub)) {
3496 char *dn = NULL;
3498 if ((dn = ads_get_dn(ads, msg_sub)) == NULL) {
3499 SAFE_FREE(host);
3500 ads_memfree(ads, hostnameDN);
3501 return ADS_ERROR(LDAP_NO_MEMORY);
3504 status = ads_del_dn(ads, dn);
3505 if (!ADS_ERR_OK(status)) {
3506 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3507 SAFE_FREE(host);
3508 ads_memfree(ads, dn);
3509 ads_memfree(ads, hostnameDN);
3510 return status;
3513 ads_memfree(ads, dn);
3516 /* there should be no subordinate objects anymore */
3517 status = ads_do_search_retry(ads, hostnameDN,
3518 LDAP_SCOPE_ONELEVEL,
3519 "(objectclass=*)", attrs, &res);
3521 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3522 SAFE_FREE(host);
3523 ads_memfree(ads, hostnameDN);
3524 return status;
3527 /* delete hostnameDN now */
3528 status = ads_del_dn(ads, hostnameDN);
3529 if (!ADS_ERR_OK(status)) {
3530 SAFE_FREE(host);
3531 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3532 ads_memfree(ads, hostnameDN);
3533 return status;
3537 ads_memfree(ads, hostnameDN);
3539 status = ads_find_machine_acct(ads, &res, host);
3540 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3541 DEBUG(3, ("Failed to remove host account.\n"));
3542 SAFE_FREE(host);
3543 return status;
3546 SAFE_FREE(host);
3547 return status;
3551 * pull all token-sids from an LDAP dn
3552 * @param ads connection to ads server
3553 * @param mem_ctx TALLOC_CTX for allocating sid array
3554 * @param dn of LDAP object
3555 * @param user_sid pointer to DOM_SID (objectSid)
3556 * @param primary_group_sid pointer to DOM_SID (self composed)
3557 * @param sids pointer to sid array to allocate
3558 * @param num_sids counter of SIDs pulled
3559 * @return status of token query
3561 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3562 TALLOC_CTX *mem_ctx,
3563 const char *dn,
3564 DOM_SID *user_sid,
3565 DOM_SID *primary_group_sid,
3566 DOM_SID **sids,
3567 size_t *num_sids)
3569 ADS_STATUS status;
3570 LDAPMessage *res = NULL;
3571 int count = 0;
3572 size_t tmp_num_sids;
3573 DOM_SID *tmp_sids;
3574 DOM_SID tmp_user_sid;
3575 DOM_SID tmp_primary_group_sid;
3576 uint32 pgid;
3577 const char *attrs[] = {
3578 "objectSid",
3579 "tokenGroups",
3580 "primaryGroupID",
3581 NULL
3584 status = ads_search_retry_dn(ads, &res, dn, attrs);
3585 if (!ADS_ERR_OK(status)) {
3586 return status;
3589 count = ads_count_replies(ads, res);
3590 if (count != 1) {
3591 ads_msgfree(ads, res);
3592 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3595 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3596 ads_msgfree(ads, res);
3597 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3600 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3601 ads_msgfree(ads, res);
3602 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3606 /* hack to compose the primary group sid without knowing the
3607 * domsid */
3609 DOM_SID domsid;
3610 uint32 dummy_rid;
3612 sid_copy(&domsid, &tmp_user_sid);
3614 if (!sid_split_rid(&domsid, &dummy_rid)) {
3615 ads_msgfree(ads, res);
3616 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3619 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3620 ads_msgfree(ads, res);
3621 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3625 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3627 if (tmp_num_sids == 0 || !tmp_sids) {
3628 ads_msgfree(ads, res);
3629 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3632 if (num_sids) {
3633 *num_sids = tmp_num_sids;
3636 if (sids) {
3637 *sids = tmp_sids;
3640 if (user_sid) {
3641 *user_sid = tmp_user_sid;
3644 if (primary_group_sid) {
3645 *primary_group_sid = tmp_primary_group_sid;
3648 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3650 ads_msgfree(ads, res);
3651 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3655 * Find a sAMAccoutName in LDAP
3656 * @param ads connection to ads server
3657 * @param mem_ctx TALLOC_CTX for allocating sid array
3658 * @param samaccountname to search
3659 * @param uac_ret uint32 pointer userAccountControl attribute value
3660 * @param dn_ret pointer to dn
3661 * @return status of token query
3663 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3664 TALLOC_CTX *mem_ctx,
3665 const char *samaccountname,
3666 uint32 *uac_ret,
3667 const char **dn_ret)
3669 ADS_STATUS status;
3670 const char *attrs[] = { "userAccountControl", NULL };
3671 const char *filter;
3672 LDAPMessage *res = NULL;
3673 char *dn = NULL;
3674 uint32 uac = 0;
3676 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3677 samaccountname);
3678 if (filter == NULL) {
3679 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3680 goto out;
3683 status = ads_do_search_all(ads, ads->config.bind_path,
3684 LDAP_SCOPE_SUBTREE,
3685 filter, attrs, &res);
3687 if (!ADS_ERR_OK(status)) {
3688 goto out;
3691 if (ads_count_replies(ads, res) != 1) {
3692 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3693 goto out;
3696 dn = ads_get_dn(ads, res);
3697 if (dn == NULL) {
3698 status = ADS_ERROR(LDAP_NO_MEMORY);
3699 goto out;
3702 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3703 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3704 goto out;
3707 if (uac_ret) {
3708 *uac_ret = uac;
3711 if (dn_ret) {
3712 *dn_ret = talloc_strdup(mem_ctx, dn);
3713 if (!*dn_ret) {
3714 status = ADS_ERROR(LDAP_NO_MEMORY);
3715 goto out;
3718 out:
3719 ads_memfree(ads, dn);
3720 ads_msgfree(ads, res);
3722 return status;
3726 * find our configuration path
3727 * @param ads connection to ads server
3728 * @param mem_ctx Pointer to talloc context
3729 * @param config_path Pointer to the config path
3730 * @return status of search
3732 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3733 TALLOC_CTX *mem_ctx,
3734 char **config_path)
3736 ADS_STATUS status;
3737 LDAPMessage *res = NULL;
3738 const char *config_context = NULL;
3739 const char *attrs[] = { "configurationNamingContext", NULL };
3741 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3742 "(objectclass=*)", attrs, &res);
3743 if (!ADS_ERR_OK(status)) {
3744 return status;
3747 config_context = ads_pull_string(ads, mem_ctx, res,
3748 "configurationNamingContext");
3749 ads_msgfree(ads, res);
3750 if (!config_context) {
3751 return ADS_ERROR(LDAP_NO_MEMORY);
3754 if (config_path) {
3755 *config_path = talloc_strdup(mem_ctx, config_context);
3756 if (!*config_path) {
3757 return ADS_ERROR(LDAP_NO_MEMORY);
3761 return ADS_ERROR(LDAP_SUCCESS);
3765 * find the displayName of an extended right
3766 * @param ads connection to ads server
3767 * @param config_path The config path
3768 * @param mem_ctx Pointer to talloc context
3769 * @param GUID struct of the rightsGUID
3770 * @return status of search
3772 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3773 const char *config_path,
3774 TALLOC_CTX *mem_ctx,
3775 const struct GUID *rights_guid)
3777 ADS_STATUS rc;
3778 LDAPMessage *res = NULL;
3779 char *expr = NULL;
3780 const char *attrs[] = { "displayName", NULL };
3781 const char *result = NULL;
3782 const char *path;
3784 if (!ads || !mem_ctx || !rights_guid) {
3785 goto done;
3788 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3789 smb_uuid_string(mem_ctx, *rights_guid));
3790 if (!expr) {
3791 goto done;
3794 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3795 if (!path) {
3796 goto done;
3799 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3800 expr, attrs, &res);
3801 if (!ADS_ERR_OK(rc)) {
3802 goto done;
3805 if (ads_count_replies(ads, res) != 1) {
3806 goto done;
3809 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3811 done:
3812 ads_msgfree(ads, res);
3813 return result;
3818 * verify or build and verify an account ou
3819 * @param mem_ctx Pointer to talloc context
3820 * @param ads connection to ads server
3821 * @param account_ou
3822 * @return status of search
3825 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3826 ADS_STRUCT *ads,
3827 const char **account_ou)
3829 struct ldb_dn *name_dn = NULL;
3830 const char *name = NULL;
3831 char *ou_string = NULL;
3833 name_dn = ldb_dn_explode(mem_ctx, *account_ou);
3834 if (name_dn) {
3835 return ADS_SUCCESS;
3838 ou_string = ads_ou_string(ads, *account_ou);
3839 if (!ou_string) {
3840 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3843 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3844 ads->config.bind_path);
3845 SAFE_FREE(ou_string);
3846 if (!name) {
3847 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3850 name_dn = ldb_dn_explode(mem_ctx, name);
3851 if (!name_dn) {
3852 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3855 *account_ou = talloc_strdup(mem_ctx, name);
3856 if (!*account_ou) {
3857 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3860 return ADS_SUCCESS;
3863 #endif