ldap: Use netlogon_pings
[samba4-gss.git] / source3 / libads / ldap.c
blobebfcc25e4620565f1f569375a4633cfcf6052129
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 "libads/netlogon_ping.h"
29 #include "../lib/tsocket/tsocket.h"
30 #include "../lib/addns/dnsquery.h"
31 #include "../libds/common/flags.h"
32 #include "smbldap.h"
33 #include "../libcli/security/security.h"
34 #include "../librpc/gen_ndr/netlogon.h"
35 #include "lib/param/loadparm.h"
36 #include "libsmb/namequery.h"
37 #include "../librpc/gen_ndr/ndr_ads.h"
38 #include "auth/credentials/credentials.h"
39 #include "passdb.h"
41 #ifdef HAVE_LDAP
43 /**
44 * @file ldap.c
45 * @brief basic ldap client-side routines for ads server communications
47 * The routines contained here should do the necessary ldap calls for
48 * ads setups.
50 * Important note: attribute names passed into ads_ routines must
51 * already be in UTF-8 format. We do not convert them because in almost
52 * all cases, they are just ascii (which is represented with the same
53 * codepoints in UTF-8). This may have to change at some point
54 **/
57 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
59 static SIG_ATOMIC_T gotalarm;
61 /***************************************************************
62 Signal function to tell us we timed out.
63 ****************************************************************/
65 static void gotalarm_sig(int signum)
67 gotalarm = 1;
70 LDAP *ldap_open_with_timeout(const char *server,
71 struct sockaddr_storage *ss,
72 int port, unsigned int to)
74 LDAP *ldp = NULL;
75 int ldap_err;
76 char *uri;
78 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
79 "%u seconds\n", server, port, to));
81 if (to) {
82 /* Setup timeout */
83 gotalarm = 0;
84 CatchSignal(SIGALRM, gotalarm_sig);
85 alarm(to);
86 /* End setup timeout. */
89 if ( strchr_m(server, ':') ) {
90 /* IPv6 URI */
91 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
92 } else {
93 /* IPv4 URI */
94 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
96 if (uri == NULL) {
97 return NULL;
100 #ifdef HAVE_LDAP_INIT_FD
102 int fd = -1;
103 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
104 unsigned timeout_ms = 1000 * to;
106 status = open_socket_out(ss, port, timeout_ms, &fd);
107 if (!NT_STATUS_IS_OK(status)) {
108 DEBUG(3, ("open_socket_out: failed to open socket\n"));
109 return NULL;
112 /* define LDAP_PROTO_TCP from openldap.h if required */
113 #ifndef LDAP_PROTO_TCP
114 #define LDAP_PROTO_TCP 1
115 #endif
116 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
118 #elif defined(HAVE_LDAP_INITIALIZE)
119 ldap_err = ldap_initialize(&ldp, uri);
120 #else
121 ldp = ldap_open(server, port);
122 if (ldp != NULL) {
123 ldap_err = LDAP_SUCCESS;
124 } else {
125 ldap_err = LDAP_OTHER;
127 #endif
128 if (ldap_err != LDAP_SUCCESS) {
129 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
130 uri, ldap_err2string(ldap_err)));
131 } else {
132 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
135 if (to) {
136 /* Teardown timeout. */
137 alarm(0);
138 CatchSignal(SIGALRM, SIG_IGN);
141 return ldp;
144 static int ldap_search_with_timeout(LDAP *ld,
145 LDAP_CONST char *base,
146 int scope,
147 LDAP_CONST char *filter,
148 char **attrs,
149 int attrsonly,
150 LDAPControl **sctrls,
151 LDAPControl **cctrls,
152 int sizelimit,
153 LDAPMessage **res )
155 int to = lp_ldap_timeout();
156 struct timeval timeout;
157 struct timeval *timeout_ptr = NULL;
158 int result;
160 DBG_DEBUG("ldap_search: base => [%s], filter => [%s], scope => [%d]\n",
161 base,
162 filter,
163 scope);
165 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
166 gotalarm = 0;
168 if (to) {
169 timeout.tv_sec = to;
170 timeout.tv_usec = 0;
171 timeout_ptr = &timeout;
173 /* Setup alarm timeout. */
174 CatchSignal(SIGALRM, gotalarm_sig);
175 /* Make the alarm time one second beyond
176 the timeout we're setting for the
177 remote search timeout, to allow that
178 to fire in preference. */
179 alarm(to+1);
180 /* End setup timeout. */
184 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
185 attrsonly, sctrls, cctrls, timeout_ptr,
186 sizelimit, res);
188 if (to) {
189 /* Teardown alarm timeout. */
190 CatchSignal(SIGALRM, SIG_IGN);
191 alarm(0);
194 if (gotalarm != 0)
195 return LDAP_TIMELIMIT_EXCEEDED;
198 * A bug in OpenLDAP means ldap_search_ext_s can return
199 * LDAP_SUCCESS but with a NULL res pointer. Cope with
200 * this. See bug #6279 for details. JRA.
203 if (*res == NULL) {
204 return LDAP_TIMELIMIT_EXCEEDED;
207 return result;
210 /**********************************************
211 Do client and server sitename match ?
212 **********************************************/
214 bool ads_sitename_match(ADS_STRUCT *ads)
216 if (ads->config.server_site_name == NULL &&
217 ads->config.client_site_name == NULL ) {
218 DEBUG(10,("ads_sitename_match: both null\n"));
219 return True;
221 if (ads->config.server_site_name &&
222 ads->config.client_site_name &&
223 strequal(ads->config.server_site_name,
224 ads->config.client_site_name)) {
225 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
226 return True;
228 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
229 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
230 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
231 return False;
234 /**********************************************
235 Is this the closest DC ?
236 **********************************************/
238 bool ads_closest_dc(ADS_STRUCT *ads)
240 if (ads->config.flags & NBT_SERVER_CLOSEST) {
241 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
242 return True;
245 /* not sure if this can ever happen */
246 if (ads_sitename_match(ads)) {
247 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
248 return True;
251 if (ads->config.client_site_name == NULL) {
252 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
253 return True;
256 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
257 ads->config.ldap_server_name));
259 return False;
262 static bool ads_fill_cldap_reply(ADS_STRUCT *ads,
263 bool gc,
264 const struct sockaddr_storage *ss,
265 const struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply)
267 TALLOC_CTX *frame = talloc_stackframe();
268 bool ret = false;
269 char addr[INET6_ADDRSTRLEN];
270 ADS_STATUS status;
271 char *dn;
273 print_sockaddr(addr, sizeof(addr), ss);
275 /* Check the CLDAP reply flags */
277 if (!(cldap_reply->server_type & NBT_SERVER_LDAP)) {
278 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
279 addr);
280 ret = false;
281 goto out;
284 /* Fill in the ads->config values */
286 ADS_TALLOC_CONST_FREE(ads->config.workgroup);
287 ADS_TALLOC_CONST_FREE(ads->config.realm);
288 ADS_TALLOC_CONST_FREE(ads->config.bind_path);
289 ADS_TALLOC_CONST_FREE(ads->config.ldap_server_name);
290 ADS_TALLOC_CONST_FREE(ads->config.server_site_name);
291 ADS_TALLOC_CONST_FREE(ads->config.client_site_name);
293 if (!check_cldap_reply_required_flags(cldap_reply->server_type,
294 ads->config.flags)) {
295 ret = false;
296 goto out;
299 ads->config.ldap_server_name = talloc_strdup(ads,
300 cldap_reply->pdc_dns_name);
301 if (ads->config.ldap_server_name == NULL) {
302 DBG_WARNING("Out of memory\n");
303 ret = false;
304 goto out;
307 ads->config.workgroup = talloc_strdup(ads, cldap_reply->domain_name);
308 if (ads->config.workgroup == NULL) {
309 DBG_WARNING("Out of memory\n");
310 ret = false;
311 goto out;
314 ads->config.realm = talloc_asprintf_strupper_m(ads,
315 "%s",
316 cldap_reply->dns_domain);
317 if (ads->config.realm == NULL) {
318 DBG_WARNING("Out of memory\n");
319 ret = false;
320 goto out;
323 status = ads_build_dn(ads->config.realm, ads, &dn);
324 if (!ADS_ERR_OK(status)) {
325 DBG_DEBUG("Failed to build bind path: %s\n",
326 ads_errstr(status));
327 ret = false;
328 goto out;
330 ads->config.bind_path = dn;
332 if (*cldap_reply->server_site) {
333 ads->config.server_site_name =
334 talloc_strdup(ads, cldap_reply->server_site);
335 if (ads->config.server_site_name == NULL) {
336 DBG_WARNING("Out of memory\n");
337 ret = false;
338 goto out;
342 if (*cldap_reply->client_site) {
343 ads->config.client_site_name =
344 talloc_strdup(ads, cldap_reply->client_site);
345 if (ads->config.client_site_name == NULL) {
346 DBG_WARNING("Out of memory\n");
347 ret = false;
348 goto out;
352 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
353 ads->ldap.ss = *ss;
355 /* Store our site name. */
356 sitename_store(cldap_reply->domain_name, cldap_reply->client_site);
357 sitename_store(cldap_reply->dns_domain, cldap_reply->client_site);
359 /* Leave this until last so that the flags are not clobbered */
360 ads->config.flags = cldap_reply->server_type;
362 ret = true;
364 out:
366 TALLOC_FREE(frame);
367 return ret;
371 try a connection to a given ldap server, returning True and setting the servers IP
372 in the ads struct if successful
374 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
375 struct sockaddr_storage *ss)
377 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply = {};
378 TALLOC_CTX *frame = talloc_stackframe();
379 bool ok;
380 char addr[INET6_ADDRSTRLEN] = { 0, };
382 if (ss == NULL) {
383 TALLOC_FREE(frame);
384 return false;
387 print_sockaddr(addr, sizeof(addr), ss);
389 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
390 addr, ads->server.realm);
392 ok = ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply);
393 if (!ok) {
394 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
395 addr, ads->server.realm);
396 TALLOC_FREE(frame);
397 return false;
400 ok = ads_fill_cldap_reply(ads, gc, ss, &cldap_reply);
401 if (!ok) {
402 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
403 addr, ads->server.realm);
404 TALLOC_FREE(frame);
405 return false;
408 TALLOC_FREE(frame);
409 return true;
412 /**********************************************************************
413 send a cldap ping to list of servers, one at a time, until one of
414 them answers it's an ldap server. Record success in the ADS_STRUCT.
415 Take note of and update negative connection cache.
416 **********************************************************************/
418 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
419 const char *domain,
420 struct samba_sockaddr *sa_list,
421 size_t count)
423 TALLOC_CTX *frame = talloc_stackframe();
424 struct timeval endtime = timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
425 uint32_t nt_version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
426 struct tsocket_address **ts_list = NULL;
427 struct samba_sockaddr **req_sa_list = NULL;
428 struct netlogon_samlogon_response **responses = NULL;
429 size_t num_requests = 0;
430 NTSTATUS status;
431 size_t i;
432 bool ok = false;
433 bool retry;
435 ts_list = talloc_zero_array(frame,
436 struct tsocket_address *,
437 count);
438 if (ts_list == NULL) {
439 TALLOC_FREE(frame);
440 return NT_STATUS_NO_MEMORY;
443 req_sa_list = talloc_zero_array(frame,
444 struct samba_sockaddr *,
445 count);
446 if (req_sa_list == NULL) {
447 TALLOC_FREE(frame);
448 return NT_STATUS_NO_MEMORY;
451 again:
453 * The retry loop is bound by the timeout
455 retry = false;
456 num_requests = 0;
458 for (i = 0; i < count; i++) {
459 char server[INET6_ADDRSTRLEN];
460 int ret;
462 if (is_zero_addr(&sa_list[i].u.ss)) {
463 continue;
466 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
468 status = check_negative_conn_cache(domain, server);
469 if (!NT_STATUS_IS_OK(status)) {
470 continue;
473 ret = tsocket_address_inet_from_strings(ts_list, "ip",
474 server, LDAP_PORT,
475 &ts_list[num_requests]);
476 if (ret != 0) {
477 status = map_nt_error_from_unix(errno);
478 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
479 server, nt_errstr(status));
480 TALLOC_FREE(frame);
481 return status;
484 req_sa_list[num_requests] = &sa_list[i];
485 num_requests += 1;
488 DBG_DEBUG("Try to create %zu netlogon connections for domain '%s' "
489 "(provided count of addresses was %zu).\n",
490 num_requests,
491 domain,
492 count);
494 if (num_requests == 0) {
495 status = NT_STATUS_NO_LOGON_SERVERS;
496 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
497 domain, num_requests, count, nt_errstr(status));
498 TALLOC_FREE(frame);
499 return status;
502 status = netlogon_pings(frame, /* mem_ctx */
503 lp_client_netlogon_ping_protocol(), /* proto */
504 ts_list, /* servers */
505 num_requests, /* num_servers */
506 (struct netlogon_ping_filter){
507 .ntversion = nt_version,
508 .domain = ads->server.realm,
509 .acct_ctrl = -1,
510 .required_flags = ads->config.flags |
511 DS_ONLY_LDAP_NEEDED,
513 1, /* min_servers */
514 endtime, /* timeout */
515 &responses);
516 if (!NT_STATUS_IS_OK(status)) {
517 DBG_WARNING("netlogon_pings(realm=%s, num_requests=%zu) "
518 "for count[%zu] - %s\n",
519 ads->server.realm,
520 num_requests, count,
521 nt_errstr(status));
522 TALLOC_FREE(frame);
523 return NT_STATUS_NO_LOGON_SERVERS;
526 for (i = 0; i < num_requests; i++) {
527 struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply = NULL;
528 char server[INET6_ADDRSTRLEN];
530 if (responses[i] == NULL) {
531 continue;
534 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
536 if (responses[i]->ntver != NETLOGON_NT_VERSION_5EX) {
537 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
538 ads->server.realm,
539 responses[i]->ntver, server);
540 continue;
543 cldap_reply = &responses[i]->data.nt5_ex;
545 /* Returns ok only if it matches the correct server type */
546 ok = ads_fill_cldap_reply(ads,
547 false,
548 &req_sa_list[i]->u.ss,
549 cldap_reply);
550 if (ok) {
551 DBG_DEBUG("realm[%s]: selected %s => %s\n",
552 ads->server.realm,
553 server, cldap_reply->pdc_dns_name);
554 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
555 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
556 cldap_reply);
558 TALLOC_FREE(frame);
559 return NT_STATUS_OK;
562 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
563 ads->server.realm,
564 server, cldap_reply->pdc_dns_name);
565 if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
566 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
567 cldap_reply);
569 add_failed_connection_entry(domain, server,
570 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID);
571 retry = true;
574 if (retry) {
575 bool expired;
577 expired = timeval_expired(&endtime);
578 if (!expired) {
579 goto again;
583 /* keep track of failures as all were not suitable */
584 for (i = 0; i < num_requests; i++) {
585 char server[INET6_ADDRSTRLEN];
587 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
589 add_failed_connection_entry(domain, server,
590 NT_STATUS_UNSUCCESSFUL);
593 status = NT_STATUS_NO_LOGON_SERVERS;
594 DBG_WARNING("realm[%s] no valid response "
595 "num_requests[%zu] for count[%zu] - %s\n",
596 ads->server.realm,
597 num_requests, count, nt_errstr(status));
598 TALLOC_FREE(frame);
599 return NT_STATUS_NO_LOGON_SERVERS;
602 /***************************************************************************
603 resolve a name and perform an "ldap ping" using NetBIOS and related methods
604 ****************************************************************************/
606 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
607 const char *domain, const char *realm)
609 size_t i;
610 size_t count = 0;
611 struct samba_sockaddr *sa_list = NULL;
612 NTSTATUS status;
614 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
615 domain));
617 status = get_sorted_dc_list(talloc_tos(),
618 domain,
619 NULL,
620 &sa_list,
621 &count,
622 false);
623 if (!NT_STATUS_IS_OK(status)) {
624 return status;
627 /* remove servers which are known to be dead based on
628 the corresponding DNS method */
629 if (*realm) {
630 for (i = 0; i < count; ++i) {
631 char server[INET6_ADDRSTRLEN];
633 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
635 if(!NT_STATUS_IS_OK(
636 check_negative_conn_cache(realm, server))) {
637 /* Ensure we add the workgroup name for this
638 IP address as negative too. */
639 add_failed_connection_entry(
640 domain, server,
641 NT_STATUS_UNSUCCESSFUL);
646 status = cldap_ping_list(ads, domain, sa_list, count);
648 TALLOC_FREE(sa_list);
650 return status;
654 /**********************************************************************
655 resolve a name and perform an "ldap ping" using DNS
656 **********************************************************************/
658 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
659 const char *realm)
661 size_t count = 0;
662 struct samba_sockaddr *sa_list = NULL;
663 NTSTATUS status;
665 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
666 realm));
668 status = get_sorted_dc_list(talloc_tos(),
669 realm,
670 sitename,
671 &sa_list,
672 &count,
673 true);
674 if (!NT_STATUS_IS_OK(status)) {
675 TALLOC_FREE(sa_list);
676 return status;
679 status = cldap_ping_list(ads, realm, sa_list, count);
681 TALLOC_FREE(sa_list);
683 return status;
686 /**********************************************************************
687 Try to find an AD dc using our internal name resolution routines
688 Try the realm first and then the workgroup name if netbios is not
689 disabled
690 **********************************************************************/
692 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
694 const char *c_domain = "";
695 const char *c_realm;
696 bool use_own_domain = False;
697 char *sitename = NULL;
698 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
699 bool ok = false;
701 /* if the realm and workgroup are both empty, assume they are ours */
703 /* realm */
704 c_realm = ads->server.realm;
706 if (c_realm == NULL)
707 c_realm = "";
709 if (!*c_realm) {
710 /* special case where no realm and no workgroup means our own */
711 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
712 use_own_domain = True;
713 c_realm = lp_realm();
717 if (!lp_disable_netbios()) {
718 if (use_own_domain) {
719 c_domain = lp_workgroup();
720 } else {
721 c_domain = ads->server.workgroup;
722 if (!*c_realm && (!c_domain || !*c_domain)) {
723 c_domain = lp_workgroup();
727 if (!c_domain) {
728 c_domain = "";
732 if (!*c_realm && !*c_domain) {
733 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
734 "what to do\n"));
735 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
739 * In case of LDAP we use get_dc_name() as that
740 * creates the custom krb5.conf file
742 if (ads->auth.flags & ADS_AUTH_GENERATE_KRB5_CONFIG) {
743 fstring srv_name;
744 struct sockaddr_storage ip_out;
746 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
747 " and falling back to domain '%s'\n",
748 c_realm, c_domain));
750 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
751 if (ok) {
752 if (is_zero_addr(&ip_out)) {
753 return NT_STATUS_NO_LOGON_SERVERS;
757 * we call ads_try_connect() to fill in the
758 * ads->config details
760 ok = ads_try_connect(ads, false, &ip_out);
761 if (ok) {
762 return NT_STATUS_OK;
766 return NT_STATUS_NO_LOGON_SERVERS;
769 if (*c_realm) {
770 sitename = sitename_fetch(talloc_tos(), c_realm);
771 status = resolve_and_ping_dns(ads, sitename, c_realm);
773 if (NT_STATUS_IS_OK(status)) {
774 TALLOC_FREE(sitename);
775 return status;
778 /* In case we failed to contact one of our closest DC on our
779 * site we
780 * need to try to find another DC, retry with a site-less SRV
781 * DNS query
782 * - Guenther */
784 if (sitename) {
785 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
786 "our site (%s), Trying to find another DC "
787 "for realm '%s' (domain '%s')\n",
788 sitename, c_realm, c_domain));
789 namecache_delete(c_realm, 0x1C);
790 status =
791 resolve_and_ping_dns(ads, NULL, c_realm);
793 if (NT_STATUS_IS_OK(status)) {
794 TALLOC_FREE(sitename);
795 return status;
799 TALLOC_FREE(sitename);
802 /* try netbios as fallback - if permitted,
803 or if configuration specifically requests it */
804 if (*c_domain) {
805 if (*c_realm) {
806 DEBUG(3, ("ads_find_dc: falling back to netbios "
807 "name resolution for domain '%s' (realm '%s')\n",
808 c_domain, c_realm));
811 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
812 if (NT_STATUS_IS_OK(status)) {
813 return status;
817 DEBUG(1, ("ads_find_dc: "
818 "name resolution for realm '%s' (domain '%s') failed: %s\n",
819 c_realm, c_domain, nt_errstr(status)));
820 return status;
824 * Connect to the LDAP server
825 * @param ads Pointer to an existing ADS_STRUCT
826 * @return status of connection
828 static ADS_STATUS ads_connect_internal(ADS_STRUCT *ads,
829 struct cli_credentials *creds)
831 int version = LDAP_VERSION3;
832 ADS_STATUS status;
833 NTSTATUS ntstatus;
834 char addr[INET6_ADDRSTRLEN];
835 struct sockaddr_storage existing_ss;
836 bool tls = false;
837 bool start_tls = false;
839 zero_sockaddr(&existing_ss);
841 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
842 SMB_ASSERT(creds != NULL);
845 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
847 * Simple anonyous binds are only
848 * allowed for anonymous credentials
850 SMB_ASSERT(cli_credentials_is_anonymous(creds));
853 if (!(ads->auth.flags & (ADS_AUTH_NO_BIND|ADS_AUTH_ANON_BIND))) {
854 ads->auth.flags |= ADS_AUTH_GENERATE_KRB5_CONFIG;
858 * ads_connect can be passed in a reused ADS_STRUCT
859 * with an existing non-zero ads->ldap.ss IP address
860 * that was stored by going through ads_find_dc()
861 * if ads->server.ldap_server was NULL.
863 * If ads->server.ldap_server is still NULL but
864 * the target address isn't the zero address, then
865 * store that address off off before zeroing out
866 * ads->ldap so we don't keep doing multiple calls
867 * to ads_find_dc() in the reuse case.
869 * If a caller wants a clean ADS_STRUCT they
870 * will TALLOC_FREE it and allocate a new one
871 * by calling ads_init(), which ensures
872 * ads->ldap.ss is a properly zero'ed out valid IP
873 * address.
875 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
876 /* Save off the address we previously found by ads_find_dc(). */
877 existing_ss = ads->ldap.ss;
880 ads_zero_ldap(ads);
881 ZERO_STRUCT(ads->ldap_tls_data);
882 ZERO_STRUCT(ads->ldap_wrap_data);
883 ads->ldap.last_attempt = time_mono(NULL);
884 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
886 /* try with a user specified server */
888 if (DEBUGLEVEL >= 11) {
889 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
890 DEBUG(11,("ads_connect: entering\n"));
891 DEBUGADD(11,("%s\n", s));
892 TALLOC_FREE(s);
895 if (ads->server.ldap_server) {
896 bool ok = false;
897 struct sockaddr_storage ss;
899 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
900 ads->server.ldap_server);
901 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
902 if (!ok) {
903 DEBUG(5,("ads_connect: unable to resolve name %s\n",
904 ads->server.ldap_server));
905 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
906 goto out;
909 if (is_zero_addr(&ss)) {
910 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
911 goto out;
914 ok = ads_try_connect(ads, ads->server.gc, &ss);
915 if (ok) {
916 goto got_connection;
919 /* The choice of which GC use is handled one level up in
920 ads_connect_gc(). If we continue on from here with
921 ads_find_dc() we will get GC searches on port 389 which
922 doesn't work. --jerry */
924 if (ads->server.gc == true) {
925 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
928 if (ads->server.no_fallback) {
929 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
930 goto out;
934 if (!is_zero_addr(&existing_ss)) {
935 /* We saved off who we should talk to. */
936 bool ok = ads_try_connect(ads,
937 ads->server.gc,
938 &existing_ss);
939 if (ok) {
940 goto got_connection;
943 * Keep trying to find a server and fall through
944 * into ads_find_dc() again.
946 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
947 "trying to find another DC.\n");
950 ntstatus = ads_find_dc(ads);
951 if (NT_STATUS_IS_OK(ntstatus)) {
952 goto got_connection;
955 status = ADS_ERROR_NT(ntstatus);
956 goto out;
958 got_connection:
960 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
961 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
963 if (!ads->auth.kdc_server) {
964 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
965 ads->auth.kdc_server = talloc_strdup(ads, addr);
966 if (ads->auth.kdc_server == NULL) {
967 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
968 goto out;
972 /* If the caller() requested no LDAP bind, then we are done */
974 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
975 status = ADS_SUCCESS;
976 goto out;
979 ads->ldap_tls_data.mem_ctx = talloc_init("ads LDAP TLS connection memory");
980 if (!ads->ldap_tls_data.mem_ctx) {
981 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
982 goto out;
985 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
986 if (!ads->ldap_wrap_data.mem_ctx) {
987 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
988 goto out;
991 /* Otherwise setup the TCP LDAP session */
993 if (ads->auth.flags & ADS_AUTH_SASL_LDAPS) {
994 tls = true;
995 ads->ldap.port = 636;
996 } else if (ads->auth.flags & ADS_AUTH_SASL_STARTTLS) {
997 tls = true;
998 start_tls = true;
999 ads->ldap.port = 389;
1000 } else {
1001 ads->ldap.port = 389;
1004 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
1005 &ads->ldap.ss,
1006 ads->ldap.port, lp_ldap_timeout());
1007 if (ads->ldap.ld == NULL) {
1008 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
1009 goto out;
1011 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
1013 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1015 if (start_tls) {
1016 unsigned int to = lp_ldap_connection_timeout();
1017 struct berval *rspdata = NULL;
1018 char *rspoid = NULL;
1019 int rc;
1021 if (to) {
1022 /* Setup timeout */
1023 gotalarm = 0;
1024 CatchSignal(SIGALRM, gotalarm_sig);
1025 alarm(to);
1026 /* End setup timeout. */
1029 rc = ldap_extended_operation_s(ads->ldap.ld,
1030 LDAP_EXOP_START_TLS,
1031 NULL,
1032 NULL,
1033 NULL,
1034 &rspoid,
1035 &rspdata);
1036 if (gotalarm != 0 && rc == LDAP_SUCCESS) {
1037 rc = LDAP_TIMEOUT;
1040 if (to) {
1041 /* Teardown timeout. */
1042 alarm(0);
1043 CatchSignal(SIGALRM, SIG_IGN);
1046 if (rspoid != NULL) {
1047 ldap_memfree(rspoid);
1050 if (rspdata != NULL) {
1051 ber_bvfree(rspdata);
1054 if (rc != LDAP_SUCCESS) {
1055 status = ADS_ERROR_LDAP(rc);
1056 goto out;
1060 if (tls) {
1061 unsigned int to = lp_ldap_connection_timeout();
1063 if (to) {
1064 /* Setup timeout */
1065 gotalarm = 0;
1066 CatchSignal(SIGALRM, gotalarm_sig);
1067 alarm(to);
1068 /* End setup timeout. */
1071 status = ads_setup_tls_wrapping(&ads->ldap_tls_data,
1072 ads->ldap.ld,
1073 ads->config.ldap_server_name);
1075 if (to) {
1076 /* Teardown timeout. */
1077 alarm(0);
1078 CatchSignal(SIGALRM, SIG_IGN);
1081 if ( !ADS_ERR_OK(status) ) {
1082 goto out;
1086 /* cache the successful connection for workgroup and realm */
1087 if (ads_closest_dc(ads)) {
1088 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
1089 saf_store( ads->server.realm, ads->config.ldap_server_name);
1092 /* fill in the current time and offsets */
1094 status = ads_current_time( ads );
1095 if ( !ADS_ERR_OK(status) ) {
1096 goto out;
1099 /* Now do the bind */
1101 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
1102 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
1103 goto out;
1106 status = ads_sasl_bind(ads, creds);
1108 out:
1109 if (DEBUGLEVEL >= 11) {
1110 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1111 DEBUG(11,("ads_connect: leaving with: %s\n",
1112 ads_errstr(status)));
1113 DEBUGADD(11,("%s\n", s));
1114 TALLOC_FREE(s);
1117 return status;
1121 * Connect to the LDAP server using without a bind
1122 * and without a tcp connection at all
1124 * @param ads Pointer to an existing ADS_STRUCT
1125 * @return status of connection
1127 ADS_STATUS ads_connect_cldap_only(ADS_STRUCT *ads)
1129 ads->auth.flags |= ADS_AUTH_NO_BIND;
1130 return ads_connect_internal(ads, NULL);
1134 * Connect to the LDAP server
1135 * @param ads Pointer to an existing ADS_STRUCT
1136 * @return status of connection
1138 ADS_STATUS ads_connect_creds(ADS_STRUCT *ads, struct cli_credentials *creds)
1140 SMB_ASSERT(creds != NULL);
1143 * We allow upgrades from
1144 * ADS_AUTH_NO_BIND if credentials
1145 * are specified
1147 ads->auth.flags &= ~ADS_AUTH_NO_BIND;
1150 * We allow upgrades from ADS_AUTH_ANON_BIND,
1151 * as we don't want to use simple binds with
1152 * non-anon credentials
1154 if (!cli_credentials_is_anonymous(creds)) {
1155 ads->auth.flags &= ~ADS_AUTH_ANON_BIND;
1158 return ads_connect_internal(ads, creds);
1162 * Connect to the LDAP server using anonymous credentials
1163 * using a simple bind without username/password
1165 * @param ads Pointer to an existing ADS_STRUCT
1166 * @return status of connection
1168 ADS_STATUS ads_connect_simple_anon(ADS_STRUCT *ads)
1170 TALLOC_CTX *frame = talloc_stackframe();
1171 struct cli_credentials *creds = NULL;
1172 ADS_STATUS status;
1174 creds = cli_credentials_init_anon(frame);
1175 if (creds == NULL) {
1176 TALLOC_FREE(frame);
1177 return ADS_ERROR_SYSTEM(errno);
1180 ads->auth.flags |= ADS_AUTH_ANON_BIND;
1181 status = ads_connect_creds(ads, creds);
1182 TALLOC_FREE(frame);
1183 return status;
1187 * Connect to the LDAP server using the machine account
1188 * @param ads Pointer to an existing ADS_STRUCT
1189 * @return status of connection
1191 ADS_STATUS ads_connect_machine(ADS_STRUCT *ads)
1193 TALLOC_CTX *frame = talloc_stackframe();
1194 struct cli_credentials *creds = NULL;
1195 ADS_STATUS status;
1196 NTSTATUS ntstatus;
1198 ntstatus = pdb_get_trust_credentials(ads->server.workgroup,
1199 ads->server.realm,
1200 frame,
1201 &creds);
1202 if (!NT_STATUS_IS_OK(ntstatus)) {
1203 TALLOC_FREE(frame);
1204 return ADS_ERROR_NT(ntstatus);
1207 status = ads_connect_creds(ads, creds);
1208 TALLOC_FREE(frame);
1209 return status;
1213 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1214 * @param ads Pointer to an existing ADS_STRUCT
1216 * Sets the ads->ldap.ss to a valid
1217 * zero ip address that can be detected by
1218 * our is_zero_addr() function. Otherwise
1219 * it is left as AF_UNSPEC (0).
1221 void ads_zero_ldap(ADS_STRUCT *ads)
1223 ZERO_STRUCT(ads->ldap);
1225 * Initialize the sockaddr_storage so we can use
1226 * sockaddr test functions against it.
1228 zero_sockaddr(&ads->ldap.ss);
1232 * Disconnect the LDAP server
1233 * @param ads Pointer to an existing ADS_STRUCT
1235 void ads_disconnect(ADS_STRUCT *ads)
1237 if (ads->ldap.ld) {
1238 ldap_unbind(ads->ldap.ld);
1239 ads->ldap.ld = NULL;
1241 if (ads->ldap_tls_data.mem_ctx) {
1242 talloc_free(ads->ldap_tls_data.mem_ctx);
1244 if (ads->ldap_wrap_data.wrap_ops &&
1245 ads->ldap_wrap_data.wrap_ops->disconnect) {
1246 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1248 if (ads->ldap_wrap_data.mem_ctx) {
1249 talloc_free(ads->ldap_wrap_data.mem_ctx);
1251 ads_zero_ldap(ads);
1252 ZERO_STRUCT(ads->ldap_tls_data);
1253 ZERO_STRUCT(ads->ldap_wrap_data);
1257 Duplicate a struct berval into talloc'ed memory
1259 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1261 struct berval *value;
1263 if (!in_val) return NULL;
1265 value = talloc_zero(ctx, struct berval);
1266 if (value == NULL)
1267 return NULL;
1268 if (in_val->bv_len == 0) return value;
1270 value->bv_len = in_val->bv_len;
1271 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1272 in_val->bv_len);
1273 return value;
1277 Make a values list out of an array of (struct berval *)
1279 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1280 const struct berval **in_vals)
1282 struct berval **values;
1283 int i;
1285 if (!in_vals) return NULL;
1286 for (i=0; in_vals[i]; i++)
1287 ; /* count values */
1288 values = talloc_zero_array(ctx, struct berval *, i+1);
1289 if (!values) return NULL;
1291 for (i=0; in_vals[i]; i++) {
1292 values[i] = dup_berval(ctx, in_vals[i]);
1294 return values;
1298 UTF8-encode a values list out of an array of (char *)
1300 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1302 char **values;
1303 int i;
1304 size_t size;
1306 if (!in_vals) return NULL;
1307 for (i=0; in_vals[i]; i++)
1308 ; /* count values */
1309 values = talloc_zero_array(ctx, char *, i+1);
1310 if (!values) return NULL;
1312 for (i=0; in_vals[i]; i++) {
1313 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1314 TALLOC_FREE(values);
1315 return NULL;
1318 return values;
1322 Pull a (char *) array out of a UTF8-encoded values list
1324 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1326 char **values;
1327 int i;
1328 size_t converted_size;
1330 if (!in_vals) return NULL;
1331 for (i=0; in_vals[i]; i++)
1332 ; /* count values */
1333 values = talloc_zero_array(ctx, char *, i+1);
1334 if (!values) return NULL;
1336 for (i=0; in_vals[i]; i++) {
1337 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1338 &converted_size)) {
1339 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1340 "%s\n", strerror(errno)));
1343 return values;
1347 * Do a search with paged results. cookie must be null on the first
1348 * call, and then returned on each subsequent call. It will be null
1349 * again when the entire search is complete
1350 * @param ads connection to ads server
1351 * @param bind_path Base dn for the search
1352 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1353 * @param expr Search expression - specified in local charset
1354 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1355 * @param res ** which will contain results - free res* with ads_msgfree()
1356 * @param count Number of entries retrieved on this page
1357 * @param cookie The paged results cookie to be returned on subsequent calls
1358 * @return status of search
1360 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1361 const char *bind_path,
1362 int scope, const char *expr,
1363 const char **attrs, void *args,
1364 LDAPMessage **res,
1365 int *count, struct berval **cookie)
1367 int rc, i, version;
1368 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1369 size_t converted_size;
1370 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1371 BerElement *cookie_be = NULL;
1372 struct berval *cookie_bv= NULL;
1373 BerElement *ext_be = NULL;
1374 struct berval *ext_bv= NULL;
1376 TALLOC_CTX *ctx;
1377 ads_control *external_control = (ads_control *) args;
1379 *res = NULL;
1381 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1382 return ADS_ERROR(LDAP_NO_MEMORY);
1384 /* 0 means the conversion worked but the result was empty
1385 so we only fail if it's -1. In any case, it always
1386 at least nulls out the dest */
1387 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1388 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1390 rc = LDAP_NO_MEMORY;
1391 goto done;
1394 if (!attrs || !(*attrs))
1395 search_attrs = NULL;
1396 else {
1397 /* This would be the utf8-encoded version...*/
1398 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1399 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1400 rc = LDAP_NO_MEMORY;
1401 goto done;
1405 /* Paged results only available on ldap v3 or later */
1406 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1407 if (version < LDAP_VERSION3) {
1408 rc = LDAP_NOT_SUPPORTED;
1409 goto done;
1412 cookie_be = ber_alloc_t(LBER_USE_DER);
1413 if (*cookie) {
1414 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1415 ber_bvfree(*cookie); /* don't need it from last time */
1416 *cookie = NULL;
1417 } else {
1418 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1420 ber_flatten(cookie_be, &cookie_bv);
1421 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1422 PagedResults.ldctl_iscritical = (char) 1;
1423 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1424 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1426 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1427 NoReferrals.ldctl_iscritical = (char) 0;
1428 NoReferrals.ldctl_value.bv_len = 0;
1429 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1431 if (external_control &&
1432 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1433 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1435 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1436 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1438 /* win2k does not accept a ldctl_value being passed in */
1440 if (external_control->val != 0) {
1442 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1443 rc = LDAP_NO_MEMORY;
1444 goto done;
1447 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1448 rc = LDAP_NO_MEMORY;
1449 goto done;
1451 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1452 rc = LDAP_NO_MEMORY;
1453 goto done;
1456 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1457 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1459 } else {
1460 ExternalCtrl.ldctl_value.bv_len = 0;
1461 ExternalCtrl.ldctl_value.bv_val = NULL;
1464 controls[0] = &NoReferrals;
1465 controls[1] = &PagedResults;
1466 controls[2] = &ExternalCtrl;
1467 controls[3] = NULL;
1469 } else {
1470 controls[0] = &NoReferrals;
1471 controls[1] = &PagedResults;
1472 controls[2] = NULL;
1475 /* we need to disable referrals as the openldap libs don't
1476 handle them and paged results at the same time. Using them
1477 together results in the result record containing the server
1478 page control being removed from the result list (tridge/jmcd)
1480 leaving this in despite the control that says don't generate
1481 referrals, in case the server doesn't support it (jmcd)
1483 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1485 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1486 search_attrs, 0, controls,
1487 NULL, LDAP_NO_LIMIT,
1488 (LDAPMessage **)res);
1490 ber_free(cookie_be, 1);
1491 ber_bvfree(cookie_bv);
1493 if (rc) {
1494 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1495 ldap_err2string(rc)));
1496 if (rc == LDAP_OTHER) {
1497 char *ldap_errmsg;
1498 int ret;
1500 ret = ldap_parse_result(ads->ldap.ld,
1501 *res,
1502 NULL,
1503 NULL,
1504 &ldap_errmsg,
1505 NULL,
1506 NULL,
1508 if (ret == LDAP_SUCCESS) {
1509 DEBUG(3, ("ldap_search_with_timeout(%s) "
1510 "error: %s\n", expr, ldap_errmsg));
1511 ldap_memfree(ldap_errmsg);
1514 goto done;
1517 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1518 NULL, &rcontrols, 0);
1520 if (!rcontrols) {
1521 goto done;
1524 for (i=0; rcontrols[i]; i++) {
1525 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1526 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1527 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1528 &cookie_bv);
1529 /* the berval is the cookie, but must be freed when
1530 it is all done */
1531 if (cookie_bv->bv_len) /* still more to do */
1532 *cookie=ber_bvdup(cookie_bv);
1533 else
1534 *cookie=NULL;
1535 ber_bvfree(cookie_bv);
1536 ber_free(cookie_be, 1);
1537 break;
1540 ldap_controls_free(rcontrols);
1542 done:
1543 talloc_destroy(ctx);
1545 if (ext_be) {
1546 ber_free(ext_be, 1);
1549 if (ext_bv) {
1550 ber_bvfree(ext_bv);
1553 if (rc != LDAP_SUCCESS && *res != NULL) {
1554 ads_msgfree(ads, *res);
1555 *res = NULL;
1558 /* if/when we decide to utf8-encode attrs, take out this next line */
1559 TALLOC_FREE(search_attrs);
1561 return ADS_ERROR(rc);
1564 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1565 int scope, const char *expr,
1566 const char **attrs, LDAPMessage **res,
1567 int *count, struct berval **cookie)
1569 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1574 * Get all results for a search. This uses ads_do_paged_search() to return
1575 * all entries in a large search.
1576 * @param ads connection to ads server
1577 * @param bind_path Base dn for the search
1578 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1579 * @param expr Search expression
1580 * @param attrs Attributes to retrieve
1581 * @param res ** which will contain results - free res* with ads_msgfree()
1582 * @return status of search
1584 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1585 int scope, const char *expr,
1586 const char **attrs, void *args,
1587 LDAPMessage **res)
1589 struct berval *cookie = NULL;
1590 int count = 0;
1591 ADS_STATUS status;
1593 *res = NULL;
1594 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1595 &count, &cookie);
1597 if (!ADS_ERR_OK(status))
1598 return status;
1600 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1601 while (cookie) {
1602 LDAPMessage *res2 = NULL;
1603 LDAPMessage *msg, *next;
1605 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1606 attrs, args, &res2, &count, &cookie);
1607 if (!ADS_ERR_OK(status)) {
1608 break;
1611 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1612 that this works on all ldap libs, but I have only tested with openldap */
1613 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1614 next = ads_next_message(ads, msg);
1615 ldap_add_result_entry((LDAPMessage **)res, msg);
1617 /* note that we do not free res2, as the memory is now
1618 part of the main returned list */
1620 #else
1621 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1622 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1623 #endif
1625 return status;
1628 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1629 int scope, const char *expr,
1630 const char **attrs, LDAPMessage **res)
1632 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1635 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1636 int scope, const char *expr,
1637 const char **attrs, uint32_t sd_flags,
1638 LDAPMessage **res)
1640 ads_control args;
1642 args.control = ADS_SD_FLAGS_OID;
1643 args.val = sd_flags;
1644 args.critical = True;
1646 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1651 * Run a function on all results for a search. Uses ads_do_paged_search() and
1652 * runs the function as each page is returned, using ads_process_results()
1653 * @param ads connection to ads server
1654 * @param bind_path Base dn for the search
1655 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1656 * @param expr Search expression - specified in local charset
1657 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1658 * @param fn Function which takes attr name, values list, and data_area
1659 * @param data_area Pointer which is passed to function on each call
1660 * @return status of search
1662 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1663 int scope, const char *expr, const char **attrs,
1664 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1665 void *data_area)
1667 struct berval *cookie = NULL;
1668 int count = 0;
1669 ADS_STATUS status;
1670 LDAPMessage *res;
1672 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1673 &count, &cookie);
1675 if (!ADS_ERR_OK(status)) return status;
1677 ads_process_results(ads, res, fn, data_area);
1678 ads_msgfree(ads, res);
1680 while (cookie) {
1681 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1682 &res, &count, &cookie);
1684 if (!ADS_ERR_OK(status)) break;
1686 ads_process_results(ads, res, fn, data_area);
1687 ads_msgfree(ads, res);
1690 return status;
1694 * Do a search with a timeout.
1695 * @param ads connection to ads server
1696 * @param bind_path Base dn for the search
1697 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1698 * @param expr Search expression
1699 * @param attrs Attributes to retrieve
1700 * @param res ** which will contain results - free res* with ads_msgfree()
1701 * @return status of search
1703 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1704 const char *expr,
1705 const char **attrs, LDAPMessage **res)
1707 int rc;
1708 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1709 size_t converted_size;
1710 TALLOC_CTX *ctx;
1712 *res = NULL;
1713 if (!(ctx = talloc_init("ads_do_search"))) {
1714 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1715 return ADS_ERROR(LDAP_NO_MEMORY);
1718 /* 0 means the conversion worked but the result was empty
1719 so we only fail if it's negative. In any case, it always
1720 at least nulls out the dest */
1721 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1722 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1724 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1725 rc = LDAP_NO_MEMORY;
1726 goto done;
1729 if (!attrs || !(*attrs))
1730 search_attrs = NULL;
1731 else {
1732 /* This would be the utf8-encoded version...*/
1733 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1734 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1736 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1737 rc = LDAP_NO_MEMORY;
1738 goto done;
1742 /* see the note in ads_do_paged_search - we *must* disable referrals */
1743 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1745 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1746 search_attrs, 0, NULL, NULL,
1747 LDAP_NO_LIMIT,
1748 (LDAPMessage **)res);
1750 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1751 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1752 rc = 0;
1755 done:
1756 talloc_destroy(ctx);
1757 /* if/when we decide to utf8-encode attrs, take out this next line */
1758 TALLOC_FREE(search_attrs);
1759 return ADS_ERROR(rc);
1762 * Do a general ADS search
1763 * @param ads connection to ads server
1764 * @param res ** which will contain results - free res* with ads_msgfree()
1765 * @param expr Search expression
1766 * @param attrs Attributes to retrieve
1767 * @return status of search
1769 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1770 const char *expr, const char **attrs)
1772 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1773 expr, attrs, res);
1777 * Do a search on a specific DistinguishedName
1778 * @param ads connection to ads server
1779 * @param res ** which will contain results - free res* with ads_msgfree()
1780 * @param dn DistinguishedName to search
1781 * @param attrs Attributes to retrieve
1782 * @return status of search
1784 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1785 const char *dn, const char **attrs)
1787 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1788 attrs, res);
1792 * Free up memory from a ads_search
1793 * @param ads connection to ads server
1794 * @param msg Search results to free
1796 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1798 if (!msg) return;
1799 ldap_msgfree(msg);
1803 * Get a dn from search results
1804 * @param ads connection to ads server
1805 * @param msg Search result
1806 * @return dn string
1808 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1810 char *utf8_dn, *unix_dn;
1811 size_t converted_size;
1813 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1815 if (!utf8_dn) {
1816 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1817 return NULL;
1820 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1821 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1822 utf8_dn ));
1823 return NULL;
1825 ldap_memfree(utf8_dn);
1826 return unix_dn;
1830 * Get the parent from a dn
1831 * @param dn the dn to return the parent from
1832 * @return parent dn string
1834 char *ads_parent_dn(const char *dn)
1836 char *p;
1838 if (dn == NULL) {
1839 return NULL;
1842 p = strchr(dn, ',');
1844 if (p == NULL) {
1845 return NULL;
1848 return p+1;
1852 * Find a machine account given a hostname
1853 * @param ads connection to ads server
1854 * @param res ** which will contain results - free res* with ads_msgfree()
1855 * @param host Hostname to search for
1856 * @return status of search
1858 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1859 const char *machine)
1861 ADS_STATUS status;
1862 char *expr;
1863 const char *attrs[] = {
1864 /* This is how Windows checks for machine accounts */
1865 "objectClass",
1866 "SamAccountName",
1867 "userAccountControl",
1868 "DnsHostName",
1869 "ServicePrincipalName",
1870 "userPrincipalName",
1872 /* Additional attributes Samba checks */
1873 "msDS-KeyVersionNumber",
1874 "msDS-AdditionalDnsHostName",
1875 "msDS-SupportedEncryptionTypes",
1876 "nTSecurityDescriptor",
1877 "objectSid",
1879 NULL
1881 TALLOC_CTX *frame = talloc_stackframe();
1883 *res = NULL;
1885 /* the easiest way to find a machine account anywhere in the tree
1886 is to look for hostname$ */
1887 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1888 if (expr == NULL) {
1889 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1890 goto done;
1893 status = ads_search(ads, res, expr, attrs);
1894 if (ADS_ERR_OK(status)) {
1895 if (ads_count_replies(ads, *res) != 1) {
1896 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1900 done:
1901 TALLOC_FREE(frame);
1902 return status;
1906 * Initialize a list of mods to be used in a modify request
1907 * @param ctx An initialized TALLOC_CTX
1908 * @return allocated ADS_MODLIST
1910 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1912 #define ADS_MODLIST_ALLOC_SIZE 10
1913 LDAPMod **mods;
1915 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1916 /* -1 is safety to make sure we don't go over the end.
1917 need to reset it to NULL before doing ldap modify */
1918 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1920 return (ADS_MODLIST)mods;
1925 add an attribute to the list, with values list already constructed
1927 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1928 int mod_op, const char *name,
1929 const void *_invals)
1931 int curmod;
1932 LDAPMod **modlist = (LDAPMod **) *mods;
1933 struct berval **ber_values = NULL;
1934 char **char_values = NULL;
1936 if (!_invals) {
1937 mod_op = LDAP_MOD_DELETE;
1938 } else {
1939 if (mod_op & LDAP_MOD_BVALUES) {
1940 const struct berval **b;
1941 b = discard_const_p(const struct berval *, _invals);
1942 ber_values = ads_dup_values(ctx, b);
1943 } else {
1944 const char **c;
1945 c = discard_const_p(const char *, _invals);
1946 char_values = ads_push_strvals(ctx, c);
1950 /* find the first empty slot */
1951 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1952 curmod++);
1953 if (modlist[curmod] == (LDAPMod *) -1) {
1954 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1955 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1956 return ADS_ERROR(LDAP_NO_MEMORY);
1957 memset(&modlist[curmod], 0,
1958 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1959 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1960 *mods = (ADS_MODLIST)modlist;
1963 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1964 return ADS_ERROR(LDAP_NO_MEMORY);
1965 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1966 if (mod_op & LDAP_MOD_BVALUES) {
1967 modlist[curmod]->mod_bvalues = ber_values;
1968 } else if (mod_op & LDAP_MOD_DELETE) {
1969 modlist[curmod]->mod_values = NULL;
1970 } else {
1971 modlist[curmod]->mod_values = char_values;
1974 modlist[curmod]->mod_op = mod_op;
1975 return ADS_ERROR(LDAP_SUCCESS);
1979 * Add a single string value to a mod list
1980 * @param ctx An initialized TALLOC_CTX
1981 * @param mods An initialized ADS_MODLIST
1982 * @param name The attribute name to add
1983 * @param val The value to add - NULL means DELETE
1984 * @return ADS STATUS indicating success of add
1986 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1987 const char *name, const char *val)
1989 const char *values[2];
1991 values[0] = val;
1992 values[1] = NULL;
1994 if (!val)
1995 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1996 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
2000 * Add an array of string values to a mod list
2001 * @param ctx An initialized TALLOC_CTX
2002 * @param mods An initialized ADS_MODLIST
2003 * @param name The attribute name to add
2004 * @param vals The array of string values to add - NULL means DELETE
2005 * @return ADS STATUS indicating success of add
2007 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2008 const char *name, const char **vals)
2010 if (!vals)
2011 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
2012 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
2013 name, (const void **) vals);
2017 * Add a single ber-encoded value to a mod list
2018 * @param ctx An initialized TALLOC_CTX
2019 * @param mods An initialized ADS_MODLIST
2020 * @param name The attribute name to add
2021 * @param val The value to add - NULL means DELETE
2022 * @return ADS STATUS indicating success of add
2024 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2025 const char *name, const struct berval *val)
2027 const struct berval *values[2];
2029 values[0] = val;
2030 values[1] = NULL;
2031 if (!val)
2032 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
2033 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
2034 name, (const void *) values);
2037 static void ads_print_error(int ret, LDAP *ld)
2039 if (ret != 0) {
2040 char *ld_error = NULL;
2041 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
2042 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
2043 ret,
2044 ldap_err2string(ret),
2045 ld_error);
2046 SAFE_FREE(ld_error);
2051 * Perform an ldap modify
2052 * @param ads connection to ads server
2053 * @param mod_dn DistinguishedName to modify
2054 * @param mods list of modifications to perform
2055 * @return status of modify
2057 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
2059 int ret,i;
2060 char *utf8_dn = NULL;
2061 size_t converted_size;
2063 this control is needed to modify that contains a currently
2064 non-existent attribute (but allowable for the object) to run
2066 LDAPControl PermitModify = {
2067 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
2068 {0, NULL},
2069 (char) 1};
2070 LDAPControl *controls[2];
2072 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
2074 controls[0] = &PermitModify;
2075 controls[1] = NULL;
2077 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
2078 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2081 /* find the end of the list, marked by NULL or -1 */
2082 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2083 /* make sure the end of the list is NULL */
2084 mods[i] = NULL;
2085 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
2086 (LDAPMod **) mods, controls, NULL);
2087 ads_print_error(ret, ads->ldap.ld);
2088 TALLOC_FREE(utf8_dn);
2089 return ADS_ERROR(ret);
2093 * Perform an ldap add
2094 * @param ads connection to ads server
2095 * @param new_dn DistinguishedName to add
2096 * @param mods list of attributes and values for DN
2097 * @return status of add
2099 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
2101 int ret, i;
2102 char *utf8_dn = NULL;
2103 size_t converted_size;
2105 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
2107 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
2108 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2109 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2112 /* find the end of the list, marked by NULL or -1 */
2113 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2114 /* make sure the end of the list is NULL */
2115 mods[i] = NULL;
2117 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
2118 ads_print_error(ret, ads->ldap.ld);
2119 TALLOC_FREE(utf8_dn);
2120 return ADS_ERROR(ret);
2124 * Delete a DistinguishedName
2125 * @param ads connection to ads server
2126 * @param new_dn DistinguishedName to delete
2127 * @return status of delete
2129 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
2131 int ret;
2132 char *utf8_dn = NULL;
2133 size_t converted_size;
2134 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
2135 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2136 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2139 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
2141 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
2142 ads_print_error(ret, ads->ldap.ld);
2143 TALLOC_FREE(utf8_dn);
2144 return ADS_ERROR(ret);
2148 * Build an org unit string
2149 * if org unit is Computers or blank then assume a container, otherwise
2150 * assume a / separated list of organisational units.
2151 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2152 * @param ads connection to ads server
2153 * @param org_unit Organizational unit
2154 * @return org unit string - caller must free
2156 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
2158 ADS_STATUS status;
2159 char *ret = NULL;
2160 char *dn = NULL;
2162 if (!org_unit || !*org_unit) {
2164 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
2166 /* samba4 might not yet respond to a wellknownobject-query */
2167 return ret ? ret : SMB_STRDUP("cn=Computers");
2170 if (strequal(org_unit, "Computers")) {
2171 return SMB_STRDUP("cn=Computers");
2174 /* jmcd: removed "\\" from the separation chars, because it is
2175 needed as an escape for chars like '#' which are valid in an
2176 OU name */
2177 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
2178 if (!ADS_ERR_OK(status)) {
2179 return NULL;
2182 return dn;
2186 * Get a org unit string for a well-known GUID
2187 * @param ads connection to ads server
2188 * @param wknguid Well known GUID
2189 * @return org unit string - caller must free
2191 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2193 ADS_STATUS status;
2194 LDAPMessage *res = NULL;
2195 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2196 **bind_dn_exp = NULL;
2197 const char *attrs[] = {"distinguishedName", NULL};
2198 int new_ln, wkn_ln, bind_ln, i;
2200 if (wknguid == NULL) {
2201 return NULL;
2204 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2205 DEBUG(1, ("asprintf failed!\n"));
2206 return NULL;
2209 status = ads_search_dn(ads, &res, base, attrs);
2210 if (!ADS_ERR_OK(status)) {
2211 DEBUG(1,("Failed while searching for: %s\n", base));
2212 goto out;
2215 if (ads_count_replies(ads, res) != 1) {
2216 goto out;
2219 /* substitute the bind-path from the well-known-guid-search result */
2220 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2221 if (!wkn_dn) {
2222 goto out;
2225 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2226 if (!wkn_dn_exp) {
2227 goto out;
2230 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2231 if (!bind_dn_exp) {
2232 goto out;
2235 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2237 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2240 new_ln = wkn_ln - bind_ln;
2242 ret = SMB_STRDUP(wkn_dn_exp[0]);
2243 if (!ret) {
2244 goto out;
2247 for (i=1; i < new_ln; i++) {
2248 char *s = NULL;
2250 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2251 SAFE_FREE(ret);
2252 goto out;
2255 SAFE_FREE(ret);
2256 ret = SMB_STRDUP(s);
2257 free(s);
2258 if (!ret) {
2259 goto out;
2263 out:
2264 SAFE_FREE(base);
2265 ads_msgfree(ads, res);
2266 TALLOC_FREE(wkn_dn);
2267 if (wkn_dn_exp) {
2268 ldap_value_free(wkn_dn_exp);
2270 if (bind_dn_exp) {
2271 ldap_value_free(bind_dn_exp);
2274 return ret;
2278 * Adds (appends) an item to an attribute array, rather then
2279 * replacing the whole list
2280 * @param ctx An initialized TALLOC_CTX
2281 * @param mods An initialized ADS_MODLIST
2282 * @param name name of the ldap attribute to append to
2283 * @param vals an array of values to add
2284 * @return status of addition
2287 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2288 const char *name, const char **vals)
2290 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2291 (const void *) vals);
2295 * This clears out all registered spn's for a given hostname
2296 * @param ads An initialized ADS_STRUCT
2297 * @param machine_name the NetBIOS name of the computer.
2298 * @return 0 upon success, non-zero otherwise.
2301 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2303 TALLOC_CTX *ctx;
2304 LDAPMessage *res = NULL;
2305 ADS_MODLIST mods;
2306 const char *servicePrincipalName[1] = {NULL};
2307 ADS_STATUS ret;
2308 char *dn_string = NULL;
2310 ret = ads_find_machine_acct(ads, &res, machine_name);
2311 if (!ADS_ERR_OK(ret)) {
2312 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2313 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2314 ads_msgfree(ads, res);
2315 return ret;
2318 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2319 ctx = talloc_init("ads_clear_service_principal_names");
2320 if (!ctx) {
2321 ads_msgfree(ads, res);
2322 return ADS_ERROR(LDAP_NO_MEMORY);
2325 if (!(mods = ads_init_mods(ctx))) {
2326 talloc_destroy(ctx);
2327 ads_msgfree(ads, res);
2328 return ADS_ERROR(LDAP_NO_MEMORY);
2330 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2331 if (!ADS_ERR_OK(ret)) {
2332 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2333 ads_msgfree(ads, res);
2334 talloc_destroy(ctx);
2335 return ret;
2337 dn_string = ads_get_dn(ads, talloc_tos(), res);
2338 if (!dn_string) {
2339 talloc_destroy(ctx);
2340 ads_msgfree(ads, res);
2341 return ADS_ERROR(LDAP_NO_MEMORY);
2343 ret = ads_gen_mod(ads, dn_string, mods);
2344 TALLOC_FREE(dn_string);
2345 if (!ADS_ERR_OK(ret)) {
2346 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2347 machine_name));
2348 ads_msgfree(ads, res);
2349 talloc_destroy(ctx);
2350 return ret;
2353 ads_msgfree(ads, res);
2354 talloc_destroy(ctx);
2355 return ret;
2359 * @brief Search for an element in a string array.
2361 * @param[in] el_array The string array to search.
2363 * @param[in] num_el The number of elements in the string array.
2365 * @param[in] el The string to search.
2367 * @return True if found, false if not.
2369 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2371 size_t i;
2373 if (el_array == NULL || num_el == 0 || el == NULL) {
2374 return false;
2377 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2378 int cmp;
2380 cmp = strcasecmp_m(el_array[i], el);
2381 if (cmp == 0) {
2382 return true;
2386 return false;
2390 * @brief This gets the service principal names of an existing computer account.
2392 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2394 * @param[in] ads The ADS context to use.
2396 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2397 * identify the computer account.
2399 * @param[in] spn_array A pointer to store the array for SPNs.
2401 * @param[in] num_spns The number of principals stored in the array.
2403 * @return 0 on success, or a ADS error if a failure occurred.
2405 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2406 ADS_STRUCT *ads,
2407 const char *machine_name,
2408 char ***spn_array,
2409 size_t *num_spns)
2411 ADS_STATUS status;
2412 LDAPMessage *res = NULL;
2413 int count;
2415 status = ads_find_machine_acct(ads,
2416 &res,
2417 machine_name);
2418 if (!ADS_ERR_OK(status)) {
2419 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2420 machine_name));
2421 return status;
2424 count = ads_count_replies(ads, res);
2425 if (count != 1) {
2426 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2427 goto done;
2430 *spn_array = ads_pull_strings(ads,
2431 mem_ctx,
2432 res,
2433 "servicePrincipalName",
2434 num_spns);
2435 if (*spn_array == NULL) {
2436 DEBUG(1, ("Host account for %s does not have service principal "
2437 "names.\n",
2438 machine_name));
2439 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2440 goto done;
2443 done:
2444 ads_msgfree(ads, res);
2446 return status;
2450 * This adds a service principal name to an existing computer account
2451 * (found by hostname) in AD.
2452 * @param ads An initialized ADS_STRUCT
2453 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2454 * @param spns An array or strings for the service principals to add,
2455 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2456 * @return 0 upon success, or non-zero if a failure occurs
2459 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2460 const char *machine_name,
2461 const char **spns)
2463 ADS_STATUS ret;
2464 TALLOC_CTX *ctx;
2465 LDAPMessage *res = NULL;
2466 ADS_MODLIST mods;
2467 char *dn_string = NULL;
2468 const char **servicePrincipalName = spns;
2470 ret = ads_find_machine_acct(ads, &res, machine_name);
2471 if (!ADS_ERR_OK(ret)) {
2472 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2473 machine_name));
2474 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2475 ads_msgfree(ads, res);
2476 return ret;
2479 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2480 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2481 ads_msgfree(ads, res);
2482 return ADS_ERROR(LDAP_NO_MEMORY);
2485 DEBUG(5,("ads_add_service_principal_name: INFO: "
2486 "Adding %s to host %s\n",
2487 spns[0] ? "N/A" : spns[0], machine_name));
2490 DEBUG(5,("ads_add_service_principal_name: INFO: "
2491 "Adding %s to host %s\n",
2492 spns[1] ? "N/A" : spns[1], machine_name));
2494 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2495 ret = ADS_ERROR(LDAP_NO_MEMORY);
2496 goto out;
2499 ret = ads_add_strlist(ctx,
2500 &mods,
2501 "servicePrincipalName",
2502 servicePrincipalName);
2503 if (!ADS_ERR_OK(ret)) {
2504 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2505 goto out;
2508 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2509 ret = ADS_ERROR(LDAP_NO_MEMORY);
2510 goto out;
2513 ret = ads_gen_mod(ads, dn_string, mods);
2514 if (!ADS_ERR_OK(ret)) {
2515 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2516 goto out;
2519 out:
2520 TALLOC_FREE( ctx );
2521 ads_msgfree(ads, res);
2522 return ret;
2525 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2526 LDAPMessage *msg)
2528 uint32_t acct_ctrl = 0;
2529 bool ok;
2531 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2532 if (!ok) {
2533 return 0;
2536 return acct_ctrl;
2539 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2540 LDAPMessage *msg,
2541 const struct berval *machine_pw_val)
2543 ADS_MODLIST mods;
2544 ADS_STATUS ret;
2545 TALLOC_CTX *frame = talloc_stackframe();
2546 uint32_t acct_control;
2547 char *control_str = NULL;
2548 const char *attrs[] = {
2549 "objectSid",
2550 NULL
2552 LDAPMessage *res = NULL;
2553 char *dn = NULL;
2555 dn = ads_get_dn(ads, frame, msg);
2556 if (dn == NULL) {
2557 ret = ADS_ERROR(LDAP_NO_MEMORY);
2558 goto done;
2561 acct_control = ads_get_acct_ctrl(ads, msg);
2562 if (acct_control == 0) {
2563 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2564 goto done;
2568 * Changing the password, disables the account. So we need to change the
2569 * userAccountControl flags to enable it again.
2571 mods = ads_init_mods(frame);
2572 if (mods == NULL) {
2573 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2574 goto done;
2577 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2579 ret = ads_gen_mod(ads, dn, mods);
2580 if (!ADS_ERR_OK(ret)) {
2581 goto done;
2583 TALLOC_FREE(mods);
2586 * To activate the account, we need to disable and enable it.
2588 acct_control |= UF_ACCOUNTDISABLE;
2590 control_str = talloc_asprintf(frame, "%u", acct_control);
2591 if (control_str == NULL) {
2592 ret = ADS_ERROR(LDAP_NO_MEMORY);
2593 goto done;
2596 mods = ads_init_mods(frame);
2597 if (mods == NULL) {
2598 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2599 goto done;
2602 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2604 ret = ads_gen_mod(ads, dn, mods);
2605 if (!ADS_ERR_OK(ret)) {
2606 goto done;
2608 TALLOC_FREE(mods);
2609 TALLOC_FREE(control_str);
2612 * Enable the account again.
2614 acct_control &= ~UF_ACCOUNTDISABLE;
2616 control_str = talloc_asprintf(frame, "%u", acct_control);
2617 if (control_str == NULL) {
2618 ret = ADS_ERROR(LDAP_NO_MEMORY);
2619 goto done;
2622 mods = ads_init_mods(frame);
2623 if (mods == NULL) {
2624 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2625 goto done;
2628 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2630 ret = ads_gen_mod(ads, dn, mods);
2631 if (!ADS_ERR_OK(ret)) {
2632 goto done;
2634 TALLOC_FREE(mods);
2635 TALLOC_FREE(control_str);
2637 ret = ads_search_dn(ads, &res, dn, attrs);
2638 ads_msgfree(ads, res);
2640 done:
2641 talloc_free(frame);
2643 return ret;
2647 * adds a machine account to the ADS server
2648 * @param ads An initialized ADS_STRUCT
2649 * @param machine_name - the NetBIOS machine name of this account.
2650 * @param account_type A number indicating the type of account to create
2651 * @param org_unit The LDAP path in which to place this account
2652 * @return 0 upon success, or non-zero otherwise
2655 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2656 const char *machine_name,
2657 const char *machine_password,
2658 const char *org_unit,
2659 uint32_t etype_list,
2660 const char *dns_domain_name)
2662 ADS_STATUS ret;
2663 char *samAccountName = NULL;
2664 char *controlstr = NULL;
2665 TALLOC_CTX *ctx = NULL;
2666 ADS_MODLIST mods;
2667 char *machine_escaped = NULL;
2668 char *dns_hostname = NULL;
2669 char *new_dn = NULL;
2670 char *utf8_pw = NULL;
2671 size_t utf8_pw_len = 0;
2672 char *utf16_pw = NULL;
2673 size_t utf16_pw_len = 0;
2674 struct berval machine_pw_val;
2675 bool ok;
2676 const char **spn_array = NULL;
2677 size_t num_spns = 0;
2678 const char *spn_prefix[] = {
2679 "HOST",
2680 "RestrictedKrbHost",
2682 size_t i;
2683 LDAPMessage *res = NULL;
2684 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2686 ctx = talloc_init("ads_add_machine_acct");
2687 if (ctx == NULL) {
2688 return ADS_ERROR(LDAP_NO_MEMORY);
2691 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2692 if (machine_escaped == NULL) {
2693 ret = ADS_ERROR(LDAP_NO_MEMORY);
2694 goto done;
2697 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2698 if (utf8_pw == NULL) {
2699 ret = ADS_ERROR(LDAP_NO_MEMORY);
2700 goto done;
2702 utf8_pw_len = strlen(utf8_pw);
2704 ok = convert_string_talloc(ctx,
2705 CH_UTF8, CH_UTF16MUNGED,
2706 utf8_pw, utf8_pw_len,
2707 (void *)&utf16_pw, &utf16_pw_len);
2708 if (!ok) {
2709 ret = ADS_ERROR(LDAP_NO_MEMORY);
2710 goto done;
2713 machine_pw_val = (struct berval) {
2714 .bv_val = utf16_pw,
2715 .bv_len = utf16_pw_len,
2718 /* Check if the machine account already exists. */
2719 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2720 if (ADS_ERR_OK(ret)) {
2721 /* Change the machine account password */
2722 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2723 ads_msgfree(ads, res);
2725 goto done;
2727 ads_msgfree(ads, res);
2729 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2730 if (new_dn == NULL) {
2731 ret = ADS_ERROR(LDAP_NO_MEMORY);
2732 goto done;
2735 /* Create machine account */
2737 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2738 if (samAccountName == NULL) {
2739 ret = ADS_ERROR(LDAP_NO_MEMORY);
2740 goto done;
2743 dns_hostname = talloc_asprintf(ctx,
2744 "%s.%s",
2745 machine_name,
2746 dns_domain_name);
2747 if (dns_hostname == NULL) {
2748 ret = ADS_ERROR(LDAP_NO_MEMORY);
2749 goto done;
2752 /* Add dns_hostname SPNs */
2753 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2754 char *spn = talloc_asprintf(ctx,
2755 "%s/%s",
2756 spn_prefix[i],
2757 dns_hostname);
2758 if (spn == NULL) {
2759 ret = ADS_ERROR(LDAP_NO_MEMORY);
2760 goto done;
2763 ok = add_string_to_array(ctx,
2764 spn,
2765 &spn_array,
2766 &num_spns);
2767 if (!ok) {
2768 ret = ADS_ERROR(LDAP_NO_MEMORY);
2769 goto done;
2773 /* Add machine_name SPNs */
2774 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2775 char *spn = talloc_asprintf(ctx,
2776 "%s/%s",
2777 spn_prefix[i],
2778 machine_name);
2779 if (spn == NULL) {
2780 ret = ADS_ERROR(LDAP_NO_MEMORY);
2781 goto done;
2784 ok = add_string_to_array(ctx,
2785 spn,
2786 &spn_array,
2787 &num_spns);
2788 if (!ok) {
2789 ret = ADS_ERROR(LDAP_NO_MEMORY);
2790 goto done;
2794 /* Make sure to NULL terminate the array */
2795 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2796 if (spn_array == NULL) {
2797 ret = ADS_ERROR(LDAP_NO_MEMORY);
2798 goto done;
2800 spn_array[num_spns] = NULL;
2802 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2803 if (controlstr == NULL) {
2804 ret = ADS_ERROR(LDAP_NO_MEMORY);
2805 goto done;
2808 mods = ads_init_mods(ctx);
2809 if (mods == NULL) {
2810 ret = ADS_ERROR(LDAP_NO_MEMORY);
2811 goto done;
2814 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2815 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2816 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2817 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2818 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2819 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2821 ret = ads_gen_add(ads, new_dn, mods);
2823 done:
2824 SAFE_FREE(machine_escaped);
2825 talloc_destroy(ctx);
2827 return ret;
2831 * move a machine account to another OU on the ADS server
2832 * @param ads - An initialized ADS_STRUCT
2833 * @param machine_name - the NetBIOS machine name of this account.
2834 * @param org_unit - The LDAP path in which to place this account
2835 * @param moved - whether we moved the machine account (optional)
2836 * @return 0 upon success, or non-zero otherwise
2839 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2840 const char *org_unit, bool *moved)
2842 ADS_STATUS rc;
2843 int ldap_status;
2844 LDAPMessage *res = NULL;
2845 char *filter = NULL;
2846 char *computer_dn = NULL;
2847 char *parent_dn;
2848 char *computer_rdn = NULL;
2849 bool need_move = False;
2851 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2852 rc = ADS_ERROR(LDAP_NO_MEMORY);
2853 goto done;
2856 /* Find pre-existing machine */
2857 rc = ads_search(ads, &res, filter, NULL);
2858 if (!ADS_ERR_OK(rc)) {
2859 goto done;
2862 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2863 if (!computer_dn) {
2864 rc = ADS_ERROR(LDAP_NO_MEMORY);
2865 goto done;
2868 parent_dn = ads_parent_dn(computer_dn);
2869 if (strequal(parent_dn, org_unit)) {
2870 goto done;
2873 need_move = True;
2875 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2876 rc = ADS_ERROR(LDAP_NO_MEMORY);
2877 goto done;
2880 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2881 org_unit, 1, NULL, NULL);
2882 rc = ADS_ERROR(ldap_status);
2884 done:
2885 ads_msgfree(ads, res);
2886 SAFE_FREE(filter);
2887 TALLOC_FREE(computer_dn);
2888 SAFE_FREE(computer_rdn);
2890 if (!ADS_ERR_OK(rc)) {
2891 need_move = False;
2894 if (moved) {
2895 *moved = need_move;
2898 return rc;
2902 dump a binary result from ldap
2904 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2906 size_t i;
2907 for (i=0; values[i]; i++) {
2908 ber_len_t j;
2909 printf("%s: ", field);
2910 for (j=0; j<values[i]->bv_len; j++) {
2911 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2913 printf("\n");
2917 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2919 int i;
2920 for (i=0; values[i]; i++) {
2921 NTSTATUS status;
2922 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2923 struct GUID guid;
2925 status = GUID_from_ndr_blob(&in, &guid);
2926 if (NT_STATUS_IS_OK(status)) {
2927 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2928 } else {
2929 printf("%s: INVALID GUID\n", field);
2935 dump a sid result from ldap
2937 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2939 int i;
2940 for (i=0; values[i]; i++) {
2941 ssize_t ret;
2942 struct dom_sid sid;
2943 struct dom_sid_buf tmp;
2944 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2945 values[i]->bv_len, &sid);
2946 if (ret == -1) {
2947 return;
2949 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2954 dump ntSecurityDescriptor
2956 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2958 TALLOC_CTX *frame = talloc_stackframe();
2959 struct security_descriptor *psd;
2960 NTSTATUS status;
2962 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2963 values[0]->bv_len, &psd);
2964 if (!NT_STATUS_IS_OK(status)) {
2965 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2966 nt_errstr(status)));
2967 TALLOC_FREE(frame);
2968 return;
2971 if (psd) {
2972 ads_disp_sd(ads, talloc_tos(), psd);
2975 TALLOC_FREE(frame);
2979 dump a string result from ldap
2981 static void dump_string(const char *field, char **values)
2983 int i;
2984 for (i=0; values[i]; i++) {
2985 printf("%s: %s\n", field, values[i]);
2990 dump a field from LDAP on stdout
2991 used for debugging
2994 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2996 const struct {
2997 const char *name;
2998 bool string;
2999 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
3000 } handlers[] = {
3001 {"objectGUID", False, dump_guid},
3002 {"netbootGUID", False, dump_guid},
3003 {"nTSecurityDescriptor", False, dump_sd},
3004 {"dnsRecord", False, dump_binary},
3005 {"objectSid", False, dump_sid},
3006 {"securityIdentifier", False, dump_sid},
3007 {"tokenGroups", False, dump_sid},
3008 {"tokenGroupsNoGCAcceptable", False, dump_sid},
3009 {"tokengroupsGlobalandUniversal", False, dump_sid},
3010 {"mS-DS-CreatorSID", False, dump_sid},
3011 {"msExchMailboxGuid", False, dump_guid},
3012 {"msDS-TrustForestTrustInfo", False, dump_binary},
3013 {NULL, True, NULL}
3015 int i;
3017 if (!field) { /* must be end of an entry */
3018 printf("\n");
3019 return False;
3022 for (i=0; handlers[i].name; i++) {
3023 if (strcasecmp_m(handlers[i].name, field) == 0) {
3024 if (!values) /* first time, indicate string or not */
3025 return handlers[i].string;
3026 handlers[i].handler(ads, field, (struct berval **) values);
3027 break;
3030 if (!handlers[i].name) {
3031 if (!values) /* first time, indicate string conversion */
3032 return True;
3033 dump_string(field, (char **)values);
3035 return False;
3039 * Dump a result from LDAP on stdout
3040 * used for debugging
3041 * @param ads connection to ads server
3042 * @param res Results to dump
3045 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
3047 ads_process_results(ads, res, ads_dump_field, NULL);
3051 * Walk through results, calling a function for each entry found.
3052 * The function receives a field name, a berval * array of values,
3053 * and a data area passed through from the start. The function is
3054 * called once with null for field and values at the end of each
3055 * entry.
3056 * @param ads connection to ads server
3057 * @param res Results to process
3058 * @param fn Function for processing each result
3059 * @param data_area user-defined area to pass to function
3061 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
3062 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
3063 void *data_area)
3065 LDAPMessage *msg;
3066 TALLOC_CTX *ctx;
3067 size_t converted_size;
3069 if (!(ctx = talloc_init("ads_process_results")))
3070 return;
3072 for (msg = ads_first_entry(ads, res); msg;
3073 msg = ads_next_entry(ads, msg)) {
3074 char *utf8_field;
3075 BerElement *b;
3077 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
3078 (LDAPMessage *)msg,&b);
3079 utf8_field;
3080 utf8_field=ldap_next_attribute(ads->ldap.ld,
3081 (LDAPMessage *)msg,b)) {
3082 struct berval **ber_vals;
3083 char **str_vals;
3084 char **utf8_vals;
3085 char *field;
3086 bool string;
3088 if (!pull_utf8_talloc(ctx, &field, utf8_field,
3089 &converted_size))
3091 DEBUG(0,("ads_process_results: "
3092 "pull_utf8_talloc failed: %s\n",
3093 strerror(errno)));
3096 string = fn(ads, field, NULL, data_area);
3098 if (string) {
3099 const char **p;
3101 utf8_vals = ldap_get_values(ads->ldap.ld,
3102 (LDAPMessage *)msg, field);
3103 p = discard_const_p(const char *, utf8_vals);
3104 str_vals = ads_pull_strvals(ctx, p);
3105 fn(ads, field, (void **) str_vals, data_area);
3106 ldap_value_free(utf8_vals);
3107 } else {
3108 ber_vals = ldap_get_values_len(ads->ldap.ld,
3109 (LDAPMessage *)msg, field);
3110 fn(ads, field, (void **) ber_vals, data_area);
3112 ldap_value_free_len(ber_vals);
3114 ldap_memfree(utf8_field);
3116 ber_free(b, 0);
3117 talloc_free_children(ctx);
3118 fn(ads, NULL, NULL, data_area); /* completed an entry */
3121 talloc_destroy(ctx);
3125 * count how many replies are in a LDAPMessage
3126 * @param ads connection to ads server
3127 * @param res Results to count
3128 * @return number of replies
3130 int ads_count_replies(ADS_STRUCT *ads, void *res)
3132 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3136 * pull the first entry from a ADS result
3137 * @param ads connection to ads server
3138 * @param res Results of search
3139 * @return first entry from result
3141 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3143 return ldap_first_entry(ads->ldap.ld, res);
3147 * pull the next entry from a ADS result
3148 * @param ads connection to ads server
3149 * @param res Results of search
3150 * @return next entry from result
3152 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3154 return ldap_next_entry(ads->ldap.ld, res);
3158 * pull the first message from a ADS result
3159 * @param ads connection to ads server
3160 * @param res Results of search
3161 * @return first message from result
3163 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3165 return ldap_first_message(ads->ldap.ld, res);
3169 * pull the next message from a ADS result
3170 * @param ads connection to ads server
3171 * @param res Results of search
3172 * @return next message from result
3174 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3176 return ldap_next_message(ads->ldap.ld, res);
3180 * pull a single string from a ADS result
3181 * @param ads connection to ads server
3182 * @param mem_ctx TALLOC_CTX to use for allocating result string
3183 * @param msg Results of search
3184 * @param field Attribute to retrieve
3185 * @return Result string in talloc context
3187 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3188 const char *field)
3190 char **values;
3191 char *ret = NULL;
3192 char *ux_string;
3193 size_t converted_size;
3195 values = ldap_get_values(ads->ldap.ld, msg, field);
3196 if (!values)
3197 return NULL;
3199 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3200 &converted_size))
3202 ret = ux_string;
3204 ldap_value_free(values);
3205 return ret;
3209 * pull an array of strings from a ADS result
3210 * @param ads connection to ads server
3211 * @param mem_ctx TALLOC_CTX to use for allocating result string
3212 * @param msg Results of search
3213 * @param field Attribute to retrieve
3214 * @return Result strings in talloc context
3216 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3217 LDAPMessage *msg, const char *field,
3218 size_t *num_values)
3220 char **values;
3221 char **ret = NULL;
3222 size_t i, converted_size;
3224 values = ldap_get_values(ads->ldap.ld, msg, field);
3225 if (!values)
3226 return NULL;
3228 *num_values = ldap_count_values(values);
3230 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3231 if (!ret) {
3232 ldap_value_free(values);
3233 return NULL;
3236 for (i=0;i<*num_values;i++) {
3237 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3238 &converted_size))
3240 ldap_value_free(values);
3241 return NULL;
3244 ret[i] = NULL;
3246 ldap_value_free(values);
3247 return ret;
3251 * pull an array of strings from a ADS result
3252 * (handle large multivalue attributes with range retrieval)
3253 * @param ads connection to ads server
3254 * @param mem_ctx TALLOC_CTX to use for allocating result string
3255 * @param msg Results of search
3256 * @param field Attribute to retrieve
3257 * @param current_strings strings returned by a previous call to this function
3258 * @param next_attribute The next query should ask for this attribute
3259 * @param num_values How many values did we get this time?
3260 * @param more_values Are there more values to get?
3261 * @return Result strings in talloc context
3263 char **ads_pull_strings_range(ADS_STRUCT *ads,
3264 TALLOC_CTX *mem_ctx,
3265 LDAPMessage *msg, const char *field,
3266 char **current_strings,
3267 const char **next_attribute,
3268 size_t *num_strings,
3269 bool *more_strings)
3271 char *attr;
3272 char *expected_range_attrib, *range_attr = NULL;
3273 BerElement *ptr = NULL;
3274 char **strings;
3275 char **new_strings;
3276 size_t num_new_strings;
3277 unsigned long int range_start;
3278 unsigned long int range_end;
3280 /* we might have been given the whole lot anyway */
3281 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3282 *more_strings = False;
3283 return strings;
3286 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3288 /* look for Range result */
3289 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3290 attr;
3291 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3292 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3293 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3294 range_attr = attr;
3295 break;
3297 ldap_memfree(attr);
3299 if (!range_attr) {
3300 ber_free(ptr, 0);
3301 /* nothing here - this field is just empty */
3302 *more_strings = False;
3303 return NULL;
3306 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3307 &range_start, &range_end) == 2) {
3308 *more_strings = True;
3309 } else {
3310 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3311 &range_start) == 1) {
3312 *more_strings = False;
3313 } else {
3314 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3315 range_attr));
3316 ldap_memfree(range_attr);
3317 *more_strings = False;
3318 return NULL;
3322 if ((*num_strings) != range_start) {
3323 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3324 " - aborting range retrieval\n",
3325 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3326 ldap_memfree(range_attr);
3327 *more_strings = False;
3328 return NULL;
3331 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3333 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3334 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3335 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3336 range_attr, (unsigned long int)range_end - range_start + 1,
3337 (unsigned long int)num_new_strings));
3338 ldap_memfree(range_attr);
3339 *more_strings = False;
3340 return NULL;
3343 strings = talloc_realloc(mem_ctx, current_strings, char *,
3344 *num_strings + num_new_strings);
3346 if (strings == NULL) {
3347 ldap_memfree(range_attr);
3348 *more_strings = False;
3349 return NULL;
3352 if (new_strings && num_new_strings) {
3353 memcpy(&strings[*num_strings], new_strings,
3354 sizeof(*new_strings) * num_new_strings);
3357 (*num_strings) += num_new_strings;
3359 if (*more_strings) {
3360 *next_attribute = talloc_asprintf(mem_ctx,
3361 "%s;range=%d-*",
3362 field,
3363 (int)*num_strings);
3365 if (!*next_attribute) {
3366 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3367 ldap_memfree(range_attr);
3368 *more_strings = False;
3369 return NULL;
3373 ldap_memfree(range_attr);
3375 return strings;
3379 * pull a single uint32_t from a ADS result
3380 * @param ads connection to ads server
3381 * @param msg Results of search
3382 * @param field Attribute to retrieve
3383 * @param v Pointer to int to store result
3384 * @return boolean indicating success
3386 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3387 uint32_t *v)
3389 char **values;
3391 values = ldap_get_values(ads->ldap.ld, msg, field);
3392 if (!values)
3393 return False;
3394 if (!values[0]) {
3395 ldap_value_free(values);
3396 return False;
3399 *v = atoi(values[0]);
3400 ldap_value_free(values);
3401 return True;
3405 * pull a single objectGUID from an ADS result
3406 * @param ads connection to ADS server
3407 * @param msg results of search
3408 * @param guid 37-byte area to receive text guid
3409 * @return boolean indicating success
3411 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3413 DATA_BLOB blob;
3414 NTSTATUS status;
3416 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3417 &blob)) {
3418 return false;
3421 status = GUID_from_ndr_blob(&blob, guid);
3422 talloc_free(blob.data);
3423 return NT_STATUS_IS_OK(status);
3428 * pull a single struct dom_sid from a ADS result
3429 * @param ads connection to ads server
3430 * @param msg Results of search
3431 * @param field Attribute to retrieve
3432 * @param sid Pointer to sid to store result
3433 * @return boolean indicating success
3435 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3436 struct dom_sid *sid)
3438 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3442 * pull an array of struct dom_sids from a ADS result
3443 * @param ads connection to ads server
3444 * @param mem_ctx TALLOC_CTX for allocating sid array
3445 * @param msg Results of search
3446 * @param field Attribute to retrieve
3447 * @param sids pointer to sid array to allocate
3448 * @return the count of SIDs pulled
3450 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3451 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3453 struct berval **values;
3454 int count, i;
3456 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3458 if (!values)
3459 return 0;
3461 for (i=0; values[i]; i++)
3462 /* nop */ ;
3464 if (i) {
3465 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3466 if (!(*sids)) {
3467 ldap_value_free_len(values);
3468 return 0;
3470 } else {
3471 (*sids) = NULL;
3474 count = 0;
3475 for (i=0; values[i]; i++) {
3476 ssize_t ret;
3477 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3478 values[i]->bv_len, &(*sids)[count]);
3479 if (ret != -1) {
3480 struct dom_sid_buf buf;
3481 DBG_DEBUG("pulling SID: %s\n",
3482 dom_sid_str_buf(&(*sids)[count], &buf));
3483 count++;
3487 ldap_value_free_len(values);
3488 return count;
3492 * pull a struct security_descriptor from a ADS result
3493 * @param ads connection to ads server
3494 * @param mem_ctx TALLOC_CTX for allocating sid array
3495 * @param msg Results of search
3496 * @param field Attribute to retrieve
3497 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3498 * @return boolean indicating success
3500 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3501 LDAPMessage *msg, const char *field,
3502 struct security_descriptor **sd)
3504 struct berval **values;
3505 bool ret = true;
3507 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3509 if (!values) return false;
3511 if (values[0]) {
3512 NTSTATUS status;
3513 status = unmarshall_sec_desc(mem_ctx,
3514 (uint8_t *)values[0]->bv_val,
3515 values[0]->bv_len, sd);
3516 if (!NT_STATUS_IS_OK(status)) {
3517 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3518 nt_errstr(status)));
3519 ret = false;
3523 ldap_value_free_len(values);
3524 return ret;
3528 * in order to support usernames longer than 21 characters we need to
3529 * use both the sAMAccountName and the userPrincipalName attributes
3530 * It seems that not all users have the userPrincipalName attribute set
3532 * @param ads connection to ads server
3533 * @param mem_ctx TALLOC_CTX for allocating sid array
3534 * @param msg Results of search
3535 * @return the username
3537 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3538 LDAPMessage *msg)
3540 #if 0 /* JERRY */
3541 char *ret, *p;
3543 /* lookup_name() only works on the sAMAccountName to
3544 returning the username portion of userPrincipalName
3545 breaks winbindd_getpwnam() */
3547 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3548 if (ret && (p = strchr_m(ret, '@'))) {
3549 *p = 0;
3550 return ret;
3552 #endif
3553 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3558 * find the update serial number - this is the core of the ldap cache
3559 * @param ads connection to ads server
3560 * @param ads connection to ADS server
3561 * @param usn Pointer to retrieved update serial number
3562 * @return status of search
3564 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3566 const char *attrs[] = {"highestCommittedUSN", NULL};
3567 ADS_STATUS status;
3568 LDAPMessage *res;
3570 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3571 if (!ADS_ERR_OK(status))
3572 return status;
3574 if (ads_count_replies(ads, res) != 1) {
3575 ads_msgfree(ads, res);
3576 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3579 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3580 ads_msgfree(ads, res);
3581 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3584 ads_msgfree(ads, res);
3585 return ADS_SUCCESS;
3588 /* parse a ADS timestring - typical string is
3589 '20020917091222.0Z0' which means 09:12.22 17th September
3590 2002, timezone 0 */
3591 static time_t ads_parse_time(const char *str)
3593 struct tm tm;
3595 ZERO_STRUCT(tm);
3597 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3598 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3599 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3600 return 0;
3602 tm.tm_year -= 1900;
3603 tm.tm_mon -= 1;
3605 return timegm(&tm);
3608 /********************************************************************
3609 ********************************************************************/
3611 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3613 const char *attrs[] = {"currentTime", NULL};
3614 ADS_STATUS status;
3615 LDAPMessage *res;
3616 char *timestr;
3617 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3618 ADS_STRUCT *ads_s = ads;
3620 /* establish a new ldap tcp session if necessary */
3622 if ( !ads->ldap.ld ) {
3624 * ADS_STRUCT may be being reused after a
3625 * DC lookup, so ads->ldap.ss may already have a
3626 * good address. If not, re-initialize the passed-in
3627 * ADS_STRUCT with the given server.XXXX parameters.
3629 * Note that this doesn't depend on
3630 * ads->server.ldap_server != NULL,
3631 * as the case where ads->server.ldap_server==NULL and
3632 * ads->ldap.ss != zero_address is precisely the DC
3633 * lookup case where ads->ldap.ss was found by going
3634 * through ads_find_dc() again we want to avoid repeating.
3636 if (is_zero_addr(&ads->ldap.ss)) {
3637 ads_s = ads_init(tmp_ctx,
3638 ads->server.realm,
3639 ads->server.workgroup,
3640 ads->server.ldap_server,
3641 ADS_SASL_PLAIN );
3642 if (ads_s == NULL) {
3643 status = ADS_ERROR(LDAP_NO_MEMORY);
3644 goto done;
3649 * Reset ads->config.flags as it can contain the flags
3650 * returned by the previous CLDAP ping when reusing the struct.
3652 ads_s->config.flags = 0;
3654 status = ads_connect_simple_anon(ads_s);
3655 if ( !ADS_ERR_OK(status))
3656 goto done;
3659 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3660 if (!ADS_ERR_OK(status)) {
3661 goto done;
3664 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3665 if (!timestr) {
3666 ads_msgfree(ads_s, res);
3667 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3668 goto done;
3671 /* but save the time and offset in the original ADS_STRUCT */
3673 ads->config.current_time = ads_parse_time(timestr);
3675 if (ads->config.current_time != 0) {
3676 ads->config.time_offset = ads->config.current_time - time(NULL);
3677 DBG_INFO("server time offset is %d seconds\n",
3678 ads->config.time_offset);
3679 } else {
3680 ads->config.time_offset = 0;
3683 DBG_INFO("server time offset is %d seconds\n",
3684 ads->config.time_offset);
3686 ads_msgfree(ads, res);
3688 status = ADS_SUCCESS;
3690 done:
3691 TALLOC_FREE(tmp_ctx);
3693 return status;
3696 /********************************************************************
3697 ********************************************************************/
3699 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3701 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3702 const char *attrs[] = {"domainFunctionality", NULL};
3703 ADS_STATUS status;
3704 LDAPMessage *res;
3705 ADS_STRUCT *ads_s = ads;
3707 *val = DS_DOMAIN_FUNCTION_2000;
3709 /* establish a new ldap tcp session if necessary */
3711 if ( !ads->ldap.ld ) {
3713 * ADS_STRUCT may be being reused after a
3714 * DC lookup, so ads->ldap.ss may already have a
3715 * good address. If not, re-initialize the passed-in
3716 * ADS_STRUCT with the given server.XXXX parameters.
3718 * Note that this doesn't depend on
3719 * ads->server.ldap_server != NULL,
3720 * as the case where ads->server.ldap_server==NULL and
3721 * ads->ldap.ss != zero_address is precisely the DC
3722 * lookup case where ads->ldap.ss was found by going
3723 * through ads_find_dc() again we want to avoid repeating.
3725 if (is_zero_addr(&ads->ldap.ss)) {
3726 ads_s = ads_init(tmp_ctx,
3727 ads->server.realm,
3728 ads->server.workgroup,
3729 ads->server.ldap_server,
3730 ADS_SASL_PLAIN );
3731 if (ads_s == NULL ) {
3732 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3733 goto done;
3738 * Reset ads->config.flags as it can contain the flags
3739 * returned by the previous CLDAP ping when reusing the struct.
3741 ads_s->config.flags = 0;
3743 status = ads_connect_simple_anon(ads_s);
3744 if ( !ADS_ERR_OK(status))
3745 goto done;
3748 /* If the attribute does not exist assume it is a Windows 2000
3749 functional domain */
3751 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3752 if (!ADS_ERR_OK(status)) {
3753 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3754 status = ADS_SUCCESS;
3756 goto done;
3759 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3760 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3762 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3765 ads_msgfree(ads_s, res);
3767 done:
3768 TALLOC_FREE(tmp_ctx);
3770 return status;
3774 * find the domain sid for our domain
3775 * @param ads connection to ads server
3776 * @param sid Pointer to domain sid
3777 * @return status of search
3779 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3781 const char *attrs[] = {"objectSid", NULL};
3782 LDAPMessage *res;
3783 ADS_STATUS rc;
3785 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3786 attrs, &res);
3787 if (!ADS_ERR_OK(rc)) return rc;
3788 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3789 ads_msgfree(ads, res);
3790 return ADS_ERROR_SYSTEM(ENOENT);
3792 ads_msgfree(ads, res);
3794 return ADS_SUCCESS;
3798 * find our site name
3799 * @param ads connection to ads server
3800 * @param mem_ctx Pointer to talloc context
3801 * @param site_name Pointer to the sitename
3802 * @return status of search
3804 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3806 ADS_STATUS status;
3807 LDAPMessage *res;
3808 const char *dn, *service_name;
3809 const char *attrs[] = { "dsServiceName", NULL };
3811 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3812 if (!ADS_ERR_OK(status)) {
3813 return status;
3816 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3817 if (service_name == NULL) {
3818 ads_msgfree(ads, res);
3819 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3822 ads_msgfree(ads, res);
3824 /* go up three levels */
3825 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3826 if (dn == NULL) {
3827 return ADS_ERROR(LDAP_NO_MEMORY);
3830 *site_name = talloc_strdup(mem_ctx, dn);
3831 if (*site_name == NULL) {
3832 return ADS_ERROR(LDAP_NO_MEMORY);
3835 return status;
3837 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3842 * find the site dn where a machine resides
3843 * @param ads connection to ads server
3844 * @param mem_ctx Pointer to talloc context
3845 * @param computer_name name of the machine
3846 * @param site_name Pointer to the sitename
3847 * @return status of search
3849 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3851 ADS_STATUS status;
3852 LDAPMessage *res;
3853 const char *parent, *filter;
3854 char *config_context = NULL;
3855 char *dn;
3857 /* shortcut a query */
3858 if (strequal(computer_name, ads->config.ldap_server_name)) {
3859 return ads_site_dn(ads, mem_ctx, site_dn);
3862 status = ads_config_path(ads, mem_ctx, &config_context);
3863 if (!ADS_ERR_OK(status)) {
3864 return status;
3867 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3868 if (filter == NULL) {
3869 return ADS_ERROR(LDAP_NO_MEMORY);
3872 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3873 filter, NULL, &res);
3874 if (!ADS_ERR_OK(status)) {
3875 return status;
3878 if (ads_count_replies(ads, res) != 1) {
3879 ads_msgfree(ads, res);
3880 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3883 dn = ads_get_dn(ads, mem_ctx, res);
3884 if (dn == NULL) {
3885 ads_msgfree(ads, res);
3886 return ADS_ERROR(LDAP_NO_MEMORY);
3889 /* go up three levels */
3890 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3891 if (parent == NULL) {
3892 ads_msgfree(ads, res);
3893 TALLOC_FREE(dn);
3894 return ADS_ERROR(LDAP_NO_MEMORY);
3897 *site_dn = talloc_strdup(mem_ctx, parent);
3898 if (*site_dn == NULL) {
3899 ads_msgfree(ads, res);
3900 TALLOC_FREE(dn);
3901 return ADS_ERROR(LDAP_NO_MEMORY);
3904 TALLOC_FREE(dn);
3905 ads_msgfree(ads, res);
3907 return status;
3911 * get the upn suffixes for a domain
3912 * @param ads connection to ads server
3913 * @param mem_ctx Pointer to talloc context
3914 * @param suffixes Pointer to an array of suffixes
3915 * @param num_suffixes Pointer to the number of suffixes
3916 * @return status of search
3918 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3920 ADS_STATUS status;
3921 LDAPMessage *res;
3922 const char *base;
3923 char *config_context = NULL;
3924 const char *attrs[] = { "uPNSuffixes", NULL };
3926 status = ads_config_path(ads, mem_ctx, &config_context);
3927 if (!ADS_ERR_OK(status)) {
3928 return status;
3931 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3932 if (base == NULL) {
3933 return ADS_ERROR(LDAP_NO_MEMORY);
3936 status = ads_search_dn(ads, &res, base, attrs);
3937 if (!ADS_ERR_OK(status)) {
3938 return status;
3941 if (ads_count_replies(ads, res) != 1) {
3942 ads_msgfree(ads, res);
3943 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3946 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3947 if ((*suffixes) == NULL) {
3948 ads_msgfree(ads, res);
3949 return ADS_ERROR(LDAP_NO_MEMORY);
3952 ads_msgfree(ads, res);
3954 return status;
3958 * get the joinable ous for a domain
3959 * @param ads connection to ads server
3960 * @param mem_ctx Pointer to talloc context
3961 * @param ous Pointer to an array of ous
3962 * @param num_ous Pointer to the number of ous
3963 * @return status of search
3965 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3966 TALLOC_CTX *mem_ctx,
3967 char ***ous,
3968 size_t *num_ous)
3970 ADS_STATUS status;
3971 LDAPMessage *res = NULL;
3972 LDAPMessage *msg = NULL;
3973 const char *attrs[] = { "dn", NULL };
3974 int count = 0;
3976 status = ads_search(ads, &res,
3977 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3978 attrs);
3979 if (!ADS_ERR_OK(status)) {
3980 return status;
3983 count = ads_count_replies(ads, res);
3984 if (count < 1) {
3985 ads_msgfree(ads, res);
3986 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3989 for (msg = ads_first_entry(ads, res); msg;
3990 msg = ads_next_entry(ads, msg)) {
3991 const char **p = discard_const_p(const char *, *ous);
3992 char *dn = NULL;
3994 dn = ads_get_dn(ads, talloc_tos(), msg);
3995 if (!dn) {
3996 ads_msgfree(ads, res);
3997 return ADS_ERROR(LDAP_NO_MEMORY);
4000 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
4001 TALLOC_FREE(dn);
4002 ads_msgfree(ads, res);
4003 return ADS_ERROR(LDAP_NO_MEMORY);
4006 TALLOC_FREE(dn);
4007 *ous = discard_const_p(char *, p);
4010 ads_msgfree(ads, res);
4012 return status;
4017 * pull a struct dom_sid from an extended dn string
4018 * @param mem_ctx TALLOC_CTX
4019 * @param extended_dn string
4020 * @param flags string type of extended_dn
4021 * @param sid pointer to a struct dom_sid
4022 * @return NT_STATUS_OK on success,
4023 * NT_INVALID_PARAMETER on error,
4024 * NT_STATUS_NOT_FOUND if no SID present
4026 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
4027 const char *extended_dn,
4028 enum ads_extended_dn_flags flags,
4029 struct dom_sid *sid)
4031 char *p, *q, *dn;
4033 if (!extended_dn) {
4034 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4037 /* otherwise extended_dn gets stripped off */
4038 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
4039 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4042 * ADS_EXTENDED_DN_HEX_STRING:
4043 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4045 * ADS_EXTENDED_DN_STRING (only with w2k3):
4046 * <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
4048 * Object with no SID, such as an Exchange Public Folder
4049 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4052 p = strchr(dn, ';');
4053 if (!p) {
4054 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4057 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
4058 DEBUG(5,("No SID present in extended dn\n"));
4059 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
4062 p += strlen(";<SID=");
4064 q = strchr(p, '>');
4065 if (!q) {
4066 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4069 *q = '\0';
4071 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
4073 switch (flags) {
4075 case ADS_EXTENDED_DN_STRING:
4076 if (!string_to_sid(sid, p)) {
4077 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4079 break;
4080 case ADS_EXTENDED_DN_HEX_STRING: {
4081 ssize_t ret;
4082 fstring buf;
4083 size_t buf_len;
4085 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
4086 if (buf_len == 0) {
4087 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4090 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
4091 if (ret == -1) {
4092 DEBUG(10,("failed to parse sid\n"));
4093 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4095 break;
4097 default:
4098 DEBUG(10,("unknown extended dn format\n"));
4099 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4102 return ADS_ERROR_NT(NT_STATUS_OK);
4105 /********************************************************************
4106 ********************************************************************/
4108 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4110 LDAPMessage *res = NULL;
4111 ADS_STATUS status;
4112 int count = 0;
4113 char *name = NULL;
4115 status = ads_find_machine_acct(ads, &res, machine_name);
4116 if (!ADS_ERR_OK(status)) {
4117 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4118 lp_netbios_name()));
4119 goto out;
4122 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4123 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4124 goto out;
4127 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4128 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4131 out:
4132 ads_msgfree(ads, res);
4134 return name;
4137 #if 0
4139 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4142 * Join a machine to a realm
4143 * Creates the machine account and sets the machine password
4144 * @param ads connection to ads server
4145 * @param machine name of host to add
4146 * @param org_unit Organizational unit to place machine in
4147 * @return status of join
4149 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4150 uint32_t account_type, const char *org_unit)
4152 ADS_STATUS status;
4153 LDAPMessage *res = NULL;
4154 char *machine;
4156 /* machine name must be lowercase */
4157 machine = SMB_STRDUP(machine_name);
4158 strlower_m(machine);
4161 status = ads_find_machine_acct(ads, (void **)&res, machine);
4162 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4163 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4164 status = ads_leave_realm(ads, machine);
4165 if (!ADS_ERR_OK(status)) {
4166 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4167 machine, ads->config.realm));
4168 return status;
4172 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4173 if (!ADS_ERR_OK(status)) {
4174 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4175 SAFE_FREE(machine);
4176 return status;
4179 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4180 if (!ADS_ERR_OK(status)) {
4181 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4182 SAFE_FREE(machine);
4183 return status;
4186 SAFE_FREE(machine);
4187 ads_msgfree(ads, res);
4189 return status;
4191 #endif
4194 * Delete a machine from the realm
4195 * @param ads connection to ads server
4196 * @param hostname Machine to remove
4197 * @return status of delete
4199 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4201 ADS_STATUS status;
4202 void *msg;
4203 LDAPMessage *res;
4204 char *hostnameDN, *host;
4205 int rc;
4206 LDAPControl ldap_control;
4207 LDAPControl * pldap_control[2] = {NULL, NULL};
4209 pldap_control[0] = &ldap_control;
4210 memset(&ldap_control, 0, sizeof(LDAPControl));
4211 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4213 /* hostname must be lowercase */
4214 host = SMB_STRDUP(hostname);
4215 if (!strlower_m(host)) {
4216 SAFE_FREE(host);
4217 return ADS_ERROR_SYSTEM(EINVAL);
4220 status = ads_find_machine_acct(ads, &res, host);
4221 if (!ADS_ERR_OK(status)) {
4222 DEBUG(0, ("Host account for %s does not exist.\n", host));
4223 SAFE_FREE(host);
4224 return status;
4227 msg = ads_first_entry(ads, res);
4228 if (!msg) {
4229 SAFE_FREE(host);
4230 return ADS_ERROR_SYSTEM(ENOENT);
4233 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4234 if (hostnameDN == NULL) {
4235 SAFE_FREE(host);
4236 return ADS_ERROR_SYSTEM(ENOENT);
4239 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4240 if (rc) {
4241 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4242 }else {
4243 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4246 if (rc != LDAP_SUCCESS) {
4247 const char *attrs[] = { "cn", NULL };
4248 LDAPMessage *msg_sub;
4250 /* we only search with scope ONE, we do not expect any further
4251 * objects to be created deeper */
4253 status = ads_do_search_retry(ads, hostnameDN,
4254 LDAP_SCOPE_ONELEVEL,
4255 "(objectclass=*)", attrs, &res);
4257 if (!ADS_ERR_OK(status)) {
4258 SAFE_FREE(host);
4259 TALLOC_FREE(hostnameDN);
4260 return status;
4263 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4264 msg_sub = ads_next_entry(ads, msg_sub)) {
4266 char *dn = NULL;
4268 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4269 SAFE_FREE(host);
4270 TALLOC_FREE(hostnameDN);
4271 return ADS_ERROR(LDAP_NO_MEMORY);
4274 status = ads_del_dn(ads, dn);
4275 if (!ADS_ERR_OK(status)) {
4276 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4277 SAFE_FREE(host);
4278 TALLOC_FREE(dn);
4279 TALLOC_FREE(hostnameDN);
4280 return status;
4283 TALLOC_FREE(dn);
4286 /* there should be no subordinate objects anymore */
4287 status = ads_do_search_retry(ads, hostnameDN,
4288 LDAP_SCOPE_ONELEVEL,
4289 "(objectclass=*)", attrs, &res);
4291 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4292 SAFE_FREE(host);
4293 TALLOC_FREE(hostnameDN);
4294 return status;
4297 /* delete hostnameDN now */
4298 status = ads_del_dn(ads, hostnameDN);
4299 if (!ADS_ERR_OK(status)) {
4300 SAFE_FREE(host);
4301 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4302 TALLOC_FREE(hostnameDN);
4303 return status;
4307 TALLOC_FREE(hostnameDN);
4309 status = ads_find_machine_acct(ads, &res, host);
4310 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4311 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4312 DEBUG(3, ("Failed to remove host account.\n"));
4313 SAFE_FREE(host);
4314 return status;
4317 SAFE_FREE(host);
4318 return ADS_SUCCESS;
4322 * pull all token-sids from an LDAP dn
4323 * @param ads connection to ads server
4324 * @param mem_ctx TALLOC_CTX for allocating sid array
4325 * @param dn of LDAP object
4326 * @param user_sid pointer to struct dom_sid (objectSid)
4327 * @param primary_group_sid pointer to struct dom_sid (self composed)
4328 * @param sids pointer to sid array to allocate
4329 * @param num_sids counter of SIDs pulled
4330 * @return status of token query
4332 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4333 TALLOC_CTX *mem_ctx,
4334 const char *dn,
4335 struct dom_sid *user_sid,
4336 struct dom_sid *primary_group_sid,
4337 struct dom_sid **sids,
4338 size_t *num_sids)
4340 ADS_STATUS status;
4341 LDAPMessage *res = NULL;
4342 int count = 0;
4343 size_t tmp_num_sids;
4344 struct dom_sid *tmp_sids;
4345 struct dom_sid tmp_user_sid;
4346 struct dom_sid tmp_primary_group_sid;
4347 uint32_t pgid;
4348 const char *attrs[] = {
4349 "objectSid",
4350 "tokenGroups",
4351 "primaryGroupID",
4352 NULL
4355 status = ads_search_retry_dn(ads, &res, dn, attrs);
4356 if (!ADS_ERR_OK(status)) {
4357 return status;
4360 count = ads_count_replies(ads, res);
4361 if (count != 1) {
4362 ads_msgfree(ads, res);
4363 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4366 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4367 ads_msgfree(ads, res);
4368 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4371 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4372 ads_msgfree(ads, res);
4373 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4377 /* hack to compose the primary group sid without knowing the
4378 * domsid */
4380 struct dom_sid domsid;
4382 sid_copy(&domsid, &tmp_user_sid);
4384 if (!sid_split_rid(&domsid, NULL)) {
4385 ads_msgfree(ads, res);
4386 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4389 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4390 ads_msgfree(ads, res);
4391 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4395 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4397 if (tmp_num_sids == 0 || !tmp_sids) {
4398 ads_msgfree(ads, res);
4399 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4402 if (num_sids) {
4403 *num_sids = tmp_num_sids;
4406 if (sids) {
4407 *sids = tmp_sids;
4410 if (user_sid) {
4411 *user_sid = tmp_user_sid;
4414 if (primary_group_sid) {
4415 *primary_group_sid = tmp_primary_group_sid;
4418 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4420 ads_msgfree(ads, res);
4421 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4425 * Find a sAMAccountName in LDAP
4426 * @param ads connection to ads server
4427 * @param mem_ctx TALLOC_CTX for allocating sid array
4428 * @param samaccountname to search
4429 * @param uac_ret uint32_t pointer userAccountControl attribute value
4430 * @param dn_ret pointer to dn
4431 * @return status of token query
4433 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4434 TALLOC_CTX *mem_ctx,
4435 const char *samaccountname,
4436 uint32_t *uac_ret,
4437 const char **dn_ret)
4439 ADS_STATUS status;
4440 const char *attrs[] = { "userAccountControl", NULL };
4441 const char *filter;
4442 LDAPMessage *res = NULL;
4443 char *dn = NULL;
4444 uint32_t uac = 0;
4446 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4447 samaccountname);
4448 if (filter == NULL) {
4449 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4450 goto out;
4453 status = ads_do_search_all(ads, ads->config.bind_path,
4454 LDAP_SCOPE_SUBTREE,
4455 filter, attrs, &res);
4457 if (!ADS_ERR_OK(status)) {
4458 goto out;
4461 if (ads_count_replies(ads, res) != 1) {
4462 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4463 goto out;
4466 dn = ads_get_dn(ads, talloc_tos(), res);
4467 if (dn == NULL) {
4468 status = ADS_ERROR(LDAP_NO_MEMORY);
4469 goto out;
4472 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4473 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4474 goto out;
4477 if (uac_ret) {
4478 *uac_ret = uac;
4481 if (dn_ret) {
4482 *dn_ret = talloc_strdup(mem_ctx, dn);
4483 if (!*dn_ret) {
4484 status = ADS_ERROR(LDAP_NO_MEMORY);
4485 goto out;
4488 out:
4489 TALLOC_FREE(dn);
4490 ads_msgfree(ads, res);
4492 return status;
4496 * find our configuration path
4497 * @param ads connection to ads server
4498 * @param mem_ctx Pointer to talloc context
4499 * @param config_path Pointer to the config path
4500 * @return status of search
4502 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4503 TALLOC_CTX *mem_ctx,
4504 char **config_path)
4506 ADS_STATUS status;
4507 LDAPMessage *res = NULL;
4508 const char *config_context = NULL;
4509 const char *attrs[] = { "configurationNamingContext", NULL };
4511 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4512 "(objectclass=*)", attrs, &res);
4513 if (!ADS_ERR_OK(status)) {
4514 return status;
4517 config_context = ads_pull_string(ads, mem_ctx, res,
4518 "configurationNamingContext");
4519 ads_msgfree(ads, res);
4520 if (!config_context) {
4521 return ADS_ERROR(LDAP_NO_MEMORY);
4524 if (config_path) {
4525 *config_path = talloc_strdup(mem_ctx, config_context);
4526 if (!*config_path) {
4527 return ADS_ERROR(LDAP_NO_MEMORY);
4531 return ADS_ERROR(LDAP_SUCCESS);
4535 * find the displayName of an extended right
4536 * @param ads connection to ads server
4537 * @param config_path The config path
4538 * @param mem_ctx Pointer to talloc context
4539 * @param GUID struct of the rightsGUID
4540 * @return status of search
4542 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4543 const char *config_path,
4544 TALLOC_CTX *mem_ctx,
4545 const struct GUID *rights_guid)
4547 ADS_STATUS rc;
4548 LDAPMessage *res = NULL;
4549 char *expr = NULL;
4550 const char *attrs[] = { "displayName", NULL };
4551 const char *result = NULL;
4552 const char *path;
4554 if (!ads || !mem_ctx || !rights_guid) {
4555 goto done;
4558 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4559 GUID_string(mem_ctx, rights_guid));
4560 if (!expr) {
4561 goto done;
4564 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4565 if (!path) {
4566 goto done;
4569 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4570 expr, attrs, &res);
4571 if (!ADS_ERR_OK(rc)) {
4572 goto done;
4575 if (ads_count_replies(ads, res) != 1) {
4576 goto done;
4579 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4581 done:
4582 ads_msgfree(ads, res);
4583 return result;
4587 * verify or build and verify an account ou
4588 * @param mem_ctx Pointer to talloc context
4589 * @param ads connection to ads server
4590 * @param account_ou
4591 * @return status of search
4594 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4595 ADS_STRUCT *ads,
4596 const char **account_ou)
4598 char **exploded_dn;
4599 const char *name;
4600 char *ou_string;
4602 if (account_ou == NULL) {
4603 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4606 if (*account_ou != NULL) {
4607 exploded_dn = ldap_explode_dn(*account_ou, 0);
4608 if (exploded_dn) {
4609 ldap_value_free(exploded_dn);
4610 return ADS_SUCCESS;
4614 ou_string = ads_ou_string(ads, *account_ou);
4615 if (!ou_string) {
4616 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4619 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4620 ads->config.bind_path);
4621 SAFE_FREE(ou_string);
4623 if (!name) {
4624 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4627 exploded_dn = ldap_explode_dn(name, 0);
4628 if (!exploded_dn) {
4629 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4631 ldap_value_free(exploded_dn);
4633 *account_ou = name;
4634 return ADS_SUCCESS;
4637 #endif