s3:libads: add ads_connect_simple_anon() helper
[Samba.git] / source3 / libads / ldap.c
blobc909e427fc5e5ff2c58ea330417377ad41532674
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"
39 #ifdef HAVE_LDAP
41 /**
42 * @file ldap.c
43 * @brief basic ldap client-side routines for ads server communications
45 * The routines contained here should do the necessary ldap calls for
46 * ads setups.
48 * Important note: attribute names passed into ads_ routines must
49 * already be in UTF-8 format. We do not convert them because in almost
50 * all cases, they are just ascii (which is represented with the same
51 * codepoints in UTF-8). This may have to change at some point
52 **/
55 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
57 static SIG_ATOMIC_T gotalarm;
59 /***************************************************************
60 Signal function to tell us we timed out.
61 ****************************************************************/
63 static void gotalarm_sig(int signum)
65 gotalarm = 1;
68 LDAP *ldap_open_with_timeout(const char *server,
69 struct sockaddr_storage *ss,
70 int port, unsigned int to)
72 LDAP *ldp = NULL;
73 int ldap_err;
74 char *uri;
76 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
77 "%u seconds\n", server, port, to));
79 if (to) {
80 /* Setup timeout */
81 gotalarm = 0;
82 CatchSignal(SIGALRM, gotalarm_sig);
83 alarm(to);
84 /* End setup timeout. */
87 if ( strchr_m(server, ':') ) {
88 /* IPv6 URI */
89 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
90 } else {
91 /* IPv4 URI */
92 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
94 if (uri == NULL) {
95 return NULL;
98 #ifdef HAVE_LDAP_INIT_FD
100 int fd = -1;
101 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
102 unsigned timeout_ms = 1000 * to;
104 status = open_socket_out(ss, port, timeout_ms, &fd);
105 if (!NT_STATUS_IS_OK(status)) {
106 DEBUG(3, ("open_socket_out: failed to open socket\n"));
107 return NULL;
110 /* define LDAP_PROTO_TCP from openldap.h if required */
111 #ifndef LDAP_PROTO_TCP
112 #define LDAP_PROTO_TCP 1
113 #endif
114 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
116 #elif defined(HAVE_LDAP_INITIALIZE)
117 ldap_err = ldap_initialize(&ldp, uri);
118 #else
119 ldp = ldap_open(server, port);
120 if (ldp != NULL) {
121 ldap_err = LDAP_SUCCESS;
122 } else {
123 ldap_err = LDAP_OTHER;
125 #endif
126 if (ldap_err != LDAP_SUCCESS) {
127 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
128 uri, ldap_err2string(ldap_err)));
129 } else {
130 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
133 if (to) {
134 /* Teardown timeout. */
135 alarm(0);
136 CatchSignal(SIGALRM, SIG_IGN);
139 return ldp;
142 static int ldap_search_with_timeout(LDAP *ld,
143 LDAP_CONST char *base,
144 int scope,
145 LDAP_CONST char *filter,
146 char **attrs,
147 int attrsonly,
148 LDAPControl **sctrls,
149 LDAPControl **cctrls,
150 int sizelimit,
151 LDAPMessage **res )
153 int to = lp_ldap_timeout();
154 struct timeval timeout;
155 struct timeval *timeout_ptr = NULL;
156 int result;
158 DBG_DEBUG("ldap_search: base => [%s], filter => [%s], scope => [%d]\n",
159 base,
160 filter,
161 scope);
163 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
164 gotalarm = 0;
166 if (to) {
167 timeout.tv_sec = to;
168 timeout.tv_usec = 0;
169 timeout_ptr = &timeout;
171 /* Setup alarm timeout. */
172 CatchSignal(SIGALRM, gotalarm_sig);
173 /* Make the alarm time one second beyond
174 the timeout we're setting for the
175 remote search timeout, to allow that
176 to fire in preference. */
177 alarm(to+1);
178 /* End setup timeout. */
182 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
183 attrsonly, sctrls, cctrls, timeout_ptr,
184 sizelimit, res);
186 if (to) {
187 /* Teardown alarm timeout. */
188 CatchSignal(SIGALRM, SIG_IGN);
189 alarm(0);
192 if (gotalarm != 0)
193 return LDAP_TIMELIMIT_EXCEEDED;
196 * A bug in OpenLDAP means ldap_search_ext_s can return
197 * LDAP_SUCCESS but with a NULL res pointer. Cope with
198 * this. See bug #6279 for details. JRA.
201 if (*res == NULL) {
202 return LDAP_TIMELIMIT_EXCEEDED;
205 return result;
208 /**********************************************
209 Do client and server sitename match ?
210 **********************************************/
212 bool ads_sitename_match(ADS_STRUCT *ads)
214 if (ads->config.server_site_name == NULL &&
215 ads->config.client_site_name == NULL ) {
216 DEBUG(10,("ads_sitename_match: both null\n"));
217 return True;
219 if (ads->config.server_site_name &&
220 ads->config.client_site_name &&
221 strequal(ads->config.server_site_name,
222 ads->config.client_site_name)) {
223 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
224 return True;
226 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
227 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
228 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
229 return False;
232 /**********************************************
233 Is this the closest DC ?
234 **********************************************/
236 bool ads_closest_dc(ADS_STRUCT *ads)
238 if (ads->config.flags & NBT_SERVER_CLOSEST) {
239 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
240 return True;
243 /* not sure if this can ever happen */
244 if (ads_sitename_match(ads)) {
245 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
246 return True;
249 if (ads->config.client_site_name == NULL) {
250 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
251 return True;
254 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
255 ads->config.ldap_server_name));
257 return False;
260 static bool ads_fill_cldap_reply(ADS_STRUCT *ads,
261 bool gc,
262 const struct sockaddr_storage *ss,
263 const struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply)
265 TALLOC_CTX *frame = talloc_stackframe();
266 bool ret = false;
267 char addr[INET6_ADDRSTRLEN];
268 ADS_STATUS status;
269 char *dn;
271 print_sockaddr(addr, sizeof(addr), ss);
273 /* Check the CLDAP reply flags */
275 if (!(cldap_reply->server_type & NBT_SERVER_LDAP)) {
276 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
277 addr);
278 ret = false;
279 goto out;
282 /* Fill in the ads->config values */
284 ADS_TALLOC_CONST_FREE(ads->config.workgroup);
285 ADS_TALLOC_CONST_FREE(ads->config.realm);
286 ADS_TALLOC_CONST_FREE(ads->config.bind_path);
287 ADS_TALLOC_CONST_FREE(ads->config.ldap_server_name);
288 ADS_TALLOC_CONST_FREE(ads->config.server_site_name);
289 ADS_TALLOC_CONST_FREE(ads->config.client_site_name);
291 if (!check_cldap_reply_required_flags(cldap_reply->server_type,
292 ads->config.flags)) {
293 ret = false;
294 goto out;
297 ads->config.ldap_server_name = talloc_strdup(ads,
298 cldap_reply->pdc_dns_name);
299 if (ads->config.ldap_server_name == NULL) {
300 DBG_WARNING("Out of memory\n");
301 ret = false;
302 goto out;
305 ads->config.workgroup = talloc_strdup(ads, cldap_reply->domain_name);
306 if (ads->config.workgroup == NULL) {
307 DBG_WARNING("Out of memory\n");
308 ret = false;
309 goto out;
312 ads->config.realm = talloc_asprintf_strupper_m(ads,
313 "%s",
314 cldap_reply->dns_domain);
315 if (ads->config.realm == NULL) {
316 DBG_WARNING("Out of memory\n");
317 ret = false;
318 goto out;
321 status = ads_build_dn(ads->config.realm, ads, &dn);
322 if (!ADS_ERR_OK(status)) {
323 DBG_DEBUG("Failed to build bind path: %s\n",
324 ads_errstr(status));
325 ret = false;
326 goto out;
328 ads->config.bind_path = dn;
330 if (*cldap_reply->server_site) {
331 ads->config.server_site_name =
332 talloc_strdup(ads, cldap_reply->server_site);
333 if (ads->config.server_site_name == NULL) {
334 DBG_WARNING("Out of memory\n");
335 ret = false;
336 goto out;
340 if (*cldap_reply->client_site) {
341 ads->config.client_site_name =
342 talloc_strdup(ads, cldap_reply->client_site);
343 if (ads->config.client_site_name == NULL) {
344 DBG_WARNING("Out of memory\n");
345 ret = false;
346 goto out;
350 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
351 ads->ldap.ss = *ss;
353 /* Store our site name. */
354 sitename_store(cldap_reply->domain_name, cldap_reply->client_site);
355 sitename_store(cldap_reply->dns_domain, cldap_reply->client_site);
357 /* Leave this until last so that the flags are not clobbered */
358 ads->config.flags = cldap_reply->server_type;
360 ret = true;
362 out:
364 TALLOC_FREE(frame);
365 return ret;
369 try a connection to a given ldap server, returning True and setting the servers IP
370 in the ads struct if successful
372 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
373 struct sockaddr_storage *ss)
375 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply = {};
376 TALLOC_CTX *frame = talloc_stackframe();
377 bool ok;
378 char addr[INET6_ADDRSTRLEN] = { 0, };
380 if (ss == NULL) {
381 TALLOC_FREE(frame);
382 return false;
385 print_sockaddr(addr, sizeof(addr), ss);
387 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
388 addr, ads->server.realm);
390 ok = ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply);
391 if (!ok) {
392 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
393 addr, ads->server.realm);
394 TALLOC_FREE(frame);
395 return false;
398 ok = ads_fill_cldap_reply(ads, gc, ss, &cldap_reply);
399 if (!ok) {
400 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
401 addr, ads->server.realm);
402 TALLOC_FREE(frame);
403 return false;
406 TALLOC_FREE(frame);
407 return true;
410 /**********************************************************************
411 send a cldap ping to list of servers, one at a time, until one of
412 them answers it's an ldap server. Record success in the ADS_STRUCT.
413 Take note of and update negative connection cache.
414 **********************************************************************/
416 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
417 const char *domain,
418 struct samba_sockaddr *sa_list,
419 size_t count)
421 TALLOC_CTX *frame = talloc_stackframe();
422 struct timeval endtime = timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
423 uint32_t nt_version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
424 struct tsocket_address **ts_list = NULL;
425 const struct tsocket_address * const *ts_list_const = NULL;
426 struct samba_sockaddr **req_sa_list = NULL;
427 struct netlogon_samlogon_response **responses = NULL;
428 size_t num_requests = 0;
429 NTSTATUS status;
430 size_t i;
431 bool ok = false;
432 bool retry;
434 ts_list = talloc_zero_array(frame,
435 struct tsocket_address *,
436 count);
437 if (ts_list == NULL) {
438 TALLOC_FREE(frame);
439 return NT_STATUS_NO_MEMORY;
442 req_sa_list = talloc_zero_array(frame,
443 struct samba_sockaddr *,
444 count);
445 if (req_sa_list == NULL) {
446 TALLOC_FREE(frame);
447 return NT_STATUS_NO_MEMORY;
450 again:
452 * The retry loop is bound by the timeout
454 retry = false;
455 num_requests = 0;
457 for (i = 0; i < count; i++) {
458 char server[INET6_ADDRSTRLEN];
459 int ret;
461 if (is_zero_addr(&sa_list[i].u.ss)) {
462 continue;
465 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
467 status = check_negative_conn_cache(domain, server);
468 if (!NT_STATUS_IS_OK(status)) {
469 continue;
472 ret = tsocket_address_inet_from_strings(ts_list, "ip",
473 server, LDAP_PORT,
474 &ts_list[num_requests]);
475 if (ret != 0) {
476 status = map_nt_error_from_unix(errno);
477 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
478 server, nt_errstr(status));
479 TALLOC_FREE(frame);
480 return status;
483 req_sa_list[num_requests] = &sa_list[i];
484 num_requests += 1;
487 DBG_DEBUG("Try to create %zu netlogon connections for domain '%s' "
488 "(provided count of addresses was %zu).\n",
489 num_requests,
490 domain,
491 count);
493 if (num_requests == 0) {
494 status = NT_STATUS_NO_LOGON_SERVERS;
495 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
496 domain, num_requests, count, nt_errstr(status));
497 TALLOC_FREE(frame);
498 return status;
501 ts_list_const = (const struct tsocket_address * const *)ts_list;
503 status = cldap_multi_netlogon(frame,
504 ts_list_const, num_requests,
505 ads->server.realm, NULL,
506 nt_version,
507 1, endtime, &responses);
508 if (!NT_STATUS_IS_OK(status)) {
509 DBG_WARNING("cldap_multi_netlogon(realm=%s, num_requests=%zu) "
510 "for count[%zu] - %s\n",
511 ads->server.realm,
512 num_requests, count,
513 nt_errstr(status));
514 TALLOC_FREE(frame);
515 return NT_STATUS_NO_LOGON_SERVERS;
518 for (i = 0; i < num_requests; i++) {
519 struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply = NULL;
520 char server[INET6_ADDRSTRLEN];
522 if (responses[i] == NULL) {
523 continue;
526 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
528 if (responses[i]->ntver != NETLOGON_NT_VERSION_5EX) {
529 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
530 ads->server.realm,
531 responses[i]->ntver, server);
532 continue;
535 cldap_reply = &responses[i]->data.nt5_ex;
537 /* Returns ok only if it matches the correct server type */
538 ok = ads_fill_cldap_reply(ads,
539 false,
540 &req_sa_list[i]->u.ss,
541 cldap_reply);
542 if (ok) {
543 DBG_DEBUG("realm[%s]: selected %s => %s\n",
544 ads->server.realm,
545 server, cldap_reply->pdc_dns_name);
546 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
547 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
548 cldap_reply);
550 TALLOC_FREE(frame);
551 return NT_STATUS_OK;
554 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
555 ads->server.realm,
556 server, cldap_reply->pdc_dns_name);
557 if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
558 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
559 cldap_reply);
561 add_failed_connection_entry(domain, server,
562 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID);
563 retry = true;
566 if (retry) {
567 bool expired;
569 expired = timeval_expired(&endtime);
570 if (!expired) {
571 goto again;
575 /* keep track of failures as all were not suitable */
576 for (i = 0; i < num_requests; i++) {
577 char server[INET6_ADDRSTRLEN];
579 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
581 add_failed_connection_entry(domain, server,
582 NT_STATUS_UNSUCCESSFUL);
585 status = NT_STATUS_NO_LOGON_SERVERS;
586 DBG_WARNING("realm[%s] no valid response "
587 "num_requests[%zu] for count[%zu] - %s\n",
588 ads->server.realm,
589 num_requests, count, nt_errstr(status));
590 TALLOC_FREE(frame);
591 return NT_STATUS_NO_LOGON_SERVERS;
594 /***************************************************************************
595 resolve a name and perform an "ldap ping" using NetBIOS and related methods
596 ****************************************************************************/
598 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
599 const char *domain, const char *realm)
601 size_t i;
602 size_t count = 0;
603 struct samba_sockaddr *sa_list = NULL;
604 NTSTATUS status;
606 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
607 domain));
609 status = get_sorted_dc_list(talloc_tos(),
610 domain,
611 NULL,
612 &sa_list,
613 &count,
614 false);
615 if (!NT_STATUS_IS_OK(status)) {
616 return status;
619 /* remove servers which are known to be dead based on
620 the corresponding DNS method */
621 if (*realm) {
622 for (i = 0; i < count; ++i) {
623 char server[INET6_ADDRSTRLEN];
625 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
627 if(!NT_STATUS_IS_OK(
628 check_negative_conn_cache(realm, server))) {
629 /* Ensure we add the workgroup name for this
630 IP address as negative too. */
631 add_failed_connection_entry(
632 domain, server,
633 NT_STATUS_UNSUCCESSFUL);
638 status = cldap_ping_list(ads, domain, sa_list, count);
640 TALLOC_FREE(sa_list);
642 return status;
646 /**********************************************************************
647 resolve a name and perform an "ldap ping" using DNS
648 **********************************************************************/
650 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
651 const char *realm)
653 size_t count = 0;
654 struct samba_sockaddr *sa_list = NULL;
655 NTSTATUS status;
657 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
658 realm));
660 status = get_sorted_dc_list(talloc_tos(),
661 realm,
662 sitename,
663 &sa_list,
664 &count,
665 true);
666 if (!NT_STATUS_IS_OK(status)) {
667 TALLOC_FREE(sa_list);
668 return status;
671 status = cldap_ping_list(ads, realm, sa_list, count);
673 TALLOC_FREE(sa_list);
675 return status;
678 /**********************************************************************
679 Try to find an AD dc using our internal name resolution routines
680 Try the realm first and then the workgroup name if netbios is not
681 disabled
682 **********************************************************************/
684 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
686 const char *c_domain = "";
687 const char *c_realm;
688 bool use_own_domain = False;
689 char *sitename = NULL;
690 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
691 bool ok = false;
693 /* if the realm and workgroup are both empty, assume they are ours */
695 /* realm */
696 c_realm = ads->server.realm;
698 if (c_realm == NULL)
699 c_realm = "";
701 if (!*c_realm) {
702 /* special case where no realm and no workgroup means our own */
703 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
704 use_own_domain = True;
705 c_realm = lp_realm();
709 if (!lp_disable_netbios()) {
710 if (use_own_domain) {
711 c_domain = lp_workgroup();
712 } else {
713 c_domain = ads->server.workgroup;
714 if (!*c_realm && (!c_domain || !*c_domain)) {
715 c_domain = lp_workgroup();
719 if (!c_domain) {
720 c_domain = "";
724 if (!*c_realm && !*c_domain) {
725 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
726 "what to do\n"));
727 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
731 * In case of LDAP we use get_dc_name() as that
732 * creates the custom krb5.conf file
734 if (ads->auth.flags & ADS_AUTH_GENERATE_KRB5_CONFIG) {
735 fstring srv_name;
736 struct sockaddr_storage ip_out;
738 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
739 " and falling back to domain '%s'\n",
740 c_realm, c_domain));
742 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
743 if (ok) {
744 if (is_zero_addr(&ip_out)) {
745 return NT_STATUS_NO_LOGON_SERVERS;
749 * we call ads_try_connect() to fill in the
750 * ads->config details
752 ok = ads_try_connect(ads, false, &ip_out);
753 if (ok) {
754 return NT_STATUS_OK;
758 return NT_STATUS_NO_LOGON_SERVERS;
761 if (*c_realm) {
762 sitename = sitename_fetch(talloc_tos(), c_realm);
763 status = resolve_and_ping_dns(ads, sitename, c_realm);
765 if (NT_STATUS_IS_OK(status)) {
766 TALLOC_FREE(sitename);
767 return status;
770 /* In case we failed to contact one of our closest DC on our
771 * site we
772 * need to try to find another DC, retry with a site-less SRV
773 * DNS query
774 * - Guenther */
776 if (sitename) {
777 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
778 "our site (%s), Trying to find another DC "
779 "for realm '%s' (domain '%s')\n",
780 sitename, c_realm, c_domain));
781 namecache_delete(c_realm, 0x1C);
782 status =
783 resolve_and_ping_dns(ads, NULL, c_realm);
785 if (NT_STATUS_IS_OK(status)) {
786 TALLOC_FREE(sitename);
787 return status;
791 TALLOC_FREE(sitename);
794 /* try netbios as fallback - if permitted,
795 or if configuration specifically requests it */
796 if (*c_domain) {
797 if (*c_realm) {
798 DEBUG(3, ("ads_find_dc: falling back to netbios "
799 "name resolution for domain '%s' (realm '%s')\n",
800 c_domain, c_realm));
803 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
804 if (NT_STATUS_IS_OK(status)) {
805 return status;
809 DEBUG(1, ("ads_find_dc: "
810 "name resolution for realm '%s' (domain '%s') failed: %s\n",
811 c_realm, c_domain, nt_errstr(status)));
812 return status;
816 * Connect to the LDAP server
817 * @param ads Pointer to an existing ADS_STRUCT
818 * @return status of connection
820 static ADS_STATUS ads_connect_internal(ADS_STRUCT *ads,
821 struct cli_credentials *creds)
823 int version = LDAP_VERSION3;
824 ADS_STATUS status;
825 NTSTATUS ntstatus;
826 char addr[INET6_ADDRSTRLEN];
827 struct sockaddr_storage existing_ss;
828 bool tls = false;
829 bool start_tls = false;
831 zero_sockaddr(&existing_ss);
833 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
834 SMB_ASSERT(creds != NULL);
837 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
839 * Simple anonyous binds are only
840 * allowed for anonymous credentials
842 SMB_ASSERT(cli_credentials_is_anonymous(creds));
845 if (!(ads->auth.flags & (ADS_AUTH_NO_BIND|ADS_AUTH_ANON_BIND))) {
846 ads->auth.flags |= ADS_AUTH_GENERATE_KRB5_CONFIG;
850 * ads_connect can be passed in a reused ADS_STRUCT
851 * with an existing non-zero ads->ldap.ss IP address
852 * that was stored by going through ads_find_dc()
853 * if ads->server.ldap_server was NULL.
855 * If ads->server.ldap_server is still NULL but
856 * the target address isn't the zero address, then
857 * store that address off off before zeroing out
858 * ads->ldap so we don't keep doing multiple calls
859 * to ads_find_dc() in the reuse case.
861 * If a caller wants a clean ADS_STRUCT they
862 * will TALLOC_FREE it and allocate a new one
863 * by calling ads_init(), which ensures
864 * ads->ldap.ss is a properly zero'ed out valid IP
865 * address.
867 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
868 /* Save off the address we previously found by ads_find_dc(). */
869 existing_ss = ads->ldap.ss;
872 ads_zero_ldap(ads);
873 ZERO_STRUCT(ads->ldap_tls_data);
874 ZERO_STRUCT(ads->ldap_wrap_data);
875 ads->ldap.last_attempt = time_mono(NULL);
876 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
878 /* try with a user specified server */
880 if (DEBUGLEVEL >= 11) {
881 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
882 DEBUG(11,("ads_connect: entering\n"));
883 DEBUGADD(11,("%s\n", s));
884 TALLOC_FREE(s);
887 if (ads->server.ldap_server) {
888 bool ok = false;
889 struct sockaddr_storage ss;
891 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
892 ads->server.ldap_server);
893 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
894 if (!ok) {
895 DEBUG(5,("ads_connect: unable to resolve name %s\n",
896 ads->server.ldap_server));
897 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
898 goto out;
901 if (is_zero_addr(&ss)) {
902 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
903 goto out;
906 ok = ads_try_connect(ads, ads->server.gc, &ss);
907 if (ok) {
908 goto got_connection;
911 /* The choice of which GC use is handled one level up in
912 ads_connect_gc(). If we continue on from here with
913 ads_find_dc() we will get GC searches on port 389 which
914 doesn't work. --jerry */
916 if (ads->server.gc == true) {
917 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
920 if (ads->server.no_fallback) {
921 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
922 goto out;
926 if (!is_zero_addr(&existing_ss)) {
927 /* We saved off who we should talk to. */
928 bool ok = ads_try_connect(ads,
929 ads->server.gc,
930 &existing_ss);
931 if (ok) {
932 goto got_connection;
935 * Keep trying to find a server and fall through
936 * into ads_find_dc() again.
938 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
939 "trying to find another DC.\n");
942 ntstatus = ads_find_dc(ads);
943 if (NT_STATUS_IS_OK(ntstatus)) {
944 goto got_connection;
947 status = ADS_ERROR_NT(ntstatus);
948 goto out;
950 got_connection:
952 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
953 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
955 if (!ads->auth.kdc_server) {
956 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
957 ads->auth.kdc_server = talloc_strdup(ads, addr);
958 if (ads->auth.kdc_server == NULL) {
959 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
960 goto out;
964 /* If the caller() requested no LDAP bind, then we are done */
966 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
967 status = ADS_SUCCESS;
968 goto out;
971 ads->ldap_tls_data.mem_ctx = talloc_init("ads LDAP TLS connection memory");
972 if (!ads->ldap_tls_data.mem_ctx) {
973 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
974 goto out;
977 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
978 if (!ads->ldap_wrap_data.mem_ctx) {
979 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
980 goto out;
983 /* Otherwise setup the TCP LDAP session */
985 if (ads->auth.flags & ADS_AUTH_SASL_LDAPS) {
986 tls = true;
987 ads->ldap.port = 636;
988 } else if (ads->auth.flags & ADS_AUTH_SASL_STARTTLS) {
989 tls = true;
990 start_tls = true;
991 ads->ldap.port = 389;
992 } else {
993 ads->ldap.port = 389;
996 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
997 &ads->ldap.ss,
998 ads->ldap.port, lp_ldap_timeout());
999 if (ads->ldap.ld == NULL) {
1000 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
1001 goto out;
1003 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
1005 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1007 if (start_tls) {
1008 unsigned int to = lp_ldap_connection_timeout();
1009 struct berval *rspdata = NULL;
1010 char *rspoid = NULL;
1011 int rc;
1013 if (to) {
1014 /* Setup timeout */
1015 gotalarm = 0;
1016 CatchSignal(SIGALRM, gotalarm_sig);
1017 alarm(to);
1018 /* End setup timeout. */
1021 rc = ldap_extended_operation_s(ads->ldap.ld,
1022 LDAP_EXOP_START_TLS,
1023 NULL,
1024 NULL,
1025 NULL,
1026 &rspoid,
1027 &rspdata);
1028 if (gotalarm != 0 && rc == LDAP_SUCCESS) {
1029 rc = LDAP_TIMEOUT;
1032 if (to) {
1033 /* Teardown timeout. */
1034 alarm(0);
1035 CatchSignal(SIGALRM, SIG_IGN);
1038 if (rspoid != NULL) {
1039 ldap_memfree(rspoid);
1042 if (rspdata != NULL) {
1043 ber_bvfree(rspdata);
1046 if (rc != LDAP_SUCCESS) {
1047 status = ADS_ERROR_LDAP(rc);
1048 goto out;
1052 if (tls) {
1053 unsigned int to = lp_ldap_connection_timeout();
1055 if (to) {
1056 /* Setup timeout */
1057 gotalarm = 0;
1058 CatchSignal(SIGALRM, gotalarm_sig);
1059 alarm(to);
1060 /* End setup timeout. */
1063 status = ads_setup_tls_wrapping(&ads->ldap_tls_data,
1064 ads->ldap.ld,
1065 ads->config.ldap_server_name);
1067 if (to) {
1068 /* Teardown timeout. */
1069 alarm(0);
1070 CatchSignal(SIGALRM, SIG_IGN);
1073 if ( !ADS_ERR_OK(status) ) {
1074 goto out;
1078 /* cache the successful connection for workgroup and realm */
1079 if (ads_closest_dc(ads)) {
1080 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
1081 saf_store( ads->server.realm, ads->config.ldap_server_name);
1084 /* fill in the current time and offsets */
1086 status = ads_current_time( ads );
1087 if ( !ADS_ERR_OK(status) ) {
1088 goto out;
1091 /* Now do the bind */
1093 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
1094 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
1095 goto out;
1098 status = ads_sasl_bind(ads, creds);
1100 out:
1101 if (DEBUGLEVEL >= 11) {
1102 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1103 DEBUG(11,("ads_connect: leaving with: %s\n",
1104 ads_errstr(status)));
1105 DEBUGADD(11,("%s\n", s));
1106 TALLOC_FREE(s);
1109 return status;
1113 * Connect to the LDAP server using without a bind
1114 * and without a tcp connection at all
1116 * @param ads Pointer to an existing ADS_STRUCT
1117 * @return status of connection
1119 ADS_STATUS ads_connect_cldap_only(ADS_STRUCT *ads)
1121 ads->auth.flags |= ADS_AUTH_NO_BIND;
1122 return ads_connect_internal(ads, NULL);
1126 * Connect to the LDAP server
1127 * @param ads Pointer to an existing ADS_STRUCT
1128 * @return status of connection
1130 ADS_STATUS ads_connect_creds(ADS_STRUCT *ads, struct cli_credentials *creds)
1132 SMB_ASSERT(creds != NULL);
1135 * We allow upgrades from
1136 * ADS_AUTH_NO_BIND if credentials
1137 * are specified
1139 ads->auth.flags &= ~ADS_AUTH_NO_BIND;
1142 * We allow upgrades from ADS_AUTH_ANON_BIND,
1143 * as we don't want to use simple binds with
1144 * non-anon credentials
1146 if (!cli_credentials_is_anonymous(creds)) {
1147 ads->auth.flags &= ~ADS_AUTH_ANON_BIND;
1150 return ads_connect_internal(ads, creds);
1154 * Connect to the LDAP server using anonymous credentials
1155 * using a simple bind without username/password
1157 * @param ads Pointer to an existing ADS_STRUCT
1158 * @return status of connection
1160 ADS_STATUS ads_connect_simple_anon(ADS_STRUCT *ads)
1162 TALLOC_CTX *frame = talloc_stackframe();
1163 struct cli_credentials *creds = NULL;
1164 ADS_STATUS status;
1166 creds = cli_credentials_init_anon(frame);
1167 if (creds == NULL) {
1168 TALLOC_FREE(frame);
1169 return ADS_ERROR_SYSTEM(errno);
1172 ads->auth.flags |= ADS_AUTH_ANON_BIND;
1173 status = ads_connect_creds(ads, creds);
1174 TALLOC_FREE(frame);
1175 return status;
1179 * Connect to the LDAP server
1180 * @param ads Pointer to an existing ADS_STRUCT
1181 * @return status of connection
1183 ADS_STATUS ads_connect(ADS_STRUCT *ads)
1185 TALLOC_CTX *frame = talloc_stackframe();
1186 struct cli_credentials *creds = NULL;
1187 ADS_STATUS status;
1188 NTSTATUS ntstatus;
1190 ntstatus = ads_legacy_creds(ads, frame, &creds);
1191 if (!NT_STATUS_IS_OK(ntstatus)) {
1192 TALLOC_FREE(frame);
1193 return ADS_ERROR_NT(ntstatus);
1196 status = ads_connect_internal(ads, creds);
1197 TALLOC_FREE(frame);
1198 return status;
1202 * Connect to the LDAP server using given credentials
1203 * @param ads Pointer to an existing ADS_STRUCT
1204 * @return status of connection
1206 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
1208 ads->auth.flags |= ADS_AUTH_USER_CREDS;
1210 return ads_connect(ads);
1214 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1215 * @param ads Pointer to an existing ADS_STRUCT
1217 * Sets the ads->ldap.ss to a valid
1218 * zero ip address that can be detected by
1219 * our is_zero_addr() function. Otherwise
1220 * it is left as AF_UNSPEC (0).
1222 void ads_zero_ldap(ADS_STRUCT *ads)
1224 ZERO_STRUCT(ads->ldap);
1226 * Initialize the sockaddr_storage so we can use
1227 * sockaddr test functions against it.
1229 zero_sockaddr(&ads->ldap.ss);
1233 * Disconnect the LDAP server
1234 * @param ads Pointer to an existing ADS_STRUCT
1236 void ads_disconnect(ADS_STRUCT *ads)
1238 if (ads->ldap.ld) {
1239 ldap_unbind(ads->ldap.ld);
1240 ads->ldap.ld = NULL;
1242 if (ads->ldap_tls_data.mem_ctx) {
1243 talloc_free(ads->ldap_tls_data.mem_ctx);
1245 if (ads->ldap_wrap_data.wrap_ops &&
1246 ads->ldap_wrap_data.wrap_ops->disconnect) {
1247 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1249 if (ads->ldap_wrap_data.mem_ctx) {
1250 talloc_free(ads->ldap_wrap_data.mem_ctx);
1252 ads_zero_ldap(ads);
1253 ZERO_STRUCT(ads->ldap_tls_data);
1254 ZERO_STRUCT(ads->ldap_wrap_data);
1258 Duplicate a struct berval into talloc'ed memory
1260 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1262 struct berval *value;
1264 if (!in_val) return NULL;
1266 value = talloc_zero(ctx, struct berval);
1267 if (value == NULL)
1268 return NULL;
1269 if (in_val->bv_len == 0) return value;
1271 value->bv_len = in_val->bv_len;
1272 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1273 in_val->bv_len);
1274 return value;
1278 Make a values list out of an array of (struct berval *)
1280 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1281 const struct berval **in_vals)
1283 struct berval **values;
1284 int i;
1286 if (!in_vals) return NULL;
1287 for (i=0; in_vals[i]; i++)
1288 ; /* count values */
1289 values = talloc_zero_array(ctx, struct berval *, i+1);
1290 if (!values) return NULL;
1292 for (i=0; in_vals[i]; i++) {
1293 values[i] = dup_berval(ctx, in_vals[i]);
1295 return values;
1299 UTF8-encode a values list out of an array of (char *)
1301 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1303 char **values;
1304 int i;
1305 size_t size;
1307 if (!in_vals) return NULL;
1308 for (i=0; in_vals[i]; i++)
1309 ; /* count values */
1310 values = talloc_zero_array(ctx, char *, i+1);
1311 if (!values) return NULL;
1313 for (i=0; in_vals[i]; i++) {
1314 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1315 TALLOC_FREE(values);
1316 return NULL;
1319 return values;
1323 Pull a (char *) array out of a UTF8-encoded values list
1325 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1327 char **values;
1328 int i;
1329 size_t converted_size;
1331 if (!in_vals) return NULL;
1332 for (i=0; in_vals[i]; i++)
1333 ; /* count values */
1334 values = talloc_zero_array(ctx, char *, i+1);
1335 if (!values) return NULL;
1337 for (i=0; in_vals[i]; i++) {
1338 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1339 &converted_size)) {
1340 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1341 "%s\n", strerror(errno)));
1344 return values;
1348 * Do a search with paged results. cookie must be null on the first
1349 * call, and then returned on each subsequent call. It will be null
1350 * again when the entire search is complete
1351 * @param ads connection to ads server
1352 * @param bind_path Base dn for the search
1353 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1354 * @param expr Search expression - specified in local charset
1355 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1356 * @param res ** which will contain results - free res* with ads_msgfree()
1357 * @param count Number of entries retrieved on this page
1358 * @param cookie The paged results cookie to be returned on subsequent calls
1359 * @return status of search
1361 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1362 const char *bind_path,
1363 int scope, const char *expr,
1364 const char **attrs, void *args,
1365 LDAPMessage **res,
1366 int *count, struct berval **cookie)
1368 int rc, i, version;
1369 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1370 size_t converted_size;
1371 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1372 BerElement *cookie_be = NULL;
1373 struct berval *cookie_bv= NULL;
1374 BerElement *ext_be = NULL;
1375 struct berval *ext_bv= NULL;
1377 TALLOC_CTX *ctx;
1378 ads_control *external_control = (ads_control *) args;
1380 *res = NULL;
1382 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1383 return ADS_ERROR(LDAP_NO_MEMORY);
1385 /* 0 means the conversion worked but the result was empty
1386 so we only fail if it's -1. In any case, it always
1387 at least nulls out the dest */
1388 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1389 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1391 rc = LDAP_NO_MEMORY;
1392 goto done;
1395 if (!attrs || !(*attrs))
1396 search_attrs = NULL;
1397 else {
1398 /* This would be the utf8-encoded version...*/
1399 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1400 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1401 rc = LDAP_NO_MEMORY;
1402 goto done;
1406 /* Paged results only available on ldap v3 or later */
1407 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1408 if (version < LDAP_VERSION3) {
1409 rc = LDAP_NOT_SUPPORTED;
1410 goto done;
1413 cookie_be = ber_alloc_t(LBER_USE_DER);
1414 if (*cookie) {
1415 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1416 ber_bvfree(*cookie); /* don't need it from last time */
1417 *cookie = NULL;
1418 } else {
1419 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1421 ber_flatten(cookie_be, &cookie_bv);
1422 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1423 PagedResults.ldctl_iscritical = (char) 1;
1424 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1425 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1427 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1428 NoReferrals.ldctl_iscritical = (char) 0;
1429 NoReferrals.ldctl_value.bv_len = 0;
1430 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1432 if (external_control &&
1433 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1434 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1436 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1437 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1439 /* win2k does not accept a ldctl_value being passed in */
1441 if (external_control->val != 0) {
1443 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1444 rc = LDAP_NO_MEMORY;
1445 goto done;
1448 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1449 rc = LDAP_NO_MEMORY;
1450 goto done;
1452 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1453 rc = LDAP_NO_MEMORY;
1454 goto done;
1457 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1458 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1460 } else {
1461 ExternalCtrl.ldctl_value.bv_len = 0;
1462 ExternalCtrl.ldctl_value.bv_val = NULL;
1465 controls[0] = &NoReferrals;
1466 controls[1] = &PagedResults;
1467 controls[2] = &ExternalCtrl;
1468 controls[3] = NULL;
1470 } else {
1471 controls[0] = &NoReferrals;
1472 controls[1] = &PagedResults;
1473 controls[2] = NULL;
1476 /* we need to disable referrals as the openldap libs don't
1477 handle them and paged results at the same time. Using them
1478 together results in the result record containing the server
1479 page control being removed from the result list (tridge/jmcd)
1481 leaving this in despite the control that says don't generate
1482 referrals, in case the server doesn't support it (jmcd)
1484 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1486 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1487 search_attrs, 0, controls,
1488 NULL, LDAP_NO_LIMIT,
1489 (LDAPMessage **)res);
1491 ber_free(cookie_be, 1);
1492 ber_bvfree(cookie_bv);
1494 if (rc) {
1495 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1496 ldap_err2string(rc)));
1497 if (rc == LDAP_OTHER) {
1498 char *ldap_errmsg;
1499 int ret;
1501 ret = ldap_parse_result(ads->ldap.ld,
1502 *res,
1503 NULL,
1504 NULL,
1505 &ldap_errmsg,
1506 NULL,
1507 NULL,
1509 if (ret == LDAP_SUCCESS) {
1510 DEBUG(3, ("ldap_search_with_timeout(%s) "
1511 "error: %s\n", expr, ldap_errmsg));
1512 ldap_memfree(ldap_errmsg);
1515 goto done;
1518 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1519 NULL, &rcontrols, 0);
1521 if (!rcontrols) {
1522 goto done;
1525 for (i=0; rcontrols[i]; i++) {
1526 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1527 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1528 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1529 &cookie_bv);
1530 /* the berval is the cookie, but must be freed when
1531 it is all done */
1532 if (cookie_bv->bv_len) /* still more to do */
1533 *cookie=ber_bvdup(cookie_bv);
1534 else
1535 *cookie=NULL;
1536 ber_bvfree(cookie_bv);
1537 ber_free(cookie_be, 1);
1538 break;
1541 ldap_controls_free(rcontrols);
1543 done:
1544 talloc_destroy(ctx);
1546 if (ext_be) {
1547 ber_free(ext_be, 1);
1550 if (ext_bv) {
1551 ber_bvfree(ext_bv);
1554 if (rc != LDAP_SUCCESS && *res != NULL) {
1555 ads_msgfree(ads, *res);
1556 *res = NULL;
1559 /* if/when we decide to utf8-encode attrs, take out this next line */
1560 TALLOC_FREE(search_attrs);
1562 return ADS_ERROR(rc);
1565 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1566 int scope, const char *expr,
1567 const char **attrs, LDAPMessage **res,
1568 int *count, struct berval **cookie)
1570 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1575 * Get all results for a search. This uses ads_do_paged_search() to return
1576 * all entries in a large search.
1577 * @param ads connection to ads server
1578 * @param bind_path Base dn for the search
1579 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1580 * @param expr Search expression
1581 * @param attrs Attributes to retrieve
1582 * @param res ** which will contain results - free res* with ads_msgfree()
1583 * @return status of search
1585 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1586 int scope, const char *expr,
1587 const char **attrs, void *args,
1588 LDAPMessage **res)
1590 struct berval *cookie = NULL;
1591 int count = 0;
1592 ADS_STATUS status;
1594 *res = NULL;
1595 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1596 &count, &cookie);
1598 if (!ADS_ERR_OK(status))
1599 return status;
1601 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1602 while (cookie) {
1603 LDAPMessage *res2 = NULL;
1604 LDAPMessage *msg, *next;
1606 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1607 attrs, args, &res2, &count, &cookie);
1608 if (!ADS_ERR_OK(status)) {
1609 break;
1612 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1613 that this works on all ldap libs, but I have only tested with openldap */
1614 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1615 next = ads_next_message(ads, msg);
1616 ldap_add_result_entry((LDAPMessage **)res, msg);
1618 /* note that we do not free res2, as the memory is now
1619 part of the main returned list */
1621 #else
1622 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1623 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1624 #endif
1626 return status;
1629 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1630 int scope, const char *expr,
1631 const char **attrs, LDAPMessage **res)
1633 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1636 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1637 int scope, const char *expr,
1638 const char **attrs, uint32_t sd_flags,
1639 LDAPMessage **res)
1641 ads_control args;
1643 args.control = ADS_SD_FLAGS_OID;
1644 args.val = sd_flags;
1645 args.critical = True;
1647 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1652 * Run a function on all results for a search. Uses ads_do_paged_search() and
1653 * runs the function as each page is returned, using ads_process_results()
1654 * @param ads connection to ads server
1655 * @param bind_path Base dn for the search
1656 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1657 * @param expr Search expression - specified in local charset
1658 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1659 * @param fn Function which takes attr name, values list, and data_area
1660 * @param data_area Pointer which is passed to function on each call
1661 * @return status of search
1663 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1664 int scope, const char *expr, const char **attrs,
1665 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1666 void *data_area)
1668 struct berval *cookie = NULL;
1669 int count = 0;
1670 ADS_STATUS status;
1671 LDAPMessage *res;
1673 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1674 &count, &cookie);
1676 if (!ADS_ERR_OK(status)) return status;
1678 ads_process_results(ads, res, fn, data_area);
1679 ads_msgfree(ads, res);
1681 while (cookie) {
1682 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1683 &res, &count, &cookie);
1685 if (!ADS_ERR_OK(status)) break;
1687 ads_process_results(ads, res, fn, data_area);
1688 ads_msgfree(ads, res);
1691 return status;
1695 * Do a search with a timeout.
1696 * @param ads connection to ads server
1697 * @param bind_path Base dn for the search
1698 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1699 * @param expr Search expression
1700 * @param attrs Attributes to retrieve
1701 * @param res ** which will contain results - free res* with ads_msgfree()
1702 * @return status of search
1704 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1705 const char *expr,
1706 const char **attrs, LDAPMessage **res)
1708 int rc;
1709 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1710 size_t converted_size;
1711 TALLOC_CTX *ctx;
1713 *res = NULL;
1714 if (!(ctx = talloc_init("ads_do_search"))) {
1715 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1716 return ADS_ERROR(LDAP_NO_MEMORY);
1719 /* 0 means the conversion worked but the result was empty
1720 so we only fail if it's negative. In any case, it always
1721 at least nulls out the dest */
1722 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1723 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1725 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1726 rc = LDAP_NO_MEMORY;
1727 goto done;
1730 if (!attrs || !(*attrs))
1731 search_attrs = NULL;
1732 else {
1733 /* This would be the utf8-encoded version...*/
1734 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1735 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1737 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1738 rc = LDAP_NO_MEMORY;
1739 goto done;
1743 /* see the note in ads_do_paged_search - we *must* disable referrals */
1744 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1746 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1747 search_attrs, 0, NULL, NULL,
1748 LDAP_NO_LIMIT,
1749 (LDAPMessage **)res);
1751 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1752 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1753 rc = 0;
1756 done:
1757 talloc_destroy(ctx);
1758 /* if/when we decide to utf8-encode attrs, take out this next line */
1759 TALLOC_FREE(search_attrs);
1760 return ADS_ERROR(rc);
1763 * Do a general ADS search
1764 * @param ads connection to ads server
1765 * @param res ** which will contain results - free res* with ads_msgfree()
1766 * @param expr Search expression
1767 * @param attrs Attributes to retrieve
1768 * @return status of search
1770 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1771 const char *expr, const char **attrs)
1773 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1774 expr, attrs, res);
1778 * Do a search on a specific DistinguishedName
1779 * @param ads connection to ads server
1780 * @param res ** which will contain results - free res* with ads_msgfree()
1781 * @param dn DistinguishedName to search
1782 * @param attrs Attributes to retrieve
1783 * @return status of search
1785 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1786 const char *dn, const char **attrs)
1788 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1789 attrs, res);
1793 * Free up memory from a ads_search
1794 * @param ads connection to ads server
1795 * @param msg Search results to free
1797 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1799 if (!msg) return;
1800 ldap_msgfree(msg);
1804 * Get a dn from search results
1805 * @param ads connection to ads server
1806 * @param msg Search result
1807 * @return dn string
1809 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1811 char *utf8_dn, *unix_dn;
1812 size_t converted_size;
1814 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1816 if (!utf8_dn) {
1817 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1818 return NULL;
1821 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1822 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1823 utf8_dn ));
1824 return NULL;
1826 ldap_memfree(utf8_dn);
1827 return unix_dn;
1831 * Get the parent from a dn
1832 * @param dn the dn to return the parent from
1833 * @return parent dn string
1835 char *ads_parent_dn(const char *dn)
1837 char *p;
1839 if (dn == NULL) {
1840 return NULL;
1843 p = strchr(dn, ',');
1845 if (p == NULL) {
1846 return NULL;
1849 return p+1;
1853 * Find a machine account given a hostname
1854 * @param ads connection to ads server
1855 * @param res ** which will contain results - free res* with ads_msgfree()
1856 * @param host Hostname to search for
1857 * @return status of search
1859 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1860 const char *machine)
1862 ADS_STATUS status;
1863 char *expr;
1864 const char *attrs[] = {
1865 /* This is how Windows checks for machine accounts */
1866 "objectClass",
1867 "SamAccountName",
1868 "userAccountControl",
1869 "DnsHostName",
1870 "ServicePrincipalName",
1871 "userPrincipalName",
1873 /* Additional attributes Samba checks */
1874 "msDS-AdditionalDnsHostName",
1875 "msDS-SupportedEncryptionTypes",
1876 "nTSecurityDescriptor",
1877 "objectSid",
1879 NULL
1881 TALLOC_CTX *frame = talloc_stackframe();
1883 *res = NULL;
1885 /* the easiest way to find a machine account anywhere in the tree
1886 is to look for hostname$ */
1887 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1888 if (expr == NULL) {
1889 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1890 goto done;
1893 status = ads_search(ads, res, expr, attrs);
1894 if (ADS_ERR_OK(status)) {
1895 if (ads_count_replies(ads, *res) != 1) {
1896 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1900 done:
1901 TALLOC_FREE(frame);
1902 return status;
1906 * Initialize a list of mods to be used in a modify request
1907 * @param ctx An initialized TALLOC_CTX
1908 * @return allocated ADS_MODLIST
1910 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1912 #define ADS_MODLIST_ALLOC_SIZE 10
1913 LDAPMod **mods;
1915 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1916 /* -1 is safety to make sure we don't go over the end.
1917 need to reset it to NULL before doing ldap modify */
1918 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1920 return (ADS_MODLIST)mods;
1925 add an attribute to the list, with values list already constructed
1927 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1928 int mod_op, const char *name,
1929 const void *_invals)
1931 int curmod;
1932 LDAPMod **modlist = (LDAPMod **) *mods;
1933 struct berval **ber_values = NULL;
1934 char **char_values = NULL;
1936 if (!_invals) {
1937 mod_op = LDAP_MOD_DELETE;
1938 } else {
1939 if (mod_op & LDAP_MOD_BVALUES) {
1940 const struct berval **b;
1941 b = discard_const_p(const struct berval *, _invals);
1942 ber_values = ads_dup_values(ctx, b);
1943 } else {
1944 const char **c;
1945 c = discard_const_p(const char *, _invals);
1946 char_values = ads_push_strvals(ctx, c);
1950 /* find the first empty slot */
1951 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1952 curmod++);
1953 if (modlist[curmod] == (LDAPMod *) -1) {
1954 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1955 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1956 return ADS_ERROR(LDAP_NO_MEMORY);
1957 memset(&modlist[curmod], 0,
1958 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1959 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1960 *mods = (ADS_MODLIST)modlist;
1963 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1964 return ADS_ERROR(LDAP_NO_MEMORY);
1965 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1966 if (mod_op & LDAP_MOD_BVALUES) {
1967 modlist[curmod]->mod_bvalues = ber_values;
1968 } else if (mod_op & LDAP_MOD_DELETE) {
1969 modlist[curmod]->mod_values = NULL;
1970 } else {
1971 modlist[curmod]->mod_values = char_values;
1974 modlist[curmod]->mod_op = mod_op;
1975 return ADS_ERROR(LDAP_SUCCESS);
1979 * Add a single string value to a mod list
1980 * @param ctx An initialized TALLOC_CTX
1981 * @param mods An initialized ADS_MODLIST
1982 * @param name The attribute name to add
1983 * @param val The value to add - NULL means DELETE
1984 * @return ADS STATUS indicating success of add
1986 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1987 const char *name, const char *val)
1989 const char *values[2];
1991 values[0] = val;
1992 values[1] = NULL;
1994 if (!val)
1995 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1996 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
2000 * Add an array of string values to a mod list
2001 * @param ctx An initialized TALLOC_CTX
2002 * @param mods An initialized ADS_MODLIST
2003 * @param name The attribute name to add
2004 * @param vals The array of string values to add - NULL means DELETE
2005 * @return ADS STATUS indicating success of add
2007 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2008 const char *name, const char **vals)
2010 if (!vals)
2011 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
2012 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
2013 name, (const void **) vals);
2017 * Add a single ber-encoded value to a mod list
2018 * @param ctx An initialized TALLOC_CTX
2019 * @param mods An initialized ADS_MODLIST
2020 * @param name The attribute name to add
2021 * @param val The value to add - NULL means DELETE
2022 * @return ADS STATUS indicating success of add
2024 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2025 const char *name, const struct berval *val)
2027 const struct berval *values[2];
2029 values[0] = val;
2030 values[1] = NULL;
2031 if (!val)
2032 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
2033 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
2034 name, (const void *) values);
2037 static void ads_print_error(int ret, LDAP *ld)
2039 if (ret != 0) {
2040 char *ld_error = NULL;
2041 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
2042 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
2043 ret,
2044 ldap_err2string(ret),
2045 ld_error);
2046 SAFE_FREE(ld_error);
2051 * Perform an ldap modify
2052 * @param ads connection to ads server
2053 * @param mod_dn DistinguishedName to modify
2054 * @param mods list of modifications to perform
2055 * @return status of modify
2057 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
2059 int ret,i;
2060 char *utf8_dn = NULL;
2061 size_t converted_size;
2063 this control is needed to modify that contains a currently
2064 non-existent attribute (but allowable for the object) to run
2066 LDAPControl PermitModify = {
2067 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
2068 {0, NULL},
2069 (char) 1};
2070 LDAPControl *controls[2];
2072 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
2074 controls[0] = &PermitModify;
2075 controls[1] = NULL;
2077 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
2078 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2081 /* find the end of the list, marked by NULL or -1 */
2082 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2083 /* make sure the end of the list is NULL */
2084 mods[i] = NULL;
2085 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
2086 (LDAPMod **) mods, controls, NULL);
2087 ads_print_error(ret, ads->ldap.ld);
2088 TALLOC_FREE(utf8_dn);
2089 return ADS_ERROR(ret);
2093 * Perform an ldap add
2094 * @param ads connection to ads server
2095 * @param new_dn DistinguishedName to add
2096 * @param mods list of attributes and values for DN
2097 * @return status of add
2099 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
2101 int ret, i;
2102 char *utf8_dn = NULL;
2103 size_t converted_size;
2105 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
2107 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
2108 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2109 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2112 /* find the end of the list, marked by NULL or -1 */
2113 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2114 /* make sure the end of the list is NULL */
2115 mods[i] = NULL;
2117 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
2118 ads_print_error(ret, ads->ldap.ld);
2119 TALLOC_FREE(utf8_dn);
2120 return ADS_ERROR(ret);
2124 * Delete a DistinguishedName
2125 * @param ads connection to ads server
2126 * @param new_dn DistinguishedName to delete
2127 * @return status of delete
2129 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
2131 int ret;
2132 char *utf8_dn = NULL;
2133 size_t converted_size;
2134 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
2135 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2136 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2139 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
2141 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
2142 ads_print_error(ret, ads->ldap.ld);
2143 TALLOC_FREE(utf8_dn);
2144 return ADS_ERROR(ret);
2148 * Build an org unit string
2149 * if org unit is Computers or blank then assume a container, otherwise
2150 * assume a / separated list of organisational units.
2151 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2152 * @param ads connection to ads server
2153 * @param org_unit Organizational unit
2154 * @return org unit string - caller must free
2156 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
2158 ADS_STATUS status;
2159 char *ret = NULL;
2160 char *dn = NULL;
2162 if (!org_unit || !*org_unit) {
2164 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
2166 /* samba4 might not yet respond to a wellknownobject-query */
2167 return ret ? ret : SMB_STRDUP("cn=Computers");
2170 if (strequal(org_unit, "Computers")) {
2171 return SMB_STRDUP("cn=Computers");
2174 /* jmcd: removed "\\" from the separation chars, because it is
2175 needed as an escape for chars like '#' which are valid in an
2176 OU name */
2177 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
2178 if (!ADS_ERR_OK(status)) {
2179 return NULL;
2182 return dn;
2186 * Get a org unit string for a well-known GUID
2187 * @param ads connection to ads server
2188 * @param wknguid Well known GUID
2189 * @return org unit string - caller must free
2191 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2193 ADS_STATUS status;
2194 LDAPMessage *res = NULL;
2195 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2196 **bind_dn_exp = NULL;
2197 const char *attrs[] = {"distinguishedName", NULL};
2198 int new_ln, wkn_ln, bind_ln, i;
2200 if (wknguid == NULL) {
2201 return NULL;
2204 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2205 DEBUG(1, ("asprintf failed!\n"));
2206 return NULL;
2209 status = ads_search_dn(ads, &res, base, attrs);
2210 if (!ADS_ERR_OK(status)) {
2211 DEBUG(1,("Failed while searching for: %s\n", base));
2212 goto out;
2215 if (ads_count_replies(ads, res) != 1) {
2216 goto out;
2219 /* substitute the bind-path from the well-known-guid-search result */
2220 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2221 if (!wkn_dn) {
2222 goto out;
2225 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2226 if (!wkn_dn_exp) {
2227 goto out;
2230 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2231 if (!bind_dn_exp) {
2232 goto out;
2235 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2237 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2240 new_ln = wkn_ln - bind_ln;
2242 ret = SMB_STRDUP(wkn_dn_exp[0]);
2243 if (!ret) {
2244 goto out;
2247 for (i=1; i < new_ln; i++) {
2248 char *s = NULL;
2250 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2251 SAFE_FREE(ret);
2252 goto out;
2255 SAFE_FREE(ret);
2256 ret = SMB_STRDUP(s);
2257 free(s);
2258 if (!ret) {
2259 goto out;
2263 out:
2264 SAFE_FREE(base);
2265 ads_msgfree(ads, res);
2266 TALLOC_FREE(wkn_dn);
2267 if (wkn_dn_exp) {
2268 ldap_value_free(wkn_dn_exp);
2270 if (bind_dn_exp) {
2271 ldap_value_free(bind_dn_exp);
2274 return ret;
2278 * Adds (appends) an item to an attribute array, rather then
2279 * replacing the whole list
2280 * @param ctx An initialized TALLOC_CTX
2281 * @param mods An initialized ADS_MODLIST
2282 * @param name name of the ldap attribute to append to
2283 * @param vals an array of values to add
2284 * @return status of addition
2287 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2288 const char *name, const char **vals)
2290 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2291 (const void *) vals);
2295 * Determines the an account's current KVNO via an LDAP lookup
2296 * @param ads An initialized ADS_STRUCT
2297 * @param account_name the NT samaccountname.
2298 * @return the kvno for the account, or -1 in case of a failure.
2301 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
2303 LDAPMessage *res = NULL;
2304 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
2305 char *filter;
2306 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
2307 char *dn_string = NULL;
2308 ADS_STATUS ret;
2310 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
2311 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
2312 return kvno;
2314 ret = ads_search(ads, &res, filter, attrs);
2315 SAFE_FREE(filter);
2316 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
2317 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
2318 ads_msgfree(ads, res);
2319 return kvno;
2322 dn_string = ads_get_dn(ads, talloc_tos(), res);
2323 if (!dn_string) {
2324 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2325 ads_msgfree(ads, res);
2326 return kvno;
2328 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
2329 TALLOC_FREE(dn_string);
2331 /* ---------------------------------------------------------
2332 * 0 is returned as a default KVNO from this point on...
2333 * This is done because Windows 2000 does not support key
2334 * version numbers. Chances are that a failure in the next
2335 * step is simply due to Windows 2000 being used for a
2336 * domain controller. */
2337 kvno = 0;
2339 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2340 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2341 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2342 ads_msgfree(ads, res);
2343 return kvno;
2346 /* Success */
2347 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2348 ads_msgfree(ads, res);
2349 return kvno;
2353 * Determines the computer account's current KVNO via an LDAP lookup
2354 * @param ads An initialized ADS_STRUCT
2355 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2356 * @return the kvno for the computer account, or -1 in case of a failure.
2359 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2361 char *computer_account = NULL;
2362 uint32_t kvno = -1;
2364 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2365 return kvno;
2368 kvno = ads_get_kvno(ads, computer_account);
2369 free(computer_account);
2371 return kvno;
2375 * This clears out all registered spn's for a given hostname
2376 * @param ads An initialized ADS_STRUCT
2377 * @param machine_name the NetBIOS name of the computer.
2378 * @return 0 upon success, non-zero otherwise.
2381 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2383 TALLOC_CTX *ctx;
2384 LDAPMessage *res = NULL;
2385 ADS_MODLIST mods;
2386 const char *servicePrincipalName[1] = {NULL};
2387 ADS_STATUS ret;
2388 char *dn_string = NULL;
2390 ret = ads_find_machine_acct(ads, &res, machine_name);
2391 if (!ADS_ERR_OK(ret)) {
2392 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2393 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2394 ads_msgfree(ads, res);
2395 return ret;
2398 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2399 ctx = talloc_init("ads_clear_service_principal_names");
2400 if (!ctx) {
2401 ads_msgfree(ads, res);
2402 return ADS_ERROR(LDAP_NO_MEMORY);
2405 if (!(mods = ads_init_mods(ctx))) {
2406 talloc_destroy(ctx);
2407 ads_msgfree(ads, res);
2408 return ADS_ERROR(LDAP_NO_MEMORY);
2410 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2411 if (!ADS_ERR_OK(ret)) {
2412 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2413 ads_msgfree(ads, res);
2414 talloc_destroy(ctx);
2415 return ret;
2417 dn_string = ads_get_dn(ads, talloc_tos(), res);
2418 if (!dn_string) {
2419 talloc_destroy(ctx);
2420 ads_msgfree(ads, res);
2421 return ADS_ERROR(LDAP_NO_MEMORY);
2423 ret = ads_gen_mod(ads, dn_string, mods);
2424 TALLOC_FREE(dn_string);
2425 if (!ADS_ERR_OK(ret)) {
2426 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2427 machine_name));
2428 ads_msgfree(ads, res);
2429 talloc_destroy(ctx);
2430 return ret;
2433 ads_msgfree(ads, res);
2434 talloc_destroy(ctx);
2435 return ret;
2439 * @brief Search for an element in a string array.
2441 * @param[in] el_array The string array to search.
2443 * @param[in] num_el The number of elements in the string array.
2445 * @param[in] el The string to search.
2447 * @return True if found, false if not.
2449 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2451 size_t i;
2453 if (el_array == NULL || num_el == 0 || el == NULL) {
2454 return false;
2457 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2458 int cmp;
2460 cmp = strcasecmp_m(el_array[i], el);
2461 if (cmp == 0) {
2462 return true;
2466 return false;
2470 * @brief This gets the service principal names of an existing computer account.
2472 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2474 * @param[in] ads The ADS context to use.
2476 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2477 * identify the computer account.
2479 * @param[in] spn_array A pointer to store the array for SPNs.
2481 * @param[in] num_spns The number of principals stored in the array.
2483 * @return 0 on success, or a ADS error if a failure occurred.
2485 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2486 ADS_STRUCT *ads,
2487 const char *machine_name,
2488 char ***spn_array,
2489 size_t *num_spns)
2491 ADS_STATUS status;
2492 LDAPMessage *res = NULL;
2493 int count;
2495 status = ads_find_machine_acct(ads,
2496 &res,
2497 machine_name);
2498 if (!ADS_ERR_OK(status)) {
2499 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2500 machine_name));
2501 return status;
2504 count = ads_count_replies(ads, res);
2505 if (count != 1) {
2506 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2507 goto done;
2510 *spn_array = ads_pull_strings(ads,
2511 mem_ctx,
2512 res,
2513 "servicePrincipalName",
2514 num_spns);
2515 if (*spn_array == NULL) {
2516 DEBUG(1, ("Host account for %s does not have service principal "
2517 "names.\n",
2518 machine_name));
2519 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2520 goto done;
2523 done:
2524 ads_msgfree(ads, res);
2526 return status;
2530 * This adds a service principal name to an existing computer account
2531 * (found by hostname) in AD.
2532 * @param ads An initialized ADS_STRUCT
2533 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2534 * @param spns An array or strings for the service principals to add,
2535 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2536 * @return 0 upon success, or non-zero if a failure occurs
2539 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2540 const char *machine_name,
2541 const char **spns)
2543 ADS_STATUS ret;
2544 TALLOC_CTX *ctx;
2545 LDAPMessage *res = NULL;
2546 ADS_MODLIST mods;
2547 char *dn_string = NULL;
2548 const char **servicePrincipalName = spns;
2550 ret = ads_find_machine_acct(ads, &res, machine_name);
2551 if (!ADS_ERR_OK(ret)) {
2552 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2553 machine_name));
2554 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2555 ads_msgfree(ads, res);
2556 return ret;
2559 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2560 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2561 ads_msgfree(ads, res);
2562 return ADS_ERROR(LDAP_NO_MEMORY);
2565 DEBUG(5,("ads_add_service_principal_name: INFO: "
2566 "Adding %s to host %s\n",
2567 spns[0] ? "N/A" : spns[0], machine_name));
2570 DEBUG(5,("ads_add_service_principal_name: INFO: "
2571 "Adding %s to host %s\n",
2572 spns[1] ? "N/A" : spns[1], machine_name));
2574 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2575 ret = ADS_ERROR(LDAP_NO_MEMORY);
2576 goto out;
2579 ret = ads_add_strlist(ctx,
2580 &mods,
2581 "servicePrincipalName",
2582 servicePrincipalName);
2583 if (!ADS_ERR_OK(ret)) {
2584 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2585 goto out;
2588 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2589 ret = ADS_ERROR(LDAP_NO_MEMORY);
2590 goto out;
2593 ret = ads_gen_mod(ads, dn_string, mods);
2594 if (!ADS_ERR_OK(ret)) {
2595 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2596 goto out;
2599 out:
2600 TALLOC_FREE( ctx );
2601 ads_msgfree(ads, res);
2602 return ret;
2605 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2606 LDAPMessage *msg)
2608 uint32_t acct_ctrl = 0;
2609 bool ok;
2611 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2612 if (!ok) {
2613 return 0;
2616 return acct_ctrl;
2619 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2620 LDAPMessage *msg,
2621 const struct berval *machine_pw_val)
2623 ADS_MODLIST mods;
2624 ADS_STATUS ret;
2625 TALLOC_CTX *frame = talloc_stackframe();
2626 uint32_t acct_control;
2627 char *control_str = NULL;
2628 const char *attrs[] = {
2629 "objectSid",
2630 NULL
2632 LDAPMessage *res = NULL;
2633 char *dn = NULL;
2635 dn = ads_get_dn(ads, frame, msg);
2636 if (dn == NULL) {
2637 ret = ADS_ERROR(LDAP_NO_MEMORY);
2638 goto done;
2641 acct_control = ads_get_acct_ctrl(ads, msg);
2642 if (acct_control == 0) {
2643 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2644 goto done;
2648 * Changing the password, disables the account. So we need to change the
2649 * userAccountControl flags to enable it again.
2651 mods = ads_init_mods(frame);
2652 if (mods == NULL) {
2653 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2654 goto done;
2657 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2659 ret = ads_gen_mod(ads, dn, mods);
2660 if (!ADS_ERR_OK(ret)) {
2661 goto done;
2663 TALLOC_FREE(mods);
2666 * To activate the account, we need to disable and enable it.
2668 acct_control |= UF_ACCOUNTDISABLE;
2670 control_str = talloc_asprintf(frame, "%u", acct_control);
2671 if (control_str == NULL) {
2672 ret = ADS_ERROR(LDAP_NO_MEMORY);
2673 goto done;
2676 mods = ads_init_mods(frame);
2677 if (mods == NULL) {
2678 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2679 goto done;
2682 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2684 ret = ads_gen_mod(ads, dn, mods);
2685 if (!ADS_ERR_OK(ret)) {
2686 goto done;
2688 TALLOC_FREE(mods);
2689 TALLOC_FREE(control_str);
2692 * Enable the account again.
2694 acct_control &= ~UF_ACCOUNTDISABLE;
2696 control_str = talloc_asprintf(frame, "%u", acct_control);
2697 if (control_str == NULL) {
2698 ret = ADS_ERROR(LDAP_NO_MEMORY);
2699 goto done;
2702 mods = ads_init_mods(frame);
2703 if (mods == NULL) {
2704 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2705 goto done;
2708 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2710 ret = ads_gen_mod(ads, dn, mods);
2711 if (!ADS_ERR_OK(ret)) {
2712 goto done;
2714 TALLOC_FREE(mods);
2715 TALLOC_FREE(control_str);
2717 ret = ads_search_dn(ads, &res, dn, attrs);
2718 ads_msgfree(ads, res);
2720 done:
2721 talloc_free(frame);
2723 return ret;
2727 * adds a machine account to the ADS server
2728 * @param ads An initialized ADS_STRUCT
2729 * @param machine_name - the NetBIOS machine name of this account.
2730 * @param account_type A number indicating the type of account to create
2731 * @param org_unit The LDAP path in which to place this account
2732 * @return 0 upon success, or non-zero otherwise
2735 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2736 const char *machine_name,
2737 const char *machine_password,
2738 const char *org_unit,
2739 uint32_t etype_list,
2740 const char *dns_domain_name)
2742 ADS_STATUS ret;
2743 char *samAccountName = NULL;
2744 char *controlstr = NULL;
2745 TALLOC_CTX *ctx = NULL;
2746 ADS_MODLIST mods;
2747 char *machine_escaped = NULL;
2748 char *dns_hostname = NULL;
2749 char *new_dn = NULL;
2750 char *utf8_pw = NULL;
2751 size_t utf8_pw_len = 0;
2752 char *utf16_pw = NULL;
2753 size_t utf16_pw_len = 0;
2754 struct berval machine_pw_val;
2755 bool ok;
2756 const char **spn_array = NULL;
2757 size_t num_spns = 0;
2758 const char *spn_prefix[] = {
2759 "HOST",
2760 "RestrictedKrbHost",
2762 size_t i;
2763 LDAPMessage *res = NULL;
2764 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2766 ctx = talloc_init("ads_add_machine_acct");
2767 if (ctx == NULL) {
2768 return ADS_ERROR(LDAP_NO_MEMORY);
2771 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2772 if (machine_escaped == NULL) {
2773 ret = ADS_ERROR(LDAP_NO_MEMORY);
2774 goto done;
2777 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2778 if (utf8_pw == NULL) {
2779 ret = ADS_ERROR(LDAP_NO_MEMORY);
2780 goto done;
2782 utf8_pw_len = strlen(utf8_pw);
2784 ok = convert_string_talloc(ctx,
2785 CH_UTF8, CH_UTF16MUNGED,
2786 utf8_pw, utf8_pw_len,
2787 (void *)&utf16_pw, &utf16_pw_len);
2788 if (!ok) {
2789 ret = ADS_ERROR(LDAP_NO_MEMORY);
2790 goto done;
2793 machine_pw_val = (struct berval) {
2794 .bv_val = utf16_pw,
2795 .bv_len = utf16_pw_len,
2798 /* Check if the machine account already exists. */
2799 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2800 if (ADS_ERR_OK(ret)) {
2801 /* Change the machine account password */
2802 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2803 ads_msgfree(ads, res);
2805 goto done;
2807 ads_msgfree(ads, res);
2809 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2810 if (new_dn == NULL) {
2811 ret = ADS_ERROR(LDAP_NO_MEMORY);
2812 goto done;
2815 /* Create machine account */
2817 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2818 if (samAccountName == NULL) {
2819 ret = ADS_ERROR(LDAP_NO_MEMORY);
2820 goto done;
2823 dns_hostname = talloc_asprintf(ctx,
2824 "%s.%s",
2825 machine_name,
2826 dns_domain_name);
2827 if (dns_hostname == NULL) {
2828 ret = ADS_ERROR(LDAP_NO_MEMORY);
2829 goto done;
2832 /* Add dns_hostname SPNs */
2833 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2834 char *spn = talloc_asprintf(ctx,
2835 "%s/%s",
2836 spn_prefix[i],
2837 dns_hostname);
2838 if (spn == NULL) {
2839 ret = ADS_ERROR(LDAP_NO_MEMORY);
2840 goto done;
2843 ok = add_string_to_array(ctx,
2844 spn,
2845 &spn_array,
2846 &num_spns);
2847 if (!ok) {
2848 ret = ADS_ERROR(LDAP_NO_MEMORY);
2849 goto done;
2853 /* Add machine_name SPNs */
2854 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2855 char *spn = talloc_asprintf(ctx,
2856 "%s/%s",
2857 spn_prefix[i],
2858 machine_name);
2859 if (spn == NULL) {
2860 ret = ADS_ERROR(LDAP_NO_MEMORY);
2861 goto done;
2864 ok = add_string_to_array(ctx,
2865 spn,
2866 &spn_array,
2867 &num_spns);
2868 if (!ok) {
2869 ret = ADS_ERROR(LDAP_NO_MEMORY);
2870 goto done;
2874 /* Make sure to NULL terminate the array */
2875 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2876 if (spn_array == NULL) {
2877 ret = ADS_ERROR(LDAP_NO_MEMORY);
2878 goto done;
2880 spn_array[num_spns] = NULL;
2882 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2883 if (controlstr == NULL) {
2884 ret = ADS_ERROR(LDAP_NO_MEMORY);
2885 goto done;
2888 mods = ads_init_mods(ctx);
2889 if (mods == NULL) {
2890 ret = ADS_ERROR(LDAP_NO_MEMORY);
2891 goto done;
2894 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2895 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2896 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2897 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2898 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2899 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2901 ret = ads_gen_add(ads, new_dn, mods);
2903 done:
2904 SAFE_FREE(machine_escaped);
2905 talloc_destroy(ctx);
2907 return ret;
2911 * move a machine account to another OU on the ADS server
2912 * @param ads - An initialized ADS_STRUCT
2913 * @param machine_name - the NetBIOS machine name of this account.
2914 * @param org_unit - The LDAP path in which to place this account
2915 * @param moved - whether we moved the machine account (optional)
2916 * @return 0 upon success, or non-zero otherwise
2919 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2920 const char *org_unit, bool *moved)
2922 ADS_STATUS rc;
2923 int ldap_status;
2924 LDAPMessage *res = NULL;
2925 char *filter = NULL;
2926 char *computer_dn = NULL;
2927 char *parent_dn;
2928 char *computer_rdn = NULL;
2929 bool need_move = False;
2931 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2932 rc = ADS_ERROR(LDAP_NO_MEMORY);
2933 goto done;
2936 /* Find pre-existing machine */
2937 rc = ads_search(ads, &res, filter, NULL);
2938 if (!ADS_ERR_OK(rc)) {
2939 goto done;
2942 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2943 if (!computer_dn) {
2944 rc = ADS_ERROR(LDAP_NO_MEMORY);
2945 goto done;
2948 parent_dn = ads_parent_dn(computer_dn);
2949 if (strequal(parent_dn, org_unit)) {
2950 goto done;
2953 need_move = True;
2955 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2956 rc = ADS_ERROR(LDAP_NO_MEMORY);
2957 goto done;
2960 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2961 org_unit, 1, NULL, NULL);
2962 rc = ADS_ERROR(ldap_status);
2964 done:
2965 ads_msgfree(ads, res);
2966 SAFE_FREE(filter);
2967 TALLOC_FREE(computer_dn);
2968 SAFE_FREE(computer_rdn);
2970 if (!ADS_ERR_OK(rc)) {
2971 need_move = False;
2974 if (moved) {
2975 *moved = need_move;
2978 return rc;
2982 dump a binary result from ldap
2984 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2986 size_t i;
2987 for (i=0; values[i]; i++) {
2988 ber_len_t j;
2989 printf("%s: ", field);
2990 for (j=0; j<values[i]->bv_len; j++) {
2991 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2993 printf("\n");
2997 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2999 int i;
3000 for (i=0; values[i]; i++) {
3001 NTSTATUS status;
3002 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
3003 struct GUID guid;
3005 status = GUID_from_ndr_blob(&in, &guid);
3006 if (NT_STATUS_IS_OK(status)) {
3007 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
3008 } else {
3009 printf("%s: INVALID GUID\n", field);
3015 dump a sid result from ldap
3017 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
3019 int i;
3020 for (i=0; values[i]; i++) {
3021 ssize_t ret;
3022 struct dom_sid sid;
3023 struct dom_sid_buf tmp;
3024 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3025 values[i]->bv_len, &sid);
3026 if (ret == -1) {
3027 return;
3029 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
3034 dump ntSecurityDescriptor
3036 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
3038 TALLOC_CTX *frame = talloc_stackframe();
3039 struct security_descriptor *psd;
3040 NTSTATUS status;
3042 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
3043 values[0]->bv_len, &psd);
3044 if (!NT_STATUS_IS_OK(status)) {
3045 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3046 nt_errstr(status)));
3047 TALLOC_FREE(frame);
3048 return;
3051 if (psd) {
3052 ads_disp_sd(ads, talloc_tos(), psd);
3055 TALLOC_FREE(frame);
3059 dump a string result from ldap
3061 static void dump_string(const char *field, char **values)
3063 int i;
3064 for (i=0; values[i]; i++) {
3065 printf("%s: %s\n", field, values[i]);
3070 dump a field from LDAP on stdout
3071 used for debugging
3074 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
3076 const struct {
3077 const char *name;
3078 bool string;
3079 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
3080 } handlers[] = {
3081 {"objectGUID", False, dump_guid},
3082 {"netbootGUID", False, dump_guid},
3083 {"nTSecurityDescriptor", False, dump_sd},
3084 {"dnsRecord", False, dump_binary},
3085 {"objectSid", False, dump_sid},
3086 {"securityIdentifier", False, dump_sid},
3087 {"tokenGroups", False, dump_sid},
3088 {"tokenGroupsNoGCAcceptable", False, dump_sid},
3089 {"tokengroupsGlobalandUniversal", False, dump_sid},
3090 {"mS-DS-CreatorSID", False, dump_sid},
3091 {"msExchMailboxGuid", False, dump_guid},
3092 {"msDS-TrustForestTrustInfo", False, dump_binary},
3093 {NULL, True, NULL}
3095 int i;
3097 if (!field) { /* must be end of an entry */
3098 printf("\n");
3099 return False;
3102 for (i=0; handlers[i].name; i++) {
3103 if (strcasecmp_m(handlers[i].name, field) == 0) {
3104 if (!values) /* first time, indicate string or not */
3105 return handlers[i].string;
3106 handlers[i].handler(ads, field, (struct berval **) values);
3107 break;
3110 if (!handlers[i].name) {
3111 if (!values) /* first time, indicate string conversion */
3112 return True;
3113 dump_string(field, (char **)values);
3115 return False;
3119 * Dump a result from LDAP on stdout
3120 * used for debugging
3121 * @param ads connection to ads server
3122 * @param res Results to dump
3125 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
3127 ads_process_results(ads, res, ads_dump_field, NULL);
3131 * Walk through results, calling a function for each entry found.
3132 * The function receives a field name, a berval * array of values,
3133 * and a data area passed through from the start. The function is
3134 * called once with null for field and values at the end of each
3135 * entry.
3136 * @param ads connection to ads server
3137 * @param res Results to process
3138 * @param fn Function for processing each result
3139 * @param data_area user-defined area to pass to function
3141 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
3142 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
3143 void *data_area)
3145 LDAPMessage *msg;
3146 TALLOC_CTX *ctx;
3147 size_t converted_size;
3149 if (!(ctx = talloc_init("ads_process_results")))
3150 return;
3152 for (msg = ads_first_entry(ads, res); msg;
3153 msg = ads_next_entry(ads, msg)) {
3154 char *utf8_field;
3155 BerElement *b;
3157 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
3158 (LDAPMessage *)msg,&b);
3159 utf8_field;
3160 utf8_field=ldap_next_attribute(ads->ldap.ld,
3161 (LDAPMessage *)msg,b)) {
3162 struct berval **ber_vals;
3163 char **str_vals;
3164 char **utf8_vals;
3165 char *field;
3166 bool string;
3168 if (!pull_utf8_talloc(ctx, &field, utf8_field,
3169 &converted_size))
3171 DEBUG(0,("ads_process_results: "
3172 "pull_utf8_talloc failed: %s\n",
3173 strerror(errno)));
3176 string = fn(ads, field, NULL, data_area);
3178 if (string) {
3179 const char **p;
3181 utf8_vals = ldap_get_values(ads->ldap.ld,
3182 (LDAPMessage *)msg, field);
3183 p = discard_const_p(const char *, utf8_vals);
3184 str_vals = ads_pull_strvals(ctx, p);
3185 fn(ads, field, (void **) str_vals, data_area);
3186 ldap_value_free(utf8_vals);
3187 } else {
3188 ber_vals = ldap_get_values_len(ads->ldap.ld,
3189 (LDAPMessage *)msg, field);
3190 fn(ads, field, (void **) ber_vals, data_area);
3192 ldap_value_free_len(ber_vals);
3194 ldap_memfree(utf8_field);
3196 ber_free(b, 0);
3197 talloc_free_children(ctx);
3198 fn(ads, NULL, NULL, data_area); /* completed an entry */
3201 talloc_destroy(ctx);
3205 * count how many replies are in a LDAPMessage
3206 * @param ads connection to ads server
3207 * @param res Results to count
3208 * @return number of replies
3210 int ads_count_replies(ADS_STRUCT *ads, void *res)
3212 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3216 * pull the first entry from a ADS result
3217 * @param ads connection to ads server
3218 * @param res Results of search
3219 * @return first entry from result
3221 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3223 return ldap_first_entry(ads->ldap.ld, res);
3227 * pull the next entry from a ADS result
3228 * @param ads connection to ads server
3229 * @param res Results of search
3230 * @return next entry from result
3232 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3234 return ldap_next_entry(ads->ldap.ld, res);
3238 * pull the first message from a ADS result
3239 * @param ads connection to ads server
3240 * @param res Results of search
3241 * @return first message from result
3243 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3245 return ldap_first_message(ads->ldap.ld, res);
3249 * pull the next message from a ADS result
3250 * @param ads connection to ads server
3251 * @param res Results of search
3252 * @return next message from result
3254 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3256 return ldap_next_message(ads->ldap.ld, res);
3260 * pull a single string from a ADS result
3261 * @param ads connection to ads server
3262 * @param mem_ctx TALLOC_CTX to use for allocating result string
3263 * @param msg Results of search
3264 * @param field Attribute to retrieve
3265 * @return Result string in talloc context
3267 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3268 const char *field)
3270 char **values;
3271 char *ret = NULL;
3272 char *ux_string;
3273 size_t converted_size;
3275 values = ldap_get_values(ads->ldap.ld, msg, field);
3276 if (!values)
3277 return NULL;
3279 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3280 &converted_size))
3282 ret = ux_string;
3284 ldap_value_free(values);
3285 return ret;
3289 * pull an array of strings from a ADS result
3290 * @param ads connection to ads server
3291 * @param mem_ctx TALLOC_CTX to use for allocating result string
3292 * @param msg Results of search
3293 * @param field Attribute to retrieve
3294 * @return Result strings in talloc context
3296 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3297 LDAPMessage *msg, const char *field,
3298 size_t *num_values)
3300 char **values;
3301 char **ret = NULL;
3302 size_t i, converted_size;
3304 values = ldap_get_values(ads->ldap.ld, msg, field);
3305 if (!values)
3306 return NULL;
3308 *num_values = ldap_count_values(values);
3310 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3311 if (!ret) {
3312 ldap_value_free(values);
3313 return NULL;
3316 for (i=0;i<*num_values;i++) {
3317 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3318 &converted_size))
3320 ldap_value_free(values);
3321 return NULL;
3324 ret[i] = NULL;
3326 ldap_value_free(values);
3327 return ret;
3331 * pull an array of strings from a ADS result
3332 * (handle large multivalue attributes with range retrieval)
3333 * @param ads connection to ads server
3334 * @param mem_ctx TALLOC_CTX to use for allocating result string
3335 * @param msg Results of search
3336 * @param field Attribute to retrieve
3337 * @param current_strings strings returned by a previous call to this function
3338 * @param next_attribute The next query should ask for this attribute
3339 * @param num_values How many values did we get this time?
3340 * @param more_values Are there more values to get?
3341 * @return Result strings in talloc context
3343 char **ads_pull_strings_range(ADS_STRUCT *ads,
3344 TALLOC_CTX *mem_ctx,
3345 LDAPMessage *msg, const char *field,
3346 char **current_strings,
3347 const char **next_attribute,
3348 size_t *num_strings,
3349 bool *more_strings)
3351 char *attr;
3352 char *expected_range_attrib, *range_attr = NULL;
3353 BerElement *ptr = NULL;
3354 char **strings;
3355 char **new_strings;
3356 size_t num_new_strings;
3357 unsigned long int range_start;
3358 unsigned long int range_end;
3360 /* we might have been given the whole lot anyway */
3361 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3362 *more_strings = False;
3363 return strings;
3366 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3368 /* look for Range result */
3369 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3370 attr;
3371 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3372 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3373 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3374 range_attr = attr;
3375 break;
3377 ldap_memfree(attr);
3379 if (!range_attr) {
3380 ber_free(ptr, 0);
3381 /* nothing here - this field is just empty */
3382 *more_strings = False;
3383 return NULL;
3386 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3387 &range_start, &range_end) == 2) {
3388 *more_strings = True;
3389 } else {
3390 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3391 &range_start) == 1) {
3392 *more_strings = False;
3393 } else {
3394 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3395 range_attr));
3396 ldap_memfree(range_attr);
3397 *more_strings = False;
3398 return NULL;
3402 if ((*num_strings) != range_start) {
3403 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3404 " - aborting range retrieval\n",
3405 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3406 ldap_memfree(range_attr);
3407 *more_strings = False;
3408 return NULL;
3411 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3413 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3414 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3415 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3416 range_attr, (unsigned long int)range_end - range_start + 1,
3417 (unsigned long int)num_new_strings));
3418 ldap_memfree(range_attr);
3419 *more_strings = False;
3420 return NULL;
3423 strings = talloc_realloc(mem_ctx, current_strings, char *,
3424 *num_strings + num_new_strings);
3426 if (strings == NULL) {
3427 ldap_memfree(range_attr);
3428 *more_strings = False;
3429 return NULL;
3432 if (new_strings && num_new_strings) {
3433 memcpy(&strings[*num_strings], new_strings,
3434 sizeof(*new_strings) * num_new_strings);
3437 (*num_strings) += num_new_strings;
3439 if (*more_strings) {
3440 *next_attribute = talloc_asprintf(mem_ctx,
3441 "%s;range=%d-*",
3442 field,
3443 (int)*num_strings);
3445 if (!*next_attribute) {
3446 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3447 ldap_memfree(range_attr);
3448 *more_strings = False;
3449 return NULL;
3453 ldap_memfree(range_attr);
3455 return strings;
3459 * pull a single uint32_t from a ADS result
3460 * @param ads connection to ads server
3461 * @param msg Results of search
3462 * @param field Attribute to retrieve
3463 * @param v Pointer to int to store result
3464 * @return boolean indicating success
3466 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3467 uint32_t *v)
3469 char **values;
3471 values = ldap_get_values(ads->ldap.ld, msg, field);
3472 if (!values)
3473 return False;
3474 if (!values[0]) {
3475 ldap_value_free(values);
3476 return False;
3479 *v = atoi(values[0]);
3480 ldap_value_free(values);
3481 return True;
3485 * pull a single objectGUID from an ADS result
3486 * @param ads connection to ADS server
3487 * @param msg results of search
3488 * @param guid 37-byte area to receive text guid
3489 * @return boolean indicating success
3491 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3493 DATA_BLOB blob;
3494 NTSTATUS status;
3496 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3497 &blob)) {
3498 return false;
3501 status = GUID_from_ndr_blob(&blob, guid);
3502 talloc_free(blob.data);
3503 return NT_STATUS_IS_OK(status);
3508 * pull a single struct dom_sid from a ADS result
3509 * @param ads connection to ads server
3510 * @param msg Results of search
3511 * @param field Attribute to retrieve
3512 * @param sid Pointer to sid to store result
3513 * @return boolean indicating success
3515 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3516 struct dom_sid *sid)
3518 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3522 * pull an array of struct dom_sids from a ADS result
3523 * @param ads connection to ads server
3524 * @param mem_ctx TALLOC_CTX for allocating sid array
3525 * @param msg Results of search
3526 * @param field Attribute to retrieve
3527 * @param sids pointer to sid array to allocate
3528 * @return the count of SIDs pulled
3530 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3531 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3533 struct berval **values;
3534 int count, i;
3536 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3538 if (!values)
3539 return 0;
3541 for (i=0; values[i]; i++)
3542 /* nop */ ;
3544 if (i) {
3545 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3546 if (!(*sids)) {
3547 ldap_value_free_len(values);
3548 return 0;
3550 } else {
3551 (*sids) = NULL;
3554 count = 0;
3555 for (i=0; values[i]; i++) {
3556 ssize_t ret;
3557 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3558 values[i]->bv_len, &(*sids)[count]);
3559 if (ret != -1) {
3560 struct dom_sid_buf buf;
3561 DBG_DEBUG("pulling SID: %s\n",
3562 dom_sid_str_buf(&(*sids)[count], &buf));
3563 count++;
3567 ldap_value_free_len(values);
3568 return count;
3572 * pull a struct security_descriptor from a ADS result
3573 * @param ads connection to ads server
3574 * @param mem_ctx TALLOC_CTX for allocating sid array
3575 * @param msg Results of search
3576 * @param field Attribute to retrieve
3577 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3578 * @return boolean indicating success
3580 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3581 LDAPMessage *msg, const char *field,
3582 struct security_descriptor **sd)
3584 struct berval **values;
3585 bool ret = true;
3587 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3589 if (!values) return false;
3591 if (values[0]) {
3592 NTSTATUS status;
3593 status = unmarshall_sec_desc(mem_ctx,
3594 (uint8_t *)values[0]->bv_val,
3595 values[0]->bv_len, sd);
3596 if (!NT_STATUS_IS_OK(status)) {
3597 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3598 nt_errstr(status)));
3599 ret = false;
3603 ldap_value_free_len(values);
3604 return ret;
3608 * in order to support usernames longer than 21 characters we need to
3609 * use both the sAMAccountName and the userPrincipalName attributes
3610 * It seems that not all users have the userPrincipalName attribute set
3612 * @param ads connection to ads server
3613 * @param mem_ctx TALLOC_CTX for allocating sid array
3614 * @param msg Results of search
3615 * @return the username
3617 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3618 LDAPMessage *msg)
3620 #if 0 /* JERRY */
3621 char *ret, *p;
3623 /* lookup_name() only works on the sAMAccountName to
3624 returning the username portion of userPrincipalName
3625 breaks winbindd_getpwnam() */
3627 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3628 if (ret && (p = strchr_m(ret, '@'))) {
3629 *p = 0;
3630 return ret;
3632 #endif
3633 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3638 * find the update serial number - this is the core of the ldap cache
3639 * @param ads connection to ads server
3640 * @param ads connection to ADS server
3641 * @param usn Pointer to retrieved update serial number
3642 * @return status of search
3644 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3646 const char *attrs[] = {"highestCommittedUSN", NULL};
3647 ADS_STATUS status;
3648 LDAPMessage *res;
3650 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3651 if (!ADS_ERR_OK(status))
3652 return status;
3654 if (ads_count_replies(ads, res) != 1) {
3655 ads_msgfree(ads, res);
3656 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3659 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3660 ads_msgfree(ads, res);
3661 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3664 ads_msgfree(ads, res);
3665 return ADS_SUCCESS;
3668 /* parse a ADS timestring - typical string is
3669 '20020917091222.0Z0' which means 09:12.22 17th September
3670 2002, timezone 0 */
3671 static time_t ads_parse_time(const char *str)
3673 struct tm tm;
3675 ZERO_STRUCT(tm);
3677 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3678 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3679 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3680 return 0;
3682 tm.tm_year -= 1900;
3683 tm.tm_mon -= 1;
3685 return timegm(&tm);
3688 /********************************************************************
3689 ********************************************************************/
3691 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3693 const char *attrs[] = {"currentTime", NULL};
3694 ADS_STATUS status;
3695 LDAPMessage *res;
3696 char *timestr;
3697 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3698 ADS_STRUCT *ads_s = ads;
3700 /* establish a new ldap tcp session if necessary */
3702 if ( !ads->ldap.ld ) {
3704 * ADS_STRUCT may be being reused after a
3705 * DC lookup, so ads->ldap.ss may already have a
3706 * good address. If not, re-initialize the passed-in
3707 * ADS_STRUCT with the given server.XXXX parameters.
3709 * Note that this doesn't depend on
3710 * ads->server.ldap_server != NULL,
3711 * as the case where ads->server.ldap_server==NULL and
3712 * ads->ldap.ss != zero_address is precisely the DC
3713 * lookup case where ads->ldap.ss was found by going
3714 * through ads_find_dc() again we want to avoid repeating.
3716 if (is_zero_addr(&ads->ldap.ss)) {
3717 ads_s = ads_init(tmp_ctx,
3718 ads->server.realm,
3719 ads->server.workgroup,
3720 ads->server.ldap_server,
3721 ADS_SASL_PLAIN );
3722 if (ads_s == NULL) {
3723 status = ADS_ERROR(LDAP_NO_MEMORY);
3724 goto done;
3729 * Reset ads->config.flags as it can contain the flags
3730 * returned by the previous CLDAP ping when reusing the struct.
3732 ads_s->config.flags = 0;
3734 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3735 status = ads_connect( ads_s );
3736 if ( !ADS_ERR_OK(status))
3737 goto done;
3740 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3741 if (!ADS_ERR_OK(status)) {
3742 goto done;
3745 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3746 if (!timestr) {
3747 ads_msgfree(ads_s, res);
3748 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3749 goto done;
3752 /* but save the time and offset in the original ADS_STRUCT */
3754 ads->config.current_time = ads_parse_time(timestr);
3756 if (ads->config.current_time != 0) {
3757 ads->config.time_offset = ads->config.current_time - time(NULL);
3758 DBG_INFO("server time offset is %d seconds\n",
3759 ads->config.time_offset);
3760 } else {
3761 ads->config.time_offset = 0;
3764 DBG_INFO("server time offset is %d seconds\n",
3765 ads->config.time_offset);
3767 ads_msgfree(ads, res);
3769 status = ADS_SUCCESS;
3771 done:
3772 TALLOC_FREE(tmp_ctx);
3774 return status;
3777 /********************************************************************
3778 ********************************************************************/
3780 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3782 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3783 const char *attrs[] = {"domainFunctionality", NULL};
3784 ADS_STATUS status;
3785 LDAPMessage *res;
3786 ADS_STRUCT *ads_s = ads;
3788 *val = DS_DOMAIN_FUNCTION_2000;
3790 /* establish a new ldap tcp session if necessary */
3792 if ( !ads->ldap.ld ) {
3794 * ADS_STRUCT may be being reused after a
3795 * DC lookup, so ads->ldap.ss may already have a
3796 * good address. If not, re-initialize the passed-in
3797 * ADS_STRUCT with the given server.XXXX parameters.
3799 * Note that this doesn't depend on
3800 * ads->server.ldap_server != NULL,
3801 * as the case where ads->server.ldap_server==NULL and
3802 * ads->ldap.ss != zero_address is precisely the DC
3803 * lookup case where ads->ldap.ss was found by going
3804 * through ads_find_dc() again we want to avoid repeating.
3806 if (is_zero_addr(&ads->ldap.ss)) {
3807 ads_s = ads_init(tmp_ctx,
3808 ads->server.realm,
3809 ads->server.workgroup,
3810 ads->server.ldap_server,
3811 ADS_SASL_PLAIN );
3812 if (ads_s == NULL ) {
3813 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3814 goto done;
3819 * Reset ads->config.flags as it can contain the flags
3820 * returned by the previous CLDAP ping when reusing the struct.
3822 ads_s->config.flags = 0;
3824 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3825 status = ads_connect( ads_s );
3826 if ( !ADS_ERR_OK(status))
3827 goto done;
3830 /* If the attribute does not exist assume it is a Windows 2000
3831 functional domain */
3833 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3834 if (!ADS_ERR_OK(status)) {
3835 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3836 status = ADS_SUCCESS;
3838 goto done;
3841 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3842 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3844 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3847 ads_msgfree(ads_s, res);
3849 done:
3850 TALLOC_FREE(tmp_ctx);
3852 return status;
3856 * find the domain sid for our domain
3857 * @param ads connection to ads server
3858 * @param sid Pointer to domain sid
3859 * @return status of search
3861 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3863 const char *attrs[] = {"objectSid", NULL};
3864 LDAPMessage *res;
3865 ADS_STATUS rc;
3867 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3868 attrs, &res);
3869 if (!ADS_ERR_OK(rc)) return rc;
3870 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3871 ads_msgfree(ads, res);
3872 return ADS_ERROR_SYSTEM(ENOENT);
3874 ads_msgfree(ads, res);
3876 return ADS_SUCCESS;
3880 * find our site name
3881 * @param ads connection to ads server
3882 * @param mem_ctx Pointer to talloc context
3883 * @param site_name Pointer to the sitename
3884 * @return status of search
3886 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3888 ADS_STATUS status;
3889 LDAPMessage *res;
3890 const char *dn, *service_name;
3891 const char *attrs[] = { "dsServiceName", NULL };
3893 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3894 if (!ADS_ERR_OK(status)) {
3895 return status;
3898 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3899 if (service_name == NULL) {
3900 ads_msgfree(ads, res);
3901 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3904 ads_msgfree(ads, res);
3906 /* go up three levels */
3907 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3908 if (dn == NULL) {
3909 return ADS_ERROR(LDAP_NO_MEMORY);
3912 *site_name = talloc_strdup(mem_ctx, dn);
3913 if (*site_name == NULL) {
3914 return ADS_ERROR(LDAP_NO_MEMORY);
3917 return status;
3919 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3924 * find the site dn where a machine resides
3925 * @param ads connection to ads server
3926 * @param mem_ctx Pointer to talloc context
3927 * @param computer_name name of the machine
3928 * @param site_name Pointer to the sitename
3929 * @return status of search
3931 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3933 ADS_STATUS status;
3934 LDAPMessage *res;
3935 const char *parent, *filter;
3936 char *config_context = NULL;
3937 char *dn;
3939 /* shortcut a query */
3940 if (strequal(computer_name, ads->config.ldap_server_name)) {
3941 return ads_site_dn(ads, mem_ctx, site_dn);
3944 status = ads_config_path(ads, mem_ctx, &config_context);
3945 if (!ADS_ERR_OK(status)) {
3946 return status;
3949 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3950 if (filter == NULL) {
3951 return ADS_ERROR(LDAP_NO_MEMORY);
3954 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3955 filter, NULL, &res);
3956 if (!ADS_ERR_OK(status)) {
3957 return status;
3960 if (ads_count_replies(ads, res) != 1) {
3961 ads_msgfree(ads, res);
3962 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3965 dn = ads_get_dn(ads, mem_ctx, res);
3966 if (dn == NULL) {
3967 ads_msgfree(ads, res);
3968 return ADS_ERROR(LDAP_NO_MEMORY);
3971 /* go up three levels */
3972 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3973 if (parent == NULL) {
3974 ads_msgfree(ads, res);
3975 TALLOC_FREE(dn);
3976 return ADS_ERROR(LDAP_NO_MEMORY);
3979 *site_dn = talloc_strdup(mem_ctx, parent);
3980 if (*site_dn == NULL) {
3981 ads_msgfree(ads, res);
3982 TALLOC_FREE(dn);
3983 return ADS_ERROR(LDAP_NO_MEMORY);
3986 TALLOC_FREE(dn);
3987 ads_msgfree(ads, res);
3989 return status;
3993 * get the upn suffixes for a domain
3994 * @param ads connection to ads server
3995 * @param mem_ctx Pointer to talloc context
3996 * @param suffixes Pointer to an array of suffixes
3997 * @param num_suffixes Pointer to the number of suffixes
3998 * @return status of search
4000 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
4002 ADS_STATUS status;
4003 LDAPMessage *res;
4004 const char *base;
4005 char *config_context = NULL;
4006 const char *attrs[] = { "uPNSuffixes", NULL };
4008 status = ads_config_path(ads, mem_ctx, &config_context);
4009 if (!ADS_ERR_OK(status)) {
4010 return status;
4013 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
4014 if (base == NULL) {
4015 return ADS_ERROR(LDAP_NO_MEMORY);
4018 status = ads_search_dn(ads, &res, base, attrs);
4019 if (!ADS_ERR_OK(status)) {
4020 return status;
4023 if (ads_count_replies(ads, res) != 1) {
4024 ads_msgfree(ads, res);
4025 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4028 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
4029 if ((*suffixes) == NULL) {
4030 ads_msgfree(ads, res);
4031 return ADS_ERROR(LDAP_NO_MEMORY);
4034 ads_msgfree(ads, res);
4036 return status;
4040 * get the joinable ous for a domain
4041 * @param ads connection to ads server
4042 * @param mem_ctx Pointer to talloc context
4043 * @param ous Pointer to an array of ous
4044 * @param num_ous Pointer to the number of ous
4045 * @return status of search
4047 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
4048 TALLOC_CTX *mem_ctx,
4049 char ***ous,
4050 size_t *num_ous)
4052 ADS_STATUS status;
4053 LDAPMessage *res = NULL;
4054 LDAPMessage *msg = NULL;
4055 const char *attrs[] = { "dn", NULL };
4056 int count = 0;
4058 status = ads_search(ads, &res,
4059 "(|(objectClass=domain)(objectclass=organizationalUnit))",
4060 attrs);
4061 if (!ADS_ERR_OK(status)) {
4062 return status;
4065 count = ads_count_replies(ads, res);
4066 if (count < 1) {
4067 ads_msgfree(ads, res);
4068 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4071 for (msg = ads_first_entry(ads, res); msg;
4072 msg = ads_next_entry(ads, msg)) {
4073 const char **p = discard_const_p(const char *, *ous);
4074 char *dn = NULL;
4076 dn = ads_get_dn(ads, talloc_tos(), msg);
4077 if (!dn) {
4078 ads_msgfree(ads, res);
4079 return ADS_ERROR(LDAP_NO_MEMORY);
4082 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
4083 TALLOC_FREE(dn);
4084 ads_msgfree(ads, res);
4085 return ADS_ERROR(LDAP_NO_MEMORY);
4088 TALLOC_FREE(dn);
4089 *ous = discard_const_p(char *, p);
4092 ads_msgfree(ads, res);
4094 return status;
4099 * pull a struct dom_sid from an extended dn string
4100 * @param mem_ctx TALLOC_CTX
4101 * @param extended_dn string
4102 * @param flags string type of extended_dn
4103 * @param sid pointer to a struct dom_sid
4104 * @return NT_STATUS_OK on success,
4105 * NT_INVALID_PARAMETER on error,
4106 * NT_STATUS_NOT_FOUND if no SID present
4108 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
4109 const char *extended_dn,
4110 enum ads_extended_dn_flags flags,
4111 struct dom_sid *sid)
4113 char *p, *q, *dn;
4115 if (!extended_dn) {
4116 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4119 /* otherwise extended_dn gets stripped off */
4120 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
4121 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4124 * ADS_EXTENDED_DN_HEX_STRING:
4125 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4127 * ADS_EXTENDED_DN_STRING (only with w2k3):
4128 * <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
4130 * Object with no SID, such as an Exchange Public Folder
4131 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4134 p = strchr(dn, ';');
4135 if (!p) {
4136 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4139 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
4140 DEBUG(5,("No SID present in extended dn\n"));
4141 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
4144 p += strlen(";<SID=");
4146 q = strchr(p, '>');
4147 if (!q) {
4148 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4151 *q = '\0';
4153 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
4155 switch (flags) {
4157 case ADS_EXTENDED_DN_STRING:
4158 if (!string_to_sid(sid, p)) {
4159 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4161 break;
4162 case ADS_EXTENDED_DN_HEX_STRING: {
4163 ssize_t ret;
4164 fstring buf;
4165 size_t buf_len;
4167 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
4168 if (buf_len == 0) {
4169 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4172 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
4173 if (ret == -1) {
4174 DEBUG(10,("failed to parse sid\n"));
4175 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4177 break;
4179 default:
4180 DEBUG(10,("unknown extended dn format\n"));
4181 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4184 return ADS_ERROR_NT(NT_STATUS_OK);
4187 /********************************************************************
4188 ********************************************************************/
4190 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4192 LDAPMessage *res = NULL;
4193 ADS_STATUS status;
4194 int count = 0;
4195 char *name = NULL;
4197 status = ads_find_machine_acct(ads, &res, machine_name);
4198 if (!ADS_ERR_OK(status)) {
4199 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4200 lp_netbios_name()));
4201 goto out;
4204 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4205 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4206 goto out;
4209 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4210 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4213 out:
4214 ads_msgfree(ads, res);
4216 return name;
4219 /********************************************************************
4220 ********************************************************************/
4222 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4223 LDAPMessage *msg, size_t *num_values)
4225 const char *field = "msDS-AdditionalDnsHostName";
4226 struct berval **values = NULL;
4227 char **ret = NULL;
4228 size_t i, converted_size;
4231 * Windows DC implicitly adds a short name for each FQDN added to
4232 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4233 * suffix "\0$" which we should ignore (see bug #14406).
4236 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4237 if (values == NULL) {
4238 return NULL;
4241 *num_values = ldap_count_values_len(values);
4243 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4244 if (ret == NULL) {
4245 ldap_value_free_len(values);
4246 return NULL;
4249 for (i = 0; i < *num_values; i++) {
4250 ret[i] = NULL;
4251 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4252 values[i]->bv_val,
4253 strnlen(values[i]->bv_val,
4254 values[i]->bv_len),
4255 &ret[i], &converted_size)) {
4256 ldap_value_free_len(values);
4257 return NULL;
4260 ret[i] = NULL;
4262 ldap_value_free_len(values);
4263 return ret;
4266 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4267 ADS_STRUCT *ads,
4268 const char *machine_name,
4269 char ***hostnames_array,
4270 size_t *num_hostnames)
4272 ADS_STATUS status;
4273 LDAPMessage *res = NULL;
4274 int count;
4276 status = ads_find_machine_acct(ads,
4277 &res,
4278 machine_name);
4279 if (!ADS_ERR_OK(status)) {
4280 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4281 machine_name));
4282 return status;
4285 count = ads_count_replies(ads, res);
4286 if (count != 1) {
4287 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4288 goto done;
4291 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4292 if (*hostnames_array == NULL) {
4293 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4294 machine_name));
4295 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4296 goto done;
4299 done:
4300 ads_msgfree(ads, res);
4302 return status;
4305 /********************************************************************
4306 ********************************************************************/
4308 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4310 LDAPMessage *res = NULL;
4311 ADS_STATUS status;
4312 int count = 0;
4313 char *name = NULL;
4315 status = ads_find_machine_acct(ads, &res, machine_name);
4316 if (!ADS_ERR_OK(status)) {
4317 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4318 lp_netbios_name()));
4319 goto out;
4322 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4323 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4324 goto out;
4327 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4328 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4331 out:
4332 ads_msgfree(ads, res);
4334 return name;
4337 /********************************************************************
4338 ********************************************************************/
4340 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4342 LDAPMessage *res = NULL;
4343 ADS_STATUS status;
4344 int count = 0;
4345 char *name = NULL;
4346 bool ok = false;
4348 status = ads_find_machine_acct(ads, &res, machine_name);
4349 if (!ADS_ERR_OK(status)) {
4350 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4351 lp_netbios_name()));
4352 goto out;
4355 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4356 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4357 goto out;
4360 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4361 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4364 out:
4365 ads_msgfree(ads, res);
4366 if (name != NULL) {
4367 ok = (strlen(name) > 0);
4369 TALLOC_FREE(name);
4370 return ok;
4373 #if 0
4375 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4378 * Join a machine to a realm
4379 * Creates the machine account and sets the machine password
4380 * @param ads connection to ads server
4381 * @param machine name of host to add
4382 * @param org_unit Organizational unit to place machine in
4383 * @return status of join
4385 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4386 uint32_t account_type, const char *org_unit)
4388 ADS_STATUS status;
4389 LDAPMessage *res = NULL;
4390 char *machine;
4392 /* machine name must be lowercase */
4393 machine = SMB_STRDUP(machine_name);
4394 strlower_m(machine);
4397 status = ads_find_machine_acct(ads, (void **)&res, machine);
4398 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4399 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4400 status = ads_leave_realm(ads, machine);
4401 if (!ADS_ERR_OK(status)) {
4402 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4403 machine, ads->config.realm));
4404 return status;
4408 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4409 if (!ADS_ERR_OK(status)) {
4410 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4411 SAFE_FREE(machine);
4412 return status;
4415 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4416 if (!ADS_ERR_OK(status)) {
4417 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4418 SAFE_FREE(machine);
4419 return status;
4422 SAFE_FREE(machine);
4423 ads_msgfree(ads, res);
4425 return status;
4427 #endif
4430 * Delete a machine from the realm
4431 * @param ads connection to ads server
4432 * @param hostname Machine to remove
4433 * @return status of delete
4435 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4437 ADS_STATUS status;
4438 void *msg;
4439 LDAPMessage *res;
4440 char *hostnameDN, *host;
4441 int rc;
4442 LDAPControl ldap_control;
4443 LDAPControl * pldap_control[2] = {NULL, NULL};
4445 pldap_control[0] = &ldap_control;
4446 memset(&ldap_control, 0, sizeof(LDAPControl));
4447 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4449 /* hostname must be lowercase */
4450 host = SMB_STRDUP(hostname);
4451 if (!strlower_m(host)) {
4452 SAFE_FREE(host);
4453 return ADS_ERROR_SYSTEM(EINVAL);
4456 status = ads_find_machine_acct(ads, &res, host);
4457 if (!ADS_ERR_OK(status)) {
4458 DEBUG(0, ("Host account for %s does not exist.\n", host));
4459 SAFE_FREE(host);
4460 return status;
4463 msg = ads_first_entry(ads, res);
4464 if (!msg) {
4465 SAFE_FREE(host);
4466 return ADS_ERROR_SYSTEM(ENOENT);
4469 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4470 if (hostnameDN == NULL) {
4471 SAFE_FREE(host);
4472 return ADS_ERROR_SYSTEM(ENOENT);
4475 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4476 if (rc) {
4477 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4478 }else {
4479 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4482 if (rc != LDAP_SUCCESS) {
4483 const char *attrs[] = { "cn", NULL };
4484 LDAPMessage *msg_sub;
4486 /* we only search with scope ONE, we do not expect any further
4487 * objects to be created deeper */
4489 status = ads_do_search_retry(ads, hostnameDN,
4490 LDAP_SCOPE_ONELEVEL,
4491 "(objectclass=*)", attrs, &res);
4493 if (!ADS_ERR_OK(status)) {
4494 SAFE_FREE(host);
4495 TALLOC_FREE(hostnameDN);
4496 return status;
4499 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4500 msg_sub = ads_next_entry(ads, msg_sub)) {
4502 char *dn = NULL;
4504 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4505 SAFE_FREE(host);
4506 TALLOC_FREE(hostnameDN);
4507 return ADS_ERROR(LDAP_NO_MEMORY);
4510 status = ads_del_dn(ads, dn);
4511 if (!ADS_ERR_OK(status)) {
4512 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4513 SAFE_FREE(host);
4514 TALLOC_FREE(dn);
4515 TALLOC_FREE(hostnameDN);
4516 return status;
4519 TALLOC_FREE(dn);
4522 /* there should be no subordinate objects anymore */
4523 status = ads_do_search_retry(ads, hostnameDN,
4524 LDAP_SCOPE_ONELEVEL,
4525 "(objectclass=*)", attrs, &res);
4527 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4528 SAFE_FREE(host);
4529 TALLOC_FREE(hostnameDN);
4530 return status;
4533 /* delete hostnameDN now */
4534 status = ads_del_dn(ads, hostnameDN);
4535 if (!ADS_ERR_OK(status)) {
4536 SAFE_FREE(host);
4537 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4538 TALLOC_FREE(hostnameDN);
4539 return status;
4543 TALLOC_FREE(hostnameDN);
4545 status = ads_find_machine_acct(ads, &res, host);
4546 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4547 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4548 DEBUG(3, ("Failed to remove host account.\n"));
4549 SAFE_FREE(host);
4550 return status;
4553 SAFE_FREE(host);
4554 return ADS_SUCCESS;
4558 * pull all token-sids from an LDAP dn
4559 * @param ads connection to ads server
4560 * @param mem_ctx TALLOC_CTX for allocating sid array
4561 * @param dn of LDAP object
4562 * @param user_sid pointer to struct dom_sid (objectSid)
4563 * @param primary_group_sid pointer to struct dom_sid (self composed)
4564 * @param sids pointer to sid array to allocate
4565 * @param num_sids counter of SIDs pulled
4566 * @return status of token query
4568 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4569 TALLOC_CTX *mem_ctx,
4570 const char *dn,
4571 struct dom_sid *user_sid,
4572 struct dom_sid *primary_group_sid,
4573 struct dom_sid **sids,
4574 size_t *num_sids)
4576 ADS_STATUS status;
4577 LDAPMessage *res = NULL;
4578 int count = 0;
4579 size_t tmp_num_sids;
4580 struct dom_sid *tmp_sids;
4581 struct dom_sid tmp_user_sid;
4582 struct dom_sid tmp_primary_group_sid;
4583 uint32_t pgid;
4584 const char *attrs[] = {
4585 "objectSid",
4586 "tokenGroups",
4587 "primaryGroupID",
4588 NULL
4591 status = ads_search_retry_dn(ads, &res, dn, attrs);
4592 if (!ADS_ERR_OK(status)) {
4593 return status;
4596 count = ads_count_replies(ads, res);
4597 if (count != 1) {
4598 ads_msgfree(ads, res);
4599 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4602 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4603 ads_msgfree(ads, res);
4604 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4607 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4608 ads_msgfree(ads, res);
4609 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4613 /* hack to compose the primary group sid without knowing the
4614 * domsid */
4616 struct dom_sid domsid;
4618 sid_copy(&domsid, &tmp_user_sid);
4620 if (!sid_split_rid(&domsid, NULL)) {
4621 ads_msgfree(ads, res);
4622 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4625 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4626 ads_msgfree(ads, res);
4627 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4631 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4633 if (tmp_num_sids == 0 || !tmp_sids) {
4634 ads_msgfree(ads, res);
4635 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4638 if (num_sids) {
4639 *num_sids = tmp_num_sids;
4642 if (sids) {
4643 *sids = tmp_sids;
4646 if (user_sid) {
4647 *user_sid = tmp_user_sid;
4650 if (primary_group_sid) {
4651 *primary_group_sid = tmp_primary_group_sid;
4654 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4656 ads_msgfree(ads, res);
4657 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4661 * Find a sAMAccountName in LDAP
4662 * @param ads connection to ads server
4663 * @param mem_ctx TALLOC_CTX for allocating sid array
4664 * @param samaccountname to search
4665 * @param uac_ret uint32_t pointer userAccountControl attribute value
4666 * @param dn_ret pointer to dn
4667 * @return status of token query
4669 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4670 TALLOC_CTX *mem_ctx,
4671 const char *samaccountname,
4672 uint32_t *uac_ret,
4673 const char **dn_ret)
4675 ADS_STATUS status;
4676 const char *attrs[] = { "userAccountControl", NULL };
4677 const char *filter;
4678 LDAPMessage *res = NULL;
4679 char *dn = NULL;
4680 uint32_t uac = 0;
4682 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4683 samaccountname);
4684 if (filter == NULL) {
4685 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4686 goto out;
4689 status = ads_do_search_all(ads, ads->config.bind_path,
4690 LDAP_SCOPE_SUBTREE,
4691 filter, attrs, &res);
4693 if (!ADS_ERR_OK(status)) {
4694 goto out;
4697 if (ads_count_replies(ads, res) != 1) {
4698 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4699 goto out;
4702 dn = ads_get_dn(ads, talloc_tos(), res);
4703 if (dn == NULL) {
4704 status = ADS_ERROR(LDAP_NO_MEMORY);
4705 goto out;
4708 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4709 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4710 goto out;
4713 if (uac_ret) {
4714 *uac_ret = uac;
4717 if (dn_ret) {
4718 *dn_ret = talloc_strdup(mem_ctx, dn);
4719 if (!*dn_ret) {
4720 status = ADS_ERROR(LDAP_NO_MEMORY);
4721 goto out;
4724 out:
4725 TALLOC_FREE(dn);
4726 ads_msgfree(ads, res);
4728 return status;
4732 * find our configuration path
4733 * @param ads connection to ads server
4734 * @param mem_ctx Pointer to talloc context
4735 * @param config_path Pointer to the config path
4736 * @return status of search
4738 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4739 TALLOC_CTX *mem_ctx,
4740 char **config_path)
4742 ADS_STATUS status;
4743 LDAPMessage *res = NULL;
4744 const char *config_context = NULL;
4745 const char *attrs[] = { "configurationNamingContext", NULL };
4747 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4748 "(objectclass=*)", attrs, &res);
4749 if (!ADS_ERR_OK(status)) {
4750 return status;
4753 config_context = ads_pull_string(ads, mem_ctx, res,
4754 "configurationNamingContext");
4755 ads_msgfree(ads, res);
4756 if (!config_context) {
4757 return ADS_ERROR(LDAP_NO_MEMORY);
4760 if (config_path) {
4761 *config_path = talloc_strdup(mem_ctx, config_context);
4762 if (!*config_path) {
4763 return ADS_ERROR(LDAP_NO_MEMORY);
4767 return ADS_ERROR(LDAP_SUCCESS);
4771 * find the displayName of an extended right
4772 * @param ads connection to ads server
4773 * @param config_path The config path
4774 * @param mem_ctx Pointer to talloc context
4775 * @param GUID struct of the rightsGUID
4776 * @return status of search
4778 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4779 const char *config_path,
4780 TALLOC_CTX *mem_ctx,
4781 const struct GUID *rights_guid)
4783 ADS_STATUS rc;
4784 LDAPMessage *res = NULL;
4785 char *expr = NULL;
4786 const char *attrs[] = { "displayName", NULL };
4787 const char *result = NULL;
4788 const char *path;
4790 if (!ads || !mem_ctx || !rights_guid) {
4791 goto done;
4794 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4795 GUID_string(mem_ctx, rights_guid));
4796 if (!expr) {
4797 goto done;
4800 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4801 if (!path) {
4802 goto done;
4805 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4806 expr, attrs, &res);
4807 if (!ADS_ERR_OK(rc)) {
4808 goto done;
4811 if (ads_count_replies(ads, res) != 1) {
4812 goto done;
4815 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4817 done:
4818 ads_msgfree(ads, res);
4819 return result;
4823 * verify or build and verify an account ou
4824 * @param mem_ctx Pointer to talloc context
4825 * @param ads connection to ads server
4826 * @param account_ou
4827 * @return status of search
4830 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4831 ADS_STRUCT *ads,
4832 const char **account_ou)
4834 char **exploded_dn;
4835 const char *name;
4836 char *ou_string;
4838 if (account_ou == NULL) {
4839 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4842 if (*account_ou != NULL) {
4843 exploded_dn = ldap_explode_dn(*account_ou, 0);
4844 if (exploded_dn) {
4845 ldap_value_free(exploded_dn);
4846 return ADS_SUCCESS;
4850 ou_string = ads_ou_string(ads, *account_ou);
4851 if (!ou_string) {
4852 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4855 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4856 ads->config.bind_path);
4857 SAFE_FREE(ou_string);
4859 if (!name) {
4860 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4863 exploded_dn = ldap_explode_dn(name, 0);
4864 if (!exploded_dn) {
4865 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4867 ldap_value_free(exploded_dn);
4869 *account_ou = name;
4870 return ADS_SUCCESS;
4873 #endif