s3:libnet:libnet_join: define list of desired encryption types only once.
[Samba.git] / source3 / libads / ldap.c
blob8232bcc80c3d9aff074639b23a0fd4f9893aa530
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 "../librpc/gen_ndr/netlogon.h"
33 #include "lib/param/loadparm.h"
35 #ifdef HAVE_LDAP
37 /**
38 * @file ldap.c
39 * @brief basic ldap client-side routines for ads server communications
41 * The routines contained here should do the necessary ldap calls for
42 * ads setups.
44 * Important note: attribute names passed into ads_ routines must
45 * already be in UTF-8 format. We do not convert them because in almost
46 * all cases, they are just ascii (which is represented with the same
47 * codepoints in UTF-8). This may have to change at some point
48 **/
51 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
53 static SIG_ATOMIC_T gotalarm;
55 /***************************************************************
56 Signal function to tell us we timed out.
57 ****************************************************************/
59 static void gotalarm_sig(int signum)
61 gotalarm = 1;
64 LDAP *ldap_open_with_timeout(const char *server,
65 struct sockaddr_storage *ss,
66 int port, unsigned int to)
68 LDAP *ldp = NULL;
69 int ldap_err;
70 char *uri;
72 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
73 "%u seconds\n", server, port, to));
75 if (to) {
76 /* Setup timeout */
77 gotalarm = 0;
78 CatchSignal(SIGALRM, gotalarm_sig);
79 alarm(to);
80 /* End setup timeout. */
83 if ( strchr_m(server, ':') ) {
84 /* IPv6 URI */
85 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
86 } else {
87 /* IPv4 URI */
88 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
90 if (uri == NULL) {
91 return NULL;
94 #ifdef HAVE_LDAP_INITIALIZE
95 ldap_err = ldap_initialize(&ldp, uri);
96 #else
97 ldp = ldap_open(server, port);
98 if (ldp != NULL) {
99 ldap_err = LDAP_SUCCESS;
100 } else {
101 ldap_err = LDAP_OTHER;
103 #endif
104 if (ldap_err != LDAP_SUCCESS) {
105 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
106 uri, ldap_err2string(ldap_err)));
107 } else {
108 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
111 if (to) {
112 /* Teardown timeout. */
113 alarm(0);
114 CatchSignal(SIGALRM, SIG_IGN);
117 return ldp;
120 static int ldap_search_with_timeout(LDAP *ld,
121 LDAP_CONST char *base,
122 int scope,
123 LDAP_CONST char *filter,
124 char **attrs,
125 int attrsonly,
126 LDAPControl **sctrls,
127 LDAPControl **cctrls,
128 int sizelimit,
129 LDAPMessage **res )
131 int to = lp_ldap_timeout();
132 struct timeval timeout;
133 struct timeval *timeout_ptr = NULL;
134 int result;
136 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
137 gotalarm = 0;
139 if (to) {
140 timeout.tv_sec = to;
141 timeout.tv_usec = 0;
142 timeout_ptr = &timeout;
144 /* Setup alarm timeout. */
145 CatchSignal(SIGALRM, gotalarm_sig);
146 /* Make the alarm time one second beyond
147 the timout we're setting for the
148 remote search timeout, to allow that
149 to fire in preference. */
150 alarm(to+1);
151 /* End setup timeout. */
155 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
156 attrsonly, sctrls, cctrls, timeout_ptr,
157 sizelimit, res);
159 if (to) {
160 /* Teardown alarm timeout. */
161 CatchSignal(SIGALRM, SIG_IGN);
162 alarm(0);
165 if (gotalarm != 0)
166 return LDAP_TIMELIMIT_EXCEEDED;
169 * A bug in OpenLDAP means ldap_search_ext_s can return
170 * LDAP_SUCCESS but with a NULL res pointer. Cope with
171 * this. See bug #6279 for details. JRA.
174 if (*res == NULL) {
175 return LDAP_TIMELIMIT_EXCEEDED;
178 return result;
181 /**********************************************
182 Do client and server sitename match ?
183 **********************************************/
185 bool ads_sitename_match(ADS_STRUCT *ads)
187 if (ads->config.server_site_name == NULL &&
188 ads->config.client_site_name == NULL ) {
189 DEBUG(10,("ads_sitename_match: both null\n"));
190 return True;
192 if (ads->config.server_site_name &&
193 ads->config.client_site_name &&
194 strequal(ads->config.server_site_name,
195 ads->config.client_site_name)) {
196 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
197 return True;
199 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
200 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
201 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
202 return False;
205 /**********************************************
206 Is this the closest DC ?
207 **********************************************/
209 bool ads_closest_dc(ADS_STRUCT *ads)
211 if (ads->config.flags & NBT_SERVER_CLOSEST) {
212 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
213 return True;
216 /* not sure if this can ever happen */
217 if (ads_sitename_match(ads)) {
218 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
219 return True;
222 if (ads->config.client_site_name == NULL) {
223 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
224 return True;
227 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
228 ads->config.ldap_server_name));
230 return False;
235 try a connection to a given ldap server, returning True and setting the servers IP
236 in the ads struct if successful
238 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
239 struct sockaddr_storage *ss)
241 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
242 TALLOC_CTX *frame = talloc_stackframe();
243 bool ret = false;
244 char addr[INET6_ADDRSTRLEN];
246 if (ss == NULL) {
247 TALLOC_FREE(frame);
248 return False;
251 print_sockaddr(addr, sizeof(addr), ss);
253 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
254 addr, ads->server.realm));
256 ZERO_STRUCT( cldap_reply );
258 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
259 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
260 ret = false;
261 goto out;
264 /* Check the CLDAP reply flags */
266 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
267 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
268 addr));
269 ret = false;
270 goto out;
273 /* Fill in the ads->config values */
275 SAFE_FREE(ads->config.realm);
276 SAFE_FREE(ads->config.bind_path);
277 SAFE_FREE(ads->config.ldap_server_name);
278 SAFE_FREE(ads->config.server_site_name);
279 SAFE_FREE(ads->config.client_site_name);
280 SAFE_FREE(ads->server.workgroup);
282 ads->config.flags = cldap_reply.server_type;
283 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
284 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
285 if (!strupper_m(ads->config.realm)) {
286 ret = false;
287 goto out;
290 ads->config.bind_path = ads_build_dn(ads->config.realm);
291 if (*cldap_reply.server_site) {
292 ads->config.server_site_name =
293 SMB_STRDUP(cldap_reply.server_site);
295 if (*cldap_reply.client_site) {
296 ads->config.client_site_name =
297 SMB_STRDUP(cldap_reply.client_site);
299 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
301 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
302 ads->ldap.ss = *ss;
304 /* Store our site name. */
305 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
306 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
308 ret = true;
310 out:
312 TALLOC_FREE(frame);
313 return ret;
316 /**********************************************************************
317 send a cldap ping to list of servers, one at a time, until one of
318 them answers it's an ldap server. Record success in the ADS_STRUCT.
319 Take note of and update negative connection cache.
320 **********************************************************************/
322 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,const char *domain,
323 struct ip_service *ip_list, int count)
325 int i;
326 bool ok;
328 for (i = 0; i < count; i++) {
329 char server[INET6_ADDRSTRLEN];
331 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
333 if (!NT_STATUS_IS_OK(
334 check_negative_conn_cache(domain, server)))
335 continue;
337 ok = ads_try_connect(ads, false, &ip_list[i].ss);
338 if (ok) {
339 return NT_STATUS_OK;
342 /* keep track of failures */
343 add_failed_connection_entry(domain, server,
344 NT_STATUS_UNSUCCESSFUL);
347 return NT_STATUS_NO_LOGON_SERVERS;
350 /***************************************************************************
351 resolve a name and perform an "ldap ping" using NetBIOS and related methods
352 ****************************************************************************/
354 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
355 const char *domain, const char *realm)
357 int count, i;
358 struct ip_service *ip_list;
359 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
361 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
362 domain));
364 status = get_sorted_dc_list(domain, NULL, &ip_list, &count,
365 false);
366 if (!NT_STATUS_IS_OK(status)) {
367 return status;
370 /* remove servers which are known to be dead based on
371 the corresponding DNS method */
372 if (*realm) {
373 for (i = 0; i < count; ++i) {
374 char server[INET6_ADDRSTRLEN];
376 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
378 if(!NT_STATUS_IS_OK(
379 check_negative_conn_cache(realm, server))) {
380 /* Ensure we add the workgroup name for this
381 IP address as negative too. */
382 add_failed_connection_entry(
383 domain, server,
384 NT_STATUS_UNSUCCESSFUL);
389 status = cldap_ping_list(ads, domain, ip_list, count);
391 SAFE_FREE(ip_list);
393 return status;
397 /**********************************************************************
398 resolve a name and perform an "ldap ping" using DNS
399 **********************************************************************/
401 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
402 const char *realm)
404 int count;
405 struct ip_service *ip_list;
406 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
408 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
409 realm));
411 status = get_sorted_dc_list(realm, sitename, &ip_list, &count,
412 true);
413 if (!NT_STATUS_IS_OK(status)) {
414 return status;
417 status = cldap_ping_list(ads, realm, ip_list, count);
419 SAFE_FREE(ip_list);
421 return status;
424 /**********************************************************************
425 Try to find an AD dc using our internal name resolution routines
426 Try the realm first and then then workgroup name if netbios is not
427 disabled
428 **********************************************************************/
430 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
432 const char *c_domain = "";
433 const char *c_realm;
434 bool use_own_domain = False;
435 char *sitename = NULL;
436 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
437 bool ok = false;
439 /* if the realm and workgroup are both empty, assume they are ours */
441 /* realm */
442 c_realm = ads->server.realm;
444 if (c_realm == NULL)
445 c_realm = "";
447 if (!*c_realm) {
448 /* special case where no realm and no workgroup means our own */
449 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
450 use_own_domain = True;
451 c_realm = lp_realm();
455 if (!lp_disable_netbios()) {
456 if (use_own_domain) {
457 c_domain = lp_workgroup();
458 } else {
459 c_domain = ads->server.workgroup;
460 if (!*c_realm && (!c_domain || !*c_domain)) {
461 c_domain = lp_workgroup();
465 if (!c_domain) {
466 c_domain = "";
470 if (!*c_realm && !*c_domain) {
471 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
472 "what to do\n"));
473 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
477 * In case of LDAP we use get_dc_name() as that
478 * creates the custom krb5.conf file
480 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
481 fstring srv_name;
482 struct sockaddr_storage ip_out;
484 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
485 " and falling back to domain '%s'\n",
486 c_realm, c_domain));
488 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
489 if (ok) {
491 * we call ads_try_connect() to fill in the
492 * ads->config details
494 ok = ads_try_connect(ads, false, &ip_out);
495 if (ok) {
496 return NT_STATUS_OK;
500 return NT_STATUS_NO_LOGON_SERVERS;
503 if (*c_realm) {
504 sitename = sitename_fetch(talloc_tos(), c_realm);
505 status = resolve_and_ping_dns(ads, sitename, c_realm);
507 if (NT_STATUS_IS_OK(status)) {
508 TALLOC_FREE(sitename);
509 return status;
512 /* In case we failed to contact one of our closest DC on our
513 * site we
514 * need to try to find another DC, retry with a site-less SRV
515 * DNS query
516 * - Guenther */
518 if (sitename) {
519 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
520 "our site (%s), Trying to find another DC "
521 "for realm '%s' (domain '%s')\n",
522 sitename, c_realm, c_domain));
523 namecache_delete(c_realm, 0x1C);
524 status =
525 resolve_and_ping_dns(ads, NULL, c_realm);
527 if (NT_STATUS_IS_OK(status)) {
528 TALLOC_FREE(sitename);
529 return status;
533 TALLOC_FREE(sitename);
536 /* try netbios as fallback - if permitted,
537 or if configuration specifically requests it */
538 if (*c_domain) {
539 if (*c_realm) {
540 DEBUG(3, ("ads_find_dc: falling back to netbios "
541 "name resolution for domain '%s' (realm '%s')\n",
542 c_domain, c_realm));
545 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
546 if (NT_STATUS_IS_OK(status)) {
547 return status;
551 DEBUG(1, ("ads_find_dc: "
552 "name resolution for realm '%s' (domain '%s') failed: %s\n",
553 c_realm, c_domain, nt_errstr(status)));
554 return status;
557 /*********************************************************************
558 *********************************************************************/
560 static NTSTATUS ads_lookup_site(void)
562 ADS_STRUCT *ads = NULL;
563 ADS_STATUS ads_status;
564 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
566 ads = ads_init(lp_realm(), NULL, NULL);
567 if (!ads) {
568 return NT_STATUS_NO_MEMORY;
571 /* The NO_BIND here will find a DC and set the client site
572 but not establish the TCP connection */
574 ads->auth.flags = ADS_AUTH_NO_BIND;
575 ads_status = ads_connect(ads);
576 if (!ADS_ERR_OK(ads_status)) {
577 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
578 ads_errstr(ads_status)));
580 nt_status = ads_ntstatus(ads_status);
582 if (ads) {
583 ads_destroy(&ads);
586 return nt_status;
589 /*********************************************************************
590 *********************************************************************/
592 static const char* host_dns_domain(const char *fqdn)
594 const char *p = fqdn;
596 /* go to next char following '.' */
598 if ((p = strchr_m(fqdn, '.')) != NULL) {
599 p++;
602 return p;
607 * Connect to the Global Catalog server
608 * @param ads Pointer to an existing ADS_STRUCT
609 * @return status of connection
611 * Simple wrapper around ads_connect() that fills in the
612 * GC ldap server information
615 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
617 TALLOC_CTX *frame = talloc_stackframe();
618 struct dns_rr_srv *gcs_list;
619 int num_gcs;
620 const char *realm = ads->server.realm;
621 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
622 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
623 int i;
624 bool done = false;
625 char *sitename = NULL;
627 if (!realm)
628 realm = lp_realm();
630 if ((sitename = sitename_fetch(frame, realm)) == NULL) {
631 ads_lookup_site();
632 sitename = sitename_fetch(frame, realm);
635 do {
636 /* We try once with a sitename and once without
637 (unless we don't have a sitename and then we're
638 done */
640 if (sitename == NULL)
641 done = true;
643 nt_status = ads_dns_query_gcs(frame,
644 realm,
645 sitename,
646 &gcs_list,
647 &num_gcs);
649 if (!NT_STATUS_IS_OK(nt_status)) {
650 ads_status = ADS_ERROR_NT(nt_status);
651 goto done;
654 /* Loop until we get a successful connection or have gone
655 through them all. When connecting a GC server, make sure that
656 the realm is the server's DNS name and not the forest root */
658 for (i=0; i<num_gcs; i++) {
659 ads->server.gc = true;
660 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
661 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
662 ads_status = ads_connect(ads);
663 if (ADS_ERR_OK(ads_status)) {
664 /* Reset the bind_dn to "". A Global Catalog server
665 may host multiple domain trees in a forest.
666 Windows 2003 GC server will accept "" as the search
667 path to imply search all domain trees in the forest */
669 SAFE_FREE(ads->config.bind_path);
670 ads->config.bind_path = SMB_STRDUP("");
673 goto done;
675 SAFE_FREE(ads->server.ldap_server);
676 SAFE_FREE(ads->server.realm);
679 TALLOC_FREE(gcs_list);
680 num_gcs = 0;
681 } while (!done);
683 done:
684 talloc_destroy(frame);
686 return ads_status;
691 * Connect to the LDAP server
692 * @param ads Pointer to an existing ADS_STRUCT
693 * @return status of connection
695 ADS_STATUS ads_connect(ADS_STRUCT *ads)
697 int version = LDAP_VERSION3;
698 ADS_STATUS status;
699 NTSTATUS ntstatus;
700 char addr[INET6_ADDRSTRLEN];
702 ZERO_STRUCT(ads->ldap);
703 ads->ldap.last_attempt = time_mono(NULL);
704 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
706 /* try with a user specified server */
708 if (DEBUGLEVEL >= 11) {
709 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
710 DEBUG(11,("ads_connect: entering\n"));
711 DEBUGADD(11,("%s\n", s));
712 TALLOC_FREE(s);
715 if (ads->server.ldap_server) {
716 bool ok = false;
717 struct sockaddr_storage ss;
719 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
720 if (!ok) {
721 DEBUG(5,("ads_connect: unable to resolve name %s\n",
722 ads->server.ldap_server));
723 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
724 goto out;
726 ok = ads_try_connect(ads, ads->server.gc, &ss);
727 if (ok) {
728 goto got_connection;
731 /* The choice of which GC use is handled one level up in
732 ads_connect_gc(). If we continue on from here with
733 ads_find_dc() we will get GC searches on port 389 which
734 doesn't work. --jerry */
736 if (ads->server.gc == true) {
737 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
741 ntstatus = ads_find_dc(ads);
742 if (NT_STATUS_IS_OK(ntstatus)) {
743 goto got_connection;
746 status = ADS_ERROR_NT(ntstatus);
747 goto out;
749 got_connection:
751 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
752 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
754 if (!ads->auth.user_name) {
755 /* Must use the userPrincipalName value here or sAMAccountName
756 and not servicePrincipalName; found by Guenther Deschner */
758 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
759 DEBUG(0,("ads_connect: asprintf fail.\n"));
760 ads->auth.user_name = NULL;
764 if (!ads->auth.realm) {
765 ads->auth.realm = SMB_STRDUP(ads->config.realm);
768 if (!ads->auth.kdc_server) {
769 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
770 ads->auth.kdc_server = SMB_STRDUP(addr);
773 /* If the caller() requested no LDAP bind, then we are done */
775 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
776 status = ADS_SUCCESS;
777 goto out;
780 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
781 if (!ads->ldap.mem_ctx) {
782 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
783 goto out;
786 /* Otherwise setup the TCP LDAP session */
788 ads->ldap.ld = ldap_open_with_timeout(addr,
789 &ads->ldap.ss,
790 ads->ldap.port, lp_ldap_timeout());
791 if (ads->ldap.ld == NULL) {
792 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
793 goto out;
795 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
797 /* cache the successful connection for workgroup and realm */
798 if (ads_closest_dc(ads)) {
799 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
800 saf_store( ads->server.realm, ads->config.ldap_server_name);
803 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
805 if ( lp_ldap_ssl_ads() ) {
806 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
807 if (!ADS_ERR_OK(status)) {
808 goto out;
812 /* fill in the current time and offsets */
814 status = ads_current_time( ads );
815 if ( !ADS_ERR_OK(status) ) {
816 goto out;
819 /* Now do the bind */
821 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
822 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
823 goto out;
826 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
827 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
828 goto out;
831 status = ads_sasl_bind(ads);
833 out:
834 if (DEBUGLEVEL >= 11) {
835 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
836 DEBUG(11,("ads_connect: leaving with: %s\n",
837 ads_errstr(status)));
838 DEBUGADD(11,("%s\n", s));
839 TALLOC_FREE(s);
842 return status;
846 * Connect to the LDAP server using given credentials
847 * @param ads Pointer to an existing ADS_STRUCT
848 * @return status of connection
850 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
852 ads->auth.flags |= ADS_AUTH_USER_CREDS;
854 return ads_connect(ads);
858 * Disconnect the LDAP server
859 * @param ads Pointer to an existing ADS_STRUCT
861 void ads_disconnect(ADS_STRUCT *ads)
863 if (ads->ldap.ld) {
864 ldap_unbind(ads->ldap.ld);
865 ads->ldap.ld = NULL;
867 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
868 ads->ldap.wrap_ops->disconnect(ads);
870 if (ads->ldap.mem_ctx) {
871 talloc_free(ads->ldap.mem_ctx);
873 ZERO_STRUCT(ads->ldap);
877 Duplicate a struct berval into talloc'ed memory
879 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
881 struct berval *value;
883 if (!in_val) return NULL;
885 value = talloc_zero(ctx, struct berval);
886 if (value == NULL)
887 return NULL;
888 if (in_val->bv_len == 0) return value;
890 value->bv_len = in_val->bv_len;
891 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
892 in_val->bv_len);
893 return value;
897 Make a values list out of an array of (struct berval *)
899 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
900 const struct berval **in_vals)
902 struct berval **values;
903 int i;
905 if (!in_vals) return NULL;
906 for (i=0; in_vals[i]; i++)
907 ; /* count values */
908 values = talloc_zero_array(ctx, struct berval *, i+1);
909 if (!values) return NULL;
911 for (i=0; in_vals[i]; i++) {
912 values[i] = dup_berval(ctx, in_vals[i]);
914 return values;
918 UTF8-encode a values list out of an array of (char *)
920 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
922 char **values;
923 int i;
924 size_t size;
926 if (!in_vals) return NULL;
927 for (i=0; in_vals[i]; i++)
928 ; /* count values */
929 values = talloc_zero_array(ctx, char *, i+1);
930 if (!values) return NULL;
932 for (i=0; in_vals[i]; i++) {
933 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
934 TALLOC_FREE(values);
935 return NULL;
938 return values;
942 Pull a (char *) array out of a UTF8-encoded values list
944 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
946 char **values;
947 int i;
948 size_t converted_size;
950 if (!in_vals) return NULL;
951 for (i=0; in_vals[i]; i++)
952 ; /* count values */
953 values = talloc_zero_array(ctx, char *, i+1);
954 if (!values) return NULL;
956 for (i=0; in_vals[i]; i++) {
957 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
958 &converted_size)) {
959 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
960 "%s", strerror(errno)));
963 return values;
967 * Do a search with paged results. cookie must be null on the first
968 * call, and then returned on each subsequent call. It will be null
969 * again when the entire search is complete
970 * @param ads connection to ads server
971 * @param bind_path Base dn for the search
972 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
973 * @param expr Search expression - specified in local charset
974 * @param attrs Attributes to retrieve - specified in utf8 or ascii
975 * @param res ** which will contain results - free res* with ads_msgfree()
976 * @param count Number of entries retrieved on this page
977 * @param cookie The paged results cookie to be returned on subsequent calls
978 * @return status of search
980 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
981 const char *bind_path,
982 int scope, const char *expr,
983 const char **attrs, void *args,
984 LDAPMessage **res,
985 int *count, struct berval **cookie)
987 int rc, i, version;
988 char *utf8_expr, *utf8_path, **search_attrs = NULL;
989 size_t converted_size;
990 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
991 BerElement *cookie_be = NULL;
992 struct berval *cookie_bv= NULL;
993 BerElement *ext_be = NULL;
994 struct berval *ext_bv= NULL;
996 TALLOC_CTX *ctx;
997 ads_control *external_control = (ads_control *) args;
999 *res = NULL;
1001 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1002 return ADS_ERROR(LDAP_NO_MEMORY);
1004 /* 0 means the conversion worked but the result was empty
1005 so we only fail if it's -1. In any case, it always
1006 at least nulls out the dest */
1007 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1008 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1010 rc = LDAP_NO_MEMORY;
1011 goto done;
1014 if (!attrs || !(*attrs))
1015 search_attrs = NULL;
1016 else {
1017 /* This would be the utf8-encoded version...*/
1018 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1019 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1020 rc = LDAP_NO_MEMORY;
1021 goto done;
1025 /* Paged results only available on ldap v3 or later */
1026 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1027 if (version < LDAP_VERSION3) {
1028 rc = LDAP_NOT_SUPPORTED;
1029 goto done;
1032 cookie_be = ber_alloc_t(LBER_USE_DER);
1033 if (*cookie) {
1034 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1035 ber_bvfree(*cookie); /* don't need it from last time */
1036 *cookie = NULL;
1037 } else {
1038 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1040 ber_flatten(cookie_be, &cookie_bv);
1041 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1042 PagedResults.ldctl_iscritical = (char) 1;
1043 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1044 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1046 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1047 NoReferrals.ldctl_iscritical = (char) 0;
1048 NoReferrals.ldctl_value.bv_len = 0;
1049 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1051 if (external_control &&
1052 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1053 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1055 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1056 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1058 /* win2k does not accept a ldctl_value beeing passed in */
1060 if (external_control->val != 0) {
1062 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1063 rc = LDAP_NO_MEMORY;
1064 goto done;
1067 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1068 rc = LDAP_NO_MEMORY;
1069 goto done;
1071 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1072 rc = LDAP_NO_MEMORY;
1073 goto done;
1076 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1077 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1079 } else {
1080 ExternalCtrl.ldctl_value.bv_len = 0;
1081 ExternalCtrl.ldctl_value.bv_val = NULL;
1084 controls[0] = &NoReferrals;
1085 controls[1] = &PagedResults;
1086 controls[2] = &ExternalCtrl;
1087 controls[3] = NULL;
1089 } else {
1090 controls[0] = &NoReferrals;
1091 controls[1] = &PagedResults;
1092 controls[2] = NULL;
1095 /* we need to disable referrals as the openldap libs don't
1096 handle them and paged results at the same time. Using them
1097 together results in the result record containing the server
1098 page control being removed from the result list (tridge/jmcd)
1100 leaving this in despite the control that says don't generate
1101 referrals, in case the server doesn't support it (jmcd)
1103 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1105 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1106 search_attrs, 0, controls,
1107 NULL, LDAP_NO_LIMIT,
1108 (LDAPMessage **)res);
1110 ber_free(cookie_be, 1);
1111 ber_bvfree(cookie_bv);
1113 if (rc) {
1114 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1115 ldap_err2string(rc)));
1116 if (rc == LDAP_OTHER) {
1117 char *ldap_errmsg;
1118 int ret;
1120 ret = ldap_parse_result(ads->ldap.ld,
1121 *res,
1122 NULL,
1123 NULL,
1124 &ldap_errmsg,
1125 NULL,
1126 NULL,
1128 if (ret == LDAP_SUCCESS) {
1129 DEBUG(3, ("ldap_search_with_timeout(%s) "
1130 "error: %s\n", expr, ldap_errmsg));
1131 ldap_memfree(ldap_errmsg);
1134 goto done;
1137 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1138 NULL, &rcontrols, 0);
1140 if (!rcontrols) {
1141 goto done;
1144 for (i=0; rcontrols[i]; i++) {
1145 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1146 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1147 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1148 &cookie_bv);
1149 /* the berval is the cookie, but must be freed when
1150 it is all done */
1151 if (cookie_bv->bv_len) /* still more to do */
1152 *cookie=ber_bvdup(cookie_bv);
1153 else
1154 *cookie=NULL;
1155 ber_bvfree(cookie_bv);
1156 ber_free(cookie_be, 1);
1157 break;
1160 ldap_controls_free(rcontrols);
1162 done:
1163 talloc_destroy(ctx);
1165 if (ext_be) {
1166 ber_free(ext_be, 1);
1169 if (ext_bv) {
1170 ber_bvfree(ext_bv);
1173 /* if/when we decide to utf8-encode attrs, take out this next line */
1174 TALLOC_FREE(search_attrs);
1176 return ADS_ERROR(rc);
1179 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1180 int scope, const char *expr,
1181 const char **attrs, LDAPMessage **res,
1182 int *count, struct berval **cookie)
1184 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1189 * Get all results for a search. This uses ads_do_paged_search() to return
1190 * all entries in a large search.
1191 * @param ads connection to ads server
1192 * @param bind_path Base dn for the search
1193 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1194 * @param expr Search expression
1195 * @param attrs Attributes to retrieve
1196 * @param res ** which will contain results - free res* with ads_msgfree()
1197 * @return status of search
1199 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1200 int scope, const char *expr,
1201 const char **attrs, void *args,
1202 LDAPMessage **res)
1204 struct berval *cookie = NULL;
1205 int count = 0;
1206 ADS_STATUS status;
1208 *res = NULL;
1209 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1210 &count, &cookie);
1212 if (!ADS_ERR_OK(status))
1213 return status;
1215 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1216 while (cookie) {
1217 LDAPMessage *res2 = NULL;
1218 LDAPMessage *msg, *next;
1220 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1221 attrs, args, &res2, &count, &cookie);
1222 if (!ADS_ERR_OK(status)) {
1223 /* Ensure we free all collected results */
1224 ads_msgfree(ads, *res);
1225 *res = NULL;
1226 break;
1229 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1230 that this works on all ldap libs, but I have only tested with openldap */
1231 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1232 next = ads_next_message(ads, msg);
1233 ldap_add_result_entry((LDAPMessage **)res, msg);
1235 /* note that we do not free res2, as the memory is now
1236 part of the main returned list */
1238 #else
1239 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1240 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1241 #endif
1243 return status;
1246 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1247 int scope, const char *expr,
1248 const char **attrs, LDAPMessage **res)
1250 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1253 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1254 int scope, const char *expr,
1255 const char **attrs, uint32_t sd_flags,
1256 LDAPMessage **res)
1258 ads_control args;
1260 args.control = ADS_SD_FLAGS_OID;
1261 args.val = sd_flags;
1262 args.critical = True;
1264 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1269 * Run a function on all results for a search. Uses ads_do_paged_search() and
1270 * runs the function as each page is returned, using ads_process_results()
1271 * @param ads connection to ads server
1272 * @param bind_path Base dn for the search
1273 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1274 * @param expr Search expression - specified in local charset
1275 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1276 * @param fn Function which takes attr name, values list, and data_area
1277 * @param data_area Pointer which is passed to function on each call
1278 * @return status of search
1280 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1281 int scope, const char *expr, const char **attrs,
1282 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1283 void *data_area)
1285 struct berval *cookie = NULL;
1286 int count = 0;
1287 ADS_STATUS status;
1288 LDAPMessage *res;
1290 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1291 &count, &cookie);
1293 if (!ADS_ERR_OK(status)) return status;
1295 ads_process_results(ads, res, fn, data_area);
1296 ads_msgfree(ads, res);
1298 while (cookie) {
1299 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1300 &res, &count, &cookie);
1302 if (!ADS_ERR_OK(status)) break;
1304 ads_process_results(ads, res, fn, data_area);
1305 ads_msgfree(ads, res);
1308 return status;
1312 * Do a search with a timeout.
1313 * @param ads connection to ads server
1314 * @param bind_path Base dn for the search
1315 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1316 * @param expr Search expression
1317 * @param attrs Attributes to retrieve
1318 * @param res ** which will contain results - free res* with ads_msgfree()
1319 * @return status of search
1321 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1322 const char *expr,
1323 const char **attrs, LDAPMessage **res)
1325 int rc;
1326 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1327 size_t converted_size;
1328 TALLOC_CTX *ctx;
1330 *res = NULL;
1331 if (!(ctx = talloc_init("ads_do_search"))) {
1332 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1333 return ADS_ERROR(LDAP_NO_MEMORY);
1336 /* 0 means the conversion worked but the result was empty
1337 so we only fail if it's negative. In any case, it always
1338 at least nulls out the dest */
1339 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1340 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1342 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1343 rc = LDAP_NO_MEMORY;
1344 goto done;
1347 if (!attrs || !(*attrs))
1348 search_attrs = NULL;
1349 else {
1350 /* This would be the utf8-encoded version...*/
1351 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1352 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1354 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1355 rc = LDAP_NO_MEMORY;
1356 goto done;
1360 /* see the note in ads_do_paged_search - we *must* disable referrals */
1361 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1363 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1364 search_attrs, 0, NULL, NULL,
1365 LDAP_NO_LIMIT,
1366 (LDAPMessage **)res);
1368 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1369 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1370 rc = 0;
1373 done:
1374 talloc_destroy(ctx);
1375 /* if/when we decide to utf8-encode attrs, take out this next line */
1376 TALLOC_FREE(search_attrs);
1377 return ADS_ERROR(rc);
1380 * Do a general ADS search
1381 * @param ads connection to ads server
1382 * @param res ** which will contain results - free res* with ads_msgfree()
1383 * @param expr Search expression
1384 * @param attrs Attributes to retrieve
1385 * @return status of search
1387 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1388 const char *expr, const char **attrs)
1390 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1391 expr, attrs, res);
1395 * Do a search on a specific DistinguishedName
1396 * @param ads connection to ads server
1397 * @param res ** which will contain results - free res* with ads_msgfree()
1398 * @param dn DistinguishName to search
1399 * @param attrs Attributes to retrieve
1400 * @return status of search
1402 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1403 const char *dn, const char **attrs)
1405 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1406 attrs, res);
1410 * Free up memory from a ads_search
1411 * @param ads connection to ads server
1412 * @param msg Search results to free
1414 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1416 if (!msg) return;
1417 ldap_msgfree(msg);
1421 * Get a dn from search results
1422 * @param ads connection to ads server
1423 * @param msg Search result
1424 * @return dn string
1426 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1428 char *utf8_dn, *unix_dn;
1429 size_t converted_size;
1431 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1433 if (!utf8_dn) {
1434 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1435 return NULL;
1438 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1439 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1440 utf8_dn ));
1441 return NULL;
1443 ldap_memfree(utf8_dn);
1444 return unix_dn;
1448 * Get the parent from a dn
1449 * @param dn the dn to return the parent from
1450 * @return parent dn string
1452 char *ads_parent_dn(const char *dn)
1454 char *p;
1456 if (dn == NULL) {
1457 return NULL;
1460 p = strchr(dn, ',');
1462 if (p == NULL) {
1463 return NULL;
1466 return p+1;
1470 * Find a machine account given a hostname
1471 * @param ads connection to ads server
1472 * @param res ** which will contain results - free res* with ads_msgfree()
1473 * @param host Hostname to search for
1474 * @return status of search
1476 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1477 const char *machine)
1479 ADS_STATUS status;
1480 char *expr;
1481 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1483 *res = NULL;
1485 /* the easiest way to find a machine account anywhere in the tree
1486 is to look for hostname$ */
1487 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1488 DEBUG(1, ("asprintf failed!\n"));
1489 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1492 status = ads_search(ads, res, expr, attrs);
1493 SAFE_FREE(expr);
1494 return status;
1498 * Initialize a list of mods to be used in a modify request
1499 * @param ctx An initialized TALLOC_CTX
1500 * @return allocated ADS_MODLIST
1502 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1504 #define ADS_MODLIST_ALLOC_SIZE 10
1505 LDAPMod **mods;
1507 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1508 /* -1 is safety to make sure we don't go over the end.
1509 need to reset it to NULL before doing ldap modify */
1510 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1512 return (ADS_MODLIST)mods;
1517 add an attribute to the list, with values list already constructed
1519 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1520 int mod_op, const char *name,
1521 const void *_invals)
1523 int curmod;
1524 LDAPMod **modlist = (LDAPMod **) *mods;
1525 struct berval **ber_values = NULL;
1526 char **char_values = NULL;
1528 if (!_invals) {
1529 mod_op = LDAP_MOD_DELETE;
1530 } else {
1531 if (mod_op & LDAP_MOD_BVALUES) {
1532 const struct berval **b;
1533 b = discard_const_p(const struct berval *, _invals);
1534 ber_values = ads_dup_values(ctx, b);
1535 } else {
1536 const char **c;
1537 c = discard_const_p(const char *, _invals);
1538 char_values = ads_push_strvals(ctx, c);
1542 /* find the first empty slot */
1543 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1544 curmod++);
1545 if (modlist[curmod] == (LDAPMod *) -1) {
1546 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1547 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1548 return ADS_ERROR(LDAP_NO_MEMORY);
1549 memset(&modlist[curmod], 0,
1550 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1551 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1552 *mods = (ADS_MODLIST)modlist;
1555 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1556 return ADS_ERROR(LDAP_NO_MEMORY);
1557 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1558 if (mod_op & LDAP_MOD_BVALUES) {
1559 modlist[curmod]->mod_bvalues = ber_values;
1560 } else if (mod_op & LDAP_MOD_DELETE) {
1561 modlist[curmod]->mod_values = NULL;
1562 } else {
1563 modlist[curmod]->mod_values = char_values;
1566 modlist[curmod]->mod_op = mod_op;
1567 return ADS_ERROR(LDAP_SUCCESS);
1571 * Add a single string value to a mod list
1572 * @param ctx An initialized TALLOC_CTX
1573 * @param mods An initialized ADS_MODLIST
1574 * @param name The attribute name to add
1575 * @param val The value to add - NULL means DELETE
1576 * @return ADS STATUS indicating success of add
1578 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1579 const char *name, const char *val)
1581 const char *values[2];
1583 values[0] = val;
1584 values[1] = NULL;
1586 if (!val)
1587 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1588 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1592 * Add an array of string values to a mod list
1593 * @param ctx An initialized TALLOC_CTX
1594 * @param mods An initialized ADS_MODLIST
1595 * @param name The attribute name to add
1596 * @param vals The array of string values to add - NULL means DELETE
1597 * @return ADS STATUS indicating success of add
1599 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1600 const char *name, const char **vals)
1602 if (!vals)
1603 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1604 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1605 name, (const void **) vals);
1608 #if 0
1610 * Add a single ber-encoded value to a mod list
1611 * @param ctx An initialized TALLOC_CTX
1612 * @param mods An initialized ADS_MODLIST
1613 * @param name The attribute name to add
1614 * @param val The value to add - NULL means DELETE
1615 * @return ADS STATUS indicating success of add
1617 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1618 const char *name, const struct berval *val)
1620 const struct berval *values[2];
1622 values[0] = val;
1623 values[1] = NULL;
1624 if (!val)
1625 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1626 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1627 name, (const void **) values);
1629 #endif
1631 static void ads_print_error(int ret, LDAP *ld)
1633 if (ret != 0) {
1634 char *ld_error = NULL;
1635 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1636 DEBUG(10,("AD LDAP failure %d (%s):\n%s\n", ret,
1637 ldap_err2string(ret), ld_error));
1638 SAFE_FREE(ld_error);
1643 * Perform an ldap modify
1644 * @param ads connection to ads server
1645 * @param mod_dn DistinguishedName to modify
1646 * @param mods list of modifications to perform
1647 * @return status of modify
1649 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1651 int ret,i;
1652 char *utf8_dn = NULL;
1653 size_t converted_size;
1655 this control is needed to modify that contains a currently
1656 non-existent attribute (but allowable for the object) to run
1658 LDAPControl PermitModify = {
1659 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1660 {0, NULL},
1661 (char) 1};
1662 LDAPControl *controls[2];
1664 controls[0] = &PermitModify;
1665 controls[1] = NULL;
1667 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1668 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1671 /* find the end of the list, marked by NULL or -1 */
1672 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1673 /* make sure the end of the list is NULL */
1674 mods[i] = NULL;
1675 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1676 (LDAPMod **) mods, controls, NULL);
1677 ads_print_error(ret, ads->ldap.ld);
1678 TALLOC_FREE(utf8_dn);
1679 return ADS_ERROR(ret);
1683 * Perform an ldap add
1684 * @param ads connection to ads server
1685 * @param new_dn DistinguishedName to add
1686 * @param mods list of attributes and values for DN
1687 * @return status of add
1689 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1691 int ret, i;
1692 char *utf8_dn = NULL;
1693 size_t converted_size;
1695 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1696 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1697 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1700 /* find the end of the list, marked by NULL or -1 */
1701 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1702 /* make sure the end of the list is NULL */
1703 mods[i] = NULL;
1705 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1706 ads_print_error(ret, ads->ldap.ld);
1707 TALLOC_FREE(utf8_dn);
1708 return ADS_ERROR(ret);
1712 * Delete a DistinguishedName
1713 * @param ads connection to ads server
1714 * @param new_dn DistinguishedName to delete
1715 * @return status of delete
1717 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1719 int ret;
1720 char *utf8_dn = NULL;
1721 size_t converted_size;
1722 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1723 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1724 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1727 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1728 ads_print_error(ret, ads->ldap.ld);
1729 TALLOC_FREE(utf8_dn);
1730 return ADS_ERROR(ret);
1734 * Build an org unit string
1735 * if org unit is Computers or blank then assume a container, otherwise
1736 * assume a / separated list of organisational units.
1737 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1738 * @param ads connection to ads server
1739 * @param org_unit Organizational unit
1740 * @return org unit string - caller must free
1742 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1744 char *ret = NULL;
1746 if (!org_unit || !*org_unit) {
1748 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1750 /* samba4 might not yet respond to a wellknownobject-query */
1751 return ret ? ret : SMB_STRDUP("cn=Computers");
1754 if (strequal(org_unit, "Computers")) {
1755 return SMB_STRDUP("cn=Computers");
1758 /* jmcd: removed "\\" from the separation chars, because it is
1759 needed as an escape for chars like '#' which are valid in an
1760 OU name */
1761 return ads_build_path(org_unit, "/", "ou=", 1);
1765 * Get a org unit string for a well-known GUID
1766 * @param ads connection to ads server
1767 * @param wknguid Well known GUID
1768 * @return org unit string - caller must free
1770 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1772 ADS_STATUS status;
1773 LDAPMessage *res = NULL;
1774 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1775 **bind_dn_exp = NULL;
1776 const char *attrs[] = {"distinguishedName", NULL};
1777 int new_ln, wkn_ln, bind_ln, i;
1779 if (wknguid == NULL) {
1780 return NULL;
1783 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1784 DEBUG(1, ("asprintf failed!\n"));
1785 return NULL;
1788 status = ads_search_dn(ads, &res, base, attrs);
1789 if (!ADS_ERR_OK(status)) {
1790 DEBUG(1,("Failed while searching for: %s\n", base));
1791 goto out;
1794 if (ads_count_replies(ads, res) != 1) {
1795 goto out;
1798 /* substitute the bind-path from the well-known-guid-search result */
1799 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1800 if (!wkn_dn) {
1801 goto out;
1804 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1805 if (!wkn_dn_exp) {
1806 goto out;
1809 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1810 if (!bind_dn_exp) {
1811 goto out;
1814 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1816 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1819 new_ln = wkn_ln - bind_ln;
1821 ret = SMB_STRDUP(wkn_dn_exp[0]);
1822 if (!ret) {
1823 goto out;
1826 for (i=1; i < new_ln; i++) {
1827 char *s = NULL;
1829 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1830 SAFE_FREE(ret);
1831 goto out;
1834 SAFE_FREE(ret);
1835 ret = SMB_STRDUP(s);
1836 free(s);
1837 if (!ret) {
1838 goto out;
1842 out:
1843 SAFE_FREE(base);
1844 ads_msgfree(ads, res);
1845 TALLOC_FREE(wkn_dn);
1846 if (wkn_dn_exp) {
1847 ldap_value_free(wkn_dn_exp);
1849 if (bind_dn_exp) {
1850 ldap_value_free(bind_dn_exp);
1853 return ret;
1857 * Adds (appends) an item to an attribute array, rather then
1858 * replacing the whole list
1859 * @param ctx An initialized TALLOC_CTX
1860 * @param mods An initialized ADS_MODLIST
1861 * @param name name of the ldap attribute to append to
1862 * @param vals an array of values to add
1863 * @return status of addition
1866 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1867 const char *name, const char **vals)
1869 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1870 (const void *) vals);
1874 * Determines the an account's current KVNO via an LDAP lookup
1875 * @param ads An initialized ADS_STRUCT
1876 * @param account_name the NT samaccountname.
1877 * @return the kvno for the account, or -1 in case of a failure.
1880 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1882 LDAPMessage *res = NULL;
1883 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1884 char *filter;
1885 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1886 char *dn_string = NULL;
1887 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1889 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1890 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1891 return kvno;
1893 ret = ads_search(ads, &res, filter, attrs);
1894 SAFE_FREE(filter);
1895 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1896 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1897 ads_msgfree(ads, res);
1898 return kvno;
1901 dn_string = ads_get_dn(ads, talloc_tos(), res);
1902 if (!dn_string) {
1903 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1904 ads_msgfree(ads, res);
1905 return kvno;
1907 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1908 TALLOC_FREE(dn_string);
1910 /* ---------------------------------------------------------
1911 * 0 is returned as a default KVNO from this point on...
1912 * This is done because Windows 2000 does not support key
1913 * version numbers. Chances are that a failure in the next
1914 * step is simply due to Windows 2000 being used for a
1915 * domain controller. */
1916 kvno = 0;
1918 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1919 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1920 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1921 ads_msgfree(ads, res);
1922 return kvno;
1925 /* Success */
1926 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1927 ads_msgfree(ads, res);
1928 return kvno;
1932 * Determines the computer account's current KVNO via an LDAP lookup
1933 * @param ads An initialized ADS_STRUCT
1934 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1935 * @return the kvno for the computer account, or -1 in case of a failure.
1938 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1940 char *computer_account = NULL;
1941 uint32_t kvno = -1;
1943 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1944 return kvno;
1947 kvno = ads_get_kvno(ads, computer_account);
1948 free(computer_account);
1950 return kvno;
1954 * This clears out all registered spn's for a given hostname
1955 * @param ads An initilaized ADS_STRUCT
1956 * @param machine_name the NetBIOS name of the computer.
1957 * @return 0 upon success, non-zero otherwise.
1960 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1962 TALLOC_CTX *ctx;
1963 LDAPMessage *res = NULL;
1964 ADS_MODLIST mods;
1965 const char *servicePrincipalName[1] = {NULL};
1966 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1967 char *dn_string = NULL;
1969 ret = ads_find_machine_acct(ads, &res, machine_name);
1970 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1971 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1972 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1973 ads_msgfree(ads, res);
1974 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1977 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1978 ctx = talloc_init("ads_clear_service_principal_names");
1979 if (!ctx) {
1980 ads_msgfree(ads, res);
1981 return ADS_ERROR(LDAP_NO_MEMORY);
1984 if (!(mods = ads_init_mods(ctx))) {
1985 talloc_destroy(ctx);
1986 ads_msgfree(ads, res);
1987 return ADS_ERROR(LDAP_NO_MEMORY);
1989 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1990 if (!ADS_ERR_OK(ret)) {
1991 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1992 ads_msgfree(ads, res);
1993 talloc_destroy(ctx);
1994 return ret;
1996 dn_string = ads_get_dn(ads, talloc_tos(), res);
1997 if (!dn_string) {
1998 talloc_destroy(ctx);
1999 ads_msgfree(ads, res);
2000 return ADS_ERROR(LDAP_NO_MEMORY);
2002 ret = ads_gen_mod(ads, dn_string, mods);
2003 TALLOC_FREE(dn_string);
2004 if (!ADS_ERR_OK(ret)) {
2005 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2006 machine_name));
2007 ads_msgfree(ads, res);
2008 talloc_destroy(ctx);
2009 return ret;
2012 ads_msgfree(ads, res);
2013 talloc_destroy(ctx);
2014 return ret;
2018 * @brief Search for an element in a string array.
2020 * @param[in] el_array The string array to search.
2022 * @param[in] num_el The number of elements in the string array.
2024 * @param[in] el The string to search.
2026 * @return True if found, false if not.
2028 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2030 size_t i;
2032 if (el_array == NULL || num_el == 0 || el == NULL) {
2033 return false;
2036 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2037 int cmp;
2039 cmp = strcasecmp_m(el_array[i], el);
2040 if (cmp == 0) {
2041 return true;
2045 return false;
2049 * @brief This gets the service principal names of an existing computer account.
2051 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2053 * @param[in] ads The ADS context to use.
2055 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2056 * identify the computer account.
2058 * @param[in] spn_array A pointer to store the array for SPNs.
2060 * @param[in] num_spns The number of principals stored in the array.
2062 * @return 0 on success, or a ADS error if a failure occured.
2064 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2065 ADS_STRUCT *ads,
2066 const char *machine_name,
2067 char ***spn_array,
2068 size_t *num_spns)
2070 ADS_STATUS status;
2071 LDAPMessage *res = NULL;
2072 int count;
2074 status = ads_find_machine_acct(ads,
2075 &res,
2076 machine_name);
2077 if (!ADS_ERR_OK(status)) {
2078 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2079 machine_name));
2080 return status;
2083 count = ads_count_replies(ads, res);
2084 if (count != 1) {
2085 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2086 goto done;
2089 *spn_array = ads_pull_strings(ads,
2090 mem_ctx,
2091 res,
2092 "servicePrincipalName",
2093 num_spns);
2094 if (*spn_array == NULL) {
2095 DEBUG(1, ("Host account for %s does not have service principal "
2096 "names.\n",
2097 machine_name));
2098 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2099 goto done;
2102 done:
2103 ads_msgfree(ads, res);
2105 return status;
2109 * This adds a service principal name to an existing computer account
2110 * (found by hostname) in AD.
2111 * @param ads An initialized ADS_STRUCT
2112 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2113 * @param my_fqdn The fully qualified DNS name of the machine
2114 * @param spn A string of the service principal to add, i.e. 'host'
2115 * @return 0 upon sucess, or non-zero if a failure occurs
2118 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
2119 const char *my_fqdn, const char *spn)
2121 ADS_STATUS ret;
2122 TALLOC_CTX *ctx;
2123 LDAPMessage *res = NULL;
2124 char *psp1, *psp2;
2125 ADS_MODLIST mods;
2126 char *dn_string = NULL;
2127 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
2129 ret = ads_find_machine_acct(ads, &res, machine_name);
2130 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2131 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2132 machine_name));
2133 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2134 spn, machine_name, ads->config.realm));
2135 ads_msgfree(ads, res);
2136 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2139 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2140 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2141 ads_msgfree(ads, res);
2142 return ADS_ERROR(LDAP_NO_MEMORY);
2145 /* add short name spn */
2147 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2148 talloc_destroy(ctx);
2149 ads_msgfree(ads, res);
2150 return ADS_ERROR(LDAP_NO_MEMORY);
2152 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2153 ret = ADS_ERROR(LDAP_NO_MEMORY);
2154 goto out;
2156 servicePrincipalName[0] = psp1;
2158 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2159 psp1, machine_name));
2162 /* add fully qualified spn */
2164 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2165 ret = ADS_ERROR(LDAP_NO_MEMORY);
2166 goto out;
2168 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2169 ret = ADS_ERROR(LDAP_NO_MEMORY);
2170 goto out;
2172 servicePrincipalName[1] = psp2;
2174 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2175 psp2, machine_name));
2177 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2178 ret = ADS_ERROR(LDAP_NO_MEMORY);
2179 goto out;
2182 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2183 if (!ADS_ERR_OK(ret)) {
2184 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2185 goto out;
2188 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2189 ret = ADS_ERROR(LDAP_NO_MEMORY);
2190 goto out;
2193 ret = ads_gen_mod(ads, dn_string, mods);
2194 if (!ADS_ERR_OK(ret)) {
2195 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2196 goto out;
2199 out:
2200 TALLOC_FREE( ctx );
2201 ads_msgfree(ads, res);
2202 return ret;
2206 * adds a machine account to the ADS server
2207 * @param ads An intialized ADS_STRUCT
2208 * @param machine_name - the NetBIOS machine name of this account.
2209 * @param account_type A number indicating the type of account to create
2210 * @param org_unit The LDAP path in which to place this account
2211 * @return 0 upon success, or non-zero otherwise
2214 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2215 const char *machine_name,
2216 const char *org_unit,
2217 uint32_t etype_list)
2219 ADS_STATUS ret;
2220 char *samAccountName, *controlstr;
2221 TALLOC_CTX *ctx;
2222 ADS_MODLIST mods;
2223 char *machine_escaped = NULL;
2224 char *new_dn;
2225 const char *objectClass[] = {"top", "person", "organizationalPerson",
2226 "user", "computer", NULL};
2227 LDAPMessage *res = NULL;
2228 uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2229 UF_DONT_EXPIRE_PASSWD |\
2230 UF_ACCOUNTDISABLE );
2231 uint32_t func_level = 0;
2233 ret = ads_domain_func_level(ads, &func_level);
2234 if (!ADS_ERR_OK(ret)) {
2235 return ret;
2238 if (!(ctx = talloc_init("ads_add_machine_acct")))
2239 return ADS_ERROR(LDAP_NO_MEMORY);
2241 ret = ADS_ERROR(LDAP_NO_MEMORY);
2243 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2244 if (!machine_escaped) {
2245 goto done;
2248 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2249 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2251 if ( !new_dn || !samAccountName ) {
2252 goto done;
2255 #ifndef ENCTYPE_ARCFOUR_HMAC
2256 acct_control |= UF_USE_DES_KEY_ONLY;
2257 #endif
2259 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2260 goto done;
2263 if (!(mods = ads_init_mods(ctx))) {
2264 goto done;
2267 ads_mod_str(ctx, &mods, "cn", machine_name);
2268 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2269 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2270 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2272 if (func_level >= DS_DOMAIN_FUNCTION_2008) {
2273 const char *etype_list_str;
2275 etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list);
2276 if (etype_list_str == NULL) {
2277 goto done;
2279 ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes",
2280 etype_list_str);
2283 ret = ads_gen_add(ads, new_dn, mods);
2285 done:
2286 SAFE_FREE(machine_escaped);
2287 ads_msgfree(ads, res);
2288 talloc_destroy(ctx);
2290 return ret;
2294 * move a machine account to another OU on the ADS server
2295 * @param ads - An intialized ADS_STRUCT
2296 * @param machine_name - the NetBIOS machine name of this account.
2297 * @param org_unit - The LDAP path in which to place this account
2298 * @param moved - whether we moved the machine account (optional)
2299 * @return 0 upon success, or non-zero otherwise
2302 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2303 const char *org_unit, bool *moved)
2305 ADS_STATUS rc;
2306 int ldap_status;
2307 LDAPMessage *res = NULL;
2308 char *filter = NULL;
2309 char *computer_dn = NULL;
2310 char *parent_dn;
2311 char *computer_rdn = NULL;
2312 bool need_move = False;
2314 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2315 rc = ADS_ERROR(LDAP_NO_MEMORY);
2316 goto done;
2319 /* Find pre-existing machine */
2320 rc = ads_search(ads, &res, filter, NULL);
2321 if (!ADS_ERR_OK(rc)) {
2322 goto done;
2325 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2326 if (!computer_dn) {
2327 rc = ADS_ERROR(LDAP_NO_MEMORY);
2328 goto done;
2331 parent_dn = ads_parent_dn(computer_dn);
2332 if (strequal(parent_dn, org_unit)) {
2333 goto done;
2336 need_move = True;
2338 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2339 rc = ADS_ERROR(LDAP_NO_MEMORY);
2340 goto done;
2343 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2344 org_unit, 1, NULL, NULL);
2345 rc = ADS_ERROR(ldap_status);
2347 done:
2348 ads_msgfree(ads, res);
2349 SAFE_FREE(filter);
2350 TALLOC_FREE(computer_dn);
2351 SAFE_FREE(computer_rdn);
2353 if (!ADS_ERR_OK(rc)) {
2354 need_move = False;
2357 if (moved) {
2358 *moved = need_move;
2361 return rc;
2365 dump a binary result from ldap
2367 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2369 int i, j;
2370 for (i=0; values[i]; i++) {
2371 printf("%s: ", field);
2372 for (j=0; j<values[i]->bv_len; j++) {
2373 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2375 printf("\n");
2379 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2381 int i;
2382 for (i=0; values[i]; i++) {
2383 NTSTATUS status;
2384 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2385 struct GUID guid;
2387 status = GUID_from_ndr_blob(&in, &guid);
2388 if (NT_STATUS_IS_OK(status)) {
2389 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2390 } else {
2391 printf("%s: INVALID GUID\n", field);
2397 dump a sid result from ldap
2399 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2401 int i;
2402 for (i=0; values[i]; i++) {
2403 struct dom_sid sid;
2404 fstring tmp;
2405 if (!sid_parse((const uint8_t *)values[i]->bv_val,
2406 values[i]->bv_len, &sid)) {
2407 return;
2409 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2414 dump ntSecurityDescriptor
2416 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2418 TALLOC_CTX *frame = talloc_stackframe();
2419 struct security_descriptor *psd;
2420 NTSTATUS status;
2422 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2423 values[0]->bv_len, &psd);
2424 if (!NT_STATUS_IS_OK(status)) {
2425 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2426 nt_errstr(status)));
2427 TALLOC_FREE(frame);
2428 return;
2431 if (psd) {
2432 ads_disp_sd(ads, talloc_tos(), psd);
2435 TALLOC_FREE(frame);
2439 dump a string result from ldap
2441 static void dump_string(const char *field, char **values)
2443 int i;
2444 for (i=0; values[i]; i++) {
2445 printf("%s: %s\n", field, values[i]);
2450 dump a field from LDAP on stdout
2451 used for debugging
2454 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2456 const struct {
2457 const char *name;
2458 bool string;
2459 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2460 } handlers[] = {
2461 {"objectGUID", False, dump_guid},
2462 {"netbootGUID", False, dump_guid},
2463 {"nTSecurityDescriptor", False, dump_sd},
2464 {"dnsRecord", False, dump_binary},
2465 {"objectSid", False, dump_sid},
2466 {"tokenGroups", False, dump_sid},
2467 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2468 {"tokengroupsGlobalandUniversal", False, dump_sid},
2469 {"mS-DS-CreatorSID", False, dump_sid},
2470 {"msExchMailboxGuid", False, dump_guid},
2471 {NULL, True, NULL}
2473 int i;
2475 if (!field) { /* must be end of an entry */
2476 printf("\n");
2477 return False;
2480 for (i=0; handlers[i].name; i++) {
2481 if (strcasecmp_m(handlers[i].name, field) == 0) {
2482 if (!values) /* first time, indicate string or not */
2483 return handlers[i].string;
2484 handlers[i].handler(ads, field, (struct berval **) values);
2485 break;
2488 if (!handlers[i].name) {
2489 if (!values) /* first time, indicate string conversion */
2490 return True;
2491 dump_string(field, (char **)values);
2493 return False;
2497 * Dump a result from LDAP on stdout
2498 * used for debugging
2499 * @param ads connection to ads server
2500 * @param res Results to dump
2503 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2505 ads_process_results(ads, res, ads_dump_field, NULL);
2509 * Walk through results, calling a function for each entry found.
2510 * The function receives a field name, a berval * array of values,
2511 * and a data area passed through from the start. The function is
2512 * called once with null for field and values at the end of each
2513 * entry.
2514 * @param ads connection to ads server
2515 * @param res Results to process
2516 * @param fn Function for processing each result
2517 * @param data_area user-defined area to pass to function
2519 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2520 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2521 void *data_area)
2523 LDAPMessage *msg;
2524 TALLOC_CTX *ctx;
2525 size_t converted_size;
2527 if (!(ctx = talloc_init("ads_process_results")))
2528 return;
2530 for (msg = ads_first_entry(ads, res); msg;
2531 msg = ads_next_entry(ads, msg)) {
2532 char *utf8_field;
2533 BerElement *b;
2535 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2536 (LDAPMessage *)msg,&b);
2537 utf8_field;
2538 utf8_field=ldap_next_attribute(ads->ldap.ld,
2539 (LDAPMessage *)msg,b)) {
2540 struct berval **ber_vals;
2541 char **str_vals;
2542 char **utf8_vals;
2543 char *field;
2544 bool string;
2546 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2547 &converted_size))
2549 DEBUG(0,("ads_process_results: "
2550 "pull_utf8_talloc failed: %s",
2551 strerror(errno)));
2554 string = fn(ads, field, NULL, data_area);
2556 if (string) {
2557 const char **p;
2559 utf8_vals = ldap_get_values(ads->ldap.ld,
2560 (LDAPMessage *)msg, field);
2561 p = discard_const_p(const char *, utf8_vals);
2562 str_vals = ads_pull_strvals(ctx, p);
2563 fn(ads, field, (void **) str_vals, data_area);
2564 ldap_value_free(utf8_vals);
2565 } else {
2566 ber_vals = ldap_get_values_len(ads->ldap.ld,
2567 (LDAPMessage *)msg, field);
2568 fn(ads, field, (void **) ber_vals, data_area);
2570 ldap_value_free_len(ber_vals);
2572 ldap_memfree(utf8_field);
2574 ber_free(b, 0);
2575 talloc_free_children(ctx);
2576 fn(ads, NULL, NULL, data_area); /* completed an entry */
2579 talloc_destroy(ctx);
2583 * count how many replies are in a LDAPMessage
2584 * @param ads connection to ads server
2585 * @param res Results to count
2586 * @return number of replies
2588 int ads_count_replies(ADS_STRUCT *ads, void *res)
2590 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2594 * pull the first entry from a ADS result
2595 * @param ads connection to ads server
2596 * @param res Results of search
2597 * @return first entry from result
2599 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2601 return ldap_first_entry(ads->ldap.ld, res);
2605 * pull the next entry from a ADS result
2606 * @param ads connection to ads server
2607 * @param res Results of search
2608 * @return next entry from result
2610 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2612 return ldap_next_entry(ads->ldap.ld, res);
2616 * pull the first message from a ADS result
2617 * @param ads connection to ads server
2618 * @param res Results of search
2619 * @return first message from result
2621 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2623 return ldap_first_message(ads->ldap.ld, res);
2627 * pull the next message from a ADS result
2628 * @param ads connection to ads server
2629 * @param res Results of search
2630 * @return next message from result
2632 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2634 return ldap_next_message(ads->ldap.ld, res);
2638 * pull a single string from a ADS result
2639 * @param ads connection to ads server
2640 * @param mem_ctx TALLOC_CTX to use for allocating result string
2641 * @param msg Results of search
2642 * @param field Attribute to retrieve
2643 * @return Result string in talloc context
2645 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2646 const char *field)
2648 char **values;
2649 char *ret = NULL;
2650 char *ux_string;
2651 size_t converted_size;
2653 values = ldap_get_values(ads->ldap.ld, msg, field);
2654 if (!values)
2655 return NULL;
2657 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2658 &converted_size))
2660 ret = ux_string;
2662 ldap_value_free(values);
2663 return ret;
2667 * pull an array of strings from a ADS result
2668 * @param ads connection to ads server
2669 * @param mem_ctx TALLOC_CTX to use for allocating result string
2670 * @param msg Results of search
2671 * @param field Attribute to retrieve
2672 * @return Result strings in talloc context
2674 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2675 LDAPMessage *msg, const char *field,
2676 size_t *num_values)
2678 char **values;
2679 char **ret = NULL;
2680 int i;
2681 size_t converted_size;
2683 values = ldap_get_values(ads->ldap.ld, msg, field);
2684 if (!values)
2685 return NULL;
2687 *num_values = ldap_count_values(values);
2689 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2690 if (!ret) {
2691 ldap_value_free(values);
2692 return NULL;
2695 for (i=0;i<*num_values;i++) {
2696 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2697 &converted_size))
2699 ldap_value_free(values);
2700 return NULL;
2703 ret[i] = NULL;
2705 ldap_value_free(values);
2706 return ret;
2710 * pull an array of strings from a ADS result
2711 * (handle large multivalue attributes with range retrieval)
2712 * @param ads connection to ads server
2713 * @param mem_ctx TALLOC_CTX to use for allocating result string
2714 * @param msg Results of search
2715 * @param field Attribute to retrieve
2716 * @param current_strings strings returned by a previous call to this function
2717 * @param next_attribute The next query should ask for this attribute
2718 * @param num_values How many values did we get this time?
2719 * @param more_values Are there more values to get?
2720 * @return Result strings in talloc context
2722 char **ads_pull_strings_range(ADS_STRUCT *ads,
2723 TALLOC_CTX *mem_ctx,
2724 LDAPMessage *msg, const char *field,
2725 char **current_strings,
2726 const char **next_attribute,
2727 size_t *num_strings,
2728 bool *more_strings)
2730 char *attr;
2731 char *expected_range_attrib, *range_attr;
2732 BerElement *ptr = NULL;
2733 char **strings;
2734 char **new_strings;
2735 size_t num_new_strings;
2736 unsigned long int range_start;
2737 unsigned long int range_end;
2739 /* we might have been given the whole lot anyway */
2740 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2741 *more_strings = False;
2742 return strings;
2745 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2747 /* look for Range result */
2748 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2749 attr;
2750 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2751 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2752 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2753 range_attr = attr;
2754 break;
2756 ldap_memfree(attr);
2758 if (!attr) {
2759 ber_free(ptr, 0);
2760 /* nothing here - this field is just empty */
2761 *more_strings = False;
2762 return NULL;
2765 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2766 &range_start, &range_end) == 2) {
2767 *more_strings = True;
2768 } else {
2769 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2770 &range_start) == 1) {
2771 *more_strings = False;
2772 } else {
2773 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2774 range_attr));
2775 ldap_memfree(range_attr);
2776 *more_strings = False;
2777 return NULL;
2781 if ((*num_strings) != range_start) {
2782 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2783 " - aborting range retreival\n",
2784 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2785 ldap_memfree(range_attr);
2786 *more_strings = False;
2787 return NULL;
2790 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2792 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2793 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2794 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2795 range_attr, (unsigned long int)range_end - range_start + 1,
2796 (unsigned long int)num_new_strings));
2797 ldap_memfree(range_attr);
2798 *more_strings = False;
2799 return NULL;
2802 strings = talloc_realloc(mem_ctx, current_strings, char *,
2803 *num_strings + num_new_strings);
2805 if (strings == NULL) {
2806 ldap_memfree(range_attr);
2807 *more_strings = False;
2808 return NULL;
2811 if (new_strings && num_new_strings) {
2812 memcpy(&strings[*num_strings], new_strings,
2813 sizeof(*new_strings) * num_new_strings);
2816 (*num_strings) += num_new_strings;
2818 if (*more_strings) {
2819 *next_attribute = talloc_asprintf(mem_ctx,
2820 "%s;range=%d-*",
2821 field,
2822 (int)*num_strings);
2824 if (!*next_attribute) {
2825 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2826 ldap_memfree(range_attr);
2827 *more_strings = False;
2828 return NULL;
2832 ldap_memfree(range_attr);
2834 return strings;
2838 * pull a single uint32_t from a ADS result
2839 * @param ads connection to ads server
2840 * @param msg Results of search
2841 * @param field Attribute to retrieve
2842 * @param v Pointer to int to store result
2843 * @return boolean inidicating success
2845 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2846 uint32_t *v)
2848 char **values;
2850 values = ldap_get_values(ads->ldap.ld, msg, field);
2851 if (!values)
2852 return False;
2853 if (!values[0]) {
2854 ldap_value_free(values);
2855 return False;
2858 *v = atoi(values[0]);
2859 ldap_value_free(values);
2860 return True;
2864 * pull a single objectGUID from an ADS result
2865 * @param ads connection to ADS server
2866 * @param msg results of search
2867 * @param guid 37-byte area to receive text guid
2868 * @return boolean indicating success
2870 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2872 DATA_BLOB blob;
2873 NTSTATUS status;
2875 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2876 &blob)) {
2877 return false;
2880 status = GUID_from_ndr_blob(&blob, guid);
2881 talloc_free(blob.data);
2882 return NT_STATUS_IS_OK(status);
2887 * pull a single struct dom_sid from a ADS result
2888 * @param ads connection to ads server
2889 * @param msg Results of search
2890 * @param field Attribute to retrieve
2891 * @param sid Pointer to sid to store result
2892 * @return boolean inidicating success
2894 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2895 struct dom_sid *sid)
2897 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2901 * pull an array of struct dom_sids 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 sids pointer to sid array to allocate
2907 * @return the count of SIDs pulled
2909 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2910 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2912 struct berval **values;
2913 bool ret;
2914 int count, i;
2916 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2918 if (!values)
2919 return 0;
2921 for (i=0; values[i]; i++)
2922 /* nop */ ;
2924 if (i) {
2925 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2926 if (!(*sids)) {
2927 ldap_value_free_len(values);
2928 return 0;
2930 } else {
2931 (*sids) = NULL;
2934 count = 0;
2935 for (i=0; values[i]; i++) {
2936 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2937 values[i]->bv_len, &(*sids)[count]);
2938 if (ret) {
2939 DEBUG(10, ("pulling SID: %s\n",
2940 sid_string_dbg(&(*sids)[count])));
2941 count++;
2945 ldap_value_free_len(values);
2946 return count;
2950 * pull a struct security_descriptor from a ADS result
2951 * @param ads connection to ads server
2952 * @param mem_ctx TALLOC_CTX for allocating sid array
2953 * @param msg Results of search
2954 * @param field Attribute to retrieve
2955 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2956 * @return boolean inidicating success
2958 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2959 LDAPMessage *msg, const char *field,
2960 struct security_descriptor **sd)
2962 struct berval **values;
2963 bool ret = true;
2965 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2967 if (!values) return false;
2969 if (values[0]) {
2970 NTSTATUS status;
2971 status = unmarshall_sec_desc(mem_ctx,
2972 (uint8_t *)values[0]->bv_val,
2973 values[0]->bv_len, sd);
2974 if (!NT_STATUS_IS_OK(status)) {
2975 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2976 nt_errstr(status)));
2977 ret = false;
2981 ldap_value_free_len(values);
2982 return ret;
2986 * in order to support usernames longer than 21 characters we need to
2987 * use both the sAMAccountName and the userPrincipalName attributes
2988 * It seems that not all users have the userPrincipalName attribute set
2990 * @param ads connection to ads server
2991 * @param mem_ctx TALLOC_CTX for allocating sid array
2992 * @param msg Results of search
2993 * @return the username
2995 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2996 LDAPMessage *msg)
2998 #if 0 /* JERRY */
2999 char *ret, *p;
3001 /* lookup_name() only works on the sAMAccountName to
3002 returning the username portion of userPrincipalName
3003 breaks winbindd_getpwnam() */
3005 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3006 if (ret && (p = strchr_m(ret, '@'))) {
3007 *p = 0;
3008 return ret;
3010 #endif
3011 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3016 * find the update serial number - this is the core of the ldap cache
3017 * @param ads connection to ads server
3018 * @param ads connection to ADS server
3019 * @param usn Pointer to retrieved update serial number
3020 * @return status of search
3022 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3024 const char *attrs[] = {"highestCommittedUSN", NULL};
3025 ADS_STATUS status;
3026 LDAPMessage *res;
3028 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3029 if (!ADS_ERR_OK(status))
3030 return status;
3032 if (ads_count_replies(ads, res) != 1) {
3033 ads_msgfree(ads, res);
3034 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3037 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3038 ads_msgfree(ads, res);
3039 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3042 ads_msgfree(ads, res);
3043 return ADS_SUCCESS;
3046 /* parse a ADS timestring - typical string is
3047 '20020917091222.0Z0' which means 09:12.22 17th September
3048 2002, timezone 0 */
3049 static time_t ads_parse_time(const char *str)
3051 struct tm tm;
3053 ZERO_STRUCT(tm);
3055 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3056 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3057 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3058 return 0;
3060 tm.tm_year -= 1900;
3061 tm.tm_mon -= 1;
3063 return timegm(&tm);
3066 /********************************************************************
3067 ********************************************************************/
3069 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3071 const char *attrs[] = {"currentTime", NULL};
3072 ADS_STATUS status;
3073 LDAPMessage *res;
3074 char *timestr;
3075 TALLOC_CTX *ctx;
3076 ADS_STRUCT *ads_s = ads;
3078 if (!(ctx = talloc_init("ads_current_time"))) {
3079 return ADS_ERROR(LDAP_NO_MEMORY);
3082 /* establish a new ldap tcp session if necessary */
3084 if ( !ads->ldap.ld ) {
3085 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3086 ads->server.ldap_server )) == NULL )
3088 goto done;
3090 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3091 status = ads_connect( ads_s );
3092 if ( !ADS_ERR_OK(status))
3093 goto done;
3096 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3097 if (!ADS_ERR_OK(status)) {
3098 goto done;
3101 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
3102 if (!timestr) {
3103 ads_msgfree(ads_s, res);
3104 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3105 goto done;
3108 /* but save the time and offset in the original ADS_STRUCT */
3110 ads->config.current_time = ads_parse_time(timestr);
3112 if (ads->config.current_time != 0) {
3113 ads->auth.time_offset = ads->config.current_time - time(NULL);
3114 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3117 ads_msgfree(ads, res);
3119 status = ADS_SUCCESS;
3121 done:
3122 /* free any temporary ads connections */
3123 if ( ads_s != ads ) {
3124 ads_destroy( &ads_s );
3126 talloc_destroy(ctx);
3128 return status;
3131 /********************************************************************
3132 ********************************************************************/
3134 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3136 const char *attrs[] = {"domainFunctionality", NULL};
3137 ADS_STATUS status;
3138 LDAPMessage *res;
3139 ADS_STRUCT *ads_s = ads;
3141 *val = DS_DOMAIN_FUNCTION_2000;
3143 /* establish a new ldap tcp session if necessary */
3145 if ( !ads->ldap.ld ) {
3146 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3147 ads->server.ldap_server )) == NULL )
3149 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3150 goto done;
3152 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3153 status = ads_connect( ads_s );
3154 if ( !ADS_ERR_OK(status))
3155 goto done;
3158 /* If the attribute does not exist assume it is a Windows 2000
3159 functional domain */
3161 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3162 if (!ADS_ERR_OK(status)) {
3163 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3164 status = ADS_SUCCESS;
3166 goto done;
3169 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3170 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3172 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3175 ads_msgfree(ads, res);
3177 done:
3178 /* free any temporary ads connections */
3179 if ( ads_s != ads ) {
3180 ads_destroy( &ads_s );
3183 return status;
3187 * find the domain sid for our domain
3188 * @param ads connection to ads server
3189 * @param sid Pointer to domain sid
3190 * @return status of search
3192 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3194 const char *attrs[] = {"objectSid", NULL};
3195 LDAPMessage *res;
3196 ADS_STATUS rc;
3198 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3199 attrs, &res);
3200 if (!ADS_ERR_OK(rc)) return rc;
3201 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3202 ads_msgfree(ads, res);
3203 return ADS_ERROR_SYSTEM(ENOENT);
3205 ads_msgfree(ads, res);
3207 return ADS_SUCCESS;
3211 * find our site name
3212 * @param ads connection to ads server
3213 * @param mem_ctx Pointer to talloc context
3214 * @param site_name Pointer to the sitename
3215 * @return status of search
3217 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3219 ADS_STATUS status;
3220 LDAPMessage *res;
3221 const char *dn, *service_name;
3222 const char *attrs[] = { "dsServiceName", NULL };
3224 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3225 if (!ADS_ERR_OK(status)) {
3226 return status;
3229 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3230 if (service_name == NULL) {
3231 ads_msgfree(ads, res);
3232 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3235 ads_msgfree(ads, res);
3237 /* go up three levels */
3238 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3239 if (dn == NULL) {
3240 return ADS_ERROR(LDAP_NO_MEMORY);
3243 *site_name = talloc_strdup(mem_ctx, dn);
3244 if (*site_name == NULL) {
3245 return ADS_ERROR(LDAP_NO_MEMORY);
3248 return status;
3250 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3255 * find the site dn where a machine resides
3256 * @param ads connection to ads server
3257 * @param mem_ctx Pointer to talloc context
3258 * @param computer_name name of the machine
3259 * @param site_name Pointer to the sitename
3260 * @return status of search
3262 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3264 ADS_STATUS status;
3265 LDAPMessage *res;
3266 const char *parent, *filter;
3267 char *config_context = NULL;
3268 char *dn;
3270 /* shortcut a query */
3271 if (strequal(computer_name, ads->config.ldap_server_name)) {
3272 return ads_site_dn(ads, mem_ctx, site_dn);
3275 status = ads_config_path(ads, mem_ctx, &config_context);
3276 if (!ADS_ERR_OK(status)) {
3277 return status;
3280 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3281 if (filter == NULL) {
3282 return ADS_ERROR(LDAP_NO_MEMORY);
3285 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3286 filter, NULL, &res);
3287 if (!ADS_ERR_OK(status)) {
3288 return status;
3291 if (ads_count_replies(ads, res) != 1) {
3292 ads_msgfree(ads, res);
3293 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3296 dn = ads_get_dn(ads, mem_ctx, res);
3297 if (dn == NULL) {
3298 ads_msgfree(ads, res);
3299 return ADS_ERROR(LDAP_NO_MEMORY);
3302 /* go up three levels */
3303 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3304 if (parent == NULL) {
3305 ads_msgfree(ads, res);
3306 TALLOC_FREE(dn);
3307 return ADS_ERROR(LDAP_NO_MEMORY);
3310 *site_dn = talloc_strdup(mem_ctx, parent);
3311 if (*site_dn == NULL) {
3312 ads_msgfree(ads, res);
3313 TALLOC_FREE(dn);
3314 return ADS_ERROR(LDAP_NO_MEMORY);
3317 TALLOC_FREE(dn);
3318 ads_msgfree(ads, res);
3320 return status;
3324 * get the upn suffixes for a domain
3325 * @param ads connection to ads server
3326 * @param mem_ctx Pointer to talloc context
3327 * @param suffixes Pointer to an array of suffixes
3328 * @param num_suffixes Pointer to the number of suffixes
3329 * @return status of search
3331 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3333 ADS_STATUS status;
3334 LDAPMessage *res;
3335 const char *base;
3336 char *config_context = NULL;
3337 const char *attrs[] = { "uPNSuffixes", NULL };
3339 status = ads_config_path(ads, mem_ctx, &config_context);
3340 if (!ADS_ERR_OK(status)) {
3341 return status;
3344 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3345 if (base == NULL) {
3346 return ADS_ERROR(LDAP_NO_MEMORY);
3349 status = ads_search_dn(ads, &res, base, attrs);
3350 if (!ADS_ERR_OK(status)) {
3351 return status;
3354 if (ads_count_replies(ads, res) != 1) {
3355 ads_msgfree(ads, res);
3356 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3359 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3360 if ((*suffixes) == NULL) {
3361 ads_msgfree(ads, res);
3362 return ADS_ERROR(LDAP_NO_MEMORY);
3365 ads_msgfree(ads, res);
3367 return status;
3371 * get the joinable ous for a domain
3372 * @param ads connection to ads server
3373 * @param mem_ctx Pointer to talloc context
3374 * @param ous Pointer to an array of ous
3375 * @param num_ous Pointer to the number of ous
3376 * @return status of search
3378 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3379 TALLOC_CTX *mem_ctx,
3380 char ***ous,
3381 size_t *num_ous)
3383 ADS_STATUS status;
3384 LDAPMessage *res = NULL;
3385 LDAPMessage *msg = NULL;
3386 const char *attrs[] = { "dn", NULL };
3387 int count = 0;
3389 status = ads_search(ads, &res,
3390 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3391 attrs);
3392 if (!ADS_ERR_OK(status)) {
3393 return status;
3396 count = ads_count_replies(ads, res);
3397 if (count < 1) {
3398 ads_msgfree(ads, res);
3399 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3402 for (msg = ads_first_entry(ads, res); msg;
3403 msg = ads_next_entry(ads, msg)) {
3404 const char **p = discard_const_p(const char *, *ous);
3405 char *dn = NULL;
3407 dn = ads_get_dn(ads, talloc_tos(), msg);
3408 if (!dn) {
3409 ads_msgfree(ads, res);
3410 return ADS_ERROR(LDAP_NO_MEMORY);
3413 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3414 TALLOC_FREE(dn);
3415 ads_msgfree(ads, res);
3416 return ADS_ERROR(LDAP_NO_MEMORY);
3419 TALLOC_FREE(dn);
3420 *ous = discard_const_p(char *, p);
3423 ads_msgfree(ads, res);
3425 return status;
3430 * pull a struct dom_sid from an extended dn string
3431 * @param mem_ctx TALLOC_CTX
3432 * @param extended_dn string
3433 * @param flags string type of extended_dn
3434 * @param sid pointer to a struct dom_sid
3435 * @return NT_STATUS_OK on success,
3436 * NT_INVALID_PARAMETER on error,
3437 * NT_STATUS_NOT_FOUND if no SID present
3439 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3440 const char *extended_dn,
3441 enum ads_extended_dn_flags flags,
3442 struct dom_sid *sid)
3444 char *p, *q, *dn;
3446 if (!extended_dn) {
3447 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3450 /* otherwise extended_dn gets stripped off */
3451 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3452 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3455 * ADS_EXTENDED_DN_HEX_STRING:
3456 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3458 * ADS_EXTENDED_DN_STRING (only with w2k3):
3459 * <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
3461 * Object with no SID, such as an Exchange Public Folder
3462 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3465 p = strchr(dn, ';');
3466 if (!p) {
3467 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3470 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3471 DEBUG(5,("No SID present in extended dn\n"));
3472 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3475 p += strlen(";<SID=");
3477 q = strchr(p, '>');
3478 if (!q) {
3479 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3482 *q = '\0';
3484 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3486 switch (flags) {
3488 case ADS_EXTENDED_DN_STRING:
3489 if (!string_to_sid(sid, p)) {
3490 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3492 break;
3493 case ADS_EXTENDED_DN_HEX_STRING: {
3494 fstring buf;
3495 size_t buf_len;
3497 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3498 if (buf_len == 0) {
3499 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3502 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3503 DEBUG(10,("failed to parse sid\n"));
3504 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3506 break;
3508 default:
3509 DEBUG(10,("unknown extended dn format\n"));
3510 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3513 return ADS_ERROR_NT(NT_STATUS_OK);
3516 /********************************************************************
3517 ********************************************************************/
3519 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3521 LDAPMessage *res = NULL;
3522 ADS_STATUS status;
3523 int count = 0;
3524 char *name = NULL;
3526 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3527 if (!ADS_ERR_OK(status)) {
3528 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3529 lp_netbios_name()));
3530 goto out;
3533 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3534 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3535 goto out;
3538 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3539 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3542 out:
3543 ads_msgfree(ads, res);
3545 return name;
3548 /********************************************************************
3549 ********************************************************************/
3551 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3553 LDAPMessage *res = NULL;
3554 ADS_STATUS status;
3555 int count = 0;
3556 char *name = NULL;
3558 status = ads_find_machine_acct(ads, &res, machine_name);
3559 if (!ADS_ERR_OK(status)) {
3560 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3561 lp_netbios_name()));
3562 goto out;
3565 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3566 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3567 goto out;
3570 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3571 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3574 out:
3575 ads_msgfree(ads, res);
3577 return name;
3580 /********************************************************************
3581 ********************************************************************/
3583 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3585 LDAPMessage *res = NULL;
3586 ADS_STATUS status;
3587 int count = 0;
3588 char *name = NULL;
3590 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3591 if (!ADS_ERR_OK(status)) {
3592 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3593 lp_netbios_name()));
3594 goto out;
3597 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3598 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3599 goto out;
3602 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3603 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3606 out:
3607 ads_msgfree(ads, res);
3609 return name;
3612 #if 0
3614 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3617 * Join a machine to a realm
3618 * Creates the machine account and sets the machine password
3619 * @param ads connection to ads server
3620 * @param machine name of host to add
3621 * @param org_unit Organizational unit to place machine in
3622 * @return status of join
3624 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3625 uint32_t account_type, const char *org_unit)
3627 ADS_STATUS status;
3628 LDAPMessage *res = NULL;
3629 char *machine;
3631 /* machine name must be lowercase */
3632 machine = SMB_STRDUP(machine_name);
3633 strlower_m(machine);
3636 status = ads_find_machine_acct(ads, (void **)&res, machine);
3637 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3638 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3639 status = ads_leave_realm(ads, machine);
3640 if (!ADS_ERR_OK(status)) {
3641 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3642 machine, ads->config.realm));
3643 return status;
3647 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3648 if (!ADS_ERR_OK(status)) {
3649 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3650 SAFE_FREE(machine);
3651 return status;
3654 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3655 if (!ADS_ERR_OK(status)) {
3656 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3657 SAFE_FREE(machine);
3658 return status;
3661 SAFE_FREE(machine);
3662 ads_msgfree(ads, res);
3664 return status;
3666 #endif
3669 * Delete a machine from the realm
3670 * @param ads connection to ads server
3671 * @param hostname Machine to remove
3672 * @return status of delete
3674 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3676 ADS_STATUS status;
3677 void *msg;
3678 LDAPMessage *res;
3679 char *hostnameDN, *host;
3680 int rc;
3681 LDAPControl ldap_control;
3682 LDAPControl * pldap_control[2] = {NULL, NULL};
3684 pldap_control[0] = &ldap_control;
3685 memset(&ldap_control, 0, sizeof(LDAPControl));
3686 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3688 /* hostname must be lowercase */
3689 host = SMB_STRDUP(hostname);
3690 if (!strlower_m(host)) {
3691 SAFE_FREE(host);
3692 return ADS_ERROR_SYSTEM(EINVAL);
3695 status = ads_find_machine_acct(ads, &res, host);
3696 if (!ADS_ERR_OK(status)) {
3697 DEBUG(0, ("Host account for %s does not exist.\n", host));
3698 SAFE_FREE(host);
3699 return status;
3702 msg = ads_first_entry(ads, res);
3703 if (!msg) {
3704 SAFE_FREE(host);
3705 return ADS_ERROR_SYSTEM(ENOENT);
3708 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3709 if (hostnameDN == NULL) {
3710 SAFE_FREE(host);
3711 return ADS_ERROR_SYSTEM(ENOENT);
3714 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3715 if (rc) {
3716 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3717 }else {
3718 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3721 if (rc != LDAP_SUCCESS) {
3722 const char *attrs[] = { "cn", NULL };
3723 LDAPMessage *msg_sub;
3725 /* we only search with scope ONE, we do not expect any further
3726 * objects to be created deeper */
3728 status = ads_do_search_retry(ads, hostnameDN,
3729 LDAP_SCOPE_ONELEVEL,
3730 "(objectclass=*)", attrs, &res);
3732 if (!ADS_ERR_OK(status)) {
3733 SAFE_FREE(host);
3734 TALLOC_FREE(hostnameDN);
3735 return status;
3738 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3739 msg_sub = ads_next_entry(ads, msg_sub)) {
3741 char *dn = NULL;
3743 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3744 SAFE_FREE(host);
3745 TALLOC_FREE(hostnameDN);
3746 return ADS_ERROR(LDAP_NO_MEMORY);
3749 status = ads_del_dn(ads, dn);
3750 if (!ADS_ERR_OK(status)) {
3751 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3752 SAFE_FREE(host);
3753 TALLOC_FREE(dn);
3754 TALLOC_FREE(hostnameDN);
3755 return status;
3758 TALLOC_FREE(dn);
3761 /* there should be no subordinate objects anymore */
3762 status = ads_do_search_retry(ads, hostnameDN,
3763 LDAP_SCOPE_ONELEVEL,
3764 "(objectclass=*)", attrs, &res);
3766 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3767 SAFE_FREE(host);
3768 TALLOC_FREE(hostnameDN);
3769 return status;
3772 /* delete hostnameDN now */
3773 status = ads_del_dn(ads, hostnameDN);
3774 if (!ADS_ERR_OK(status)) {
3775 SAFE_FREE(host);
3776 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3777 TALLOC_FREE(hostnameDN);
3778 return status;
3782 TALLOC_FREE(hostnameDN);
3784 status = ads_find_machine_acct(ads, &res, host);
3785 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3786 DEBUG(3, ("Failed to remove host account.\n"));
3787 SAFE_FREE(host);
3788 return status;
3791 SAFE_FREE(host);
3792 return status;
3796 * pull all token-sids from an LDAP dn
3797 * @param ads connection to ads server
3798 * @param mem_ctx TALLOC_CTX for allocating sid array
3799 * @param dn of LDAP object
3800 * @param user_sid pointer to struct dom_sid (objectSid)
3801 * @param primary_group_sid pointer to struct dom_sid (self composed)
3802 * @param sids pointer to sid array to allocate
3803 * @param num_sids counter of SIDs pulled
3804 * @return status of token query
3806 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3807 TALLOC_CTX *mem_ctx,
3808 const char *dn,
3809 struct dom_sid *user_sid,
3810 struct dom_sid *primary_group_sid,
3811 struct dom_sid **sids,
3812 size_t *num_sids)
3814 ADS_STATUS status;
3815 LDAPMessage *res = NULL;
3816 int count = 0;
3817 size_t tmp_num_sids;
3818 struct dom_sid *tmp_sids;
3819 struct dom_sid tmp_user_sid;
3820 struct dom_sid tmp_primary_group_sid;
3821 uint32_t pgid;
3822 const char *attrs[] = {
3823 "objectSid",
3824 "tokenGroups",
3825 "primaryGroupID",
3826 NULL
3829 status = ads_search_retry_dn(ads, &res, dn, attrs);
3830 if (!ADS_ERR_OK(status)) {
3831 return status;
3834 count = ads_count_replies(ads, res);
3835 if (count != 1) {
3836 ads_msgfree(ads, res);
3837 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3840 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3841 ads_msgfree(ads, res);
3842 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3845 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3846 ads_msgfree(ads, res);
3847 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3851 /* hack to compose the primary group sid without knowing the
3852 * domsid */
3854 struct dom_sid domsid;
3856 sid_copy(&domsid, &tmp_user_sid);
3858 if (!sid_split_rid(&domsid, NULL)) {
3859 ads_msgfree(ads, res);
3860 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3863 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3864 ads_msgfree(ads, res);
3865 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3869 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3871 if (tmp_num_sids == 0 || !tmp_sids) {
3872 ads_msgfree(ads, res);
3873 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3876 if (num_sids) {
3877 *num_sids = tmp_num_sids;
3880 if (sids) {
3881 *sids = tmp_sids;
3884 if (user_sid) {
3885 *user_sid = tmp_user_sid;
3888 if (primary_group_sid) {
3889 *primary_group_sid = tmp_primary_group_sid;
3892 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3894 ads_msgfree(ads, res);
3895 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3899 * Find a sAMAccoutName in LDAP
3900 * @param ads connection to ads server
3901 * @param mem_ctx TALLOC_CTX for allocating sid array
3902 * @param samaccountname to search
3903 * @param uac_ret uint32_t pointer userAccountControl attribute value
3904 * @param dn_ret pointer to dn
3905 * @return status of token query
3907 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3908 TALLOC_CTX *mem_ctx,
3909 const char *samaccountname,
3910 uint32_t *uac_ret,
3911 const char **dn_ret)
3913 ADS_STATUS status;
3914 const char *attrs[] = { "userAccountControl", NULL };
3915 const char *filter;
3916 LDAPMessage *res = NULL;
3917 char *dn = NULL;
3918 uint32_t uac = 0;
3920 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3921 samaccountname);
3922 if (filter == NULL) {
3923 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3924 goto out;
3927 status = ads_do_search_all(ads, ads->config.bind_path,
3928 LDAP_SCOPE_SUBTREE,
3929 filter, attrs, &res);
3931 if (!ADS_ERR_OK(status)) {
3932 goto out;
3935 if (ads_count_replies(ads, res) != 1) {
3936 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3937 goto out;
3940 dn = ads_get_dn(ads, talloc_tos(), res);
3941 if (dn == NULL) {
3942 status = ADS_ERROR(LDAP_NO_MEMORY);
3943 goto out;
3946 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3947 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3948 goto out;
3951 if (uac_ret) {
3952 *uac_ret = uac;
3955 if (dn_ret) {
3956 *dn_ret = talloc_strdup(mem_ctx, dn);
3957 if (!*dn_ret) {
3958 status = ADS_ERROR(LDAP_NO_MEMORY);
3959 goto out;
3962 out:
3963 TALLOC_FREE(dn);
3964 ads_msgfree(ads, res);
3966 return status;
3970 * find our configuration path
3971 * @param ads connection to ads server
3972 * @param mem_ctx Pointer to talloc context
3973 * @param config_path Pointer to the config path
3974 * @return status of search
3976 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3977 TALLOC_CTX *mem_ctx,
3978 char **config_path)
3980 ADS_STATUS status;
3981 LDAPMessage *res = NULL;
3982 const char *config_context = NULL;
3983 const char *attrs[] = { "configurationNamingContext", NULL };
3985 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3986 "(objectclass=*)", attrs, &res);
3987 if (!ADS_ERR_OK(status)) {
3988 return status;
3991 config_context = ads_pull_string(ads, mem_ctx, res,
3992 "configurationNamingContext");
3993 ads_msgfree(ads, res);
3994 if (!config_context) {
3995 return ADS_ERROR(LDAP_NO_MEMORY);
3998 if (config_path) {
3999 *config_path = talloc_strdup(mem_ctx, config_context);
4000 if (!*config_path) {
4001 return ADS_ERROR(LDAP_NO_MEMORY);
4005 return ADS_ERROR(LDAP_SUCCESS);
4009 * find the displayName of an extended right
4010 * @param ads connection to ads server
4011 * @param config_path The config path
4012 * @param mem_ctx Pointer to talloc context
4013 * @param GUID struct of the rightsGUID
4014 * @return status of search
4016 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4017 const char *config_path,
4018 TALLOC_CTX *mem_ctx,
4019 const struct GUID *rights_guid)
4021 ADS_STATUS rc;
4022 LDAPMessage *res = NULL;
4023 char *expr = NULL;
4024 const char *attrs[] = { "displayName", NULL };
4025 const char *result = NULL;
4026 const char *path;
4028 if (!ads || !mem_ctx || !rights_guid) {
4029 goto done;
4032 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4033 GUID_string(mem_ctx, rights_guid));
4034 if (!expr) {
4035 goto done;
4038 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4039 if (!path) {
4040 goto done;
4043 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4044 expr, attrs, &res);
4045 if (!ADS_ERR_OK(rc)) {
4046 goto done;
4049 if (ads_count_replies(ads, res) != 1) {
4050 goto done;
4053 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4055 done:
4056 ads_msgfree(ads, res);
4057 return result;
4061 * verify or build and verify an account ou
4062 * @param mem_ctx Pointer to talloc context
4063 * @param ads connection to ads server
4064 * @param account_ou
4065 * @return status of search
4068 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4069 ADS_STRUCT *ads,
4070 const char **account_ou)
4072 char **exploded_dn;
4073 const char *name;
4074 char *ou_string;
4076 if (account_ou == NULL) {
4077 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4080 if (*account_ou != NULL) {
4081 exploded_dn = ldap_explode_dn(*account_ou, 0);
4082 if (exploded_dn) {
4083 ldap_value_free(exploded_dn);
4084 return ADS_SUCCESS;
4088 ou_string = ads_ou_string(ads, *account_ou);
4089 if (!ou_string) {
4090 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4093 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4094 ads->config.bind_path);
4095 SAFE_FREE(ou_string);
4097 if (!name) {
4098 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4101 exploded_dn = ldap_explode_dn(name, 0);
4102 if (!exploded_dn) {
4103 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4105 ldap_value_free(exploded_dn);
4107 *account_ou = name;
4108 return ADS_SUCCESS;
4111 #endif