ctdb-server: Clean up connection tracking functions
[samba4-gss.git] / source3 / libads / ldap.c
blob6fad112ca0068eb22cbeb530ffdd42210de23a0c
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "ads.h"
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/tsocket/tsocket.h"
29 #include "../lib/addns/dnsquery.h"
30 #include "../libds/common/flags.h"
31 #include "smbldap.h"
32 #include "../libcli/security/security.h"
33 #include "../librpc/gen_ndr/netlogon.h"
34 #include "lib/param/loadparm.h"
35 #include "libsmb/namequery.h"
36 #include "../librpc/gen_ndr/ndr_ads.h"
37 #include "auth/credentials/credentials.h"
38 #include "passdb.h"
40 #ifdef HAVE_LDAP
42 /**
43 * @file ldap.c
44 * @brief basic ldap client-side routines for ads server communications
46 * The routines contained here should do the necessary ldap calls for
47 * ads setups.
49 * Important note: attribute names passed into ads_ routines must
50 * already be in UTF-8 format. We do not convert them because in almost
51 * all cases, they are just ascii (which is represented with the same
52 * codepoints in UTF-8). This may have to change at some point
53 **/
56 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
58 static SIG_ATOMIC_T gotalarm;
60 /***************************************************************
61 Signal function to tell us we timed out.
62 ****************************************************************/
64 static void gotalarm_sig(int signum)
66 gotalarm = 1;
69 LDAP *ldap_open_with_timeout(const char *server,
70 struct sockaddr_storage *ss,
71 int port, unsigned int to)
73 LDAP *ldp = NULL;
74 int ldap_err;
75 char *uri;
77 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
78 "%u seconds\n", server, port, to));
80 if (to) {
81 /* Setup timeout */
82 gotalarm = 0;
83 CatchSignal(SIGALRM, gotalarm_sig);
84 alarm(to);
85 /* End setup timeout. */
88 if ( strchr_m(server, ':') ) {
89 /* IPv6 URI */
90 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
91 } else {
92 /* IPv4 URI */
93 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
95 if (uri == NULL) {
96 return NULL;
99 #ifdef HAVE_LDAP_INIT_FD
101 int fd = -1;
102 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
103 unsigned timeout_ms = 1000 * to;
105 status = open_socket_out(ss, port, timeout_ms, &fd);
106 if (!NT_STATUS_IS_OK(status)) {
107 DEBUG(3, ("open_socket_out: failed to open socket\n"));
108 return NULL;
111 /* define LDAP_PROTO_TCP from openldap.h if required */
112 #ifndef LDAP_PROTO_TCP
113 #define LDAP_PROTO_TCP 1
114 #endif
115 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
117 #elif defined(HAVE_LDAP_INITIALIZE)
118 ldap_err = ldap_initialize(&ldp, uri);
119 #else
120 ldp = ldap_open(server, port);
121 if (ldp != NULL) {
122 ldap_err = LDAP_SUCCESS;
123 } else {
124 ldap_err = LDAP_OTHER;
126 #endif
127 if (ldap_err != LDAP_SUCCESS) {
128 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
129 uri, ldap_err2string(ldap_err)));
130 } else {
131 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
134 if (to) {
135 /* Teardown timeout. */
136 alarm(0);
137 CatchSignal(SIGALRM, SIG_IGN);
140 return ldp;
143 static int ldap_search_with_timeout(LDAP *ld,
144 LDAP_CONST char *base,
145 int scope,
146 LDAP_CONST char *filter,
147 char **attrs,
148 int attrsonly,
149 LDAPControl **sctrls,
150 LDAPControl **cctrls,
151 int sizelimit,
152 LDAPMessage **res )
154 int to = lp_ldap_timeout();
155 struct timeval timeout;
156 struct timeval *timeout_ptr = NULL;
157 int result;
159 DBG_DEBUG("ldap_search: base => [%s], filter => [%s], scope => [%d]\n",
160 base,
161 filter,
162 scope);
164 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
165 gotalarm = 0;
167 if (to) {
168 timeout.tv_sec = to;
169 timeout.tv_usec = 0;
170 timeout_ptr = &timeout;
172 /* Setup alarm timeout. */
173 CatchSignal(SIGALRM, gotalarm_sig);
174 /* Make the alarm time one second beyond
175 the timeout we're setting for the
176 remote search timeout, to allow that
177 to fire in preference. */
178 alarm(to+1);
179 /* End setup timeout. */
183 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
184 attrsonly, sctrls, cctrls, timeout_ptr,
185 sizelimit, res);
187 if (to) {
188 /* Teardown alarm timeout. */
189 CatchSignal(SIGALRM, SIG_IGN);
190 alarm(0);
193 if (gotalarm != 0)
194 return LDAP_TIMELIMIT_EXCEEDED;
197 * A bug in OpenLDAP means ldap_search_ext_s can return
198 * LDAP_SUCCESS but with a NULL res pointer. Cope with
199 * this. See bug #6279 for details. JRA.
202 if (*res == NULL) {
203 return LDAP_TIMELIMIT_EXCEEDED;
206 return result;
209 /**********************************************
210 Do client and server sitename match ?
211 **********************************************/
213 bool ads_sitename_match(ADS_STRUCT *ads)
215 if (ads->config.server_site_name == NULL &&
216 ads->config.client_site_name == NULL ) {
217 DEBUG(10,("ads_sitename_match: both null\n"));
218 return True;
220 if (ads->config.server_site_name &&
221 ads->config.client_site_name &&
222 strequal(ads->config.server_site_name,
223 ads->config.client_site_name)) {
224 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
225 return True;
227 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
228 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
229 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
230 return False;
233 /**********************************************
234 Is this the closest DC ?
235 **********************************************/
237 bool ads_closest_dc(ADS_STRUCT *ads)
239 if (ads->config.flags & NBT_SERVER_CLOSEST) {
240 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
241 return True;
244 /* not sure if this can ever happen */
245 if (ads_sitename_match(ads)) {
246 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
247 return True;
250 if (ads->config.client_site_name == NULL) {
251 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
252 return True;
255 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
256 ads->config.ldap_server_name));
258 return False;
261 static bool ads_fill_cldap_reply(ADS_STRUCT *ads,
262 bool gc,
263 const struct sockaddr_storage *ss,
264 const struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply)
266 TALLOC_CTX *frame = talloc_stackframe();
267 bool ret = false;
268 char addr[INET6_ADDRSTRLEN];
269 ADS_STATUS status;
270 char *dn;
272 print_sockaddr(addr, sizeof(addr), ss);
274 /* Check the CLDAP reply flags */
276 if (!(cldap_reply->server_type & NBT_SERVER_LDAP)) {
277 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
278 addr);
279 ret = false;
280 goto out;
283 /* Fill in the ads->config values */
285 ADS_TALLOC_CONST_FREE(ads->config.workgroup);
286 ADS_TALLOC_CONST_FREE(ads->config.realm);
287 ADS_TALLOC_CONST_FREE(ads->config.bind_path);
288 ADS_TALLOC_CONST_FREE(ads->config.ldap_server_name);
289 ADS_TALLOC_CONST_FREE(ads->config.server_site_name);
290 ADS_TALLOC_CONST_FREE(ads->config.client_site_name);
292 if (!check_cldap_reply_required_flags(cldap_reply->server_type,
293 ads->config.flags)) {
294 ret = false;
295 goto out;
298 ads->config.ldap_server_name = talloc_strdup(ads,
299 cldap_reply->pdc_dns_name);
300 if (ads->config.ldap_server_name == NULL) {
301 DBG_WARNING("Out of memory\n");
302 ret = false;
303 goto out;
306 ads->config.workgroup = talloc_strdup(ads, cldap_reply->domain_name);
307 if (ads->config.workgroup == NULL) {
308 DBG_WARNING("Out of memory\n");
309 ret = false;
310 goto out;
313 ads->config.realm = talloc_asprintf_strupper_m(ads,
314 "%s",
315 cldap_reply->dns_domain);
316 if (ads->config.realm == NULL) {
317 DBG_WARNING("Out of memory\n");
318 ret = false;
319 goto out;
322 status = ads_build_dn(ads->config.realm, ads, &dn);
323 if (!ADS_ERR_OK(status)) {
324 DBG_DEBUG("Failed to build bind path: %s\n",
325 ads_errstr(status));
326 ret = false;
327 goto out;
329 ads->config.bind_path = dn;
331 if (*cldap_reply->server_site) {
332 ads->config.server_site_name =
333 talloc_strdup(ads, cldap_reply->server_site);
334 if (ads->config.server_site_name == NULL) {
335 DBG_WARNING("Out of memory\n");
336 ret = false;
337 goto out;
341 if (*cldap_reply->client_site) {
342 ads->config.client_site_name =
343 talloc_strdup(ads, cldap_reply->client_site);
344 if (ads->config.client_site_name == NULL) {
345 DBG_WARNING("Out of memory\n");
346 ret = false;
347 goto out;
351 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
352 ads->ldap.ss = *ss;
354 /* Store our site name. */
355 sitename_store(cldap_reply->domain_name, cldap_reply->client_site);
356 sitename_store(cldap_reply->dns_domain, cldap_reply->client_site);
358 /* Leave this until last so that the flags are not clobbered */
359 ads->config.flags = cldap_reply->server_type;
361 ret = true;
363 out:
365 TALLOC_FREE(frame);
366 return ret;
370 try a connection to a given ldap server, returning True and setting the servers IP
371 in the ads struct if successful
373 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
374 struct sockaddr_storage *ss)
376 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply = {};
377 TALLOC_CTX *frame = talloc_stackframe();
378 bool ok;
379 char addr[INET6_ADDRSTRLEN] = { 0, };
381 if (ss == NULL) {
382 TALLOC_FREE(frame);
383 return false;
386 print_sockaddr(addr, sizeof(addr), ss);
388 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
389 addr, ads->server.realm);
391 ok = ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply);
392 if (!ok) {
393 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
394 addr, ads->server.realm);
395 TALLOC_FREE(frame);
396 return false;
399 ok = ads_fill_cldap_reply(ads, gc, ss, &cldap_reply);
400 if (!ok) {
401 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
402 addr, ads->server.realm);
403 TALLOC_FREE(frame);
404 return false;
407 TALLOC_FREE(frame);
408 return true;
411 /**********************************************************************
412 send a cldap ping to list of servers, one at a time, until one of
413 them answers it's an ldap server. Record success in the ADS_STRUCT.
414 Take note of and update negative connection cache.
415 **********************************************************************/
417 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
418 const char *domain,
419 struct samba_sockaddr *sa_list,
420 size_t count)
422 TALLOC_CTX *frame = talloc_stackframe();
423 struct timeval endtime = timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
424 uint32_t nt_version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
425 struct tsocket_address **ts_list = NULL;
426 const struct tsocket_address * const *ts_list_const = NULL;
427 struct samba_sockaddr **req_sa_list = NULL;
428 struct netlogon_samlogon_response **responses = NULL;
429 size_t num_requests = 0;
430 NTSTATUS status;
431 size_t i;
432 bool ok = false;
433 bool retry;
435 ts_list = talloc_zero_array(frame,
436 struct tsocket_address *,
437 count);
438 if (ts_list == NULL) {
439 TALLOC_FREE(frame);
440 return NT_STATUS_NO_MEMORY;
443 req_sa_list = talloc_zero_array(frame,
444 struct samba_sockaddr *,
445 count);
446 if (req_sa_list == NULL) {
447 TALLOC_FREE(frame);
448 return NT_STATUS_NO_MEMORY;
451 again:
453 * The retry loop is bound by the timeout
455 retry = false;
456 num_requests = 0;
458 for (i = 0; i < count; i++) {
459 char server[INET6_ADDRSTRLEN];
460 int ret;
462 if (is_zero_addr(&sa_list[i].u.ss)) {
463 continue;
466 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
468 status = check_negative_conn_cache(domain, server);
469 if (!NT_STATUS_IS_OK(status)) {
470 continue;
473 ret = tsocket_address_inet_from_strings(ts_list, "ip",
474 server, LDAP_PORT,
475 &ts_list[num_requests]);
476 if (ret != 0) {
477 status = map_nt_error_from_unix(errno);
478 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
479 server, nt_errstr(status));
480 TALLOC_FREE(frame);
481 return status;
484 req_sa_list[num_requests] = &sa_list[i];
485 num_requests += 1;
488 DBG_DEBUG("Try to create %zu netlogon connections for domain '%s' "
489 "(provided count of addresses was %zu).\n",
490 num_requests,
491 domain,
492 count);
494 if (num_requests == 0) {
495 status = NT_STATUS_NO_LOGON_SERVERS;
496 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
497 domain, num_requests, count, nt_errstr(status));
498 TALLOC_FREE(frame);
499 return status;
502 ts_list_const = (const struct tsocket_address * const *)ts_list;
504 status = cldap_multi_netlogon(frame,
505 ts_list_const, num_requests,
506 ads->server.realm, NULL,
507 nt_version,
508 1, endtime, &responses);
509 if (!NT_STATUS_IS_OK(status)) {
510 DBG_WARNING("cldap_multi_netlogon(realm=%s, num_requests=%zu) "
511 "for count[%zu] - %s\n",
512 ads->server.realm,
513 num_requests, count,
514 nt_errstr(status));
515 TALLOC_FREE(frame);
516 return NT_STATUS_NO_LOGON_SERVERS;
519 for (i = 0; i < num_requests; i++) {
520 struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply = NULL;
521 char server[INET6_ADDRSTRLEN];
523 if (responses[i] == NULL) {
524 continue;
527 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
529 if (responses[i]->ntver != NETLOGON_NT_VERSION_5EX) {
530 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
531 ads->server.realm,
532 responses[i]->ntver, server);
533 continue;
536 cldap_reply = &responses[i]->data.nt5_ex;
538 /* Returns ok only if it matches the correct server type */
539 ok = ads_fill_cldap_reply(ads,
540 false,
541 &req_sa_list[i]->u.ss,
542 cldap_reply);
543 if (ok) {
544 DBG_DEBUG("realm[%s]: selected %s => %s\n",
545 ads->server.realm,
546 server, cldap_reply->pdc_dns_name);
547 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
548 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
549 cldap_reply);
551 TALLOC_FREE(frame);
552 return NT_STATUS_OK;
555 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
556 ads->server.realm,
557 server, cldap_reply->pdc_dns_name);
558 if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
559 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
560 cldap_reply);
562 add_failed_connection_entry(domain, server,
563 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID);
564 retry = true;
567 if (retry) {
568 bool expired;
570 expired = timeval_expired(&endtime);
571 if (!expired) {
572 goto again;
576 /* keep track of failures as all were not suitable */
577 for (i = 0; i < num_requests; i++) {
578 char server[INET6_ADDRSTRLEN];
580 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
582 add_failed_connection_entry(domain, server,
583 NT_STATUS_UNSUCCESSFUL);
586 status = NT_STATUS_NO_LOGON_SERVERS;
587 DBG_WARNING("realm[%s] no valid response "
588 "num_requests[%zu] for count[%zu] - %s\n",
589 ads->server.realm,
590 num_requests, count, nt_errstr(status));
591 TALLOC_FREE(frame);
592 return NT_STATUS_NO_LOGON_SERVERS;
595 /***************************************************************************
596 resolve a name and perform an "ldap ping" using NetBIOS and related methods
597 ****************************************************************************/
599 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
600 const char *domain, const char *realm)
602 size_t i;
603 size_t count = 0;
604 struct samba_sockaddr *sa_list = NULL;
605 NTSTATUS status;
607 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
608 domain));
610 status = get_sorted_dc_list(talloc_tos(),
611 domain,
612 NULL,
613 &sa_list,
614 &count,
615 false);
616 if (!NT_STATUS_IS_OK(status)) {
617 return status;
620 /* remove servers which are known to be dead based on
621 the corresponding DNS method */
622 if (*realm) {
623 for (i = 0; i < count; ++i) {
624 char server[INET6_ADDRSTRLEN];
626 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
628 if(!NT_STATUS_IS_OK(
629 check_negative_conn_cache(realm, server))) {
630 /* Ensure we add the workgroup name for this
631 IP address as negative too. */
632 add_failed_connection_entry(
633 domain, server,
634 NT_STATUS_UNSUCCESSFUL);
639 status = cldap_ping_list(ads, domain, sa_list, count);
641 TALLOC_FREE(sa_list);
643 return status;
647 /**********************************************************************
648 resolve a name and perform an "ldap ping" using DNS
649 **********************************************************************/
651 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
652 const char *realm)
654 size_t count = 0;
655 struct samba_sockaddr *sa_list = NULL;
656 NTSTATUS status;
658 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
659 realm));
661 status = get_sorted_dc_list(talloc_tos(),
662 realm,
663 sitename,
664 &sa_list,
665 &count,
666 true);
667 if (!NT_STATUS_IS_OK(status)) {
668 TALLOC_FREE(sa_list);
669 return status;
672 status = cldap_ping_list(ads, realm, sa_list, count);
674 TALLOC_FREE(sa_list);
676 return status;
679 /**********************************************************************
680 Try to find an AD dc using our internal name resolution routines
681 Try the realm first and then the workgroup name if netbios is not
682 disabled
683 **********************************************************************/
685 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
687 const char *c_domain = "";
688 const char *c_realm;
689 bool use_own_domain = False;
690 char *sitename = NULL;
691 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
692 bool ok = false;
694 /* if the realm and workgroup are both empty, assume they are ours */
696 /* realm */
697 c_realm = ads->server.realm;
699 if (c_realm == NULL)
700 c_realm = "";
702 if (!*c_realm) {
703 /* special case where no realm and no workgroup means our own */
704 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
705 use_own_domain = True;
706 c_realm = lp_realm();
710 if (!lp_disable_netbios()) {
711 if (use_own_domain) {
712 c_domain = lp_workgroup();
713 } else {
714 c_domain = ads->server.workgroup;
715 if (!*c_realm && (!c_domain || !*c_domain)) {
716 c_domain = lp_workgroup();
720 if (!c_domain) {
721 c_domain = "";
725 if (!*c_realm && !*c_domain) {
726 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
727 "what to do\n"));
728 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
732 * In case of LDAP we use get_dc_name() as that
733 * creates the custom krb5.conf file
735 if (ads->auth.flags & ADS_AUTH_GENERATE_KRB5_CONFIG) {
736 fstring srv_name;
737 struct sockaddr_storage ip_out;
739 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
740 " and falling back to domain '%s'\n",
741 c_realm, c_domain));
743 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
744 if (ok) {
745 if (is_zero_addr(&ip_out)) {
746 return NT_STATUS_NO_LOGON_SERVERS;
750 * we call ads_try_connect() to fill in the
751 * ads->config details
753 ok = ads_try_connect(ads, false, &ip_out);
754 if (ok) {
755 return NT_STATUS_OK;
759 return NT_STATUS_NO_LOGON_SERVERS;
762 if (*c_realm) {
763 sitename = sitename_fetch(talloc_tos(), c_realm);
764 status = resolve_and_ping_dns(ads, sitename, c_realm);
766 if (NT_STATUS_IS_OK(status)) {
767 TALLOC_FREE(sitename);
768 return status;
771 /* In case we failed to contact one of our closest DC on our
772 * site we
773 * need to try to find another DC, retry with a site-less SRV
774 * DNS query
775 * - Guenther */
777 if (sitename) {
778 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
779 "our site (%s), Trying to find another DC "
780 "for realm '%s' (domain '%s')\n",
781 sitename, c_realm, c_domain));
782 namecache_delete(c_realm, 0x1C);
783 status =
784 resolve_and_ping_dns(ads, NULL, c_realm);
786 if (NT_STATUS_IS_OK(status)) {
787 TALLOC_FREE(sitename);
788 return status;
792 TALLOC_FREE(sitename);
795 /* try netbios as fallback - if permitted,
796 or if configuration specifically requests it */
797 if (*c_domain) {
798 if (*c_realm) {
799 DEBUG(3, ("ads_find_dc: falling back to netbios "
800 "name resolution for domain '%s' (realm '%s')\n",
801 c_domain, c_realm));
804 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
805 if (NT_STATUS_IS_OK(status)) {
806 return status;
810 DEBUG(1, ("ads_find_dc: "
811 "name resolution for realm '%s' (domain '%s') failed: %s\n",
812 c_realm, c_domain, nt_errstr(status)));
813 return status;
817 * Connect to the LDAP server
818 * @param ads Pointer to an existing ADS_STRUCT
819 * @return status of connection
821 static ADS_STATUS ads_connect_internal(ADS_STRUCT *ads,
822 struct cli_credentials *creds)
824 int version = LDAP_VERSION3;
825 ADS_STATUS status;
826 NTSTATUS ntstatus;
827 char addr[INET6_ADDRSTRLEN];
828 struct sockaddr_storage existing_ss;
829 bool tls = false;
830 bool start_tls = false;
832 zero_sockaddr(&existing_ss);
834 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
835 SMB_ASSERT(creds != NULL);
838 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
840 * Simple anonyous binds are only
841 * allowed for anonymous credentials
843 SMB_ASSERT(cli_credentials_is_anonymous(creds));
846 if (!(ads->auth.flags & (ADS_AUTH_NO_BIND|ADS_AUTH_ANON_BIND))) {
847 ads->auth.flags |= ADS_AUTH_GENERATE_KRB5_CONFIG;
851 * ads_connect can be passed in a reused ADS_STRUCT
852 * with an existing non-zero ads->ldap.ss IP address
853 * that was stored by going through ads_find_dc()
854 * if ads->server.ldap_server was NULL.
856 * If ads->server.ldap_server is still NULL but
857 * the target address isn't the zero address, then
858 * store that address off off before zeroing out
859 * ads->ldap so we don't keep doing multiple calls
860 * to ads_find_dc() in the reuse case.
862 * If a caller wants a clean ADS_STRUCT they
863 * will TALLOC_FREE it and allocate a new one
864 * by calling ads_init(), which ensures
865 * ads->ldap.ss is a properly zero'ed out valid IP
866 * address.
868 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
869 /* Save off the address we previously found by ads_find_dc(). */
870 existing_ss = ads->ldap.ss;
873 ads_zero_ldap(ads);
874 ZERO_STRUCT(ads->ldap_tls_data);
875 ZERO_STRUCT(ads->ldap_wrap_data);
876 ads->ldap.last_attempt = time_mono(NULL);
877 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
879 /* try with a user specified server */
881 if (DEBUGLEVEL >= 11) {
882 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
883 DEBUG(11,("ads_connect: entering\n"));
884 DEBUGADD(11,("%s\n", s));
885 TALLOC_FREE(s);
888 if (ads->server.ldap_server) {
889 bool ok = false;
890 struct sockaddr_storage ss;
892 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
893 ads->server.ldap_server);
894 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
895 if (!ok) {
896 DEBUG(5,("ads_connect: unable to resolve name %s\n",
897 ads->server.ldap_server));
898 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
899 goto out;
902 if (is_zero_addr(&ss)) {
903 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
904 goto out;
907 ok = ads_try_connect(ads, ads->server.gc, &ss);
908 if (ok) {
909 goto got_connection;
912 /* The choice of which GC use is handled one level up in
913 ads_connect_gc(). If we continue on from here with
914 ads_find_dc() we will get GC searches on port 389 which
915 doesn't work. --jerry */
917 if (ads->server.gc == true) {
918 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
921 if (ads->server.no_fallback) {
922 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
923 goto out;
927 if (!is_zero_addr(&existing_ss)) {
928 /* We saved off who we should talk to. */
929 bool ok = ads_try_connect(ads,
930 ads->server.gc,
931 &existing_ss);
932 if (ok) {
933 goto got_connection;
936 * Keep trying to find a server and fall through
937 * into ads_find_dc() again.
939 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
940 "trying to find another DC.\n");
943 ntstatus = ads_find_dc(ads);
944 if (NT_STATUS_IS_OK(ntstatus)) {
945 goto got_connection;
948 status = ADS_ERROR_NT(ntstatus);
949 goto out;
951 got_connection:
953 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
954 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
956 if (!ads->auth.kdc_server) {
957 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
958 ads->auth.kdc_server = talloc_strdup(ads, addr);
959 if (ads->auth.kdc_server == NULL) {
960 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
961 goto out;
965 /* If the caller() requested no LDAP bind, then we are done */
967 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
968 status = ADS_SUCCESS;
969 goto out;
972 ads->ldap_tls_data.mem_ctx = talloc_init("ads LDAP TLS connection memory");
973 if (!ads->ldap_tls_data.mem_ctx) {
974 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
975 goto out;
978 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
979 if (!ads->ldap_wrap_data.mem_ctx) {
980 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
981 goto out;
984 /* Otherwise setup the TCP LDAP session */
986 if (ads->auth.flags & ADS_AUTH_SASL_LDAPS) {
987 tls = true;
988 ads->ldap.port = 636;
989 } else if (ads->auth.flags & ADS_AUTH_SASL_STARTTLS) {
990 tls = true;
991 start_tls = true;
992 ads->ldap.port = 389;
993 } else {
994 ads->ldap.port = 389;
997 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
998 &ads->ldap.ss,
999 ads->ldap.port, lp_ldap_timeout());
1000 if (ads->ldap.ld == NULL) {
1001 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
1002 goto out;
1004 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
1006 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1008 if (start_tls) {
1009 unsigned int to = lp_ldap_connection_timeout();
1010 struct berval *rspdata = NULL;
1011 char *rspoid = NULL;
1012 int rc;
1014 if (to) {
1015 /* Setup timeout */
1016 gotalarm = 0;
1017 CatchSignal(SIGALRM, gotalarm_sig);
1018 alarm(to);
1019 /* End setup timeout. */
1022 rc = ldap_extended_operation_s(ads->ldap.ld,
1023 LDAP_EXOP_START_TLS,
1024 NULL,
1025 NULL,
1026 NULL,
1027 &rspoid,
1028 &rspdata);
1029 if (gotalarm != 0 && rc == LDAP_SUCCESS) {
1030 rc = LDAP_TIMEOUT;
1033 if (to) {
1034 /* Teardown timeout. */
1035 alarm(0);
1036 CatchSignal(SIGALRM, SIG_IGN);
1039 if (rspoid != NULL) {
1040 ldap_memfree(rspoid);
1043 if (rspdata != NULL) {
1044 ber_bvfree(rspdata);
1047 if (rc != LDAP_SUCCESS) {
1048 status = ADS_ERROR_LDAP(rc);
1049 goto out;
1053 if (tls) {
1054 unsigned int to = lp_ldap_connection_timeout();
1056 if (to) {
1057 /* Setup timeout */
1058 gotalarm = 0;
1059 CatchSignal(SIGALRM, gotalarm_sig);
1060 alarm(to);
1061 /* End setup timeout. */
1064 status = ads_setup_tls_wrapping(&ads->ldap_tls_data,
1065 ads->ldap.ld,
1066 ads->config.ldap_server_name);
1068 if (to) {
1069 /* Teardown timeout. */
1070 alarm(0);
1071 CatchSignal(SIGALRM, SIG_IGN);
1074 if ( !ADS_ERR_OK(status) ) {
1075 goto out;
1079 /* cache the successful connection for workgroup and realm */
1080 if (ads_closest_dc(ads)) {
1081 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
1082 saf_store( ads->server.realm, ads->config.ldap_server_name);
1085 /* fill in the current time and offsets */
1087 status = ads_current_time( ads );
1088 if ( !ADS_ERR_OK(status) ) {
1089 goto out;
1092 /* Now do the bind */
1094 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
1095 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
1096 goto out;
1099 status = ads_sasl_bind(ads, creds);
1101 out:
1102 if (DEBUGLEVEL >= 11) {
1103 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1104 DEBUG(11,("ads_connect: leaving with: %s\n",
1105 ads_errstr(status)));
1106 DEBUGADD(11,("%s\n", s));
1107 TALLOC_FREE(s);
1110 return status;
1114 * Connect to the LDAP server using without a bind
1115 * and without a tcp connection at all
1117 * @param ads Pointer to an existing ADS_STRUCT
1118 * @return status of connection
1120 ADS_STATUS ads_connect_cldap_only(ADS_STRUCT *ads)
1122 ads->auth.flags |= ADS_AUTH_NO_BIND;
1123 return ads_connect_internal(ads, NULL);
1127 * Connect to the LDAP server
1128 * @param ads Pointer to an existing ADS_STRUCT
1129 * @return status of connection
1131 ADS_STATUS ads_connect_creds(ADS_STRUCT *ads, struct cli_credentials *creds)
1133 SMB_ASSERT(creds != NULL);
1136 * We allow upgrades from
1137 * ADS_AUTH_NO_BIND if credentials
1138 * are specified
1140 ads->auth.flags &= ~ADS_AUTH_NO_BIND;
1143 * We allow upgrades from ADS_AUTH_ANON_BIND,
1144 * as we don't want to use simple binds with
1145 * non-anon credentials
1147 if (!cli_credentials_is_anonymous(creds)) {
1148 ads->auth.flags &= ~ADS_AUTH_ANON_BIND;
1151 return ads_connect_internal(ads, creds);
1155 * Connect to the LDAP server using anonymous credentials
1156 * using a simple bind without username/password
1158 * @param ads Pointer to an existing ADS_STRUCT
1159 * @return status of connection
1161 ADS_STATUS ads_connect_simple_anon(ADS_STRUCT *ads)
1163 TALLOC_CTX *frame = talloc_stackframe();
1164 struct cli_credentials *creds = NULL;
1165 ADS_STATUS status;
1167 creds = cli_credentials_init_anon(frame);
1168 if (creds == NULL) {
1169 TALLOC_FREE(frame);
1170 return ADS_ERROR_SYSTEM(errno);
1173 ads->auth.flags |= ADS_AUTH_ANON_BIND;
1174 status = ads_connect_creds(ads, creds);
1175 TALLOC_FREE(frame);
1176 return status;
1180 * Connect to the LDAP server using the machine account
1181 * @param ads Pointer to an existing ADS_STRUCT
1182 * @return status of connection
1184 ADS_STATUS ads_connect_machine(ADS_STRUCT *ads)
1186 TALLOC_CTX *frame = talloc_stackframe();
1187 struct cli_credentials *creds = NULL;
1188 ADS_STATUS status;
1189 NTSTATUS ntstatus;
1191 ntstatus = pdb_get_trust_credentials(ads->server.workgroup,
1192 ads->server.realm,
1193 frame,
1194 &creds);
1195 if (!NT_STATUS_IS_OK(ntstatus)) {
1196 TALLOC_FREE(frame);
1197 return ADS_ERROR_NT(ntstatus);
1200 status = ads_connect_creds(ads, creds);
1201 TALLOC_FREE(frame);
1202 return status;
1206 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1207 * @param ads Pointer to an existing ADS_STRUCT
1209 * Sets the ads->ldap.ss to a valid
1210 * zero ip address that can be detected by
1211 * our is_zero_addr() function. Otherwise
1212 * it is left as AF_UNSPEC (0).
1214 void ads_zero_ldap(ADS_STRUCT *ads)
1216 ZERO_STRUCT(ads->ldap);
1218 * Initialize the sockaddr_storage so we can use
1219 * sockaddr test functions against it.
1221 zero_sockaddr(&ads->ldap.ss);
1225 * Disconnect the LDAP server
1226 * @param ads Pointer to an existing ADS_STRUCT
1228 void ads_disconnect(ADS_STRUCT *ads)
1230 if (ads->ldap.ld) {
1231 ldap_unbind(ads->ldap.ld);
1232 ads->ldap.ld = NULL;
1234 if (ads->ldap_tls_data.mem_ctx) {
1235 talloc_free(ads->ldap_tls_data.mem_ctx);
1237 if (ads->ldap_wrap_data.wrap_ops &&
1238 ads->ldap_wrap_data.wrap_ops->disconnect) {
1239 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1241 if (ads->ldap_wrap_data.mem_ctx) {
1242 talloc_free(ads->ldap_wrap_data.mem_ctx);
1244 ads_zero_ldap(ads);
1245 ZERO_STRUCT(ads->ldap_tls_data);
1246 ZERO_STRUCT(ads->ldap_wrap_data);
1250 Duplicate a struct berval into talloc'ed memory
1252 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1254 struct berval *value;
1256 if (!in_val) return NULL;
1258 value = talloc_zero(ctx, struct berval);
1259 if (value == NULL)
1260 return NULL;
1261 if (in_val->bv_len == 0) return value;
1263 value->bv_len = in_val->bv_len;
1264 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1265 in_val->bv_len);
1266 return value;
1270 Make a values list out of an array of (struct berval *)
1272 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1273 const struct berval **in_vals)
1275 struct berval **values;
1276 int i;
1278 if (!in_vals) return NULL;
1279 for (i=0; in_vals[i]; i++)
1280 ; /* count values */
1281 values = talloc_zero_array(ctx, struct berval *, i+1);
1282 if (!values) return NULL;
1284 for (i=0; in_vals[i]; i++) {
1285 values[i] = dup_berval(ctx, in_vals[i]);
1287 return values;
1291 UTF8-encode a values list out of an array of (char *)
1293 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1295 char **values;
1296 int i;
1297 size_t size;
1299 if (!in_vals) return NULL;
1300 for (i=0; in_vals[i]; i++)
1301 ; /* count values */
1302 values = talloc_zero_array(ctx, char *, i+1);
1303 if (!values) return NULL;
1305 for (i=0; in_vals[i]; i++) {
1306 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1307 TALLOC_FREE(values);
1308 return NULL;
1311 return values;
1315 Pull a (char *) array out of a UTF8-encoded values list
1317 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1319 char **values;
1320 int i;
1321 size_t converted_size;
1323 if (!in_vals) return NULL;
1324 for (i=0; in_vals[i]; i++)
1325 ; /* count values */
1326 values = talloc_zero_array(ctx, char *, i+1);
1327 if (!values) return NULL;
1329 for (i=0; in_vals[i]; i++) {
1330 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1331 &converted_size)) {
1332 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1333 "%s\n", strerror(errno)));
1336 return values;
1340 * Do a search with paged results. cookie must be null on the first
1341 * call, and then returned on each subsequent call. It will be null
1342 * again when the entire search is complete
1343 * @param ads connection to ads server
1344 * @param bind_path Base dn for the search
1345 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1346 * @param expr Search expression - specified in local charset
1347 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1348 * @param res ** which will contain results - free res* with ads_msgfree()
1349 * @param count Number of entries retrieved on this page
1350 * @param cookie The paged results cookie to be returned on subsequent calls
1351 * @return status of search
1353 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1354 const char *bind_path,
1355 int scope, const char *expr,
1356 const char **attrs, void *args,
1357 LDAPMessage **res,
1358 int *count, struct berval **cookie)
1360 int rc, i, version;
1361 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1362 size_t converted_size;
1363 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1364 BerElement *cookie_be = NULL;
1365 struct berval *cookie_bv= NULL;
1366 BerElement *ext_be = NULL;
1367 struct berval *ext_bv= NULL;
1369 TALLOC_CTX *ctx;
1370 ads_control *external_control = (ads_control *) args;
1372 *res = NULL;
1374 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1375 return ADS_ERROR(LDAP_NO_MEMORY);
1377 /* 0 means the conversion worked but the result was empty
1378 so we only fail if it's -1. In any case, it always
1379 at least nulls out the dest */
1380 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1381 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1383 rc = LDAP_NO_MEMORY;
1384 goto done;
1387 if (!attrs || !(*attrs))
1388 search_attrs = NULL;
1389 else {
1390 /* This would be the utf8-encoded version...*/
1391 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1392 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1393 rc = LDAP_NO_MEMORY;
1394 goto done;
1398 /* Paged results only available on ldap v3 or later */
1399 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1400 if (version < LDAP_VERSION3) {
1401 rc = LDAP_NOT_SUPPORTED;
1402 goto done;
1405 cookie_be = ber_alloc_t(LBER_USE_DER);
1406 if (*cookie) {
1407 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1408 ber_bvfree(*cookie); /* don't need it from last time */
1409 *cookie = NULL;
1410 } else {
1411 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1413 ber_flatten(cookie_be, &cookie_bv);
1414 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1415 PagedResults.ldctl_iscritical = (char) 1;
1416 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1417 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1419 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1420 NoReferrals.ldctl_iscritical = (char) 0;
1421 NoReferrals.ldctl_value.bv_len = 0;
1422 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1424 if (external_control &&
1425 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1426 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1428 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1429 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1431 /* win2k does not accept a ldctl_value being passed in */
1433 if (external_control->val != 0) {
1435 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1436 rc = LDAP_NO_MEMORY;
1437 goto done;
1440 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1441 rc = LDAP_NO_MEMORY;
1442 goto done;
1444 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1445 rc = LDAP_NO_MEMORY;
1446 goto done;
1449 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1450 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1452 } else {
1453 ExternalCtrl.ldctl_value.bv_len = 0;
1454 ExternalCtrl.ldctl_value.bv_val = NULL;
1457 controls[0] = &NoReferrals;
1458 controls[1] = &PagedResults;
1459 controls[2] = &ExternalCtrl;
1460 controls[3] = NULL;
1462 } else {
1463 controls[0] = &NoReferrals;
1464 controls[1] = &PagedResults;
1465 controls[2] = NULL;
1468 /* we need to disable referrals as the openldap libs don't
1469 handle them and paged results at the same time. Using them
1470 together results in the result record containing the server
1471 page control being removed from the result list (tridge/jmcd)
1473 leaving this in despite the control that says don't generate
1474 referrals, in case the server doesn't support it (jmcd)
1476 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1478 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1479 search_attrs, 0, controls,
1480 NULL, LDAP_NO_LIMIT,
1481 (LDAPMessage **)res);
1483 ber_free(cookie_be, 1);
1484 ber_bvfree(cookie_bv);
1486 if (rc) {
1487 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1488 ldap_err2string(rc)));
1489 if (rc == LDAP_OTHER) {
1490 char *ldap_errmsg;
1491 int ret;
1493 ret = ldap_parse_result(ads->ldap.ld,
1494 *res,
1495 NULL,
1496 NULL,
1497 &ldap_errmsg,
1498 NULL,
1499 NULL,
1501 if (ret == LDAP_SUCCESS) {
1502 DEBUG(3, ("ldap_search_with_timeout(%s) "
1503 "error: %s\n", expr, ldap_errmsg));
1504 ldap_memfree(ldap_errmsg);
1507 goto done;
1510 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1511 NULL, &rcontrols, 0);
1513 if (!rcontrols) {
1514 goto done;
1517 for (i=0; rcontrols[i]; i++) {
1518 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1519 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1520 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1521 &cookie_bv);
1522 /* the berval is the cookie, but must be freed when
1523 it is all done */
1524 if (cookie_bv->bv_len) /* still more to do */
1525 *cookie=ber_bvdup(cookie_bv);
1526 else
1527 *cookie=NULL;
1528 ber_bvfree(cookie_bv);
1529 ber_free(cookie_be, 1);
1530 break;
1533 ldap_controls_free(rcontrols);
1535 done:
1536 talloc_destroy(ctx);
1538 if (ext_be) {
1539 ber_free(ext_be, 1);
1542 if (ext_bv) {
1543 ber_bvfree(ext_bv);
1546 if (rc != LDAP_SUCCESS && *res != NULL) {
1547 ads_msgfree(ads, *res);
1548 *res = NULL;
1551 /* if/when we decide to utf8-encode attrs, take out this next line */
1552 TALLOC_FREE(search_attrs);
1554 return ADS_ERROR(rc);
1557 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1558 int scope, const char *expr,
1559 const char **attrs, LDAPMessage **res,
1560 int *count, struct berval **cookie)
1562 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1567 * Get all results for a search. This uses ads_do_paged_search() to return
1568 * all entries in a large search.
1569 * @param ads connection to ads server
1570 * @param bind_path Base dn for the search
1571 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1572 * @param expr Search expression
1573 * @param attrs Attributes to retrieve
1574 * @param res ** which will contain results - free res* with ads_msgfree()
1575 * @return status of search
1577 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1578 int scope, const char *expr,
1579 const char **attrs, void *args,
1580 LDAPMessage **res)
1582 struct berval *cookie = NULL;
1583 int count = 0;
1584 ADS_STATUS status;
1586 *res = NULL;
1587 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1588 &count, &cookie);
1590 if (!ADS_ERR_OK(status))
1591 return status;
1593 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1594 while (cookie) {
1595 LDAPMessage *res2 = NULL;
1596 LDAPMessage *msg, *next;
1598 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1599 attrs, args, &res2, &count, &cookie);
1600 if (!ADS_ERR_OK(status)) {
1601 break;
1604 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1605 that this works on all ldap libs, but I have only tested with openldap */
1606 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1607 next = ads_next_message(ads, msg);
1608 ldap_add_result_entry((LDAPMessage **)res, msg);
1610 /* note that we do not free res2, as the memory is now
1611 part of the main returned list */
1613 #else
1614 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1615 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1616 #endif
1618 return status;
1621 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1622 int scope, const char *expr,
1623 const char **attrs, LDAPMessage **res)
1625 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1628 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1629 int scope, const char *expr,
1630 const char **attrs, uint32_t sd_flags,
1631 LDAPMessage **res)
1633 ads_control args;
1635 args.control = ADS_SD_FLAGS_OID;
1636 args.val = sd_flags;
1637 args.critical = True;
1639 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1644 * Run a function on all results for a search. Uses ads_do_paged_search() and
1645 * runs the function as each page is returned, using ads_process_results()
1646 * @param ads connection to ads server
1647 * @param bind_path Base dn for the search
1648 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1649 * @param expr Search expression - specified in local charset
1650 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1651 * @param fn Function which takes attr name, values list, and data_area
1652 * @param data_area Pointer which is passed to function on each call
1653 * @return status of search
1655 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1656 int scope, const char *expr, const char **attrs,
1657 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1658 void *data_area)
1660 struct berval *cookie = NULL;
1661 int count = 0;
1662 ADS_STATUS status;
1663 LDAPMessage *res;
1665 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1666 &count, &cookie);
1668 if (!ADS_ERR_OK(status)) return status;
1670 ads_process_results(ads, res, fn, data_area);
1671 ads_msgfree(ads, res);
1673 while (cookie) {
1674 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1675 &res, &count, &cookie);
1677 if (!ADS_ERR_OK(status)) break;
1679 ads_process_results(ads, res, fn, data_area);
1680 ads_msgfree(ads, res);
1683 return status;
1687 * Do a search with a timeout.
1688 * @param ads connection to ads server
1689 * @param bind_path Base dn for the search
1690 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1691 * @param expr Search expression
1692 * @param attrs Attributes to retrieve
1693 * @param res ** which will contain results - free res* with ads_msgfree()
1694 * @return status of search
1696 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1697 const char *expr,
1698 const char **attrs, LDAPMessage **res)
1700 int rc;
1701 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1702 size_t converted_size;
1703 TALLOC_CTX *ctx;
1705 *res = NULL;
1706 if (!(ctx = talloc_init("ads_do_search"))) {
1707 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1708 return ADS_ERROR(LDAP_NO_MEMORY);
1711 /* 0 means the conversion worked but the result was empty
1712 so we only fail if it's negative. In any case, it always
1713 at least nulls out the dest */
1714 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1715 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1717 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1718 rc = LDAP_NO_MEMORY;
1719 goto done;
1722 if (!attrs || !(*attrs))
1723 search_attrs = NULL;
1724 else {
1725 /* This would be the utf8-encoded version...*/
1726 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1727 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1729 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1730 rc = LDAP_NO_MEMORY;
1731 goto done;
1735 /* see the note in ads_do_paged_search - we *must* disable referrals */
1736 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1738 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1739 search_attrs, 0, NULL, NULL,
1740 LDAP_NO_LIMIT,
1741 (LDAPMessage **)res);
1743 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1744 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1745 rc = 0;
1748 done:
1749 talloc_destroy(ctx);
1750 /* if/when we decide to utf8-encode attrs, take out this next line */
1751 TALLOC_FREE(search_attrs);
1752 return ADS_ERROR(rc);
1755 * Do a general ADS search
1756 * @param ads connection to ads server
1757 * @param res ** which will contain results - free res* with ads_msgfree()
1758 * @param expr Search expression
1759 * @param attrs Attributes to retrieve
1760 * @return status of search
1762 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1763 const char *expr, const char **attrs)
1765 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1766 expr, attrs, res);
1770 * Do a search on a specific DistinguishedName
1771 * @param ads connection to ads server
1772 * @param res ** which will contain results - free res* with ads_msgfree()
1773 * @param dn DistinguishedName to search
1774 * @param attrs Attributes to retrieve
1775 * @return status of search
1777 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1778 const char *dn, const char **attrs)
1780 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1781 attrs, res);
1785 * Free up memory from a ads_search
1786 * @param ads connection to ads server
1787 * @param msg Search results to free
1789 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1791 if (!msg) return;
1792 ldap_msgfree(msg);
1796 * Get a dn from search results
1797 * @param ads connection to ads server
1798 * @param msg Search result
1799 * @return dn string
1801 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1803 char *utf8_dn, *unix_dn;
1804 size_t converted_size;
1806 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1808 if (!utf8_dn) {
1809 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1810 return NULL;
1813 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1814 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1815 utf8_dn ));
1816 return NULL;
1818 ldap_memfree(utf8_dn);
1819 return unix_dn;
1823 * Get the parent from a dn
1824 * @param dn the dn to return the parent from
1825 * @return parent dn string
1827 char *ads_parent_dn(const char *dn)
1829 char *p;
1831 if (dn == NULL) {
1832 return NULL;
1835 p = strchr(dn, ',');
1837 if (p == NULL) {
1838 return NULL;
1841 return p+1;
1845 * Find a machine account given a hostname
1846 * @param ads connection to ads server
1847 * @param res ** which will contain results - free res* with ads_msgfree()
1848 * @param host Hostname to search for
1849 * @return status of search
1851 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1852 const char *machine)
1854 ADS_STATUS status;
1855 char *expr;
1856 const char *attrs[] = {
1857 /* This is how Windows checks for machine accounts */
1858 "objectClass",
1859 "SamAccountName",
1860 "userAccountControl",
1861 "DnsHostName",
1862 "ServicePrincipalName",
1863 "userPrincipalName",
1865 /* Additional attributes Samba checks */
1866 "msDS-KeyVersionNumber",
1867 "msDS-AdditionalDnsHostName",
1868 "msDS-SupportedEncryptionTypes",
1869 "nTSecurityDescriptor",
1870 "objectSid",
1872 NULL
1874 TALLOC_CTX *frame = talloc_stackframe();
1876 *res = NULL;
1878 /* the easiest way to find a machine account anywhere in the tree
1879 is to look for hostname$ */
1880 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1881 if (expr == NULL) {
1882 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1883 goto done;
1886 status = ads_search(ads, res, expr, attrs);
1887 if (ADS_ERR_OK(status)) {
1888 if (ads_count_replies(ads, *res) != 1) {
1889 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1893 done:
1894 TALLOC_FREE(frame);
1895 return status;
1899 * Initialize a list of mods to be used in a modify request
1900 * @param ctx An initialized TALLOC_CTX
1901 * @return allocated ADS_MODLIST
1903 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1905 #define ADS_MODLIST_ALLOC_SIZE 10
1906 LDAPMod **mods;
1908 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1909 /* -1 is safety to make sure we don't go over the end.
1910 need to reset it to NULL before doing ldap modify */
1911 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1913 return (ADS_MODLIST)mods;
1918 add an attribute to the list, with values list already constructed
1920 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1921 int mod_op, const char *name,
1922 const void *_invals)
1924 int curmod;
1925 LDAPMod **modlist = (LDAPMod **) *mods;
1926 struct berval **ber_values = NULL;
1927 char **char_values = NULL;
1929 if (!_invals) {
1930 mod_op = LDAP_MOD_DELETE;
1931 } else {
1932 if (mod_op & LDAP_MOD_BVALUES) {
1933 const struct berval **b;
1934 b = discard_const_p(const struct berval *, _invals);
1935 ber_values = ads_dup_values(ctx, b);
1936 } else {
1937 const char **c;
1938 c = discard_const_p(const char *, _invals);
1939 char_values = ads_push_strvals(ctx, c);
1943 /* find the first empty slot */
1944 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1945 curmod++);
1946 if (modlist[curmod] == (LDAPMod *) -1) {
1947 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1948 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1949 return ADS_ERROR(LDAP_NO_MEMORY);
1950 memset(&modlist[curmod], 0,
1951 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1952 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1953 *mods = (ADS_MODLIST)modlist;
1956 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1957 return ADS_ERROR(LDAP_NO_MEMORY);
1958 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1959 if (mod_op & LDAP_MOD_BVALUES) {
1960 modlist[curmod]->mod_bvalues = ber_values;
1961 } else if (mod_op & LDAP_MOD_DELETE) {
1962 modlist[curmod]->mod_values = NULL;
1963 } else {
1964 modlist[curmod]->mod_values = char_values;
1967 modlist[curmod]->mod_op = mod_op;
1968 return ADS_ERROR(LDAP_SUCCESS);
1972 * Add a single string value to a mod list
1973 * @param ctx An initialized TALLOC_CTX
1974 * @param mods An initialized ADS_MODLIST
1975 * @param name The attribute name to add
1976 * @param val The value to add - NULL means DELETE
1977 * @return ADS STATUS indicating success of add
1979 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1980 const char *name, const char *val)
1982 const char *values[2];
1984 values[0] = val;
1985 values[1] = NULL;
1987 if (!val)
1988 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1989 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1993 * Add an array of string values to a mod list
1994 * @param ctx An initialized TALLOC_CTX
1995 * @param mods An initialized ADS_MODLIST
1996 * @param name The attribute name to add
1997 * @param vals The array of string values to add - NULL means DELETE
1998 * @return ADS STATUS indicating success of add
2000 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2001 const char *name, const char **vals)
2003 if (!vals)
2004 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
2005 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
2006 name, (const void **) vals);
2010 * Add a single ber-encoded value to a mod list
2011 * @param ctx An initialized TALLOC_CTX
2012 * @param mods An initialized ADS_MODLIST
2013 * @param name The attribute name to add
2014 * @param val The value to add - NULL means DELETE
2015 * @return ADS STATUS indicating success of add
2017 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2018 const char *name, const struct berval *val)
2020 const struct berval *values[2];
2022 values[0] = val;
2023 values[1] = NULL;
2024 if (!val)
2025 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
2026 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
2027 name, (const void *) values);
2030 static void ads_print_error(int ret, LDAP *ld)
2032 if (ret != 0) {
2033 char *ld_error = NULL;
2034 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
2035 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
2036 ret,
2037 ldap_err2string(ret),
2038 ld_error);
2039 SAFE_FREE(ld_error);
2044 * Perform an ldap modify
2045 * @param ads connection to ads server
2046 * @param mod_dn DistinguishedName to modify
2047 * @param mods list of modifications to perform
2048 * @return status of modify
2050 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
2052 int ret,i;
2053 char *utf8_dn = NULL;
2054 size_t converted_size;
2056 this control is needed to modify that contains a currently
2057 non-existent attribute (but allowable for the object) to run
2059 LDAPControl PermitModify = {
2060 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
2061 {0, NULL},
2062 (char) 1};
2063 LDAPControl *controls[2];
2065 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
2067 controls[0] = &PermitModify;
2068 controls[1] = NULL;
2070 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
2071 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2074 /* find the end of the list, marked by NULL or -1 */
2075 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2076 /* make sure the end of the list is NULL */
2077 mods[i] = NULL;
2078 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
2079 (LDAPMod **) mods, controls, NULL);
2080 ads_print_error(ret, ads->ldap.ld);
2081 TALLOC_FREE(utf8_dn);
2082 return ADS_ERROR(ret);
2086 * Perform an ldap add
2087 * @param ads connection to ads server
2088 * @param new_dn DistinguishedName to add
2089 * @param mods list of attributes and values for DN
2090 * @return status of add
2092 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
2094 int ret, i;
2095 char *utf8_dn = NULL;
2096 size_t converted_size;
2098 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
2100 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
2101 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2102 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2105 /* find the end of the list, marked by NULL or -1 */
2106 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2107 /* make sure the end of the list is NULL */
2108 mods[i] = NULL;
2110 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
2111 ads_print_error(ret, ads->ldap.ld);
2112 TALLOC_FREE(utf8_dn);
2113 return ADS_ERROR(ret);
2117 * Delete a DistinguishedName
2118 * @param ads connection to ads server
2119 * @param new_dn DistinguishedName to delete
2120 * @return status of delete
2122 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
2124 int ret;
2125 char *utf8_dn = NULL;
2126 size_t converted_size;
2127 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
2128 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2129 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2132 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
2134 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
2135 ads_print_error(ret, ads->ldap.ld);
2136 TALLOC_FREE(utf8_dn);
2137 return ADS_ERROR(ret);
2141 * Build an org unit string
2142 * if org unit is Computers or blank then assume a container, otherwise
2143 * assume a / separated list of organisational units.
2144 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2145 * @param ads connection to ads server
2146 * @param org_unit Organizational unit
2147 * @return org unit string - caller must free
2149 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
2151 ADS_STATUS status;
2152 char *ret = NULL;
2153 char *dn = NULL;
2155 if (!org_unit || !*org_unit) {
2157 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
2159 /* samba4 might not yet respond to a wellknownobject-query */
2160 return ret ? ret : SMB_STRDUP("cn=Computers");
2163 if (strequal(org_unit, "Computers")) {
2164 return SMB_STRDUP("cn=Computers");
2167 /* jmcd: removed "\\" from the separation chars, because it is
2168 needed as an escape for chars like '#' which are valid in an
2169 OU name */
2170 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
2171 if (!ADS_ERR_OK(status)) {
2172 return NULL;
2175 return dn;
2179 * Get a org unit string for a well-known GUID
2180 * @param ads connection to ads server
2181 * @param wknguid Well known GUID
2182 * @return org unit string - caller must free
2184 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2186 ADS_STATUS status;
2187 LDAPMessage *res = NULL;
2188 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2189 **bind_dn_exp = NULL;
2190 const char *attrs[] = {"distinguishedName", NULL};
2191 int new_ln, wkn_ln, bind_ln, i;
2193 if (wknguid == NULL) {
2194 return NULL;
2197 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2198 DEBUG(1, ("asprintf failed!\n"));
2199 return NULL;
2202 status = ads_search_dn(ads, &res, base, attrs);
2203 if (!ADS_ERR_OK(status)) {
2204 DEBUG(1,("Failed while searching for: %s\n", base));
2205 goto out;
2208 if (ads_count_replies(ads, res) != 1) {
2209 goto out;
2212 /* substitute the bind-path from the well-known-guid-search result */
2213 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2214 if (!wkn_dn) {
2215 goto out;
2218 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2219 if (!wkn_dn_exp) {
2220 goto out;
2223 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2224 if (!bind_dn_exp) {
2225 goto out;
2228 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2230 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2233 new_ln = wkn_ln - bind_ln;
2235 ret = SMB_STRDUP(wkn_dn_exp[0]);
2236 if (!ret) {
2237 goto out;
2240 for (i=1; i < new_ln; i++) {
2241 char *s = NULL;
2243 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2244 SAFE_FREE(ret);
2245 goto out;
2248 SAFE_FREE(ret);
2249 ret = SMB_STRDUP(s);
2250 free(s);
2251 if (!ret) {
2252 goto out;
2256 out:
2257 SAFE_FREE(base);
2258 ads_msgfree(ads, res);
2259 TALLOC_FREE(wkn_dn);
2260 if (wkn_dn_exp) {
2261 ldap_value_free(wkn_dn_exp);
2263 if (bind_dn_exp) {
2264 ldap_value_free(bind_dn_exp);
2267 return ret;
2271 * Adds (appends) an item to an attribute array, rather then
2272 * replacing the whole list
2273 * @param ctx An initialized TALLOC_CTX
2274 * @param mods An initialized ADS_MODLIST
2275 * @param name name of the ldap attribute to append to
2276 * @param vals an array of values to add
2277 * @return status of addition
2280 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2281 const char *name, const char **vals)
2283 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2284 (const void *) vals);
2288 * This clears out all registered spn's for a given hostname
2289 * @param ads An initialized ADS_STRUCT
2290 * @param machine_name the NetBIOS name of the computer.
2291 * @return 0 upon success, non-zero otherwise.
2294 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2296 TALLOC_CTX *ctx;
2297 LDAPMessage *res = NULL;
2298 ADS_MODLIST mods;
2299 const char *servicePrincipalName[1] = {NULL};
2300 ADS_STATUS ret;
2301 char *dn_string = NULL;
2303 ret = ads_find_machine_acct(ads, &res, machine_name);
2304 if (!ADS_ERR_OK(ret)) {
2305 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2306 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2307 ads_msgfree(ads, res);
2308 return ret;
2311 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2312 ctx = talloc_init("ads_clear_service_principal_names");
2313 if (!ctx) {
2314 ads_msgfree(ads, res);
2315 return ADS_ERROR(LDAP_NO_MEMORY);
2318 if (!(mods = ads_init_mods(ctx))) {
2319 talloc_destroy(ctx);
2320 ads_msgfree(ads, res);
2321 return ADS_ERROR(LDAP_NO_MEMORY);
2323 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2324 if (!ADS_ERR_OK(ret)) {
2325 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2326 ads_msgfree(ads, res);
2327 talloc_destroy(ctx);
2328 return ret;
2330 dn_string = ads_get_dn(ads, talloc_tos(), res);
2331 if (!dn_string) {
2332 talloc_destroy(ctx);
2333 ads_msgfree(ads, res);
2334 return ADS_ERROR(LDAP_NO_MEMORY);
2336 ret = ads_gen_mod(ads, dn_string, mods);
2337 TALLOC_FREE(dn_string);
2338 if (!ADS_ERR_OK(ret)) {
2339 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2340 machine_name));
2341 ads_msgfree(ads, res);
2342 talloc_destroy(ctx);
2343 return ret;
2346 ads_msgfree(ads, res);
2347 talloc_destroy(ctx);
2348 return ret;
2352 * @brief Search for an element in a string array.
2354 * @param[in] el_array The string array to search.
2356 * @param[in] num_el The number of elements in the string array.
2358 * @param[in] el The string to search.
2360 * @return True if found, false if not.
2362 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2364 size_t i;
2366 if (el_array == NULL || num_el == 0 || el == NULL) {
2367 return false;
2370 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2371 int cmp;
2373 cmp = strcasecmp_m(el_array[i], el);
2374 if (cmp == 0) {
2375 return true;
2379 return false;
2383 * @brief This gets the service principal names of an existing computer account.
2385 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2387 * @param[in] ads The ADS context to use.
2389 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2390 * identify the computer account.
2392 * @param[in] spn_array A pointer to store the array for SPNs.
2394 * @param[in] num_spns The number of principals stored in the array.
2396 * @return 0 on success, or a ADS error if a failure occurred.
2398 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2399 ADS_STRUCT *ads,
2400 const char *machine_name,
2401 char ***spn_array,
2402 size_t *num_spns)
2404 ADS_STATUS status;
2405 LDAPMessage *res = NULL;
2406 int count;
2408 status = ads_find_machine_acct(ads,
2409 &res,
2410 machine_name);
2411 if (!ADS_ERR_OK(status)) {
2412 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2413 machine_name));
2414 return status;
2417 count = ads_count_replies(ads, res);
2418 if (count != 1) {
2419 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2420 goto done;
2423 *spn_array = ads_pull_strings(ads,
2424 mem_ctx,
2425 res,
2426 "servicePrincipalName",
2427 num_spns);
2428 if (*spn_array == NULL) {
2429 DEBUG(1, ("Host account for %s does not have service principal "
2430 "names.\n",
2431 machine_name));
2432 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2433 goto done;
2436 done:
2437 ads_msgfree(ads, res);
2439 return status;
2443 * This adds a service principal name to an existing computer account
2444 * (found by hostname) in AD.
2445 * @param ads An initialized ADS_STRUCT
2446 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2447 * @param spns An array or strings for the service principals to add,
2448 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2449 * @return 0 upon success, or non-zero if a failure occurs
2452 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2453 const char *machine_name,
2454 const char **spns)
2456 ADS_STATUS ret;
2457 TALLOC_CTX *ctx;
2458 LDAPMessage *res = NULL;
2459 ADS_MODLIST mods;
2460 char *dn_string = NULL;
2461 const char **servicePrincipalName = spns;
2463 ret = ads_find_machine_acct(ads, &res, machine_name);
2464 if (!ADS_ERR_OK(ret)) {
2465 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2466 machine_name));
2467 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2468 ads_msgfree(ads, res);
2469 return ret;
2472 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2473 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2474 ads_msgfree(ads, res);
2475 return ADS_ERROR(LDAP_NO_MEMORY);
2478 DEBUG(5,("ads_add_service_principal_name: INFO: "
2479 "Adding %s to host %s\n",
2480 spns[0] ? "N/A" : spns[0], machine_name));
2483 DEBUG(5,("ads_add_service_principal_name: INFO: "
2484 "Adding %s to host %s\n",
2485 spns[1] ? "N/A" : spns[1], machine_name));
2487 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2488 ret = ADS_ERROR(LDAP_NO_MEMORY);
2489 goto out;
2492 ret = ads_add_strlist(ctx,
2493 &mods,
2494 "servicePrincipalName",
2495 servicePrincipalName);
2496 if (!ADS_ERR_OK(ret)) {
2497 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2498 goto out;
2501 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2502 ret = ADS_ERROR(LDAP_NO_MEMORY);
2503 goto out;
2506 ret = ads_gen_mod(ads, dn_string, mods);
2507 if (!ADS_ERR_OK(ret)) {
2508 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2509 goto out;
2512 out:
2513 TALLOC_FREE( ctx );
2514 ads_msgfree(ads, res);
2515 return ret;
2518 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2519 LDAPMessage *msg)
2521 uint32_t acct_ctrl = 0;
2522 bool ok;
2524 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2525 if (!ok) {
2526 return 0;
2529 return acct_ctrl;
2532 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2533 LDAPMessage *msg,
2534 const struct berval *machine_pw_val)
2536 ADS_MODLIST mods;
2537 ADS_STATUS ret;
2538 TALLOC_CTX *frame = talloc_stackframe();
2539 uint32_t acct_control;
2540 char *control_str = NULL;
2541 const char *attrs[] = {
2542 "objectSid",
2543 NULL
2545 LDAPMessage *res = NULL;
2546 char *dn = NULL;
2548 dn = ads_get_dn(ads, frame, msg);
2549 if (dn == NULL) {
2550 ret = ADS_ERROR(LDAP_NO_MEMORY);
2551 goto done;
2554 acct_control = ads_get_acct_ctrl(ads, msg);
2555 if (acct_control == 0) {
2556 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2557 goto done;
2561 * Changing the password, disables the account. So we need to change the
2562 * userAccountControl flags to enable it again.
2564 mods = ads_init_mods(frame);
2565 if (mods == NULL) {
2566 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2567 goto done;
2570 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2572 ret = ads_gen_mod(ads, dn, mods);
2573 if (!ADS_ERR_OK(ret)) {
2574 goto done;
2576 TALLOC_FREE(mods);
2579 * To activate the account, we need to disable and enable it.
2581 acct_control |= UF_ACCOUNTDISABLE;
2583 control_str = talloc_asprintf(frame, "%u", acct_control);
2584 if (control_str == NULL) {
2585 ret = ADS_ERROR(LDAP_NO_MEMORY);
2586 goto done;
2589 mods = ads_init_mods(frame);
2590 if (mods == NULL) {
2591 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2592 goto done;
2595 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2597 ret = ads_gen_mod(ads, dn, mods);
2598 if (!ADS_ERR_OK(ret)) {
2599 goto done;
2601 TALLOC_FREE(mods);
2602 TALLOC_FREE(control_str);
2605 * Enable the account again.
2607 acct_control &= ~UF_ACCOUNTDISABLE;
2609 control_str = talloc_asprintf(frame, "%u", acct_control);
2610 if (control_str == NULL) {
2611 ret = ADS_ERROR(LDAP_NO_MEMORY);
2612 goto done;
2615 mods = ads_init_mods(frame);
2616 if (mods == NULL) {
2617 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2618 goto done;
2621 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2623 ret = ads_gen_mod(ads, dn, mods);
2624 if (!ADS_ERR_OK(ret)) {
2625 goto done;
2627 TALLOC_FREE(mods);
2628 TALLOC_FREE(control_str);
2630 ret = ads_search_dn(ads, &res, dn, attrs);
2631 ads_msgfree(ads, res);
2633 done:
2634 talloc_free(frame);
2636 return ret;
2640 * adds a machine account to the ADS server
2641 * @param ads An initialized ADS_STRUCT
2642 * @param machine_name - the NetBIOS machine name of this account.
2643 * @param account_type A number indicating the type of account to create
2644 * @param org_unit The LDAP path in which to place this account
2645 * @return 0 upon success, or non-zero otherwise
2648 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2649 const char *machine_name,
2650 const char *machine_password,
2651 const char *org_unit,
2652 uint32_t etype_list,
2653 const char *dns_domain_name)
2655 ADS_STATUS ret;
2656 char *samAccountName = NULL;
2657 char *controlstr = NULL;
2658 TALLOC_CTX *ctx = NULL;
2659 ADS_MODLIST mods;
2660 char *machine_escaped = NULL;
2661 char *dns_hostname = NULL;
2662 char *new_dn = NULL;
2663 char *utf8_pw = NULL;
2664 size_t utf8_pw_len = 0;
2665 char *utf16_pw = NULL;
2666 size_t utf16_pw_len = 0;
2667 struct berval machine_pw_val;
2668 bool ok;
2669 const char **spn_array = NULL;
2670 size_t num_spns = 0;
2671 const char *spn_prefix[] = {
2672 "HOST",
2673 "RestrictedKrbHost",
2675 size_t i;
2676 LDAPMessage *res = NULL;
2677 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2679 ctx = talloc_init("ads_add_machine_acct");
2680 if (ctx == NULL) {
2681 return ADS_ERROR(LDAP_NO_MEMORY);
2684 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2685 if (machine_escaped == NULL) {
2686 ret = ADS_ERROR(LDAP_NO_MEMORY);
2687 goto done;
2690 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2691 if (utf8_pw == NULL) {
2692 ret = ADS_ERROR(LDAP_NO_MEMORY);
2693 goto done;
2695 utf8_pw_len = strlen(utf8_pw);
2697 ok = convert_string_talloc(ctx,
2698 CH_UTF8, CH_UTF16MUNGED,
2699 utf8_pw, utf8_pw_len,
2700 (void *)&utf16_pw, &utf16_pw_len);
2701 if (!ok) {
2702 ret = ADS_ERROR(LDAP_NO_MEMORY);
2703 goto done;
2706 machine_pw_val = (struct berval) {
2707 .bv_val = utf16_pw,
2708 .bv_len = utf16_pw_len,
2711 /* Check if the machine account already exists. */
2712 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2713 if (ADS_ERR_OK(ret)) {
2714 /* Change the machine account password */
2715 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2716 ads_msgfree(ads, res);
2718 goto done;
2720 ads_msgfree(ads, res);
2722 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2723 if (new_dn == NULL) {
2724 ret = ADS_ERROR(LDAP_NO_MEMORY);
2725 goto done;
2728 /* Create machine account */
2730 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2731 if (samAccountName == NULL) {
2732 ret = ADS_ERROR(LDAP_NO_MEMORY);
2733 goto done;
2736 dns_hostname = talloc_asprintf(ctx,
2737 "%s.%s",
2738 machine_name,
2739 dns_domain_name);
2740 if (dns_hostname == NULL) {
2741 ret = ADS_ERROR(LDAP_NO_MEMORY);
2742 goto done;
2745 /* Add dns_hostname SPNs */
2746 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2747 char *spn = talloc_asprintf(ctx,
2748 "%s/%s",
2749 spn_prefix[i],
2750 dns_hostname);
2751 if (spn == NULL) {
2752 ret = ADS_ERROR(LDAP_NO_MEMORY);
2753 goto done;
2756 ok = add_string_to_array(ctx,
2757 spn,
2758 &spn_array,
2759 &num_spns);
2760 if (!ok) {
2761 ret = ADS_ERROR(LDAP_NO_MEMORY);
2762 goto done;
2766 /* Add machine_name SPNs */
2767 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2768 char *spn = talloc_asprintf(ctx,
2769 "%s/%s",
2770 spn_prefix[i],
2771 machine_name);
2772 if (spn == NULL) {
2773 ret = ADS_ERROR(LDAP_NO_MEMORY);
2774 goto done;
2777 ok = add_string_to_array(ctx,
2778 spn,
2779 &spn_array,
2780 &num_spns);
2781 if (!ok) {
2782 ret = ADS_ERROR(LDAP_NO_MEMORY);
2783 goto done;
2787 /* Make sure to NULL terminate the array */
2788 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2789 if (spn_array == NULL) {
2790 ret = ADS_ERROR(LDAP_NO_MEMORY);
2791 goto done;
2793 spn_array[num_spns] = NULL;
2795 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2796 if (controlstr == NULL) {
2797 ret = ADS_ERROR(LDAP_NO_MEMORY);
2798 goto done;
2801 mods = ads_init_mods(ctx);
2802 if (mods == NULL) {
2803 ret = ADS_ERROR(LDAP_NO_MEMORY);
2804 goto done;
2807 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2808 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2809 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2810 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2811 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2812 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2814 ret = ads_gen_add(ads, new_dn, mods);
2816 done:
2817 SAFE_FREE(machine_escaped);
2818 talloc_destroy(ctx);
2820 return ret;
2824 * move a machine account to another OU on the ADS server
2825 * @param ads - An initialized ADS_STRUCT
2826 * @param machine_name - the NetBIOS machine name of this account.
2827 * @param org_unit - The LDAP path in which to place this account
2828 * @param moved - whether we moved the machine account (optional)
2829 * @return 0 upon success, or non-zero otherwise
2832 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2833 const char *org_unit, bool *moved)
2835 ADS_STATUS rc;
2836 int ldap_status;
2837 LDAPMessage *res = NULL;
2838 char *filter = NULL;
2839 char *computer_dn = NULL;
2840 char *parent_dn;
2841 char *computer_rdn = NULL;
2842 bool need_move = False;
2844 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2845 rc = ADS_ERROR(LDAP_NO_MEMORY);
2846 goto done;
2849 /* Find pre-existing machine */
2850 rc = ads_search(ads, &res, filter, NULL);
2851 if (!ADS_ERR_OK(rc)) {
2852 goto done;
2855 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2856 if (!computer_dn) {
2857 rc = ADS_ERROR(LDAP_NO_MEMORY);
2858 goto done;
2861 parent_dn = ads_parent_dn(computer_dn);
2862 if (strequal(parent_dn, org_unit)) {
2863 goto done;
2866 need_move = True;
2868 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2869 rc = ADS_ERROR(LDAP_NO_MEMORY);
2870 goto done;
2873 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2874 org_unit, 1, NULL, NULL);
2875 rc = ADS_ERROR(ldap_status);
2877 done:
2878 ads_msgfree(ads, res);
2879 SAFE_FREE(filter);
2880 TALLOC_FREE(computer_dn);
2881 SAFE_FREE(computer_rdn);
2883 if (!ADS_ERR_OK(rc)) {
2884 need_move = False;
2887 if (moved) {
2888 *moved = need_move;
2891 return rc;
2895 dump a binary result from ldap
2897 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2899 size_t i;
2900 for (i=0; values[i]; i++) {
2901 ber_len_t j;
2902 printf("%s: ", field);
2903 for (j=0; j<values[i]->bv_len; j++) {
2904 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2906 printf("\n");
2910 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2912 int i;
2913 for (i=0; values[i]; i++) {
2914 NTSTATUS status;
2915 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2916 struct GUID guid;
2918 status = GUID_from_ndr_blob(&in, &guid);
2919 if (NT_STATUS_IS_OK(status)) {
2920 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2921 } else {
2922 printf("%s: INVALID GUID\n", field);
2928 dump a sid result from ldap
2930 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2932 int i;
2933 for (i=0; values[i]; i++) {
2934 ssize_t ret;
2935 struct dom_sid sid;
2936 struct dom_sid_buf tmp;
2937 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2938 values[i]->bv_len, &sid);
2939 if (ret == -1) {
2940 return;
2942 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2947 dump ntSecurityDescriptor
2949 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2951 TALLOC_CTX *frame = talloc_stackframe();
2952 struct security_descriptor *psd;
2953 NTSTATUS status;
2955 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2956 values[0]->bv_len, &psd);
2957 if (!NT_STATUS_IS_OK(status)) {
2958 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2959 nt_errstr(status)));
2960 TALLOC_FREE(frame);
2961 return;
2964 if (psd) {
2965 ads_disp_sd(ads, talloc_tos(), psd);
2968 TALLOC_FREE(frame);
2972 dump a string result from ldap
2974 static void dump_string(const char *field, char **values)
2976 int i;
2977 for (i=0; values[i]; i++) {
2978 printf("%s: %s\n", field, values[i]);
2983 dump a field from LDAP on stdout
2984 used for debugging
2987 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2989 const struct {
2990 const char *name;
2991 bool string;
2992 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2993 } handlers[] = {
2994 {"objectGUID", False, dump_guid},
2995 {"netbootGUID", False, dump_guid},
2996 {"nTSecurityDescriptor", False, dump_sd},
2997 {"dnsRecord", False, dump_binary},
2998 {"objectSid", False, dump_sid},
2999 {"securityIdentifier", False, dump_sid},
3000 {"tokenGroups", False, dump_sid},
3001 {"tokenGroupsNoGCAcceptable", False, dump_sid},
3002 {"tokengroupsGlobalandUniversal", False, dump_sid},
3003 {"mS-DS-CreatorSID", False, dump_sid},
3004 {"msExchMailboxGuid", False, dump_guid},
3005 {"msDS-TrustForestTrustInfo", False, dump_binary},
3006 {NULL, True, NULL}
3008 int i;
3010 if (!field) { /* must be end of an entry */
3011 printf("\n");
3012 return False;
3015 for (i=0; handlers[i].name; i++) {
3016 if (strcasecmp_m(handlers[i].name, field) == 0) {
3017 if (!values) /* first time, indicate string or not */
3018 return handlers[i].string;
3019 handlers[i].handler(ads, field, (struct berval **) values);
3020 break;
3023 if (!handlers[i].name) {
3024 if (!values) /* first time, indicate string conversion */
3025 return True;
3026 dump_string(field, (char **)values);
3028 return False;
3032 * Dump a result from LDAP on stdout
3033 * used for debugging
3034 * @param ads connection to ads server
3035 * @param res Results to dump
3038 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
3040 ads_process_results(ads, res, ads_dump_field, NULL);
3044 * Walk through results, calling a function for each entry found.
3045 * The function receives a field name, a berval * array of values,
3046 * and a data area passed through from the start. The function is
3047 * called once with null for field and values at the end of each
3048 * entry.
3049 * @param ads connection to ads server
3050 * @param res Results to process
3051 * @param fn Function for processing each result
3052 * @param data_area user-defined area to pass to function
3054 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
3055 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
3056 void *data_area)
3058 LDAPMessage *msg;
3059 TALLOC_CTX *ctx;
3060 size_t converted_size;
3062 if (!(ctx = talloc_init("ads_process_results")))
3063 return;
3065 for (msg = ads_first_entry(ads, res); msg;
3066 msg = ads_next_entry(ads, msg)) {
3067 char *utf8_field;
3068 BerElement *b;
3070 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
3071 (LDAPMessage *)msg,&b);
3072 utf8_field;
3073 utf8_field=ldap_next_attribute(ads->ldap.ld,
3074 (LDAPMessage *)msg,b)) {
3075 struct berval **ber_vals;
3076 char **str_vals;
3077 char **utf8_vals;
3078 char *field;
3079 bool string;
3081 if (!pull_utf8_talloc(ctx, &field, utf8_field,
3082 &converted_size))
3084 DEBUG(0,("ads_process_results: "
3085 "pull_utf8_talloc failed: %s\n",
3086 strerror(errno)));
3089 string = fn(ads, field, NULL, data_area);
3091 if (string) {
3092 const char **p;
3094 utf8_vals = ldap_get_values(ads->ldap.ld,
3095 (LDAPMessage *)msg, field);
3096 p = discard_const_p(const char *, utf8_vals);
3097 str_vals = ads_pull_strvals(ctx, p);
3098 fn(ads, field, (void **) str_vals, data_area);
3099 ldap_value_free(utf8_vals);
3100 } else {
3101 ber_vals = ldap_get_values_len(ads->ldap.ld,
3102 (LDAPMessage *)msg, field);
3103 fn(ads, field, (void **) ber_vals, data_area);
3105 ldap_value_free_len(ber_vals);
3107 ldap_memfree(utf8_field);
3109 ber_free(b, 0);
3110 talloc_free_children(ctx);
3111 fn(ads, NULL, NULL, data_area); /* completed an entry */
3114 talloc_destroy(ctx);
3118 * count how many replies are in a LDAPMessage
3119 * @param ads connection to ads server
3120 * @param res Results to count
3121 * @return number of replies
3123 int ads_count_replies(ADS_STRUCT *ads, void *res)
3125 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3129 * pull the first entry from a ADS result
3130 * @param ads connection to ads server
3131 * @param res Results of search
3132 * @return first entry from result
3134 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3136 return ldap_first_entry(ads->ldap.ld, res);
3140 * pull the next entry from a ADS result
3141 * @param ads connection to ads server
3142 * @param res Results of search
3143 * @return next entry from result
3145 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3147 return ldap_next_entry(ads->ldap.ld, res);
3151 * pull the first message from a ADS result
3152 * @param ads connection to ads server
3153 * @param res Results of search
3154 * @return first message from result
3156 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3158 return ldap_first_message(ads->ldap.ld, res);
3162 * pull the next message from a ADS result
3163 * @param ads connection to ads server
3164 * @param res Results of search
3165 * @return next message from result
3167 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3169 return ldap_next_message(ads->ldap.ld, res);
3173 * pull a single string from a ADS result
3174 * @param ads connection to ads server
3175 * @param mem_ctx TALLOC_CTX to use for allocating result string
3176 * @param msg Results of search
3177 * @param field Attribute to retrieve
3178 * @return Result string in talloc context
3180 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3181 const char *field)
3183 char **values;
3184 char *ret = NULL;
3185 char *ux_string;
3186 size_t converted_size;
3188 values = ldap_get_values(ads->ldap.ld, msg, field);
3189 if (!values)
3190 return NULL;
3192 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3193 &converted_size))
3195 ret = ux_string;
3197 ldap_value_free(values);
3198 return ret;
3202 * pull an array of strings from a ADS result
3203 * @param ads connection to ads server
3204 * @param mem_ctx TALLOC_CTX to use for allocating result string
3205 * @param msg Results of search
3206 * @param field Attribute to retrieve
3207 * @return Result strings in talloc context
3209 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3210 LDAPMessage *msg, const char *field,
3211 size_t *num_values)
3213 char **values;
3214 char **ret = NULL;
3215 size_t i, converted_size;
3217 values = ldap_get_values(ads->ldap.ld, msg, field);
3218 if (!values)
3219 return NULL;
3221 *num_values = ldap_count_values(values);
3223 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3224 if (!ret) {
3225 ldap_value_free(values);
3226 return NULL;
3229 for (i=0;i<*num_values;i++) {
3230 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3231 &converted_size))
3233 ldap_value_free(values);
3234 return NULL;
3237 ret[i] = NULL;
3239 ldap_value_free(values);
3240 return ret;
3244 * pull an array of strings from a ADS result
3245 * (handle large multivalue attributes with range retrieval)
3246 * @param ads connection to ads server
3247 * @param mem_ctx TALLOC_CTX to use for allocating result string
3248 * @param msg Results of search
3249 * @param field Attribute to retrieve
3250 * @param current_strings strings returned by a previous call to this function
3251 * @param next_attribute The next query should ask for this attribute
3252 * @param num_values How many values did we get this time?
3253 * @param more_values Are there more values to get?
3254 * @return Result strings in talloc context
3256 char **ads_pull_strings_range(ADS_STRUCT *ads,
3257 TALLOC_CTX *mem_ctx,
3258 LDAPMessage *msg, const char *field,
3259 char **current_strings,
3260 const char **next_attribute,
3261 size_t *num_strings,
3262 bool *more_strings)
3264 char *attr;
3265 char *expected_range_attrib, *range_attr = NULL;
3266 BerElement *ptr = NULL;
3267 char **strings;
3268 char **new_strings;
3269 size_t num_new_strings;
3270 unsigned long int range_start;
3271 unsigned long int range_end;
3273 /* we might have been given the whole lot anyway */
3274 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3275 *more_strings = False;
3276 return strings;
3279 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3281 /* look for Range result */
3282 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3283 attr;
3284 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3285 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3286 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3287 range_attr = attr;
3288 break;
3290 ldap_memfree(attr);
3292 if (!range_attr) {
3293 ber_free(ptr, 0);
3294 /* nothing here - this field is just empty */
3295 *more_strings = False;
3296 return NULL;
3299 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3300 &range_start, &range_end) == 2) {
3301 *more_strings = True;
3302 } else {
3303 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3304 &range_start) == 1) {
3305 *more_strings = False;
3306 } else {
3307 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3308 range_attr));
3309 ldap_memfree(range_attr);
3310 *more_strings = False;
3311 return NULL;
3315 if ((*num_strings) != range_start) {
3316 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3317 " - aborting range retrieval\n",
3318 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3319 ldap_memfree(range_attr);
3320 *more_strings = False;
3321 return NULL;
3324 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3326 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3327 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3328 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3329 range_attr, (unsigned long int)range_end - range_start + 1,
3330 (unsigned long int)num_new_strings));
3331 ldap_memfree(range_attr);
3332 *more_strings = False;
3333 return NULL;
3336 strings = talloc_realloc(mem_ctx, current_strings, char *,
3337 *num_strings + num_new_strings);
3339 if (strings == NULL) {
3340 ldap_memfree(range_attr);
3341 *more_strings = False;
3342 return NULL;
3345 if (new_strings && num_new_strings) {
3346 memcpy(&strings[*num_strings], new_strings,
3347 sizeof(*new_strings) * num_new_strings);
3350 (*num_strings) += num_new_strings;
3352 if (*more_strings) {
3353 *next_attribute = talloc_asprintf(mem_ctx,
3354 "%s;range=%d-*",
3355 field,
3356 (int)*num_strings);
3358 if (!*next_attribute) {
3359 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3360 ldap_memfree(range_attr);
3361 *more_strings = False;
3362 return NULL;
3366 ldap_memfree(range_attr);
3368 return strings;
3372 * pull a single uint32_t from a ADS result
3373 * @param ads connection to ads server
3374 * @param msg Results of search
3375 * @param field Attribute to retrieve
3376 * @param v Pointer to int to store result
3377 * @return boolean indicating success
3379 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3380 uint32_t *v)
3382 char **values;
3384 values = ldap_get_values(ads->ldap.ld, msg, field);
3385 if (!values)
3386 return False;
3387 if (!values[0]) {
3388 ldap_value_free(values);
3389 return False;
3392 *v = atoi(values[0]);
3393 ldap_value_free(values);
3394 return True;
3398 * pull a single objectGUID from an ADS result
3399 * @param ads connection to ADS server
3400 * @param msg results of search
3401 * @param guid 37-byte area to receive text guid
3402 * @return boolean indicating success
3404 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3406 DATA_BLOB blob;
3407 NTSTATUS status;
3409 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3410 &blob)) {
3411 return false;
3414 status = GUID_from_ndr_blob(&blob, guid);
3415 talloc_free(blob.data);
3416 return NT_STATUS_IS_OK(status);
3421 * pull a single struct dom_sid from a ADS result
3422 * @param ads connection to ads server
3423 * @param msg Results of search
3424 * @param field Attribute to retrieve
3425 * @param sid Pointer to sid to store result
3426 * @return boolean indicating success
3428 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3429 struct dom_sid *sid)
3431 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3435 * pull an array of struct dom_sids from a ADS result
3436 * @param ads connection to ads server
3437 * @param mem_ctx TALLOC_CTX for allocating sid array
3438 * @param msg Results of search
3439 * @param field Attribute to retrieve
3440 * @param sids pointer to sid array to allocate
3441 * @return the count of SIDs pulled
3443 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3444 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3446 struct berval **values;
3447 int count, i;
3449 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3451 if (!values)
3452 return 0;
3454 for (i=0; values[i]; i++)
3455 /* nop */ ;
3457 if (i) {
3458 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3459 if (!(*sids)) {
3460 ldap_value_free_len(values);
3461 return 0;
3463 } else {
3464 (*sids) = NULL;
3467 count = 0;
3468 for (i=0; values[i]; i++) {
3469 ssize_t ret;
3470 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3471 values[i]->bv_len, &(*sids)[count]);
3472 if (ret != -1) {
3473 struct dom_sid_buf buf;
3474 DBG_DEBUG("pulling SID: %s\n",
3475 dom_sid_str_buf(&(*sids)[count], &buf));
3476 count++;
3480 ldap_value_free_len(values);
3481 return count;
3485 * pull a struct security_descriptor from a ADS result
3486 * @param ads connection to ads server
3487 * @param mem_ctx TALLOC_CTX for allocating sid array
3488 * @param msg Results of search
3489 * @param field Attribute to retrieve
3490 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3491 * @return boolean indicating success
3493 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3494 LDAPMessage *msg, const char *field,
3495 struct security_descriptor **sd)
3497 struct berval **values;
3498 bool ret = true;
3500 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3502 if (!values) return false;
3504 if (values[0]) {
3505 NTSTATUS status;
3506 status = unmarshall_sec_desc(mem_ctx,
3507 (uint8_t *)values[0]->bv_val,
3508 values[0]->bv_len, sd);
3509 if (!NT_STATUS_IS_OK(status)) {
3510 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3511 nt_errstr(status)));
3512 ret = false;
3516 ldap_value_free_len(values);
3517 return ret;
3521 * in order to support usernames longer than 21 characters we need to
3522 * use both the sAMAccountName and the userPrincipalName attributes
3523 * It seems that not all users have the userPrincipalName attribute set
3525 * @param ads connection to ads server
3526 * @param mem_ctx TALLOC_CTX for allocating sid array
3527 * @param msg Results of search
3528 * @return the username
3530 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3531 LDAPMessage *msg)
3533 #if 0 /* JERRY */
3534 char *ret, *p;
3536 /* lookup_name() only works on the sAMAccountName to
3537 returning the username portion of userPrincipalName
3538 breaks winbindd_getpwnam() */
3540 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3541 if (ret && (p = strchr_m(ret, '@'))) {
3542 *p = 0;
3543 return ret;
3545 #endif
3546 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3551 * find the update serial number - this is the core of the ldap cache
3552 * @param ads connection to ads server
3553 * @param ads connection to ADS server
3554 * @param usn Pointer to retrieved update serial number
3555 * @return status of search
3557 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3559 const char *attrs[] = {"highestCommittedUSN", NULL};
3560 ADS_STATUS status;
3561 LDAPMessage *res;
3563 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3564 if (!ADS_ERR_OK(status))
3565 return status;
3567 if (ads_count_replies(ads, res) != 1) {
3568 ads_msgfree(ads, res);
3569 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3572 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3573 ads_msgfree(ads, res);
3574 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3577 ads_msgfree(ads, res);
3578 return ADS_SUCCESS;
3581 /* parse a ADS timestring - typical string is
3582 '20020917091222.0Z0' which means 09:12.22 17th September
3583 2002, timezone 0 */
3584 static time_t ads_parse_time(const char *str)
3586 struct tm tm;
3588 ZERO_STRUCT(tm);
3590 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3591 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3592 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3593 return 0;
3595 tm.tm_year -= 1900;
3596 tm.tm_mon -= 1;
3598 return timegm(&tm);
3601 /********************************************************************
3602 ********************************************************************/
3604 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3606 const char *attrs[] = {"currentTime", NULL};
3607 ADS_STATUS status;
3608 LDAPMessage *res;
3609 char *timestr;
3610 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3611 ADS_STRUCT *ads_s = ads;
3613 /* establish a new ldap tcp session if necessary */
3615 if ( !ads->ldap.ld ) {
3617 * ADS_STRUCT may be being reused after a
3618 * DC lookup, so ads->ldap.ss may already have a
3619 * good address. If not, re-initialize the passed-in
3620 * ADS_STRUCT with the given server.XXXX parameters.
3622 * Note that this doesn't depend on
3623 * ads->server.ldap_server != NULL,
3624 * as the case where ads->server.ldap_server==NULL and
3625 * ads->ldap.ss != zero_address is precisely the DC
3626 * lookup case where ads->ldap.ss was found by going
3627 * through ads_find_dc() again we want to avoid repeating.
3629 if (is_zero_addr(&ads->ldap.ss)) {
3630 ads_s = ads_init(tmp_ctx,
3631 ads->server.realm,
3632 ads->server.workgroup,
3633 ads->server.ldap_server,
3634 ADS_SASL_PLAIN );
3635 if (ads_s == NULL) {
3636 status = ADS_ERROR(LDAP_NO_MEMORY);
3637 goto done;
3642 * Reset ads->config.flags as it can contain the flags
3643 * returned by the previous CLDAP ping when reusing the struct.
3645 ads_s->config.flags = 0;
3647 status = ads_connect_simple_anon(ads_s);
3648 if ( !ADS_ERR_OK(status))
3649 goto done;
3652 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3653 if (!ADS_ERR_OK(status)) {
3654 goto done;
3657 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3658 if (!timestr) {
3659 ads_msgfree(ads_s, res);
3660 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3661 goto done;
3664 /* but save the time and offset in the original ADS_STRUCT */
3666 ads->config.current_time = ads_parse_time(timestr);
3668 if (ads->config.current_time != 0) {
3669 ads->config.time_offset = ads->config.current_time - time(NULL);
3670 DBG_INFO("server time offset is %d seconds\n",
3671 ads->config.time_offset);
3672 } else {
3673 ads->config.time_offset = 0;
3676 DBG_INFO("server time offset is %d seconds\n",
3677 ads->config.time_offset);
3679 ads_msgfree(ads, res);
3681 status = ADS_SUCCESS;
3683 done:
3684 TALLOC_FREE(tmp_ctx);
3686 return status;
3689 /********************************************************************
3690 ********************************************************************/
3692 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3694 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3695 const char *attrs[] = {"domainFunctionality", NULL};
3696 ADS_STATUS status;
3697 LDAPMessage *res;
3698 ADS_STRUCT *ads_s = ads;
3700 *val = DS_DOMAIN_FUNCTION_2000;
3702 /* establish a new ldap tcp session if necessary */
3704 if ( !ads->ldap.ld ) {
3706 * ADS_STRUCT may be being reused after a
3707 * DC lookup, so ads->ldap.ss may already have a
3708 * good address. If not, re-initialize the passed-in
3709 * ADS_STRUCT with the given server.XXXX parameters.
3711 * Note that this doesn't depend on
3712 * ads->server.ldap_server != NULL,
3713 * as the case where ads->server.ldap_server==NULL and
3714 * ads->ldap.ss != zero_address is precisely the DC
3715 * lookup case where ads->ldap.ss was found by going
3716 * through ads_find_dc() again we want to avoid repeating.
3718 if (is_zero_addr(&ads->ldap.ss)) {
3719 ads_s = ads_init(tmp_ctx,
3720 ads->server.realm,
3721 ads->server.workgroup,
3722 ads->server.ldap_server,
3723 ADS_SASL_PLAIN );
3724 if (ads_s == NULL ) {
3725 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3726 goto done;
3731 * Reset ads->config.flags as it can contain the flags
3732 * returned by the previous CLDAP ping when reusing the struct.
3734 ads_s->config.flags = 0;
3736 status = ads_connect_simple_anon(ads_s);
3737 if ( !ADS_ERR_OK(status))
3738 goto done;
3741 /* If the attribute does not exist assume it is a Windows 2000
3742 functional domain */
3744 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3745 if (!ADS_ERR_OK(status)) {
3746 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3747 status = ADS_SUCCESS;
3749 goto done;
3752 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3753 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3755 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3758 ads_msgfree(ads_s, res);
3760 done:
3761 TALLOC_FREE(tmp_ctx);
3763 return status;
3767 * find the domain sid for our domain
3768 * @param ads connection to ads server
3769 * @param sid Pointer to domain sid
3770 * @return status of search
3772 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3774 const char *attrs[] = {"objectSid", NULL};
3775 LDAPMessage *res;
3776 ADS_STATUS rc;
3778 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3779 attrs, &res);
3780 if (!ADS_ERR_OK(rc)) return rc;
3781 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3782 ads_msgfree(ads, res);
3783 return ADS_ERROR_SYSTEM(ENOENT);
3785 ads_msgfree(ads, res);
3787 return ADS_SUCCESS;
3791 * find our site name
3792 * @param ads connection to ads server
3793 * @param mem_ctx Pointer to talloc context
3794 * @param site_name Pointer to the sitename
3795 * @return status of search
3797 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3799 ADS_STATUS status;
3800 LDAPMessage *res;
3801 const char *dn, *service_name;
3802 const char *attrs[] = { "dsServiceName", NULL };
3804 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3805 if (!ADS_ERR_OK(status)) {
3806 return status;
3809 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3810 if (service_name == NULL) {
3811 ads_msgfree(ads, res);
3812 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3815 ads_msgfree(ads, res);
3817 /* go up three levels */
3818 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3819 if (dn == NULL) {
3820 return ADS_ERROR(LDAP_NO_MEMORY);
3823 *site_name = talloc_strdup(mem_ctx, dn);
3824 if (*site_name == NULL) {
3825 return ADS_ERROR(LDAP_NO_MEMORY);
3828 return status;
3830 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3835 * find the site dn where a machine resides
3836 * @param ads connection to ads server
3837 * @param mem_ctx Pointer to talloc context
3838 * @param computer_name name of the machine
3839 * @param site_name Pointer to the sitename
3840 * @return status of search
3842 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3844 ADS_STATUS status;
3845 LDAPMessage *res;
3846 const char *parent, *filter;
3847 char *config_context = NULL;
3848 char *dn;
3850 /* shortcut a query */
3851 if (strequal(computer_name, ads->config.ldap_server_name)) {
3852 return ads_site_dn(ads, mem_ctx, site_dn);
3855 status = ads_config_path(ads, mem_ctx, &config_context);
3856 if (!ADS_ERR_OK(status)) {
3857 return status;
3860 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3861 if (filter == NULL) {
3862 return ADS_ERROR(LDAP_NO_MEMORY);
3865 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3866 filter, NULL, &res);
3867 if (!ADS_ERR_OK(status)) {
3868 return status;
3871 if (ads_count_replies(ads, res) != 1) {
3872 ads_msgfree(ads, res);
3873 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3876 dn = ads_get_dn(ads, mem_ctx, res);
3877 if (dn == NULL) {
3878 ads_msgfree(ads, res);
3879 return ADS_ERROR(LDAP_NO_MEMORY);
3882 /* go up three levels */
3883 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3884 if (parent == NULL) {
3885 ads_msgfree(ads, res);
3886 TALLOC_FREE(dn);
3887 return ADS_ERROR(LDAP_NO_MEMORY);
3890 *site_dn = talloc_strdup(mem_ctx, parent);
3891 if (*site_dn == NULL) {
3892 ads_msgfree(ads, res);
3893 TALLOC_FREE(dn);
3894 return ADS_ERROR(LDAP_NO_MEMORY);
3897 TALLOC_FREE(dn);
3898 ads_msgfree(ads, res);
3900 return status;
3904 * get the upn suffixes for a domain
3905 * @param ads connection to ads server
3906 * @param mem_ctx Pointer to talloc context
3907 * @param suffixes Pointer to an array of suffixes
3908 * @param num_suffixes Pointer to the number of suffixes
3909 * @return status of search
3911 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3913 ADS_STATUS status;
3914 LDAPMessage *res;
3915 const char *base;
3916 char *config_context = NULL;
3917 const char *attrs[] = { "uPNSuffixes", NULL };
3919 status = ads_config_path(ads, mem_ctx, &config_context);
3920 if (!ADS_ERR_OK(status)) {
3921 return status;
3924 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3925 if (base == NULL) {
3926 return ADS_ERROR(LDAP_NO_MEMORY);
3929 status = ads_search_dn(ads, &res, base, attrs);
3930 if (!ADS_ERR_OK(status)) {
3931 return status;
3934 if (ads_count_replies(ads, res) != 1) {
3935 ads_msgfree(ads, res);
3936 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3939 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3940 if ((*suffixes) == NULL) {
3941 ads_msgfree(ads, res);
3942 return ADS_ERROR(LDAP_NO_MEMORY);
3945 ads_msgfree(ads, res);
3947 return status;
3951 * get the joinable ous for a domain
3952 * @param ads connection to ads server
3953 * @param mem_ctx Pointer to talloc context
3954 * @param ous Pointer to an array of ous
3955 * @param num_ous Pointer to the number of ous
3956 * @return status of search
3958 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3959 TALLOC_CTX *mem_ctx,
3960 char ***ous,
3961 size_t *num_ous)
3963 ADS_STATUS status;
3964 LDAPMessage *res = NULL;
3965 LDAPMessage *msg = NULL;
3966 const char *attrs[] = { "dn", NULL };
3967 int count = 0;
3969 status = ads_search(ads, &res,
3970 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3971 attrs);
3972 if (!ADS_ERR_OK(status)) {
3973 return status;
3976 count = ads_count_replies(ads, res);
3977 if (count < 1) {
3978 ads_msgfree(ads, res);
3979 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3982 for (msg = ads_first_entry(ads, res); msg;
3983 msg = ads_next_entry(ads, msg)) {
3984 const char **p = discard_const_p(const char *, *ous);
3985 char *dn = NULL;
3987 dn = ads_get_dn(ads, talloc_tos(), msg);
3988 if (!dn) {
3989 ads_msgfree(ads, res);
3990 return ADS_ERROR(LDAP_NO_MEMORY);
3993 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3994 TALLOC_FREE(dn);
3995 ads_msgfree(ads, res);
3996 return ADS_ERROR(LDAP_NO_MEMORY);
3999 TALLOC_FREE(dn);
4000 *ous = discard_const_p(char *, p);
4003 ads_msgfree(ads, res);
4005 return status;
4010 * pull a struct dom_sid from an extended dn string
4011 * @param mem_ctx TALLOC_CTX
4012 * @param extended_dn string
4013 * @param flags string type of extended_dn
4014 * @param sid pointer to a struct dom_sid
4015 * @return NT_STATUS_OK on success,
4016 * NT_INVALID_PARAMETER on error,
4017 * NT_STATUS_NOT_FOUND if no SID present
4019 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
4020 const char *extended_dn,
4021 enum ads_extended_dn_flags flags,
4022 struct dom_sid *sid)
4024 char *p, *q, *dn;
4026 if (!extended_dn) {
4027 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4030 /* otherwise extended_dn gets stripped off */
4031 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
4032 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4035 * ADS_EXTENDED_DN_HEX_STRING:
4036 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4038 * ADS_EXTENDED_DN_STRING (only with w2k3):
4039 * <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
4041 * Object with no SID, such as an Exchange Public Folder
4042 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4045 p = strchr(dn, ';');
4046 if (!p) {
4047 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4050 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
4051 DEBUG(5,("No SID present in extended dn\n"));
4052 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
4055 p += strlen(";<SID=");
4057 q = strchr(p, '>');
4058 if (!q) {
4059 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4062 *q = '\0';
4064 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
4066 switch (flags) {
4068 case ADS_EXTENDED_DN_STRING:
4069 if (!string_to_sid(sid, p)) {
4070 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4072 break;
4073 case ADS_EXTENDED_DN_HEX_STRING: {
4074 ssize_t ret;
4075 fstring buf;
4076 size_t buf_len;
4078 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
4079 if (buf_len == 0) {
4080 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4083 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
4084 if (ret == -1) {
4085 DEBUG(10,("failed to parse sid\n"));
4086 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4088 break;
4090 default:
4091 DEBUG(10,("unknown extended dn format\n"));
4092 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4095 return ADS_ERROR_NT(NT_STATUS_OK);
4098 /********************************************************************
4099 ********************************************************************/
4101 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4103 LDAPMessage *res = NULL;
4104 ADS_STATUS status;
4105 int count = 0;
4106 char *name = NULL;
4108 status = ads_find_machine_acct(ads, &res, machine_name);
4109 if (!ADS_ERR_OK(status)) {
4110 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4111 lp_netbios_name()));
4112 goto out;
4115 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4116 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4117 goto out;
4120 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4121 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4124 out:
4125 ads_msgfree(ads, res);
4127 return name;
4130 #if 0
4132 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4135 * Join a machine to a realm
4136 * Creates the machine account and sets the machine password
4137 * @param ads connection to ads server
4138 * @param machine name of host to add
4139 * @param org_unit Organizational unit to place machine in
4140 * @return status of join
4142 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4143 uint32_t account_type, const char *org_unit)
4145 ADS_STATUS status;
4146 LDAPMessage *res = NULL;
4147 char *machine;
4149 /* machine name must be lowercase */
4150 machine = SMB_STRDUP(machine_name);
4151 strlower_m(machine);
4154 status = ads_find_machine_acct(ads, (void **)&res, machine);
4155 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4156 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4157 status = ads_leave_realm(ads, machine);
4158 if (!ADS_ERR_OK(status)) {
4159 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4160 machine, ads->config.realm));
4161 return status;
4165 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4166 if (!ADS_ERR_OK(status)) {
4167 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4168 SAFE_FREE(machine);
4169 return status;
4172 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4173 if (!ADS_ERR_OK(status)) {
4174 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4175 SAFE_FREE(machine);
4176 return status;
4179 SAFE_FREE(machine);
4180 ads_msgfree(ads, res);
4182 return status;
4184 #endif
4187 * Delete a machine from the realm
4188 * @param ads connection to ads server
4189 * @param hostname Machine to remove
4190 * @return status of delete
4192 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4194 ADS_STATUS status;
4195 void *msg;
4196 LDAPMessage *res;
4197 char *hostnameDN, *host;
4198 int rc;
4199 LDAPControl ldap_control;
4200 LDAPControl * pldap_control[2] = {NULL, NULL};
4202 pldap_control[0] = &ldap_control;
4203 memset(&ldap_control, 0, sizeof(LDAPControl));
4204 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4206 /* hostname must be lowercase */
4207 host = SMB_STRDUP(hostname);
4208 if (!strlower_m(host)) {
4209 SAFE_FREE(host);
4210 return ADS_ERROR_SYSTEM(EINVAL);
4213 status = ads_find_machine_acct(ads, &res, host);
4214 if (!ADS_ERR_OK(status)) {
4215 DEBUG(0, ("Host account for %s does not exist.\n", host));
4216 SAFE_FREE(host);
4217 return status;
4220 msg = ads_first_entry(ads, res);
4221 if (!msg) {
4222 SAFE_FREE(host);
4223 return ADS_ERROR_SYSTEM(ENOENT);
4226 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4227 if (hostnameDN == NULL) {
4228 SAFE_FREE(host);
4229 return ADS_ERROR_SYSTEM(ENOENT);
4232 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4233 if (rc) {
4234 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4235 }else {
4236 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4239 if (rc != LDAP_SUCCESS) {
4240 const char *attrs[] = { "cn", NULL };
4241 LDAPMessage *msg_sub;
4243 /* we only search with scope ONE, we do not expect any further
4244 * objects to be created deeper */
4246 status = ads_do_search_retry(ads, hostnameDN,
4247 LDAP_SCOPE_ONELEVEL,
4248 "(objectclass=*)", attrs, &res);
4250 if (!ADS_ERR_OK(status)) {
4251 SAFE_FREE(host);
4252 TALLOC_FREE(hostnameDN);
4253 return status;
4256 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4257 msg_sub = ads_next_entry(ads, msg_sub)) {
4259 char *dn = NULL;
4261 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4262 SAFE_FREE(host);
4263 TALLOC_FREE(hostnameDN);
4264 return ADS_ERROR(LDAP_NO_MEMORY);
4267 status = ads_del_dn(ads, dn);
4268 if (!ADS_ERR_OK(status)) {
4269 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4270 SAFE_FREE(host);
4271 TALLOC_FREE(dn);
4272 TALLOC_FREE(hostnameDN);
4273 return status;
4276 TALLOC_FREE(dn);
4279 /* there should be no subordinate objects anymore */
4280 status = ads_do_search_retry(ads, hostnameDN,
4281 LDAP_SCOPE_ONELEVEL,
4282 "(objectclass=*)", attrs, &res);
4284 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4285 SAFE_FREE(host);
4286 TALLOC_FREE(hostnameDN);
4287 return status;
4290 /* delete hostnameDN now */
4291 status = ads_del_dn(ads, hostnameDN);
4292 if (!ADS_ERR_OK(status)) {
4293 SAFE_FREE(host);
4294 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4295 TALLOC_FREE(hostnameDN);
4296 return status;
4300 TALLOC_FREE(hostnameDN);
4302 status = ads_find_machine_acct(ads, &res, host);
4303 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4304 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4305 DEBUG(3, ("Failed to remove host account.\n"));
4306 SAFE_FREE(host);
4307 return status;
4310 SAFE_FREE(host);
4311 return ADS_SUCCESS;
4315 * pull all token-sids from an LDAP dn
4316 * @param ads connection to ads server
4317 * @param mem_ctx TALLOC_CTX for allocating sid array
4318 * @param dn of LDAP object
4319 * @param user_sid pointer to struct dom_sid (objectSid)
4320 * @param primary_group_sid pointer to struct dom_sid (self composed)
4321 * @param sids pointer to sid array to allocate
4322 * @param num_sids counter of SIDs pulled
4323 * @return status of token query
4325 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4326 TALLOC_CTX *mem_ctx,
4327 const char *dn,
4328 struct dom_sid *user_sid,
4329 struct dom_sid *primary_group_sid,
4330 struct dom_sid **sids,
4331 size_t *num_sids)
4333 ADS_STATUS status;
4334 LDAPMessage *res = NULL;
4335 int count = 0;
4336 size_t tmp_num_sids;
4337 struct dom_sid *tmp_sids;
4338 struct dom_sid tmp_user_sid;
4339 struct dom_sid tmp_primary_group_sid;
4340 uint32_t pgid;
4341 const char *attrs[] = {
4342 "objectSid",
4343 "tokenGroups",
4344 "primaryGroupID",
4345 NULL
4348 status = ads_search_retry_dn(ads, &res, dn, attrs);
4349 if (!ADS_ERR_OK(status)) {
4350 return status;
4353 count = ads_count_replies(ads, res);
4354 if (count != 1) {
4355 ads_msgfree(ads, res);
4356 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4359 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4360 ads_msgfree(ads, res);
4361 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4364 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4365 ads_msgfree(ads, res);
4366 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4370 /* hack to compose the primary group sid without knowing the
4371 * domsid */
4373 struct dom_sid domsid;
4375 sid_copy(&domsid, &tmp_user_sid);
4377 if (!sid_split_rid(&domsid, NULL)) {
4378 ads_msgfree(ads, res);
4379 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4382 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4383 ads_msgfree(ads, res);
4384 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4388 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4390 if (tmp_num_sids == 0 || !tmp_sids) {
4391 ads_msgfree(ads, res);
4392 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4395 if (num_sids) {
4396 *num_sids = tmp_num_sids;
4399 if (sids) {
4400 *sids = tmp_sids;
4403 if (user_sid) {
4404 *user_sid = tmp_user_sid;
4407 if (primary_group_sid) {
4408 *primary_group_sid = tmp_primary_group_sid;
4411 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4413 ads_msgfree(ads, res);
4414 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4418 * Find a sAMAccountName in LDAP
4419 * @param ads connection to ads server
4420 * @param mem_ctx TALLOC_CTX for allocating sid array
4421 * @param samaccountname to search
4422 * @param uac_ret uint32_t pointer userAccountControl attribute value
4423 * @param dn_ret pointer to dn
4424 * @return status of token query
4426 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4427 TALLOC_CTX *mem_ctx,
4428 const char *samaccountname,
4429 uint32_t *uac_ret,
4430 const char **dn_ret)
4432 ADS_STATUS status;
4433 const char *attrs[] = { "userAccountControl", NULL };
4434 const char *filter;
4435 LDAPMessage *res = NULL;
4436 char *dn = NULL;
4437 uint32_t uac = 0;
4439 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4440 samaccountname);
4441 if (filter == NULL) {
4442 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4443 goto out;
4446 status = ads_do_search_all(ads, ads->config.bind_path,
4447 LDAP_SCOPE_SUBTREE,
4448 filter, attrs, &res);
4450 if (!ADS_ERR_OK(status)) {
4451 goto out;
4454 if (ads_count_replies(ads, res) != 1) {
4455 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4456 goto out;
4459 dn = ads_get_dn(ads, talloc_tos(), res);
4460 if (dn == NULL) {
4461 status = ADS_ERROR(LDAP_NO_MEMORY);
4462 goto out;
4465 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4466 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4467 goto out;
4470 if (uac_ret) {
4471 *uac_ret = uac;
4474 if (dn_ret) {
4475 *dn_ret = talloc_strdup(mem_ctx, dn);
4476 if (!*dn_ret) {
4477 status = ADS_ERROR(LDAP_NO_MEMORY);
4478 goto out;
4481 out:
4482 TALLOC_FREE(dn);
4483 ads_msgfree(ads, res);
4485 return status;
4489 * find our configuration path
4490 * @param ads connection to ads server
4491 * @param mem_ctx Pointer to talloc context
4492 * @param config_path Pointer to the config path
4493 * @return status of search
4495 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4496 TALLOC_CTX *mem_ctx,
4497 char **config_path)
4499 ADS_STATUS status;
4500 LDAPMessage *res = NULL;
4501 const char *config_context = NULL;
4502 const char *attrs[] = { "configurationNamingContext", NULL };
4504 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4505 "(objectclass=*)", attrs, &res);
4506 if (!ADS_ERR_OK(status)) {
4507 return status;
4510 config_context = ads_pull_string(ads, mem_ctx, res,
4511 "configurationNamingContext");
4512 ads_msgfree(ads, res);
4513 if (!config_context) {
4514 return ADS_ERROR(LDAP_NO_MEMORY);
4517 if (config_path) {
4518 *config_path = talloc_strdup(mem_ctx, config_context);
4519 if (!*config_path) {
4520 return ADS_ERROR(LDAP_NO_MEMORY);
4524 return ADS_ERROR(LDAP_SUCCESS);
4528 * find the displayName of an extended right
4529 * @param ads connection to ads server
4530 * @param config_path The config path
4531 * @param mem_ctx Pointer to talloc context
4532 * @param GUID struct of the rightsGUID
4533 * @return status of search
4535 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4536 const char *config_path,
4537 TALLOC_CTX *mem_ctx,
4538 const struct GUID *rights_guid)
4540 ADS_STATUS rc;
4541 LDAPMessage *res = NULL;
4542 char *expr = NULL;
4543 const char *attrs[] = { "displayName", NULL };
4544 const char *result = NULL;
4545 const char *path;
4547 if (!ads || !mem_ctx || !rights_guid) {
4548 goto done;
4551 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4552 GUID_string(mem_ctx, rights_guid));
4553 if (!expr) {
4554 goto done;
4557 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4558 if (!path) {
4559 goto done;
4562 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4563 expr, attrs, &res);
4564 if (!ADS_ERR_OK(rc)) {
4565 goto done;
4568 if (ads_count_replies(ads, res) != 1) {
4569 goto done;
4572 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4574 done:
4575 ads_msgfree(ads, res);
4576 return result;
4580 * verify or build and verify an account ou
4581 * @param mem_ctx Pointer to talloc context
4582 * @param ads connection to ads server
4583 * @param account_ou
4584 * @return status of search
4587 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4588 ADS_STRUCT *ads,
4589 const char **account_ou)
4591 char **exploded_dn;
4592 const char *name;
4593 char *ou_string;
4595 if (account_ou == NULL) {
4596 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4599 if (*account_ou != NULL) {
4600 exploded_dn = ldap_explode_dn(*account_ou, 0);
4601 if (exploded_dn) {
4602 ldap_value_free(exploded_dn);
4603 return ADS_SUCCESS;
4607 ou_string = ads_ou_string(ads, *account_ou);
4608 if (!ou_string) {
4609 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4612 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4613 ads->config.bind_path);
4614 SAFE_FREE(ou_string);
4616 if (!name) {
4617 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4620 exploded_dn = ldap_explode_dn(name, 0);
4621 if (!exploded_dn) {
4622 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4624 ldap_value_free(exploded_dn);
4626 *account_ou = name;
4627 return ADS_SUCCESS;
4630 #endif