libads: further split resolve_and_ping into dns and netbios implementations
[Samba.git] / source3 / libads / ldap.c
blob5c53c6326e0a51f999303e4038a1e05fe8a131e0
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(1, ("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(1, ("ads_find_dc: failed to find a valid DC on "
519 "our site (%s), "
520 "trying to find another DC\n",
521 sitename));
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(1, ("ads_find_dc: falling back to netbios "
540 "name resolution for domain %s\n",
541 c_domain));
544 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
547 return status;
550 /*********************************************************************
551 *********************************************************************/
553 static NTSTATUS ads_lookup_site(void)
555 ADS_STRUCT *ads = NULL;
556 ADS_STATUS ads_status;
557 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
559 ads = ads_init(lp_realm(), NULL, NULL);
560 if (!ads) {
561 return NT_STATUS_NO_MEMORY;
564 /* The NO_BIND here will find a DC and set the client site
565 but not establish the TCP connection */
567 ads->auth.flags = ADS_AUTH_NO_BIND;
568 ads_status = ads_connect(ads);
569 if (!ADS_ERR_OK(ads_status)) {
570 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
571 ads_errstr(ads_status)));
573 nt_status = ads_ntstatus(ads_status);
575 if (ads) {
576 ads_destroy(&ads);
579 return nt_status;
582 /*********************************************************************
583 *********************************************************************/
585 static const char* host_dns_domain(const char *fqdn)
587 const char *p = fqdn;
589 /* go to next char following '.' */
591 if ((p = strchr_m(fqdn, '.')) != NULL) {
592 p++;
595 return p;
600 * Connect to the Global Catalog server
601 * @param ads Pointer to an existing ADS_STRUCT
602 * @return status of connection
604 * Simple wrapper around ads_connect() that fills in the
605 * GC ldap server information
608 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
610 TALLOC_CTX *frame = talloc_stackframe();
611 struct dns_rr_srv *gcs_list;
612 int num_gcs;
613 const char *realm = ads->server.realm;
614 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
615 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
616 int i;
617 bool done = false;
618 char *sitename = NULL;
620 if (!realm)
621 realm = lp_realm();
623 if ((sitename = sitename_fetch(frame, realm)) == NULL) {
624 ads_lookup_site();
625 sitename = sitename_fetch(frame, realm);
628 do {
629 /* We try once with a sitename and once without
630 (unless we don't have a sitename and then we're
631 done */
633 if (sitename == NULL)
634 done = true;
636 nt_status = ads_dns_query_gcs(frame,
637 realm,
638 sitename,
639 &gcs_list,
640 &num_gcs);
642 if (!NT_STATUS_IS_OK(nt_status)) {
643 ads_status = ADS_ERROR_NT(nt_status);
644 goto done;
647 /* Loop until we get a successful connection or have gone
648 through them all. When connecting a GC server, make sure that
649 the realm is the server's DNS name and not the forest root */
651 for (i=0; i<num_gcs; i++) {
652 ads->server.gc = true;
653 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
654 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
655 ads_status = ads_connect(ads);
656 if (ADS_ERR_OK(ads_status)) {
657 /* Reset the bind_dn to "". A Global Catalog server
658 may host multiple domain trees in a forest.
659 Windows 2003 GC server will accept "" as the search
660 path to imply search all domain trees in the forest */
662 SAFE_FREE(ads->config.bind_path);
663 ads->config.bind_path = SMB_STRDUP("");
666 goto done;
668 SAFE_FREE(ads->server.ldap_server);
669 SAFE_FREE(ads->server.realm);
672 TALLOC_FREE(gcs_list);
673 num_gcs = 0;
674 } while (!done);
676 done:
677 talloc_destroy(frame);
679 return ads_status;
684 * Connect to the LDAP server
685 * @param ads Pointer to an existing ADS_STRUCT
686 * @return status of connection
688 ADS_STATUS ads_connect(ADS_STRUCT *ads)
690 int version = LDAP_VERSION3;
691 ADS_STATUS status;
692 NTSTATUS ntstatus;
693 char addr[INET6_ADDRSTRLEN];
695 ZERO_STRUCT(ads->ldap);
696 ads->ldap.last_attempt = time_mono(NULL);
697 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
699 /* try with a user specified server */
701 if (DEBUGLEVEL >= 11) {
702 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
703 DEBUG(11,("ads_connect: entering\n"));
704 DEBUGADD(11,("%s\n", s));
705 TALLOC_FREE(s);
708 if (ads->server.ldap_server) {
709 bool ok = false;
710 struct sockaddr_storage ss;
712 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
713 if (!ok) {
714 DEBUG(5,("ads_connect: unable to resolve name %s\n",
715 ads->server.ldap_server));
716 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
717 goto out;
719 ok = ads_try_connect(ads, ads->server.gc, &ss);
720 if (ok) {
721 goto got_connection;
724 /* The choice of which GC use is handled one level up in
725 ads_connect_gc(). If we continue on from here with
726 ads_find_dc() we will get GC searches on port 389 which
727 doesn't work. --jerry */
729 if (ads->server.gc == true) {
730 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
734 ntstatus = ads_find_dc(ads);
735 if (NT_STATUS_IS_OK(ntstatus)) {
736 goto got_connection;
739 status = ADS_ERROR_NT(ntstatus);
740 goto out;
742 got_connection:
744 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
745 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
747 if (!ads->auth.user_name) {
748 /* Must use the userPrincipalName value here or sAMAccountName
749 and not servicePrincipalName; found by Guenther Deschner */
751 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
752 DEBUG(0,("ads_connect: asprintf fail.\n"));
753 ads->auth.user_name = NULL;
757 if (!ads->auth.realm) {
758 ads->auth.realm = SMB_STRDUP(ads->config.realm);
761 if (!ads->auth.kdc_server) {
762 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
763 ads->auth.kdc_server = SMB_STRDUP(addr);
766 /* If the caller() requested no LDAP bind, then we are done */
768 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
769 status = ADS_SUCCESS;
770 goto out;
773 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
774 if (!ads->ldap.mem_ctx) {
775 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
776 goto out;
779 /* Otherwise setup the TCP LDAP session */
781 ads->ldap.ld = ldap_open_with_timeout(addr,
782 &ads->ldap.ss,
783 ads->ldap.port, lp_ldap_timeout());
784 if (ads->ldap.ld == NULL) {
785 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
786 goto out;
788 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
790 /* cache the successful connection for workgroup and realm */
791 if (ads_closest_dc(ads)) {
792 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
793 saf_store( ads->server.realm, ads->config.ldap_server_name);
796 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
798 if ( lp_ldap_ssl_ads() ) {
799 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
800 if (!ADS_ERR_OK(status)) {
801 goto out;
805 /* fill in the current time and offsets */
807 status = ads_current_time( ads );
808 if ( !ADS_ERR_OK(status) ) {
809 goto out;
812 /* Now do the bind */
814 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
815 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
816 goto out;
819 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
820 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
821 goto out;
824 status = ads_sasl_bind(ads);
826 out:
827 if (DEBUGLEVEL >= 11) {
828 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
829 DEBUG(11,("ads_connect: leaving with: %s\n",
830 ads_errstr(status)));
831 DEBUGADD(11,("%s\n", s));
832 TALLOC_FREE(s);
835 return status;
839 * Connect to the LDAP server using given credentials
840 * @param ads Pointer to an existing ADS_STRUCT
841 * @return status of connection
843 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
845 ads->auth.flags |= ADS_AUTH_USER_CREDS;
847 return ads_connect(ads);
851 * Disconnect the LDAP server
852 * @param ads Pointer to an existing ADS_STRUCT
854 void ads_disconnect(ADS_STRUCT *ads)
856 if (ads->ldap.ld) {
857 ldap_unbind(ads->ldap.ld);
858 ads->ldap.ld = NULL;
860 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
861 ads->ldap.wrap_ops->disconnect(ads);
863 if (ads->ldap.mem_ctx) {
864 talloc_free(ads->ldap.mem_ctx);
866 ZERO_STRUCT(ads->ldap);
870 Duplicate a struct berval into talloc'ed memory
872 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
874 struct berval *value;
876 if (!in_val) return NULL;
878 value = talloc_zero(ctx, struct berval);
879 if (value == NULL)
880 return NULL;
881 if (in_val->bv_len == 0) return value;
883 value->bv_len = in_val->bv_len;
884 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
885 in_val->bv_len);
886 return value;
890 Make a values list out of an array of (struct berval *)
892 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
893 const struct berval **in_vals)
895 struct berval **values;
896 int i;
898 if (!in_vals) return NULL;
899 for (i=0; in_vals[i]; i++)
900 ; /* count values */
901 values = talloc_zero_array(ctx, struct berval *, i+1);
902 if (!values) return NULL;
904 for (i=0; in_vals[i]; i++) {
905 values[i] = dup_berval(ctx, in_vals[i]);
907 return values;
911 UTF8-encode a values list out of an array of (char *)
913 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
915 char **values;
916 int i;
917 size_t size;
919 if (!in_vals) return NULL;
920 for (i=0; in_vals[i]; i++)
921 ; /* count values */
922 values = talloc_zero_array(ctx, char *, i+1);
923 if (!values) return NULL;
925 for (i=0; in_vals[i]; i++) {
926 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
927 TALLOC_FREE(values);
928 return NULL;
931 return values;
935 Pull a (char *) array out of a UTF8-encoded values list
937 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
939 char **values;
940 int i;
941 size_t converted_size;
943 if (!in_vals) return NULL;
944 for (i=0; in_vals[i]; i++)
945 ; /* count values */
946 values = talloc_zero_array(ctx, char *, i+1);
947 if (!values) return NULL;
949 for (i=0; in_vals[i]; i++) {
950 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
951 &converted_size)) {
952 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
953 "%s", strerror(errno)));
956 return values;
960 * Do a search with paged results. cookie must be null on the first
961 * call, and then returned on each subsequent call. It will be null
962 * again when the entire search is complete
963 * @param ads connection to ads server
964 * @param bind_path Base dn for the search
965 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
966 * @param expr Search expression - specified in local charset
967 * @param attrs Attributes to retrieve - specified in utf8 or ascii
968 * @param res ** which will contain results - free res* with ads_msgfree()
969 * @param count Number of entries retrieved on this page
970 * @param cookie The paged results cookie to be returned on subsequent calls
971 * @return status of search
973 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
974 const char *bind_path,
975 int scope, const char *expr,
976 const char **attrs, void *args,
977 LDAPMessage **res,
978 int *count, struct berval **cookie)
980 int rc, i, version;
981 char *utf8_expr, *utf8_path, **search_attrs = NULL;
982 size_t converted_size;
983 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
984 BerElement *cookie_be = NULL;
985 struct berval *cookie_bv= NULL;
986 BerElement *ext_be = NULL;
987 struct berval *ext_bv= NULL;
989 TALLOC_CTX *ctx;
990 ads_control *external_control = (ads_control *) args;
992 *res = NULL;
994 if (!(ctx = talloc_init("ads_do_paged_search_args")))
995 return ADS_ERROR(LDAP_NO_MEMORY);
997 /* 0 means the conversion worked but the result was empty
998 so we only fail if it's -1. In any case, it always
999 at least nulls out the dest */
1000 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1001 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1003 rc = LDAP_NO_MEMORY;
1004 goto done;
1007 if (!attrs || !(*attrs))
1008 search_attrs = NULL;
1009 else {
1010 /* This would be the utf8-encoded version...*/
1011 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1012 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1013 rc = LDAP_NO_MEMORY;
1014 goto done;
1018 /* Paged results only available on ldap v3 or later */
1019 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1020 if (version < LDAP_VERSION3) {
1021 rc = LDAP_NOT_SUPPORTED;
1022 goto done;
1025 cookie_be = ber_alloc_t(LBER_USE_DER);
1026 if (*cookie) {
1027 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1028 ber_bvfree(*cookie); /* don't need it from last time */
1029 *cookie = NULL;
1030 } else {
1031 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1033 ber_flatten(cookie_be, &cookie_bv);
1034 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1035 PagedResults.ldctl_iscritical = (char) 1;
1036 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1037 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1039 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1040 NoReferrals.ldctl_iscritical = (char) 0;
1041 NoReferrals.ldctl_value.bv_len = 0;
1042 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1044 if (external_control &&
1045 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1046 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1048 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1049 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1051 /* win2k does not accept a ldctl_value beeing passed in */
1053 if (external_control->val != 0) {
1055 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1056 rc = LDAP_NO_MEMORY;
1057 goto done;
1060 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1061 rc = LDAP_NO_MEMORY;
1062 goto done;
1064 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1065 rc = LDAP_NO_MEMORY;
1066 goto done;
1069 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1070 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1072 } else {
1073 ExternalCtrl.ldctl_value.bv_len = 0;
1074 ExternalCtrl.ldctl_value.bv_val = NULL;
1077 controls[0] = &NoReferrals;
1078 controls[1] = &PagedResults;
1079 controls[2] = &ExternalCtrl;
1080 controls[3] = NULL;
1082 } else {
1083 controls[0] = &NoReferrals;
1084 controls[1] = &PagedResults;
1085 controls[2] = NULL;
1088 /* we need to disable referrals as the openldap libs don't
1089 handle them and paged results at the same time. Using them
1090 together results in the result record containing the server
1091 page control being removed from the result list (tridge/jmcd)
1093 leaving this in despite the control that says don't generate
1094 referrals, in case the server doesn't support it (jmcd)
1096 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1098 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1099 search_attrs, 0, controls,
1100 NULL, LDAP_NO_LIMIT,
1101 (LDAPMessage **)res);
1103 ber_free(cookie_be, 1);
1104 ber_bvfree(cookie_bv);
1106 if (rc) {
1107 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1108 ldap_err2string(rc)));
1109 if (rc == LDAP_OTHER) {
1110 char *ldap_errmsg;
1111 int ret;
1113 ret = ldap_parse_result(ads->ldap.ld,
1114 *res,
1115 NULL,
1116 NULL,
1117 &ldap_errmsg,
1118 NULL,
1119 NULL,
1121 if (ret == LDAP_SUCCESS) {
1122 DEBUG(3, ("ldap_search_with_timeout(%s) "
1123 "error: %s\n", expr, ldap_errmsg));
1124 ldap_memfree(ldap_errmsg);
1127 goto done;
1130 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1131 NULL, &rcontrols, 0);
1133 if (!rcontrols) {
1134 goto done;
1137 for (i=0; rcontrols[i]; i++) {
1138 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1139 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1140 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1141 &cookie_bv);
1142 /* the berval is the cookie, but must be freed when
1143 it is all done */
1144 if (cookie_bv->bv_len) /* still more to do */
1145 *cookie=ber_bvdup(cookie_bv);
1146 else
1147 *cookie=NULL;
1148 ber_bvfree(cookie_bv);
1149 ber_free(cookie_be, 1);
1150 break;
1153 ldap_controls_free(rcontrols);
1155 done:
1156 talloc_destroy(ctx);
1158 if (ext_be) {
1159 ber_free(ext_be, 1);
1162 if (ext_bv) {
1163 ber_bvfree(ext_bv);
1166 /* if/when we decide to utf8-encode attrs, take out this next line */
1167 TALLOC_FREE(search_attrs);
1169 return ADS_ERROR(rc);
1172 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1173 int scope, const char *expr,
1174 const char **attrs, LDAPMessage **res,
1175 int *count, struct berval **cookie)
1177 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1182 * Get all results for a search. This uses ads_do_paged_search() to return
1183 * all entries in a large search.
1184 * @param ads connection to ads server
1185 * @param bind_path Base dn for the search
1186 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1187 * @param expr Search expression
1188 * @param attrs Attributes to retrieve
1189 * @param res ** which will contain results - free res* with ads_msgfree()
1190 * @return status of search
1192 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1193 int scope, const char *expr,
1194 const char **attrs, void *args,
1195 LDAPMessage **res)
1197 struct berval *cookie = NULL;
1198 int count = 0;
1199 ADS_STATUS status;
1201 *res = NULL;
1202 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1203 &count, &cookie);
1205 if (!ADS_ERR_OK(status))
1206 return status;
1208 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1209 while (cookie) {
1210 LDAPMessage *res2 = NULL;
1211 LDAPMessage *msg, *next;
1213 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1214 attrs, args, &res2, &count, &cookie);
1215 if (!ADS_ERR_OK(status)) {
1216 /* Ensure we free all collected results */
1217 ads_msgfree(ads, *res);
1218 *res = NULL;
1219 break;
1222 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1223 that this works on all ldap libs, but I have only tested with openldap */
1224 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1225 next = ads_next_message(ads, msg);
1226 ldap_add_result_entry((LDAPMessage **)res, msg);
1228 /* note that we do not free res2, as the memory is now
1229 part of the main returned list */
1231 #else
1232 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1233 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1234 #endif
1236 return status;
1239 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1240 int scope, const char *expr,
1241 const char **attrs, LDAPMessage **res)
1243 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1246 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1247 int scope, const char *expr,
1248 const char **attrs, uint32_t sd_flags,
1249 LDAPMessage **res)
1251 ads_control args;
1253 args.control = ADS_SD_FLAGS_OID;
1254 args.val = sd_flags;
1255 args.critical = True;
1257 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1262 * Run a function on all results for a search. Uses ads_do_paged_search() and
1263 * runs the function as each page is returned, using ads_process_results()
1264 * @param ads connection to ads server
1265 * @param bind_path Base dn for the search
1266 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1267 * @param expr Search expression - specified in local charset
1268 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1269 * @param fn Function which takes attr name, values list, and data_area
1270 * @param data_area Pointer which is passed to function on each call
1271 * @return status of search
1273 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1274 int scope, const char *expr, const char **attrs,
1275 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1276 void *data_area)
1278 struct berval *cookie = NULL;
1279 int count = 0;
1280 ADS_STATUS status;
1281 LDAPMessage *res;
1283 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1284 &count, &cookie);
1286 if (!ADS_ERR_OK(status)) return status;
1288 ads_process_results(ads, res, fn, data_area);
1289 ads_msgfree(ads, res);
1291 while (cookie) {
1292 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1293 &res, &count, &cookie);
1295 if (!ADS_ERR_OK(status)) break;
1297 ads_process_results(ads, res, fn, data_area);
1298 ads_msgfree(ads, res);
1301 return status;
1305 * Do a search with a timeout.
1306 * @param ads connection to ads server
1307 * @param bind_path Base dn for the search
1308 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1309 * @param expr Search expression
1310 * @param attrs Attributes to retrieve
1311 * @param res ** which will contain results - free res* with ads_msgfree()
1312 * @return status of search
1314 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1315 const char *expr,
1316 const char **attrs, LDAPMessage **res)
1318 int rc;
1319 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1320 size_t converted_size;
1321 TALLOC_CTX *ctx;
1323 *res = NULL;
1324 if (!(ctx = talloc_init("ads_do_search"))) {
1325 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1326 return ADS_ERROR(LDAP_NO_MEMORY);
1329 /* 0 means the conversion worked but the result was empty
1330 so we only fail if it's negative. In any case, it always
1331 at least nulls out the dest */
1332 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1333 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1335 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1336 rc = LDAP_NO_MEMORY;
1337 goto done;
1340 if (!attrs || !(*attrs))
1341 search_attrs = NULL;
1342 else {
1343 /* This would be the utf8-encoded version...*/
1344 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1345 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1347 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1348 rc = LDAP_NO_MEMORY;
1349 goto done;
1353 /* see the note in ads_do_paged_search - we *must* disable referrals */
1354 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1356 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1357 search_attrs, 0, NULL, NULL,
1358 LDAP_NO_LIMIT,
1359 (LDAPMessage **)res);
1361 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1362 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1363 rc = 0;
1366 done:
1367 talloc_destroy(ctx);
1368 /* if/when we decide to utf8-encode attrs, take out this next line */
1369 TALLOC_FREE(search_attrs);
1370 return ADS_ERROR(rc);
1373 * Do a general ADS search
1374 * @param ads connection to ads server
1375 * @param res ** which will contain results - free res* with ads_msgfree()
1376 * @param expr Search expression
1377 * @param attrs Attributes to retrieve
1378 * @return status of search
1380 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1381 const char *expr, const char **attrs)
1383 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1384 expr, attrs, res);
1388 * Do a search on a specific DistinguishedName
1389 * @param ads connection to ads server
1390 * @param res ** which will contain results - free res* with ads_msgfree()
1391 * @param dn DistinguishName to search
1392 * @param attrs Attributes to retrieve
1393 * @return status of search
1395 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1396 const char *dn, const char **attrs)
1398 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1399 attrs, res);
1403 * Free up memory from a ads_search
1404 * @param ads connection to ads server
1405 * @param msg Search results to free
1407 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1409 if (!msg) return;
1410 ldap_msgfree(msg);
1414 * Get a dn from search results
1415 * @param ads connection to ads server
1416 * @param msg Search result
1417 * @return dn string
1419 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1421 char *utf8_dn, *unix_dn;
1422 size_t converted_size;
1424 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1426 if (!utf8_dn) {
1427 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1428 return NULL;
1431 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1432 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1433 utf8_dn ));
1434 return NULL;
1436 ldap_memfree(utf8_dn);
1437 return unix_dn;
1441 * Get the parent from a dn
1442 * @param dn the dn to return the parent from
1443 * @return parent dn string
1445 char *ads_parent_dn(const char *dn)
1447 char *p;
1449 if (dn == NULL) {
1450 return NULL;
1453 p = strchr(dn, ',');
1455 if (p == NULL) {
1456 return NULL;
1459 return p+1;
1463 * Find a machine account given a hostname
1464 * @param ads connection to ads server
1465 * @param res ** which will contain results - free res* with ads_msgfree()
1466 * @param host Hostname to search for
1467 * @return status of search
1469 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1470 const char *machine)
1472 ADS_STATUS status;
1473 char *expr;
1474 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1476 *res = NULL;
1478 /* the easiest way to find a machine account anywhere in the tree
1479 is to look for hostname$ */
1480 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1481 DEBUG(1, ("asprintf failed!\n"));
1482 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1485 status = ads_search(ads, res, expr, attrs);
1486 SAFE_FREE(expr);
1487 return status;
1491 * Initialize a list of mods to be used in a modify request
1492 * @param ctx An initialized TALLOC_CTX
1493 * @return allocated ADS_MODLIST
1495 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1497 #define ADS_MODLIST_ALLOC_SIZE 10
1498 LDAPMod **mods;
1500 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1501 /* -1 is safety to make sure we don't go over the end.
1502 need to reset it to NULL before doing ldap modify */
1503 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1505 return (ADS_MODLIST)mods;
1510 add an attribute to the list, with values list already constructed
1512 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1513 int mod_op, const char *name,
1514 const void *_invals)
1516 int curmod;
1517 LDAPMod **modlist = (LDAPMod **) *mods;
1518 struct berval **ber_values = NULL;
1519 char **char_values = NULL;
1521 if (!_invals) {
1522 mod_op = LDAP_MOD_DELETE;
1523 } else {
1524 if (mod_op & LDAP_MOD_BVALUES) {
1525 const struct berval **b;
1526 b = discard_const_p(const struct berval *, _invals);
1527 ber_values = ads_dup_values(ctx, b);
1528 } else {
1529 const char **c;
1530 c = discard_const_p(const char *, _invals);
1531 char_values = ads_push_strvals(ctx, c);
1535 /* find the first empty slot */
1536 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1537 curmod++);
1538 if (modlist[curmod] == (LDAPMod *) -1) {
1539 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1540 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1541 return ADS_ERROR(LDAP_NO_MEMORY);
1542 memset(&modlist[curmod], 0,
1543 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1544 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1545 *mods = (ADS_MODLIST)modlist;
1548 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1549 return ADS_ERROR(LDAP_NO_MEMORY);
1550 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1551 if (mod_op & LDAP_MOD_BVALUES) {
1552 modlist[curmod]->mod_bvalues = ber_values;
1553 } else if (mod_op & LDAP_MOD_DELETE) {
1554 modlist[curmod]->mod_values = NULL;
1555 } else {
1556 modlist[curmod]->mod_values = char_values;
1559 modlist[curmod]->mod_op = mod_op;
1560 return ADS_ERROR(LDAP_SUCCESS);
1564 * Add a single string value to a mod list
1565 * @param ctx An initialized TALLOC_CTX
1566 * @param mods An initialized ADS_MODLIST
1567 * @param name The attribute name to add
1568 * @param val The value to add - NULL means DELETE
1569 * @return ADS STATUS indicating success of add
1571 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1572 const char *name, const char *val)
1574 const char *values[2];
1576 values[0] = val;
1577 values[1] = NULL;
1579 if (!val)
1580 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1581 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1585 * Add an array of string values to a mod list
1586 * @param ctx An initialized TALLOC_CTX
1587 * @param mods An initialized ADS_MODLIST
1588 * @param name The attribute name to add
1589 * @param vals The array of string values to add - NULL means DELETE
1590 * @return ADS STATUS indicating success of add
1592 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1593 const char *name, const char **vals)
1595 if (!vals)
1596 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1597 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1598 name, (const void **) vals);
1601 #if 0
1603 * Add a single ber-encoded value to a mod list
1604 * @param ctx An initialized TALLOC_CTX
1605 * @param mods An initialized ADS_MODLIST
1606 * @param name The attribute name to add
1607 * @param val The value to add - NULL means DELETE
1608 * @return ADS STATUS indicating success of add
1610 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1611 const char *name, const struct berval *val)
1613 const struct berval *values[2];
1615 values[0] = val;
1616 values[1] = NULL;
1617 if (!val)
1618 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1619 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1620 name, (const void **) values);
1622 #endif
1625 * Perform an ldap modify
1626 * @param ads connection to ads server
1627 * @param mod_dn DistinguishedName to modify
1628 * @param mods list of modifications to perform
1629 * @return status of modify
1631 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1633 int ret,i;
1634 char *utf8_dn = NULL;
1635 size_t converted_size;
1637 this control is needed to modify that contains a currently
1638 non-existent attribute (but allowable for the object) to run
1640 LDAPControl PermitModify = {
1641 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1642 {0, NULL},
1643 (char) 1};
1644 LDAPControl *controls[2];
1646 controls[0] = &PermitModify;
1647 controls[1] = NULL;
1649 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1650 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1653 /* find the end of the list, marked by NULL or -1 */
1654 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1655 /* make sure the end of the list is NULL */
1656 mods[i] = NULL;
1657 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1658 (LDAPMod **) mods, controls, NULL);
1659 TALLOC_FREE(utf8_dn);
1660 return ADS_ERROR(ret);
1664 * Perform an ldap add
1665 * @param ads connection to ads server
1666 * @param new_dn DistinguishedName to add
1667 * @param mods list of attributes and values for DN
1668 * @return status of add
1670 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1672 int ret, i;
1673 char *utf8_dn = NULL;
1674 size_t converted_size;
1676 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1677 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1678 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1681 /* find the end of the list, marked by NULL or -1 */
1682 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1683 /* make sure the end of the list is NULL */
1684 mods[i] = NULL;
1686 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1687 TALLOC_FREE(utf8_dn);
1688 return ADS_ERROR(ret);
1692 * Delete a DistinguishedName
1693 * @param ads connection to ads server
1694 * @param new_dn DistinguishedName to delete
1695 * @return status of delete
1697 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1699 int ret;
1700 char *utf8_dn = NULL;
1701 size_t converted_size;
1702 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1703 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1704 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1707 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1708 TALLOC_FREE(utf8_dn);
1709 return ADS_ERROR(ret);
1713 * Build an org unit string
1714 * if org unit is Computers or blank then assume a container, otherwise
1715 * assume a / separated list of organisational units.
1716 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1717 * @param ads connection to ads server
1718 * @param org_unit Organizational unit
1719 * @return org unit string - caller must free
1721 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1723 char *ret = NULL;
1725 if (!org_unit || !*org_unit) {
1727 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1729 /* samba4 might not yet respond to a wellknownobject-query */
1730 return ret ? ret : SMB_STRDUP("cn=Computers");
1733 if (strequal(org_unit, "Computers")) {
1734 return SMB_STRDUP("cn=Computers");
1737 /* jmcd: removed "\\" from the separation chars, because it is
1738 needed as an escape for chars like '#' which are valid in an
1739 OU name */
1740 return ads_build_path(org_unit, "/", "ou=", 1);
1744 * Get a org unit string for a well-known GUID
1745 * @param ads connection to ads server
1746 * @param wknguid Well known GUID
1747 * @return org unit string - caller must free
1749 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1751 ADS_STATUS status;
1752 LDAPMessage *res = NULL;
1753 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1754 **bind_dn_exp = NULL;
1755 const char *attrs[] = {"distinguishedName", NULL};
1756 int new_ln, wkn_ln, bind_ln, i;
1758 if (wknguid == NULL) {
1759 return NULL;
1762 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1763 DEBUG(1, ("asprintf failed!\n"));
1764 return NULL;
1767 status = ads_search_dn(ads, &res, base, attrs);
1768 if (!ADS_ERR_OK(status)) {
1769 DEBUG(1,("Failed while searching for: %s\n", base));
1770 goto out;
1773 if (ads_count_replies(ads, res) != 1) {
1774 goto out;
1777 /* substitute the bind-path from the well-known-guid-search result */
1778 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1779 if (!wkn_dn) {
1780 goto out;
1783 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1784 if (!wkn_dn_exp) {
1785 goto out;
1788 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1789 if (!bind_dn_exp) {
1790 goto out;
1793 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1795 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1798 new_ln = wkn_ln - bind_ln;
1800 ret = SMB_STRDUP(wkn_dn_exp[0]);
1801 if (!ret) {
1802 goto out;
1805 for (i=1; i < new_ln; i++) {
1806 char *s = NULL;
1808 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1809 SAFE_FREE(ret);
1810 goto out;
1813 SAFE_FREE(ret);
1814 ret = SMB_STRDUP(s);
1815 free(s);
1816 if (!ret) {
1817 goto out;
1821 out:
1822 SAFE_FREE(base);
1823 ads_msgfree(ads, res);
1824 TALLOC_FREE(wkn_dn);
1825 if (wkn_dn_exp) {
1826 ldap_value_free(wkn_dn_exp);
1828 if (bind_dn_exp) {
1829 ldap_value_free(bind_dn_exp);
1832 return ret;
1836 * Adds (appends) an item to an attribute array, rather then
1837 * replacing the whole list
1838 * @param ctx An initialized TALLOC_CTX
1839 * @param mods An initialized ADS_MODLIST
1840 * @param name name of the ldap attribute to append to
1841 * @param vals an array of values to add
1842 * @return status of addition
1845 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1846 const char *name, const char **vals)
1848 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1849 (const void *) vals);
1853 * Determines the an account's current KVNO via an LDAP lookup
1854 * @param ads An initialized ADS_STRUCT
1855 * @param account_name the NT samaccountname.
1856 * @return the kvno for the account, or -1 in case of a failure.
1859 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1861 LDAPMessage *res = NULL;
1862 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1863 char *filter;
1864 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1865 char *dn_string = NULL;
1866 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1868 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1869 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1870 return kvno;
1872 ret = ads_search(ads, &res, filter, attrs);
1873 SAFE_FREE(filter);
1874 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1875 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1876 ads_msgfree(ads, res);
1877 return kvno;
1880 dn_string = ads_get_dn(ads, talloc_tos(), res);
1881 if (!dn_string) {
1882 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1883 ads_msgfree(ads, res);
1884 return kvno;
1886 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1887 TALLOC_FREE(dn_string);
1889 /* ---------------------------------------------------------
1890 * 0 is returned as a default KVNO from this point on...
1891 * This is done because Windows 2000 does not support key
1892 * version numbers. Chances are that a failure in the next
1893 * step is simply due to Windows 2000 being used for a
1894 * domain controller. */
1895 kvno = 0;
1897 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1898 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1899 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1900 ads_msgfree(ads, res);
1901 return kvno;
1904 /* Success */
1905 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1906 ads_msgfree(ads, res);
1907 return kvno;
1911 * Determines the computer account's current KVNO via an LDAP lookup
1912 * @param ads An initialized ADS_STRUCT
1913 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1914 * @return the kvno for the computer account, or -1 in case of a failure.
1917 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1919 char *computer_account = NULL;
1920 uint32_t kvno = -1;
1922 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1923 return kvno;
1926 kvno = ads_get_kvno(ads, computer_account);
1927 free(computer_account);
1929 return kvno;
1933 * This clears out all registered spn's for a given hostname
1934 * @param ads An initilaized ADS_STRUCT
1935 * @param machine_name the NetBIOS name of the computer.
1936 * @return 0 upon success, non-zero otherwise.
1939 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1941 TALLOC_CTX *ctx;
1942 LDAPMessage *res = NULL;
1943 ADS_MODLIST mods;
1944 const char *servicePrincipalName[1] = {NULL};
1945 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1946 char *dn_string = NULL;
1948 ret = ads_find_machine_acct(ads, &res, machine_name);
1949 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1950 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1951 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1952 ads_msgfree(ads, res);
1953 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1956 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1957 ctx = talloc_init("ads_clear_service_principal_names");
1958 if (!ctx) {
1959 ads_msgfree(ads, res);
1960 return ADS_ERROR(LDAP_NO_MEMORY);
1963 if (!(mods = ads_init_mods(ctx))) {
1964 talloc_destroy(ctx);
1965 ads_msgfree(ads, res);
1966 return ADS_ERROR(LDAP_NO_MEMORY);
1968 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1969 if (!ADS_ERR_OK(ret)) {
1970 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1971 ads_msgfree(ads, res);
1972 talloc_destroy(ctx);
1973 return ret;
1975 dn_string = ads_get_dn(ads, talloc_tos(), res);
1976 if (!dn_string) {
1977 talloc_destroy(ctx);
1978 ads_msgfree(ads, res);
1979 return ADS_ERROR(LDAP_NO_MEMORY);
1981 ret = ads_gen_mod(ads, dn_string, mods);
1982 TALLOC_FREE(dn_string);
1983 if (!ADS_ERR_OK(ret)) {
1984 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1985 machine_name));
1986 ads_msgfree(ads, res);
1987 talloc_destroy(ctx);
1988 return ret;
1991 ads_msgfree(ads, res);
1992 talloc_destroy(ctx);
1993 return ret;
1997 * @brief Search for an element in a string array.
1999 * @param[in] el_array The string array to search.
2001 * @param[in] num_el The number of elements in the string array.
2003 * @param[in] el The string to search.
2005 * @return True if found, false if not.
2007 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2009 size_t i;
2011 if (el_array == NULL || num_el == 0 || el == NULL) {
2012 return false;
2015 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2016 int cmp;
2018 cmp = strcasecmp_m(el_array[i], el);
2019 if (cmp == 0) {
2020 return true;
2024 return false;
2028 * @brief This gets the service principal names of an existing computer account.
2030 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2032 * @param[in] ads The ADS context to use.
2034 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2035 * identify the computer account.
2037 * @param[in] spn_array A pointer to store the array for SPNs.
2039 * @param[in] num_spns The number of principals stored in the array.
2041 * @return 0 on success, or a ADS error if a failure occured.
2043 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2044 ADS_STRUCT *ads,
2045 const char *machine_name,
2046 char ***spn_array,
2047 size_t *num_spns)
2049 ADS_STATUS status;
2050 LDAPMessage *res = NULL;
2051 int count;
2053 status = ads_find_machine_acct(ads,
2054 &res,
2055 machine_name);
2056 if (!ADS_ERR_OK(status)) {
2057 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2058 machine_name));
2059 return status;
2062 count = ads_count_replies(ads, res);
2063 if (count != 1) {
2064 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2065 goto done;
2068 *spn_array = ads_pull_strings(ads,
2069 mem_ctx,
2070 res,
2071 "servicePrincipalName",
2072 num_spns);
2074 done:
2075 ads_msgfree(ads, res);
2077 return status;
2081 * This adds a service principal name to an existing computer account
2082 * (found by hostname) in AD.
2083 * @param ads An initialized ADS_STRUCT
2084 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2085 * @param my_fqdn The fully qualified DNS name of the machine
2086 * @param spn A string of the service principal to add, i.e. 'host'
2087 * @return 0 upon sucess, or non-zero if a failure occurs
2090 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
2091 const char *my_fqdn, const char *spn)
2093 ADS_STATUS ret;
2094 TALLOC_CTX *ctx;
2095 LDAPMessage *res = NULL;
2096 char *psp1, *psp2;
2097 ADS_MODLIST mods;
2098 char *dn_string = NULL;
2099 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
2101 ret = ads_find_machine_acct(ads, &res, machine_name);
2102 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2103 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2104 machine_name));
2105 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2106 spn, machine_name, ads->config.realm));
2107 ads_msgfree(ads, res);
2108 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2111 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2112 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2113 ads_msgfree(ads, res);
2114 return ADS_ERROR(LDAP_NO_MEMORY);
2117 /* add short name spn */
2119 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2120 talloc_destroy(ctx);
2121 ads_msgfree(ads, res);
2122 return ADS_ERROR(LDAP_NO_MEMORY);
2124 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2125 ret = ADS_ERROR(LDAP_NO_MEMORY);
2126 goto out;
2128 servicePrincipalName[0] = psp1;
2130 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2131 psp1, machine_name));
2134 /* add fully qualified spn */
2136 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2137 ret = ADS_ERROR(LDAP_NO_MEMORY);
2138 goto out;
2140 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2141 ret = ADS_ERROR(LDAP_NO_MEMORY);
2142 goto out;
2144 servicePrincipalName[1] = psp2;
2146 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2147 psp2, machine_name));
2149 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2150 ret = ADS_ERROR(LDAP_NO_MEMORY);
2151 goto out;
2154 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2155 if (!ADS_ERR_OK(ret)) {
2156 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2157 goto out;
2160 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2161 ret = ADS_ERROR(LDAP_NO_MEMORY);
2162 goto out;
2165 ret = ads_gen_mod(ads, dn_string, mods);
2166 if (!ADS_ERR_OK(ret)) {
2167 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2168 goto out;
2171 out:
2172 TALLOC_FREE( ctx );
2173 ads_msgfree(ads, res);
2174 return ret;
2178 * adds a machine account to the ADS server
2179 * @param ads An intialized ADS_STRUCT
2180 * @param machine_name - the NetBIOS machine name of this account.
2181 * @param account_type A number indicating the type of account to create
2182 * @param org_unit The LDAP path in which to place this account
2183 * @return 0 upon success, or non-zero otherwise
2186 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2187 const char *org_unit)
2189 ADS_STATUS ret;
2190 char *samAccountName, *controlstr;
2191 TALLOC_CTX *ctx;
2192 ADS_MODLIST mods;
2193 char *machine_escaped = NULL;
2194 char *new_dn;
2195 const char *objectClass[] = {"top", "person", "organizationalPerson",
2196 "user", "computer", NULL};
2197 LDAPMessage *res = NULL;
2198 uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2199 UF_DONT_EXPIRE_PASSWD |\
2200 UF_ACCOUNTDISABLE );
2202 if (!(ctx = talloc_init("ads_add_machine_acct")))
2203 return ADS_ERROR(LDAP_NO_MEMORY);
2205 ret = ADS_ERROR(LDAP_NO_MEMORY);
2207 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2208 if (!machine_escaped) {
2209 goto done;
2212 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2213 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2215 if ( !new_dn || !samAccountName ) {
2216 goto done;
2219 #ifndef ENCTYPE_ARCFOUR_HMAC
2220 acct_control |= UF_USE_DES_KEY_ONLY;
2221 #endif
2223 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2224 goto done;
2227 if (!(mods = ads_init_mods(ctx))) {
2228 goto done;
2231 ads_mod_str(ctx, &mods, "cn", machine_name);
2232 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2233 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2234 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2236 ret = ads_gen_add(ads, new_dn, mods);
2238 done:
2239 SAFE_FREE(machine_escaped);
2240 ads_msgfree(ads, res);
2241 talloc_destroy(ctx);
2243 return ret;
2247 * move a machine account to another OU on the ADS server
2248 * @param ads - An intialized ADS_STRUCT
2249 * @param machine_name - the NetBIOS machine name of this account.
2250 * @param org_unit - The LDAP path in which to place this account
2251 * @param moved - whether we moved the machine account (optional)
2252 * @return 0 upon success, or non-zero otherwise
2255 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2256 const char *org_unit, bool *moved)
2258 ADS_STATUS rc;
2259 int ldap_status;
2260 LDAPMessage *res = NULL;
2261 char *filter = NULL;
2262 char *computer_dn = NULL;
2263 char *parent_dn;
2264 char *computer_rdn = NULL;
2265 bool need_move = False;
2267 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2268 rc = ADS_ERROR(LDAP_NO_MEMORY);
2269 goto done;
2272 /* Find pre-existing machine */
2273 rc = ads_search(ads, &res, filter, NULL);
2274 if (!ADS_ERR_OK(rc)) {
2275 goto done;
2278 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2279 if (!computer_dn) {
2280 rc = ADS_ERROR(LDAP_NO_MEMORY);
2281 goto done;
2284 parent_dn = ads_parent_dn(computer_dn);
2285 if (strequal(parent_dn, org_unit)) {
2286 goto done;
2289 need_move = True;
2291 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2292 rc = ADS_ERROR(LDAP_NO_MEMORY);
2293 goto done;
2296 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2297 org_unit, 1, NULL, NULL);
2298 rc = ADS_ERROR(ldap_status);
2300 done:
2301 ads_msgfree(ads, res);
2302 SAFE_FREE(filter);
2303 TALLOC_FREE(computer_dn);
2304 SAFE_FREE(computer_rdn);
2306 if (!ADS_ERR_OK(rc)) {
2307 need_move = False;
2310 if (moved) {
2311 *moved = need_move;
2314 return rc;
2318 dump a binary result from ldap
2320 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2322 int i, j;
2323 for (i=0; values[i]; i++) {
2324 printf("%s: ", field);
2325 for (j=0; j<values[i]->bv_len; j++) {
2326 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2328 printf("\n");
2332 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2334 int i;
2335 for (i=0; values[i]; i++) {
2336 NTSTATUS status;
2337 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2338 struct GUID guid;
2340 status = GUID_from_ndr_blob(&in, &guid);
2341 if (NT_STATUS_IS_OK(status)) {
2342 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2343 } else {
2344 printf("%s: INVALID GUID\n", field);
2350 dump a sid result from ldap
2352 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2354 int i;
2355 for (i=0; values[i]; i++) {
2356 struct dom_sid sid;
2357 fstring tmp;
2358 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2359 return;
2361 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2366 dump ntSecurityDescriptor
2368 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2370 TALLOC_CTX *frame = talloc_stackframe();
2371 struct security_descriptor *psd;
2372 NTSTATUS status;
2374 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2375 values[0]->bv_len, &psd);
2376 if (!NT_STATUS_IS_OK(status)) {
2377 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2378 nt_errstr(status)));
2379 TALLOC_FREE(frame);
2380 return;
2383 if (psd) {
2384 ads_disp_sd(ads, talloc_tos(), psd);
2387 TALLOC_FREE(frame);
2391 dump a string result from ldap
2393 static void dump_string(const char *field, char **values)
2395 int i;
2396 for (i=0; values[i]; i++) {
2397 printf("%s: %s\n", field, values[i]);
2402 dump a field from LDAP on stdout
2403 used for debugging
2406 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2408 const struct {
2409 const char *name;
2410 bool string;
2411 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2412 } handlers[] = {
2413 {"objectGUID", False, dump_guid},
2414 {"netbootGUID", False, dump_guid},
2415 {"nTSecurityDescriptor", False, dump_sd},
2416 {"dnsRecord", False, dump_binary},
2417 {"objectSid", False, dump_sid},
2418 {"tokenGroups", False, dump_sid},
2419 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2420 {"tokengroupsGlobalandUniversal", False, dump_sid},
2421 {"mS-DS-CreatorSID", False, dump_sid},
2422 {"msExchMailboxGuid", False, dump_guid},
2423 {NULL, True, NULL}
2425 int i;
2427 if (!field) { /* must be end of an entry */
2428 printf("\n");
2429 return False;
2432 for (i=0; handlers[i].name; i++) {
2433 if (strcasecmp_m(handlers[i].name, field) == 0) {
2434 if (!values) /* first time, indicate string or not */
2435 return handlers[i].string;
2436 handlers[i].handler(ads, field, (struct berval **) values);
2437 break;
2440 if (!handlers[i].name) {
2441 if (!values) /* first time, indicate string conversion */
2442 return True;
2443 dump_string(field, (char **)values);
2445 return False;
2449 * Dump a result from LDAP on stdout
2450 * used for debugging
2451 * @param ads connection to ads server
2452 * @param res Results to dump
2455 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2457 ads_process_results(ads, res, ads_dump_field, NULL);
2461 * Walk through results, calling a function for each entry found.
2462 * The function receives a field name, a berval * array of values,
2463 * and a data area passed through from the start. The function is
2464 * called once with null for field and values at the end of each
2465 * entry.
2466 * @param ads connection to ads server
2467 * @param res Results to process
2468 * @param fn Function for processing each result
2469 * @param data_area user-defined area to pass to function
2471 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2472 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2473 void *data_area)
2475 LDAPMessage *msg;
2476 TALLOC_CTX *ctx;
2477 size_t converted_size;
2479 if (!(ctx = talloc_init("ads_process_results")))
2480 return;
2482 for (msg = ads_first_entry(ads, res); msg;
2483 msg = ads_next_entry(ads, msg)) {
2484 char *utf8_field;
2485 BerElement *b;
2487 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2488 (LDAPMessage *)msg,&b);
2489 utf8_field;
2490 utf8_field=ldap_next_attribute(ads->ldap.ld,
2491 (LDAPMessage *)msg,b)) {
2492 struct berval **ber_vals;
2493 char **str_vals;
2494 char **utf8_vals;
2495 char *field;
2496 bool string;
2498 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2499 &converted_size))
2501 DEBUG(0,("ads_process_results: "
2502 "pull_utf8_talloc failed: %s",
2503 strerror(errno)));
2506 string = fn(ads, field, NULL, data_area);
2508 if (string) {
2509 const char **p;
2511 utf8_vals = ldap_get_values(ads->ldap.ld,
2512 (LDAPMessage *)msg, field);
2513 p = discard_const_p(const char *, utf8_vals);
2514 str_vals = ads_pull_strvals(ctx, p);
2515 fn(ads, field, (void **) str_vals, data_area);
2516 ldap_value_free(utf8_vals);
2517 } else {
2518 ber_vals = ldap_get_values_len(ads->ldap.ld,
2519 (LDAPMessage *)msg, field);
2520 fn(ads, field, (void **) ber_vals, data_area);
2522 ldap_value_free_len(ber_vals);
2524 ldap_memfree(utf8_field);
2526 ber_free(b, 0);
2527 talloc_free_children(ctx);
2528 fn(ads, NULL, NULL, data_area); /* completed an entry */
2531 talloc_destroy(ctx);
2535 * count how many replies are in a LDAPMessage
2536 * @param ads connection to ads server
2537 * @param res Results to count
2538 * @return number of replies
2540 int ads_count_replies(ADS_STRUCT *ads, void *res)
2542 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2546 * pull the first entry from a ADS result
2547 * @param ads connection to ads server
2548 * @param res Results of search
2549 * @return first entry from result
2551 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2553 return ldap_first_entry(ads->ldap.ld, res);
2557 * pull the next entry from a ADS result
2558 * @param ads connection to ads server
2559 * @param res Results of search
2560 * @return next entry from result
2562 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2564 return ldap_next_entry(ads->ldap.ld, res);
2568 * pull the first message from a ADS result
2569 * @param ads connection to ads server
2570 * @param res Results of search
2571 * @return first message from result
2573 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2575 return ldap_first_message(ads->ldap.ld, res);
2579 * pull the next message from a ADS result
2580 * @param ads connection to ads server
2581 * @param res Results of search
2582 * @return next message from result
2584 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2586 return ldap_next_message(ads->ldap.ld, res);
2590 * pull a single string from a ADS result
2591 * @param ads connection to ads server
2592 * @param mem_ctx TALLOC_CTX to use for allocating result string
2593 * @param msg Results of search
2594 * @param field Attribute to retrieve
2595 * @return Result string in talloc context
2597 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2598 const char *field)
2600 char **values;
2601 char *ret = NULL;
2602 char *ux_string;
2603 size_t converted_size;
2605 values = ldap_get_values(ads->ldap.ld, msg, field);
2606 if (!values)
2607 return NULL;
2609 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2610 &converted_size))
2612 ret = ux_string;
2614 ldap_value_free(values);
2615 return ret;
2619 * pull an array of strings from a ADS result
2620 * @param ads connection to ads server
2621 * @param mem_ctx TALLOC_CTX to use for allocating result string
2622 * @param msg Results of search
2623 * @param field Attribute to retrieve
2624 * @return Result strings in talloc context
2626 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2627 LDAPMessage *msg, const char *field,
2628 size_t *num_values)
2630 char **values;
2631 char **ret = NULL;
2632 int i;
2633 size_t converted_size;
2635 values = ldap_get_values(ads->ldap.ld, msg, field);
2636 if (!values)
2637 return NULL;
2639 *num_values = ldap_count_values(values);
2641 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2642 if (!ret) {
2643 ldap_value_free(values);
2644 return NULL;
2647 for (i=0;i<*num_values;i++) {
2648 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2649 &converted_size))
2651 ldap_value_free(values);
2652 return NULL;
2655 ret[i] = NULL;
2657 ldap_value_free(values);
2658 return ret;
2662 * pull an array of strings from a ADS result
2663 * (handle large multivalue attributes with range retrieval)
2664 * @param ads connection to ads server
2665 * @param mem_ctx TALLOC_CTX to use for allocating result string
2666 * @param msg Results of search
2667 * @param field Attribute to retrieve
2668 * @param current_strings strings returned by a previous call to this function
2669 * @param next_attribute The next query should ask for this attribute
2670 * @param num_values How many values did we get this time?
2671 * @param more_values Are there more values to get?
2672 * @return Result strings in talloc context
2674 char **ads_pull_strings_range(ADS_STRUCT *ads,
2675 TALLOC_CTX *mem_ctx,
2676 LDAPMessage *msg, const char *field,
2677 char **current_strings,
2678 const char **next_attribute,
2679 size_t *num_strings,
2680 bool *more_strings)
2682 char *attr;
2683 char *expected_range_attrib, *range_attr;
2684 BerElement *ptr = NULL;
2685 char **strings;
2686 char **new_strings;
2687 size_t num_new_strings;
2688 unsigned long int range_start;
2689 unsigned long int range_end;
2691 /* we might have been given the whole lot anyway */
2692 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2693 *more_strings = False;
2694 return strings;
2697 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2699 /* look for Range result */
2700 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2701 attr;
2702 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2703 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2704 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2705 range_attr = attr;
2706 break;
2708 ldap_memfree(attr);
2710 if (!attr) {
2711 ber_free(ptr, 0);
2712 /* nothing here - this field is just empty */
2713 *more_strings = False;
2714 return NULL;
2717 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2718 &range_start, &range_end) == 2) {
2719 *more_strings = True;
2720 } else {
2721 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2722 &range_start) == 1) {
2723 *more_strings = False;
2724 } else {
2725 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2726 range_attr));
2727 ldap_memfree(range_attr);
2728 *more_strings = False;
2729 return NULL;
2733 if ((*num_strings) != range_start) {
2734 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2735 " - aborting range retreival\n",
2736 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2737 ldap_memfree(range_attr);
2738 *more_strings = False;
2739 return NULL;
2742 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2744 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2745 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2746 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2747 range_attr, (unsigned long int)range_end - range_start + 1,
2748 (unsigned long int)num_new_strings));
2749 ldap_memfree(range_attr);
2750 *more_strings = False;
2751 return NULL;
2754 strings = talloc_realloc(mem_ctx, current_strings, char *,
2755 *num_strings + num_new_strings);
2757 if (strings == NULL) {
2758 ldap_memfree(range_attr);
2759 *more_strings = False;
2760 return NULL;
2763 if (new_strings && num_new_strings) {
2764 memcpy(&strings[*num_strings], new_strings,
2765 sizeof(*new_strings) * num_new_strings);
2768 (*num_strings) += num_new_strings;
2770 if (*more_strings) {
2771 *next_attribute = talloc_asprintf(mem_ctx,
2772 "%s;range=%d-*",
2773 field,
2774 (int)*num_strings);
2776 if (!*next_attribute) {
2777 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2778 ldap_memfree(range_attr);
2779 *more_strings = False;
2780 return NULL;
2784 ldap_memfree(range_attr);
2786 return strings;
2790 * pull a single uint32_t from a ADS result
2791 * @param ads connection to ads server
2792 * @param msg Results of search
2793 * @param field Attribute to retrieve
2794 * @param v Pointer to int to store result
2795 * @return boolean inidicating success
2797 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2798 uint32_t *v)
2800 char **values;
2802 values = ldap_get_values(ads->ldap.ld, msg, field);
2803 if (!values)
2804 return False;
2805 if (!values[0]) {
2806 ldap_value_free(values);
2807 return False;
2810 *v = atoi(values[0]);
2811 ldap_value_free(values);
2812 return True;
2816 * pull a single objectGUID from an ADS result
2817 * @param ads connection to ADS server
2818 * @param msg results of search
2819 * @param guid 37-byte area to receive text guid
2820 * @return boolean indicating success
2822 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2824 DATA_BLOB blob;
2825 NTSTATUS status;
2827 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2828 &blob)) {
2829 return false;
2832 status = GUID_from_ndr_blob(&blob, guid);
2833 talloc_free(blob.data);
2834 return NT_STATUS_IS_OK(status);
2839 * pull a single struct dom_sid from a ADS result
2840 * @param ads connection to ads server
2841 * @param msg Results of search
2842 * @param field Attribute to retrieve
2843 * @param sid Pointer to sid to store result
2844 * @return boolean inidicating success
2846 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2847 struct dom_sid *sid)
2849 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2853 * pull an array of struct dom_sids from a ADS result
2854 * @param ads connection to ads server
2855 * @param mem_ctx TALLOC_CTX for allocating sid array
2856 * @param msg Results of search
2857 * @param field Attribute to retrieve
2858 * @param sids pointer to sid array to allocate
2859 * @return the count of SIDs pulled
2861 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2862 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2864 struct berval **values;
2865 bool ret;
2866 int count, i;
2868 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2870 if (!values)
2871 return 0;
2873 for (i=0; values[i]; i++)
2874 /* nop */ ;
2876 if (i) {
2877 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2878 if (!(*sids)) {
2879 ldap_value_free_len(values);
2880 return 0;
2882 } else {
2883 (*sids) = NULL;
2886 count = 0;
2887 for (i=0; values[i]; i++) {
2888 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2889 if (ret) {
2890 DEBUG(10, ("pulling SID: %s\n",
2891 sid_string_dbg(&(*sids)[count])));
2892 count++;
2896 ldap_value_free_len(values);
2897 return count;
2901 * pull a struct security_descriptor from a ADS result
2902 * @param ads connection to ads server
2903 * @param mem_ctx TALLOC_CTX for allocating sid array
2904 * @param msg Results of search
2905 * @param field Attribute to retrieve
2906 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2907 * @return boolean inidicating success
2909 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2910 LDAPMessage *msg, const char *field,
2911 struct security_descriptor **sd)
2913 struct berval **values;
2914 bool ret = true;
2916 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2918 if (!values) return false;
2920 if (values[0]) {
2921 NTSTATUS status;
2922 status = unmarshall_sec_desc(mem_ctx,
2923 (uint8_t *)values[0]->bv_val,
2924 values[0]->bv_len, sd);
2925 if (!NT_STATUS_IS_OK(status)) {
2926 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2927 nt_errstr(status)));
2928 ret = false;
2932 ldap_value_free_len(values);
2933 return ret;
2937 * in order to support usernames longer than 21 characters we need to
2938 * use both the sAMAccountName and the userPrincipalName attributes
2939 * It seems that not all users have the userPrincipalName attribute set
2941 * @param ads connection to ads server
2942 * @param mem_ctx TALLOC_CTX for allocating sid array
2943 * @param msg Results of search
2944 * @return the username
2946 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2947 LDAPMessage *msg)
2949 #if 0 /* JERRY */
2950 char *ret, *p;
2952 /* lookup_name() only works on the sAMAccountName to
2953 returning the username portion of userPrincipalName
2954 breaks winbindd_getpwnam() */
2956 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2957 if (ret && (p = strchr_m(ret, '@'))) {
2958 *p = 0;
2959 return ret;
2961 #endif
2962 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2967 * find the update serial number - this is the core of the ldap cache
2968 * @param ads connection to ads server
2969 * @param ads connection to ADS server
2970 * @param usn Pointer to retrieved update serial number
2971 * @return status of search
2973 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2975 const char *attrs[] = {"highestCommittedUSN", NULL};
2976 ADS_STATUS status;
2977 LDAPMessage *res;
2979 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2980 if (!ADS_ERR_OK(status))
2981 return status;
2983 if (ads_count_replies(ads, res) != 1) {
2984 ads_msgfree(ads, res);
2985 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2988 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2989 ads_msgfree(ads, res);
2990 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2993 ads_msgfree(ads, res);
2994 return ADS_SUCCESS;
2997 /* parse a ADS timestring - typical string is
2998 '20020917091222.0Z0' which means 09:12.22 17th September
2999 2002, timezone 0 */
3000 static time_t ads_parse_time(const char *str)
3002 struct tm tm;
3004 ZERO_STRUCT(tm);
3006 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3007 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3008 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3009 return 0;
3011 tm.tm_year -= 1900;
3012 tm.tm_mon -= 1;
3014 return timegm(&tm);
3017 /********************************************************************
3018 ********************************************************************/
3020 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3022 const char *attrs[] = {"currentTime", NULL};
3023 ADS_STATUS status;
3024 LDAPMessage *res;
3025 char *timestr;
3026 TALLOC_CTX *ctx;
3027 ADS_STRUCT *ads_s = ads;
3029 if (!(ctx = talloc_init("ads_current_time"))) {
3030 return ADS_ERROR(LDAP_NO_MEMORY);
3033 /* establish a new ldap tcp session if necessary */
3035 if ( !ads->ldap.ld ) {
3036 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3037 ads->server.ldap_server )) == NULL )
3039 goto done;
3041 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3042 status = ads_connect( ads_s );
3043 if ( !ADS_ERR_OK(status))
3044 goto done;
3047 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3048 if (!ADS_ERR_OK(status)) {
3049 goto done;
3052 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
3053 if (!timestr) {
3054 ads_msgfree(ads_s, res);
3055 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3056 goto done;
3059 /* but save the time and offset in the original ADS_STRUCT */
3061 ads->config.current_time = ads_parse_time(timestr);
3063 if (ads->config.current_time != 0) {
3064 ads->auth.time_offset = ads->config.current_time - time(NULL);
3065 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3068 ads_msgfree(ads, res);
3070 status = ADS_SUCCESS;
3072 done:
3073 /* free any temporary ads connections */
3074 if ( ads_s != ads ) {
3075 ads_destroy( &ads_s );
3077 talloc_destroy(ctx);
3079 return status;
3082 /********************************************************************
3083 ********************************************************************/
3085 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3087 const char *attrs[] = {"domainFunctionality", NULL};
3088 ADS_STATUS status;
3089 LDAPMessage *res;
3090 ADS_STRUCT *ads_s = ads;
3092 *val = DS_DOMAIN_FUNCTION_2000;
3094 /* establish a new ldap tcp session if necessary */
3096 if ( !ads->ldap.ld ) {
3097 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3098 ads->server.ldap_server )) == NULL )
3100 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3101 goto done;
3103 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3104 status = ads_connect( ads_s );
3105 if ( !ADS_ERR_OK(status))
3106 goto done;
3109 /* If the attribute does not exist assume it is a Windows 2000
3110 functional domain */
3112 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3113 if (!ADS_ERR_OK(status)) {
3114 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3115 status = ADS_SUCCESS;
3117 goto done;
3120 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3121 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3123 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3126 ads_msgfree(ads, res);
3128 done:
3129 /* free any temporary ads connections */
3130 if ( ads_s != ads ) {
3131 ads_destroy( &ads_s );
3134 return status;
3138 * find the domain sid for our domain
3139 * @param ads connection to ads server
3140 * @param sid Pointer to domain sid
3141 * @return status of search
3143 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3145 const char *attrs[] = {"objectSid", NULL};
3146 LDAPMessage *res;
3147 ADS_STATUS rc;
3149 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3150 attrs, &res);
3151 if (!ADS_ERR_OK(rc)) return rc;
3152 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3153 ads_msgfree(ads, res);
3154 return ADS_ERROR_SYSTEM(ENOENT);
3156 ads_msgfree(ads, res);
3158 return ADS_SUCCESS;
3162 * find our site name
3163 * @param ads connection to ads server
3164 * @param mem_ctx Pointer to talloc context
3165 * @param site_name Pointer to the sitename
3166 * @return status of search
3168 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3170 ADS_STATUS status;
3171 LDAPMessage *res;
3172 const char *dn, *service_name;
3173 const char *attrs[] = { "dsServiceName", NULL };
3175 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3176 if (!ADS_ERR_OK(status)) {
3177 return status;
3180 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3181 if (service_name == NULL) {
3182 ads_msgfree(ads, res);
3183 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3186 ads_msgfree(ads, res);
3188 /* go up three levels */
3189 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3190 if (dn == NULL) {
3191 return ADS_ERROR(LDAP_NO_MEMORY);
3194 *site_name = talloc_strdup(mem_ctx, dn);
3195 if (*site_name == NULL) {
3196 return ADS_ERROR(LDAP_NO_MEMORY);
3199 return status;
3201 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3206 * find the site dn where a machine resides
3207 * @param ads connection to ads server
3208 * @param mem_ctx Pointer to talloc context
3209 * @param computer_name name of the machine
3210 * @param site_name Pointer to the sitename
3211 * @return status of search
3213 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3215 ADS_STATUS status;
3216 LDAPMessage *res;
3217 const char *parent, *filter;
3218 char *config_context = NULL;
3219 char *dn;
3221 /* shortcut a query */
3222 if (strequal(computer_name, ads->config.ldap_server_name)) {
3223 return ads_site_dn(ads, mem_ctx, site_dn);
3226 status = ads_config_path(ads, mem_ctx, &config_context);
3227 if (!ADS_ERR_OK(status)) {
3228 return status;
3231 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3232 if (filter == NULL) {
3233 return ADS_ERROR(LDAP_NO_MEMORY);
3236 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3237 filter, NULL, &res);
3238 if (!ADS_ERR_OK(status)) {
3239 return status;
3242 if (ads_count_replies(ads, res) != 1) {
3243 ads_msgfree(ads, res);
3244 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3247 dn = ads_get_dn(ads, mem_ctx, res);
3248 if (dn == NULL) {
3249 ads_msgfree(ads, res);
3250 return ADS_ERROR(LDAP_NO_MEMORY);
3253 /* go up three levels */
3254 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3255 if (parent == NULL) {
3256 ads_msgfree(ads, res);
3257 TALLOC_FREE(dn);
3258 return ADS_ERROR(LDAP_NO_MEMORY);
3261 *site_dn = talloc_strdup(mem_ctx, parent);
3262 if (*site_dn == NULL) {
3263 ads_msgfree(ads, res);
3264 TALLOC_FREE(dn);
3265 return ADS_ERROR(LDAP_NO_MEMORY);
3268 TALLOC_FREE(dn);
3269 ads_msgfree(ads, res);
3271 return status;
3275 * get the upn suffixes for a domain
3276 * @param ads connection to ads server
3277 * @param mem_ctx Pointer to talloc context
3278 * @param suffixes Pointer to an array of suffixes
3279 * @param num_suffixes Pointer to the number of suffixes
3280 * @return status of search
3282 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3284 ADS_STATUS status;
3285 LDAPMessage *res;
3286 const char *base;
3287 char *config_context = NULL;
3288 const char *attrs[] = { "uPNSuffixes", NULL };
3290 status = ads_config_path(ads, mem_ctx, &config_context);
3291 if (!ADS_ERR_OK(status)) {
3292 return status;
3295 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3296 if (base == NULL) {
3297 return ADS_ERROR(LDAP_NO_MEMORY);
3300 status = ads_search_dn(ads, &res, base, attrs);
3301 if (!ADS_ERR_OK(status)) {
3302 return status;
3305 if (ads_count_replies(ads, res) != 1) {
3306 ads_msgfree(ads, res);
3307 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3310 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3311 if ((*suffixes) == NULL) {
3312 ads_msgfree(ads, res);
3313 return ADS_ERROR(LDAP_NO_MEMORY);
3316 ads_msgfree(ads, res);
3318 return status;
3322 * get the joinable ous for a domain
3323 * @param ads connection to ads server
3324 * @param mem_ctx Pointer to talloc context
3325 * @param ous Pointer to an array of ous
3326 * @param num_ous Pointer to the number of ous
3327 * @return status of search
3329 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3330 TALLOC_CTX *mem_ctx,
3331 char ***ous,
3332 size_t *num_ous)
3334 ADS_STATUS status;
3335 LDAPMessage *res = NULL;
3336 LDAPMessage *msg = NULL;
3337 const char *attrs[] = { "dn", NULL };
3338 int count = 0;
3340 status = ads_search(ads, &res,
3341 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3342 attrs);
3343 if (!ADS_ERR_OK(status)) {
3344 return status;
3347 count = ads_count_replies(ads, res);
3348 if (count < 1) {
3349 ads_msgfree(ads, res);
3350 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3353 for (msg = ads_first_entry(ads, res); msg;
3354 msg = ads_next_entry(ads, msg)) {
3355 const char **p = discard_const_p(const char *, *ous);
3356 char *dn = NULL;
3358 dn = ads_get_dn(ads, talloc_tos(), msg);
3359 if (!dn) {
3360 ads_msgfree(ads, res);
3361 return ADS_ERROR(LDAP_NO_MEMORY);
3364 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3365 TALLOC_FREE(dn);
3366 ads_msgfree(ads, res);
3367 return ADS_ERROR(LDAP_NO_MEMORY);
3370 TALLOC_FREE(dn);
3371 *ous = discard_const_p(char *, p);
3374 ads_msgfree(ads, res);
3376 return status;
3381 * pull a struct dom_sid from an extended dn string
3382 * @param mem_ctx TALLOC_CTX
3383 * @param extended_dn string
3384 * @param flags string type of extended_dn
3385 * @param sid pointer to a struct dom_sid
3386 * @return NT_STATUS_OK on success,
3387 * NT_INVALID_PARAMETER on error,
3388 * NT_STATUS_NOT_FOUND if no SID present
3390 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3391 const char *extended_dn,
3392 enum ads_extended_dn_flags flags,
3393 struct dom_sid *sid)
3395 char *p, *q, *dn;
3397 if (!extended_dn) {
3398 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3401 /* otherwise extended_dn gets stripped off */
3402 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3403 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3406 * ADS_EXTENDED_DN_HEX_STRING:
3407 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3409 * ADS_EXTENDED_DN_STRING (only with w2k3):
3410 * <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
3412 * Object with no SID, such as an Exchange Public Folder
3413 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3416 p = strchr(dn, ';');
3417 if (!p) {
3418 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3421 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3422 DEBUG(5,("No SID present in extended dn\n"));
3423 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3426 p += strlen(";<SID=");
3428 q = strchr(p, '>');
3429 if (!q) {
3430 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3433 *q = '\0';
3435 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3437 switch (flags) {
3439 case ADS_EXTENDED_DN_STRING:
3440 if (!string_to_sid(sid, p)) {
3441 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3443 break;
3444 case ADS_EXTENDED_DN_HEX_STRING: {
3445 fstring buf;
3446 size_t buf_len;
3448 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3449 if (buf_len == 0) {
3450 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3453 if (!sid_parse(buf, buf_len, sid)) {
3454 DEBUG(10,("failed to parse sid\n"));
3455 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3457 break;
3459 default:
3460 DEBUG(10,("unknown extended dn format\n"));
3461 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3464 return ADS_ERROR_NT(NT_STATUS_OK);
3467 /********************************************************************
3468 ********************************************************************/
3470 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3472 LDAPMessage *res = NULL;
3473 ADS_STATUS status;
3474 int count = 0;
3475 char *name = NULL;
3477 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3478 if (!ADS_ERR_OK(status)) {
3479 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3480 lp_netbios_name()));
3481 goto out;
3484 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3485 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3486 goto out;
3489 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3490 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3493 out:
3494 ads_msgfree(ads, res);
3496 return name;
3499 /********************************************************************
3500 ********************************************************************/
3502 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3504 LDAPMessage *res = NULL;
3505 ADS_STATUS status;
3506 int count = 0;
3507 char *name = NULL;
3509 status = ads_find_machine_acct(ads, &res, machine_name);
3510 if (!ADS_ERR_OK(status)) {
3511 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3512 lp_netbios_name()));
3513 goto out;
3516 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3517 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3518 goto out;
3521 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3522 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3525 out:
3526 ads_msgfree(ads, res);
3528 return name;
3531 /********************************************************************
3532 ********************************************************************/
3534 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3536 LDAPMessage *res = NULL;
3537 ADS_STATUS status;
3538 int count = 0;
3539 char *name = NULL;
3541 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3542 if (!ADS_ERR_OK(status)) {
3543 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3544 lp_netbios_name()));
3545 goto out;
3548 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3549 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3550 goto out;
3553 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3554 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3557 out:
3558 ads_msgfree(ads, res);
3560 return name;
3563 #if 0
3565 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3568 * Join a machine to a realm
3569 * Creates the machine account and sets the machine password
3570 * @param ads connection to ads server
3571 * @param machine name of host to add
3572 * @param org_unit Organizational unit to place machine in
3573 * @return status of join
3575 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3576 uint32_t account_type, const char *org_unit)
3578 ADS_STATUS status;
3579 LDAPMessage *res = NULL;
3580 char *machine;
3582 /* machine name must be lowercase */
3583 machine = SMB_STRDUP(machine_name);
3584 strlower_m(machine);
3587 status = ads_find_machine_acct(ads, (void **)&res, machine);
3588 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3589 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3590 status = ads_leave_realm(ads, machine);
3591 if (!ADS_ERR_OK(status)) {
3592 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3593 machine, ads->config.realm));
3594 return status;
3598 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3599 if (!ADS_ERR_OK(status)) {
3600 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3601 SAFE_FREE(machine);
3602 return status;
3605 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3606 if (!ADS_ERR_OK(status)) {
3607 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3608 SAFE_FREE(machine);
3609 return status;
3612 SAFE_FREE(machine);
3613 ads_msgfree(ads, res);
3615 return status;
3617 #endif
3620 * Delete a machine from the realm
3621 * @param ads connection to ads server
3622 * @param hostname Machine to remove
3623 * @return status of delete
3625 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3627 ADS_STATUS status;
3628 void *msg;
3629 LDAPMessage *res;
3630 char *hostnameDN, *host;
3631 int rc;
3632 LDAPControl ldap_control;
3633 LDAPControl * pldap_control[2] = {NULL, NULL};
3635 pldap_control[0] = &ldap_control;
3636 memset(&ldap_control, 0, sizeof(LDAPControl));
3637 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3639 /* hostname must be lowercase */
3640 host = SMB_STRDUP(hostname);
3641 if (!strlower_m(host)) {
3642 SAFE_FREE(host);
3643 return ADS_ERROR_SYSTEM(EINVAL);
3646 status = ads_find_machine_acct(ads, &res, host);
3647 if (!ADS_ERR_OK(status)) {
3648 DEBUG(0, ("Host account for %s does not exist.\n", host));
3649 SAFE_FREE(host);
3650 return status;
3653 msg = ads_first_entry(ads, res);
3654 if (!msg) {
3655 SAFE_FREE(host);
3656 return ADS_ERROR_SYSTEM(ENOENT);
3659 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3660 if (hostnameDN == NULL) {
3661 SAFE_FREE(host);
3662 return ADS_ERROR_SYSTEM(ENOENT);
3665 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3666 if (rc) {
3667 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3668 }else {
3669 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3672 if (rc != LDAP_SUCCESS) {
3673 const char *attrs[] = { "cn", NULL };
3674 LDAPMessage *msg_sub;
3676 /* we only search with scope ONE, we do not expect any further
3677 * objects to be created deeper */
3679 status = ads_do_search_retry(ads, hostnameDN,
3680 LDAP_SCOPE_ONELEVEL,
3681 "(objectclass=*)", attrs, &res);
3683 if (!ADS_ERR_OK(status)) {
3684 SAFE_FREE(host);
3685 TALLOC_FREE(hostnameDN);
3686 return status;
3689 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3690 msg_sub = ads_next_entry(ads, msg_sub)) {
3692 char *dn = NULL;
3694 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3695 SAFE_FREE(host);
3696 TALLOC_FREE(hostnameDN);
3697 return ADS_ERROR(LDAP_NO_MEMORY);
3700 status = ads_del_dn(ads, dn);
3701 if (!ADS_ERR_OK(status)) {
3702 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3703 SAFE_FREE(host);
3704 TALLOC_FREE(dn);
3705 TALLOC_FREE(hostnameDN);
3706 return status;
3709 TALLOC_FREE(dn);
3712 /* there should be no subordinate objects anymore */
3713 status = ads_do_search_retry(ads, hostnameDN,
3714 LDAP_SCOPE_ONELEVEL,
3715 "(objectclass=*)", attrs, &res);
3717 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3718 SAFE_FREE(host);
3719 TALLOC_FREE(hostnameDN);
3720 return status;
3723 /* delete hostnameDN now */
3724 status = ads_del_dn(ads, hostnameDN);
3725 if (!ADS_ERR_OK(status)) {
3726 SAFE_FREE(host);
3727 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3728 TALLOC_FREE(hostnameDN);
3729 return status;
3733 TALLOC_FREE(hostnameDN);
3735 status = ads_find_machine_acct(ads, &res, host);
3736 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3737 DEBUG(3, ("Failed to remove host account.\n"));
3738 SAFE_FREE(host);
3739 return status;
3742 SAFE_FREE(host);
3743 return status;
3747 * pull all token-sids from an LDAP dn
3748 * @param ads connection to ads server
3749 * @param mem_ctx TALLOC_CTX for allocating sid array
3750 * @param dn of LDAP object
3751 * @param user_sid pointer to struct dom_sid (objectSid)
3752 * @param primary_group_sid pointer to struct dom_sid (self composed)
3753 * @param sids pointer to sid array to allocate
3754 * @param num_sids counter of SIDs pulled
3755 * @return status of token query
3757 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3758 TALLOC_CTX *mem_ctx,
3759 const char *dn,
3760 struct dom_sid *user_sid,
3761 struct dom_sid *primary_group_sid,
3762 struct dom_sid **sids,
3763 size_t *num_sids)
3765 ADS_STATUS status;
3766 LDAPMessage *res = NULL;
3767 int count = 0;
3768 size_t tmp_num_sids;
3769 struct dom_sid *tmp_sids;
3770 struct dom_sid tmp_user_sid;
3771 struct dom_sid tmp_primary_group_sid;
3772 uint32_t pgid;
3773 const char *attrs[] = {
3774 "objectSid",
3775 "tokenGroups",
3776 "primaryGroupID",
3777 NULL
3780 status = ads_search_retry_dn(ads, &res, dn, attrs);
3781 if (!ADS_ERR_OK(status)) {
3782 return status;
3785 count = ads_count_replies(ads, res);
3786 if (count != 1) {
3787 ads_msgfree(ads, res);
3788 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3791 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3792 ads_msgfree(ads, res);
3793 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3796 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3797 ads_msgfree(ads, res);
3798 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3802 /* hack to compose the primary group sid without knowing the
3803 * domsid */
3805 struct dom_sid domsid;
3807 sid_copy(&domsid, &tmp_user_sid);
3809 if (!sid_split_rid(&domsid, NULL)) {
3810 ads_msgfree(ads, res);
3811 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3814 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3815 ads_msgfree(ads, res);
3816 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3820 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3822 if (tmp_num_sids == 0 || !tmp_sids) {
3823 ads_msgfree(ads, res);
3824 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3827 if (num_sids) {
3828 *num_sids = tmp_num_sids;
3831 if (sids) {
3832 *sids = tmp_sids;
3835 if (user_sid) {
3836 *user_sid = tmp_user_sid;
3839 if (primary_group_sid) {
3840 *primary_group_sid = tmp_primary_group_sid;
3843 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3845 ads_msgfree(ads, res);
3846 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3850 * Find a sAMAccoutName in LDAP
3851 * @param ads connection to ads server
3852 * @param mem_ctx TALLOC_CTX for allocating sid array
3853 * @param samaccountname to search
3854 * @param uac_ret uint32_t pointer userAccountControl attribute value
3855 * @param dn_ret pointer to dn
3856 * @return status of token query
3858 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3859 TALLOC_CTX *mem_ctx,
3860 const char *samaccountname,
3861 uint32_t *uac_ret,
3862 const char **dn_ret)
3864 ADS_STATUS status;
3865 const char *attrs[] = { "userAccountControl", NULL };
3866 const char *filter;
3867 LDAPMessage *res = NULL;
3868 char *dn = NULL;
3869 uint32_t uac = 0;
3871 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3872 samaccountname);
3873 if (filter == NULL) {
3874 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3875 goto out;
3878 status = ads_do_search_all(ads, ads->config.bind_path,
3879 LDAP_SCOPE_SUBTREE,
3880 filter, attrs, &res);
3882 if (!ADS_ERR_OK(status)) {
3883 goto out;
3886 if (ads_count_replies(ads, res) != 1) {
3887 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3888 goto out;
3891 dn = ads_get_dn(ads, talloc_tos(), res);
3892 if (dn == NULL) {
3893 status = ADS_ERROR(LDAP_NO_MEMORY);
3894 goto out;
3897 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3898 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3899 goto out;
3902 if (uac_ret) {
3903 *uac_ret = uac;
3906 if (dn_ret) {
3907 *dn_ret = talloc_strdup(mem_ctx, dn);
3908 if (!*dn_ret) {
3909 status = ADS_ERROR(LDAP_NO_MEMORY);
3910 goto out;
3913 out:
3914 TALLOC_FREE(dn);
3915 ads_msgfree(ads, res);
3917 return status;
3921 * find our configuration path
3922 * @param ads connection to ads server
3923 * @param mem_ctx Pointer to talloc context
3924 * @param config_path Pointer to the config path
3925 * @return status of search
3927 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3928 TALLOC_CTX *mem_ctx,
3929 char **config_path)
3931 ADS_STATUS status;
3932 LDAPMessage *res = NULL;
3933 const char *config_context = NULL;
3934 const char *attrs[] = { "configurationNamingContext", NULL };
3936 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3937 "(objectclass=*)", attrs, &res);
3938 if (!ADS_ERR_OK(status)) {
3939 return status;
3942 config_context = ads_pull_string(ads, mem_ctx, res,
3943 "configurationNamingContext");
3944 ads_msgfree(ads, res);
3945 if (!config_context) {
3946 return ADS_ERROR(LDAP_NO_MEMORY);
3949 if (config_path) {
3950 *config_path = talloc_strdup(mem_ctx, config_context);
3951 if (!*config_path) {
3952 return ADS_ERROR(LDAP_NO_MEMORY);
3956 return ADS_ERROR(LDAP_SUCCESS);
3960 * find the displayName of an extended right
3961 * @param ads connection to ads server
3962 * @param config_path The config path
3963 * @param mem_ctx Pointer to talloc context
3964 * @param GUID struct of the rightsGUID
3965 * @return status of search
3967 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3968 const char *config_path,
3969 TALLOC_CTX *mem_ctx,
3970 const struct GUID *rights_guid)
3972 ADS_STATUS rc;
3973 LDAPMessage *res = NULL;
3974 char *expr = NULL;
3975 const char *attrs[] = { "displayName", NULL };
3976 const char *result = NULL;
3977 const char *path;
3979 if (!ads || !mem_ctx || !rights_guid) {
3980 goto done;
3983 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3984 GUID_string(mem_ctx, rights_guid));
3985 if (!expr) {
3986 goto done;
3989 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3990 if (!path) {
3991 goto done;
3994 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3995 expr, attrs, &res);
3996 if (!ADS_ERR_OK(rc)) {
3997 goto done;
4000 if (ads_count_replies(ads, res) != 1) {
4001 goto done;
4004 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4006 done:
4007 ads_msgfree(ads, res);
4008 return result;
4012 * verify or build and verify an account ou
4013 * @param mem_ctx Pointer to talloc context
4014 * @param ads connection to ads server
4015 * @param account_ou
4016 * @return status of search
4019 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4020 ADS_STRUCT *ads,
4021 const char **account_ou)
4023 char **exploded_dn;
4024 const char *name;
4025 char *ou_string;
4027 exploded_dn = ldap_explode_dn(*account_ou, 0);
4028 if (exploded_dn) {
4029 ldap_value_free(exploded_dn);
4030 return ADS_SUCCESS;
4033 ou_string = ads_ou_string(ads, *account_ou);
4034 if (!ou_string) {
4035 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4038 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4039 ads->config.bind_path);
4040 SAFE_FREE(ou_string);
4042 if (!name) {
4043 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4046 exploded_dn = ldap_explode_dn(name, 0);
4047 if (!exploded_dn) {
4048 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4050 ldap_value_free(exploded_dn);
4052 *account_ou = name;
4053 return ADS_SUCCESS;
4056 #endif