selftest/Samba4: make use of get_cmd_env_vars() to setup all relevant env variables
[Samba.git] / source3 / libads / ldap.c
blob08f3a138e822d3a530f323e78e31039481f7b2ef
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-AdditionalDnsHostName",
1867 "msDS-SupportedEncryptionTypes",
1868 "nTSecurityDescriptor",
1869 "objectSid",
1871 NULL
1873 TALLOC_CTX *frame = talloc_stackframe();
1875 *res = NULL;
1877 /* the easiest way to find a machine account anywhere in the tree
1878 is to look for hostname$ */
1879 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1880 if (expr == NULL) {
1881 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1882 goto done;
1885 status = ads_search(ads, res, expr, attrs);
1886 if (ADS_ERR_OK(status)) {
1887 if (ads_count_replies(ads, *res) != 1) {
1888 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1892 done:
1893 TALLOC_FREE(frame);
1894 return status;
1898 * Initialize a list of mods to be used in a modify request
1899 * @param ctx An initialized TALLOC_CTX
1900 * @return allocated ADS_MODLIST
1902 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1904 #define ADS_MODLIST_ALLOC_SIZE 10
1905 LDAPMod **mods;
1907 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1908 /* -1 is safety to make sure we don't go over the end.
1909 need to reset it to NULL before doing ldap modify */
1910 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1912 return (ADS_MODLIST)mods;
1917 add an attribute to the list, with values list already constructed
1919 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1920 int mod_op, const char *name,
1921 const void *_invals)
1923 int curmod;
1924 LDAPMod **modlist = (LDAPMod **) *mods;
1925 struct berval **ber_values = NULL;
1926 char **char_values = NULL;
1928 if (!_invals) {
1929 mod_op = LDAP_MOD_DELETE;
1930 } else {
1931 if (mod_op & LDAP_MOD_BVALUES) {
1932 const struct berval **b;
1933 b = discard_const_p(const struct berval *, _invals);
1934 ber_values = ads_dup_values(ctx, b);
1935 } else {
1936 const char **c;
1937 c = discard_const_p(const char *, _invals);
1938 char_values = ads_push_strvals(ctx, c);
1942 /* find the first empty slot */
1943 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1944 curmod++);
1945 if (modlist[curmod] == (LDAPMod *) -1) {
1946 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1947 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1948 return ADS_ERROR(LDAP_NO_MEMORY);
1949 memset(&modlist[curmod], 0,
1950 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1951 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1952 *mods = (ADS_MODLIST)modlist;
1955 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1956 return ADS_ERROR(LDAP_NO_MEMORY);
1957 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1958 if (mod_op & LDAP_MOD_BVALUES) {
1959 modlist[curmod]->mod_bvalues = ber_values;
1960 } else if (mod_op & LDAP_MOD_DELETE) {
1961 modlist[curmod]->mod_values = NULL;
1962 } else {
1963 modlist[curmod]->mod_values = char_values;
1966 modlist[curmod]->mod_op = mod_op;
1967 return ADS_ERROR(LDAP_SUCCESS);
1971 * Add a single string value to a mod list
1972 * @param ctx An initialized TALLOC_CTX
1973 * @param mods An initialized ADS_MODLIST
1974 * @param name The attribute name to add
1975 * @param val The value to add - NULL means DELETE
1976 * @return ADS STATUS indicating success of add
1978 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1979 const char *name, const char *val)
1981 const char *values[2];
1983 values[0] = val;
1984 values[1] = NULL;
1986 if (!val)
1987 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1988 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1992 * Add an array of string values to a mod list
1993 * @param ctx An initialized TALLOC_CTX
1994 * @param mods An initialized ADS_MODLIST
1995 * @param name The attribute name to add
1996 * @param vals The array of string values to add - NULL means DELETE
1997 * @return ADS STATUS indicating success of add
1999 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2000 const char *name, const char **vals)
2002 if (!vals)
2003 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
2004 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
2005 name, (const void **) vals);
2009 * Add a single ber-encoded value to a mod list
2010 * @param ctx An initialized TALLOC_CTX
2011 * @param mods An initialized ADS_MODLIST
2012 * @param name The attribute name to add
2013 * @param val The value to add - NULL means DELETE
2014 * @return ADS STATUS indicating success of add
2016 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2017 const char *name, const struct berval *val)
2019 const struct berval *values[2];
2021 values[0] = val;
2022 values[1] = NULL;
2023 if (!val)
2024 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
2025 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
2026 name, (const void *) values);
2029 static void ads_print_error(int ret, LDAP *ld)
2031 if (ret != 0) {
2032 char *ld_error = NULL;
2033 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
2034 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
2035 ret,
2036 ldap_err2string(ret),
2037 ld_error);
2038 SAFE_FREE(ld_error);
2043 * Perform an ldap modify
2044 * @param ads connection to ads server
2045 * @param mod_dn DistinguishedName to modify
2046 * @param mods list of modifications to perform
2047 * @return status of modify
2049 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
2051 int ret,i;
2052 char *utf8_dn = NULL;
2053 size_t converted_size;
2055 this control is needed to modify that contains a currently
2056 non-existent attribute (but allowable for the object) to run
2058 LDAPControl PermitModify = {
2059 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
2060 {0, NULL},
2061 (char) 1};
2062 LDAPControl *controls[2];
2064 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
2066 controls[0] = &PermitModify;
2067 controls[1] = NULL;
2069 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
2070 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2073 /* find the end of the list, marked by NULL or -1 */
2074 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2075 /* make sure the end of the list is NULL */
2076 mods[i] = NULL;
2077 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
2078 (LDAPMod **) mods, controls, NULL);
2079 ads_print_error(ret, ads->ldap.ld);
2080 TALLOC_FREE(utf8_dn);
2081 return ADS_ERROR(ret);
2085 * Perform an ldap add
2086 * @param ads connection to ads server
2087 * @param new_dn DistinguishedName to add
2088 * @param mods list of attributes and values for DN
2089 * @return status of add
2091 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
2093 int ret, i;
2094 char *utf8_dn = NULL;
2095 size_t converted_size;
2097 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
2099 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
2100 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2101 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2104 /* find the end of the list, marked by NULL or -1 */
2105 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2106 /* make sure the end of the list is NULL */
2107 mods[i] = NULL;
2109 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
2110 ads_print_error(ret, ads->ldap.ld);
2111 TALLOC_FREE(utf8_dn);
2112 return ADS_ERROR(ret);
2116 * Delete a DistinguishedName
2117 * @param ads connection to ads server
2118 * @param new_dn DistinguishedName to delete
2119 * @return status of delete
2121 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
2123 int ret;
2124 char *utf8_dn = NULL;
2125 size_t converted_size;
2126 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
2127 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2128 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2131 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
2133 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
2134 ads_print_error(ret, ads->ldap.ld);
2135 TALLOC_FREE(utf8_dn);
2136 return ADS_ERROR(ret);
2140 * Build an org unit string
2141 * if org unit is Computers or blank then assume a container, otherwise
2142 * assume a / separated list of organisational units.
2143 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2144 * @param ads connection to ads server
2145 * @param org_unit Organizational unit
2146 * @return org unit string - caller must free
2148 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
2150 ADS_STATUS status;
2151 char *ret = NULL;
2152 char *dn = NULL;
2154 if (!org_unit || !*org_unit) {
2156 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
2158 /* samba4 might not yet respond to a wellknownobject-query */
2159 return ret ? ret : SMB_STRDUP("cn=Computers");
2162 if (strequal(org_unit, "Computers")) {
2163 return SMB_STRDUP("cn=Computers");
2166 /* jmcd: removed "\\" from the separation chars, because it is
2167 needed as an escape for chars like '#' which are valid in an
2168 OU name */
2169 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
2170 if (!ADS_ERR_OK(status)) {
2171 return NULL;
2174 return dn;
2178 * Get a org unit string for a well-known GUID
2179 * @param ads connection to ads server
2180 * @param wknguid Well known GUID
2181 * @return org unit string - caller must free
2183 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2185 ADS_STATUS status;
2186 LDAPMessage *res = NULL;
2187 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2188 **bind_dn_exp = NULL;
2189 const char *attrs[] = {"distinguishedName", NULL};
2190 int new_ln, wkn_ln, bind_ln, i;
2192 if (wknguid == NULL) {
2193 return NULL;
2196 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2197 DEBUG(1, ("asprintf failed!\n"));
2198 return NULL;
2201 status = ads_search_dn(ads, &res, base, attrs);
2202 if (!ADS_ERR_OK(status)) {
2203 DEBUG(1,("Failed while searching for: %s\n", base));
2204 goto out;
2207 if (ads_count_replies(ads, res) != 1) {
2208 goto out;
2211 /* substitute the bind-path from the well-known-guid-search result */
2212 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2213 if (!wkn_dn) {
2214 goto out;
2217 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2218 if (!wkn_dn_exp) {
2219 goto out;
2222 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2223 if (!bind_dn_exp) {
2224 goto out;
2227 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2229 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2232 new_ln = wkn_ln - bind_ln;
2234 ret = SMB_STRDUP(wkn_dn_exp[0]);
2235 if (!ret) {
2236 goto out;
2239 for (i=1; i < new_ln; i++) {
2240 char *s = NULL;
2242 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2243 SAFE_FREE(ret);
2244 goto out;
2247 SAFE_FREE(ret);
2248 ret = SMB_STRDUP(s);
2249 free(s);
2250 if (!ret) {
2251 goto out;
2255 out:
2256 SAFE_FREE(base);
2257 ads_msgfree(ads, res);
2258 TALLOC_FREE(wkn_dn);
2259 if (wkn_dn_exp) {
2260 ldap_value_free(wkn_dn_exp);
2262 if (bind_dn_exp) {
2263 ldap_value_free(bind_dn_exp);
2266 return ret;
2270 * Adds (appends) an item to an attribute array, rather then
2271 * replacing the whole list
2272 * @param ctx An initialized TALLOC_CTX
2273 * @param mods An initialized ADS_MODLIST
2274 * @param name name of the ldap attribute to append to
2275 * @param vals an array of values to add
2276 * @return status of addition
2279 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2280 const char *name, const char **vals)
2282 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2283 (const void *) vals);
2287 * Determines the an account's current KVNO via an LDAP lookup
2288 * @param ads An initialized ADS_STRUCT
2289 * @param account_name the NT samaccountname.
2290 * @return the kvno for the account, or -1 in case of a failure.
2293 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
2295 LDAPMessage *res = NULL;
2296 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
2297 char *filter;
2298 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
2299 char *dn_string = NULL;
2300 ADS_STATUS ret;
2302 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
2303 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
2304 return kvno;
2306 ret = ads_search(ads, &res, filter, attrs);
2307 SAFE_FREE(filter);
2308 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
2309 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
2310 ads_msgfree(ads, res);
2311 return kvno;
2314 dn_string = ads_get_dn(ads, talloc_tos(), res);
2315 if (!dn_string) {
2316 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2317 ads_msgfree(ads, res);
2318 return kvno;
2320 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
2321 TALLOC_FREE(dn_string);
2323 /* ---------------------------------------------------------
2324 * 0 is returned as a default KVNO from this point on...
2325 * This is done because Windows 2000 does not support key
2326 * version numbers. Chances are that a failure in the next
2327 * step is simply due to Windows 2000 being used for a
2328 * domain controller. */
2329 kvno = 0;
2331 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2332 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2333 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2334 ads_msgfree(ads, res);
2335 return kvno;
2338 /* Success */
2339 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2340 ads_msgfree(ads, res);
2341 return kvno;
2345 * Determines the computer account's current KVNO via an LDAP lookup
2346 * @param ads An initialized ADS_STRUCT
2347 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2348 * @return the kvno for the computer account, or -1 in case of a failure.
2351 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2353 char *computer_account = NULL;
2354 uint32_t kvno = -1;
2356 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2357 return kvno;
2360 kvno = ads_get_kvno(ads, computer_account);
2361 free(computer_account);
2363 return kvno;
2367 * This clears out all registered spn's for a given hostname
2368 * @param ads An initialized ADS_STRUCT
2369 * @param machine_name the NetBIOS name of the computer.
2370 * @return 0 upon success, non-zero otherwise.
2373 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2375 TALLOC_CTX *ctx;
2376 LDAPMessage *res = NULL;
2377 ADS_MODLIST mods;
2378 const char *servicePrincipalName[1] = {NULL};
2379 ADS_STATUS ret;
2380 char *dn_string = NULL;
2382 ret = ads_find_machine_acct(ads, &res, machine_name);
2383 if (!ADS_ERR_OK(ret)) {
2384 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2385 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2386 ads_msgfree(ads, res);
2387 return ret;
2390 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2391 ctx = talloc_init("ads_clear_service_principal_names");
2392 if (!ctx) {
2393 ads_msgfree(ads, res);
2394 return ADS_ERROR(LDAP_NO_MEMORY);
2397 if (!(mods = ads_init_mods(ctx))) {
2398 talloc_destroy(ctx);
2399 ads_msgfree(ads, res);
2400 return ADS_ERROR(LDAP_NO_MEMORY);
2402 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2403 if (!ADS_ERR_OK(ret)) {
2404 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2405 ads_msgfree(ads, res);
2406 talloc_destroy(ctx);
2407 return ret;
2409 dn_string = ads_get_dn(ads, talloc_tos(), res);
2410 if (!dn_string) {
2411 talloc_destroy(ctx);
2412 ads_msgfree(ads, res);
2413 return ADS_ERROR(LDAP_NO_MEMORY);
2415 ret = ads_gen_mod(ads, dn_string, mods);
2416 TALLOC_FREE(dn_string);
2417 if (!ADS_ERR_OK(ret)) {
2418 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2419 machine_name));
2420 ads_msgfree(ads, res);
2421 talloc_destroy(ctx);
2422 return ret;
2425 ads_msgfree(ads, res);
2426 talloc_destroy(ctx);
2427 return ret;
2431 * @brief Search for an element in a string array.
2433 * @param[in] el_array The string array to search.
2435 * @param[in] num_el The number of elements in the string array.
2437 * @param[in] el The string to search.
2439 * @return True if found, false if not.
2441 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2443 size_t i;
2445 if (el_array == NULL || num_el == 0 || el == NULL) {
2446 return false;
2449 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2450 int cmp;
2452 cmp = strcasecmp_m(el_array[i], el);
2453 if (cmp == 0) {
2454 return true;
2458 return false;
2462 * @brief This gets the service principal names of an existing computer account.
2464 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2466 * @param[in] ads The ADS context to use.
2468 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2469 * identify the computer account.
2471 * @param[in] spn_array A pointer to store the array for SPNs.
2473 * @param[in] num_spns The number of principals stored in the array.
2475 * @return 0 on success, or a ADS error if a failure occurred.
2477 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2478 ADS_STRUCT *ads,
2479 const char *machine_name,
2480 char ***spn_array,
2481 size_t *num_spns)
2483 ADS_STATUS status;
2484 LDAPMessage *res = NULL;
2485 int count;
2487 status = ads_find_machine_acct(ads,
2488 &res,
2489 machine_name);
2490 if (!ADS_ERR_OK(status)) {
2491 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2492 machine_name));
2493 return status;
2496 count = ads_count_replies(ads, res);
2497 if (count != 1) {
2498 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2499 goto done;
2502 *spn_array = ads_pull_strings(ads,
2503 mem_ctx,
2504 res,
2505 "servicePrincipalName",
2506 num_spns);
2507 if (*spn_array == NULL) {
2508 DEBUG(1, ("Host account for %s does not have service principal "
2509 "names.\n",
2510 machine_name));
2511 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2512 goto done;
2515 done:
2516 ads_msgfree(ads, res);
2518 return status;
2522 * This adds a service principal name to an existing computer account
2523 * (found by hostname) in AD.
2524 * @param ads An initialized ADS_STRUCT
2525 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2526 * @param spns An array or strings for the service principals to add,
2527 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2528 * @return 0 upon success, or non-zero if a failure occurs
2531 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2532 const char *machine_name,
2533 const char **spns)
2535 ADS_STATUS ret;
2536 TALLOC_CTX *ctx;
2537 LDAPMessage *res = NULL;
2538 ADS_MODLIST mods;
2539 char *dn_string = NULL;
2540 const char **servicePrincipalName = spns;
2542 ret = ads_find_machine_acct(ads, &res, machine_name);
2543 if (!ADS_ERR_OK(ret)) {
2544 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2545 machine_name));
2546 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2547 ads_msgfree(ads, res);
2548 return ret;
2551 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2552 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2553 ads_msgfree(ads, res);
2554 return ADS_ERROR(LDAP_NO_MEMORY);
2557 DEBUG(5,("ads_add_service_principal_name: INFO: "
2558 "Adding %s to host %s\n",
2559 spns[0] ? "N/A" : spns[0], machine_name));
2562 DEBUG(5,("ads_add_service_principal_name: INFO: "
2563 "Adding %s to host %s\n",
2564 spns[1] ? "N/A" : spns[1], machine_name));
2566 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2567 ret = ADS_ERROR(LDAP_NO_MEMORY);
2568 goto out;
2571 ret = ads_add_strlist(ctx,
2572 &mods,
2573 "servicePrincipalName",
2574 servicePrincipalName);
2575 if (!ADS_ERR_OK(ret)) {
2576 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2577 goto out;
2580 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2581 ret = ADS_ERROR(LDAP_NO_MEMORY);
2582 goto out;
2585 ret = ads_gen_mod(ads, dn_string, mods);
2586 if (!ADS_ERR_OK(ret)) {
2587 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2588 goto out;
2591 out:
2592 TALLOC_FREE( ctx );
2593 ads_msgfree(ads, res);
2594 return ret;
2597 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2598 LDAPMessage *msg)
2600 uint32_t acct_ctrl = 0;
2601 bool ok;
2603 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2604 if (!ok) {
2605 return 0;
2608 return acct_ctrl;
2611 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2612 LDAPMessage *msg,
2613 const struct berval *machine_pw_val)
2615 ADS_MODLIST mods;
2616 ADS_STATUS ret;
2617 TALLOC_CTX *frame = talloc_stackframe();
2618 uint32_t acct_control;
2619 char *control_str = NULL;
2620 const char *attrs[] = {
2621 "objectSid",
2622 NULL
2624 LDAPMessage *res = NULL;
2625 char *dn = NULL;
2627 dn = ads_get_dn(ads, frame, msg);
2628 if (dn == NULL) {
2629 ret = ADS_ERROR(LDAP_NO_MEMORY);
2630 goto done;
2633 acct_control = ads_get_acct_ctrl(ads, msg);
2634 if (acct_control == 0) {
2635 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2636 goto done;
2640 * Changing the password, disables the account. So we need to change the
2641 * userAccountControl flags to enable it again.
2643 mods = ads_init_mods(frame);
2644 if (mods == NULL) {
2645 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2646 goto done;
2649 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2651 ret = ads_gen_mod(ads, dn, mods);
2652 if (!ADS_ERR_OK(ret)) {
2653 goto done;
2655 TALLOC_FREE(mods);
2658 * To activate the account, we need to disable and enable it.
2660 acct_control |= UF_ACCOUNTDISABLE;
2662 control_str = talloc_asprintf(frame, "%u", acct_control);
2663 if (control_str == NULL) {
2664 ret = ADS_ERROR(LDAP_NO_MEMORY);
2665 goto done;
2668 mods = ads_init_mods(frame);
2669 if (mods == NULL) {
2670 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2671 goto done;
2674 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2676 ret = ads_gen_mod(ads, dn, mods);
2677 if (!ADS_ERR_OK(ret)) {
2678 goto done;
2680 TALLOC_FREE(mods);
2681 TALLOC_FREE(control_str);
2684 * Enable the account again.
2686 acct_control &= ~UF_ACCOUNTDISABLE;
2688 control_str = talloc_asprintf(frame, "%u", acct_control);
2689 if (control_str == NULL) {
2690 ret = ADS_ERROR(LDAP_NO_MEMORY);
2691 goto done;
2694 mods = ads_init_mods(frame);
2695 if (mods == NULL) {
2696 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2697 goto done;
2700 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2702 ret = ads_gen_mod(ads, dn, mods);
2703 if (!ADS_ERR_OK(ret)) {
2704 goto done;
2706 TALLOC_FREE(mods);
2707 TALLOC_FREE(control_str);
2709 ret = ads_search_dn(ads, &res, dn, attrs);
2710 ads_msgfree(ads, res);
2712 done:
2713 talloc_free(frame);
2715 return ret;
2719 * adds a machine account to the ADS server
2720 * @param ads An initialized ADS_STRUCT
2721 * @param machine_name - the NetBIOS machine name of this account.
2722 * @param account_type A number indicating the type of account to create
2723 * @param org_unit The LDAP path in which to place this account
2724 * @return 0 upon success, or non-zero otherwise
2727 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2728 const char *machine_name,
2729 const char *machine_password,
2730 const char *org_unit,
2731 uint32_t etype_list,
2732 const char *dns_domain_name)
2734 ADS_STATUS ret;
2735 char *samAccountName = NULL;
2736 char *controlstr = NULL;
2737 TALLOC_CTX *ctx = NULL;
2738 ADS_MODLIST mods;
2739 char *machine_escaped = NULL;
2740 char *dns_hostname = NULL;
2741 char *new_dn = NULL;
2742 char *utf8_pw = NULL;
2743 size_t utf8_pw_len = 0;
2744 char *utf16_pw = NULL;
2745 size_t utf16_pw_len = 0;
2746 struct berval machine_pw_val;
2747 bool ok;
2748 const char **spn_array = NULL;
2749 size_t num_spns = 0;
2750 const char *spn_prefix[] = {
2751 "HOST",
2752 "RestrictedKrbHost",
2754 size_t i;
2755 LDAPMessage *res = NULL;
2756 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2758 ctx = talloc_init("ads_add_machine_acct");
2759 if (ctx == NULL) {
2760 return ADS_ERROR(LDAP_NO_MEMORY);
2763 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2764 if (machine_escaped == NULL) {
2765 ret = ADS_ERROR(LDAP_NO_MEMORY);
2766 goto done;
2769 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2770 if (utf8_pw == NULL) {
2771 ret = ADS_ERROR(LDAP_NO_MEMORY);
2772 goto done;
2774 utf8_pw_len = strlen(utf8_pw);
2776 ok = convert_string_talloc(ctx,
2777 CH_UTF8, CH_UTF16MUNGED,
2778 utf8_pw, utf8_pw_len,
2779 (void *)&utf16_pw, &utf16_pw_len);
2780 if (!ok) {
2781 ret = ADS_ERROR(LDAP_NO_MEMORY);
2782 goto done;
2785 machine_pw_val = (struct berval) {
2786 .bv_val = utf16_pw,
2787 .bv_len = utf16_pw_len,
2790 /* Check if the machine account already exists. */
2791 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2792 if (ADS_ERR_OK(ret)) {
2793 /* Change the machine account password */
2794 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2795 ads_msgfree(ads, res);
2797 goto done;
2799 ads_msgfree(ads, res);
2801 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2802 if (new_dn == NULL) {
2803 ret = ADS_ERROR(LDAP_NO_MEMORY);
2804 goto done;
2807 /* Create machine account */
2809 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2810 if (samAccountName == NULL) {
2811 ret = ADS_ERROR(LDAP_NO_MEMORY);
2812 goto done;
2815 dns_hostname = talloc_asprintf(ctx,
2816 "%s.%s",
2817 machine_name,
2818 dns_domain_name);
2819 if (dns_hostname == NULL) {
2820 ret = ADS_ERROR(LDAP_NO_MEMORY);
2821 goto done;
2824 /* Add dns_hostname SPNs */
2825 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2826 char *spn = talloc_asprintf(ctx,
2827 "%s/%s",
2828 spn_prefix[i],
2829 dns_hostname);
2830 if (spn == NULL) {
2831 ret = ADS_ERROR(LDAP_NO_MEMORY);
2832 goto done;
2835 ok = add_string_to_array(ctx,
2836 spn,
2837 &spn_array,
2838 &num_spns);
2839 if (!ok) {
2840 ret = ADS_ERROR(LDAP_NO_MEMORY);
2841 goto done;
2845 /* Add machine_name SPNs */
2846 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2847 char *spn = talloc_asprintf(ctx,
2848 "%s/%s",
2849 spn_prefix[i],
2850 machine_name);
2851 if (spn == NULL) {
2852 ret = ADS_ERROR(LDAP_NO_MEMORY);
2853 goto done;
2856 ok = add_string_to_array(ctx,
2857 spn,
2858 &spn_array,
2859 &num_spns);
2860 if (!ok) {
2861 ret = ADS_ERROR(LDAP_NO_MEMORY);
2862 goto done;
2866 /* Make sure to NULL terminate the array */
2867 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2868 if (spn_array == NULL) {
2869 ret = ADS_ERROR(LDAP_NO_MEMORY);
2870 goto done;
2872 spn_array[num_spns] = NULL;
2874 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2875 if (controlstr == NULL) {
2876 ret = ADS_ERROR(LDAP_NO_MEMORY);
2877 goto done;
2880 mods = ads_init_mods(ctx);
2881 if (mods == NULL) {
2882 ret = ADS_ERROR(LDAP_NO_MEMORY);
2883 goto done;
2886 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2887 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2888 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2889 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2890 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2891 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2893 ret = ads_gen_add(ads, new_dn, mods);
2895 done:
2896 SAFE_FREE(machine_escaped);
2897 talloc_destroy(ctx);
2899 return ret;
2903 * move a machine account to another OU on the ADS server
2904 * @param ads - An initialized ADS_STRUCT
2905 * @param machine_name - the NetBIOS machine name of this account.
2906 * @param org_unit - The LDAP path in which to place this account
2907 * @param moved - whether we moved the machine account (optional)
2908 * @return 0 upon success, or non-zero otherwise
2911 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2912 const char *org_unit, bool *moved)
2914 ADS_STATUS rc;
2915 int ldap_status;
2916 LDAPMessage *res = NULL;
2917 char *filter = NULL;
2918 char *computer_dn = NULL;
2919 char *parent_dn;
2920 char *computer_rdn = NULL;
2921 bool need_move = False;
2923 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2924 rc = ADS_ERROR(LDAP_NO_MEMORY);
2925 goto done;
2928 /* Find pre-existing machine */
2929 rc = ads_search(ads, &res, filter, NULL);
2930 if (!ADS_ERR_OK(rc)) {
2931 goto done;
2934 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2935 if (!computer_dn) {
2936 rc = ADS_ERROR(LDAP_NO_MEMORY);
2937 goto done;
2940 parent_dn = ads_parent_dn(computer_dn);
2941 if (strequal(parent_dn, org_unit)) {
2942 goto done;
2945 need_move = True;
2947 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2948 rc = ADS_ERROR(LDAP_NO_MEMORY);
2949 goto done;
2952 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2953 org_unit, 1, NULL, NULL);
2954 rc = ADS_ERROR(ldap_status);
2956 done:
2957 ads_msgfree(ads, res);
2958 SAFE_FREE(filter);
2959 TALLOC_FREE(computer_dn);
2960 SAFE_FREE(computer_rdn);
2962 if (!ADS_ERR_OK(rc)) {
2963 need_move = False;
2966 if (moved) {
2967 *moved = need_move;
2970 return rc;
2974 dump a binary result from ldap
2976 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2978 size_t i;
2979 for (i=0; values[i]; i++) {
2980 ber_len_t j;
2981 printf("%s: ", field);
2982 for (j=0; j<values[i]->bv_len; j++) {
2983 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2985 printf("\n");
2989 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2991 int i;
2992 for (i=0; values[i]; i++) {
2993 NTSTATUS status;
2994 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2995 struct GUID guid;
2997 status = GUID_from_ndr_blob(&in, &guid);
2998 if (NT_STATUS_IS_OK(status)) {
2999 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
3000 } else {
3001 printf("%s: INVALID GUID\n", field);
3007 dump a sid result from ldap
3009 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
3011 int i;
3012 for (i=0; values[i]; i++) {
3013 ssize_t ret;
3014 struct dom_sid sid;
3015 struct dom_sid_buf tmp;
3016 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3017 values[i]->bv_len, &sid);
3018 if (ret == -1) {
3019 return;
3021 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
3026 dump ntSecurityDescriptor
3028 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
3030 TALLOC_CTX *frame = talloc_stackframe();
3031 struct security_descriptor *psd;
3032 NTSTATUS status;
3034 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
3035 values[0]->bv_len, &psd);
3036 if (!NT_STATUS_IS_OK(status)) {
3037 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3038 nt_errstr(status)));
3039 TALLOC_FREE(frame);
3040 return;
3043 if (psd) {
3044 ads_disp_sd(ads, talloc_tos(), psd);
3047 TALLOC_FREE(frame);
3051 dump a string result from ldap
3053 static void dump_string(const char *field, char **values)
3055 int i;
3056 for (i=0; values[i]; i++) {
3057 printf("%s: %s\n", field, values[i]);
3062 dump a field from LDAP on stdout
3063 used for debugging
3066 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
3068 const struct {
3069 const char *name;
3070 bool string;
3071 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
3072 } handlers[] = {
3073 {"objectGUID", False, dump_guid},
3074 {"netbootGUID", False, dump_guid},
3075 {"nTSecurityDescriptor", False, dump_sd},
3076 {"dnsRecord", False, dump_binary},
3077 {"objectSid", False, dump_sid},
3078 {"securityIdentifier", False, dump_sid},
3079 {"tokenGroups", False, dump_sid},
3080 {"tokenGroupsNoGCAcceptable", False, dump_sid},
3081 {"tokengroupsGlobalandUniversal", False, dump_sid},
3082 {"mS-DS-CreatorSID", False, dump_sid},
3083 {"msExchMailboxGuid", False, dump_guid},
3084 {"msDS-TrustForestTrustInfo", False, dump_binary},
3085 {NULL, True, NULL}
3087 int i;
3089 if (!field) { /* must be end of an entry */
3090 printf("\n");
3091 return False;
3094 for (i=0; handlers[i].name; i++) {
3095 if (strcasecmp_m(handlers[i].name, field) == 0) {
3096 if (!values) /* first time, indicate string or not */
3097 return handlers[i].string;
3098 handlers[i].handler(ads, field, (struct berval **) values);
3099 break;
3102 if (!handlers[i].name) {
3103 if (!values) /* first time, indicate string conversion */
3104 return True;
3105 dump_string(field, (char **)values);
3107 return False;
3111 * Dump a result from LDAP on stdout
3112 * used for debugging
3113 * @param ads connection to ads server
3114 * @param res Results to dump
3117 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
3119 ads_process_results(ads, res, ads_dump_field, NULL);
3123 * Walk through results, calling a function for each entry found.
3124 * The function receives a field name, a berval * array of values,
3125 * and a data area passed through from the start. The function is
3126 * called once with null for field and values at the end of each
3127 * entry.
3128 * @param ads connection to ads server
3129 * @param res Results to process
3130 * @param fn Function for processing each result
3131 * @param data_area user-defined area to pass to function
3133 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
3134 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
3135 void *data_area)
3137 LDAPMessage *msg;
3138 TALLOC_CTX *ctx;
3139 size_t converted_size;
3141 if (!(ctx = talloc_init("ads_process_results")))
3142 return;
3144 for (msg = ads_first_entry(ads, res); msg;
3145 msg = ads_next_entry(ads, msg)) {
3146 char *utf8_field;
3147 BerElement *b;
3149 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
3150 (LDAPMessage *)msg,&b);
3151 utf8_field;
3152 utf8_field=ldap_next_attribute(ads->ldap.ld,
3153 (LDAPMessage *)msg,b)) {
3154 struct berval **ber_vals;
3155 char **str_vals;
3156 char **utf8_vals;
3157 char *field;
3158 bool string;
3160 if (!pull_utf8_talloc(ctx, &field, utf8_field,
3161 &converted_size))
3163 DEBUG(0,("ads_process_results: "
3164 "pull_utf8_talloc failed: %s\n",
3165 strerror(errno)));
3168 string = fn(ads, field, NULL, data_area);
3170 if (string) {
3171 const char **p;
3173 utf8_vals = ldap_get_values(ads->ldap.ld,
3174 (LDAPMessage *)msg, field);
3175 p = discard_const_p(const char *, utf8_vals);
3176 str_vals = ads_pull_strvals(ctx, p);
3177 fn(ads, field, (void **) str_vals, data_area);
3178 ldap_value_free(utf8_vals);
3179 } else {
3180 ber_vals = ldap_get_values_len(ads->ldap.ld,
3181 (LDAPMessage *)msg, field);
3182 fn(ads, field, (void **) ber_vals, data_area);
3184 ldap_value_free_len(ber_vals);
3186 ldap_memfree(utf8_field);
3188 ber_free(b, 0);
3189 talloc_free_children(ctx);
3190 fn(ads, NULL, NULL, data_area); /* completed an entry */
3193 talloc_destroy(ctx);
3197 * count how many replies are in a LDAPMessage
3198 * @param ads connection to ads server
3199 * @param res Results to count
3200 * @return number of replies
3202 int ads_count_replies(ADS_STRUCT *ads, void *res)
3204 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3208 * pull the first entry from a ADS result
3209 * @param ads connection to ads server
3210 * @param res Results of search
3211 * @return first entry from result
3213 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3215 return ldap_first_entry(ads->ldap.ld, res);
3219 * pull the next entry from a ADS result
3220 * @param ads connection to ads server
3221 * @param res Results of search
3222 * @return next entry from result
3224 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3226 return ldap_next_entry(ads->ldap.ld, res);
3230 * pull the first message from a ADS result
3231 * @param ads connection to ads server
3232 * @param res Results of search
3233 * @return first message from result
3235 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3237 return ldap_first_message(ads->ldap.ld, res);
3241 * pull the next message from a ADS result
3242 * @param ads connection to ads server
3243 * @param res Results of search
3244 * @return next message from result
3246 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3248 return ldap_next_message(ads->ldap.ld, res);
3252 * pull a single string from a ADS result
3253 * @param ads connection to ads server
3254 * @param mem_ctx TALLOC_CTX to use for allocating result string
3255 * @param msg Results of search
3256 * @param field Attribute to retrieve
3257 * @return Result string in talloc context
3259 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3260 const char *field)
3262 char **values;
3263 char *ret = NULL;
3264 char *ux_string;
3265 size_t converted_size;
3267 values = ldap_get_values(ads->ldap.ld, msg, field);
3268 if (!values)
3269 return NULL;
3271 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3272 &converted_size))
3274 ret = ux_string;
3276 ldap_value_free(values);
3277 return ret;
3281 * pull an array of strings from a ADS result
3282 * @param ads connection to ads server
3283 * @param mem_ctx TALLOC_CTX to use for allocating result string
3284 * @param msg Results of search
3285 * @param field Attribute to retrieve
3286 * @return Result strings in talloc context
3288 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3289 LDAPMessage *msg, const char *field,
3290 size_t *num_values)
3292 char **values;
3293 char **ret = NULL;
3294 size_t i, converted_size;
3296 values = ldap_get_values(ads->ldap.ld, msg, field);
3297 if (!values)
3298 return NULL;
3300 *num_values = ldap_count_values(values);
3302 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3303 if (!ret) {
3304 ldap_value_free(values);
3305 return NULL;
3308 for (i=0;i<*num_values;i++) {
3309 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3310 &converted_size))
3312 ldap_value_free(values);
3313 return NULL;
3316 ret[i] = NULL;
3318 ldap_value_free(values);
3319 return ret;
3323 * pull an array of strings from a ADS result
3324 * (handle large multivalue attributes with range retrieval)
3325 * @param ads connection to ads server
3326 * @param mem_ctx TALLOC_CTX to use for allocating result string
3327 * @param msg Results of search
3328 * @param field Attribute to retrieve
3329 * @param current_strings strings returned by a previous call to this function
3330 * @param next_attribute The next query should ask for this attribute
3331 * @param num_values How many values did we get this time?
3332 * @param more_values Are there more values to get?
3333 * @return Result strings in talloc context
3335 char **ads_pull_strings_range(ADS_STRUCT *ads,
3336 TALLOC_CTX *mem_ctx,
3337 LDAPMessage *msg, const char *field,
3338 char **current_strings,
3339 const char **next_attribute,
3340 size_t *num_strings,
3341 bool *more_strings)
3343 char *attr;
3344 char *expected_range_attrib, *range_attr = NULL;
3345 BerElement *ptr = NULL;
3346 char **strings;
3347 char **new_strings;
3348 size_t num_new_strings;
3349 unsigned long int range_start;
3350 unsigned long int range_end;
3352 /* we might have been given the whole lot anyway */
3353 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3354 *more_strings = False;
3355 return strings;
3358 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3360 /* look for Range result */
3361 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3362 attr;
3363 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3364 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3365 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3366 range_attr = attr;
3367 break;
3369 ldap_memfree(attr);
3371 if (!range_attr) {
3372 ber_free(ptr, 0);
3373 /* nothing here - this field is just empty */
3374 *more_strings = False;
3375 return NULL;
3378 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3379 &range_start, &range_end) == 2) {
3380 *more_strings = True;
3381 } else {
3382 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3383 &range_start) == 1) {
3384 *more_strings = False;
3385 } else {
3386 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3387 range_attr));
3388 ldap_memfree(range_attr);
3389 *more_strings = False;
3390 return NULL;
3394 if ((*num_strings) != range_start) {
3395 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3396 " - aborting range retrieval\n",
3397 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3398 ldap_memfree(range_attr);
3399 *more_strings = False;
3400 return NULL;
3403 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3405 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3406 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3407 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3408 range_attr, (unsigned long int)range_end - range_start + 1,
3409 (unsigned long int)num_new_strings));
3410 ldap_memfree(range_attr);
3411 *more_strings = False;
3412 return NULL;
3415 strings = talloc_realloc(mem_ctx, current_strings, char *,
3416 *num_strings + num_new_strings);
3418 if (strings == NULL) {
3419 ldap_memfree(range_attr);
3420 *more_strings = False;
3421 return NULL;
3424 if (new_strings && num_new_strings) {
3425 memcpy(&strings[*num_strings], new_strings,
3426 sizeof(*new_strings) * num_new_strings);
3429 (*num_strings) += num_new_strings;
3431 if (*more_strings) {
3432 *next_attribute = talloc_asprintf(mem_ctx,
3433 "%s;range=%d-*",
3434 field,
3435 (int)*num_strings);
3437 if (!*next_attribute) {
3438 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3439 ldap_memfree(range_attr);
3440 *more_strings = False;
3441 return NULL;
3445 ldap_memfree(range_attr);
3447 return strings;
3451 * pull a single uint32_t from a ADS result
3452 * @param ads connection to ads server
3453 * @param msg Results of search
3454 * @param field Attribute to retrieve
3455 * @param v Pointer to int to store result
3456 * @return boolean indicating success
3458 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3459 uint32_t *v)
3461 char **values;
3463 values = ldap_get_values(ads->ldap.ld, msg, field);
3464 if (!values)
3465 return False;
3466 if (!values[0]) {
3467 ldap_value_free(values);
3468 return False;
3471 *v = atoi(values[0]);
3472 ldap_value_free(values);
3473 return True;
3477 * pull a single objectGUID from an ADS result
3478 * @param ads connection to ADS server
3479 * @param msg results of search
3480 * @param guid 37-byte area to receive text guid
3481 * @return boolean indicating success
3483 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3485 DATA_BLOB blob;
3486 NTSTATUS status;
3488 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3489 &blob)) {
3490 return false;
3493 status = GUID_from_ndr_blob(&blob, guid);
3494 talloc_free(blob.data);
3495 return NT_STATUS_IS_OK(status);
3500 * pull a single struct dom_sid from a ADS result
3501 * @param ads connection to ads server
3502 * @param msg Results of search
3503 * @param field Attribute to retrieve
3504 * @param sid Pointer to sid to store result
3505 * @return boolean indicating success
3507 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3508 struct dom_sid *sid)
3510 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3514 * pull an array of struct dom_sids from a ADS result
3515 * @param ads connection to ads server
3516 * @param mem_ctx TALLOC_CTX for allocating sid array
3517 * @param msg Results of search
3518 * @param field Attribute to retrieve
3519 * @param sids pointer to sid array to allocate
3520 * @return the count of SIDs pulled
3522 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3523 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3525 struct berval **values;
3526 int count, i;
3528 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3530 if (!values)
3531 return 0;
3533 for (i=0; values[i]; i++)
3534 /* nop */ ;
3536 if (i) {
3537 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3538 if (!(*sids)) {
3539 ldap_value_free_len(values);
3540 return 0;
3542 } else {
3543 (*sids) = NULL;
3546 count = 0;
3547 for (i=0; values[i]; i++) {
3548 ssize_t ret;
3549 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3550 values[i]->bv_len, &(*sids)[count]);
3551 if (ret != -1) {
3552 struct dom_sid_buf buf;
3553 DBG_DEBUG("pulling SID: %s\n",
3554 dom_sid_str_buf(&(*sids)[count], &buf));
3555 count++;
3559 ldap_value_free_len(values);
3560 return count;
3564 * pull a struct security_descriptor from a ADS result
3565 * @param ads connection to ads server
3566 * @param mem_ctx TALLOC_CTX for allocating sid array
3567 * @param msg Results of search
3568 * @param field Attribute to retrieve
3569 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3570 * @return boolean indicating success
3572 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3573 LDAPMessage *msg, const char *field,
3574 struct security_descriptor **sd)
3576 struct berval **values;
3577 bool ret = true;
3579 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3581 if (!values) return false;
3583 if (values[0]) {
3584 NTSTATUS status;
3585 status = unmarshall_sec_desc(mem_ctx,
3586 (uint8_t *)values[0]->bv_val,
3587 values[0]->bv_len, sd);
3588 if (!NT_STATUS_IS_OK(status)) {
3589 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3590 nt_errstr(status)));
3591 ret = false;
3595 ldap_value_free_len(values);
3596 return ret;
3600 * in order to support usernames longer than 21 characters we need to
3601 * use both the sAMAccountName and the userPrincipalName attributes
3602 * It seems that not all users have the userPrincipalName attribute set
3604 * @param ads connection to ads server
3605 * @param mem_ctx TALLOC_CTX for allocating sid array
3606 * @param msg Results of search
3607 * @return the username
3609 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3610 LDAPMessage *msg)
3612 #if 0 /* JERRY */
3613 char *ret, *p;
3615 /* lookup_name() only works on the sAMAccountName to
3616 returning the username portion of userPrincipalName
3617 breaks winbindd_getpwnam() */
3619 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3620 if (ret && (p = strchr_m(ret, '@'))) {
3621 *p = 0;
3622 return ret;
3624 #endif
3625 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3630 * find the update serial number - this is the core of the ldap cache
3631 * @param ads connection to ads server
3632 * @param ads connection to ADS server
3633 * @param usn Pointer to retrieved update serial number
3634 * @return status of search
3636 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3638 const char *attrs[] = {"highestCommittedUSN", NULL};
3639 ADS_STATUS status;
3640 LDAPMessage *res;
3642 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3643 if (!ADS_ERR_OK(status))
3644 return status;
3646 if (ads_count_replies(ads, res) != 1) {
3647 ads_msgfree(ads, res);
3648 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3651 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3652 ads_msgfree(ads, res);
3653 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3656 ads_msgfree(ads, res);
3657 return ADS_SUCCESS;
3660 /* parse a ADS timestring - typical string is
3661 '20020917091222.0Z0' which means 09:12.22 17th September
3662 2002, timezone 0 */
3663 static time_t ads_parse_time(const char *str)
3665 struct tm tm;
3667 ZERO_STRUCT(tm);
3669 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3670 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3671 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3672 return 0;
3674 tm.tm_year -= 1900;
3675 tm.tm_mon -= 1;
3677 return timegm(&tm);
3680 /********************************************************************
3681 ********************************************************************/
3683 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3685 const char *attrs[] = {"currentTime", NULL};
3686 ADS_STATUS status;
3687 LDAPMessage *res;
3688 char *timestr;
3689 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3690 ADS_STRUCT *ads_s = ads;
3692 /* establish a new ldap tcp session if necessary */
3694 if ( !ads->ldap.ld ) {
3696 * ADS_STRUCT may be being reused after a
3697 * DC lookup, so ads->ldap.ss may already have a
3698 * good address. If not, re-initialize the passed-in
3699 * ADS_STRUCT with the given server.XXXX parameters.
3701 * Note that this doesn't depend on
3702 * ads->server.ldap_server != NULL,
3703 * as the case where ads->server.ldap_server==NULL and
3704 * ads->ldap.ss != zero_address is precisely the DC
3705 * lookup case where ads->ldap.ss was found by going
3706 * through ads_find_dc() again we want to avoid repeating.
3708 if (is_zero_addr(&ads->ldap.ss)) {
3709 ads_s = ads_init(tmp_ctx,
3710 ads->server.realm,
3711 ads->server.workgroup,
3712 ads->server.ldap_server,
3713 ADS_SASL_PLAIN );
3714 if (ads_s == NULL) {
3715 status = ADS_ERROR(LDAP_NO_MEMORY);
3716 goto done;
3721 * Reset ads->config.flags as it can contain the flags
3722 * returned by the previous CLDAP ping when reusing the struct.
3724 ads_s->config.flags = 0;
3726 status = ads_connect_simple_anon(ads_s);
3727 if ( !ADS_ERR_OK(status))
3728 goto done;
3731 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3732 if (!ADS_ERR_OK(status)) {
3733 goto done;
3736 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3737 if (!timestr) {
3738 ads_msgfree(ads_s, res);
3739 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3740 goto done;
3743 /* but save the time and offset in the original ADS_STRUCT */
3745 ads->config.current_time = ads_parse_time(timestr);
3747 if (ads->config.current_time != 0) {
3748 ads->config.time_offset = ads->config.current_time - time(NULL);
3749 DBG_INFO("server time offset is %d seconds\n",
3750 ads->config.time_offset);
3751 } else {
3752 ads->config.time_offset = 0;
3755 DBG_INFO("server time offset is %d seconds\n",
3756 ads->config.time_offset);
3758 ads_msgfree(ads, res);
3760 status = ADS_SUCCESS;
3762 done:
3763 TALLOC_FREE(tmp_ctx);
3765 return status;
3768 /********************************************************************
3769 ********************************************************************/
3771 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3773 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3774 const char *attrs[] = {"domainFunctionality", NULL};
3775 ADS_STATUS status;
3776 LDAPMessage *res;
3777 ADS_STRUCT *ads_s = ads;
3779 *val = DS_DOMAIN_FUNCTION_2000;
3781 /* establish a new ldap tcp session if necessary */
3783 if ( !ads->ldap.ld ) {
3785 * ADS_STRUCT may be being reused after a
3786 * DC lookup, so ads->ldap.ss may already have a
3787 * good address. If not, re-initialize the passed-in
3788 * ADS_STRUCT with the given server.XXXX parameters.
3790 * Note that this doesn't depend on
3791 * ads->server.ldap_server != NULL,
3792 * as the case where ads->server.ldap_server==NULL and
3793 * ads->ldap.ss != zero_address is precisely the DC
3794 * lookup case where ads->ldap.ss was found by going
3795 * through ads_find_dc() again we want to avoid repeating.
3797 if (is_zero_addr(&ads->ldap.ss)) {
3798 ads_s = ads_init(tmp_ctx,
3799 ads->server.realm,
3800 ads->server.workgroup,
3801 ads->server.ldap_server,
3802 ADS_SASL_PLAIN );
3803 if (ads_s == NULL ) {
3804 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3805 goto done;
3810 * Reset ads->config.flags as it can contain the flags
3811 * returned by the previous CLDAP ping when reusing the struct.
3813 ads_s->config.flags = 0;
3815 status = ads_connect_simple_anon(ads_s);
3816 if ( !ADS_ERR_OK(status))
3817 goto done;
3820 /* If the attribute does not exist assume it is a Windows 2000
3821 functional domain */
3823 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3824 if (!ADS_ERR_OK(status)) {
3825 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3826 status = ADS_SUCCESS;
3828 goto done;
3831 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3832 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3834 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3837 ads_msgfree(ads_s, res);
3839 done:
3840 TALLOC_FREE(tmp_ctx);
3842 return status;
3846 * find the domain sid for our domain
3847 * @param ads connection to ads server
3848 * @param sid Pointer to domain sid
3849 * @return status of search
3851 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3853 const char *attrs[] = {"objectSid", NULL};
3854 LDAPMessage *res;
3855 ADS_STATUS rc;
3857 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3858 attrs, &res);
3859 if (!ADS_ERR_OK(rc)) return rc;
3860 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3861 ads_msgfree(ads, res);
3862 return ADS_ERROR_SYSTEM(ENOENT);
3864 ads_msgfree(ads, res);
3866 return ADS_SUCCESS;
3870 * find our site name
3871 * @param ads connection to ads server
3872 * @param mem_ctx Pointer to talloc context
3873 * @param site_name Pointer to the sitename
3874 * @return status of search
3876 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3878 ADS_STATUS status;
3879 LDAPMessage *res;
3880 const char *dn, *service_name;
3881 const char *attrs[] = { "dsServiceName", NULL };
3883 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3884 if (!ADS_ERR_OK(status)) {
3885 return status;
3888 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3889 if (service_name == NULL) {
3890 ads_msgfree(ads, res);
3891 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3894 ads_msgfree(ads, res);
3896 /* go up three levels */
3897 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3898 if (dn == NULL) {
3899 return ADS_ERROR(LDAP_NO_MEMORY);
3902 *site_name = talloc_strdup(mem_ctx, dn);
3903 if (*site_name == NULL) {
3904 return ADS_ERROR(LDAP_NO_MEMORY);
3907 return status;
3909 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3914 * find the site dn where a machine resides
3915 * @param ads connection to ads server
3916 * @param mem_ctx Pointer to talloc context
3917 * @param computer_name name of the machine
3918 * @param site_name Pointer to the sitename
3919 * @return status of search
3921 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3923 ADS_STATUS status;
3924 LDAPMessage *res;
3925 const char *parent, *filter;
3926 char *config_context = NULL;
3927 char *dn;
3929 /* shortcut a query */
3930 if (strequal(computer_name, ads->config.ldap_server_name)) {
3931 return ads_site_dn(ads, mem_ctx, site_dn);
3934 status = ads_config_path(ads, mem_ctx, &config_context);
3935 if (!ADS_ERR_OK(status)) {
3936 return status;
3939 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3940 if (filter == NULL) {
3941 return ADS_ERROR(LDAP_NO_MEMORY);
3944 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3945 filter, NULL, &res);
3946 if (!ADS_ERR_OK(status)) {
3947 return status;
3950 if (ads_count_replies(ads, res) != 1) {
3951 ads_msgfree(ads, res);
3952 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3955 dn = ads_get_dn(ads, mem_ctx, res);
3956 if (dn == NULL) {
3957 ads_msgfree(ads, res);
3958 return ADS_ERROR(LDAP_NO_MEMORY);
3961 /* go up three levels */
3962 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3963 if (parent == NULL) {
3964 ads_msgfree(ads, res);
3965 TALLOC_FREE(dn);
3966 return ADS_ERROR(LDAP_NO_MEMORY);
3969 *site_dn = talloc_strdup(mem_ctx, parent);
3970 if (*site_dn == NULL) {
3971 ads_msgfree(ads, res);
3972 TALLOC_FREE(dn);
3973 return ADS_ERROR(LDAP_NO_MEMORY);
3976 TALLOC_FREE(dn);
3977 ads_msgfree(ads, res);
3979 return status;
3983 * get the upn suffixes for a domain
3984 * @param ads connection to ads server
3985 * @param mem_ctx Pointer to talloc context
3986 * @param suffixes Pointer to an array of suffixes
3987 * @param num_suffixes Pointer to the number of suffixes
3988 * @return status of search
3990 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3992 ADS_STATUS status;
3993 LDAPMessage *res;
3994 const char *base;
3995 char *config_context = NULL;
3996 const char *attrs[] = { "uPNSuffixes", NULL };
3998 status = ads_config_path(ads, mem_ctx, &config_context);
3999 if (!ADS_ERR_OK(status)) {
4000 return status;
4003 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
4004 if (base == NULL) {
4005 return ADS_ERROR(LDAP_NO_MEMORY);
4008 status = ads_search_dn(ads, &res, base, attrs);
4009 if (!ADS_ERR_OK(status)) {
4010 return status;
4013 if (ads_count_replies(ads, res) != 1) {
4014 ads_msgfree(ads, res);
4015 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4018 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
4019 if ((*suffixes) == NULL) {
4020 ads_msgfree(ads, res);
4021 return ADS_ERROR(LDAP_NO_MEMORY);
4024 ads_msgfree(ads, res);
4026 return status;
4030 * get the joinable ous for a domain
4031 * @param ads connection to ads server
4032 * @param mem_ctx Pointer to talloc context
4033 * @param ous Pointer to an array of ous
4034 * @param num_ous Pointer to the number of ous
4035 * @return status of search
4037 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
4038 TALLOC_CTX *mem_ctx,
4039 char ***ous,
4040 size_t *num_ous)
4042 ADS_STATUS status;
4043 LDAPMessage *res = NULL;
4044 LDAPMessage *msg = NULL;
4045 const char *attrs[] = { "dn", NULL };
4046 int count = 0;
4048 status = ads_search(ads, &res,
4049 "(|(objectClass=domain)(objectclass=organizationalUnit))",
4050 attrs);
4051 if (!ADS_ERR_OK(status)) {
4052 return status;
4055 count = ads_count_replies(ads, res);
4056 if (count < 1) {
4057 ads_msgfree(ads, res);
4058 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4061 for (msg = ads_first_entry(ads, res); msg;
4062 msg = ads_next_entry(ads, msg)) {
4063 const char **p = discard_const_p(const char *, *ous);
4064 char *dn = NULL;
4066 dn = ads_get_dn(ads, talloc_tos(), msg);
4067 if (!dn) {
4068 ads_msgfree(ads, res);
4069 return ADS_ERROR(LDAP_NO_MEMORY);
4072 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
4073 TALLOC_FREE(dn);
4074 ads_msgfree(ads, res);
4075 return ADS_ERROR(LDAP_NO_MEMORY);
4078 TALLOC_FREE(dn);
4079 *ous = discard_const_p(char *, p);
4082 ads_msgfree(ads, res);
4084 return status;
4089 * pull a struct dom_sid from an extended dn string
4090 * @param mem_ctx TALLOC_CTX
4091 * @param extended_dn string
4092 * @param flags string type of extended_dn
4093 * @param sid pointer to a struct dom_sid
4094 * @return NT_STATUS_OK on success,
4095 * NT_INVALID_PARAMETER on error,
4096 * NT_STATUS_NOT_FOUND if no SID present
4098 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
4099 const char *extended_dn,
4100 enum ads_extended_dn_flags flags,
4101 struct dom_sid *sid)
4103 char *p, *q, *dn;
4105 if (!extended_dn) {
4106 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4109 /* otherwise extended_dn gets stripped off */
4110 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
4111 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4114 * ADS_EXTENDED_DN_HEX_STRING:
4115 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4117 * ADS_EXTENDED_DN_STRING (only with w2k3):
4118 * <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
4120 * Object with no SID, such as an Exchange Public Folder
4121 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4124 p = strchr(dn, ';');
4125 if (!p) {
4126 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4129 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
4130 DEBUG(5,("No SID present in extended dn\n"));
4131 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
4134 p += strlen(";<SID=");
4136 q = strchr(p, '>');
4137 if (!q) {
4138 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4141 *q = '\0';
4143 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
4145 switch (flags) {
4147 case ADS_EXTENDED_DN_STRING:
4148 if (!string_to_sid(sid, p)) {
4149 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4151 break;
4152 case ADS_EXTENDED_DN_HEX_STRING: {
4153 ssize_t ret;
4154 fstring buf;
4155 size_t buf_len;
4157 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
4158 if (buf_len == 0) {
4159 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4162 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
4163 if (ret == -1) {
4164 DEBUG(10,("failed to parse sid\n"));
4165 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4167 break;
4169 default:
4170 DEBUG(10,("unknown extended dn format\n"));
4171 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4174 return ADS_ERROR_NT(NT_STATUS_OK);
4177 /********************************************************************
4178 ********************************************************************/
4180 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4182 LDAPMessage *res = NULL;
4183 ADS_STATUS status;
4184 int count = 0;
4185 char *name = NULL;
4187 status = ads_find_machine_acct(ads, &res, machine_name);
4188 if (!ADS_ERR_OK(status)) {
4189 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4190 lp_netbios_name()));
4191 goto out;
4194 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4195 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4196 goto out;
4199 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4200 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4203 out:
4204 ads_msgfree(ads, res);
4206 return name;
4209 /********************************************************************
4210 ********************************************************************/
4212 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4213 LDAPMessage *msg, size_t *num_values)
4215 const char *field = "msDS-AdditionalDnsHostName";
4216 struct berval **values = NULL;
4217 char **ret = NULL;
4218 size_t i, converted_size;
4221 * Windows DC implicitly adds a short name for each FQDN added to
4222 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4223 * suffix "\0$" which we should ignore (see bug #14406).
4226 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4227 if (values == NULL) {
4228 return NULL;
4231 *num_values = ldap_count_values_len(values);
4233 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4234 if (ret == NULL) {
4235 ldap_value_free_len(values);
4236 return NULL;
4239 for (i = 0; i < *num_values; i++) {
4240 ret[i] = NULL;
4241 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4242 values[i]->bv_val,
4243 strnlen(values[i]->bv_val,
4244 values[i]->bv_len),
4245 &ret[i], &converted_size)) {
4246 ldap_value_free_len(values);
4247 return NULL;
4250 ret[i] = NULL;
4252 ldap_value_free_len(values);
4253 return ret;
4256 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4257 ADS_STRUCT *ads,
4258 const char *machine_name,
4259 char ***hostnames_array,
4260 size_t *num_hostnames)
4262 ADS_STATUS status;
4263 LDAPMessage *res = NULL;
4264 int count;
4266 status = ads_find_machine_acct(ads,
4267 &res,
4268 machine_name);
4269 if (!ADS_ERR_OK(status)) {
4270 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4271 machine_name));
4272 return status;
4275 count = ads_count_replies(ads, res);
4276 if (count != 1) {
4277 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4278 goto done;
4281 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4282 if (*hostnames_array == NULL) {
4283 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4284 machine_name));
4285 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4286 goto done;
4289 done:
4290 ads_msgfree(ads, res);
4292 return status;
4295 /********************************************************************
4296 ********************************************************************/
4298 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4300 LDAPMessage *res = NULL;
4301 ADS_STATUS status;
4302 int count = 0;
4303 char *name = NULL;
4305 status = ads_find_machine_acct(ads, &res, machine_name);
4306 if (!ADS_ERR_OK(status)) {
4307 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4308 lp_netbios_name()));
4309 goto out;
4312 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4313 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4314 goto out;
4317 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4318 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4321 out:
4322 ads_msgfree(ads, res);
4324 return name;
4327 /********************************************************************
4328 ********************************************************************/
4330 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4332 LDAPMessage *res = NULL;
4333 ADS_STATUS status;
4334 int count = 0;
4335 char *name = NULL;
4336 bool ok = false;
4338 status = ads_find_machine_acct(ads, &res, machine_name);
4339 if (!ADS_ERR_OK(status)) {
4340 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4341 lp_netbios_name()));
4342 goto out;
4345 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4346 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4347 goto out;
4350 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4351 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4354 out:
4355 ads_msgfree(ads, res);
4356 if (name != NULL) {
4357 ok = (strlen(name) > 0);
4359 TALLOC_FREE(name);
4360 return ok;
4363 #if 0
4365 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4368 * Join a machine to a realm
4369 * Creates the machine account and sets the machine password
4370 * @param ads connection to ads server
4371 * @param machine name of host to add
4372 * @param org_unit Organizational unit to place machine in
4373 * @return status of join
4375 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4376 uint32_t account_type, const char *org_unit)
4378 ADS_STATUS status;
4379 LDAPMessage *res = NULL;
4380 char *machine;
4382 /* machine name must be lowercase */
4383 machine = SMB_STRDUP(machine_name);
4384 strlower_m(machine);
4387 status = ads_find_machine_acct(ads, (void **)&res, machine);
4388 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4389 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4390 status = ads_leave_realm(ads, machine);
4391 if (!ADS_ERR_OK(status)) {
4392 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4393 machine, ads->config.realm));
4394 return status;
4398 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4399 if (!ADS_ERR_OK(status)) {
4400 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4401 SAFE_FREE(machine);
4402 return status;
4405 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4406 if (!ADS_ERR_OK(status)) {
4407 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4408 SAFE_FREE(machine);
4409 return status;
4412 SAFE_FREE(machine);
4413 ads_msgfree(ads, res);
4415 return status;
4417 #endif
4420 * Delete a machine from the realm
4421 * @param ads connection to ads server
4422 * @param hostname Machine to remove
4423 * @return status of delete
4425 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4427 ADS_STATUS status;
4428 void *msg;
4429 LDAPMessage *res;
4430 char *hostnameDN, *host;
4431 int rc;
4432 LDAPControl ldap_control;
4433 LDAPControl * pldap_control[2] = {NULL, NULL};
4435 pldap_control[0] = &ldap_control;
4436 memset(&ldap_control, 0, sizeof(LDAPControl));
4437 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4439 /* hostname must be lowercase */
4440 host = SMB_STRDUP(hostname);
4441 if (!strlower_m(host)) {
4442 SAFE_FREE(host);
4443 return ADS_ERROR_SYSTEM(EINVAL);
4446 status = ads_find_machine_acct(ads, &res, host);
4447 if (!ADS_ERR_OK(status)) {
4448 DEBUG(0, ("Host account for %s does not exist.\n", host));
4449 SAFE_FREE(host);
4450 return status;
4453 msg = ads_first_entry(ads, res);
4454 if (!msg) {
4455 SAFE_FREE(host);
4456 return ADS_ERROR_SYSTEM(ENOENT);
4459 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4460 if (hostnameDN == NULL) {
4461 SAFE_FREE(host);
4462 return ADS_ERROR_SYSTEM(ENOENT);
4465 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4466 if (rc) {
4467 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4468 }else {
4469 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4472 if (rc != LDAP_SUCCESS) {
4473 const char *attrs[] = { "cn", NULL };
4474 LDAPMessage *msg_sub;
4476 /* we only search with scope ONE, we do not expect any further
4477 * objects to be created deeper */
4479 status = ads_do_search_retry(ads, hostnameDN,
4480 LDAP_SCOPE_ONELEVEL,
4481 "(objectclass=*)", attrs, &res);
4483 if (!ADS_ERR_OK(status)) {
4484 SAFE_FREE(host);
4485 TALLOC_FREE(hostnameDN);
4486 return status;
4489 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4490 msg_sub = ads_next_entry(ads, msg_sub)) {
4492 char *dn = NULL;
4494 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4495 SAFE_FREE(host);
4496 TALLOC_FREE(hostnameDN);
4497 return ADS_ERROR(LDAP_NO_MEMORY);
4500 status = ads_del_dn(ads, dn);
4501 if (!ADS_ERR_OK(status)) {
4502 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4503 SAFE_FREE(host);
4504 TALLOC_FREE(dn);
4505 TALLOC_FREE(hostnameDN);
4506 return status;
4509 TALLOC_FREE(dn);
4512 /* there should be no subordinate objects anymore */
4513 status = ads_do_search_retry(ads, hostnameDN,
4514 LDAP_SCOPE_ONELEVEL,
4515 "(objectclass=*)", attrs, &res);
4517 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4518 SAFE_FREE(host);
4519 TALLOC_FREE(hostnameDN);
4520 return status;
4523 /* delete hostnameDN now */
4524 status = ads_del_dn(ads, hostnameDN);
4525 if (!ADS_ERR_OK(status)) {
4526 SAFE_FREE(host);
4527 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4528 TALLOC_FREE(hostnameDN);
4529 return status;
4533 TALLOC_FREE(hostnameDN);
4535 status = ads_find_machine_acct(ads, &res, host);
4536 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4537 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4538 DEBUG(3, ("Failed to remove host account.\n"));
4539 SAFE_FREE(host);
4540 return status;
4543 SAFE_FREE(host);
4544 return ADS_SUCCESS;
4548 * pull all token-sids from an LDAP dn
4549 * @param ads connection to ads server
4550 * @param mem_ctx TALLOC_CTX for allocating sid array
4551 * @param dn of LDAP object
4552 * @param user_sid pointer to struct dom_sid (objectSid)
4553 * @param primary_group_sid pointer to struct dom_sid (self composed)
4554 * @param sids pointer to sid array to allocate
4555 * @param num_sids counter of SIDs pulled
4556 * @return status of token query
4558 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4559 TALLOC_CTX *mem_ctx,
4560 const char *dn,
4561 struct dom_sid *user_sid,
4562 struct dom_sid *primary_group_sid,
4563 struct dom_sid **sids,
4564 size_t *num_sids)
4566 ADS_STATUS status;
4567 LDAPMessage *res = NULL;
4568 int count = 0;
4569 size_t tmp_num_sids;
4570 struct dom_sid *tmp_sids;
4571 struct dom_sid tmp_user_sid;
4572 struct dom_sid tmp_primary_group_sid;
4573 uint32_t pgid;
4574 const char *attrs[] = {
4575 "objectSid",
4576 "tokenGroups",
4577 "primaryGroupID",
4578 NULL
4581 status = ads_search_retry_dn(ads, &res, dn, attrs);
4582 if (!ADS_ERR_OK(status)) {
4583 return status;
4586 count = ads_count_replies(ads, res);
4587 if (count != 1) {
4588 ads_msgfree(ads, res);
4589 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4592 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4593 ads_msgfree(ads, res);
4594 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4597 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4598 ads_msgfree(ads, res);
4599 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4603 /* hack to compose the primary group sid without knowing the
4604 * domsid */
4606 struct dom_sid domsid;
4608 sid_copy(&domsid, &tmp_user_sid);
4610 if (!sid_split_rid(&domsid, NULL)) {
4611 ads_msgfree(ads, res);
4612 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4615 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4616 ads_msgfree(ads, res);
4617 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4621 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4623 if (tmp_num_sids == 0 || !tmp_sids) {
4624 ads_msgfree(ads, res);
4625 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4628 if (num_sids) {
4629 *num_sids = tmp_num_sids;
4632 if (sids) {
4633 *sids = tmp_sids;
4636 if (user_sid) {
4637 *user_sid = tmp_user_sid;
4640 if (primary_group_sid) {
4641 *primary_group_sid = tmp_primary_group_sid;
4644 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4646 ads_msgfree(ads, res);
4647 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4651 * Find a sAMAccountName in LDAP
4652 * @param ads connection to ads server
4653 * @param mem_ctx TALLOC_CTX for allocating sid array
4654 * @param samaccountname to search
4655 * @param uac_ret uint32_t pointer userAccountControl attribute value
4656 * @param dn_ret pointer to dn
4657 * @return status of token query
4659 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4660 TALLOC_CTX *mem_ctx,
4661 const char *samaccountname,
4662 uint32_t *uac_ret,
4663 const char **dn_ret)
4665 ADS_STATUS status;
4666 const char *attrs[] = { "userAccountControl", NULL };
4667 const char *filter;
4668 LDAPMessage *res = NULL;
4669 char *dn = NULL;
4670 uint32_t uac = 0;
4672 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4673 samaccountname);
4674 if (filter == NULL) {
4675 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4676 goto out;
4679 status = ads_do_search_all(ads, ads->config.bind_path,
4680 LDAP_SCOPE_SUBTREE,
4681 filter, attrs, &res);
4683 if (!ADS_ERR_OK(status)) {
4684 goto out;
4687 if (ads_count_replies(ads, res) != 1) {
4688 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4689 goto out;
4692 dn = ads_get_dn(ads, talloc_tos(), res);
4693 if (dn == NULL) {
4694 status = ADS_ERROR(LDAP_NO_MEMORY);
4695 goto out;
4698 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4699 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4700 goto out;
4703 if (uac_ret) {
4704 *uac_ret = uac;
4707 if (dn_ret) {
4708 *dn_ret = talloc_strdup(mem_ctx, dn);
4709 if (!*dn_ret) {
4710 status = ADS_ERROR(LDAP_NO_MEMORY);
4711 goto out;
4714 out:
4715 TALLOC_FREE(dn);
4716 ads_msgfree(ads, res);
4718 return status;
4722 * find our configuration path
4723 * @param ads connection to ads server
4724 * @param mem_ctx Pointer to talloc context
4725 * @param config_path Pointer to the config path
4726 * @return status of search
4728 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4729 TALLOC_CTX *mem_ctx,
4730 char **config_path)
4732 ADS_STATUS status;
4733 LDAPMessage *res = NULL;
4734 const char *config_context = NULL;
4735 const char *attrs[] = { "configurationNamingContext", NULL };
4737 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4738 "(objectclass=*)", attrs, &res);
4739 if (!ADS_ERR_OK(status)) {
4740 return status;
4743 config_context = ads_pull_string(ads, mem_ctx, res,
4744 "configurationNamingContext");
4745 ads_msgfree(ads, res);
4746 if (!config_context) {
4747 return ADS_ERROR(LDAP_NO_MEMORY);
4750 if (config_path) {
4751 *config_path = talloc_strdup(mem_ctx, config_context);
4752 if (!*config_path) {
4753 return ADS_ERROR(LDAP_NO_MEMORY);
4757 return ADS_ERROR(LDAP_SUCCESS);
4761 * find the displayName of an extended right
4762 * @param ads connection to ads server
4763 * @param config_path The config path
4764 * @param mem_ctx Pointer to talloc context
4765 * @param GUID struct of the rightsGUID
4766 * @return status of search
4768 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4769 const char *config_path,
4770 TALLOC_CTX *mem_ctx,
4771 const struct GUID *rights_guid)
4773 ADS_STATUS rc;
4774 LDAPMessage *res = NULL;
4775 char *expr = NULL;
4776 const char *attrs[] = { "displayName", NULL };
4777 const char *result = NULL;
4778 const char *path;
4780 if (!ads || !mem_ctx || !rights_guid) {
4781 goto done;
4784 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4785 GUID_string(mem_ctx, rights_guid));
4786 if (!expr) {
4787 goto done;
4790 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4791 if (!path) {
4792 goto done;
4795 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4796 expr, attrs, &res);
4797 if (!ADS_ERR_OK(rc)) {
4798 goto done;
4801 if (ads_count_replies(ads, res) != 1) {
4802 goto done;
4805 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4807 done:
4808 ads_msgfree(ads, res);
4809 return result;
4813 * verify or build and verify an account ou
4814 * @param mem_ctx Pointer to talloc context
4815 * @param ads connection to ads server
4816 * @param account_ou
4817 * @return status of search
4820 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4821 ADS_STRUCT *ads,
4822 const char **account_ou)
4824 char **exploded_dn;
4825 const char *name;
4826 char *ou_string;
4828 if (account_ou == NULL) {
4829 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4832 if (*account_ou != NULL) {
4833 exploded_dn = ldap_explode_dn(*account_ou, 0);
4834 if (exploded_dn) {
4835 ldap_value_free(exploded_dn);
4836 return ADS_SUCCESS;
4840 ou_string = ads_ou_string(ads, *account_ou);
4841 if (!ou_string) {
4842 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4845 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4846 ads->config.bind_path);
4847 SAFE_FREE(ou_string);
4849 if (!name) {
4850 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4853 exploded_dn = ldap_explode_dn(name, 0);
4854 if (!exploded_dn) {
4855 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4857 ldap_value_free(exploded_dn);
4859 *account_ou = name;
4860 return ADS_SUCCESS;
4863 #endif