ctdb-tests: Update statd-callout tests to handle both modes
[samba4-gss.git] / source3 / libads / ldap.c
blob48c5b263ff922a1b0385bb6fc821c5789b246b9b
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 /* Fill in the ads->config values */
279 ADS_TALLOC_CONST_FREE(ads->config.workgroup);
280 ADS_TALLOC_CONST_FREE(ads->config.realm);
281 ADS_TALLOC_CONST_FREE(ads->config.bind_path);
282 ADS_TALLOC_CONST_FREE(ads->config.ldap_server_name);
283 ADS_TALLOC_CONST_FREE(ads->config.server_site_name);
284 ADS_TALLOC_CONST_FREE(ads->config.client_site_name);
286 ads->config.ldap_server_name = talloc_strdup(ads,
287 cldap_reply->pdc_dns_name);
288 if (ads->config.ldap_server_name == NULL) {
289 DBG_WARNING("Out of memory\n");
290 ret = false;
291 goto out;
294 ads->config.workgroup = talloc_strdup(ads, cldap_reply->domain_name);
295 if (ads->config.workgroup == NULL) {
296 DBG_WARNING("Out of memory\n");
297 ret = false;
298 goto out;
301 ads->config.realm = talloc_asprintf_strupper_m(ads,
302 "%s",
303 cldap_reply->dns_domain);
304 if (ads->config.realm == NULL) {
305 DBG_WARNING("Out of memory\n");
306 ret = false;
307 goto out;
310 status = ads_build_dn(ads->config.realm, ads, &dn);
311 if (!ADS_ERR_OK(status)) {
312 DBG_DEBUG("Failed to build bind path: %s\n",
313 ads_errstr(status));
314 ret = false;
315 goto out;
317 ads->config.bind_path = dn;
319 if (*cldap_reply->server_site) {
320 ads->config.server_site_name =
321 talloc_strdup(ads, cldap_reply->server_site);
322 if (ads->config.server_site_name == NULL) {
323 DBG_WARNING("Out of memory\n");
324 ret = false;
325 goto out;
329 if (*cldap_reply->client_site) {
330 ads->config.client_site_name =
331 talloc_strdup(ads, cldap_reply->client_site);
332 if (ads->config.client_site_name == NULL) {
333 DBG_WARNING("Out of memory\n");
334 ret = false;
335 goto out;
339 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
340 ads->ldap.ss = *ss;
342 /* Store our site name. */
343 sitename_store(cldap_reply->domain_name, cldap_reply->client_site);
344 sitename_store(cldap_reply->dns_domain, cldap_reply->client_site);
346 /* Leave this until last so that the flags are not clobbered */
347 ads->config.flags = cldap_reply->server_type;
349 ret = true;
351 out:
353 TALLOC_FREE(frame);
354 return ret;
358 try a connection to a given ldap server, returning True and setting the servers IP
359 in the ads struct if successful
361 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
362 struct sockaddr_storage *ss)
364 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply = {};
365 TALLOC_CTX *frame = talloc_stackframe();
366 bool ok;
367 char addr[INET6_ADDRSTRLEN] = { 0, };
369 if (ss == NULL) {
370 TALLOC_FREE(frame);
371 return false;
374 print_sockaddr(addr, sizeof(addr), ss);
376 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
377 addr, ads->server.realm);
379 ok = ads_cldap_netlogon_5(frame,
381 ads->server.realm,
382 ads->config.flags | DS_ONLY_LDAP_NEEDED,
383 &cldap_reply);
384 if (!ok) {
385 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
386 addr, ads->server.realm);
387 TALLOC_FREE(frame);
388 return false;
391 ok = ads_fill_cldap_reply(ads, gc, ss, &cldap_reply);
392 if (!ok) {
393 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
394 addr, ads->server.realm);
395 TALLOC_FREE(frame);
396 return false;
399 TALLOC_FREE(frame);
400 return true;
403 /**********************************************************************
404 send a cldap ping to list of servers, one at a time, until one of
405 them answers it's an ldap server. Record success in the ADS_STRUCT.
406 Take note of and update negative connection cache.
407 **********************************************************************/
409 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
410 const char *domain,
411 struct samba_sockaddr *sa_list,
412 size_t count)
414 TALLOC_CTX *frame = talloc_stackframe();
415 struct timeval endtime = timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
416 uint32_t nt_version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
417 struct tsocket_address **ts_list = NULL;
418 struct samba_sockaddr **req_sa_list = NULL;
419 struct netlogon_samlogon_response **responses = NULL;
420 size_t num_requests = 0;
421 NTSTATUS status;
422 size_t i;
423 bool ok = false;
424 bool retry;
426 ts_list = talloc_zero_array(frame,
427 struct tsocket_address *,
428 count);
429 if (ts_list == NULL) {
430 TALLOC_FREE(frame);
431 return NT_STATUS_NO_MEMORY;
434 req_sa_list = talloc_zero_array(frame,
435 struct samba_sockaddr *,
436 count);
437 if (req_sa_list == NULL) {
438 TALLOC_FREE(frame);
439 return NT_STATUS_NO_MEMORY;
442 again:
444 * The retry loop is bound by the timeout
446 retry = false;
447 num_requests = 0;
449 for (i = 0; i < count; i++) {
450 char server[INET6_ADDRSTRLEN];
451 int ret;
453 if (is_zero_addr(&sa_list[i].u.ss)) {
454 continue;
457 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
459 status = check_negative_conn_cache(domain, server);
460 if (!NT_STATUS_IS_OK(status)) {
461 continue;
464 ret = tsocket_address_inet_from_strings(ts_list, "ip",
465 server, LDAP_PORT,
466 &ts_list[num_requests]);
467 if (ret != 0) {
468 status = map_nt_error_from_unix(errno);
469 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
470 server, nt_errstr(status));
471 TALLOC_FREE(frame);
472 return status;
475 req_sa_list[num_requests] = &sa_list[i];
476 num_requests += 1;
479 DBG_DEBUG("Try to create %zu netlogon connections for domain '%s' "
480 "(provided count of addresses was %zu).\n",
481 num_requests,
482 domain,
483 count);
485 if (num_requests == 0) {
486 status = NT_STATUS_NO_LOGON_SERVERS;
487 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
488 domain, num_requests, count, nt_errstr(status));
489 TALLOC_FREE(frame);
490 return status;
493 status = netlogon_pings(frame, /* mem_ctx */
494 lp_client_netlogon_ping_protocol(), /* proto */
495 ts_list, /* servers */
496 num_requests, /* num_servers */
497 (struct netlogon_ping_filter){
498 .ntversion = nt_version,
499 .domain = ads->server.realm,
500 .acct_ctrl = -1,
501 .required_flags = ads->config.flags |
502 DS_ONLY_LDAP_NEEDED,
504 1, /* min_servers */
505 endtime, /* timeout */
506 &responses);
507 if (!NT_STATUS_IS_OK(status)) {
508 DBG_WARNING("netlogon_pings(realm=%s, num_requests=%zu) "
509 "for count[%zu] - %s\n",
510 ads->server.realm,
511 num_requests, count,
512 nt_errstr(status));
513 TALLOC_FREE(frame);
514 return NT_STATUS_NO_LOGON_SERVERS;
517 for (i = 0; i < num_requests; i++) {
518 struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply = NULL;
519 char server[INET6_ADDRSTRLEN];
521 if (responses[i] == NULL) {
522 continue;
525 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
527 if (responses[i]->ntver != NETLOGON_NT_VERSION_5EX) {
528 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
529 ads->server.realm,
530 responses[i]->ntver, server);
531 continue;
534 cldap_reply = &responses[i]->data.nt5_ex;
536 /* Returns ok only if it matches the correct server type */
537 ok = ads_fill_cldap_reply(ads,
538 false,
539 &req_sa_list[i]->u.ss,
540 cldap_reply);
541 if (ok) {
542 DBG_DEBUG("realm[%s]: selected %s => %s\n",
543 ads->server.realm,
544 server, cldap_reply->pdc_dns_name);
545 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
546 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
547 cldap_reply);
549 TALLOC_FREE(frame);
550 return NT_STATUS_OK;
553 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
554 ads->server.realm,
555 server, cldap_reply->pdc_dns_name);
556 if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
557 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
558 cldap_reply);
560 add_failed_connection_entry(domain, server,
561 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID);
562 retry = true;
565 if (retry) {
566 bool expired;
568 expired = timeval_expired(&endtime);
569 if (!expired) {
570 goto again;
574 /* keep track of failures as all were not suitable */
575 for (i = 0; i < num_requests; i++) {
576 char server[INET6_ADDRSTRLEN];
578 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
580 add_failed_connection_entry(domain, server,
581 NT_STATUS_UNSUCCESSFUL);
584 status = NT_STATUS_NO_LOGON_SERVERS;
585 DBG_WARNING("realm[%s] no valid response "
586 "num_requests[%zu] for count[%zu] - %s\n",
587 ads->server.realm,
588 num_requests, count, nt_errstr(status));
589 TALLOC_FREE(frame);
590 return NT_STATUS_NO_LOGON_SERVERS;
593 /***************************************************************************
594 resolve a name and perform an "ldap ping" using NetBIOS and related methods
595 ****************************************************************************/
597 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
598 const char *domain, const char *realm)
600 size_t i;
601 size_t count = 0;
602 struct samba_sockaddr *sa_list = NULL;
603 NTSTATUS status;
605 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
606 domain));
608 status = get_sorted_dc_list(talloc_tos(),
609 domain,
610 NULL,
611 &sa_list,
612 &count,
613 false);
614 if (!NT_STATUS_IS_OK(status)) {
615 return status;
618 /* remove servers which are known to be dead based on
619 the corresponding DNS method */
620 if (*realm) {
621 for (i = 0; i < count; ++i) {
622 char server[INET6_ADDRSTRLEN];
624 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
626 if(!NT_STATUS_IS_OK(
627 check_negative_conn_cache(realm, server))) {
628 /* Ensure we add the workgroup name for this
629 IP address as negative too. */
630 add_failed_connection_entry(
631 domain, server,
632 NT_STATUS_UNSUCCESSFUL);
637 status = cldap_ping_list(ads, domain, sa_list, count);
639 TALLOC_FREE(sa_list);
641 return status;
645 /**********************************************************************
646 resolve a name and perform an "ldap ping" using DNS
647 **********************************************************************/
649 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
650 const char *realm)
652 size_t count = 0;
653 struct samba_sockaddr *sa_list = NULL;
654 NTSTATUS status;
656 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
657 realm));
659 status = get_sorted_dc_list(talloc_tos(),
660 realm,
661 sitename,
662 &sa_list,
663 &count,
664 true);
665 if (!NT_STATUS_IS_OK(status)) {
666 TALLOC_FREE(sa_list);
667 return status;
670 status = cldap_ping_list(ads, realm, sa_list, count);
672 TALLOC_FREE(sa_list);
674 return status;
677 /**********************************************************************
678 Try to find an AD dc using our internal name resolution routines
679 Try the realm first and then the workgroup name if netbios is not
680 disabled
681 **********************************************************************/
683 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
685 const char *c_domain = "";
686 const char *c_realm;
687 bool use_own_domain = False;
688 char *sitename = NULL;
689 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
690 bool ok = false;
692 /* if the realm and workgroup are both empty, assume they are ours */
694 /* realm */
695 c_realm = ads->server.realm;
697 if (c_realm == NULL)
698 c_realm = "";
700 if (!*c_realm) {
701 /* special case where no realm and no workgroup means our own */
702 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
703 use_own_domain = True;
704 c_realm = lp_realm();
708 if (!lp_disable_netbios()) {
709 if (use_own_domain) {
710 c_domain = lp_workgroup();
711 } else {
712 c_domain = ads->server.workgroup;
713 if (!*c_realm && (!c_domain || !*c_domain)) {
714 c_domain = lp_workgroup();
718 if (!c_domain) {
719 c_domain = "";
723 if (!*c_realm && !*c_domain) {
724 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
725 "what to do\n"));
726 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
730 * In case of LDAP we use get_dc_name() as that
731 * creates the custom krb5.conf file
733 if (ads->auth.flags & ADS_AUTH_GENERATE_KRB5_CONFIG) {
734 fstring srv_name;
735 struct sockaddr_storage ip_out;
737 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
738 " and falling back to domain '%s'\n",
739 c_realm, c_domain));
741 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
742 if (ok) {
743 if (is_zero_addr(&ip_out)) {
744 return NT_STATUS_NO_LOGON_SERVERS;
748 * we call ads_try_connect() to fill in the
749 * ads->config details
751 ok = ads_try_connect(ads, false, &ip_out);
752 if (ok) {
753 return NT_STATUS_OK;
757 return NT_STATUS_NO_LOGON_SERVERS;
760 if (*c_realm) {
761 sitename = sitename_fetch(talloc_tos(), c_realm);
762 status = resolve_and_ping_dns(ads, sitename, c_realm);
764 if (NT_STATUS_IS_OK(status)) {
765 TALLOC_FREE(sitename);
766 return status;
769 /* In case we failed to contact one of our closest DC on our
770 * site we
771 * need to try to find another DC, retry with a site-less SRV
772 * DNS query
773 * - Guenther */
775 if (sitename) {
776 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
777 "our site (%s), Trying to find another DC "
778 "for realm '%s' (domain '%s')\n",
779 sitename, c_realm, c_domain));
780 namecache_delete(c_realm, 0x1C);
781 status =
782 resolve_and_ping_dns(ads, NULL, c_realm);
784 if (NT_STATUS_IS_OK(status)) {
785 TALLOC_FREE(sitename);
786 return status;
790 TALLOC_FREE(sitename);
793 /* try netbios as fallback - if permitted,
794 or if configuration specifically requests it */
795 if (*c_domain) {
796 if (*c_realm) {
797 DEBUG(3, ("ads_find_dc: falling back to netbios "
798 "name resolution for domain '%s' (realm '%s')\n",
799 c_domain, c_realm));
802 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
803 if (NT_STATUS_IS_OK(status)) {
804 return status;
808 DEBUG(1, ("ads_find_dc: "
809 "name resolution for realm '%s' (domain '%s') failed: %s\n",
810 c_realm, c_domain, nt_errstr(status)));
811 return status;
815 * Connect to the LDAP server
816 * @param ads Pointer to an existing ADS_STRUCT
817 * @return status of connection
819 static ADS_STATUS ads_connect_internal(ADS_STRUCT *ads,
820 struct cli_credentials *creds)
822 int version = LDAP_VERSION3;
823 ADS_STATUS status;
824 NTSTATUS ntstatus;
825 char addr[INET6_ADDRSTRLEN];
826 struct sockaddr_storage existing_ss;
827 bool tls = false;
828 bool start_tls = false;
830 zero_sockaddr(&existing_ss);
832 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
833 SMB_ASSERT(creds != NULL);
836 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
838 * Simple anonyous binds are only
839 * allowed for anonymous credentials
841 SMB_ASSERT(cli_credentials_is_anonymous(creds));
844 if (!(ads->auth.flags & (ADS_AUTH_NO_BIND|ADS_AUTH_ANON_BIND))) {
845 ads->auth.flags |= ADS_AUTH_GENERATE_KRB5_CONFIG;
849 * ads_connect can be passed in a reused ADS_STRUCT
850 * with an existing non-zero ads->ldap.ss IP address
851 * that was stored by going through ads_find_dc()
852 * if ads->server.ldap_server was NULL.
854 * If ads->server.ldap_server is still NULL but
855 * the target address isn't the zero address, then
856 * store that address off off before zeroing out
857 * ads->ldap so we don't keep doing multiple calls
858 * to ads_find_dc() in the reuse case.
860 * If a caller wants a clean ADS_STRUCT they
861 * will TALLOC_FREE it and allocate a new one
862 * by calling ads_init(), which ensures
863 * ads->ldap.ss is a properly zero'ed out valid IP
864 * address.
866 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
867 /* Save off the address we previously found by ads_find_dc(). */
868 existing_ss = ads->ldap.ss;
871 ads_zero_ldap(ads);
872 ZERO_STRUCT(ads->ldap_tls_data);
873 ZERO_STRUCT(ads->ldap_wrap_data);
874 ads->ldap.last_attempt = time_mono(NULL);
875 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
877 /* try with a user specified server */
879 if (DEBUGLEVEL >= 11) {
880 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
881 DEBUG(11,("ads_connect: entering\n"));
882 DEBUGADD(11,("%s\n", s));
883 TALLOC_FREE(s);
886 if (ads->server.ldap_server) {
887 bool ok = false;
888 struct sockaddr_storage ss;
890 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
891 ads->server.ldap_server);
892 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
893 if (!ok) {
894 DEBUG(5,("ads_connect: unable to resolve name %s\n",
895 ads->server.ldap_server));
896 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
897 goto out;
900 if (is_zero_addr(&ss)) {
901 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
902 goto out;
905 ok = ads_try_connect(ads, ads->server.gc, &ss);
906 if (ok) {
907 goto got_connection;
910 /* The choice of which GC use is handled one level up in
911 ads_connect_gc(). If we continue on from here with
912 ads_find_dc() we will get GC searches on port 389 which
913 doesn't work. --jerry */
915 if (ads->server.gc == true) {
916 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
919 if (ads->server.no_fallback) {
920 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
921 goto out;
925 if (!is_zero_addr(&existing_ss)) {
926 /* We saved off who we should talk to. */
927 bool ok = ads_try_connect(ads,
928 ads->server.gc,
929 &existing_ss);
930 if (ok) {
931 goto got_connection;
934 * Keep trying to find a server and fall through
935 * into ads_find_dc() again.
937 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
938 "trying to find another DC.\n");
941 ntstatus = ads_find_dc(ads);
942 if (NT_STATUS_IS_OK(ntstatus)) {
943 goto got_connection;
946 status = ADS_ERROR_NT(ntstatus);
947 goto out;
949 got_connection:
951 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
952 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
954 if (!ads->auth.kdc_server) {
955 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
956 ads->auth.kdc_server = talloc_strdup(ads, addr);
957 if (ads->auth.kdc_server == NULL) {
958 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
959 goto out;
963 /* If the caller() requested no LDAP bind, then we are done */
965 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
966 status = ADS_SUCCESS;
967 goto out;
970 ads->ldap_tls_data.mem_ctx = talloc_init("ads LDAP TLS connection memory");
971 if (!ads->ldap_tls_data.mem_ctx) {
972 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
973 goto out;
976 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
977 if (!ads->ldap_wrap_data.mem_ctx) {
978 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
979 goto out;
982 /* Otherwise setup the TCP LDAP session */
984 if (ads->auth.flags & ADS_AUTH_SASL_LDAPS) {
985 tls = true;
986 ads->ldap.port = 636;
987 } else if (ads->auth.flags & ADS_AUTH_SASL_STARTTLS) {
988 tls = true;
989 start_tls = true;
990 ads->ldap.port = 389;
991 } else {
992 ads->ldap.port = 389;
995 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
996 &ads->ldap.ss,
997 ads->ldap.port, lp_ldap_timeout());
998 if (ads->ldap.ld == NULL) {
999 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
1000 goto out;
1002 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
1004 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1006 if (start_tls) {
1007 unsigned int to = lp_ldap_connection_timeout();
1008 struct berval *rspdata = NULL;
1009 char *rspoid = NULL;
1010 int rc;
1012 if (to) {
1013 /* Setup timeout */
1014 gotalarm = 0;
1015 CatchSignal(SIGALRM, gotalarm_sig);
1016 alarm(to);
1017 /* End setup timeout. */
1020 rc = ldap_extended_operation_s(ads->ldap.ld,
1021 LDAP_EXOP_START_TLS,
1022 NULL,
1023 NULL,
1024 NULL,
1025 &rspoid,
1026 &rspdata);
1027 if (gotalarm != 0 && rc == LDAP_SUCCESS) {
1028 rc = LDAP_TIMEOUT;
1031 if (to) {
1032 /* Teardown timeout. */
1033 alarm(0);
1034 CatchSignal(SIGALRM, SIG_IGN);
1037 if (rspoid != NULL) {
1038 ldap_memfree(rspoid);
1041 if (rspdata != NULL) {
1042 ber_bvfree(rspdata);
1045 if (rc != LDAP_SUCCESS) {
1046 status = ADS_ERROR_LDAP(rc);
1047 goto out;
1051 if (tls) {
1052 unsigned int to = lp_ldap_connection_timeout();
1054 if (to) {
1055 /* Setup timeout */
1056 gotalarm = 0;
1057 CatchSignal(SIGALRM, gotalarm_sig);
1058 alarm(to);
1059 /* End setup timeout. */
1062 status = ads_setup_tls_wrapping(&ads->ldap_tls_data,
1063 ads->ldap.ld,
1064 ads->config.ldap_server_name);
1066 if (to) {
1067 /* Teardown timeout. */
1068 alarm(0);
1069 CatchSignal(SIGALRM, SIG_IGN);
1072 if ( !ADS_ERR_OK(status) ) {
1073 goto out;
1077 /* cache the successful connection for workgroup and realm */
1078 if (ads_closest_dc(ads)) {
1079 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
1080 saf_store( ads->server.realm, ads->config.ldap_server_name);
1083 /* fill in the current time and offsets */
1085 status = ads_current_time( ads );
1086 if ( !ADS_ERR_OK(status) ) {
1087 goto out;
1090 /* Now do the bind */
1092 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
1093 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
1094 goto out;
1097 status = ads_sasl_bind(ads, creds);
1099 out:
1100 if (DEBUGLEVEL >= 11) {
1101 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1102 DEBUG(11,("ads_connect: leaving with: %s\n",
1103 ads_errstr(status)));
1104 DEBUGADD(11,("%s\n", s));
1105 TALLOC_FREE(s);
1108 return status;
1112 * Connect to the LDAP server using without a bind
1113 * and without a tcp connection at all
1115 * @param ads Pointer to an existing ADS_STRUCT
1116 * @return status of connection
1118 ADS_STATUS ads_connect_cldap_only(ADS_STRUCT *ads)
1120 ads->auth.flags |= ADS_AUTH_NO_BIND;
1121 return ads_connect_internal(ads, NULL);
1125 * Connect to the LDAP server
1126 * @param ads Pointer to an existing ADS_STRUCT
1127 * @return status of connection
1129 ADS_STATUS ads_connect_creds(ADS_STRUCT *ads, struct cli_credentials *creds)
1131 SMB_ASSERT(creds != NULL);
1134 * We allow upgrades from
1135 * ADS_AUTH_NO_BIND if credentials
1136 * are specified
1138 ads->auth.flags &= ~ADS_AUTH_NO_BIND;
1141 * We allow upgrades from ADS_AUTH_ANON_BIND,
1142 * as we don't want to use simple binds with
1143 * non-anon credentials
1145 if (!cli_credentials_is_anonymous(creds)) {
1146 ads->auth.flags &= ~ADS_AUTH_ANON_BIND;
1149 return ads_connect_internal(ads, creds);
1153 * Connect to the LDAP server using anonymous credentials
1154 * using a simple bind without username/password
1156 * @param ads Pointer to an existing ADS_STRUCT
1157 * @return status of connection
1159 ADS_STATUS ads_connect_simple_anon(ADS_STRUCT *ads)
1161 TALLOC_CTX *frame = talloc_stackframe();
1162 struct cli_credentials *creds = NULL;
1163 ADS_STATUS status;
1165 creds = cli_credentials_init_anon(frame);
1166 if (creds == NULL) {
1167 TALLOC_FREE(frame);
1168 return ADS_ERROR_SYSTEM(errno);
1171 ads->auth.flags |= ADS_AUTH_ANON_BIND;
1172 status = ads_connect_creds(ads, creds);
1173 TALLOC_FREE(frame);
1174 return status;
1178 * Connect to the LDAP server using the machine account
1179 * @param ads Pointer to an existing ADS_STRUCT
1180 * @return status of connection
1182 ADS_STATUS ads_connect_machine(ADS_STRUCT *ads)
1184 TALLOC_CTX *frame = talloc_stackframe();
1185 struct cli_credentials *creds = NULL;
1186 ADS_STATUS status;
1187 NTSTATUS ntstatus;
1189 ntstatus = pdb_get_trust_credentials(ads->server.workgroup,
1190 ads->server.realm,
1191 frame,
1192 &creds);
1193 if (!NT_STATUS_IS_OK(ntstatus)) {
1194 TALLOC_FREE(frame);
1195 return ADS_ERROR_NT(ntstatus);
1198 status = ads_connect_creds(ads, creds);
1199 TALLOC_FREE(frame);
1200 return status;
1204 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1205 * @param ads Pointer to an existing ADS_STRUCT
1207 * Sets the ads->ldap.ss to a valid
1208 * zero ip address that can be detected by
1209 * our is_zero_addr() function. Otherwise
1210 * it is left as AF_UNSPEC (0).
1212 void ads_zero_ldap(ADS_STRUCT *ads)
1214 ZERO_STRUCT(ads->ldap);
1216 * Initialize the sockaddr_storage so we can use
1217 * sockaddr test functions against it.
1219 zero_sockaddr(&ads->ldap.ss);
1223 * Disconnect the LDAP server
1224 * @param ads Pointer to an existing ADS_STRUCT
1226 void ads_disconnect(ADS_STRUCT *ads)
1228 if (ads->ldap.ld) {
1229 ldap_unbind(ads->ldap.ld);
1230 ads->ldap.ld = NULL;
1232 if (ads->ldap_tls_data.mem_ctx) {
1233 talloc_free(ads->ldap_tls_data.mem_ctx);
1235 if (ads->ldap_wrap_data.wrap_ops &&
1236 ads->ldap_wrap_data.wrap_ops->disconnect) {
1237 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1239 if (ads->ldap_wrap_data.mem_ctx) {
1240 talloc_free(ads->ldap_wrap_data.mem_ctx);
1242 ads_zero_ldap(ads);
1243 ZERO_STRUCT(ads->ldap_tls_data);
1244 ZERO_STRUCT(ads->ldap_wrap_data);
1248 Duplicate a struct berval into talloc'ed memory
1250 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1252 struct berval *value;
1254 if (!in_val) return NULL;
1256 value = talloc_zero(ctx, struct berval);
1257 if (value == NULL)
1258 return NULL;
1259 if (in_val->bv_len == 0) return value;
1261 value->bv_len = in_val->bv_len;
1262 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1263 in_val->bv_len);
1264 return value;
1268 Make a values list out of an array of (struct berval *)
1270 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1271 const struct berval **in_vals)
1273 struct berval **values;
1274 int i;
1276 if (!in_vals) return NULL;
1277 for (i=0; in_vals[i]; i++)
1278 ; /* count values */
1279 values = talloc_zero_array(ctx, struct berval *, i+1);
1280 if (!values) return NULL;
1282 for (i=0; in_vals[i]; i++) {
1283 values[i] = dup_berval(ctx, in_vals[i]);
1285 return values;
1289 UTF8-encode a values list out of an array of (char *)
1291 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1293 char **values;
1294 int i;
1295 size_t size;
1297 if (!in_vals) return NULL;
1298 for (i=0; in_vals[i]; i++)
1299 ; /* count values */
1300 values = talloc_zero_array(ctx, char *, i+1);
1301 if (!values) return NULL;
1303 for (i=0; in_vals[i]; i++) {
1304 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1305 TALLOC_FREE(values);
1306 return NULL;
1309 return values;
1313 Pull a (char *) array out of a UTF8-encoded values list
1315 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1317 char **values;
1318 int i;
1319 size_t converted_size;
1321 if (!in_vals) return NULL;
1322 for (i=0; in_vals[i]; i++)
1323 ; /* count values */
1324 values = talloc_zero_array(ctx, char *, i+1);
1325 if (!values) return NULL;
1327 for (i=0; in_vals[i]; i++) {
1328 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1329 &converted_size)) {
1330 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1331 "%s\n", strerror(errno)));
1334 return values;
1338 * Do a search with paged results. cookie must be null on the first
1339 * call, and then returned on each subsequent call. It will be null
1340 * again when the entire search is complete
1341 * @param ads connection to ads server
1342 * @param bind_path Base dn for the search
1343 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1344 * @param expr Search expression - specified in local charset
1345 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1346 * @param res ** which will contain results - free res* with ads_msgfree()
1347 * @param count Number of entries retrieved on this page
1348 * @param cookie The paged results cookie to be returned on subsequent calls
1349 * @return status of search
1351 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1352 const char *bind_path,
1353 int scope, const char *expr,
1354 const char **attrs, void *args,
1355 LDAPMessage **res,
1356 int *count, struct berval **cookie)
1358 int rc, i, version;
1359 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1360 size_t converted_size;
1361 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1362 BerElement *cookie_be = NULL;
1363 struct berval *cookie_bv= NULL;
1364 BerElement *ext_be = NULL;
1365 struct berval *ext_bv= NULL;
1367 TALLOC_CTX *ctx;
1368 ads_control *external_control = (ads_control *) args;
1370 *res = NULL;
1372 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1373 return ADS_ERROR(LDAP_NO_MEMORY);
1375 /* 0 means the conversion worked but the result was empty
1376 so we only fail if it's -1. In any case, it always
1377 at least nulls out the dest */
1378 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1379 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1381 rc = LDAP_NO_MEMORY;
1382 goto done;
1385 if (!attrs || !(*attrs))
1386 search_attrs = NULL;
1387 else {
1388 /* This would be the utf8-encoded version...*/
1389 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1390 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1391 rc = LDAP_NO_MEMORY;
1392 goto done;
1396 /* Paged results only available on ldap v3 or later */
1397 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1398 if (version < LDAP_VERSION3) {
1399 rc = LDAP_NOT_SUPPORTED;
1400 goto done;
1403 cookie_be = ber_alloc_t(LBER_USE_DER);
1404 if (*cookie) {
1405 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1406 ber_bvfree(*cookie); /* don't need it from last time */
1407 *cookie = NULL;
1408 } else {
1409 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1411 ber_flatten(cookie_be, &cookie_bv);
1412 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1413 PagedResults.ldctl_iscritical = (char) 1;
1414 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1415 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1417 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1418 NoReferrals.ldctl_iscritical = (char) 0;
1419 NoReferrals.ldctl_value.bv_len = 0;
1420 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1422 if (external_control &&
1423 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1424 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1426 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1427 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1429 /* win2k does not accept a ldctl_value being passed in */
1431 if (external_control->val != 0) {
1433 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1434 rc = LDAP_NO_MEMORY;
1435 goto done;
1438 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1439 rc = LDAP_NO_MEMORY;
1440 goto done;
1442 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1443 rc = LDAP_NO_MEMORY;
1444 goto done;
1447 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1448 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1450 } else {
1451 ExternalCtrl.ldctl_value.bv_len = 0;
1452 ExternalCtrl.ldctl_value.bv_val = NULL;
1455 controls[0] = &NoReferrals;
1456 controls[1] = &PagedResults;
1457 controls[2] = &ExternalCtrl;
1458 controls[3] = NULL;
1460 } else {
1461 controls[0] = &NoReferrals;
1462 controls[1] = &PagedResults;
1463 controls[2] = NULL;
1466 /* we need to disable referrals as the openldap libs don't
1467 handle them and paged results at the same time. Using them
1468 together results in the result record containing the server
1469 page control being removed from the result list (tridge/jmcd)
1471 leaving this in despite the control that says don't generate
1472 referrals, in case the server doesn't support it (jmcd)
1474 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1476 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1477 search_attrs, 0, controls,
1478 NULL, LDAP_NO_LIMIT,
1479 (LDAPMessage **)res);
1481 ber_free(cookie_be, 1);
1482 ber_bvfree(cookie_bv);
1484 if (rc) {
1485 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1486 ldap_err2string(rc)));
1487 if (rc == LDAP_OTHER) {
1488 char *ldap_errmsg;
1489 int ret;
1491 ret = ldap_parse_result(ads->ldap.ld,
1492 *res,
1493 NULL,
1494 NULL,
1495 &ldap_errmsg,
1496 NULL,
1497 NULL,
1499 if (ret == LDAP_SUCCESS) {
1500 DEBUG(3, ("ldap_search_with_timeout(%s) "
1501 "error: %s\n", expr, ldap_errmsg));
1502 ldap_memfree(ldap_errmsg);
1505 goto done;
1508 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1509 NULL, &rcontrols, 0);
1511 if (!rcontrols) {
1512 goto done;
1515 for (i=0; rcontrols[i]; i++) {
1516 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1517 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1518 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1519 &cookie_bv);
1520 /* the berval is the cookie, but must be freed when
1521 it is all done */
1522 if (cookie_bv->bv_len) /* still more to do */
1523 *cookie=ber_bvdup(cookie_bv);
1524 else
1525 *cookie=NULL;
1526 ber_bvfree(cookie_bv);
1527 ber_free(cookie_be, 1);
1528 break;
1531 ldap_controls_free(rcontrols);
1533 done:
1534 talloc_destroy(ctx);
1536 if (ext_be) {
1537 ber_free(ext_be, 1);
1540 if (ext_bv) {
1541 ber_bvfree(ext_bv);
1544 if (rc != LDAP_SUCCESS && *res != NULL) {
1545 ads_msgfree(ads, *res);
1546 *res = NULL;
1549 /* if/when we decide to utf8-encode attrs, take out this next line */
1550 TALLOC_FREE(search_attrs);
1552 return ADS_ERROR(rc);
1555 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1556 int scope, const char *expr,
1557 const char **attrs, LDAPMessage **res,
1558 int *count, struct berval **cookie)
1560 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1565 * Get all results for a search. This uses ads_do_paged_search() to return
1566 * all entries in a large search.
1567 * @param ads connection to ads server
1568 * @param bind_path Base dn for the search
1569 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1570 * @param expr Search expression
1571 * @param attrs Attributes to retrieve
1572 * @param res ** which will contain results - free res* with ads_msgfree()
1573 * @return status of search
1575 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1576 int scope, const char *expr,
1577 const char **attrs, void *args,
1578 LDAPMessage **res)
1580 struct berval *cookie = NULL;
1581 int count = 0;
1582 ADS_STATUS status;
1584 *res = NULL;
1585 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1586 &count, &cookie);
1588 if (!ADS_ERR_OK(status))
1589 return status;
1591 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1592 while (cookie) {
1593 LDAPMessage *res2 = NULL;
1594 LDAPMessage *msg, *next;
1596 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1597 attrs, args, &res2, &count, &cookie);
1598 if (!ADS_ERR_OK(status)) {
1599 break;
1602 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1603 that this works on all ldap libs, but I have only tested with openldap */
1604 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1605 next = ads_next_message(ads, msg);
1606 ldap_add_result_entry((LDAPMessage **)res, msg);
1608 /* note that we do not free res2, as the memory is now
1609 part of the main returned list */
1611 #else
1612 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1613 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1614 #endif
1616 return status;
1619 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1620 int scope, const char *expr,
1621 const char **attrs, LDAPMessage **res)
1623 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1626 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1627 int scope, const char *expr,
1628 const char **attrs, uint32_t sd_flags,
1629 LDAPMessage **res)
1631 ads_control args;
1633 args.control = ADS_SD_FLAGS_OID;
1634 args.val = sd_flags;
1635 args.critical = True;
1637 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1642 * Run a function on all results for a search. Uses ads_do_paged_search() and
1643 * runs the function as each page is returned, using ads_process_results()
1644 * @param ads connection to ads server
1645 * @param bind_path Base dn for the search
1646 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1647 * @param expr Search expression - specified in local charset
1648 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1649 * @param fn Function which takes attr name, values list, and data_area
1650 * @param data_area Pointer which is passed to function on each call
1651 * @return status of search
1653 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1654 int scope, const char *expr, const char **attrs,
1655 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1656 void *data_area)
1658 struct berval *cookie = NULL;
1659 int count = 0;
1660 ADS_STATUS status;
1661 LDAPMessage *res;
1663 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1664 &count, &cookie);
1666 if (!ADS_ERR_OK(status)) return status;
1668 ads_process_results(ads, res, fn, data_area);
1669 ads_msgfree(ads, res);
1671 while (cookie) {
1672 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1673 &res, &count, &cookie);
1675 if (!ADS_ERR_OK(status)) break;
1677 ads_process_results(ads, res, fn, data_area);
1678 ads_msgfree(ads, res);
1681 return status;
1685 * Do a search with a timeout.
1686 * @param ads connection to ads server
1687 * @param bind_path Base dn for the search
1688 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1689 * @param expr Search expression
1690 * @param attrs Attributes to retrieve
1691 * @param res ** which will contain results - free res* with ads_msgfree()
1692 * @return status of search
1694 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1695 const char *expr,
1696 const char **attrs, LDAPMessage **res)
1698 int rc;
1699 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1700 size_t converted_size;
1701 TALLOC_CTX *ctx;
1703 *res = NULL;
1704 if (!(ctx = talloc_init("ads_do_search"))) {
1705 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1706 return ADS_ERROR(LDAP_NO_MEMORY);
1709 /* 0 means the conversion worked but the result was empty
1710 so we only fail if it's negative. In any case, it always
1711 at least nulls out the dest */
1712 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1713 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1715 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1716 rc = LDAP_NO_MEMORY;
1717 goto done;
1720 if (!attrs || !(*attrs))
1721 search_attrs = NULL;
1722 else {
1723 /* This would be the utf8-encoded version...*/
1724 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1725 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1727 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1728 rc = LDAP_NO_MEMORY;
1729 goto done;
1733 /* see the note in ads_do_paged_search - we *must* disable referrals */
1734 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1736 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1737 search_attrs, 0, NULL, NULL,
1738 LDAP_NO_LIMIT,
1739 (LDAPMessage **)res);
1741 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1742 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1743 rc = 0;
1746 done:
1747 talloc_destroy(ctx);
1748 /* if/when we decide to utf8-encode attrs, take out this next line */
1749 TALLOC_FREE(search_attrs);
1750 return ADS_ERROR(rc);
1753 * Do a general ADS search
1754 * @param ads connection to ads server
1755 * @param res ** which will contain results - free res* with ads_msgfree()
1756 * @param expr Search expression
1757 * @param attrs Attributes to retrieve
1758 * @return status of search
1760 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1761 const char *expr, const char **attrs)
1763 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1764 expr, attrs, res);
1768 * Do a search on a specific DistinguishedName
1769 * @param ads connection to ads server
1770 * @param res ** which will contain results - free res* with ads_msgfree()
1771 * @param dn DistinguishedName to search
1772 * @param attrs Attributes to retrieve
1773 * @return status of search
1775 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1776 const char *dn, const char **attrs)
1778 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1779 attrs, res);
1783 * Free up memory from a ads_search
1784 * @param ads connection to ads server
1785 * @param msg Search results to free
1787 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1789 if (!msg) return;
1790 ldap_msgfree(msg);
1794 * Get a dn from search results
1795 * @param ads connection to ads server
1796 * @param msg Search result
1797 * @return dn string
1799 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1801 char *utf8_dn, *unix_dn;
1802 size_t converted_size;
1804 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1806 if (!utf8_dn) {
1807 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1808 return NULL;
1811 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1812 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1813 utf8_dn ));
1814 return NULL;
1816 ldap_memfree(utf8_dn);
1817 return unix_dn;
1821 * Get the parent from a dn
1822 * @param dn the dn to return the parent from
1823 * @return parent dn string
1825 char *ads_parent_dn(const char *dn)
1827 char *p;
1829 if (dn == NULL) {
1830 return NULL;
1833 p = strchr(dn, ',');
1835 if (p == NULL) {
1836 return NULL;
1839 return p+1;
1843 * Find a machine account given a hostname
1844 * @param ads connection to ads server
1845 * @param res ** which will contain results - free res* with ads_msgfree()
1846 * @param host Hostname to search for
1847 * @return status of search
1849 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1850 const char *machine)
1852 ADS_STATUS status;
1853 char *expr;
1854 const char *attrs[] = {
1855 /* This is how Windows checks for machine accounts */
1856 "objectClass",
1857 "SamAccountName",
1858 "userAccountControl",
1859 "DnsHostName",
1860 "ServicePrincipalName",
1861 "userPrincipalName",
1863 /* Additional attributes Samba checks */
1864 "msDS-KeyVersionNumber",
1865 "msDS-AdditionalDnsHostName",
1866 "msDS-SupportedEncryptionTypes",
1867 "nTSecurityDescriptor",
1868 "objectSid",
1870 NULL
1872 TALLOC_CTX *frame = talloc_stackframe();
1874 *res = NULL;
1876 /* the easiest way to find a machine account anywhere in the tree
1877 is to look for hostname$ */
1878 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1879 if (expr == NULL) {
1880 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1881 goto done;
1884 status = ads_search(ads, res, expr, attrs);
1885 if (ADS_ERR_OK(status)) {
1886 if (ads_count_replies(ads, *res) != 1) {
1887 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1891 done:
1892 TALLOC_FREE(frame);
1893 return status;
1897 * Initialize a list of mods to be used in a modify request
1898 * @param ctx An initialized TALLOC_CTX
1899 * @return allocated ADS_MODLIST
1901 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1903 #define ADS_MODLIST_ALLOC_SIZE 10
1904 LDAPMod **mods;
1906 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1907 /* -1 is safety to make sure we don't go over the end.
1908 need to reset it to NULL before doing ldap modify */
1909 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1911 return (ADS_MODLIST)mods;
1916 add an attribute to the list, with values list already constructed
1918 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1919 int mod_op, const char *name,
1920 const void *_invals)
1922 int curmod;
1923 LDAPMod **modlist = (LDAPMod **) *mods;
1924 struct berval **ber_values = NULL;
1925 char **char_values = NULL;
1927 if (!_invals) {
1928 mod_op = LDAP_MOD_DELETE;
1929 } else {
1930 if (mod_op & LDAP_MOD_BVALUES) {
1931 const struct berval **b;
1932 b = discard_const_p(const struct berval *, _invals);
1933 ber_values = ads_dup_values(ctx, b);
1934 } else {
1935 const char **c;
1936 c = discard_const_p(const char *, _invals);
1937 char_values = ads_push_strvals(ctx, c);
1941 /* find the first empty slot */
1942 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1943 curmod++);
1944 if (modlist[curmod] == (LDAPMod *) -1) {
1945 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1946 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1947 return ADS_ERROR(LDAP_NO_MEMORY);
1948 memset(&modlist[curmod], 0,
1949 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1950 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1951 *mods = (ADS_MODLIST)modlist;
1954 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1955 return ADS_ERROR(LDAP_NO_MEMORY);
1956 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1957 if (mod_op & LDAP_MOD_BVALUES) {
1958 modlist[curmod]->mod_bvalues = ber_values;
1959 } else if (mod_op & LDAP_MOD_DELETE) {
1960 modlist[curmod]->mod_values = NULL;
1961 } else {
1962 modlist[curmod]->mod_values = char_values;
1965 modlist[curmod]->mod_op = mod_op;
1966 return ADS_ERROR(LDAP_SUCCESS);
1970 * Add a single string value to a mod list
1971 * @param ctx An initialized TALLOC_CTX
1972 * @param mods An initialized ADS_MODLIST
1973 * @param name The attribute name to add
1974 * @param val The value to add - NULL means DELETE
1975 * @return ADS STATUS indicating success of add
1977 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1978 const char *name, const char *val)
1980 const char *values[2];
1982 values[0] = val;
1983 values[1] = NULL;
1985 if (!val)
1986 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1987 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1991 * Add an array of string values to a mod list
1992 * @param ctx An initialized TALLOC_CTX
1993 * @param mods An initialized ADS_MODLIST
1994 * @param name The attribute name to add
1995 * @param vals The array of string values to add - NULL means DELETE
1996 * @return ADS STATUS indicating success of add
1998 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1999 const char *name, const char **vals)
2001 if (!vals)
2002 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
2003 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
2004 name, (const void **) vals);
2008 * Add a single ber-encoded value to a mod list
2009 * @param ctx An initialized TALLOC_CTX
2010 * @param mods An initialized ADS_MODLIST
2011 * @param name The attribute name to add
2012 * @param val The value to add - NULL means DELETE
2013 * @return ADS STATUS indicating success of add
2015 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2016 const char *name, const struct berval *val)
2018 const struct berval *values[2];
2020 values[0] = val;
2021 values[1] = NULL;
2022 if (!val)
2023 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
2024 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
2025 name, (const void *) values);
2028 static void ads_print_error(int ret, LDAP *ld)
2030 if (ret != 0) {
2031 char *ld_error = NULL;
2032 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
2033 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
2034 ret,
2035 ldap_err2string(ret),
2036 ld_error);
2037 SAFE_FREE(ld_error);
2042 * Perform an ldap modify
2043 * @param ads connection to ads server
2044 * @param mod_dn DistinguishedName to modify
2045 * @param mods list of modifications to perform
2046 * @return status of modify
2048 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
2050 int ret,i;
2051 char *utf8_dn = NULL;
2052 size_t converted_size;
2054 this control is needed to modify that contains a currently
2055 non-existent attribute (but allowable for the object) to run
2057 LDAPControl PermitModify = {
2058 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
2059 {0, NULL},
2060 (char) 1};
2061 LDAPControl *controls[2];
2063 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
2065 controls[0] = &PermitModify;
2066 controls[1] = NULL;
2068 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
2069 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2072 /* find the end of the list, marked by NULL or -1 */
2073 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2074 /* make sure the end of the list is NULL */
2075 mods[i] = NULL;
2076 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
2077 (LDAPMod **) mods, controls, NULL);
2078 ads_print_error(ret, ads->ldap.ld);
2079 TALLOC_FREE(utf8_dn);
2080 return ADS_ERROR(ret);
2084 * Perform an ldap add
2085 * @param ads connection to ads server
2086 * @param new_dn DistinguishedName to add
2087 * @param mods list of attributes and values for DN
2088 * @return status of add
2090 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
2092 int ret, i;
2093 char *utf8_dn = NULL;
2094 size_t converted_size;
2096 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
2098 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
2099 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2100 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2103 /* find the end of the list, marked by NULL or -1 */
2104 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2105 /* make sure the end of the list is NULL */
2106 mods[i] = NULL;
2108 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
2109 ads_print_error(ret, ads->ldap.ld);
2110 TALLOC_FREE(utf8_dn);
2111 return ADS_ERROR(ret);
2115 * Delete a DistinguishedName
2116 * @param ads connection to ads server
2117 * @param new_dn DistinguishedName to delete
2118 * @return status of delete
2120 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
2122 int ret;
2123 char *utf8_dn = NULL;
2124 size_t converted_size;
2125 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
2126 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2127 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2130 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
2132 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
2133 ads_print_error(ret, ads->ldap.ld);
2134 TALLOC_FREE(utf8_dn);
2135 return ADS_ERROR(ret);
2139 * Build an org unit string
2140 * if org unit is Computers or blank then assume a container, otherwise
2141 * assume a / separated list of organisational units.
2142 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2143 * @param ads connection to ads server
2144 * @param org_unit Organizational unit
2145 * @return org unit string - caller must free
2147 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
2149 ADS_STATUS status;
2150 char *ret = NULL;
2151 char *dn = NULL;
2153 if (!org_unit || !*org_unit) {
2155 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
2157 /* samba4 might not yet respond to a wellknownobject-query */
2158 return ret ? ret : SMB_STRDUP("cn=Computers");
2161 if (strequal(org_unit, "Computers")) {
2162 return SMB_STRDUP("cn=Computers");
2165 /* jmcd: removed "\\" from the separation chars, because it is
2166 needed as an escape for chars like '#' which are valid in an
2167 OU name */
2168 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
2169 if (!ADS_ERR_OK(status)) {
2170 return NULL;
2173 return dn;
2177 * Get a org unit string for a well-known GUID
2178 * @param ads connection to ads server
2179 * @param wknguid Well known GUID
2180 * @return org unit string - caller must free
2182 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2184 ADS_STATUS status;
2185 LDAPMessage *res = NULL;
2186 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2187 **bind_dn_exp = NULL;
2188 const char *attrs[] = {"distinguishedName", NULL};
2189 int new_ln, wkn_ln, bind_ln, i;
2191 if (wknguid == NULL) {
2192 return NULL;
2195 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2196 DEBUG(1, ("asprintf failed!\n"));
2197 return NULL;
2200 status = ads_search_dn(ads, &res, base, attrs);
2201 if (!ADS_ERR_OK(status)) {
2202 DEBUG(1,("Failed while searching for: %s\n", base));
2203 goto out;
2206 if (ads_count_replies(ads, res) != 1) {
2207 goto out;
2210 /* substitute the bind-path from the well-known-guid-search result */
2211 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2212 if (!wkn_dn) {
2213 goto out;
2216 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2217 if (!wkn_dn_exp) {
2218 goto out;
2221 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2222 if (!bind_dn_exp) {
2223 goto out;
2226 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2228 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2231 new_ln = wkn_ln - bind_ln;
2233 ret = SMB_STRDUP(wkn_dn_exp[0]);
2234 if (!ret) {
2235 goto out;
2238 for (i=1; i < new_ln; i++) {
2239 char *s = NULL;
2241 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2242 SAFE_FREE(ret);
2243 goto out;
2246 SAFE_FREE(ret);
2247 ret = SMB_STRDUP(s);
2248 free(s);
2249 if (!ret) {
2250 goto out;
2254 out:
2255 SAFE_FREE(base);
2256 ads_msgfree(ads, res);
2257 TALLOC_FREE(wkn_dn);
2258 if (wkn_dn_exp) {
2259 ldap_value_free(wkn_dn_exp);
2261 if (bind_dn_exp) {
2262 ldap_value_free(bind_dn_exp);
2265 return ret;
2269 * Adds (appends) an item to an attribute array, rather then
2270 * replacing the whole list
2271 * @param ctx An initialized TALLOC_CTX
2272 * @param mods An initialized ADS_MODLIST
2273 * @param name name of the ldap attribute to append to
2274 * @param vals an array of values to add
2275 * @return status of addition
2278 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2279 const char *name, const char **vals)
2281 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2282 (const void *) vals);
2286 * This clears out all registered spn's for a given hostname
2287 * @param ads An initialized ADS_STRUCT
2288 * @param machine_name the NetBIOS name of the computer.
2289 * @return 0 upon success, non-zero otherwise.
2292 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2294 TALLOC_CTX *ctx;
2295 LDAPMessage *res = NULL;
2296 ADS_MODLIST mods;
2297 const char *servicePrincipalName[1] = {NULL};
2298 ADS_STATUS ret;
2299 char *dn_string = NULL;
2301 ret = ads_find_machine_acct(ads, &res, machine_name);
2302 if (!ADS_ERR_OK(ret)) {
2303 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2304 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2305 ads_msgfree(ads, res);
2306 return ret;
2309 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2310 ctx = talloc_init("ads_clear_service_principal_names");
2311 if (!ctx) {
2312 ads_msgfree(ads, res);
2313 return ADS_ERROR(LDAP_NO_MEMORY);
2316 if (!(mods = ads_init_mods(ctx))) {
2317 talloc_destroy(ctx);
2318 ads_msgfree(ads, res);
2319 return ADS_ERROR(LDAP_NO_MEMORY);
2321 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2322 if (!ADS_ERR_OK(ret)) {
2323 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2324 ads_msgfree(ads, res);
2325 talloc_destroy(ctx);
2326 return ret;
2328 dn_string = ads_get_dn(ads, talloc_tos(), res);
2329 if (!dn_string) {
2330 talloc_destroy(ctx);
2331 ads_msgfree(ads, res);
2332 return ADS_ERROR(LDAP_NO_MEMORY);
2334 ret = ads_gen_mod(ads, dn_string, mods);
2335 TALLOC_FREE(dn_string);
2336 if (!ADS_ERR_OK(ret)) {
2337 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2338 machine_name));
2339 ads_msgfree(ads, res);
2340 talloc_destroy(ctx);
2341 return ret;
2344 ads_msgfree(ads, res);
2345 talloc_destroy(ctx);
2346 return ret;
2350 * @brief Search for an element in a string array.
2352 * @param[in] el_array The string array to search.
2354 * @param[in] num_el The number of elements in the string array.
2356 * @param[in] el The string to search.
2358 * @return True if found, false if not.
2360 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2362 size_t i;
2364 if (el_array == NULL || num_el == 0 || el == NULL) {
2365 return false;
2368 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2369 int cmp;
2371 cmp = strcasecmp_m(el_array[i], el);
2372 if (cmp == 0) {
2373 return true;
2377 return false;
2381 * @brief This gets the service principal names of an existing computer account.
2383 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2385 * @param[in] ads The ADS context to use.
2387 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2388 * identify the computer account.
2390 * @param[in] spn_array A pointer to store the array for SPNs.
2392 * @param[in] num_spns The number of principals stored in the array.
2394 * @return 0 on success, or a ADS error if a failure occurred.
2396 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2397 ADS_STRUCT *ads,
2398 const char *machine_name,
2399 char ***spn_array,
2400 size_t *num_spns)
2402 ADS_STATUS status;
2403 LDAPMessage *res = NULL;
2404 int count;
2406 status = ads_find_machine_acct(ads,
2407 &res,
2408 machine_name);
2409 if (!ADS_ERR_OK(status)) {
2410 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2411 machine_name));
2412 return status;
2415 count = ads_count_replies(ads, res);
2416 if (count != 1) {
2417 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2418 goto done;
2421 *spn_array = ads_pull_strings(ads,
2422 mem_ctx,
2423 res,
2424 "servicePrincipalName",
2425 num_spns);
2426 if (*spn_array == NULL) {
2427 DEBUG(1, ("Host account for %s does not have service principal "
2428 "names.\n",
2429 machine_name));
2430 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2431 goto done;
2434 done:
2435 ads_msgfree(ads, res);
2437 return status;
2441 * This adds a service principal name to an existing computer account
2442 * (found by hostname) in AD.
2443 * @param ads An initialized ADS_STRUCT
2444 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2445 * @param spns An array or strings for the service principals to add,
2446 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2447 * @return 0 upon success, or non-zero if a failure occurs
2450 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2451 const char *machine_name,
2452 const char **spns)
2454 ADS_STATUS ret;
2455 TALLOC_CTX *ctx;
2456 LDAPMessage *res = NULL;
2457 ADS_MODLIST mods;
2458 char *dn_string = NULL;
2459 const char **servicePrincipalName = spns;
2461 ret = ads_find_machine_acct(ads, &res, machine_name);
2462 if (!ADS_ERR_OK(ret)) {
2463 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2464 machine_name));
2465 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2466 ads_msgfree(ads, res);
2467 return ret;
2470 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2471 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2472 ads_msgfree(ads, res);
2473 return ADS_ERROR(LDAP_NO_MEMORY);
2476 DEBUG(5,("ads_add_service_principal_name: INFO: "
2477 "Adding %s to host %s\n",
2478 spns[0] ? "N/A" : spns[0], machine_name));
2481 DEBUG(5,("ads_add_service_principal_name: INFO: "
2482 "Adding %s to host %s\n",
2483 spns[1] ? "N/A" : spns[1], machine_name));
2485 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2486 ret = ADS_ERROR(LDAP_NO_MEMORY);
2487 goto out;
2490 ret = ads_add_strlist(ctx,
2491 &mods,
2492 "servicePrincipalName",
2493 servicePrincipalName);
2494 if (!ADS_ERR_OK(ret)) {
2495 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2496 goto out;
2499 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2500 ret = ADS_ERROR(LDAP_NO_MEMORY);
2501 goto out;
2504 ret = ads_gen_mod(ads, dn_string, mods);
2505 if (!ADS_ERR_OK(ret)) {
2506 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2507 goto out;
2510 out:
2511 TALLOC_FREE( ctx );
2512 ads_msgfree(ads, res);
2513 return ret;
2516 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2517 LDAPMessage *msg)
2519 uint32_t acct_ctrl = 0;
2520 bool ok;
2522 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2523 if (!ok) {
2524 return 0;
2527 return acct_ctrl;
2530 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2531 LDAPMessage *msg,
2532 const struct berval *machine_pw_val)
2534 ADS_MODLIST mods;
2535 ADS_STATUS ret;
2536 TALLOC_CTX *frame = talloc_stackframe();
2537 uint32_t acct_control;
2538 char *control_str = NULL;
2539 const char *attrs[] = {
2540 "objectSid",
2541 NULL
2543 LDAPMessage *res = NULL;
2544 char *dn = NULL;
2546 dn = ads_get_dn(ads, frame, msg);
2547 if (dn == NULL) {
2548 ret = ADS_ERROR(LDAP_NO_MEMORY);
2549 goto done;
2552 acct_control = ads_get_acct_ctrl(ads, msg);
2553 if (acct_control == 0) {
2554 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2555 goto done;
2559 * Changing the password, disables the account. So we need to change the
2560 * userAccountControl flags to enable it again.
2562 mods = ads_init_mods(frame);
2563 if (mods == NULL) {
2564 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2565 goto done;
2568 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2570 ret = ads_gen_mod(ads, dn, mods);
2571 if (!ADS_ERR_OK(ret)) {
2572 goto done;
2574 TALLOC_FREE(mods);
2577 * To activate the account, we need to disable and enable it.
2579 acct_control |= UF_ACCOUNTDISABLE;
2581 control_str = talloc_asprintf(frame, "%u", acct_control);
2582 if (control_str == NULL) {
2583 ret = ADS_ERROR(LDAP_NO_MEMORY);
2584 goto done;
2587 mods = ads_init_mods(frame);
2588 if (mods == NULL) {
2589 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2590 goto done;
2593 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2595 ret = ads_gen_mod(ads, dn, mods);
2596 if (!ADS_ERR_OK(ret)) {
2597 goto done;
2599 TALLOC_FREE(mods);
2600 TALLOC_FREE(control_str);
2603 * Enable the account again.
2605 acct_control &= ~UF_ACCOUNTDISABLE;
2607 control_str = talloc_asprintf(frame, "%u", acct_control);
2608 if (control_str == NULL) {
2609 ret = ADS_ERROR(LDAP_NO_MEMORY);
2610 goto done;
2613 mods = ads_init_mods(frame);
2614 if (mods == NULL) {
2615 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2616 goto done;
2619 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2621 ret = ads_gen_mod(ads, dn, mods);
2622 if (!ADS_ERR_OK(ret)) {
2623 goto done;
2625 TALLOC_FREE(mods);
2626 TALLOC_FREE(control_str);
2628 ret = ads_search_dn(ads, &res, dn, attrs);
2629 ads_msgfree(ads, res);
2631 done:
2632 talloc_free(frame);
2634 return ret;
2638 * adds a machine account to the ADS server
2639 * @param ads An initialized ADS_STRUCT
2640 * @param machine_name - the NetBIOS machine name of this account.
2641 * @param account_type A number indicating the type of account to create
2642 * @param org_unit The LDAP path in which to place this account
2643 * @return 0 upon success, or non-zero otherwise
2646 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2647 const char *machine_name,
2648 const char *machine_password,
2649 const char *org_unit,
2650 uint32_t etype_list,
2651 const char *dns_domain_name)
2653 ADS_STATUS ret;
2654 char *samAccountName = NULL;
2655 char *controlstr = NULL;
2656 TALLOC_CTX *ctx = NULL;
2657 ADS_MODLIST mods;
2658 char *machine_escaped = NULL;
2659 char *dns_hostname = NULL;
2660 char *new_dn = NULL;
2661 char *utf8_pw = NULL;
2662 size_t utf8_pw_len = 0;
2663 char *utf16_pw = NULL;
2664 size_t utf16_pw_len = 0;
2665 struct berval machine_pw_val;
2666 bool ok;
2667 const char **spn_array = NULL;
2668 size_t num_spns = 0;
2669 const char *spn_prefix[] = {
2670 "HOST",
2671 "RestrictedKrbHost",
2673 size_t i;
2674 LDAPMessage *res = NULL;
2675 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2677 ctx = talloc_init("ads_add_machine_acct");
2678 if (ctx == NULL) {
2679 return ADS_ERROR(LDAP_NO_MEMORY);
2682 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2683 if (machine_escaped == NULL) {
2684 ret = ADS_ERROR(LDAP_NO_MEMORY);
2685 goto done;
2688 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2689 if (utf8_pw == NULL) {
2690 ret = ADS_ERROR(LDAP_NO_MEMORY);
2691 goto done;
2693 utf8_pw_len = strlen(utf8_pw);
2695 ok = convert_string_talloc(ctx,
2696 CH_UTF8, CH_UTF16MUNGED,
2697 utf8_pw, utf8_pw_len,
2698 (void *)&utf16_pw, &utf16_pw_len);
2699 if (!ok) {
2700 ret = ADS_ERROR(LDAP_NO_MEMORY);
2701 goto done;
2704 machine_pw_val = (struct berval) {
2705 .bv_val = utf16_pw,
2706 .bv_len = utf16_pw_len,
2709 /* Check if the machine account already exists. */
2710 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2711 if (ADS_ERR_OK(ret)) {
2712 /* Change the machine account password */
2713 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2714 ads_msgfree(ads, res);
2716 goto done;
2718 ads_msgfree(ads, res);
2720 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2721 if (new_dn == NULL) {
2722 ret = ADS_ERROR(LDAP_NO_MEMORY);
2723 goto done;
2726 /* Create machine account */
2728 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2729 if (samAccountName == NULL) {
2730 ret = ADS_ERROR(LDAP_NO_MEMORY);
2731 goto done;
2734 dns_hostname = talloc_asprintf(ctx,
2735 "%s.%s",
2736 machine_name,
2737 dns_domain_name);
2738 if (dns_hostname == NULL) {
2739 ret = ADS_ERROR(LDAP_NO_MEMORY);
2740 goto done;
2743 /* Add dns_hostname SPNs */
2744 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2745 char *spn = talloc_asprintf(ctx,
2746 "%s/%s",
2747 spn_prefix[i],
2748 dns_hostname);
2749 if (spn == NULL) {
2750 ret = ADS_ERROR(LDAP_NO_MEMORY);
2751 goto done;
2754 ok = add_string_to_array(ctx,
2755 spn,
2756 &spn_array,
2757 &num_spns);
2758 if (!ok) {
2759 ret = ADS_ERROR(LDAP_NO_MEMORY);
2760 goto done;
2764 /* Add machine_name SPNs */
2765 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2766 char *spn = talloc_asprintf(ctx,
2767 "%s/%s",
2768 spn_prefix[i],
2769 machine_name);
2770 if (spn == NULL) {
2771 ret = ADS_ERROR(LDAP_NO_MEMORY);
2772 goto done;
2775 ok = add_string_to_array(ctx,
2776 spn,
2777 &spn_array,
2778 &num_spns);
2779 if (!ok) {
2780 ret = ADS_ERROR(LDAP_NO_MEMORY);
2781 goto done;
2785 /* Make sure to NULL terminate the array */
2786 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2787 if (spn_array == NULL) {
2788 ret = ADS_ERROR(LDAP_NO_MEMORY);
2789 goto done;
2791 spn_array[num_spns] = NULL;
2793 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2794 if (controlstr == NULL) {
2795 ret = ADS_ERROR(LDAP_NO_MEMORY);
2796 goto done;
2799 mods = ads_init_mods(ctx);
2800 if (mods == NULL) {
2801 ret = ADS_ERROR(LDAP_NO_MEMORY);
2802 goto done;
2805 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2806 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2807 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2808 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2809 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2810 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2812 ret = ads_gen_add(ads, new_dn, mods);
2814 done:
2815 SAFE_FREE(machine_escaped);
2816 talloc_destroy(ctx);
2818 return ret;
2822 * move a machine account to another OU on the ADS server
2823 * @param ads - An initialized ADS_STRUCT
2824 * @param machine_name - the NetBIOS machine name of this account.
2825 * @param org_unit - The LDAP path in which to place this account
2826 * @param moved - whether we moved the machine account (optional)
2827 * @return 0 upon success, or non-zero otherwise
2830 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2831 const char *org_unit, bool *moved)
2833 ADS_STATUS rc;
2834 int ldap_status;
2835 LDAPMessage *res = NULL;
2836 char *filter = NULL;
2837 char *computer_dn = NULL;
2838 char *parent_dn;
2839 char *computer_rdn = NULL;
2840 bool need_move = False;
2842 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2843 rc = ADS_ERROR(LDAP_NO_MEMORY);
2844 goto done;
2847 /* Find pre-existing machine */
2848 rc = ads_search(ads, &res, filter, NULL);
2849 if (!ADS_ERR_OK(rc)) {
2850 goto done;
2853 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2854 if (!computer_dn) {
2855 rc = ADS_ERROR(LDAP_NO_MEMORY);
2856 goto done;
2859 parent_dn = ads_parent_dn(computer_dn);
2860 if (strequal(parent_dn, org_unit)) {
2861 goto done;
2864 need_move = True;
2866 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2867 rc = ADS_ERROR(LDAP_NO_MEMORY);
2868 goto done;
2871 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2872 org_unit, 1, NULL, NULL);
2873 rc = ADS_ERROR(ldap_status);
2875 done:
2876 ads_msgfree(ads, res);
2877 SAFE_FREE(filter);
2878 TALLOC_FREE(computer_dn);
2879 SAFE_FREE(computer_rdn);
2881 if (!ADS_ERR_OK(rc)) {
2882 need_move = False;
2885 if (moved) {
2886 *moved = need_move;
2889 return rc;
2893 dump a binary result from ldap
2895 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2897 size_t i;
2898 for (i=0; values[i]; i++) {
2899 ber_len_t j;
2900 printf("%s: ", field);
2901 for (j=0; j<values[i]->bv_len; j++) {
2902 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2904 printf("\n");
2908 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2910 int i;
2911 for (i=0; values[i]; i++) {
2912 NTSTATUS status;
2913 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2914 struct GUID guid;
2916 status = GUID_from_ndr_blob(&in, &guid);
2917 if (NT_STATUS_IS_OK(status)) {
2918 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2919 } else {
2920 printf("%s: INVALID GUID\n", field);
2926 dump a sid result from ldap
2928 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2930 int i;
2931 for (i=0; values[i]; i++) {
2932 ssize_t ret;
2933 struct dom_sid sid;
2934 struct dom_sid_buf tmp;
2935 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2936 values[i]->bv_len, &sid);
2937 if (ret == -1) {
2938 return;
2940 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2945 dump ntSecurityDescriptor
2947 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2949 TALLOC_CTX *frame = talloc_stackframe();
2950 struct security_descriptor *psd;
2951 NTSTATUS status;
2953 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2954 values[0]->bv_len, &psd);
2955 if (!NT_STATUS_IS_OK(status)) {
2956 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2957 nt_errstr(status)));
2958 TALLOC_FREE(frame);
2959 return;
2962 if (psd) {
2963 ads_disp_sd(ads, talloc_tos(), psd);
2966 TALLOC_FREE(frame);
2970 dump a string result from ldap
2972 static void dump_string(const char *field, char **values)
2974 int i;
2975 for (i=0; values[i]; i++) {
2976 printf("%s: %s\n", field, values[i]);
2981 dump a field from LDAP on stdout
2982 used for debugging
2985 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2987 const struct {
2988 const char *name;
2989 bool string;
2990 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2991 } handlers[] = {
2992 {"objectGUID", False, dump_guid},
2993 {"netbootGUID", False, dump_guid},
2994 {"nTSecurityDescriptor", False, dump_sd},
2995 {"dnsRecord", False, dump_binary},
2996 {"objectSid", False, dump_sid},
2997 {"securityIdentifier", False, dump_sid},
2998 {"tokenGroups", False, dump_sid},
2999 {"tokenGroupsNoGCAcceptable", False, dump_sid},
3000 {"tokengroupsGlobalandUniversal", False, dump_sid},
3001 {"mS-DS-CreatorSID", False, dump_sid},
3002 {"msExchMailboxGuid", False, dump_guid},
3003 {"msDS-TrustForestTrustInfo", False, dump_binary},
3004 {NULL, True, NULL}
3006 int i;
3008 if (!field) { /* must be end of an entry */
3009 printf("\n");
3010 return False;
3013 for (i=0; handlers[i].name; i++) {
3014 if (strcasecmp_m(handlers[i].name, field) == 0) {
3015 if (!values) /* first time, indicate string or not */
3016 return handlers[i].string;
3017 handlers[i].handler(ads, field, (struct berval **) values);
3018 break;
3021 if (!handlers[i].name) {
3022 if (!values) /* first time, indicate string conversion */
3023 return True;
3024 dump_string(field, (char **)values);
3026 return False;
3030 * Dump a result from LDAP on stdout
3031 * used for debugging
3032 * @param ads connection to ads server
3033 * @param res Results to dump
3036 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
3038 ads_process_results(ads, res, ads_dump_field, NULL);
3042 * Walk through results, calling a function for each entry found.
3043 * The function receives a field name, a berval * array of values,
3044 * and a data area passed through from the start. The function is
3045 * called once with null for field and values at the end of each
3046 * entry.
3047 * @param ads connection to ads server
3048 * @param res Results to process
3049 * @param fn Function for processing each result
3050 * @param data_area user-defined area to pass to function
3052 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
3053 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
3054 void *data_area)
3056 LDAPMessage *msg;
3057 TALLOC_CTX *ctx;
3058 size_t converted_size;
3060 if (!(ctx = talloc_init("ads_process_results")))
3061 return;
3063 for (msg = ads_first_entry(ads, res); msg;
3064 msg = ads_next_entry(ads, msg)) {
3065 char *utf8_field;
3066 BerElement *b;
3068 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
3069 (LDAPMessage *)msg,&b);
3070 utf8_field;
3071 utf8_field=ldap_next_attribute(ads->ldap.ld,
3072 (LDAPMessage *)msg,b)) {
3073 struct berval **ber_vals;
3074 char **str_vals;
3075 char **utf8_vals;
3076 char *field;
3077 bool string;
3079 if (!pull_utf8_talloc(ctx, &field, utf8_field,
3080 &converted_size))
3082 DEBUG(0,("ads_process_results: "
3083 "pull_utf8_talloc failed: %s\n",
3084 strerror(errno)));
3087 string = fn(ads, field, NULL, data_area);
3089 if (string) {
3090 const char **p;
3092 utf8_vals = ldap_get_values(ads->ldap.ld,
3093 (LDAPMessage *)msg, field);
3094 p = discard_const_p(const char *, utf8_vals);
3095 str_vals = ads_pull_strvals(ctx, p);
3096 fn(ads, field, (void **) str_vals, data_area);
3097 ldap_value_free(utf8_vals);
3098 } else {
3099 ber_vals = ldap_get_values_len(ads->ldap.ld,
3100 (LDAPMessage *)msg, field);
3101 fn(ads, field, (void **) ber_vals, data_area);
3103 ldap_value_free_len(ber_vals);
3105 ldap_memfree(utf8_field);
3107 ber_free(b, 0);
3108 talloc_free_children(ctx);
3109 fn(ads, NULL, NULL, data_area); /* completed an entry */
3112 talloc_destroy(ctx);
3116 * count how many replies are in a LDAPMessage
3117 * @param ads connection to ads server
3118 * @param res Results to count
3119 * @return number of replies
3121 int ads_count_replies(ADS_STRUCT *ads, void *res)
3123 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3127 * pull the first entry from a ADS result
3128 * @param ads connection to ads server
3129 * @param res Results of search
3130 * @return first entry from result
3132 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3134 return ldap_first_entry(ads->ldap.ld, res);
3138 * pull the next entry from a ADS result
3139 * @param ads connection to ads server
3140 * @param res Results of search
3141 * @return next entry from result
3143 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3145 return ldap_next_entry(ads->ldap.ld, res);
3149 * pull the first message from a ADS result
3150 * @param ads connection to ads server
3151 * @param res Results of search
3152 * @return first message from result
3154 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3156 return ldap_first_message(ads->ldap.ld, res);
3160 * pull the next message from a ADS result
3161 * @param ads connection to ads server
3162 * @param res Results of search
3163 * @return next message from result
3165 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3167 return ldap_next_message(ads->ldap.ld, res);
3171 * pull a single string from a ADS result
3172 * @param ads connection to ads server
3173 * @param mem_ctx TALLOC_CTX to use for allocating result string
3174 * @param msg Results of search
3175 * @param field Attribute to retrieve
3176 * @return Result string in talloc context
3178 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3179 const char *field)
3181 char **values;
3182 char *ret = NULL;
3183 char *ux_string;
3184 size_t converted_size;
3186 values = ldap_get_values(ads->ldap.ld, msg, field);
3187 if (!values)
3188 return NULL;
3190 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3191 &converted_size))
3193 ret = ux_string;
3195 ldap_value_free(values);
3196 return ret;
3200 * pull an array of strings from a ADS result
3201 * @param ads connection to ads server
3202 * @param mem_ctx TALLOC_CTX to use for allocating result string
3203 * @param msg Results of search
3204 * @param field Attribute to retrieve
3205 * @return Result strings in talloc context
3207 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3208 LDAPMessage *msg, const char *field,
3209 size_t *num_values)
3211 char **values;
3212 char **ret = NULL;
3213 size_t i, converted_size;
3215 values = ldap_get_values(ads->ldap.ld, msg, field);
3216 if (!values)
3217 return NULL;
3219 *num_values = ldap_count_values(values);
3221 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3222 if (!ret) {
3223 ldap_value_free(values);
3224 return NULL;
3227 for (i=0;i<*num_values;i++) {
3228 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3229 &converted_size))
3231 ldap_value_free(values);
3232 return NULL;
3235 ret[i] = NULL;
3237 ldap_value_free(values);
3238 return ret;
3242 * pull an array of strings from a ADS result
3243 * (handle large multivalue attributes with range retrieval)
3244 * @param ads connection to ads server
3245 * @param mem_ctx TALLOC_CTX to use for allocating result string
3246 * @param msg Results of search
3247 * @param field Attribute to retrieve
3248 * @param current_strings strings returned by a previous call to this function
3249 * @param next_attribute The next query should ask for this attribute
3250 * @param num_values How many values did we get this time?
3251 * @param more_values Are there more values to get?
3252 * @return Result strings in talloc context
3254 char **ads_pull_strings_range(ADS_STRUCT *ads,
3255 TALLOC_CTX *mem_ctx,
3256 LDAPMessage *msg, const char *field,
3257 char **current_strings,
3258 const char **next_attribute,
3259 size_t *num_strings,
3260 bool *more_strings)
3262 char *attr;
3263 char *expected_range_attrib, *range_attr = NULL;
3264 BerElement *ptr = NULL;
3265 char **strings;
3266 char **new_strings;
3267 size_t num_new_strings;
3268 unsigned long int range_start;
3269 unsigned long int range_end;
3271 /* we might have been given the whole lot anyway */
3272 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3273 *more_strings = False;
3274 return strings;
3277 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3279 /* look for Range result */
3280 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3281 attr;
3282 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3283 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3284 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3285 range_attr = attr;
3286 break;
3288 ldap_memfree(attr);
3290 if (!range_attr) {
3291 ber_free(ptr, 0);
3292 /* nothing here - this field is just empty */
3293 *more_strings = False;
3294 return NULL;
3297 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3298 &range_start, &range_end) == 2) {
3299 *more_strings = True;
3300 } else {
3301 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3302 &range_start) == 1) {
3303 *more_strings = False;
3304 } else {
3305 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3306 range_attr));
3307 ldap_memfree(range_attr);
3308 *more_strings = False;
3309 return NULL;
3313 if ((*num_strings) != range_start) {
3314 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3315 " - aborting range retrieval\n",
3316 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3317 ldap_memfree(range_attr);
3318 *more_strings = False;
3319 return NULL;
3322 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3324 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3325 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3326 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3327 range_attr, (unsigned long int)range_end - range_start + 1,
3328 (unsigned long int)num_new_strings));
3329 ldap_memfree(range_attr);
3330 *more_strings = False;
3331 return NULL;
3334 strings = talloc_realloc(mem_ctx, current_strings, char *,
3335 *num_strings + num_new_strings);
3337 if (strings == NULL) {
3338 ldap_memfree(range_attr);
3339 *more_strings = False;
3340 return NULL;
3343 if (new_strings && num_new_strings) {
3344 memcpy(&strings[*num_strings], new_strings,
3345 sizeof(*new_strings) * num_new_strings);
3348 (*num_strings) += num_new_strings;
3350 if (*more_strings) {
3351 *next_attribute = talloc_asprintf(mem_ctx,
3352 "%s;range=%d-*",
3353 field,
3354 (int)*num_strings);
3356 if (!*next_attribute) {
3357 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3358 ldap_memfree(range_attr);
3359 *more_strings = False;
3360 return NULL;
3364 ldap_memfree(range_attr);
3366 return strings;
3370 * pull a single uint32_t from a ADS result
3371 * @param ads connection to ads server
3372 * @param msg Results of search
3373 * @param field Attribute to retrieve
3374 * @param v Pointer to int to store result
3375 * @return boolean indicating success
3377 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3378 uint32_t *v)
3380 char **values;
3382 values = ldap_get_values(ads->ldap.ld, msg, field);
3383 if (!values)
3384 return False;
3385 if (!values[0]) {
3386 ldap_value_free(values);
3387 return False;
3390 *v = atoi(values[0]);
3391 ldap_value_free(values);
3392 return True;
3396 * pull a single objectGUID from an ADS result
3397 * @param ads connection to ADS server
3398 * @param msg results of search
3399 * @param guid 37-byte area to receive text guid
3400 * @return boolean indicating success
3402 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3404 DATA_BLOB blob;
3405 NTSTATUS status;
3407 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3408 &blob)) {
3409 return false;
3412 status = GUID_from_ndr_blob(&blob, guid);
3413 talloc_free(blob.data);
3414 return NT_STATUS_IS_OK(status);
3419 * pull a single struct dom_sid from a ADS result
3420 * @param ads connection to ads server
3421 * @param msg Results of search
3422 * @param field Attribute to retrieve
3423 * @param sid Pointer to sid to store result
3424 * @return boolean indicating success
3426 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3427 struct dom_sid *sid)
3429 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3433 * pull an array of struct dom_sids from a ADS result
3434 * @param ads connection to ads server
3435 * @param mem_ctx TALLOC_CTX for allocating sid array
3436 * @param msg Results of search
3437 * @param field Attribute to retrieve
3438 * @param sids pointer to sid array to allocate
3439 * @return the count of SIDs pulled
3441 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3442 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3444 struct berval **values;
3445 int count, i;
3447 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3449 if (!values)
3450 return 0;
3452 for (i=0; values[i]; i++)
3453 /* nop */ ;
3455 if (i) {
3456 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3457 if (!(*sids)) {
3458 ldap_value_free_len(values);
3459 return 0;
3461 } else {
3462 (*sids) = NULL;
3465 count = 0;
3466 for (i=0; values[i]; i++) {
3467 ssize_t ret;
3468 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3469 values[i]->bv_len, &(*sids)[count]);
3470 if (ret != -1) {
3471 struct dom_sid_buf buf;
3472 DBG_DEBUG("pulling SID: %s\n",
3473 dom_sid_str_buf(&(*sids)[count], &buf));
3474 count++;
3478 ldap_value_free_len(values);
3479 return count;
3483 * pull a struct security_descriptor from a ADS result
3484 * @param ads connection to ads server
3485 * @param mem_ctx TALLOC_CTX for allocating sid array
3486 * @param msg Results of search
3487 * @param field Attribute to retrieve
3488 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3489 * @return boolean indicating success
3491 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3492 LDAPMessage *msg, const char *field,
3493 struct security_descriptor **sd)
3495 struct berval **values;
3496 bool ret = true;
3498 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3500 if (!values) return false;
3502 if (values[0]) {
3503 NTSTATUS status;
3504 status = unmarshall_sec_desc(mem_ctx,
3505 (uint8_t *)values[0]->bv_val,
3506 values[0]->bv_len, sd);
3507 if (!NT_STATUS_IS_OK(status)) {
3508 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3509 nt_errstr(status)));
3510 ret = false;
3514 ldap_value_free_len(values);
3515 return ret;
3519 * in order to support usernames longer than 21 characters we need to
3520 * use both the sAMAccountName and the userPrincipalName attributes
3521 * It seems that not all users have the userPrincipalName attribute set
3523 * @param ads connection to ads server
3524 * @param mem_ctx TALLOC_CTX for allocating sid array
3525 * @param msg Results of search
3526 * @return the username
3528 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3529 LDAPMessage *msg)
3531 #if 0 /* JERRY */
3532 char *ret, *p;
3534 /* lookup_name() only works on the sAMAccountName to
3535 returning the username portion of userPrincipalName
3536 breaks winbindd_getpwnam() */
3538 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3539 if (ret && (p = strchr_m(ret, '@'))) {
3540 *p = 0;
3541 return ret;
3543 #endif
3544 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3549 * find the update serial number - this is the core of the ldap cache
3550 * @param ads connection to ads server
3551 * @param ads connection to ADS server
3552 * @param usn Pointer to retrieved update serial number
3553 * @return status of search
3555 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3557 const char *attrs[] = {"highestCommittedUSN", NULL};
3558 ADS_STATUS status;
3559 LDAPMessage *res;
3561 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3562 if (!ADS_ERR_OK(status))
3563 return status;
3565 if (ads_count_replies(ads, res) != 1) {
3566 ads_msgfree(ads, res);
3567 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3570 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3571 ads_msgfree(ads, res);
3572 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3575 ads_msgfree(ads, res);
3576 return ADS_SUCCESS;
3579 /* parse a ADS timestring - typical string is
3580 '20020917091222.0Z0' which means 09:12.22 17th September
3581 2002, timezone 0 */
3582 static time_t ads_parse_time(const char *str)
3584 struct tm tm;
3586 ZERO_STRUCT(tm);
3588 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3589 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3590 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3591 return 0;
3593 tm.tm_year -= 1900;
3594 tm.tm_mon -= 1;
3596 return timegm(&tm);
3599 /********************************************************************
3600 ********************************************************************/
3602 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3604 const char *attrs[] = {"currentTime", NULL};
3605 ADS_STATUS status;
3606 LDAPMessage *res;
3607 char *timestr;
3608 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3609 ADS_STRUCT *ads_s = ads;
3611 /* establish a new ldap tcp session if necessary */
3613 if ( !ads->ldap.ld ) {
3615 * ADS_STRUCT may be being reused after a
3616 * DC lookup, so ads->ldap.ss may already have a
3617 * good address. If not, re-initialize the passed-in
3618 * ADS_STRUCT with the given server.XXXX parameters.
3620 * Note that this doesn't depend on
3621 * ads->server.ldap_server != NULL,
3622 * as the case where ads->server.ldap_server==NULL and
3623 * ads->ldap.ss != zero_address is precisely the DC
3624 * lookup case where ads->ldap.ss was found by going
3625 * through ads_find_dc() again we want to avoid repeating.
3627 if (is_zero_addr(&ads->ldap.ss)) {
3628 ads_s = ads_init(tmp_ctx,
3629 ads->server.realm,
3630 ads->server.workgroup,
3631 ads->server.ldap_server,
3632 ADS_SASL_PLAIN );
3633 if (ads_s == NULL) {
3634 status = ADS_ERROR(LDAP_NO_MEMORY);
3635 goto done;
3640 * Reset ads->config.flags as it can contain the flags
3641 * returned by the previous CLDAP ping when reusing the struct.
3643 ads_s->config.flags = 0;
3645 status = ads_connect_simple_anon(ads_s);
3646 if ( !ADS_ERR_OK(status))
3647 goto done;
3650 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3651 if (!ADS_ERR_OK(status)) {
3652 goto done;
3655 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3656 if (!timestr) {
3657 ads_msgfree(ads_s, res);
3658 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3659 goto done;
3662 /* but save the time and offset in the original ADS_STRUCT */
3664 ads->config.current_time = ads_parse_time(timestr);
3666 if (ads->config.current_time != 0) {
3667 ads->config.time_offset = ads->config.current_time - time(NULL);
3668 DBG_INFO("server time offset is %d seconds\n",
3669 ads->config.time_offset);
3670 } else {
3671 ads->config.time_offset = 0;
3674 DBG_INFO("server time offset is %d seconds\n",
3675 ads->config.time_offset);
3677 ads_msgfree(ads, res);
3679 status = ADS_SUCCESS;
3681 done:
3682 TALLOC_FREE(tmp_ctx);
3684 return status;
3687 /********************************************************************
3688 ********************************************************************/
3690 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3692 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3693 const char *attrs[] = {"domainFunctionality", NULL};
3694 ADS_STATUS status;
3695 LDAPMessage *res;
3696 ADS_STRUCT *ads_s = ads;
3698 *val = DS_DOMAIN_FUNCTION_2000;
3700 /* establish a new ldap tcp session if necessary */
3702 if ( !ads->ldap.ld ) {
3704 * ADS_STRUCT may be being reused after a
3705 * DC lookup, so ads->ldap.ss may already have a
3706 * good address. If not, re-initialize the passed-in
3707 * ADS_STRUCT with the given server.XXXX parameters.
3709 * Note that this doesn't depend on
3710 * ads->server.ldap_server != NULL,
3711 * as the case where ads->server.ldap_server==NULL and
3712 * ads->ldap.ss != zero_address is precisely the DC
3713 * lookup case where ads->ldap.ss was found by going
3714 * through ads_find_dc() again we want to avoid repeating.
3716 if (is_zero_addr(&ads->ldap.ss)) {
3717 ads_s = ads_init(tmp_ctx,
3718 ads->server.realm,
3719 ads->server.workgroup,
3720 ads->server.ldap_server,
3721 ADS_SASL_PLAIN );
3722 if (ads_s == NULL ) {
3723 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3724 goto done;
3729 * Reset ads->config.flags as it can contain the flags
3730 * returned by the previous CLDAP ping when reusing the struct.
3732 ads_s->config.flags = 0;
3734 status = ads_connect_simple_anon(ads_s);
3735 if ( !ADS_ERR_OK(status))
3736 goto done;
3739 /* If the attribute does not exist assume it is a Windows 2000
3740 functional domain */
3742 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3743 if (!ADS_ERR_OK(status)) {
3744 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3745 status = ADS_SUCCESS;
3747 goto done;
3750 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3751 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3753 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3756 ads_msgfree(ads_s, res);
3758 done:
3759 TALLOC_FREE(tmp_ctx);
3761 return status;
3765 * find the domain sid for our domain
3766 * @param ads connection to ads server
3767 * @param sid Pointer to domain sid
3768 * @return status of search
3770 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3772 const char *attrs[] = {"objectSid", NULL};
3773 LDAPMessage *res;
3774 ADS_STATUS rc;
3776 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3777 attrs, &res);
3778 if (!ADS_ERR_OK(rc)) return rc;
3779 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3780 ads_msgfree(ads, res);
3781 return ADS_ERROR_SYSTEM(ENOENT);
3783 ads_msgfree(ads, res);
3785 return ADS_SUCCESS;
3789 * find our site name
3790 * @param ads connection to ads server
3791 * @param mem_ctx Pointer to talloc context
3792 * @param site_name Pointer to the sitename
3793 * @return status of search
3795 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3797 ADS_STATUS status;
3798 LDAPMessage *res;
3799 const char *dn, *service_name;
3800 const char *attrs[] = { "dsServiceName", NULL };
3802 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3803 if (!ADS_ERR_OK(status)) {
3804 return status;
3807 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3808 if (service_name == NULL) {
3809 ads_msgfree(ads, res);
3810 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3813 ads_msgfree(ads, res);
3815 /* go up three levels */
3816 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3817 if (dn == NULL) {
3818 return ADS_ERROR(LDAP_NO_MEMORY);
3821 *site_name = talloc_strdup(mem_ctx, dn);
3822 if (*site_name == NULL) {
3823 return ADS_ERROR(LDAP_NO_MEMORY);
3826 return status;
3828 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3833 * find the site dn where a machine resides
3834 * @param ads connection to ads server
3835 * @param mem_ctx Pointer to talloc context
3836 * @param computer_name name of the machine
3837 * @param site_name Pointer to the sitename
3838 * @return status of search
3840 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3842 ADS_STATUS status;
3843 LDAPMessage *res;
3844 const char *parent, *filter;
3845 char *config_context = NULL;
3846 char *dn;
3848 /* shortcut a query */
3849 if (strequal(computer_name, ads->config.ldap_server_name)) {
3850 return ads_site_dn(ads, mem_ctx, site_dn);
3853 status = ads_config_path(ads, mem_ctx, &config_context);
3854 if (!ADS_ERR_OK(status)) {
3855 return status;
3858 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3859 if (filter == NULL) {
3860 return ADS_ERROR(LDAP_NO_MEMORY);
3863 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3864 filter, NULL, &res);
3865 if (!ADS_ERR_OK(status)) {
3866 return status;
3869 if (ads_count_replies(ads, res) != 1) {
3870 ads_msgfree(ads, res);
3871 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3874 dn = ads_get_dn(ads, mem_ctx, res);
3875 if (dn == NULL) {
3876 ads_msgfree(ads, res);
3877 return ADS_ERROR(LDAP_NO_MEMORY);
3880 /* go up three levels */
3881 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3882 if (parent == NULL) {
3883 ads_msgfree(ads, res);
3884 TALLOC_FREE(dn);
3885 return ADS_ERROR(LDAP_NO_MEMORY);
3888 *site_dn = talloc_strdup(mem_ctx, parent);
3889 if (*site_dn == NULL) {
3890 ads_msgfree(ads, res);
3891 TALLOC_FREE(dn);
3892 return ADS_ERROR(LDAP_NO_MEMORY);
3895 TALLOC_FREE(dn);
3896 ads_msgfree(ads, res);
3898 return status;
3902 * get the upn suffixes for a domain
3903 * @param ads connection to ads server
3904 * @param mem_ctx Pointer to talloc context
3905 * @param suffixes Pointer to an array of suffixes
3906 * @param num_suffixes Pointer to the number of suffixes
3907 * @return status of search
3909 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3911 ADS_STATUS status;
3912 LDAPMessage *res;
3913 const char *base;
3914 char *config_context = NULL;
3915 const char *attrs[] = { "uPNSuffixes", NULL };
3917 status = ads_config_path(ads, mem_ctx, &config_context);
3918 if (!ADS_ERR_OK(status)) {
3919 return status;
3922 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3923 if (base == NULL) {
3924 return ADS_ERROR(LDAP_NO_MEMORY);
3927 status = ads_search_dn(ads, &res, base, attrs);
3928 if (!ADS_ERR_OK(status)) {
3929 return status;
3932 if (ads_count_replies(ads, res) != 1) {
3933 ads_msgfree(ads, res);
3934 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3937 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3938 if ((*suffixes) == NULL) {
3939 ads_msgfree(ads, res);
3940 return ADS_ERROR(LDAP_NO_MEMORY);
3943 ads_msgfree(ads, res);
3945 return status;
3949 * get the joinable ous for a domain
3950 * @param ads connection to ads server
3951 * @param mem_ctx Pointer to talloc context
3952 * @param ous Pointer to an array of ous
3953 * @param num_ous Pointer to the number of ous
3954 * @return status of search
3956 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3957 TALLOC_CTX *mem_ctx,
3958 char ***ous,
3959 size_t *num_ous)
3961 ADS_STATUS status;
3962 LDAPMessage *res = NULL;
3963 LDAPMessage *msg = NULL;
3964 const char *attrs[] = { "dn", NULL };
3965 int count = 0;
3967 status = ads_search(ads, &res,
3968 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3969 attrs);
3970 if (!ADS_ERR_OK(status)) {
3971 return status;
3974 count = ads_count_replies(ads, res);
3975 if (count < 1) {
3976 ads_msgfree(ads, res);
3977 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3980 for (msg = ads_first_entry(ads, res); msg;
3981 msg = ads_next_entry(ads, msg)) {
3982 const char **p = discard_const_p(const char *, *ous);
3983 char *dn = NULL;
3985 dn = ads_get_dn(ads, talloc_tos(), msg);
3986 if (!dn) {
3987 ads_msgfree(ads, res);
3988 return ADS_ERROR(LDAP_NO_MEMORY);
3991 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3992 TALLOC_FREE(dn);
3993 ads_msgfree(ads, res);
3994 return ADS_ERROR(LDAP_NO_MEMORY);
3997 TALLOC_FREE(dn);
3998 *ous = discard_const_p(char *, p);
4001 ads_msgfree(ads, res);
4003 return status;
4008 * pull a struct dom_sid from an extended dn string
4009 * @param mem_ctx TALLOC_CTX
4010 * @param extended_dn string
4011 * @param flags string type of extended_dn
4012 * @param sid pointer to a struct dom_sid
4013 * @return NT_STATUS_OK on success,
4014 * NT_INVALID_PARAMETER on error,
4015 * NT_STATUS_NOT_FOUND if no SID present
4017 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
4018 const char *extended_dn,
4019 enum ads_extended_dn_flags flags,
4020 struct dom_sid *sid)
4022 char *p, *q, *dn;
4024 if (!extended_dn) {
4025 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4028 /* otherwise extended_dn gets stripped off */
4029 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
4030 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4033 * ADS_EXTENDED_DN_HEX_STRING:
4034 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4036 * ADS_EXTENDED_DN_STRING (only with w2k3):
4037 * <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
4039 * Object with no SID, such as an Exchange Public Folder
4040 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4043 p = strchr(dn, ';');
4044 if (!p) {
4045 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4048 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
4049 DEBUG(5,("No SID present in extended dn\n"));
4050 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
4053 p += strlen(";<SID=");
4055 q = strchr(p, '>');
4056 if (!q) {
4057 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4060 *q = '\0';
4062 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
4064 switch (flags) {
4066 case ADS_EXTENDED_DN_STRING:
4067 if (!string_to_sid(sid, p)) {
4068 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4070 break;
4071 case ADS_EXTENDED_DN_HEX_STRING: {
4072 ssize_t ret;
4073 fstring buf;
4074 size_t buf_len;
4076 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
4077 if (buf_len == 0) {
4078 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4081 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
4082 if (ret == -1) {
4083 DEBUG(10,("failed to parse sid\n"));
4084 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4086 break;
4088 default:
4089 DEBUG(10,("unknown extended dn format\n"));
4090 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4093 return ADS_ERROR_NT(NT_STATUS_OK);
4096 /********************************************************************
4097 ********************************************************************/
4099 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4101 LDAPMessage *res = NULL;
4102 ADS_STATUS status;
4103 int count = 0;
4104 char *name = NULL;
4106 status = ads_find_machine_acct(ads, &res, machine_name);
4107 if (!ADS_ERR_OK(status)) {
4108 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4109 lp_netbios_name()));
4110 goto out;
4113 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4114 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4115 goto out;
4118 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4119 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4122 out:
4123 ads_msgfree(ads, res);
4125 return name;
4128 #if 0
4130 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4133 * Join a machine to a realm
4134 * Creates the machine account and sets the machine password
4135 * @param ads connection to ads server
4136 * @param machine name of host to add
4137 * @param org_unit Organizational unit to place machine in
4138 * @return status of join
4140 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4141 uint32_t account_type, const char *org_unit)
4143 ADS_STATUS status;
4144 LDAPMessage *res = NULL;
4145 char *machine;
4147 /* machine name must be lowercase */
4148 machine = SMB_STRDUP(machine_name);
4149 strlower_m(machine);
4152 status = ads_find_machine_acct(ads, (void **)&res, machine);
4153 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4154 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4155 status = ads_leave_realm(ads, machine);
4156 if (!ADS_ERR_OK(status)) {
4157 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4158 machine, ads->config.realm));
4159 return status;
4163 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4164 if (!ADS_ERR_OK(status)) {
4165 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4166 SAFE_FREE(machine);
4167 return status;
4170 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4171 if (!ADS_ERR_OK(status)) {
4172 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4173 SAFE_FREE(machine);
4174 return status;
4177 SAFE_FREE(machine);
4178 ads_msgfree(ads, res);
4180 return status;
4182 #endif
4185 * Delete a machine from the realm
4186 * @param ads connection to ads server
4187 * @param hostname Machine to remove
4188 * @return status of delete
4190 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4192 ADS_STATUS status;
4193 void *msg;
4194 LDAPMessage *res;
4195 char *hostnameDN, *host;
4196 int rc;
4197 LDAPControl ldap_control;
4198 LDAPControl * pldap_control[2] = {NULL, NULL};
4200 pldap_control[0] = &ldap_control;
4201 memset(&ldap_control, 0, sizeof(LDAPControl));
4202 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4204 /* hostname must be lowercase */
4205 host = SMB_STRDUP(hostname);
4206 if (!strlower_m(host)) {
4207 SAFE_FREE(host);
4208 return ADS_ERROR_SYSTEM(EINVAL);
4211 status = ads_find_machine_acct(ads, &res, host);
4212 if (!ADS_ERR_OK(status)) {
4213 DEBUG(0, ("Host account for %s does not exist.\n", host));
4214 SAFE_FREE(host);
4215 return status;
4218 msg = ads_first_entry(ads, res);
4219 if (!msg) {
4220 SAFE_FREE(host);
4221 return ADS_ERROR_SYSTEM(ENOENT);
4224 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4225 if (hostnameDN == NULL) {
4226 SAFE_FREE(host);
4227 return ADS_ERROR_SYSTEM(ENOENT);
4230 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4231 if (rc) {
4232 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4233 }else {
4234 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4237 if (rc != LDAP_SUCCESS) {
4238 const char *attrs[] = { "cn", NULL };
4239 LDAPMessage *msg_sub;
4241 /* we only search with scope ONE, we do not expect any further
4242 * objects to be created deeper */
4244 status = ads_do_search_retry(ads, hostnameDN,
4245 LDAP_SCOPE_ONELEVEL,
4246 "(objectclass=*)", attrs, &res);
4248 if (!ADS_ERR_OK(status)) {
4249 SAFE_FREE(host);
4250 TALLOC_FREE(hostnameDN);
4251 return status;
4254 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4255 msg_sub = ads_next_entry(ads, msg_sub)) {
4257 char *dn = NULL;
4259 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4260 SAFE_FREE(host);
4261 TALLOC_FREE(hostnameDN);
4262 return ADS_ERROR(LDAP_NO_MEMORY);
4265 status = ads_del_dn(ads, dn);
4266 if (!ADS_ERR_OK(status)) {
4267 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4268 SAFE_FREE(host);
4269 TALLOC_FREE(dn);
4270 TALLOC_FREE(hostnameDN);
4271 return status;
4274 TALLOC_FREE(dn);
4277 /* there should be no subordinate objects anymore */
4278 status = ads_do_search_retry(ads, hostnameDN,
4279 LDAP_SCOPE_ONELEVEL,
4280 "(objectclass=*)", attrs, &res);
4282 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4283 SAFE_FREE(host);
4284 TALLOC_FREE(hostnameDN);
4285 return status;
4288 /* delete hostnameDN now */
4289 status = ads_del_dn(ads, hostnameDN);
4290 if (!ADS_ERR_OK(status)) {
4291 SAFE_FREE(host);
4292 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4293 TALLOC_FREE(hostnameDN);
4294 return status;
4298 TALLOC_FREE(hostnameDN);
4300 status = ads_find_machine_acct(ads, &res, host);
4301 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4302 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4303 DEBUG(3, ("Failed to remove host account.\n"));
4304 SAFE_FREE(host);
4305 return status;
4308 SAFE_FREE(host);
4309 return ADS_SUCCESS;
4313 * pull all token-sids from an LDAP dn
4314 * @param ads connection to ads server
4315 * @param mem_ctx TALLOC_CTX for allocating sid array
4316 * @param dn of LDAP object
4317 * @param user_sid pointer to struct dom_sid (objectSid)
4318 * @param primary_group_sid pointer to struct dom_sid (self composed)
4319 * @param sids pointer to sid array to allocate
4320 * @param num_sids counter of SIDs pulled
4321 * @return status of token query
4323 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4324 TALLOC_CTX *mem_ctx,
4325 const char *dn,
4326 struct dom_sid *user_sid,
4327 struct dom_sid *primary_group_sid,
4328 struct dom_sid **sids,
4329 size_t *num_sids)
4331 ADS_STATUS status;
4332 LDAPMessage *res = NULL;
4333 int count = 0;
4334 size_t tmp_num_sids;
4335 struct dom_sid *tmp_sids;
4336 struct dom_sid tmp_user_sid;
4337 struct dom_sid tmp_primary_group_sid;
4338 uint32_t pgid;
4339 const char *attrs[] = {
4340 "objectSid",
4341 "tokenGroups",
4342 "primaryGroupID",
4343 NULL
4346 status = ads_search_retry_dn(ads, &res, dn, attrs);
4347 if (!ADS_ERR_OK(status)) {
4348 return status;
4351 count = ads_count_replies(ads, res);
4352 if (count != 1) {
4353 ads_msgfree(ads, res);
4354 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4357 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4358 ads_msgfree(ads, res);
4359 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4362 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4363 ads_msgfree(ads, res);
4364 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4368 /* hack to compose the primary group sid without knowing the
4369 * domsid */
4371 struct dom_sid domsid;
4373 sid_copy(&domsid, &tmp_user_sid);
4375 if (!sid_split_rid(&domsid, NULL)) {
4376 ads_msgfree(ads, res);
4377 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4380 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4381 ads_msgfree(ads, res);
4382 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4386 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4388 if (tmp_num_sids == 0 || !tmp_sids) {
4389 ads_msgfree(ads, res);
4390 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4393 if (num_sids) {
4394 *num_sids = tmp_num_sids;
4397 if (sids) {
4398 *sids = tmp_sids;
4401 if (user_sid) {
4402 *user_sid = tmp_user_sid;
4405 if (primary_group_sid) {
4406 *primary_group_sid = tmp_primary_group_sid;
4409 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4411 ads_msgfree(ads, res);
4412 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4416 * Find a sAMAccountName in LDAP
4417 * @param ads connection to ads server
4418 * @param mem_ctx TALLOC_CTX for allocating sid array
4419 * @param samaccountname to search
4420 * @param uac_ret uint32_t pointer userAccountControl attribute value
4421 * @param dn_ret pointer to dn
4422 * @return status of token query
4424 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4425 TALLOC_CTX *mem_ctx,
4426 const char *samaccountname,
4427 uint32_t *uac_ret,
4428 const char **dn_ret)
4430 ADS_STATUS status;
4431 const char *attrs[] = { "userAccountControl", NULL };
4432 const char *filter;
4433 LDAPMessage *res = NULL;
4434 char *dn = NULL;
4435 uint32_t uac = 0;
4437 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4438 samaccountname);
4439 if (filter == NULL) {
4440 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4441 goto out;
4444 status = ads_do_search_all(ads, ads->config.bind_path,
4445 LDAP_SCOPE_SUBTREE,
4446 filter, attrs, &res);
4448 if (!ADS_ERR_OK(status)) {
4449 goto out;
4452 if (ads_count_replies(ads, res) != 1) {
4453 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4454 goto out;
4457 dn = ads_get_dn(ads, talloc_tos(), res);
4458 if (dn == NULL) {
4459 status = ADS_ERROR(LDAP_NO_MEMORY);
4460 goto out;
4463 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4464 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4465 goto out;
4468 if (uac_ret) {
4469 *uac_ret = uac;
4472 if (dn_ret) {
4473 *dn_ret = talloc_strdup(mem_ctx, dn);
4474 if (!*dn_ret) {
4475 status = ADS_ERROR(LDAP_NO_MEMORY);
4476 goto out;
4479 out:
4480 TALLOC_FREE(dn);
4481 ads_msgfree(ads, res);
4483 return status;
4487 * find our configuration path
4488 * @param ads connection to ads server
4489 * @param mem_ctx Pointer to talloc context
4490 * @param config_path Pointer to the config path
4491 * @return status of search
4493 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4494 TALLOC_CTX *mem_ctx,
4495 char **config_path)
4497 ADS_STATUS status;
4498 LDAPMessage *res = NULL;
4499 const char *config_context = NULL;
4500 const char *attrs[] = { "configurationNamingContext", NULL };
4502 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4503 "(objectclass=*)", attrs, &res);
4504 if (!ADS_ERR_OK(status)) {
4505 return status;
4508 config_context = ads_pull_string(ads, mem_ctx, res,
4509 "configurationNamingContext");
4510 ads_msgfree(ads, res);
4511 if (!config_context) {
4512 return ADS_ERROR(LDAP_NO_MEMORY);
4515 if (config_path) {
4516 *config_path = talloc_strdup(mem_ctx, config_context);
4517 if (!*config_path) {
4518 return ADS_ERROR(LDAP_NO_MEMORY);
4522 return ADS_ERROR(LDAP_SUCCESS);
4526 * find the displayName of an extended right
4527 * @param ads connection to ads server
4528 * @param config_path The config path
4529 * @param mem_ctx Pointer to talloc context
4530 * @param GUID struct of the rightsGUID
4531 * @return status of search
4533 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4534 const char *config_path,
4535 TALLOC_CTX *mem_ctx,
4536 const struct GUID *rights_guid)
4538 ADS_STATUS rc;
4539 LDAPMessage *res = NULL;
4540 char *expr = NULL;
4541 const char *attrs[] = { "displayName", NULL };
4542 const char *result = NULL;
4543 const char *path;
4545 if (!ads || !mem_ctx || !rights_guid) {
4546 goto done;
4549 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4550 GUID_string(mem_ctx, rights_guid));
4551 if (!expr) {
4552 goto done;
4555 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4556 if (!path) {
4557 goto done;
4560 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4561 expr, attrs, &res);
4562 if (!ADS_ERR_OK(rc)) {
4563 goto done;
4566 if (ads_count_replies(ads, res) != 1) {
4567 goto done;
4570 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4572 done:
4573 ads_msgfree(ads, res);
4574 return result;
4578 * verify or build and verify an account ou
4579 * @param mem_ctx Pointer to talloc context
4580 * @param ads connection to ads server
4581 * @param account_ou
4582 * @return status of search
4585 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4586 ADS_STRUCT *ads,
4587 const char **account_ou)
4589 char **exploded_dn;
4590 const char *name;
4591 char *ou_string;
4593 if (account_ou == NULL) {
4594 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4597 if (*account_ou != NULL) {
4598 exploded_dn = ldap_explode_dn(*account_ou, 0);
4599 if (exploded_dn) {
4600 ldap_value_free(exploded_dn);
4601 return ADS_SUCCESS;
4605 ou_string = ads_ou_string(ads, *account_ou);
4606 if (!ou_string) {
4607 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4610 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4611 ads->config.bind_path);
4612 SAFE_FREE(ou_string);
4614 if (!name) {
4615 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4618 exploded_dn = ldap_explode_dn(name, 0);
4619 if (!exploded_dn) {
4620 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4622 ldap_value_free(exploded_dn);
4624 *account_ou = name;
4625 return ADS_SUCCESS;
4628 #endif