s3:libads: improve debug levels/messages in ads_find_dc()
[Samba.git] / source3 / libads / ldap.c
blob87631641ff1953e5194d30ce0ff10cddd0a18f44
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 "ads.h"
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/addns/dnsquery.h"
29 #include "../libds/common/flags.h"
30 #include "smbldap.h"
31 #include "../libcli/security/security.h"
32 #include "lib/param/loadparm.h"
34 #ifdef HAVE_LDAP
36 /**
37 * @file ldap.c
38 * @brief basic ldap client-side routines for ads server communications
40 * The routines contained here should do the necessary ldap calls for
41 * ads setups.
43 * Important note: attribute names passed into ads_ routines must
44 * already be in UTF-8 format. We do not convert them because in almost
45 * all cases, they are just ascii (which is represented with the same
46 * codepoints in UTF-8). This may have to change at some point
47 **/
50 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
52 static SIG_ATOMIC_T gotalarm;
54 /***************************************************************
55 Signal function to tell us we timed out.
56 ****************************************************************/
58 static void gotalarm_sig(int signum)
60 gotalarm = 1;
63 LDAP *ldap_open_with_timeout(const char *server,
64 struct sockaddr_storage *ss,
65 int port, unsigned int to)
67 LDAP *ldp = NULL;
68 int ldap_err;
69 char *uri;
71 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
72 "%u seconds\n", server, port, to));
74 if (to) {
75 /* Setup timeout */
76 gotalarm = 0;
77 CatchSignal(SIGALRM, gotalarm_sig);
78 alarm(to);
79 /* End setup timeout. */
82 if ( strchr_m(server, ':') ) {
83 /* IPv6 URI */
84 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
85 } else {
86 /* IPv4 URI */
87 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
89 if (uri == NULL) {
90 return NULL;
93 #ifdef HAVE_LDAP_INITIALIZE
94 ldap_err = ldap_initialize(&ldp, uri);
95 #else
96 ldp = ldap_open(server, port);
97 if (ldp != NULL) {
98 ldap_err = LDAP_SUCCESS;
99 } else {
100 ldap_err = LDAP_OTHER;
102 #endif
103 if (ldap_err != LDAP_SUCCESS) {
104 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
105 uri, ldap_err2string(ldap_err)));
106 } else {
107 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
110 if (to) {
111 /* Teardown timeout. */
112 alarm(0);
113 CatchSignal(SIGALRM, SIG_IGN);
116 return ldp;
119 static int ldap_search_with_timeout(LDAP *ld,
120 LDAP_CONST char *base,
121 int scope,
122 LDAP_CONST char *filter,
123 char **attrs,
124 int attrsonly,
125 LDAPControl **sctrls,
126 LDAPControl **cctrls,
127 int sizelimit,
128 LDAPMessage **res )
130 int to = lp_ldap_timeout();
131 struct timeval timeout;
132 struct timeval *timeout_ptr = NULL;
133 int result;
135 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
136 gotalarm = 0;
138 if (to) {
139 timeout.tv_sec = to;
140 timeout.tv_usec = 0;
141 timeout_ptr = &timeout;
143 /* Setup alarm timeout. */
144 CatchSignal(SIGALRM, gotalarm_sig);
145 /* Make the alarm time one second beyond
146 the timout we're setting for the
147 remote search timeout, to allow that
148 to fire in preference. */
149 alarm(to+1);
150 /* End setup timeout. */
154 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
155 attrsonly, sctrls, cctrls, timeout_ptr,
156 sizelimit, res);
158 if (to) {
159 /* Teardown alarm timeout. */
160 CatchSignal(SIGALRM, SIG_IGN);
161 alarm(0);
164 if (gotalarm != 0)
165 return LDAP_TIMELIMIT_EXCEEDED;
168 * A bug in OpenLDAP means ldap_search_ext_s can return
169 * LDAP_SUCCESS but with a NULL res pointer. Cope with
170 * this. See bug #6279 for details. JRA.
173 if (*res == NULL) {
174 return LDAP_TIMELIMIT_EXCEEDED;
177 return result;
180 /**********************************************
181 Do client and server sitename match ?
182 **********************************************/
184 bool ads_sitename_match(ADS_STRUCT *ads)
186 if (ads->config.server_site_name == NULL &&
187 ads->config.client_site_name == NULL ) {
188 DEBUG(10,("ads_sitename_match: both null\n"));
189 return True;
191 if (ads->config.server_site_name &&
192 ads->config.client_site_name &&
193 strequal(ads->config.server_site_name,
194 ads->config.client_site_name)) {
195 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
196 return True;
198 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
199 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
200 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
201 return False;
204 /**********************************************
205 Is this the closest DC ?
206 **********************************************/
208 bool ads_closest_dc(ADS_STRUCT *ads)
210 if (ads->config.flags & NBT_SERVER_CLOSEST) {
211 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
212 return True;
215 /* not sure if this can ever happen */
216 if (ads_sitename_match(ads)) {
217 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
218 return True;
221 if (ads->config.client_site_name == NULL) {
222 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
223 return True;
226 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
227 ads->config.ldap_server_name));
229 return False;
234 try a connection to a given ldap server, returning True and setting the servers IP
235 in the ads struct if successful
237 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
238 struct sockaddr_storage *ss)
240 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
241 TALLOC_CTX *frame = talloc_stackframe();
242 bool ret = false;
243 char addr[INET6_ADDRSTRLEN];
245 if (ss == NULL) {
246 TALLOC_FREE(frame);
247 return False;
250 print_sockaddr(addr, sizeof(addr), ss);
252 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
253 addr, ads->server.realm));
255 ZERO_STRUCT( cldap_reply );
257 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
258 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
259 ret = false;
260 goto out;
263 /* Check the CLDAP reply flags */
265 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
266 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
267 addr));
268 ret = false;
269 goto out;
272 /* Fill in the ads->config values */
274 SAFE_FREE(ads->config.realm);
275 SAFE_FREE(ads->config.bind_path);
276 SAFE_FREE(ads->config.ldap_server_name);
277 SAFE_FREE(ads->config.server_site_name);
278 SAFE_FREE(ads->config.client_site_name);
279 SAFE_FREE(ads->server.workgroup);
281 ads->config.flags = cldap_reply.server_type;
282 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
283 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
284 if (!strupper_m(ads->config.realm)) {
285 ret = false;
286 goto out;
289 ads->config.bind_path = ads_build_dn(ads->config.realm);
290 if (*cldap_reply.server_site) {
291 ads->config.server_site_name =
292 SMB_STRDUP(cldap_reply.server_site);
294 if (*cldap_reply.client_site) {
295 ads->config.client_site_name =
296 SMB_STRDUP(cldap_reply.client_site);
298 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
300 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
301 ads->ldap.ss = *ss;
303 /* Store our site name. */
304 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
305 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
307 ret = true;
309 out:
311 TALLOC_FREE(frame);
312 return ret;
315 /**********************************************************************
316 send a cldap ping to list of servers, one at a time, until one of
317 them answers it's an ldap server. Record success in the ADS_STRUCT.
318 Take note of and update negative connection cache.
319 **********************************************************************/
321 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,const char *domain,
322 struct ip_service *ip_list, int count)
324 int i;
325 bool ok;
327 for (i = 0; i < count; i++) {
328 char server[INET6_ADDRSTRLEN];
330 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
332 if (!NT_STATUS_IS_OK(
333 check_negative_conn_cache(domain, server)))
334 continue;
336 ok = ads_try_connect(ads, false, &ip_list[i].ss);
337 if (ok) {
338 return NT_STATUS_OK;
341 /* keep track of failures */
342 add_failed_connection_entry(domain, server,
343 NT_STATUS_UNSUCCESSFUL);
346 return NT_STATUS_NO_LOGON_SERVERS;
349 /***************************************************************************
350 resolve a name and perform an "ldap ping" using NetBIOS and related methods
351 ****************************************************************************/
353 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
354 const char *domain, const char *realm)
356 int count, i;
357 struct ip_service *ip_list;
358 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
360 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
361 domain));
363 status = get_sorted_dc_list(domain, NULL, &ip_list, &count,
364 false);
365 if (!NT_STATUS_IS_OK(status)) {
366 return status;
369 /* remove servers which are known to be dead based on
370 the corresponding DNS method */
371 if (*realm) {
372 for (i = 0; i < count; ++i) {
373 char server[INET6_ADDRSTRLEN];
375 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
377 if(!NT_STATUS_IS_OK(
378 check_negative_conn_cache(realm, server))) {
379 /* Ensure we add the workgroup name for this
380 IP address as negative too. */
381 add_failed_connection_entry(
382 domain, server,
383 NT_STATUS_UNSUCCESSFUL);
388 status = cldap_ping_list(ads, domain, ip_list, count);
390 SAFE_FREE(ip_list);
392 return status;
396 /**********************************************************************
397 resolve a name and perform an "ldap ping" using DNS
398 **********************************************************************/
400 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
401 const char *realm)
403 int count;
404 struct ip_service *ip_list;
405 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
407 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
408 realm));
410 status = get_sorted_dc_list(realm, sitename, &ip_list, &count,
411 true);
412 if (!NT_STATUS_IS_OK(status)) {
413 return status;
416 status = cldap_ping_list(ads, realm, ip_list, count);
418 SAFE_FREE(ip_list);
420 return status;
423 /**********************************************************************
424 Try to find an AD dc using our internal name resolution routines
425 Try the realm first and then then workgroup name if netbios is not
426 disabled
427 **********************************************************************/
429 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
431 const char *c_domain = "";
432 const char *c_realm;
433 bool use_own_domain = False;
434 char *sitename = NULL;
435 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
436 bool ok = false;
438 /* if the realm and workgroup are both empty, assume they are ours */
440 /* realm */
441 c_realm = ads->server.realm;
443 if (c_realm == NULL)
444 c_realm = "";
446 if (!*c_realm) {
447 /* special case where no realm and no workgroup means our own */
448 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
449 use_own_domain = True;
450 c_realm = lp_realm();
454 if (!lp_disable_netbios()) {
455 if (use_own_domain) {
456 c_domain = lp_workgroup();
457 } else {
458 c_domain = ads->server.workgroup;
459 if (!*c_realm && (!c_domain || !*c_domain)) {
460 c_domain = lp_workgroup();
464 if (!c_domain) {
465 c_domain = "";
469 if (!*c_realm && !*c_domain) {
470 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
471 "what to do\n"));
472 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
476 * In case of LDAP we use get_dc_name() as that
477 * creates the custom krb5.conf file
479 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
480 fstring srv_name;
481 struct sockaddr_storage ip_out;
483 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
484 " and falling back to domain '%s'\n",
485 c_realm, c_domain));
487 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
488 if (ok) {
490 * we call ads_try_connect() to fill in the
491 * ads->config details
493 ok = ads_try_connect(ads, false, &ip_out);
494 if (ok) {
495 return NT_STATUS_OK;
499 return NT_STATUS_NO_LOGON_SERVERS;
502 if (*c_realm) {
503 sitename = sitename_fetch(talloc_tos(), c_realm);
504 status = resolve_and_ping_dns(ads, sitename, c_realm);
506 if (NT_STATUS_IS_OK(status)) {
507 TALLOC_FREE(sitename);
508 return status;
511 /* In case we failed to contact one of our closest DC on our
512 * site we
513 * need to try to find another DC, retry with a site-less SRV
514 * DNS query
515 * - Guenther */
517 if (sitename) {
518 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
519 "our site (%s), Trying to find another DC "
520 "for realm '%s' (domain '%s')\n",
521 sitename, c_realm, c_domain));
522 namecache_delete(c_realm, 0x1C);
523 status =
524 resolve_and_ping_dns(ads, NULL, c_realm);
526 if (NT_STATUS_IS_OK(status)) {
527 TALLOC_FREE(sitename);
528 return status;
532 TALLOC_FREE(sitename);
535 /* try netbios as fallback - if permitted,
536 or if configuration specifically requests it */
537 if (*c_domain) {
538 if (*c_realm) {
539 DEBUG(3, ("ads_find_dc: falling back to netbios "
540 "name resolution for domain '%s' (realm '%s')\n",
541 c_domain, c_realm));
544 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
545 if (NT_STATUS_IS_OK(status)) {
546 return status;
550 DEBUG(1, ("ads_find_dc: "
551 "name resolution for realm '%s' (domain '%s') failed: %s\n",
552 c_realm, c_domain, nt_errstr(status)));
553 return status;
556 /*********************************************************************
557 *********************************************************************/
559 static NTSTATUS ads_lookup_site(void)
561 ADS_STRUCT *ads = NULL;
562 ADS_STATUS ads_status;
563 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
565 ads = ads_init(lp_realm(), NULL, NULL);
566 if (!ads) {
567 return NT_STATUS_NO_MEMORY;
570 /* The NO_BIND here will find a DC and set the client site
571 but not establish the TCP connection */
573 ads->auth.flags = ADS_AUTH_NO_BIND;
574 ads_status = ads_connect(ads);
575 if (!ADS_ERR_OK(ads_status)) {
576 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
577 ads_errstr(ads_status)));
579 nt_status = ads_ntstatus(ads_status);
581 if (ads) {
582 ads_destroy(&ads);
585 return nt_status;
588 /*********************************************************************
589 *********************************************************************/
591 static const char* host_dns_domain(const char *fqdn)
593 const char *p = fqdn;
595 /* go to next char following '.' */
597 if ((p = strchr_m(fqdn, '.')) != NULL) {
598 p++;
601 return p;
606 * Connect to the Global Catalog server
607 * @param ads Pointer to an existing ADS_STRUCT
608 * @return status of connection
610 * Simple wrapper around ads_connect() that fills in the
611 * GC ldap server information
614 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
616 TALLOC_CTX *frame = talloc_stackframe();
617 struct dns_rr_srv *gcs_list;
618 int num_gcs;
619 const char *realm = ads->server.realm;
620 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
621 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
622 int i;
623 bool done = false;
624 char *sitename = NULL;
626 if (!realm)
627 realm = lp_realm();
629 if ((sitename = sitename_fetch(frame, realm)) == NULL) {
630 ads_lookup_site();
631 sitename = sitename_fetch(frame, realm);
634 do {
635 /* We try once with a sitename and once without
636 (unless we don't have a sitename and then we're
637 done */
639 if (sitename == NULL)
640 done = true;
642 nt_status = ads_dns_query_gcs(frame,
643 realm,
644 sitename,
645 &gcs_list,
646 &num_gcs);
648 if (!NT_STATUS_IS_OK(nt_status)) {
649 ads_status = ADS_ERROR_NT(nt_status);
650 goto done;
653 /* Loop until we get a successful connection or have gone
654 through them all. When connecting a GC server, make sure that
655 the realm is the server's DNS name and not the forest root */
657 for (i=0; i<num_gcs; i++) {
658 ads->server.gc = true;
659 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
660 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
661 ads_status = ads_connect(ads);
662 if (ADS_ERR_OK(ads_status)) {
663 /* Reset the bind_dn to "". A Global Catalog server
664 may host multiple domain trees in a forest.
665 Windows 2003 GC server will accept "" as the search
666 path to imply search all domain trees in the forest */
668 SAFE_FREE(ads->config.bind_path);
669 ads->config.bind_path = SMB_STRDUP("");
672 goto done;
674 SAFE_FREE(ads->server.ldap_server);
675 SAFE_FREE(ads->server.realm);
678 TALLOC_FREE(gcs_list);
679 num_gcs = 0;
680 } while (!done);
682 done:
683 talloc_destroy(frame);
685 return ads_status;
690 * Connect to the LDAP server
691 * @param ads Pointer to an existing ADS_STRUCT
692 * @return status of connection
694 ADS_STATUS ads_connect(ADS_STRUCT *ads)
696 int version = LDAP_VERSION3;
697 ADS_STATUS status;
698 NTSTATUS ntstatus;
699 char addr[INET6_ADDRSTRLEN];
701 ZERO_STRUCT(ads->ldap);
702 ads->ldap.last_attempt = time_mono(NULL);
703 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
705 /* try with a user specified server */
707 if (DEBUGLEVEL >= 11) {
708 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
709 DEBUG(11,("ads_connect: entering\n"));
710 DEBUGADD(11,("%s\n", s));
711 TALLOC_FREE(s);
714 if (ads->server.ldap_server) {
715 bool ok = false;
716 struct sockaddr_storage ss;
718 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
719 if (!ok) {
720 DEBUG(5,("ads_connect: unable to resolve name %s\n",
721 ads->server.ldap_server));
722 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
723 goto out;
725 ok = ads_try_connect(ads, ads->server.gc, &ss);
726 if (ok) {
727 goto got_connection;
730 /* The choice of which GC use is handled one level up in
731 ads_connect_gc(). If we continue on from here with
732 ads_find_dc() we will get GC searches on port 389 which
733 doesn't work. --jerry */
735 if (ads->server.gc == true) {
736 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
740 ntstatus = ads_find_dc(ads);
741 if (NT_STATUS_IS_OK(ntstatus)) {
742 goto got_connection;
745 status = ADS_ERROR_NT(ntstatus);
746 goto out;
748 got_connection:
750 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
751 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
753 if (!ads->auth.user_name) {
754 /* Must use the userPrincipalName value here or sAMAccountName
755 and not servicePrincipalName; found by Guenther Deschner */
757 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
758 DEBUG(0,("ads_connect: asprintf fail.\n"));
759 ads->auth.user_name = NULL;
763 if (!ads->auth.realm) {
764 ads->auth.realm = SMB_STRDUP(ads->config.realm);
767 if (!ads->auth.kdc_server) {
768 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
769 ads->auth.kdc_server = SMB_STRDUP(addr);
772 /* If the caller() requested no LDAP bind, then we are done */
774 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
775 status = ADS_SUCCESS;
776 goto out;
779 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
780 if (!ads->ldap.mem_ctx) {
781 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
782 goto out;
785 /* Otherwise setup the TCP LDAP session */
787 ads->ldap.ld = ldap_open_with_timeout(addr,
788 &ads->ldap.ss,
789 ads->ldap.port, lp_ldap_timeout());
790 if (ads->ldap.ld == NULL) {
791 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
792 goto out;
794 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
796 /* cache the successful connection for workgroup and realm */
797 if (ads_closest_dc(ads)) {
798 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
799 saf_store( ads->server.realm, ads->config.ldap_server_name);
802 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
804 if ( lp_ldap_ssl_ads() ) {
805 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
806 if (!ADS_ERR_OK(status)) {
807 goto out;
811 /* fill in the current time and offsets */
813 status = ads_current_time( ads );
814 if ( !ADS_ERR_OK(status) ) {
815 goto out;
818 /* Now do the bind */
820 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
821 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
822 goto out;
825 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
826 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
827 goto out;
830 status = ads_sasl_bind(ads);
832 out:
833 if (DEBUGLEVEL >= 11) {
834 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
835 DEBUG(11,("ads_connect: leaving with: %s\n",
836 ads_errstr(status)));
837 DEBUGADD(11,("%s\n", s));
838 TALLOC_FREE(s);
841 return status;
845 * Connect to the LDAP server using given credentials
846 * @param ads Pointer to an existing ADS_STRUCT
847 * @return status of connection
849 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
851 ads->auth.flags |= ADS_AUTH_USER_CREDS;
853 return ads_connect(ads);
857 * Disconnect the LDAP server
858 * @param ads Pointer to an existing ADS_STRUCT
860 void ads_disconnect(ADS_STRUCT *ads)
862 if (ads->ldap.ld) {
863 ldap_unbind(ads->ldap.ld);
864 ads->ldap.ld = NULL;
866 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
867 ads->ldap.wrap_ops->disconnect(ads);
869 if (ads->ldap.mem_ctx) {
870 talloc_free(ads->ldap.mem_ctx);
872 ZERO_STRUCT(ads->ldap);
876 Duplicate a struct berval into talloc'ed memory
878 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
880 struct berval *value;
882 if (!in_val) return NULL;
884 value = talloc_zero(ctx, struct berval);
885 if (value == NULL)
886 return NULL;
887 if (in_val->bv_len == 0) return value;
889 value->bv_len = in_val->bv_len;
890 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
891 in_val->bv_len);
892 return value;
896 Make a values list out of an array of (struct berval *)
898 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
899 const struct berval **in_vals)
901 struct berval **values;
902 int i;
904 if (!in_vals) return NULL;
905 for (i=0; in_vals[i]; i++)
906 ; /* count values */
907 values = talloc_zero_array(ctx, struct berval *, i+1);
908 if (!values) return NULL;
910 for (i=0; in_vals[i]; i++) {
911 values[i] = dup_berval(ctx, in_vals[i]);
913 return values;
917 UTF8-encode a values list out of an array of (char *)
919 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
921 char **values;
922 int i;
923 size_t size;
925 if (!in_vals) return NULL;
926 for (i=0; in_vals[i]; i++)
927 ; /* count values */
928 values = talloc_zero_array(ctx, char *, i+1);
929 if (!values) return NULL;
931 for (i=0; in_vals[i]; i++) {
932 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
933 TALLOC_FREE(values);
934 return NULL;
937 return values;
941 Pull a (char *) array out of a UTF8-encoded values list
943 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
945 char **values;
946 int i;
947 size_t converted_size;
949 if (!in_vals) return NULL;
950 for (i=0; in_vals[i]; i++)
951 ; /* count values */
952 values = talloc_zero_array(ctx, char *, i+1);
953 if (!values) return NULL;
955 for (i=0; in_vals[i]; i++) {
956 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
957 &converted_size)) {
958 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
959 "%s", strerror(errno)));
962 return values;
966 * Do a search with paged results. cookie must be null on the first
967 * call, and then returned on each subsequent call. It will be null
968 * again when the entire search is complete
969 * @param ads connection to ads server
970 * @param bind_path Base dn for the search
971 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
972 * @param expr Search expression - specified in local charset
973 * @param attrs Attributes to retrieve - specified in utf8 or ascii
974 * @param res ** which will contain results - free res* with ads_msgfree()
975 * @param count Number of entries retrieved on this page
976 * @param cookie The paged results cookie to be returned on subsequent calls
977 * @return status of search
979 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
980 const char *bind_path,
981 int scope, const char *expr,
982 const char **attrs, void *args,
983 LDAPMessage **res,
984 int *count, struct berval **cookie)
986 int rc, i, version;
987 char *utf8_expr, *utf8_path, **search_attrs = NULL;
988 size_t converted_size;
989 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
990 BerElement *cookie_be = NULL;
991 struct berval *cookie_bv= NULL;
992 BerElement *ext_be = NULL;
993 struct berval *ext_bv= NULL;
995 TALLOC_CTX *ctx;
996 ads_control *external_control = (ads_control *) args;
998 *res = NULL;
1000 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1001 return ADS_ERROR(LDAP_NO_MEMORY);
1003 /* 0 means the conversion worked but the result was empty
1004 so we only fail if it's -1. In any case, it always
1005 at least nulls out the dest */
1006 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1007 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1009 rc = LDAP_NO_MEMORY;
1010 goto done;
1013 if (!attrs || !(*attrs))
1014 search_attrs = NULL;
1015 else {
1016 /* This would be the utf8-encoded version...*/
1017 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1018 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1019 rc = LDAP_NO_MEMORY;
1020 goto done;
1024 /* Paged results only available on ldap v3 or later */
1025 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1026 if (version < LDAP_VERSION3) {
1027 rc = LDAP_NOT_SUPPORTED;
1028 goto done;
1031 cookie_be = ber_alloc_t(LBER_USE_DER);
1032 if (*cookie) {
1033 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1034 ber_bvfree(*cookie); /* don't need it from last time */
1035 *cookie = NULL;
1036 } else {
1037 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1039 ber_flatten(cookie_be, &cookie_bv);
1040 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1041 PagedResults.ldctl_iscritical = (char) 1;
1042 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1043 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1045 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1046 NoReferrals.ldctl_iscritical = (char) 0;
1047 NoReferrals.ldctl_value.bv_len = 0;
1048 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1050 if (external_control &&
1051 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1052 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1054 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1055 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1057 /* win2k does not accept a ldctl_value beeing passed in */
1059 if (external_control->val != 0) {
1061 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1062 rc = LDAP_NO_MEMORY;
1063 goto done;
1066 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1067 rc = LDAP_NO_MEMORY;
1068 goto done;
1070 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1071 rc = LDAP_NO_MEMORY;
1072 goto done;
1075 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1076 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1078 } else {
1079 ExternalCtrl.ldctl_value.bv_len = 0;
1080 ExternalCtrl.ldctl_value.bv_val = NULL;
1083 controls[0] = &NoReferrals;
1084 controls[1] = &PagedResults;
1085 controls[2] = &ExternalCtrl;
1086 controls[3] = NULL;
1088 } else {
1089 controls[0] = &NoReferrals;
1090 controls[1] = &PagedResults;
1091 controls[2] = NULL;
1094 /* we need to disable referrals as the openldap libs don't
1095 handle them and paged results at the same time. Using them
1096 together results in the result record containing the server
1097 page control being removed from the result list (tridge/jmcd)
1099 leaving this in despite the control that says don't generate
1100 referrals, in case the server doesn't support it (jmcd)
1102 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1104 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1105 search_attrs, 0, controls,
1106 NULL, LDAP_NO_LIMIT,
1107 (LDAPMessage **)res);
1109 ber_free(cookie_be, 1);
1110 ber_bvfree(cookie_bv);
1112 if (rc) {
1113 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1114 ldap_err2string(rc)));
1115 if (rc == LDAP_OTHER) {
1116 char *ldap_errmsg;
1117 int ret;
1119 ret = ldap_parse_result(ads->ldap.ld,
1120 *res,
1121 NULL,
1122 NULL,
1123 &ldap_errmsg,
1124 NULL,
1125 NULL,
1127 if (ret == LDAP_SUCCESS) {
1128 DEBUG(3, ("ldap_search_with_timeout(%s) "
1129 "error: %s\n", expr, ldap_errmsg));
1130 ldap_memfree(ldap_errmsg);
1133 goto done;
1136 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1137 NULL, &rcontrols, 0);
1139 if (!rcontrols) {
1140 goto done;
1143 for (i=0; rcontrols[i]; i++) {
1144 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1145 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1146 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1147 &cookie_bv);
1148 /* the berval is the cookie, but must be freed when
1149 it is all done */
1150 if (cookie_bv->bv_len) /* still more to do */
1151 *cookie=ber_bvdup(cookie_bv);
1152 else
1153 *cookie=NULL;
1154 ber_bvfree(cookie_bv);
1155 ber_free(cookie_be, 1);
1156 break;
1159 ldap_controls_free(rcontrols);
1161 done:
1162 talloc_destroy(ctx);
1164 if (ext_be) {
1165 ber_free(ext_be, 1);
1168 if (ext_bv) {
1169 ber_bvfree(ext_bv);
1172 /* if/when we decide to utf8-encode attrs, take out this next line */
1173 TALLOC_FREE(search_attrs);
1175 return ADS_ERROR(rc);
1178 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1179 int scope, const char *expr,
1180 const char **attrs, LDAPMessage **res,
1181 int *count, struct berval **cookie)
1183 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1188 * Get all results for a search. This uses ads_do_paged_search() to return
1189 * all entries in a large search.
1190 * @param ads connection to ads server
1191 * @param bind_path Base dn for the search
1192 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1193 * @param expr Search expression
1194 * @param attrs Attributes to retrieve
1195 * @param res ** which will contain results - free res* with ads_msgfree()
1196 * @return status of search
1198 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1199 int scope, const char *expr,
1200 const char **attrs, void *args,
1201 LDAPMessage **res)
1203 struct berval *cookie = NULL;
1204 int count = 0;
1205 ADS_STATUS status;
1207 *res = NULL;
1208 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1209 &count, &cookie);
1211 if (!ADS_ERR_OK(status))
1212 return status;
1214 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1215 while (cookie) {
1216 LDAPMessage *res2 = NULL;
1217 LDAPMessage *msg, *next;
1219 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1220 attrs, args, &res2, &count, &cookie);
1221 if (!ADS_ERR_OK(status)) {
1222 /* Ensure we free all collected results */
1223 ads_msgfree(ads, *res);
1224 *res = NULL;
1225 break;
1228 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1229 that this works on all ldap libs, but I have only tested with openldap */
1230 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1231 next = ads_next_message(ads, msg);
1232 ldap_add_result_entry((LDAPMessage **)res, msg);
1234 /* note that we do not free res2, as the memory is now
1235 part of the main returned list */
1237 #else
1238 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1239 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1240 #endif
1242 return status;
1245 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1246 int scope, const char *expr,
1247 const char **attrs, LDAPMessage **res)
1249 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1252 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1253 int scope, const char *expr,
1254 const char **attrs, uint32_t sd_flags,
1255 LDAPMessage **res)
1257 ads_control args;
1259 args.control = ADS_SD_FLAGS_OID;
1260 args.val = sd_flags;
1261 args.critical = True;
1263 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1268 * Run a function on all results for a search. Uses ads_do_paged_search() and
1269 * runs the function as each page is returned, using ads_process_results()
1270 * @param ads connection to ads server
1271 * @param bind_path Base dn for the search
1272 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1273 * @param expr Search expression - specified in local charset
1274 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1275 * @param fn Function which takes attr name, values list, and data_area
1276 * @param data_area Pointer which is passed to function on each call
1277 * @return status of search
1279 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1280 int scope, const char *expr, const char **attrs,
1281 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1282 void *data_area)
1284 struct berval *cookie = NULL;
1285 int count = 0;
1286 ADS_STATUS status;
1287 LDAPMessage *res;
1289 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1290 &count, &cookie);
1292 if (!ADS_ERR_OK(status)) return status;
1294 ads_process_results(ads, res, fn, data_area);
1295 ads_msgfree(ads, res);
1297 while (cookie) {
1298 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1299 &res, &count, &cookie);
1301 if (!ADS_ERR_OK(status)) break;
1303 ads_process_results(ads, res, fn, data_area);
1304 ads_msgfree(ads, res);
1307 return status;
1311 * Do a search with a timeout.
1312 * @param ads connection to ads server
1313 * @param bind_path Base dn for the search
1314 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1315 * @param expr Search expression
1316 * @param attrs Attributes to retrieve
1317 * @param res ** which will contain results - free res* with ads_msgfree()
1318 * @return status of search
1320 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1321 const char *expr,
1322 const char **attrs, LDAPMessage **res)
1324 int rc;
1325 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1326 size_t converted_size;
1327 TALLOC_CTX *ctx;
1329 *res = NULL;
1330 if (!(ctx = talloc_init("ads_do_search"))) {
1331 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1332 return ADS_ERROR(LDAP_NO_MEMORY);
1335 /* 0 means the conversion worked but the result was empty
1336 so we only fail if it's negative. In any case, it always
1337 at least nulls out the dest */
1338 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1339 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1341 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1342 rc = LDAP_NO_MEMORY;
1343 goto done;
1346 if (!attrs || !(*attrs))
1347 search_attrs = NULL;
1348 else {
1349 /* This would be the utf8-encoded version...*/
1350 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1351 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1353 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1354 rc = LDAP_NO_MEMORY;
1355 goto done;
1359 /* see the note in ads_do_paged_search - we *must* disable referrals */
1360 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1362 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1363 search_attrs, 0, NULL, NULL,
1364 LDAP_NO_LIMIT,
1365 (LDAPMessage **)res);
1367 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1368 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1369 rc = 0;
1372 done:
1373 talloc_destroy(ctx);
1374 /* if/when we decide to utf8-encode attrs, take out this next line */
1375 TALLOC_FREE(search_attrs);
1376 return ADS_ERROR(rc);
1379 * Do a general ADS search
1380 * @param ads connection to ads server
1381 * @param res ** which will contain results - free res* with ads_msgfree()
1382 * @param expr Search expression
1383 * @param attrs Attributes to retrieve
1384 * @return status of search
1386 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1387 const char *expr, const char **attrs)
1389 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1390 expr, attrs, res);
1394 * Do a search on a specific DistinguishedName
1395 * @param ads connection to ads server
1396 * @param res ** which will contain results - free res* with ads_msgfree()
1397 * @param dn DistinguishName to search
1398 * @param attrs Attributes to retrieve
1399 * @return status of search
1401 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1402 const char *dn, const char **attrs)
1404 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1405 attrs, res);
1409 * Free up memory from a ads_search
1410 * @param ads connection to ads server
1411 * @param msg Search results to free
1413 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1415 if (!msg) return;
1416 ldap_msgfree(msg);
1420 * Get a dn from search results
1421 * @param ads connection to ads server
1422 * @param msg Search result
1423 * @return dn string
1425 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1427 char *utf8_dn, *unix_dn;
1428 size_t converted_size;
1430 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1432 if (!utf8_dn) {
1433 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1434 return NULL;
1437 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1438 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1439 utf8_dn ));
1440 return NULL;
1442 ldap_memfree(utf8_dn);
1443 return unix_dn;
1447 * Get the parent from a dn
1448 * @param dn the dn to return the parent from
1449 * @return parent dn string
1451 char *ads_parent_dn(const char *dn)
1453 char *p;
1455 if (dn == NULL) {
1456 return NULL;
1459 p = strchr(dn, ',');
1461 if (p == NULL) {
1462 return NULL;
1465 return p+1;
1469 * Find a machine account given a hostname
1470 * @param ads connection to ads server
1471 * @param res ** which will contain results - free res* with ads_msgfree()
1472 * @param host Hostname to search for
1473 * @return status of search
1475 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1476 const char *machine)
1478 ADS_STATUS status;
1479 char *expr;
1480 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1482 *res = NULL;
1484 /* the easiest way to find a machine account anywhere in the tree
1485 is to look for hostname$ */
1486 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1487 DEBUG(1, ("asprintf failed!\n"));
1488 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1491 status = ads_search(ads, res, expr, attrs);
1492 SAFE_FREE(expr);
1493 return status;
1497 * Initialize a list of mods to be used in a modify request
1498 * @param ctx An initialized TALLOC_CTX
1499 * @return allocated ADS_MODLIST
1501 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1503 #define ADS_MODLIST_ALLOC_SIZE 10
1504 LDAPMod **mods;
1506 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1507 /* -1 is safety to make sure we don't go over the end.
1508 need to reset it to NULL before doing ldap modify */
1509 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1511 return (ADS_MODLIST)mods;
1516 add an attribute to the list, with values list already constructed
1518 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1519 int mod_op, const char *name,
1520 const void *_invals)
1522 int curmod;
1523 LDAPMod **modlist = (LDAPMod **) *mods;
1524 struct berval **ber_values = NULL;
1525 char **char_values = NULL;
1527 if (!_invals) {
1528 mod_op = LDAP_MOD_DELETE;
1529 } else {
1530 if (mod_op & LDAP_MOD_BVALUES) {
1531 const struct berval **b;
1532 b = discard_const_p(const struct berval *, _invals);
1533 ber_values = ads_dup_values(ctx, b);
1534 } else {
1535 const char **c;
1536 c = discard_const_p(const char *, _invals);
1537 char_values = ads_push_strvals(ctx, c);
1541 /* find the first empty slot */
1542 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1543 curmod++);
1544 if (modlist[curmod] == (LDAPMod *) -1) {
1545 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1546 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1547 return ADS_ERROR(LDAP_NO_MEMORY);
1548 memset(&modlist[curmod], 0,
1549 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1550 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1551 *mods = (ADS_MODLIST)modlist;
1554 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1555 return ADS_ERROR(LDAP_NO_MEMORY);
1556 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1557 if (mod_op & LDAP_MOD_BVALUES) {
1558 modlist[curmod]->mod_bvalues = ber_values;
1559 } else if (mod_op & LDAP_MOD_DELETE) {
1560 modlist[curmod]->mod_values = NULL;
1561 } else {
1562 modlist[curmod]->mod_values = char_values;
1565 modlist[curmod]->mod_op = mod_op;
1566 return ADS_ERROR(LDAP_SUCCESS);
1570 * Add a single string value to a mod list
1571 * @param ctx An initialized TALLOC_CTX
1572 * @param mods An initialized ADS_MODLIST
1573 * @param name The attribute name to add
1574 * @param val The value to add - NULL means DELETE
1575 * @return ADS STATUS indicating success of add
1577 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1578 const char *name, const char *val)
1580 const char *values[2];
1582 values[0] = val;
1583 values[1] = NULL;
1585 if (!val)
1586 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1587 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1591 * Add an array of string values to a mod list
1592 * @param ctx An initialized TALLOC_CTX
1593 * @param mods An initialized ADS_MODLIST
1594 * @param name The attribute name to add
1595 * @param vals The array of string values to add - NULL means DELETE
1596 * @return ADS STATUS indicating success of add
1598 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1599 const char *name, const char **vals)
1601 if (!vals)
1602 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1603 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1604 name, (const void **) vals);
1607 #if 0
1609 * Add a single ber-encoded value to a mod list
1610 * @param ctx An initialized TALLOC_CTX
1611 * @param mods An initialized ADS_MODLIST
1612 * @param name The attribute name to add
1613 * @param val The value to add - NULL means DELETE
1614 * @return ADS STATUS indicating success of add
1616 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1617 const char *name, const struct berval *val)
1619 const struct berval *values[2];
1621 values[0] = val;
1622 values[1] = NULL;
1623 if (!val)
1624 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1625 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1626 name, (const void **) values);
1628 #endif
1631 * Perform an ldap modify
1632 * @param ads connection to ads server
1633 * @param mod_dn DistinguishedName to modify
1634 * @param mods list of modifications to perform
1635 * @return status of modify
1637 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1639 int ret,i;
1640 char *utf8_dn = NULL;
1641 size_t converted_size;
1643 this control is needed to modify that contains a currently
1644 non-existent attribute (but allowable for the object) to run
1646 LDAPControl PermitModify = {
1647 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1648 {0, NULL},
1649 (char) 1};
1650 LDAPControl *controls[2];
1652 controls[0] = &PermitModify;
1653 controls[1] = NULL;
1655 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1656 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1659 /* find the end of the list, marked by NULL or -1 */
1660 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1661 /* make sure the end of the list is NULL */
1662 mods[i] = NULL;
1663 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1664 (LDAPMod **) mods, controls, NULL);
1665 TALLOC_FREE(utf8_dn);
1666 return ADS_ERROR(ret);
1670 * Perform an ldap add
1671 * @param ads connection to ads server
1672 * @param new_dn DistinguishedName to add
1673 * @param mods list of attributes and values for DN
1674 * @return status of add
1676 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1678 int ret, i;
1679 char *utf8_dn = NULL;
1680 size_t converted_size;
1682 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1683 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1684 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1687 /* find the end of the list, marked by NULL or -1 */
1688 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1689 /* make sure the end of the list is NULL */
1690 mods[i] = NULL;
1692 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1693 TALLOC_FREE(utf8_dn);
1694 return ADS_ERROR(ret);
1698 * Delete a DistinguishedName
1699 * @param ads connection to ads server
1700 * @param new_dn DistinguishedName to delete
1701 * @return status of delete
1703 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1705 int ret;
1706 char *utf8_dn = NULL;
1707 size_t converted_size;
1708 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1709 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1710 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1713 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1714 TALLOC_FREE(utf8_dn);
1715 return ADS_ERROR(ret);
1719 * Build an org unit string
1720 * if org unit is Computers or blank then assume a container, otherwise
1721 * assume a / separated list of organisational units.
1722 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1723 * @param ads connection to ads server
1724 * @param org_unit Organizational unit
1725 * @return org unit string - caller must free
1727 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1729 char *ret = NULL;
1731 if (!org_unit || !*org_unit) {
1733 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1735 /* samba4 might not yet respond to a wellknownobject-query */
1736 return ret ? ret : SMB_STRDUP("cn=Computers");
1739 if (strequal(org_unit, "Computers")) {
1740 return SMB_STRDUP("cn=Computers");
1743 /* jmcd: removed "\\" from the separation chars, because it is
1744 needed as an escape for chars like '#' which are valid in an
1745 OU name */
1746 return ads_build_path(org_unit, "/", "ou=", 1);
1750 * Get a org unit string for a well-known GUID
1751 * @param ads connection to ads server
1752 * @param wknguid Well known GUID
1753 * @return org unit string - caller must free
1755 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1757 ADS_STATUS status;
1758 LDAPMessage *res = NULL;
1759 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1760 **bind_dn_exp = NULL;
1761 const char *attrs[] = {"distinguishedName", NULL};
1762 int new_ln, wkn_ln, bind_ln, i;
1764 if (wknguid == NULL) {
1765 return NULL;
1768 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1769 DEBUG(1, ("asprintf failed!\n"));
1770 return NULL;
1773 status = ads_search_dn(ads, &res, base, attrs);
1774 if (!ADS_ERR_OK(status)) {
1775 DEBUG(1,("Failed while searching for: %s\n", base));
1776 goto out;
1779 if (ads_count_replies(ads, res) != 1) {
1780 goto out;
1783 /* substitute the bind-path from the well-known-guid-search result */
1784 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1785 if (!wkn_dn) {
1786 goto out;
1789 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1790 if (!wkn_dn_exp) {
1791 goto out;
1794 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1795 if (!bind_dn_exp) {
1796 goto out;
1799 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1801 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1804 new_ln = wkn_ln - bind_ln;
1806 ret = SMB_STRDUP(wkn_dn_exp[0]);
1807 if (!ret) {
1808 goto out;
1811 for (i=1; i < new_ln; i++) {
1812 char *s = NULL;
1814 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1815 SAFE_FREE(ret);
1816 goto out;
1819 SAFE_FREE(ret);
1820 ret = SMB_STRDUP(s);
1821 free(s);
1822 if (!ret) {
1823 goto out;
1827 out:
1828 SAFE_FREE(base);
1829 ads_msgfree(ads, res);
1830 TALLOC_FREE(wkn_dn);
1831 if (wkn_dn_exp) {
1832 ldap_value_free(wkn_dn_exp);
1834 if (bind_dn_exp) {
1835 ldap_value_free(bind_dn_exp);
1838 return ret;
1842 * Adds (appends) an item to an attribute array, rather then
1843 * replacing the whole list
1844 * @param ctx An initialized TALLOC_CTX
1845 * @param mods An initialized ADS_MODLIST
1846 * @param name name of the ldap attribute to append to
1847 * @param vals an array of values to add
1848 * @return status of addition
1851 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1852 const char *name, const char **vals)
1854 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1855 (const void *) vals);
1859 * Determines the an account's current KVNO via an LDAP lookup
1860 * @param ads An initialized ADS_STRUCT
1861 * @param account_name the NT samaccountname.
1862 * @return the kvno for the account, or -1 in case of a failure.
1865 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1867 LDAPMessage *res = NULL;
1868 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1869 char *filter;
1870 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1871 char *dn_string = NULL;
1872 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1874 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1875 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1876 return kvno;
1878 ret = ads_search(ads, &res, filter, attrs);
1879 SAFE_FREE(filter);
1880 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1881 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1882 ads_msgfree(ads, res);
1883 return kvno;
1886 dn_string = ads_get_dn(ads, talloc_tos(), res);
1887 if (!dn_string) {
1888 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1889 ads_msgfree(ads, res);
1890 return kvno;
1892 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1893 TALLOC_FREE(dn_string);
1895 /* ---------------------------------------------------------
1896 * 0 is returned as a default KVNO from this point on...
1897 * This is done because Windows 2000 does not support key
1898 * version numbers. Chances are that a failure in the next
1899 * step is simply due to Windows 2000 being used for a
1900 * domain controller. */
1901 kvno = 0;
1903 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1904 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1905 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1906 ads_msgfree(ads, res);
1907 return kvno;
1910 /* Success */
1911 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1912 ads_msgfree(ads, res);
1913 return kvno;
1917 * Determines the computer account's current KVNO via an LDAP lookup
1918 * @param ads An initialized ADS_STRUCT
1919 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1920 * @return the kvno for the computer account, or -1 in case of a failure.
1923 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1925 char *computer_account = NULL;
1926 uint32_t kvno = -1;
1928 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1929 return kvno;
1932 kvno = ads_get_kvno(ads, computer_account);
1933 free(computer_account);
1935 return kvno;
1939 * This clears out all registered spn's for a given hostname
1940 * @param ads An initilaized ADS_STRUCT
1941 * @param machine_name the NetBIOS name of the computer.
1942 * @return 0 upon success, non-zero otherwise.
1945 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1947 TALLOC_CTX *ctx;
1948 LDAPMessage *res = NULL;
1949 ADS_MODLIST mods;
1950 const char *servicePrincipalName[1] = {NULL};
1951 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1952 char *dn_string = NULL;
1954 ret = ads_find_machine_acct(ads, &res, machine_name);
1955 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1956 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1957 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1958 ads_msgfree(ads, res);
1959 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1962 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1963 ctx = talloc_init("ads_clear_service_principal_names");
1964 if (!ctx) {
1965 ads_msgfree(ads, res);
1966 return ADS_ERROR(LDAP_NO_MEMORY);
1969 if (!(mods = ads_init_mods(ctx))) {
1970 talloc_destroy(ctx);
1971 ads_msgfree(ads, res);
1972 return ADS_ERROR(LDAP_NO_MEMORY);
1974 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1975 if (!ADS_ERR_OK(ret)) {
1976 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1977 ads_msgfree(ads, res);
1978 talloc_destroy(ctx);
1979 return ret;
1981 dn_string = ads_get_dn(ads, talloc_tos(), res);
1982 if (!dn_string) {
1983 talloc_destroy(ctx);
1984 ads_msgfree(ads, res);
1985 return ADS_ERROR(LDAP_NO_MEMORY);
1987 ret = ads_gen_mod(ads, dn_string, mods);
1988 TALLOC_FREE(dn_string);
1989 if (!ADS_ERR_OK(ret)) {
1990 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1991 machine_name));
1992 ads_msgfree(ads, res);
1993 talloc_destroy(ctx);
1994 return ret;
1997 ads_msgfree(ads, res);
1998 talloc_destroy(ctx);
1999 return ret;
2003 * @brief Search for an element in a string array.
2005 * @param[in] el_array The string array to search.
2007 * @param[in] num_el The number of elements in the string array.
2009 * @param[in] el The string to search.
2011 * @return True if found, false if not.
2013 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2015 size_t i;
2017 if (el_array == NULL || num_el == 0 || el == NULL) {
2018 return false;
2021 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2022 int cmp;
2024 cmp = strcasecmp_m(el_array[i], el);
2025 if (cmp == 0) {
2026 return true;
2030 return false;
2034 * @brief This gets the service principal names of an existing computer account.
2036 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2038 * @param[in] ads The ADS context to use.
2040 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2041 * identify the computer account.
2043 * @param[in] spn_array A pointer to store the array for SPNs.
2045 * @param[in] num_spns The number of principals stored in the array.
2047 * @return 0 on success, or a ADS error if a failure occured.
2049 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2050 ADS_STRUCT *ads,
2051 const char *machine_name,
2052 char ***spn_array,
2053 size_t *num_spns)
2055 ADS_STATUS status;
2056 LDAPMessage *res = NULL;
2057 int count;
2059 status = ads_find_machine_acct(ads,
2060 &res,
2061 machine_name);
2062 if (!ADS_ERR_OK(status)) {
2063 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2064 machine_name));
2065 return status;
2068 count = ads_count_replies(ads, res);
2069 if (count != 1) {
2070 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2071 goto done;
2074 *spn_array = ads_pull_strings(ads,
2075 mem_ctx,
2076 res,
2077 "servicePrincipalName",
2078 num_spns);
2080 done:
2081 ads_msgfree(ads, res);
2083 return status;
2087 * This adds a service principal name to an existing computer account
2088 * (found by hostname) in AD.
2089 * @param ads An initialized ADS_STRUCT
2090 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2091 * @param my_fqdn The fully qualified DNS name of the machine
2092 * @param spn A string of the service principal to add, i.e. 'host'
2093 * @return 0 upon sucess, or non-zero if a failure occurs
2096 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
2097 const char *my_fqdn, const char *spn)
2099 ADS_STATUS ret;
2100 TALLOC_CTX *ctx;
2101 LDAPMessage *res = NULL;
2102 char *psp1, *psp2;
2103 ADS_MODLIST mods;
2104 char *dn_string = NULL;
2105 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
2107 ret = ads_find_machine_acct(ads, &res, machine_name);
2108 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2109 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2110 machine_name));
2111 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2112 spn, machine_name, ads->config.realm));
2113 ads_msgfree(ads, res);
2114 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2117 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2118 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2119 ads_msgfree(ads, res);
2120 return ADS_ERROR(LDAP_NO_MEMORY);
2123 /* add short name spn */
2125 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2126 talloc_destroy(ctx);
2127 ads_msgfree(ads, res);
2128 return ADS_ERROR(LDAP_NO_MEMORY);
2130 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2131 ret = ADS_ERROR(LDAP_NO_MEMORY);
2132 goto out;
2134 servicePrincipalName[0] = psp1;
2136 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2137 psp1, machine_name));
2140 /* add fully qualified spn */
2142 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2143 ret = ADS_ERROR(LDAP_NO_MEMORY);
2144 goto out;
2146 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2147 ret = ADS_ERROR(LDAP_NO_MEMORY);
2148 goto out;
2150 servicePrincipalName[1] = psp2;
2152 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2153 psp2, machine_name));
2155 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2156 ret = ADS_ERROR(LDAP_NO_MEMORY);
2157 goto out;
2160 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2161 if (!ADS_ERR_OK(ret)) {
2162 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2163 goto out;
2166 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2167 ret = ADS_ERROR(LDAP_NO_MEMORY);
2168 goto out;
2171 ret = ads_gen_mod(ads, dn_string, mods);
2172 if (!ADS_ERR_OK(ret)) {
2173 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2174 goto out;
2177 out:
2178 TALLOC_FREE( ctx );
2179 ads_msgfree(ads, res);
2180 return ret;
2184 * adds a machine account to the ADS server
2185 * @param ads An intialized ADS_STRUCT
2186 * @param machine_name - the NetBIOS machine name of this account.
2187 * @param account_type A number indicating the type of account to create
2188 * @param org_unit The LDAP path in which to place this account
2189 * @return 0 upon success, or non-zero otherwise
2192 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2193 const char *org_unit)
2195 ADS_STATUS ret;
2196 char *samAccountName, *controlstr;
2197 TALLOC_CTX *ctx;
2198 ADS_MODLIST mods;
2199 char *machine_escaped = NULL;
2200 char *new_dn;
2201 const char *objectClass[] = {"top", "person", "organizationalPerson",
2202 "user", "computer", NULL};
2203 LDAPMessage *res = NULL;
2204 uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2205 UF_DONT_EXPIRE_PASSWD |\
2206 UF_ACCOUNTDISABLE );
2208 if (!(ctx = talloc_init("ads_add_machine_acct")))
2209 return ADS_ERROR(LDAP_NO_MEMORY);
2211 ret = ADS_ERROR(LDAP_NO_MEMORY);
2213 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2214 if (!machine_escaped) {
2215 goto done;
2218 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2219 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2221 if ( !new_dn || !samAccountName ) {
2222 goto done;
2225 #ifndef ENCTYPE_ARCFOUR_HMAC
2226 acct_control |= UF_USE_DES_KEY_ONLY;
2227 #endif
2229 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2230 goto done;
2233 if (!(mods = ads_init_mods(ctx))) {
2234 goto done;
2237 ads_mod_str(ctx, &mods, "cn", machine_name);
2238 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2239 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2240 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2242 ret = ads_gen_add(ads, new_dn, mods);
2244 done:
2245 SAFE_FREE(machine_escaped);
2246 ads_msgfree(ads, res);
2247 talloc_destroy(ctx);
2249 return ret;
2253 * move a machine account to another OU on the ADS server
2254 * @param ads - An intialized ADS_STRUCT
2255 * @param machine_name - the NetBIOS machine name of this account.
2256 * @param org_unit - The LDAP path in which to place this account
2257 * @param moved - whether we moved the machine account (optional)
2258 * @return 0 upon success, or non-zero otherwise
2261 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2262 const char *org_unit, bool *moved)
2264 ADS_STATUS rc;
2265 int ldap_status;
2266 LDAPMessage *res = NULL;
2267 char *filter = NULL;
2268 char *computer_dn = NULL;
2269 char *parent_dn;
2270 char *computer_rdn = NULL;
2271 bool need_move = False;
2273 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2274 rc = ADS_ERROR(LDAP_NO_MEMORY);
2275 goto done;
2278 /* Find pre-existing machine */
2279 rc = ads_search(ads, &res, filter, NULL);
2280 if (!ADS_ERR_OK(rc)) {
2281 goto done;
2284 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2285 if (!computer_dn) {
2286 rc = ADS_ERROR(LDAP_NO_MEMORY);
2287 goto done;
2290 parent_dn = ads_parent_dn(computer_dn);
2291 if (strequal(parent_dn, org_unit)) {
2292 goto done;
2295 need_move = True;
2297 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2298 rc = ADS_ERROR(LDAP_NO_MEMORY);
2299 goto done;
2302 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2303 org_unit, 1, NULL, NULL);
2304 rc = ADS_ERROR(ldap_status);
2306 done:
2307 ads_msgfree(ads, res);
2308 SAFE_FREE(filter);
2309 TALLOC_FREE(computer_dn);
2310 SAFE_FREE(computer_rdn);
2312 if (!ADS_ERR_OK(rc)) {
2313 need_move = False;
2316 if (moved) {
2317 *moved = need_move;
2320 return rc;
2324 dump a binary result from ldap
2326 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2328 int i, j;
2329 for (i=0; values[i]; i++) {
2330 printf("%s: ", field);
2331 for (j=0; j<values[i]->bv_len; j++) {
2332 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2334 printf("\n");
2338 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2340 int i;
2341 for (i=0; values[i]; i++) {
2342 NTSTATUS status;
2343 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2344 struct GUID guid;
2346 status = GUID_from_ndr_blob(&in, &guid);
2347 if (NT_STATUS_IS_OK(status)) {
2348 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2349 } else {
2350 printf("%s: INVALID GUID\n", field);
2356 dump a sid result from ldap
2358 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2360 int i;
2361 for (i=0; values[i]; i++) {
2362 struct dom_sid sid;
2363 fstring tmp;
2364 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2365 return;
2367 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2372 dump ntSecurityDescriptor
2374 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2376 TALLOC_CTX *frame = talloc_stackframe();
2377 struct security_descriptor *psd;
2378 NTSTATUS status;
2380 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2381 values[0]->bv_len, &psd);
2382 if (!NT_STATUS_IS_OK(status)) {
2383 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2384 nt_errstr(status)));
2385 TALLOC_FREE(frame);
2386 return;
2389 if (psd) {
2390 ads_disp_sd(ads, talloc_tos(), psd);
2393 TALLOC_FREE(frame);
2397 dump a string result from ldap
2399 static void dump_string(const char *field, char **values)
2401 int i;
2402 for (i=0; values[i]; i++) {
2403 printf("%s: %s\n", field, values[i]);
2408 dump a field from LDAP on stdout
2409 used for debugging
2412 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2414 const struct {
2415 const char *name;
2416 bool string;
2417 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2418 } handlers[] = {
2419 {"objectGUID", False, dump_guid},
2420 {"netbootGUID", False, dump_guid},
2421 {"nTSecurityDescriptor", False, dump_sd},
2422 {"dnsRecord", False, dump_binary},
2423 {"objectSid", False, dump_sid},
2424 {"tokenGroups", False, dump_sid},
2425 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2426 {"tokengroupsGlobalandUniversal", False, dump_sid},
2427 {"mS-DS-CreatorSID", False, dump_sid},
2428 {"msExchMailboxGuid", False, dump_guid},
2429 {NULL, True, NULL}
2431 int i;
2433 if (!field) { /* must be end of an entry */
2434 printf("\n");
2435 return False;
2438 for (i=0; handlers[i].name; i++) {
2439 if (strcasecmp_m(handlers[i].name, field) == 0) {
2440 if (!values) /* first time, indicate string or not */
2441 return handlers[i].string;
2442 handlers[i].handler(ads, field, (struct berval **) values);
2443 break;
2446 if (!handlers[i].name) {
2447 if (!values) /* first time, indicate string conversion */
2448 return True;
2449 dump_string(field, (char **)values);
2451 return False;
2455 * Dump a result from LDAP on stdout
2456 * used for debugging
2457 * @param ads connection to ads server
2458 * @param res Results to dump
2461 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2463 ads_process_results(ads, res, ads_dump_field, NULL);
2467 * Walk through results, calling a function for each entry found.
2468 * The function receives a field name, a berval * array of values,
2469 * and a data area passed through from the start. The function is
2470 * called once with null for field and values at the end of each
2471 * entry.
2472 * @param ads connection to ads server
2473 * @param res Results to process
2474 * @param fn Function for processing each result
2475 * @param data_area user-defined area to pass to function
2477 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2478 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2479 void *data_area)
2481 LDAPMessage *msg;
2482 TALLOC_CTX *ctx;
2483 size_t converted_size;
2485 if (!(ctx = talloc_init("ads_process_results")))
2486 return;
2488 for (msg = ads_first_entry(ads, res); msg;
2489 msg = ads_next_entry(ads, msg)) {
2490 char *utf8_field;
2491 BerElement *b;
2493 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2494 (LDAPMessage *)msg,&b);
2495 utf8_field;
2496 utf8_field=ldap_next_attribute(ads->ldap.ld,
2497 (LDAPMessage *)msg,b)) {
2498 struct berval **ber_vals;
2499 char **str_vals;
2500 char **utf8_vals;
2501 char *field;
2502 bool string;
2504 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2505 &converted_size))
2507 DEBUG(0,("ads_process_results: "
2508 "pull_utf8_talloc failed: %s",
2509 strerror(errno)));
2512 string = fn(ads, field, NULL, data_area);
2514 if (string) {
2515 const char **p;
2517 utf8_vals = ldap_get_values(ads->ldap.ld,
2518 (LDAPMessage *)msg, field);
2519 p = discard_const_p(const char *, utf8_vals);
2520 str_vals = ads_pull_strvals(ctx, p);
2521 fn(ads, field, (void **) str_vals, data_area);
2522 ldap_value_free(utf8_vals);
2523 } else {
2524 ber_vals = ldap_get_values_len(ads->ldap.ld,
2525 (LDAPMessage *)msg, field);
2526 fn(ads, field, (void **) ber_vals, data_area);
2528 ldap_value_free_len(ber_vals);
2530 ldap_memfree(utf8_field);
2532 ber_free(b, 0);
2533 talloc_free_children(ctx);
2534 fn(ads, NULL, NULL, data_area); /* completed an entry */
2537 talloc_destroy(ctx);
2541 * count how many replies are in a LDAPMessage
2542 * @param ads connection to ads server
2543 * @param res Results to count
2544 * @return number of replies
2546 int ads_count_replies(ADS_STRUCT *ads, void *res)
2548 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2552 * pull the first entry from a ADS result
2553 * @param ads connection to ads server
2554 * @param res Results of search
2555 * @return first entry from result
2557 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2559 return ldap_first_entry(ads->ldap.ld, res);
2563 * pull the next entry from a ADS result
2564 * @param ads connection to ads server
2565 * @param res Results of search
2566 * @return next entry from result
2568 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2570 return ldap_next_entry(ads->ldap.ld, res);
2574 * pull the first message from a ADS result
2575 * @param ads connection to ads server
2576 * @param res Results of search
2577 * @return first message from result
2579 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2581 return ldap_first_message(ads->ldap.ld, res);
2585 * pull the next message from a ADS result
2586 * @param ads connection to ads server
2587 * @param res Results of search
2588 * @return next message from result
2590 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2592 return ldap_next_message(ads->ldap.ld, res);
2596 * pull a single string from a ADS result
2597 * @param ads connection to ads server
2598 * @param mem_ctx TALLOC_CTX to use for allocating result string
2599 * @param msg Results of search
2600 * @param field Attribute to retrieve
2601 * @return Result string in talloc context
2603 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2604 const char *field)
2606 char **values;
2607 char *ret = NULL;
2608 char *ux_string;
2609 size_t converted_size;
2611 values = ldap_get_values(ads->ldap.ld, msg, field);
2612 if (!values)
2613 return NULL;
2615 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2616 &converted_size))
2618 ret = ux_string;
2620 ldap_value_free(values);
2621 return ret;
2625 * pull an array of strings from a ADS result
2626 * @param ads connection to ads server
2627 * @param mem_ctx TALLOC_CTX to use for allocating result string
2628 * @param msg Results of search
2629 * @param field Attribute to retrieve
2630 * @return Result strings in talloc context
2632 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2633 LDAPMessage *msg, const char *field,
2634 size_t *num_values)
2636 char **values;
2637 char **ret = NULL;
2638 int i;
2639 size_t converted_size;
2641 values = ldap_get_values(ads->ldap.ld, msg, field);
2642 if (!values)
2643 return NULL;
2645 *num_values = ldap_count_values(values);
2647 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2648 if (!ret) {
2649 ldap_value_free(values);
2650 return NULL;
2653 for (i=0;i<*num_values;i++) {
2654 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2655 &converted_size))
2657 ldap_value_free(values);
2658 return NULL;
2661 ret[i] = NULL;
2663 ldap_value_free(values);
2664 return ret;
2668 * pull an array of strings from a ADS result
2669 * (handle large multivalue attributes with range retrieval)
2670 * @param ads connection to ads server
2671 * @param mem_ctx TALLOC_CTX to use for allocating result string
2672 * @param msg Results of search
2673 * @param field Attribute to retrieve
2674 * @param current_strings strings returned by a previous call to this function
2675 * @param next_attribute The next query should ask for this attribute
2676 * @param num_values How many values did we get this time?
2677 * @param more_values Are there more values to get?
2678 * @return Result strings in talloc context
2680 char **ads_pull_strings_range(ADS_STRUCT *ads,
2681 TALLOC_CTX *mem_ctx,
2682 LDAPMessage *msg, const char *field,
2683 char **current_strings,
2684 const char **next_attribute,
2685 size_t *num_strings,
2686 bool *more_strings)
2688 char *attr;
2689 char *expected_range_attrib, *range_attr;
2690 BerElement *ptr = NULL;
2691 char **strings;
2692 char **new_strings;
2693 size_t num_new_strings;
2694 unsigned long int range_start;
2695 unsigned long int range_end;
2697 /* we might have been given the whole lot anyway */
2698 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2699 *more_strings = False;
2700 return strings;
2703 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2705 /* look for Range result */
2706 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2707 attr;
2708 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2709 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2710 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2711 range_attr = attr;
2712 break;
2714 ldap_memfree(attr);
2716 if (!attr) {
2717 ber_free(ptr, 0);
2718 /* nothing here - this field is just empty */
2719 *more_strings = False;
2720 return NULL;
2723 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2724 &range_start, &range_end) == 2) {
2725 *more_strings = True;
2726 } else {
2727 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2728 &range_start) == 1) {
2729 *more_strings = False;
2730 } else {
2731 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2732 range_attr));
2733 ldap_memfree(range_attr);
2734 *more_strings = False;
2735 return NULL;
2739 if ((*num_strings) != range_start) {
2740 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2741 " - aborting range retreival\n",
2742 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2743 ldap_memfree(range_attr);
2744 *more_strings = False;
2745 return NULL;
2748 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2750 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2751 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2752 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2753 range_attr, (unsigned long int)range_end - range_start + 1,
2754 (unsigned long int)num_new_strings));
2755 ldap_memfree(range_attr);
2756 *more_strings = False;
2757 return NULL;
2760 strings = talloc_realloc(mem_ctx, current_strings, char *,
2761 *num_strings + num_new_strings);
2763 if (strings == NULL) {
2764 ldap_memfree(range_attr);
2765 *more_strings = False;
2766 return NULL;
2769 if (new_strings && num_new_strings) {
2770 memcpy(&strings[*num_strings], new_strings,
2771 sizeof(*new_strings) * num_new_strings);
2774 (*num_strings) += num_new_strings;
2776 if (*more_strings) {
2777 *next_attribute = talloc_asprintf(mem_ctx,
2778 "%s;range=%d-*",
2779 field,
2780 (int)*num_strings);
2782 if (!*next_attribute) {
2783 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2784 ldap_memfree(range_attr);
2785 *more_strings = False;
2786 return NULL;
2790 ldap_memfree(range_attr);
2792 return strings;
2796 * pull a single uint32_t from a ADS result
2797 * @param ads connection to ads server
2798 * @param msg Results of search
2799 * @param field Attribute to retrieve
2800 * @param v Pointer to int to store result
2801 * @return boolean inidicating success
2803 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2804 uint32_t *v)
2806 char **values;
2808 values = ldap_get_values(ads->ldap.ld, msg, field);
2809 if (!values)
2810 return False;
2811 if (!values[0]) {
2812 ldap_value_free(values);
2813 return False;
2816 *v = atoi(values[0]);
2817 ldap_value_free(values);
2818 return True;
2822 * pull a single objectGUID from an ADS result
2823 * @param ads connection to ADS server
2824 * @param msg results of search
2825 * @param guid 37-byte area to receive text guid
2826 * @return boolean indicating success
2828 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2830 DATA_BLOB blob;
2831 NTSTATUS status;
2833 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2834 &blob)) {
2835 return false;
2838 status = GUID_from_ndr_blob(&blob, guid);
2839 talloc_free(blob.data);
2840 return NT_STATUS_IS_OK(status);
2845 * pull a single struct dom_sid from a ADS result
2846 * @param ads connection to ads server
2847 * @param msg Results of search
2848 * @param field Attribute to retrieve
2849 * @param sid Pointer to sid to store result
2850 * @return boolean inidicating success
2852 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2853 struct dom_sid *sid)
2855 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2859 * pull an array of struct dom_sids from a ADS result
2860 * @param ads connection to ads server
2861 * @param mem_ctx TALLOC_CTX for allocating sid array
2862 * @param msg Results of search
2863 * @param field Attribute to retrieve
2864 * @param sids pointer to sid array to allocate
2865 * @return the count of SIDs pulled
2867 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2868 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2870 struct berval **values;
2871 bool ret;
2872 int count, i;
2874 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2876 if (!values)
2877 return 0;
2879 for (i=0; values[i]; i++)
2880 /* nop */ ;
2882 if (i) {
2883 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2884 if (!(*sids)) {
2885 ldap_value_free_len(values);
2886 return 0;
2888 } else {
2889 (*sids) = NULL;
2892 count = 0;
2893 for (i=0; values[i]; i++) {
2894 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2895 if (ret) {
2896 DEBUG(10, ("pulling SID: %s\n",
2897 sid_string_dbg(&(*sids)[count])));
2898 count++;
2902 ldap_value_free_len(values);
2903 return count;
2907 * pull a struct security_descriptor from a ADS result
2908 * @param ads connection to ads server
2909 * @param mem_ctx TALLOC_CTX for allocating sid array
2910 * @param msg Results of search
2911 * @param field Attribute to retrieve
2912 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2913 * @return boolean inidicating success
2915 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2916 LDAPMessage *msg, const char *field,
2917 struct security_descriptor **sd)
2919 struct berval **values;
2920 bool ret = true;
2922 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2924 if (!values) return false;
2926 if (values[0]) {
2927 NTSTATUS status;
2928 status = unmarshall_sec_desc(mem_ctx,
2929 (uint8_t *)values[0]->bv_val,
2930 values[0]->bv_len, sd);
2931 if (!NT_STATUS_IS_OK(status)) {
2932 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2933 nt_errstr(status)));
2934 ret = false;
2938 ldap_value_free_len(values);
2939 return ret;
2943 * in order to support usernames longer than 21 characters we need to
2944 * use both the sAMAccountName and the userPrincipalName attributes
2945 * It seems that not all users have the userPrincipalName attribute set
2947 * @param ads connection to ads server
2948 * @param mem_ctx TALLOC_CTX for allocating sid array
2949 * @param msg Results of search
2950 * @return the username
2952 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2953 LDAPMessage *msg)
2955 #if 0 /* JERRY */
2956 char *ret, *p;
2958 /* lookup_name() only works on the sAMAccountName to
2959 returning the username portion of userPrincipalName
2960 breaks winbindd_getpwnam() */
2962 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2963 if (ret && (p = strchr_m(ret, '@'))) {
2964 *p = 0;
2965 return ret;
2967 #endif
2968 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2973 * find the update serial number - this is the core of the ldap cache
2974 * @param ads connection to ads server
2975 * @param ads connection to ADS server
2976 * @param usn Pointer to retrieved update serial number
2977 * @return status of search
2979 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2981 const char *attrs[] = {"highestCommittedUSN", NULL};
2982 ADS_STATUS status;
2983 LDAPMessage *res;
2985 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2986 if (!ADS_ERR_OK(status))
2987 return status;
2989 if (ads_count_replies(ads, res) != 1) {
2990 ads_msgfree(ads, res);
2991 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2994 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2995 ads_msgfree(ads, res);
2996 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2999 ads_msgfree(ads, res);
3000 return ADS_SUCCESS;
3003 /* parse a ADS timestring - typical string is
3004 '20020917091222.0Z0' which means 09:12.22 17th September
3005 2002, timezone 0 */
3006 static time_t ads_parse_time(const char *str)
3008 struct tm tm;
3010 ZERO_STRUCT(tm);
3012 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3013 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3014 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3015 return 0;
3017 tm.tm_year -= 1900;
3018 tm.tm_mon -= 1;
3020 return timegm(&tm);
3023 /********************************************************************
3024 ********************************************************************/
3026 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3028 const char *attrs[] = {"currentTime", NULL};
3029 ADS_STATUS status;
3030 LDAPMessage *res;
3031 char *timestr;
3032 TALLOC_CTX *ctx;
3033 ADS_STRUCT *ads_s = ads;
3035 if (!(ctx = talloc_init("ads_current_time"))) {
3036 return ADS_ERROR(LDAP_NO_MEMORY);
3039 /* establish a new ldap tcp session if necessary */
3041 if ( !ads->ldap.ld ) {
3042 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3043 ads->server.ldap_server )) == NULL )
3045 goto done;
3047 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3048 status = ads_connect( ads_s );
3049 if ( !ADS_ERR_OK(status))
3050 goto done;
3053 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3054 if (!ADS_ERR_OK(status)) {
3055 goto done;
3058 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
3059 if (!timestr) {
3060 ads_msgfree(ads_s, res);
3061 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3062 goto done;
3065 /* but save the time and offset in the original ADS_STRUCT */
3067 ads->config.current_time = ads_parse_time(timestr);
3069 if (ads->config.current_time != 0) {
3070 ads->auth.time_offset = ads->config.current_time - time(NULL);
3071 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3074 ads_msgfree(ads, res);
3076 status = ADS_SUCCESS;
3078 done:
3079 /* free any temporary ads connections */
3080 if ( ads_s != ads ) {
3081 ads_destroy( &ads_s );
3083 talloc_destroy(ctx);
3085 return status;
3088 /********************************************************************
3089 ********************************************************************/
3091 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3093 const char *attrs[] = {"domainFunctionality", NULL};
3094 ADS_STATUS status;
3095 LDAPMessage *res;
3096 ADS_STRUCT *ads_s = ads;
3098 *val = DS_DOMAIN_FUNCTION_2000;
3100 /* establish a new ldap tcp session if necessary */
3102 if ( !ads->ldap.ld ) {
3103 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3104 ads->server.ldap_server )) == NULL )
3106 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3107 goto done;
3109 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3110 status = ads_connect( ads_s );
3111 if ( !ADS_ERR_OK(status))
3112 goto done;
3115 /* If the attribute does not exist assume it is a Windows 2000
3116 functional domain */
3118 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3119 if (!ADS_ERR_OK(status)) {
3120 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3121 status = ADS_SUCCESS;
3123 goto done;
3126 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3127 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3129 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3132 ads_msgfree(ads, res);
3134 done:
3135 /* free any temporary ads connections */
3136 if ( ads_s != ads ) {
3137 ads_destroy( &ads_s );
3140 return status;
3144 * find the domain sid for our domain
3145 * @param ads connection to ads server
3146 * @param sid Pointer to domain sid
3147 * @return status of search
3149 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3151 const char *attrs[] = {"objectSid", NULL};
3152 LDAPMessage *res;
3153 ADS_STATUS rc;
3155 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3156 attrs, &res);
3157 if (!ADS_ERR_OK(rc)) return rc;
3158 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3159 ads_msgfree(ads, res);
3160 return ADS_ERROR_SYSTEM(ENOENT);
3162 ads_msgfree(ads, res);
3164 return ADS_SUCCESS;
3168 * find our site name
3169 * @param ads connection to ads server
3170 * @param mem_ctx Pointer to talloc context
3171 * @param site_name Pointer to the sitename
3172 * @return status of search
3174 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3176 ADS_STATUS status;
3177 LDAPMessage *res;
3178 const char *dn, *service_name;
3179 const char *attrs[] = { "dsServiceName", NULL };
3181 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3182 if (!ADS_ERR_OK(status)) {
3183 return status;
3186 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3187 if (service_name == NULL) {
3188 ads_msgfree(ads, res);
3189 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3192 ads_msgfree(ads, res);
3194 /* go up three levels */
3195 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3196 if (dn == NULL) {
3197 return ADS_ERROR(LDAP_NO_MEMORY);
3200 *site_name = talloc_strdup(mem_ctx, dn);
3201 if (*site_name == NULL) {
3202 return ADS_ERROR(LDAP_NO_MEMORY);
3205 return status;
3207 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3212 * find the site dn where a machine resides
3213 * @param ads connection to ads server
3214 * @param mem_ctx Pointer to talloc context
3215 * @param computer_name name of the machine
3216 * @param site_name Pointer to the sitename
3217 * @return status of search
3219 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3221 ADS_STATUS status;
3222 LDAPMessage *res;
3223 const char *parent, *filter;
3224 char *config_context = NULL;
3225 char *dn;
3227 /* shortcut a query */
3228 if (strequal(computer_name, ads->config.ldap_server_name)) {
3229 return ads_site_dn(ads, mem_ctx, site_dn);
3232 status = ads_config_path(ads, mem_ctx, &config_context);
3233 if (!ADS_ERR_OK(status)) {
3234 return status;
3237 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3238 if (filter == NULL) {
3239 return ADS_ERROR(LDAP_NO_MEMORY);
3242 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3243 filter, NULL, &res);
3244 if (!ADS_ERR_OK(status)) {
3245 return status;
3248 if (ads_count_replies(ads, res) != 1) {
3249 ads_msgfree(ads, res);
3250 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3253 dn = ads_get_dn(ads, mem_ctx, res);
3254 if (dn == NULL) {
3255 ads_msgfree(ads, res);
3256 return ADS_ERROR(LDAP_NO_MEMORY);
3259 /* go up three levels */
3260 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3261 if (parent == NULL) {
3262 ads_msgfree(ads, res);
3263 TALLOC_FREE(dn);
3264 return ADS_ERROR(LDAP_NO_MEMORY);
3267 *site_dn = talloc_strdup(mem_ctx, parent);
3268 if (*site_dn == NULL) {
3269 ads_msgfree(ads, res);
3270 TALLOC_FREE(dn);
3271 return ADS_ERROR(LDAP_NO_MEMORY);
3274 TALLOC_FREE(dn);
3275 ads_msgfree(ads, res);
3277 return status;
3281 * get the upn suffixes for a domain
3282 * @param ads connection to ads server
3283 * @param mem_ctx Pointer to talloc context
3284 * @param suffixes Pointer to an array of suffixes
3285 * @param num_suffixes Pointer to the number of suffixes
3286 * @return status of search
3288 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3290 ADS_STATUS status;
3291 LDAPMessage *res;
3292 const char *base;
3293 char *config_context = NULL;
3294 const char *attrs[] = { "uPNSuffixes", NULL };
3296 status = ads_config_path(ads, mem_ctx, &config_context);
3297 if (!ADS_ERR_OK(status)) {
3298 return status;
3301 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3302 if (base == NULL) {
3303 return ADS_ERROR(LDAP_NO_MEMORY);
3306 status = ads_search_dn(ads, &res, base, attrs);
3307 if (!ADS_ERR_OK(status)) {
3308 return status;
3311 if (ads_count_replies(ads, res) != 1) {
3312 ads_msgfree(ads, res);
3313 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3316 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3317 if ((*suffixes) == NULL) {
3318 ads_msgfree(ads, res);
3319 return ADS_ERROR(LDAP_NO_MEMORY);
3322 ads_msgfree(ads, res);
3324 return status;
3328 * get the joinable ous for a domain
3329 * @param ads connection to ads server
3330 * @param mem_ctx Pointer to talloc context
3331 * @param ous Pointer to an array of ous
3332 * @param num_ous Pointer to the number of ous
3333 * @return status of search
3335 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3336 TALLOC_CTX *mem_ctx,
3337 char ***ous,
3338 size_t *num_ous)
3340 ADS_STATUS status;
3341 LDAPMessage *res = NULL;
3342 LDAPMessage *msg = NULL;
3343 const char *attrs[] = { "dn", NULL };
3344 int count = 0;
3346 status = ads_search(ads, &res,
3347 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3348 attrs);
3349 if (!ADS_ERR_OK(status)) {
3350 return status;
3353 count = ads_count_replies(ads, res);
3354 if (count < 1) {
3355 ads_msgfree(ads, res);
3356 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3359 for (msg = ads_first_entry(ads, res); msg;
3360 msg = ads_next_entry(ads, msg)) {
3361 const char **p = discard_const_p(const char *, *ous);
3362 char *dn = NULL;
3364 dn = ads_get_dn(ads, talloc_tos(), msg);
3365 if (!dn) {
3366 ads_msgfree(ads, res);
3367 return ADS_ERROR(LDAP_NO_MEMORY);
3370 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3371 TALLOC_FREE(dn);
3372 ads_msgfree(ads, res);
3373 return ADS_ERROR(LDAP_NO_MEMORY);
3376 TALLOC_FREE(dn);
3377 *ous = discard_const_p(char *, p);
3380 ads_msgfree(ads, res);
3382 return status;
3387 * pull a struct dom_sid from an extended dn string
3388 * @param mem_ctx TALLOC_CTX
3389 * @param extended_dn string
3390 * @param flags string type of extended_dn
3391 * @param sid pointer to a struct dom_sid
3392 * @return NT_STATUS_OK on success,
3393 * NT_INVALID_PARAMETER on error,
3394 * NT_STATUS_NOT_FOUND if no SID present
3396 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3397 const char *extended_dn,
3398 enum ads_extended_dn_flags flags,
3399 struct dom_sid *sid)
3401 char *p, *q, *dn;
3403 if (!extended_dn) {
3404 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3407 /* otherwise extended_dn gets stripped off */
3408 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3409 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3412 * ADS_EXTENDED_DN_HEX_STRING:
3413 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3415 * ADS_EXTENDED_DN_STRING (only with w2k3):
3416 * <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
3418 * Object with no SID, such as an Exchange Public Folder
3419 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3422 p = strchr(dn, ';');
3423 if (!p) {
3424 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3427 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3428 DEBUG(5,("No SID present in extended dn\n"));
3429 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3432 p += strlen(";<SID=");
3434 q = strchr(p, '>');
3435 if (!q) {
3436 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3439 *q = '\0';
3441 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3443 switch (flags) {
3445 case ADS_EXTENDED_DN_STRING:
3446 if (!string_to_sid(sid, p)) {
3447 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3449 break;
3450 case ADS_EXTENDED_DN_HEX_STRING: {
3451 fstring buf;
3452 size_t buf_len;
3454 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3455 if (buf_len == 0) {
3456 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3459 if (!sid_parse(buf, buf_len, sid)) {
3460 DEBUG(10,("failed to parse sid\n"));
3461 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3463 break;
3465 default:
3466 DEBUG(10,("unknown extended dn format\n"));
3467 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3470 return ADS_ERROR_NT(NT_STATUS_OK);
3473 /********************************************************************
3474 ********************************************************************/
3476 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3478 LDAPMessage *res = NULL;
3479 ADS_STATUS status;
3480 int count = 0;
3481 char *name = NULL;
3483 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3484 if (!ADS_ERR_OK(status)) {
3485 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3486 lp_netbios_name()));
3487 goto out;
3490 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3491 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3492 goto out;
3495 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3496 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3499 out:
3500 ads_msgfree(ads, res);
3502 return name;
3505 /********************************************************************
3506 ********************************************************************/
3508 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3510 LDAPMessage *res = NULL;
3511 ADS_STATUS status;
3512 int count = 0;
3513 char *name = NULL;
3515 status = ads_find_machine_acct(ads, &res, machine_name);
3516 if (!ADS_ERR_OK(status)) {
3517 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3518 lp_netbios_name()));
3519 goto out;
3522 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3523 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3524 goto out;
3527 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3528 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3531 out:
3532 ads_msgfree(ads, res);
3534 return name;
3537 /********************************************************************
3538 ********************************************************************/
3540 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3542 LDAPMessage *res = NULL;
3543 ADS_STATUS status;
3544 int count = 0;
3545 char *name = NULL;
3547 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3548 if (!ADS_ERR_OK(status)) {
3549 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3550 lp_netbios_name()));
3551 goto out;
3554 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3555 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3556 goto out;
3559 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3560 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3563 out:
3564 ads_msgfree(ads, res);
3566 return name;
3569 #if 0
3571 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3574 * Join a machine to a realm
3575 * Creates the machine account and sets the machine password
3576 * @param ads connection to ads server
3577 * @param machine name of host to add
3578 * @param org_unit Organizational unit to place machine in
3579 * @return status of join
3581 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3582 uint32_t account_type, const char *org_unit)
3584 ADS_STATUS status;
3585 LDAPMessage *res = NULL;
3586 char *machine;
3588 /* machine name must be lowercase */
3589 machine = SMB_STRDUP(machine_name);
3590 strlower_m(machine);
3593 status = ads_find_machine_acct(ads, (void **)&res, machine);
3594 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3595 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3596 status = ads_leave_realm(ads, machine);
3597 if (!ADS_ERR_OK(status)) {
3598 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3599 machine, ads->config.realm));
3600 return status;
3604 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3605 if (!ADS_ERR_OK(status)) {
3606 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3607 SAFE_FREE(machine);
3608 return status;
3611 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3612 if (!ADS_ERR_OK(status)) {
3613 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3614 SAFE_FREE(machine);
3615 return status;
3618 SAFE_FREE(machine);
3619 ads_msgfree(ads, res);
3621 return status;
3623 #endif
3626 * Delete a machine from the realm
3627 * @param ads connection to ads server
3628 * @param hostname Machine to remove
3629 * @return status of delete
3631 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3633 ADS_STATUS status;
3634 void *msg;
3635 LDAPMessage *res;
3636 char *hostnameDN, *host;
3637 int rc;
3638 LDAPControl ldap_control;
3639 LDAPControl * pldap_control[2] = {NULL, NULL};
3641 pldap_control[0] = &ldap_control;
3642 memset(&ldap_control, 0, sizeof(LDAPControl));
3643 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3645 /* hostname must be lowercase */
3646 host = SMB_STRDUP(hostname);
3647 if (!strlower_m(host)) {
3648 SAFE_FREE(host);
3649 return ADS_ERROR_SYSTEM(EINVAL);
3652 status = ads_find_machine_acct(ads, &res, host);
3653 if (!ADS_ERR_OK(status)) {
3654 DEBUG(0, ("Host account for %s does not exist.\n", host));
3655 SAFE_FREE(host);
3656 return status;
3659 msg = ads_first_entry(ads, res);
3660 if (!msg) {
3661 SAFE_FREE(host);
3662 return ADS_ERROR_SYSTEM(ENOENT);
3665 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3666 if (hostnameDN == NULL) {
3667 SAFE_FREE(host);
3668 return ADS_ERROR_SYSTEM(ENOENT);
3671 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3672 if (rc) {
3673 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3674 }else {
3675 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3678 if (rc != LDAP_SUCCESS) {
3679 const char *attrs[] = { "cn", NULL };
3680 LDAPMessage *msg_sub;
3682 /* we only search with scope ONE, we do not expect any further
3683 * objects to be created deeper */
3685 status = ads_do_search_retry(ads, hostnameDN,
3686 LDAP_SCOPE_ONELEVEL,
3687 "(objectclass=*)", attrs, &res);
3689 if (!ADS_ERR_OK(status)) {
3690 SAFE_FREE(host);
3691 TALLOC_FREE(hostnameDN);
3692 return status;
3695 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3696 msg_sub = ads_next_entry(ads, msg_sub)) {
3698 char *dn = NULL;
3700 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3701 SAFE_FREE(host);
3702 TALLOC_FREE(hostnameDN);
3703 return ADS_ERROR(LDAP_NO_MEMORY);
3706 status = ads_del_dn(ads, dn);
3707 if (!ADS_ERR_OK(status)) {
3708 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3709 SAFE_FREE(host);
3710 TALLOC_FREE(dn);
3711 TALLOC_FREE(hostnameDN);
3712 return status;
3715 TALLOC_FREE(dn);
3718 /* there should be no subordinate objects anymore */
3719 status = ads_do_search_retry(ads, hostnameDN,
3720 LDAP_SCOPE_ONELEVEL,
3721 "(objectclass=*)", attrs, &res);
3723 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3724 SAFE_FREE(host);
3725 TALLOC_FREE(hostnameDN);
3726 return status;
3729 /* delete hostnameDN now */
3730 status = ads_del_dn(ads, hostnameDN);
3731 if (!ADS_ERR_OK(status)) {
3732 SAFE_FREE(host);
3733 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3734 TALLOC_FREE(hostnameDN);
3735 return status;
3739 TALLOC_FREE(hostnameDN);
3741 status = ads_find_machine_acct(ads, &res, host);
3742 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3743 DEBUG(3, ("Failed to remove host account.\n"));
3744 SAFE_FREE(host);
3745 return status;
3748 SAFE_FREE(host);
3749 return status;
3753 * pull all token-sids from an LDAP dn
3754 * @param ads connection to ads server
3755 * @param mem_ctx TALLOC_CTX for allocating sid array
3756 * @param dn of LDAP object
3757 * @param user_sid pointer to struct dom_sid (objectSid)
3758 * @param primary_group_sid pointer to struct dom_sid (self composed)
3759 * @param sids pointer to sid array to allocate
3760 * @param num_sids counter of SIDs pulled
3761 * @return status of token query
3763 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3764 TALLOC_CTX *mem_ctx,
3765 const char *dn,
3766 struct dom_sid *user_sid,
3767 struct dom_sid *primary_group_sid,
3768 struct dom_sid **sids,
3769 size_t *num_sids)
3771 ADS_STATUS status;
3772 LDAPMessage *res = NULL;
3773 int count = 0;
3774 size_t tmp_num_sids;
3775 struct dom_sid *tmp_sids;
3776 struct dom_sid tmp_user_sid;
3777 struct dom_sid tmp_primary_group_sid;
3778 uint32_t pgid;
3779 const char *attrs[] = {
3780 "objectSid",
3781 "tokenGroups",
3782 "primaryGroupID",
3783 NULL
3786 status = ads_search_retry_dn(ads, &res, dn, attrs);
3787 if (!ADS_ERR_OK(status)) {
3788 return status;
3791 count = ads_count_replies(ads, res);
3792 if (count != 1) {
3793 ads_msgfree(ads, res);
3794 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3797 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3798 ads_msgfree(ads, res);
3799 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3802 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3803 ads_msgfree(ads, res);
3804 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3808 /* hack to compose the primary group sid without knowing the
3809 * domsid */
3811 struct dom_sid domsid;
3813 sid_copy(&domsid, &tmp_user_sid);
3815 if (!sid_split_rid(&domsid, NULL)) {
3816 ads_msgfree(ads, res);
3817 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3820 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3821 ads_msgfree(ads, res);
3822 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3826 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3828 if (tmp_num_sids == 0 || !tmp_sids) {
3829 ads_msgfree(ads, res);
3830 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3833 if (num_sids) {
3834 *num_sids = tmp_num_sids;
3837 if (sids) {
3838 *sids = tmp_sids;
3841 if (user_sid) {
3842 *user_sid = tmp_user_sid;
3845 if (primary_group_sid) {
3846 *primary_group_sid = tmp_primary_group_sid;
3849 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3851 ads_msgfree(ads, res);
3852 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3856 * Find a sAMAccoutName in LDAP
3857 * @param ads connection to ads server
3858 * @param mem_ctx TALLOC_CTX for allocating sid array
3859 * @param samaccountname to search
3860 * @param uac_ret uint32_t pointer userAccountControl attribute value
3861 * @param dn_ret pointer to dn
3862 * @return status of token query
3864 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3865 TALLOC_CTX *mem_ctx,
3866 const char *samaccountname,
3867 uint32_t *uac_ret,
3868 const char **dn_ret)
3870 ADS_STATUS status;
3871 const char *attrs[] = { "userAccountControl", NULL };
3872 const char *filter;
3873 LDAPMessage *res = NULL;
3874 char *dn = NULL;
3875 uint32_t uac = 0;
3877 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3878 samaccountname);
3879 if (filter == NULL) {
3880 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3881 goto out;
3884 status = ads_do_search_all(ads, ads->config.bind_path,
3885 LDAP_SCOPE_SUBTREE,
3886 filter, attrs, &res);
3888 if (!ADS_ERR_OK(status)) {
3889 goto out;
3892 if (ads_count_replies(ads, res) != 1) {
3893 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3894 goto out;
3897 dn = ads_get_dn(ads, talloc_tos(), res);
3898 if (dn == NULL) {
3899 status = ADS_ERROR(LDAP_NO_MEMORY);
3900 goto out;
3903 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3904 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3905 goto out;
3908 if (uac_ret) {
3909 *uac_ret = uac;
3912 if (dn_ret) {
3913 *dn_ret = talloc_strdup(mem_ctx, dn);
3914 if (!*dn_ret) {
3915 status = ADS_ERROR(LDAP_NO_MEMORY);
3916 goto out;
3919 out:
3920 TALLOC_FREE(dn);
3921 ads_msgfree(ads, res);
3923 return status;
3927 * find our configuration path
3928 * @param ads connection to ads server
3929 * @param mem_ctx Pointer to talloc context
3930 * @param config_path Pointer to the config path
3931 * @return status of search
3933 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3934 TALLOC_CTX *mem_ctx,
3935 char **config_path)
3937 ADS_STATUS status;
3938 LDAPMessage *res = NULL;
3939 const char *config_context = NULL;
3940 const char *attrs[] = { "configurationNamingContext", NULL };
3942 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3943 "(objectclass=*)", attrs, &res);
3944 if (!ADS_ERR_OK(status)) {
3945 return status;
3948 config_context = ads_pull_string(ads, mem_ctx, res,
3949 "configurationNamingContext");
3950 ads_msgfree(ads, res);
3951 if (!config_context) {
3952 return ADS_ERROR(LDAP_NO_MEMORY);
3955 if (config_path) {
3956 *config_path = talloc_strdup(mem_ctx, config_context);
3957 if (!*config_path) {
3958 return ADS_ERROR(LDAP_NO_MEMORY);
3962 return ADS_ERROR(LDAP_SUCCESS);
3966 * find the displayName of an extended right
3967 * @param ads connection to ads server
3968 * @param config_path The config path
3969 * @param mem_ctx Pointer to talloc context
3970 * @param GUID struct of the rightsGUID
3971 * @return status of search
3973 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3974 const char *config_path,
3975 TALLOC_CTX *mem_ctx,
3976 const struct GUID *rights_guid)
3978 ADS_STATUS rc;
3979 LDAPMessage *res = NULL;
3980 char *expr = NULL;
3981 const char *attrs[] = { "displayName", NULL };
3982 const char *result = NULL;
3983 const char *path;
3985 if (!ads || !mem_ctx || !rights_guid) {
3986 goto done;
3989 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3990 GUID_string(mem_ctx, rights_guid));
3991 if (!expr) {
3992 goto done;
3995 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3996 if (!path) {
3997 goto done;
4000 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4001 expr, attrs, &res);
4002 if (!ADS_ERR_OK(rc)) {
4003 goto done;
4006 if (ads_count_replies(ads, res) != 1) {
4007 goto done;
4010 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4012 done:
4013 ads_msgfree(ads, res);
4014 return result;
4018 * verify or build and verify an account ou
4019 * @param mem_ctx Pointer to talloc context
4020 * @param ads connection to ads server
4021 * @param account_ou
4022 * @return status of search
4025 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4026 ADS_STRUCT *ads,
4027 const char **account_ou)
4029 char **exploded_dn;
4030 const char *name;
4031 char *ou_string;
4033 exploded_dn = ldap_explode_dn(*account_ou, 0);
4034 if (exploded_dn) {
4035 ldap_value_free(exploded_dn);
4036 return ADS_SUCCESS;
4039 ou_string = ads_ou_string(ads, *account_ou);
4040 if (!ou_string) {
4041 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4044 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4045 ads->config.bind_path);
4046 SAFE_FREE(ou_string);
4048 if (!name) {
4049 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4052 exploded_dn = ldap_explode_dn(name, 0);
4053 if (!exploded_dn) {
4054 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4056 ldap_value_free(exploded_dn);
4058 *account_ou = name;
4059 return ADS_SUCCESS;
4062 #endif