s3:libads: move ads->auth.time_offset to ads->config.time_offset
[Samba.git] / source3 / libads / ldap.c
blobbb219baec804dc4d383e1c6c48ddae60fb46b57c
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(ADS_STRUCT *ads)
1132 TALLOC_CTX *frame = talloc_stackframe();
1133 struct cli_credentials *creds = NULL;
1134 ADS_STATUS status;
1135 NTSTATUS ntstatus;
1137 ntstatus = ads_legacy_creds(ads, frame, &creds);
1138 if (!NT_STATUS_IS_OK(ntstatus)) {
1139 TALLOC_FREE(frame);
1140 return ADS_ERROR_NT(ntstatus);
1143 status = ads_connect_internal(ads, creds);
1144 TALLOC_FREE(frame);
1145 return status;
1149 * Connect to the LDAP server using given credentials
1150 * @param ads Pointer to an existing ADS_STRUCT
1151 * @return status of connection
1153 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
1155 ads->auth.flags |= ADS_AUTH_USER_CREDS;
1157 return ads_connect(ads);
1161 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1162 * @param ads Pointer to an existing ADS_STRUCT
1164 * Sets the ads->ldap.ss to a valid
1165 * zero ip address that can be detected by
1166 * our is_zero_addr() function. Otherwise
1167 * it is left as AF_UNSPEC (0).
1169 void ads_zero_ldap(ADS_STRUCT *ads)
1171 ZERO_STRUCT(ads->ldap);
1173 * Initialize the sockaddr_storage so we can use
1174 * sockaddr test functions against it.
1176 zero_sockaddr(&ads->ldap.ss);
1180 * Disconnect the LDAP server
1181 * @param ads Pointer to an existing ADS_STRUCT
1183 void ads_disconnect(ADS_STRUCT *ads)
1185 if (ads->ldap.ld) {
1186 ldap_unbind(ads->ldap.ld);
1187 ads->ldap.ld = NULL;
1189 if (ads->ldap_tls_data.mem_ctx) {
1190 talloc_free(ads->ldap_tls_data.mem_ctx);
1192 if (ads->ldap_wrap_data.wrap_ops &&
1193 ads->ldap_wrap_data.wrap_ops->disconnect) {
1194 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1196 if (ads->ldap_wrap_data.mem_ctx) {
1197 talloc_free(ads->ldap_wrap_data.mem_ctx);
1199 ads_zero_ldap(ads);
1200 ZERO_STRUCT(ads->ldap_tls_data);
1201 ZERO_STRUCT(ads->ldap_wrap_data);
1205 Duplicate a struct berval into talloc'ed memory
1207 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1209 struct berval *value;
1211 if (!in_val) return NULL;
1213 value = talloc_zero(ctx, struct berval);
1214 if (value == NULL)
1215 return NULL;
1216 if (in_val->bv_len == 0) return value;
1218 value->bv_len = in_val->bv_len;
1219 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1220 in_val->bv_len);
1221 return value;
1225 Make a values list out of an array of (struct berval *)
1227 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1228 const struct berval **in_vals)
1230 struct berval **values;
1231 int i;
1233 if (!in_vals) return NULL;
1234 for (i=0; in_vals[i]; i++)
1235 ; /* count values */
1236 values = talloc_zero_array(ctx, struct berval *, i+1);
1237 if (!values) return NULL;
1239 for (i=0; in_vals[i]; i++) {
1240 values[i] = dup_berval(ctx, in_vals[i]);
1242 return values;
1246 UTF8-encode a values list out of an array of (char *)
1248 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1250 char **values;
1251 int i;
1252 size_t size;
1254 if (!in_vals) return NULL;
1255 for (i=0; in_vals[i]; i++)
1256 ; /* count values */
1257 values = talloc_zero_array(ctx, char *, i+1);
1258 if (!values) return NULL;
1260 for (i=0; in_vals[i]; i++) {
1261 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1262 TALLOC_FREE(values);
1263 return NULL;
1266 return values;
1270 Pull a (char *) array out of a UTF8-encoded values list
1272 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1274 char **values;
1275 int i;
1276 size_t converted_size;
1278 if (!in_vals) return NULL;
1279 for (i=0; in_vals[i]; i++)
1280 ; /* count values */
1281 values = talloc_zero_array(ctx, char *, i+1);
1282 if (!values) return NULL;
1284 for (i=0; in_vals[i]; i++) {
1285 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1286 &converted_size)) {
1287 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1288 "%s\n", strerror(errno)));
1291 return values;
1295 * Do a search with paged results. cookie must be null on the first
1296 * call, and then returned on each subsequent call. It will be null
1297 * again when the entire search is complete
1298 * @param ads connection to ads server
1299 * @param bind_path Base dn for the search
1300 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1301 * @param expr Search expression - specified in local charset
1302 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1303 * @param res ** which will contain results - free res* with ads_msgfree()
1304 * @param count Number of entries retrieved on this page
1305 * @param cookie The paged results cookie to be returned on subsequent calls
1306 * @return status of search
1308 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1309 const char *bind_path,
1310 int scope, const char *expr,
1311 const char **attrs, void *args,
1312 LDAPMessage **res,
1313 int *count, struct berval **cookie)
1315 int rc, i, version;
1316 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1317 size_t converted_size;
1318 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1319 BerElement *cookie_be = NULL;
1320 struct berval *cookie_bv= NULL;
1321 BerElement *ext_be = NULL;
1322 struct berval *ext_bv= NULL;
1324 TALLOC_CTX *ctx;
1325 ads_control *external_control = (ads_control *) args;
1327 *res = NULL;
1329 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1330 return ADS_ERROR(LDAP_NO_MEMORY);
1332 /* 0 means the conversion worked but the result was empty
1333 so we only fail if it's -1. In any case, it always
1334 at least nulls out the dest */
1335 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1336 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1338 rc = LDAP_NO_MEMORY;
1339 goto done;
1342 if (!attrs || !(*attrs))
1343 search_attrs = NULL;
1344 else {
1345 /* This would be the utf8-encoded version...*/
1346 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1347 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1348 rc = LDAP_NO_MEMORY;
1349 goto done;
1353 /* Paged results only available on ldap v3 or later */
1354 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1355 if (version < LDAP_VERSION3) {
1356 rc = LDAP_NOT_SUPPORTED;
1357 goto done;
1360 cookie_be = ber_alloc_t(LBER_USE_DER);
1361 if (*cookie) {
1362 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1363 ber_bvfree(*cookie); /* don't need it from last time */
1364 *cookie = NULL;
1365 } else {
1366 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1368 ber_flatten(cookie_be, &cookie_bv);
1369 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1370 PagedResults.ldctl_iscritical = (char) 1;
1371 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1372 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1374 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1375 NoReferrals.ldctl_iscritical = (char) 0;
1376 NoReferrals.ldctl_value.bv_len = 0;
1377 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1379 if (external_control &&
1380 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1381 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1383 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1384 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1386 /* win2k does not accept a ldctl_value being passed in */
1388 if (external_control->val != 0) {
1390 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1391 rc = LDAP_NO_MEMORY;
1392 goto done;
1395 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1396 rc = LDAP_NO_MEMORY;
1397 goto done;
1399 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1400 rc = LDAP_NO_MEMORY;
1401 goto done;
1404 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1405 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1407 } else {
1408 ExternalCtrl.ldctl_value.bv_len = 0;
1409 ExternalCtrl.ldctl_value.bv_val = NULL;
1412 controls[0] = &NoReferrals;
1413 controls[1] = &PagedResults;
1414 controls[2] = &ExternalCtrl;
1415 controls[3] = NULL;
1417 } else {
1418 controls[0] = &NoReferrals;
1419 controls[1] = &PagedResults;
1420 controls[2] = NULL;
1423 /* we need to disable referrals as the openldap libs don't
1424 handle them and paged results at the same time. Using them
1425 together results in the result record containing the server
1426 page control being removed from the result list (tridge/jmcd)
1428 leaving this in despite the control that says don't generate
1429 referrals, in case the server doesn't support it (jmcd)
1431 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1433 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1434 search_attrs, 0, controls,
1435 NULL, LDAP_NO_LIMIT,
1436 (LDAPMessage **)res);
1438 ber_free(cookie_be, 1);
1439 ber_bvfree(cookie_bv);
1441 if (rc) {
1442 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1443 ldap_err2string(rc)));
1444 if (rc == LDAP_OTHER) {
1445 char *ldap_errmsg;
1446 int ret;
1448 ret = ldap_parse_result(ads->ldap.ld,
1449 *res,
1450 NULL,
1451 NULL,
1452 &ldap_errmsg,
1453 NULL,
1454 NULL,
1456 if (ret == LDAP_SUCCESS) {
1457 DEBUG(3, ("ldap_search_with_timeout(%s) "
1458 "error: %s\n", expr, ldap_errmsg));
1459 ldap_memfree(ldap_errmsg);
1462 goto done;
1465 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1466 NULL, &rcontrols, 0);
1468 if (!rcontrols) {
1469 goto done;
1472 for (i=0; rcontrols[i]; i++) {
1473 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1474 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1475 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1476 &cookie_bv);
1477 /* the berval is the cookie, but must be freed when
1478 it is all done */
1479 if (cookie_bv->bv_len) /* still more to do */
1480 *cookie=ber_bvdup(cookie_bv);
1481 else
1482 *cookie=NULL;
1483 ber_bvfree(cookie_bv);
1484 ber_free(cookie_be, 1);
1485 break;
1488 ldap_controls_free(rcontrols);
1490 done:
1491 talloc_destroy(ctx);
1493 if (ext_be) {
1494 ber_free(ext_be, 1);
1497 if (ext_bv) {
1498 ber_bvfree(ext_bv);
1501 if (rc != LDAP_SUCCESS && *res != NULL) {
1502 ads_msgfree(ads, *res);
1503 *res = NULL;
1506 /* if/when we decide to utf8-encode attrs, take out this next line */
1507 TALLOC_FREE(search_attrs);
1509 return ADS_ERROR(rc);
1512 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1513 int scope, const char *expr,
1514 const char **attrs, LDAPMessage **res,
1515 int *count, struct berval **cookie)
1517 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1522 * Get all results for a search. This uses ads_do_paged_search() to return
1523 * all entries in a large search.
1524 * @param ads connection to ads server
1525 * @param bind_path Base dn for the search
1526 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1527 * @param expr Search expression
1528 * @param attrs Attributes to retrieve
1529 * @param res ** which will contain results - free res* with ads_msgfree()
1530 * @return status of search
1532 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1533 int scope, const char *expr,
1534 const char **attrs, void *args,
1535 LDAPMessage **res)
1537 struct berval *cookie = NULL;
1538 int count = 0;
1539 ADS_STATUS status;
1541 *res = NULL;
1542 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1543 &count, &cookie);
1545 if (!ADS_ERR_OK(status))
1546 return status;
1548 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1549 while (cookie) {
1550 LDAPMessage *res2 = NULL;
1551 LDAPMessage *msg, *next;
1553 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1554 attrs, args, &res2, &count, &cookie);
1555 if (!ADS_ERR_OK(status)) {
1556 break;
1559 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1560 that this works on all ldap libs, but I have only tested with openldap */
1561 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1562 next = ads_next_message(ads, msg);
1563 ldap_add_result_entry((LDAPMessage **)res, msg);
1565 /* note that we do not free res2, as the memory is now
1566 part of the main returned list */
1568 #else
1569 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1570 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1571 #endif
1573 return status;
1576 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1577 int scope, const char *expr,
1578 const char **attrs, LDAPMessage **res)
1580 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1583 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1584 int scope, const char *expr,
1585 const char **attrs, uint32_t sd_flags,
1586 LDAPMessage **res)
1588 ads_control args;
1590 args.control = ADS_SD_FLAGS_OID;
1591 args.val = sd_flags;
1592 args.critical = True;
1594 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1599 * Run a function on all results for a search. Uses ads_do_paged_search() and
1600 * runs the function as each page is returned, using ads_process_results()
1601 * @param ads connection to ads server
1602 * @param bind_path Base dn for the search
1603 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1604 * @param expr Search expression - specified in local charset
1605 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1606 * @param fn Function which takes attr name, values list, and data_area
1607 * @param data_area Pointer which is passed to function on each call
1608 * @return status of search
1610 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1611 int scope, const char *expr, const char **attrs,
1612 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1613 void *data_area)
1615 struct berval *cookie = NULL;
1616 int count = 0;
1617 ADS_STATUS status;
1618 LDAPMessage *res;
1620 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1621 &count, &cookie);
1623 if (!ADS_ERR_OK(status)) return status;
1625 ads_process_results(ads, res, fn, data_area);
1626 ads_msgfree(ads, res);
1628 while (cookie) {
1629 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1630 &res, &count, &cookie);
1632 if (!ADS_ERR_OK(status)) break;
1634 ads_process_results(ads, res, fn, data_area);
1635 ads_msgfree(ads, res);
1638 return status;
1642 * Do a search with a timeout.
1643 * @param ads connection to ads server
1644 * @param bind_path Base dn for the search
1645 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1646 * @param expr Search expression
1647 * @param attrs Attributes to retrieve
1648 * @param res ** which will contain results - free res* with ads_msgfree()
1649 * @return status of search
1651 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1652 const char *expr,
1653 const char **attrs, LDAPMessage **res)
1655 int rc;
1656 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1657 size_t converted_size;
1658 TALLOC_CTX *ctx;
1660 *res = NULL;
1661 if (!(ctx = talloc_init("ads_do_search"))) {
1662 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1663 return ADS_ERROR(LDAP_NO_MEMORY);
1666 /* 0 means the conversion worked but the result was empty
1667 so we only fail if it's negative. In any case, it always
1668 at least nulls out the dest */
1669 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1670 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1672 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1673 rc = LDAP_NO_MEMORY;
1674 goto done;
1677 if (!attrs || !(*attrs))
1678 search_attrs = NULL;
1679 else {
1680 /* This would be the utf8-encoded version...*/
1681 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1682 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1684 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1685 rc = LDAP_NO_MEMORY;
1686 goto done;
1690 /* see the note in ads_do_paged_search - we *must* disable referrals */
1691 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1693 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1694 search_attrs, 0, NULL, NULL,
1695 LDAP_NO_LIMIT,
1696 (LDAPMessage **)res);
1698 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1699 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1700 rc = 0;
1703 done:
1704 talloc_destroy(ctx);
1705 /* if/when we decide to utf8-encode attrs, take out this next line */
1706 TALLOC_FREE(search_attrs);
1707 return ADS_ERROR(rc);
1710 * Do a general ADS search
1711 * @param ads connection to ads server
1712 * @param res ** which will contain results - free res* with ads_msgfree()
1713 * @param expr Search expression
1714 * @param attrs Attributes to retrieve
1715 * @return status of search
1717 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1718 const char *expr, const char **attrs)
1720 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1721 expr, attrs, res);
1725 * Do a search on a specific DistinguishedName
1726 * @param ads connection to ads server
1727 * @param res ** which will contain results - free res* with ads_msgfree()
1728 * @param dn DistinguishedName to search
1729 * @param attrs Attributes to retrieve
1730 * @return status of search
1732 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1733 const char *dn, const char **attrs)
1735 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1736 attrs, res);
1740 * Free up memory from a ads_search
1741 * @param ads connection to ads server
1742 * @param msg Search results to free
1744 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1746 if (!msg) return;
1747 ldap_msgfree(msg);
1751 * Get a dn from search results
1752 * @param ads connection to ads server
1753 * @param msg Search result
1754 * @return dn string
1756 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1758 char *utf8_dn, *unix_dn;
1759 size_t converted_size;
1761 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1763 if (!utf8_dn) {
1764 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1765 return NULL;
1768 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1769 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1770 utf8_dn ));
1771 return NULL;
1773 ldap_memfree(utf8_dn);
1774 return unix_dn;
1778 * Get the parent from a dn
1779 * @param dn the dn to return the parent from
1780 * @return parent dn string
1782 char *ads_parent_dn(const char *dn)
1784 char *p;
1786 if (dn == NULL) {
1787 return NULL;
1790 p = strchr(dn, ',');
1792 if (p == NULL) {
1793 return NULL;
1796 return p+1;
1800 * Find a machine account given a hostname
1801 * @param ads connection to ads server
1802 * @param res ** which will contain results - free res* with ads_msgfree()
1803 * @param host Hostname to search for
1804 * @return status of search
1806 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1807 const char *machine)
1809 ADS_STATUS status;
1810 char *expr;
1811 const char *attrs[] = {
1812 /* This is how Windows checks for machine accounts */
1813 "objectClass",
1814 "SamAccountName",
1815 "userAccountControl",
1816 "DnsHostName",
1817 "ServicePrincipalName",
1818 "userPrincipalName",
1820 /* Additional attributes Samba checks */
1821 "msDS-AdditionalDnsHostName",
1822 "msDS-SupportedEncryptionTypes",
1823 "nTSecurityDescriptor",
1824 "objectSid",
1826 NULL
1828 TALLOC_CTX *frame = talloc_stackframe();
1830 *res = NULL;
1832 /* the easiest way to find a machine account anywhere in the tree
1833 is to look for hostname$ */
1834 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1835 if (expr == NULL) {
1836 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1837 goto done;
1840 status = ads_search(ads, res, expr, attrs);
1841 if (ADS_ERR_OK(status)) {
1842 if (ads_count_replies(ads, *res) != 1) {
1843 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1847 done:
1848 TALLOC_FREE(frame);
1849 return status;
1853 * Initialize a list of mods to be used in a modify request
1854 * @param ctx An initialized TALLOC_CTX
1855 * @return allocated ADS_MODLIST
1857 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1859 #define ADS_MODLIST_ALLOC_SIZE 10
1860 LDAPMod **mods;
1862 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1863 /* -1 is safety to make sure we don't go over the end.
1864 need to reset it to NULL before doing ldap modify */
1865 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1867 return (ADS_MODLIST)mods;
1872 add an attribute to the list, with values list already constructed
1874 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1875 int mod_op, const char *name,
1876 const void *_invals)
1878 int curmod;
1879 LDAPMod **modlist = (LDAPMod **) *mods;
1880 struct berval **ber_values = NULL;
1881 char **char_values = NULL;
1883 if (!_invals) {
1884 mod_op = LDAP_MOD_DELETE;
1885 } else {
1886 if (mod_op & LDAP_MOD_BVALUES) {
1887 const struct berval **b;
1888 b = discard_const_p(const struct berval *, _invals);
1889 ber_values = ads_dup_values(ctx, b);
1890 } else {
1891 const char **c;
1892 c = discard_const_p(const char *, _invals);
1893 char_values = ads_push_strvals(ctx, c);
1897 /* find the first empty slot */
1898 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1899 curmod++);
1900 if (modlist[curmod] == (LDAPMod *) -1) {
1901 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1902 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1903 return ADS_ERROR(LDAP_NO_MEMORY);
1904 memset(&modlist[curmod], 0,
1905 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1906 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1907 *mods = (ADS_MODLIST)modlist;
1910 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1911 return ADS_ERROR(LDAP_NO_MEMORY);
1912 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1913 if (mod_op & LDAP_MOD_BVALUES) {
1914 modlist[curmod]->mod_bvalues = ber_values;
1915 } else if (mod_op & LDAP_MOD_DELETE) {
1916 modlist[curmod]->mod_values = NULL;
1917 } else {
1918 modlist[curmod]->mod_values = char_values;
1921 modlist[curmod]->mod_op = mod_op;
1922 return ADS_ERROR(LDAP_SUCCESS);
1926 * Add a single string value to a mod list
1927 * @param ctx An initialized TALLOC_CTX
1928 * @param mods An initialized ADS_MODLIST
1929 * @param name The attribute name to add
1930 * @param val The value to add - NULL means DELETE
1931 * @return ADS STATUS indicating success of add
1933 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1934 const char *name, const char *val)
1936 const char *values[2];
1938 values[0] = val;
1939 values[1] = NULL;
1941 if (!val)
1942 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1943 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1947 * Add an array of string values to a mod list
1948 * @param ctx An initialized TALLOC_CTX
1949 * @param mods An initialized ADS_MODLIST
1950 * @param name The attribute name to add
1951 * @param vals The array of string values to add - NULL means DELETE
1952 * @return ADS STATUS indicating success of add
1954 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1955 const char *name, const char **vals)
1957 if (!vals)
1958 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1959 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1960 name, (const void **) vals);
1964 * Add a single ber-encoded value to a mod list
1965 * @param ctx An initialized TALLOC_CTX
1966 * @param mods An initialized ADS_MODLIST
1967 * @param name The attribute name to add
1968 * @param val The value to add - NULL means DELETE
1969 * @return ADS STATUS indicating success of add
1971 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1972 const char *name, const struct berval *val)
1974 const struct berval *values[2];
1976 values[0] = val;
1977 values[1] = NULL;
1978 if (!val)
1979 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1980 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1981 name, (const void **) values);
1984 static void ads_print_error(int ret, LDAP *ld)
1986 if (ret != 0) {
1987 char *ld_error = NULL;
1988 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1989 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1990 ret,
1991 ldap_err2string(ret),
1992 ld_error);
1993 SAFE_FREE(ld_error);
1998 * Perform an ldap modify
1999 * @param ads connection to ads server
2000 * @param mod_dn DistinguishedName to modify
2001 * @param mods list of modifications to perform
2002 * @return status of modify
2004 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
2006 int ret,i;
2007 char *utf8_dn = NULL;
2008 size_t converted_size;
2010 this control is needed to modify that contains a currently
2011 non-existent attribute (but allowable for the object) to run
2013 LDAPControl PermitModify = {
2014 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
2015 {0, NULL},
2016 (char) 1};
2017 LDAPControl *controls[2];
2019 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
2021 controls[0] = &PermitModify;
2022 controls[1] = NULL;
2024 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
2025 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2028 /* find the end of the list, marked by NULL or -1 */
2029 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2030 /* make sure the end of the list is NULL */
2031 mods[i] = NULL;
2032 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
2033 (LDAPMod **) mods, controls, NULL);
2034 ads_print_error(ret, ads->ldap.ld);
2035 TALLOC_FREE(utf8_dn);
2036 return ADS_ERROR(ret);
2040 * Perform an ldap add
2041 * @param ads connection to ads server
2042 * @param new_dn DistinguishedName to add
2043 * @param mods list of attributes and values for DN
2044 * @return status of add
2046 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
2048 int ret, i;
2049 char *utf8_dn = NULL;
2050 size_t converted_size;
2052 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
2054 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
2055 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2056 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2059 /* find the end of the list, marked by NULL or -1 */
2060 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2061 /* make sure the end of the list is NULL */
2062 mods[i] = NULL;
2064 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
2065 ads_print_error(ret, ads->ldap.ld);
2066 TALLOC_FREE(utf8_dn);
2067 return ADS_ERROR(ret);
2071 * Delete a DistinguishedName
2072 * @param ads connection to ads server
2073 * @param new_dn DistinguishedName to delete
2074 * @return status of delete
2076 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
2078 int ret;
2079 char *utf8_dn = NULL;
2080 size_t converted_size;
2081 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
2082 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2083 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2086 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
2088 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
2089 ads_print_error(ret, ads->ldap.ld);
2090 TALLOC_FREE(utf8_dn);
2091 return ADS_ERROR(ret);
2095 * Build an org unit string
2096 * if org unit is Computers or blank then assume a container, otherwise
2097 * assume a / separated list of organisational units.
2098 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2099 * @param ads connection to ads server
2100 * @param org_unit Organizational unit
2101 * @return org unit string - caller must free
2103 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
2105 ADS_STATUS status;
2106 char *ret = NULL;
2107 char *dn = NULL;
2109 if (!org_unit || !*org_unit) {
2111 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
2113 /* samba4 might not yet respond to a wellknownobject-query */
2114 return ret ? ret : SMB_STRDUP("cn=Computers");
2117 if (strequal(org_unit, "Computers")) {
2118 return SMB_STRDUP("cn=Computers");
2121 /* jmcd: removed "\\" from the separation chars, because it is
2122 needed as an escape for chars like '#' which are valid in an
2123 OU name */
2124 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
2125 if (!ADS_ERR_OK(status)) {
2126 return NULL;
2129 return dn;
2133 * Get a org unit string for a well-known GUID
2134 * @param ads connection to ads server
2135 * @param wknguid Well known GUID
2136 * @return org unit string - caller must free
2138 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2140 ADS_STATUS status;
2141 LDAPMessage *res = NULL;
2142 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2143 **bind_dn_exp = NULL;
2144 const char *attrs[] = {"distinguishedName", NULL};
2145 int new_ln, wkn_ln, bind_ln, i;
2147 if (wknguid == NULL) {
2148 return NULL;
2151 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2152 DEBUG(1, ("asprintf failed!\n"));
2153 return NULL;
2156 status = ads_search_dn(ads, &res, base, attrs);
2157 if (!ADS_ERR_OK(status)) {
2158 DEBUG(1,("Failed while searching for: %s\n", base));
2159 goto out;
2162 if (ads_count_replies(ads, res) != 1) {
2163 goto out;
2166 /* substitute the bind-path from the well-known-guid-search result */
2167 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2168 if (!wkn_dn) {
2169 goto out;
2172 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2173 if (!wkn_dn_exp) {
2174 goto out;
2177 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2178 if (!bind_dn_exp) {
2179 goto out;
2182 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2184 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2187 new_ln = wkn_ln - bind_ln;
2189 ret = SMB_STRDUP(wkn_dn_exp[0]);
2190 if (!ret) {
2191 goto out;
2194 for (i=1; i < new_ln; i++) {
2195 char *s = NULL;
2197 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2198 SAFE_FREE(ret);
2199 goto out;
2202 SAFE_FREE(ret);
2203 ret = SMB_STRDUP(s);
2204 free(s);
2205 if (!ret) {
2206 goto out;
2210 out:
2211 SAFE_FREE(base);
2212 ads_msgfree(ads, res);
2213 TALLOC_FREE(wkn_dn);
2214 if (wkn_dn_exp) {
2215 ldap_value_free(wkn_dn_exp);
2217 if (bind_dn_exp) {
2218 ldap_value_free(bind_dn_exp);
2221 return ret;
2225 * Adds (appends) an item to an attribute array, rather then
2226 * replacing the whole list
2227 * @param ctx An initialized TALLOC_CTX
2228 * @param mods An initialized ADS_MODLIST
2229 * @param name name of the ldap attribute to append to
2230 * @param vals an array of values to add
2231 * @return status of addition
2234 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2235 const char *name, const char **vals)
2237 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2238 (const void *) vals);
2242 * Determines the an account's current KVNO via an LDAP lookup
2243 * @param ads An initialized ADS_STRUCT
2244 * @param account_name the NT samaccountname.
2245 * @return the kvno for the account, or -1 in case of a failure.
2248 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
2250 LDAPMessage *res = NULL;
2251 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
2252 char *filter;
2253 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
2254 char *dn_string = NULL;
2255 ADS_STATUS ret;
2257 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
2258 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
2259 return kvno;
2261 ret = ads_search(ads, &res, filter, attrs);
2262 SAFE_FREE(filter);
2263 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
2264 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
2265 ads_msgfree(ads, res);
2266 return kvno;
2269 dn_string = ads_get_dn(ads, talloc_tos(), res);
2270 if (!dn_string) {
2271 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2272 ads_msgfree(ads, res);
2273 return kvno;
2275 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
2276 TALLOC_FREE(dn_string);
2278 /* ---------------------------------------------------------
2279 * 0 is returned as a default KVNO from this point on...
2280 * This is done because Windows 2000 does not support key
2281 * version numbers. Chances are that a failure in the next
2282 * step is simply due to Windows 2000 being used for a
2283 * domain controller. */
2284 kvno = 0;
2286 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2287 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2288 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2289 ads_msgfree(ads, res);
2290 return kvno;
2293 /* Success */
2294 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2295 ads_msgfree(ads, res);
2296 return kvno;
2300 * Determines the computer account's current KVNO via an LDAP lookup
2301 * @param ads An initialized ADS_STRUCT
2302 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2303 * @return the kvno for the computer account, or -1 in case of a failure.
2306 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2308 char *computer_account = NULL;
2309 uint32_t kvno = -1;
2311 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2312 return kvno;
2315 kvno = ads_get_kvno(ads, computer_account);
2316 free(computer_account);
2318 return kvno;
2322 * This clears out all registered spn's for a given hostname
2323 * @param ads An initialized ADS_STRUCT
2324 * @param machine_name the NetBIOS name of the computer.
2325 * @return 0 upon success, non-zero otherwise.
2328 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2330 TALLOC_CTX *ctx;
2331 LDAPMessage *res = NULL;
2332 ADS_MODLIST mods;
2333 const char *servicePrincipalName[1] = {NULL};
2334 ADS_STATUS ret;
2335 char *dn_string = NULL;
2337 ret = ads_find_machine_acct(ads, &res, machine_name);
2338 if (!ADS_ERR_OK(ret)) {
2339 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2340 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2341 ads_msgfree(ads, res);
2342 return ret;
2345 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2346 ctx = talloc_init("ads_clear_service_principal_names");
2347 if (!ctx) {
2348 ads_msgfree(ads, res);
2349 return ADS_ERROR(LDAP_NO_MEMORY);
2352 if (!(mods = ads_init_mods(ctx))) {
2353 talloc_destroy(ctx);
2354 ads_msgfree(ads, res);
2355 return ADS_ERROR(LDAP_NO_MEMORY);
2357 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2358 if (!ADS_ERR_OK(ret)) {
2359 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2360 ads_msgfree(ads, res);
2361 talloc_destroy(ctx);
2362 return ret;
2364 dn_string = ads_get_dn(ads, talloc_tos(), res);
2365 if (!dn_string) {
2366 talloc_destroy(ctx);
2367 ads_msgfree(ads, res);
2368 return ADS_ERROR(LDAP_NO_MEMORY);
2370 ret = ads_gen_mod(ads, dn_string, mods);
2371 TALLOC_FREE(dn_string);
2372 if (!ADS_ERR_OK(ret)) {
2373 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2374 machine_name));
2375 ads_msgfree(ads, res);
2376 talloc_destroy(ctx);
2377 return ret;
2380 ads_msgfree(ads, res);
2381 talloc_destroy(ctx);
2382 return ret;
2386 * @brief Search for an element in a string array.
2388 * @param[in] el_array The string array to search.
2390 * @param[in] num_el The number of elements in the string array.
2392 * @param[in] el The string to search.
2394 * @return True if found, false if not.
2396 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2398 size_t i;
2400 if (el_array == NULL || num_el == 0 || el == NULL) {
2401 return false;
2404 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2405 int cmp;
2407 cmp = strcasecmp_m(el_array[i], el);
2408 if (cmp == 0) {
2409 return true;
2413 return false;
2417 * @brief This gets the service principal names of an existing computer account.
2419 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2421 * @param[in] ads The ADS context to use.
2423 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2424 * identify the computer account.
2426 * @param[in] spn_array A pointer to store the array for SPNs.
2428 * @param[in] num_spns The number of principals stored in the array.
2430 * @return 0 on success, or a ADS error if a failure occurred.
2432 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2433 ADS_STRUCT *ads,
2434 const char *machine_name,
2435 char ***spn_array,
2436 size_t *num_spns)
2438 ADS_STATUS status;
2439 LDAPMessage *res = NULL;
2440 int count;
2442 status = ads_find_machine_acct(ads,
2443 &res,
2444 machine_name);
2445 if (!ADS_ERR_OK(status)) {
2446 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2447 machine_name));
2448 return status;
2451 count = ads_count_replies(ads, res);
2452 if (count != 1) {
2453 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2454 goto done;
2457 *spn_array = ads_pull_strings(ads,
2458 mem_ctx,
2459 res,
2460 "servicePrincipalName",
2461 num_spns);
2462 if (*spn_array == NULL) {
2463 DEBUG(1, ("Host account for %s does not have service principal "
2464 "names.\n",
2465 machine_name));
2466 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2467 goto done;
2470 done:
2471 ads_msgfree(ads, res);
2473 return status;
2477 * This adds a service principal name to an existing computer account
2478 * (found by hostname) in AD.
2479 * @param ads An initialized ADS_STRUCT
2480 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2481 * @param spns An array or strings for the service principals to add,
2482 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2483 * @return 0 upon success, or non-zero if a failure occurs
2486 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2487 const char *machine_name,
2488 const char **spns)
2490 ADS_STATUS ret;
2491 TALLOC_CTX *ctx;
2492 LDAPMessage *res = NULL;
2493 ADS_MODLIST mods;
2494 char *dn_string = NULL;
2495 const char **servicePrincipalName = spns;
2497 ret = ads_find_machine_acct(ads, &res, machine_name);
2498 if (!ADS_ERR_OK(ret)) {
2499 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2500 machine_name));
2501 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2502 ads_msgfree(ads, res);
2503 return ret;
2506 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2507 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2508 ads_msgfree(ads, res);
2509 return ADS_ERROR(LDAP_NO_MEMORY);
2512 DEBUG(5,("ads_add_service_principal_name: INFO: "
2513 "Adding %s to host %s\n",
2514 spns[0] ? "N/A" : spns[0], machine_name));
2517 DEBUG(5,("ads_add_service_principal_name: INFO: "
2518 "Adding %s to host %s\n",
2519 spns[1] ? "N/A" : spns[1], machine_name));
2521 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2522 ret = ADS_ERROR(LDAP_NO_MEMORY);
2523 goto out;
2526 ret = ads_add_strlist(ctx,
2527 &mods,
2528 "servicePrincipalName",
2529 servicePrincipalName);
2530 if (!ADS_ERR_OK(ret)) {
2531 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2532 goto out;
2535 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2536 ret = ADS_ERROR(LDAP_NO_MEMORY);
2537 goto out;
2540 ret = ads_gen_mod(ads, dn_string, mods);
2541 if (!ADS_ERR_OK(ret)) {
2542 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2543 goto out;
2546 out:
2547 TALLOC_FREE( ctx );
2548 ads_msgfree(ads, res);
2549 return ret;
2552 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2553 LDAPMessage *msg)
2555 uint32_t acct_ctrl = 0;
2556 bool ok;
2558 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2559 if (!ok) {
2560 return 0;
2563 return acct_ctrl;
2566 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2567 LDAPMessage *msg,
2568 const struct berval *machine_pw_val)
2570 ADS_MODLIST mods;
2571 ADS_STATUS ret;
2572 TALLOC_CTX *frame = talloc_stackframe();
2573 uint32_t acct_control;
2574 char *control_str = NULL;
2575 const char *attrs[] = {
2576 "objectSid",
2577 NULL
2579 LDAPMessage *res = NULL;
2580 char *dn = NULL;
2582 dn = ads_get_dn(ads, frame, msg);
2583 if (dn == NULL) {
2584 ret = ADS_ERROR(LDAP_NO_MEMORY);
2585 goto done;
2588 acct_control = ads_get_acct_ctrl(ads, msg);
2589 if (acct_control == 0) {
2590 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2591 goto done;
2595 * Changing the password, disables the account. So we need to change the
2596 * userAccountControl flags to enable it again.
2598 mods = ads_init_mods(frame);
2599 if (mods == NULL) {
2600 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2601 goto done;
2604 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2606 ret = ads_gen_mod(ads, dn, mods);
2607 if (!ADS_ERR_OK(ret)) {
2608 goto done;
2610 TALLOC_FREE(mods);
2613 * To activate the account, we need to disable and enable it.
2615 acct_control |= UF_ACCOUNTDISABLE;
2617 control_str = talloc_asprintf(frame, "%u", acct_control);
2618 if (control_str == NULL) {
2619 ret = ADS_ERROR(LDAP_NO_MEMORY);
2620 goto done;
2623 mods = ads_init_mods(frame);
2624 if (mods == NULL) {
2625 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2626 goto done;
2629 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2631 ret = ads_gen_mod(ads, dn, mods);
2632 if (!ADS_ERR_OK(ret)) {
2633 goto done;
2635 TALLOC_FREE(mods);
2636 TALLOC_FREE(control_str);
2639 * Enable the account again.
2641 acct_control &= ~UF_ACCOUNTDISABLE;
2643 control_str = talloc_asprintf(frame, "%u", acct_control);
2644 if (control_str == NULL) {
2645 ret = ADS_ERROR(LDAP_NO_MEMORY);
2646 goto done;
2649 mods = ads_init_mods(frame);
2650 if (mods == NULL) {
2651 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2652 goto done;
2655 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2657 ret = ads_gen_mod(ads, dn, mods);
2658 if (!ADS_ERR_OK(ret)) {
2659 goto done;
2661 TALLOC_FREE(mods);
2662 TALLOC_FREE(control_str);
2664 ret = ads_search_dn(ads, &res, dn, attrs);
2665 ads_msgfree(ads, res);
2667 done:
2668 talloc_free(frame);
2670 return ret;
2674 * adds a machine account to the ADS server
2675 * @param ads An initialized ADS_STRUCT
2676 * @param machine_name - the NetBIOS machine name of this account.
2677 * @param account_type A number indicating the type of account to create
2678 * @param org_unit The LDAP path in which to place this account
2679 * @return 0 upon success, or non-zero otherwise
2682 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2683 const char *machine_name,
2684 const char *machine_password,
2685 const char *org_unit,
2686 uint32_t etype_list,
2687 const char *dns_domain_name)
2689 ADS_STATUS ret;
2690 char *samAccountName = NULL;
2691 char *controlstr = NULL;
2692 TALLOC_CTX *ctx = NULL;
2693 ADS_MODLIST mods;
2694 char *machine_escaped = NULL;
2695 char *dns_hostname = NULL;
2696 char *new_dn = NULL;
2697 char *utf8_pw = NULL;
2698 size_t utf8_pw_len = 0;
2699 char *utf16_pw = NULL;
2700 size_t utf16_pw_len = 0;
2701 struct berval machine_pw_val;
2702 bool ok;
2703 const char **spn_array = NULL;
2704 size_t num_spns = 0;
2705 const char *spn_prefix[] = {
2706 "HOST",
2707 "RestrictedKrbHost",
2709 size_t i;
2710 LDAPMessage *res = NULL;
2711 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2713 ctx = talloc_init("ads_add_machine_acct");
2714 if (ctx == NULL) {
2715 return ADS_ERROR(LDAP_NO_MEMORY);
2718 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2719 if (machine_escaped == NULL) {
2720 ret = ADS_ERROR(LDAP_NO_MEMORY);
2721 goto done;
2724 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2725 if (utf8_pw == NULL) {
2726 ret = ADS_ERROR(LDAP_NO_MEMORY);
2727 goto done;
2729 utf8_pw_len = strlen(utf8_pw);
2731 ok = convert_string_talloc(ctx,
2732 CH_UTF8, CH_UTF16MUNGED,
2733 utf8_pw, utf8_pw_len,
2734 (void *)&utf16_pw, &utf16_pw_len);
2735 if (!ok) {
2736 ret = ADS_ERROR(LDAP_NO_MEMORY);
2737 goto done;
2740 machine_pw_val = (struct berval) {
2741 .bv_val = utf16_pw,
2742 .bv_len = utf16_pw_len,
2745 /* Check if the machine account already exists. */
2746 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2747 if (ADS_ERR_OK(ret)) {
2748 /* Change the machine account password */
2749 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2750 ads_msgfree(ads, res);
2752 goto done;
2754 ads_msgfree(ads, res);
2756 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2757 if (new_dn == NULL) {
2758 ret = ADS_ERROR(LDAP_NO_MEMORY);
2759 goto done;
2762 /* Create machine account */
2764 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2765 if (samAccountName == NULL) {
2766 ret = ADS_ERROR(LDAP_NO_MEMORY);
2767 goto done;
2770 dns_hostname = talloc_asprintf(ctx,
2771 "%s.%s",
2772 machine_name,
2773 dns_domain_name);
2774 if (dns_hostname == NULL) {
2775 ret = ADS_ERROR(LDAP_NO_MEMORY);
2776 goto done;
2779 /* Add dns_hostname SPNs */
2780 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2781 char *spn = talloc_asprintf(ctx,
2782 "%s/%s",
2783 spn_prefix[i],
2784 dns_hostname);
2785 if (spn == NULL) {
2786 ret = ADS_ERROR(LDAP_NO_MEMORY);
2787 goto done;
2790 ok = add_string_to_array(ctx,
2791 spn,
2792 &spn_array,
2793 &num_spns);
2794 if (!ok) {
2795 ret = ADS_ERROR(LDAP_NO_MEMORY);
2796 goto done;
2800 /* Add machine_name SPNs */
2801 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2802 char *spn = talloc_asprintf(ctx,
2803 "%s/%s",
2804 spn_prefix[i],
2805 machine_name);
2806 if (spn == NULL) {
2807 ret = ADS_ERROR(LDAP_NO_MEMORY);
2808 goto done;
2811 ok = add_string_to_array(ctx,
2812 spn,
2813 &spn_array,
2814 &num_spns);
2815 if (!ok) {
2816 ret = ADS_ERROR(LDAP_NO_MEMORY);
2817 goto done;
2821 /* Make sure to NULL terminate the array */
2822 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2823 if (spn_array == NULL) {
2824 ret = ADS_ERROR(LDAP_NO_MEMORY);
2825 goto done;
2827 spn_array[num_spns] = NULL;
2829 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2830 if (controlstr == NULL) {
2831 ret = ADS_ERROR(LDAP_NO_MEMORY);
2832 goto done;
2835 mods = ads_init_mods(ctx);
2836 if (mods == NULL) {
2837 ret = ADS_ERROR(LDAP_NO_MEMORY);
2838 goto done;
2841 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2842 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2843 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2844 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2845 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2846 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2848 ret = ads_gen_add(ads, new_dn, mods);
2850 done:
2851 SAFE_FREE(machine_escaped);
2852 talloc_destroy(ctx);
2854 return ret;
2858 * move a machine account to another OU on the ADS server
2859 * @param ads - An initialized ADS_STRUCT
2860 * @param machine_name - the NetBIOS machine name of this account.
2861 * @param org_unit - The LDAP path in which to place this account
2862 * @param moved - whether we moved the machine account (optional)
2863 * @return 0 upon success, or non-zero otherwise
2866 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2867 const char *org_unit, bool *moved)
2869 ADS_STATUS rc;
2870 int ldap_status;
2871 LDAPMessage *res = NULL;
2872 char *filter = NULL;
2873 char *computer_dn = NULL;
2874 char *parent_dn;
2875 char *computer_rdn = NULL;
2876 bool need_move = False;
2878 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2879 rc = ADS_ERROR(LDAP_NO_MEMORY);
2880 goto done;
2883 /* Find pre-existing machine */
2884 rc = ads_search(ads, &res, filter, NULL);
2885 if (!ADS_ERR_OK(rc)) {
2886 goto done;
2889 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2890 if (!computer_dn) {
2891 rc = ADS_ERROR(LDAP_NO_MEMORY);
2892 goto done;
2895 parent_dn = ads_parent_dn(computer_dn);
2896 if (strequal(parent_dn, org_unit)) {
2897 goto done;
2900 need_move = True;
2902 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2903 rc = ADS_ERROR(LDAP_NO_MEMORY);
2904 goto done;
2907 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2908 org_unit, 1, NULL, NULL);
2909 rc = ADS_ERROR(ldap_status);
2911 done:
2912 ads_msgfree(ads, res);
2913 SAFE_FREE(filter);
2914 TALLOC_FREE(computer_dn);
2915 SAFE_FREE(computer_rdn);
2917 if (!ADS_ERR_OK(rc)) {
2918 need_move = False;
2921 if (moved) {
2922 *moved = need_move;
2925 return rc;
2929 dump a binary result from ldap
2931 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2933 size_t i;
2934 for (i=0; values[i]; i++) {
2935 ber_len_t j;
2936 printf("%s: ", field);
2937 for (j=0; j<values[i]->bv_len; j++) {
2938 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2940 printf("\n");
2944 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2946 int i;
2947 for (i=0; values[i]; i++) {
2948 NTSTATUS status;
2949 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2950 struct GUID guid;
2952 status = GUID_from_ndr_blob(&in, &guid);
2953 if (NT_STATUS_IS_OK(status)) {
2954 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2955 } else {
2956 printf("%s: INVALID GUID\n", field);
2962 dump a sid result from ldap
2964 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2966 int i;
2967 for (i=0; values[i]; i++) {
2968 ssize_t ret;
2969 struct dom_sid sid;
2970 struct dom_sid_buf tmp;
2971 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2972 values[i]->bv_len, &sid);
2973 if (ret == -1) {
2974 return;
2976 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2981 dump ntSecurityDescriptor
2983 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2985 TALLOC_CTX *frame = talloc_stackframe();
2986 struct security_descriptor *psd;
2987 NTSTATUS status;
2989 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2990 values[0]->bv_len, &psd);
2991 if (!NT_STATUS_IS_OK(status)) {
2992 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2993 nt_errstr(status)));
2994 TALLOC_FREE(frame);
2995 return;
2998 if (psd) {
2999 ads_disp_sd(ads, talloc_tos(), psd);
3002 TALLOC_FREE(frame);
3006 dump a string result from ldap
3008 static void dump_string(const char *field, char **values)
3010 int i;
3011 for (i=0; values[i]; i++) {
3012 printf("%s: %s\n", field, values[i]);
3017 dump a field from LDAP on stdout
3018 used for debugging
3021 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
3023 const struct {
3024 const char *name;
3025 bool string;
3026 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
3027 } handlers[] = {
3028 {"objectGUID", False, dump_guid},
3029 {"netbootGUID", False, dump_guid},
3030 {"nTSecurityDescriptor", False, dump_sd},
3031 {"dnsRecord", False, dump_binary},
3032 {"objectSid", False, dump_sid},
3033 {"securityIdentifier", False, dump_sid},
3034 {"tokenGroups", False, dump_sid},
3035 {"tokenGroupsNoGCAcceptable", False, dump_sid},
3036 {"tokengroupsGlobalandUniversal", False, dump_sid},
3037 {"mS-DS-CreatorSID", False, dump_sid},
3038 {"msExchMailboxGuid", False, dump_guid},
3039 {"msDS-TrustForestTrustInfo", False, dump_binary},
3040 {NULL, True, NULL}
3042 int i;
3044 if (!field) { /* must be end of an entry */
3045 printf("\n");
3046 return False;
3049 for (i=0; handlers[i].name; i++) {
3050 if (strcasecmp_m(handlers[i].name, field) == 0) {
3051 if (!values) /* first time, indicate string or not */
3052 return handlers[i].string;
3053 handlers[i].handler(ads, field, (struct berval **) values);
3054 break;
3057 if (!handlers[i].name) {
3058 if (!values) /* first time, indicate string conversion */
3059 return True;
3060 dump_string(field, (char **)values);
3062 return False;
3066 * Dump a result from LDAP on stdout
3067 * used for debugging
3068 * @param ads connection to ads server
3069 * @param res Results to dump
3072 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
3074 ads_process_results(ads, res, ads_dump_field, NULL);
3078 * Walk through results, calling a function for each entry found.
3079 * The function receives a field name, a berval * array of values,
3080 * and a data area passed through from the start. The function is
3081 * called once with null for field and values at the end of each
3082 * entry.
3083 * @param ads connection to ads server
3084 * @param res Results to process
3085 * @param fn Function for processing each result
3086 * @param data_area user-defined area to pass to function
3088 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
3089 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
3090 void *data_area)
3092 LDAPMessage *msg;
3093 TALLOC_CTX *ctx;
3094 size_t converted_size;
3096 if (!(ctx = talloc_init("ads_process_results")))
3097 return;
3099 for (msg = ads_first_entry(ads, res); msg;
3100 msg = ads_next_entry(ads, msg)) {
3101 char *utf8_field;
3102 BerElement *b;
3104 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
3105 (LDAPMessage *)msg,&b);
3106 utf8_field;
3107 utf8_field=ldap_next_attribute(ads->ldap.ld,
3108 (LDAPMessage *)msg,b)) {
3109 struct berval **ber_vals;
3110 char **str_vals;
3111 char **utf8_vals;
3112 char *field;
3113 bool string;
3115 if (!pull_utf8_talloc(ctx, &field, utf8_field,
3116 &converted_size))
3118 DEBUG(0,("ads_process_results: "
3119 "pull_utf8_talloc failed: %s\n",
3120 strerror(errno)));
3123 string = fn(ads, field, NULL, data_area);
3125 if (string) {
3126 const char **p;
3128 utf8_vals = ldap_get_values(ads->ldap.ld,
3129 (LDAPMessage *)msg, field);
3130 p = discard_const_p(const char *, utf8_vals);
3131 str_vals = ads_pull_strvals(ctx, p);
3132 fn(ads, field, (void **) str_vals, data_area);
3133 ldap_value_free(utf8_vals);
3134 } else {
3135 ber_vals = ldap_get_values_len(ads->ldap.ld,
3136 (LDAPMessage *)msg, field);
3137 fn(ads, field, (void **) ber_vals, data_area);
3139 ldap_value_free_len(ber_vals);
3141 ldap_memfree(utf8_field);
3143 ber_free(b, 0);
3144 talloc_free_children(ctx);
3145 fn(ads, NULL, NULL, data_area); /* completed an entry */
3148 talloc_destroy(ctx);
3152 * count how many replies are in a LDAPMessage
3153 * @param ads connection to ads server
3154 * @param res Results to count
3155 * @return number of replies
3157 int ads_count_replies(ADS_STRUCT *ads, void *res)
3159 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3163 * pull the first entry from a ADS result
3164 * @param ads connection to ads server
3165 * @param res Results of search
3166 * @return first entry from result
3168 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3170 return ldap_first_entry(ads->ldap.ld, res);
3174 * pull the next entry from a ADS result
3175 * @param ads connection to ads server
3176 * @param res Results of search
3177 * @return next entry from result
3179 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3181 return ldap_next_entry(ads->ldap.ld, res);
3185 * pull the first message from a ADS result
3186 * @param ads connection to ads server
3187 * @param res Results of search
3188 * @return first message from result
3190 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3192 return ldap_first_message(ads->ldap.ld, res);
3196 * pull the next message from a ADS result
3197 * @param ads connection to ads server
3198 * @param res Results of search
3199 * @return next message from result
3201 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3203 return ldap_next_message(ads->ldap.ld, res);
3207 * pull a single string from a ADS result
3208 * @param ads connection to ads server
3209 * @param mem_ctx TALLOC_CTX to use for allocating result string
3210 * @param msg Results of search
3211 * @param field Attribute to retrieve
3212 * @return Result string in talloc context
3214 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3215 const char *field)
3217 char **values;
3218 char *ret = NULL;
3219 char *ux_string;
3220 size_t converted_size;
3222 values = ldap_get_values(ads->ldap.ld, msg, field);
3223 if (!values)
3224 return NULL;
3226 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3227 &converted_size))
3229 ret = ux_string;
3231 ldap_value_free(values);
3232 return ret;
3236 * pull an array of strings from a ADS result
3237 * @param ads connection to ads server
3238 * @param mem_ctx TALLOC_CTX to use for allocating result string
3239 * @param msg Results of search
3240 * @param field Attribute to retrieve
3241 * @return Result strings in talloc context
3243 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3244 LDAPMessage *msg, const char *field,
3245 size_t *num_values)
3247 char **values;
3248 char **ret = NULL;
3249 size_t i, converted_size;
3251 values = ldap_get_values(ads->ldap.ld, msg, field);
3252 if (!values)
3253 return NULL;
3255 *num_values = ldap_count_values(values);
3257 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3258 if (!ret) {
3259 ldap_value_free(values);
3260 return NULL;
3263 for (i=0;i<*num_values;i++) {
3264 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3265 &converted_size))
3267 ldap_value_free(values);
3268 return NULL;
3271 ret[i] = NULL;
3273 ldap_value_free(values);
3274 return ret;
3278 * pull an array of strings from a ADS result
3279 * (handle large multivalue attributes with range retrieval)
3280 * @param ads connection to ads server
3281 * @param mem_ctx TALLOC_CTX to use for allocating result string
3282 * @param msg Results of search
3283 * @param field Attribute to retrieve
3284 * @param current_strings strings returned by a previous call to this function
3285 * @param next_attribute The next query should ask for this attribute
3286 * @param num_values How many values did we get this time?
3287 * @param more_values Are there more values to get?
3288 * @return Result strings in talloc context
3290 char **ads_pull_strings_range(ADS_STRUCT *ads,
3291 TALLOC_CTX *mem_ctx,
3292 LDAPMessage *msg, const char *field,
3293 char **current_strings,
3294 const char **next_attribute,
3295 size_t *num_strings,
3296 bool *more_strings)
3298 char *attr;
3299 char *expected_range_attrib, *range_attr = NULL;
3300 BerElement *ptr = NULL;
3301 char **strings;
3302 char **new_strings;
3303 size_t num_new_strings;
3304 unsigned long int range_start;
3305 unsigned long int range_end;
3307 /* we might have been given the whole lot anyway */
3308 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3309 *more_strings = False;
3310 return strings;
3313 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3315 /* look for Range result */
3316 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3317 attr;
3318 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3319 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3320 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3321 range_attr = attr;
3322 break;
3324 ldap_memfree(attr);
3326 if (!range_attr) {
3327 ber_free(ptr, 0);
3328 /* nothing here - this field is just empty */
3329 *more_strings = False;
3330 return NULL;
3333 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3334 &range_start, &range_end) == 2) {
3335 *more_strings = True;
3336 } else {
3337 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3338 &range_start) == 1) {
3339 *more_strings = False;
3340 } else {
3341 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3342 range_attr));
3343 ldap_memfree(range_attr);
3344 *more_strings = False;
3345 return NULL;
3349 if ((*num_strings) != range_start) {
3350 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3351 " - aborting range retrieval\n",
3352 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3353 ldap_memfree(range_attr);
3354 *more_strings = False;
3355 return NULL;
3358 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3360 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3361 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3362 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3363 range_attr, (unsigned long int)range_end - range_start + 1,
3364 (unsigned long int)num_new_strings));
3365 ldap_memfree(range_attr);
3366 *more_strings = False;
3367 return NULL;
3370 strings = talloc_realloc(mem_ctx, current_strings, char *,
3371 *num_strings + num_new_strings);
3373 if (strings == NULL) {
3374 ldap_memfree(range_attr);
3375 *more_strings = False;
3376 return NULL;
3379 if (new_strings && num_new_strings) {
3380 memcpy(&strings[*num_strings], new_strings,
3381 sizeof(*new_strings) * num_new_strings);
3384 (*num_strings) += num_new_strings;
3386 if (*more_strings) {
3387 *next_attribute = talloc_asprintf(mem_ctx,
3388 "%s;range=%d-*",
3389 field,
3390 (int)*num_strings);
3392 if (!*next_attribute) {
3393 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3394 ldap_memfree(range_attr);
3395 *more_strings = False;
3396 return NULL;
3400 ldap_memfree(range_attr);
3402 return strings;
3406 * pull a single uint32_t from a ADS result
3407 * @param ads connection to ads server
3408 * @param msg Results of search
3409 * @param field Attribute to retrieve
3410 * @param v Pointer to int to store result
3411 * @return boolean indicating success
3413 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3414 uint32_t *v)
3416 char **values;
3418 values = ldap_get_values(ads->ldap.ld, msg, field);
3419 if (!values)
3420 return False;
3421 if (!values[0]) {
3422 ldap_value_free(values);
3423 return False;
3426 *v = atoi(values[0]);
3427 ldap_value_free(values);
3428 return True;
3432 * pull a single objectGUID from an ADS result
3433 * @param ads connection to ADS server
3434 * @param msg results of search
3435 * @param guid 37-byte area to receive text guid
3436 * @return boolean indicating success
3438 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3440 DATA_BLOB blob;
3441 NTSTATUS status;
3443 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3444 &blob)) {
3445 return false;
3448 status = GUID_from_ndr_blob(&blob, guid);
3449 talloc_free(blob.data);
3450 return NT_STATUS_IS_OK(status);
3455 * pull a single struct dom_sid from a ADS result
3456 * @param ads connection to ads server
3457 * @param msg Results of search
3458 * @param field Attribute to retrieve
3459 * @param sid Pointer to sid to store result
3460 * @return boolean indicating success
3462 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3463 struct dom_sid *sid)
3465 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3469 * pull an array of struct dom_sids from a ADS result
3470 * @param ads connection to ads server
3471 * @param mem_ctx TALLOC_CTX for allocating sid array
3472 * @param msg Results of search
3473 * @param field Attribute to retrieve
3474 * @param sids pointer to sid array to allocate
3475 * @return the count of SIDs pulled
3477 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3478 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3480 struct berval **values;
3481 int count, i;
3483 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3485 if (!values)
3486 return 0;
3488 for (i=0; values[i]; i++)
3489 /* nop */ ;
3491 if (i) {
3492 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3493 if (!(*sids)) {
3494 ldap_value_free_len(values);
3495 return 0;
3497 } else {
3498 (*sids) = NULL;
3501 count = 0;
3502 for (i=0; values[i]; i++) {
3503 ssize_t ret;
3504 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3505 values[i]->bv_len, &(*sids)[count]);
3506 if (ret != -1) {
3507 struct dom_sid_buf buf;
3508 DBG_DEBUG("pulling SID: %s\n",
3509 dom_sid_str_buf(&(*sids)[count], &buf));
3510 count++;
3514 ldap_value_free_len(values);
3515 return count;
3519 * pull a struct security_descriptor from a ADS result
3520 * @param ads connection to ads server
3521 * @param mem_ctx TALLOC_CTX for allocating sid array
3522 * @param msg Results of search
3523 * @param field Attribute to retrieve
3524 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3525 * @return boolean indicating success
3527 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3528 LDAPMessage *msg, const char *field,
3529 struct security_descriptor **sd)
3531 struct berval **values;
3532 bool ret = true;
3534 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3536 if (!values) return false;
3538 if (values[0]) {
3539 NTSTATUS status;
3540 status = unmarshall_sec_desc(mem_ctx,
3541 (uint8_t *)values[0]->bv_val,
3542 values[0]->bv_len, sd);
3543 if (!NT_STATUS_IS_OK(status)) {
3544 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3545 nt_errstr(status)));
3546 ret = false;
3550 ldap_value_free_len(values);
3551 return ret;
3555 * in order to support usernames longer than 21 characters we need to
3556 * use both the sAMAccountName and the userPrincipalName attributes
3557 * It seems that not all users have the userPrincipalName attribute set
3559 * @param ads connection to ads server
3560 * @param mem_ctx TALLOC_CTX for allocating sid array
3561 * @param msg Results of search
3562 * @return the username
3564 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3565 LDAPMessage *msg)
3567 #if 0 /* JERRY */
3568 char *ret, *p;
3570 /* lookup_name() only works on the sAMAccountName to
3571 returning the username portion of userPrincipalName
3572 breaks winbindd_getpwnam() */
3574 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3575 if (ret && (p = strchr_m(ret, '@'))) {
3576 *p = 0;
3577 return ret;
3579 #endif
3580 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3585 * find the update serial number - this is the core of the ldap cache
3586 * @param ads connection to ads server
3587 * @param ads connection to ADS server
3588 * @param usn Pointer to retrieved update serial number
3589 * @return status of search
3591 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3593 const char *attrs[] = {"highestCommittedUSN", NULL};
3594 ADS_STATUS status;
3595 LDAPMessage *res;
3597 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3598 if (!ADS_ERR_OK(status))
3599 return status;
3601 if (ads_count_replies(ads, res) != 1) {
3602 ads_msgfree(ads, res);
3603 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3606 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3607 ads_msgfree(ads, res);
3608 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3611 ads_msgfree(ads, res);
3612 return ADS_SUCCESS;
3615 /* parse a ADS timestring - typical string is
3616 '20020917091222.0Z0' which means 09:12.22 17th September
3617 2002, timezone 0 */
3618 static time_t ads_parse_time(const char *str)
3620 struct tm tm;
3622 ZERO_STRUCT(tm);
3624 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3625 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3626 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3627 return 0;
3629 tm.tm_year -= 1900;
3630 tm.tm_mon -= 1;
3632 return timegm(&tm);
3635 /********************************************************************
3636 ********************************************************************/
3638 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3640 const char *attrs[] = {"currentTime", NULL};
3641 ADS_STATUS status;
3642 LDAPMessage *res;
3643 char *timestr;
3644 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3645 ADS_STRUCT *ads_s = ads;
3647 /* establish a new ldap tcp session if necessary */
3649 if ( !ads->ldap.ld ) {
3651 * ADS_STRUCT may be being reused after a
3652 * DC lookup, so ads->ldap.ss may already have a
3653 * good address. If not, re-initialize the passed-in
3654 * ADS_STRUCT with the given server.XXXX parameters.
3656 * Note that this doesn't depend on
3657 * ads->server.ldap_server != NULL,
3658 * as the case where ads->server.ldap_server==NULL and
3659 * ads->ldap.ss != zero_address is precisely the DC
3660 * lookup case where ads->ldap.ss was found by going
3661 * through ads_find_dc() again we want to avoid repeating.
3663 if (is_zero_addr(&ads->ldap.ss)) {
3664 ads_s = ads_init(tmp_ctx,
3665 ads->server.realm,
3666 ads->server.workgroup,
3667 ads->server.ldap_server,
3668 ADS_SASL_PLAIN );
3669 if (ads_s == NULL) {
3670 status = ADS_ERROR(LDAP_NO_MEMORY);
3671 goto done;
3676 * Reset ads->config.flags as it can contain the flags
3677 * returned by the previous CLDAP ping when reusing the struct.
3679 ads_s->config.flags = 0;
3681 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3682 status = ads_connect( ads_s );
3683 if ( !ADS_ERR_OK(status))
3684 goto done;
3687 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3688 if (!ADS_ERR_OK(status)) {
3689 goto done;
3692 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3693 if (!timestr) {
3694 ads_msgfree(ads_s, res);
3695 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3696 goto done;
3699 /* but save the time and offset in the original ADS_STRUCT */
3701 ads->config.current_time = ads_parse_time(timestr);
3703 if (ads->config.current_time != 0) {
3704 ads->config.time_offset = ads->config.current_time - time(NULL);
3705 DBG_INFO("server time offset is %d seconds\n",
3706 ads->config.time_offset);
3707 } else {
3708 ads->config.time_offset = 0;
3711 DBG_INFO("server time offset is %d seconds\n",
3712 ads->config.time_offset);
3714 ads_msgfree(ads, res);
3716 status = ADS_SUCCESS;
3718 done:
3719 TALLOC_FREE(tmp_ctx);
3721 return status;
3724 /********************************************************************
3725 ********************************************************************/
3727 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3729 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3730 const char *attrs[] = {"domainFunctionality", NULL};
3731 ADS_STATUS status;
3732 LDAPMessage *res;
3733 ADS_STRUCT *ads_s = ads;
3735 *val = DS_DOMAIN_FUNCTION_2000;
3737 /* establish a new ldap tcp session if necessary */
3739 if ( !ads->ldap.ld ) {
3741 * ADS_STRUCT may be being reused after a
3742 * DC lookup, so ads->ldap.ss may already have a
3743 * good address. If not, re-initialize the passed-in
3744 * ADS_STRUCT with the given server.XXXX parameters.
3746 * Note that this doesn't depend on
3747 * ads->server.ldap_server != NULL,
3748 * as the case where ads->server.ldap_server==NULL and
3749 * ads->ldap.ss != zero_address is precisely the DC
3750 * lookup case where ads->ldap.ss was found by going
3751 * through ads_find_dc() again we want to avoid repeating.
3753 if (is_zero_addr(&ads->ldap.ss)) {
3754 ads_s = ads_init(tmp_ctx,
3755 ads->server.realm,
3756 ads->server.workgroup,
3757 ads->server.ldap_server,
3758 ADS_SASL_PLAIN );
3759 if (ads_s == NULL ) {
3760 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3761 goto done;
3766 * Reset ads->config.flags as it can contain the flags
3767 * returned by the previous CLDAP ping when reusing the struct.
3769 ads_s->config.flags = 0;
3771 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3772 status = ads_connect( ads_s );
3773 if ( !ADS_ERR_OK(status))
3774 goto done;
3777 /* If the attribute does not exist assume it is a Windows 2000
3778 functional domain */
3780 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3781 if (!ADS_ERR_OK(status)) {
3782 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3783 status = ADS_SUCCESS;
3785 goto done;
3788 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3789 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3791 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3794 ads_msgfree(ads_s, res);
3796 done:
3797 TALLOC_FREE(tmp_ctx);
3799 return status;
3803 * find the domain sid for our domain
3804 * @param ads connection to ads server
3805 * @param sid Pointer to domain sid
3806 * @return status of search
3808 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3810 const char *attrs[] = {"objectSid", NULL};
3811 LDAPMessage *res;
3812 ADS_STATUS rc;
3814 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3815 attrs, &res);
3816 if (!ADS_ERR_OK(rc)) return rc;
3817 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3818 ads_msgfree(ads, res);
3819 return ADS_ERROR_SYSTEM(ENOENT);
3821 ads_msgfree(ads, res);
3823 return ADS_SUCCESS;
3827 * find our site name
3828 * @param ads connection to ads server
3829 * @param mem_ctx Pointer to talloc context
3830 * @param site_name Pointer to the sitename
3831 * @return status of search
3833 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3835 ADS_STATUS status;
3836 LDAPMessage *res;
3837 const char *dn, *service_name;
3838 const char *attrs[] = { "dsServiceName", NULL };
3840 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3841 if (!ADS_ERR_OK(status)) {
3842 return status;
3845 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3846 if (service_name == NULL) {
3847 ads_msgfree(ads, res);
3848 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3851 ads_msgfree(ads, res);
3853 /* go up three levels */
3854 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3855 if (dn == NULL) {
3856 return ADS_ERROR(LDAP_NO_MEMORY);
3859 *site_name = talloc_strdup(mem_ctx, dn);
3860 if (*site_name == NULL) {
3861 return ADS_ERROR(LDAP_NO_MEMORY);
3864 return status;
3866 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3871 * find the site dn where a machine resides
3872 * @param ads connection to ads server
3873 * @param mem_ctx Pointer to talloc context
3874 * @param computer_name name of the machine
3875 * @param site_name Pointer to the sitename
3876 * @return status of search
3878 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3880 ADS_STATUS status;
3881 LDAPMessage *res;
3882 const char *parent, *filter;
3883 char *config_context = NULL;
3884 char *dn;
3886 /* shortcut a query */
3887 if (strequal(computer_name, ads->config.ldap_server_name)) {
3888 return ads_site_dn(ads, mem_ctx, site_dn);
3891 status = ads_config_path(ads, mem_ctx, &config_context);
3892 if (!ADS_ERR_OK(status)) {
3893 return status;
3896 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3897 if (filter == NULL) {
3898 return ADS_ERROR(LDAP_NO_MEMORY);
3901 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3902 filter, NULL, &res);
3903 if (!ADS_ERR_OK(status)) {
3904 return status;
3907 if (ads_count_replies(ads, res) != 1) {
3908 ads_msgfree(ads, res);
3909 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3912 dn = ads_get_dn(ads, mem_ctx, res);
3913 if (dn == NULL) {
3914 ads_msgfree(ads, res);
3915 return ADS_ERROR(LDAP_NO_MEMORY);
3918 /* go up three levels */
3919 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3920 if (parent == NULL) {
3921 ads_msgfree(ads, res);
3922 TALLOC_FREE(dn);
3923 return ADS_ERROR(LDAP_NO_MEMORY);
3926 *site_dn = talloc_strdup(mem_ctx, parent);
3927 if (*site_dn == NULL) {
3928 ads_msgfree(ads, res);
3929 TALLOC_FREE(dn);
3930 return ADS_ERROR(LDAP_NO_MEMORY);
3933 TALLOC_FREE(dn);
3934 ads_msgfree(ads, res);
3936 return status;
3940 * get the upn suffixes for a domain
3941 * @param ads connection to ads server
3942 * @param mem_ctx Pointer to talloc context
3943 * @param suffixes Pointer to an array of suffixes
3944 * @param num_suffixes Pointer to the number of suffixes
3945 * @return status of search
3947 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3949 ADS_STATUS status;
3950 LDAPMessage *res;
3951 const char *base;
3952 char *config_context = NULL;
3953 const char *attrs[] = { "uPNSuffixes", NULL };
3955 status = ads_config_path(ads, mem_ctx, &config_context);
3956 if (!ADS_ERR_OK(status)) {
3957 return status;
3960 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3961 if (base == NULL) {
3962 return ADS_ERROR(LDAP_NO_MEMORY);
3965 status = ads_search_dn(ads, &res, base, attrs);
3966 if (!ADS_ERR_OK(status)) {
3967 return status;
3970 if (ads_count_replies(ads, res) != 1) {
3971 ads_msgfree(ads, res);
3972 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3975 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3976 if ((*suffixes) == NULL) {
3977 ads_msgfree(ads, res);
3978 return ADS_ERROR(LDAP_NO_MEMORY);
3981 ads_msgfree(ads, res);
3983 return status;
3987 * get the joinable ous for a domain
3988 * @param ads connection to ads server
3989 * @param mem_ctx Pointer to talloc context
3990 * @param ous Pointer to an array of ous
3991 * @param num_ous Pointer to the number of ous
3992 * @return status of search
3994 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3995 TALLOC_CTX *mem_ctx,
3996 char ***ous,
3997 size_t *num_ous)
3999 ADS_STATUS status;
4000 LDAPMessage *res = NULL;
4001 LDAPMessage *msg = NULL;
4002 const char *attrs[] = { "dn", NULL };
4003 int count = 0;
4005 status = ads_search(ads, &res,
4006 "(|(objectClass=domain)(objectclass=organizationalUnit))",
4007 attrs);
4008 if (!ADS_ERR_OK(status)) {
4009 return status;
4012 count = ads_count_replies(ads, res);
4013 if (count < 1) {
4014 ads_msgfree(ads, res);
4015 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4018 for (msg = ads_first_entry(ads, res); msg;
4019 msg = ads_next_entry(ads, msg)) {
4020 const char **p = discard_const_p(const char *, *ous);
4021 char *dn = NULL;
4023 dn = ads_get_dn(ads, talloc_tos(), msg);
4024 if (!dn) {
4025 ads_msgfree(ads, res);
4026 return ADS_ERROR(LDAP_NO_MEMORY);
4029 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
4030 TALLOC_FREE(dn);
4031 ads_msgfree(ads, res);
4032 return ADS_ERROR(LDAP_NO_MEMORY);
4035 TALLOC_FREE(dn);
4036 *ous = discard_const_p(char *, p);
4039 ads_msgfree(ads, res);
4041 return status;
4046 * pull a struct dom_sid from an extended dn string
4047 * @param mem_ctx TALLOC_CTX
4048 * @param extended_dn string
4049 * @param flags string type of extended_dn
4050 * @param sid pointer to a struct dom_sid
4051 * @return NT_STATUS_OK on success,
4052 * NT_INVALID_PARAMETER on error,
4053 * NT_STATUS_NOT_FOUND if no SID present
4055 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
4056 const char *extended_dn,
4057 enum ads_extended_dn_flags flags,
4058 struct dom_sid *sid)
4060 char *p, *q, *dn;
4062 if (!extended_dn) {
4063 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4066 /* otherwise extended_dn gets stripped off */
4067 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
4068 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4071 * ADS_EXTENDED_DN_HEX_STRING:
4072 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4074 * ADS_EXTENDED_DN_STRING (only with w2k3):
4075 * <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
4077 * Object with no SID, such as an Exchange Public Folder
4078 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4081 p = strchr(dn, ';');
4082 if (!p) {
4083 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4086 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
4087 DEBUG(5,("No SID present in extended dn\n"));
4088 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
4091 p += strlen(";<SID=");
4093 q = strchr(p, '>');
4094 if (!q) {
4095 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4098 *q = '\0';
4100 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
4102 switch (flags) {
4104 case ADS_EXTENDED_DN_STRING:
4105 if (!string_to_sid(sid, p)) {
4106 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4108 break;
4109 case ADS_EXTENDED_DN_HEX_STRING: {
4110 ssize_t ret;
4111 fstring buf;
4112 size_t buf_len;
4114 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
4115 if (buf_len == 0) {
4116 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4119 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
4120 if (ret == -1) {
4121 DEBUG(10,("failed to parse sid\n"));
4122 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4124 break;
4126 default:
4127 DEBUG(10,("unknown extended dn format\n"));
4128 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4131 return ADS_ERROR_NT(NT_STATUS_OK);
4134 /********************************************************************
4135 ********************************************************************/
4137 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4139 LDAPMessage *res = NULL;
4140 ADS_STATUS status;
4141 int count = 0;
4142 char *name = NULL;
4144 status = ads_find_machine_acct(ads, &res, machine_name);
4145 if (!ADS_ERR_OK(status)) {
4146 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4147 lp_netbios_name()));
4148 goto out;
4151 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4152 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4153 goto out;
4156 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4157 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4160 out:
4161 ads_msgfree(ads, res);
4163 return name;
4166 /********************************************************************
4167 ********************************************************************/
4169 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4170 LDAPMessage *msg, size_t *num_values)
4172 const char *field = "msDS-AdditionalDnsHostName";
4173 struct berval **values = NULL;
4174 char **ret = NULL;
4175 size_t i, converted_size;
4178 * Windows DC implicitly adds a short name for each FQDN added to
4179 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4180 * suffix "\0$" which we should ignore (see bug #14406).
4183 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4184 if (values == NULL) {
4185 return NULL;
4188 *num_values = ldap_count_values_len(values);
4190 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4191 if (ret == NULL) {
4192 ldap_value_free_len(values);
4193 return NULL;
4196 for (i = 0; i < *num_values; i++) {
4197 ret[i] = NULL;
4198 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4199 values[i]->bv_val,
4200 strnlen(values[i]->bv_val,
4201 values[i]->bv_len),
4202 &ret[i], &converted_size)) {
4203 ldap_value_free_len(values);
4204 return NULL;
4207 ret[i] = NULL;
4209 ldap_value_free_len(values);
4210 return ret;
4213 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4214 ADS_STRUCT *ads,
4215 const char *machine_name,
4216 char ***hostnames_array,
4217 size_t *num_hostnames)
4219 ADS_STATUS status;
4220 LDAPMessage *res = NULL;
4221 int count;
4223 status = ads_find_machine_acct(ads,
4224 &res,
4225 machine_name);
4226 if (!ADS_ERR_OK(status)) {
4227 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4228 machine_name));
4229 return status;
4232 count = ads_count_replies(ads, res);
4233 if (count != 1) {
4234 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4235 goto done;
4238 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4239 if (*hostnames_array == NULL) {
4240 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4241 machine_name));
4242 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4243 goto done;
4246 done:
4247 ads_msgfree(ads, res);
4249 return status;
4252 /********************************************************************
4253 ********************************************************************/
4255 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4257 LDAPMessage *res = NULL;
4258 ADS_STATUS status;
4259 int count = 0;
4260 char *name = NULL;
4262 status = ads_find_machine_acct(ads, &res, machine_name);
4263 if (!ADS_ERR_OK(status)) {
4264 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4265 lp_netbios_name()));
4266 goto out;
4269 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4270 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4271 goto out;
4274 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4275 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4278 out:
4279 ads_msgfree(ads, res);
4281 return name;
4284 /********************************************************************
4285 ********************************************************************/
4287 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4289 LDAPMessage *res = NULL;
4290 ADS_STATUS status;
4291 int count = 0;
4292 char *name = NULL;
4293 bool ok = false;
4295 status = ads_find_machine_acct(ads, &res, machine_name);
4296 if (!ADS_ERR_OK(status)) {
4297 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4298 lp_netbios_name()));
4299 goto out;
4302 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4303 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4304 goto out;
4307 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4308 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4311 out:
4312 ads_msgfree(ads, res);
4313 if (name != NULL) {
4314 ok = (strlen(name) > 0);
4316 TALLOC_FREE(name);
4317 return ok;
4320 #if 0
4322 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4325 * Join a machine to a realm
4326 * Creates the machine account and sets the machine password
4327 * @param ads connection to ads server
4328 * @param machine name of host to add
4329 * @param org_unit Organizational unit to place machine in
4330 * @return status of join
4332 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4333 uint32_t account_type, const char *org_unit)
4335 ADS_STATUS status;
4336 LDAPMessage *res = NULL;
4337 char *machine;
4339 /* machine name must be lowercase */
4340 machine = SMB_STRDUP(machine_name);
4341 strlower_m(machine);
4344 status = ads_find_machine_acct(ads, (void **)&res, machine);
4345 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4346 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4347 status = ads_leave_realm(ads, machine);
4348 if (!ADS_ERR_OK(status)) {
4349 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4350 machine, ads->config.realm));
4351 return status;
4355 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4356 if (!ADS_ERR_OK(status)) {
4357 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4358 SAFE_FREE(machine);
4359 return status;
4362 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4363 if (!ADS_ERR_OK(status)) {
4364 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4365 SAFE_FREE(machine);
4366 return status;
4369 SAFE_FREE(machine);
4370 ads_msgfree(ads, res);
4372 return status;
4374 #endif
4377 * Delete a machine from the realm
4378 * @param ads connection to ads server
4379 * @param hostname Machine to remove
4380 * @return status of delete
4382 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4384 ADS_STATUS status;
4385 void *msg;
4386 LDAPMessage *res;
4387 char *hostnameDN, *host;
4388 int rc;
4389 LDAPControl ldap_control;
4390 LDAPControl * pldap_control[2] = {NULL, NULL};
4392 pldap_control[0] = &ldap_control;
4393 memset(&ldap_control, 0, sizeof(LDAPControl));
4394 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4396 /* hostname must be lowercase */
4397 host = SMB_STRDUP(hostname);
4398 if (!strlower_m(host)) {
4399 SAFE_FREE(host);
4400 return ADS_ERROR_SYSTEM(EINVAL);
4403 status = ads_find_machine_acct(ads, &res, host);
4404 if (!ADS_ERR_OK(status)) {
4405 DEBUG(0, ("Host account for %s does not exist.\n", host));
4406 SAFE_FREE(host);
4407 return status;
4410 msg = ads_first_entry(ads, res);
4411 if (!msg) {
4412 SAFE_FREE(host);
4413 return ADS_ERROR_SYSTEM(ENOENT);
4416 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4417 if (hostnameDN == NULL) {
4418 SAFE_FREE(host);
4419 return ADS_ERROR_SYSTEM(ENOENT);
4422 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4423 if (rc) {
4424 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4425 }else {
4426 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4429 if (rc != LDAP_SUCCESS) {
4430 const char *attrs[] = { "cn", NULL };
4431 LDAPMessage *msg_sub;
4433 /* we only search with scope ONE, we do not expect any further
4434 * objects to be created deeper */
4436 status = ads_do_search_retry(ads, hostnameDN,
4437 LDAP_SCOPE_ONELEVEL,
4438 "(objectclass=*)", attrs, &res);
4440 if (!ADS_ERR_OK(status)) {
4441 SAFE_FREE(host);
4442 TALLOC_FREE(hostnameDN);
4443 return status;
4446 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4447 msg_sub = ads_next_entry(ads, msg_sub)) {
4449 char *dn = NULL;
4451 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4452 SAFE_FREE(host);
4453 TALLOC_FREE(hostnameDN);
4454 return ADS_ERROR(LDAP_NO_MEMORY);
4457 status = ads_del_dn(ads, dn);
4458 if (!ADS_ERR_OK(status)) {
4459 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4460 SAFE_FREE(host);
4461 TALLOC_FREE(dn);
4462 TALLOC_FREE(hostnameDN);
4463 return status;
4466 TALLOC_FREE(dn);
4469 /* there should be no subordinate objects anymore */
4470 status = ads_do_search_retry(ads, hostnameDN,
4471 LDAP_SCOPE_ONELEVEL,
4472 "(objectclass=*)", attrs, &res);
4474 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4475 SAFE_FREE(host);
4476 TALLOC_FREE(hostnameDN);
4477 return status;
4480 /* delete hostnameDN now */
4481 status = ads_del_dn(ads, hostnameDN);
4482 if (!ADS_ERR_OK(status)) {
4483 SAFE_FREE(host);
4484 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4485 TALLOC_FREE(hostnameDN);
4486 return status;
4490 TALLOC_FREE(hostnameDN);
4492 status = ads_find_machine_acct(ads, &res, host);
4493 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4494 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4495 DEBUG(3, ("Failed to remove host account.\n"));
4496 SAFE_FREE(host);
4497 return status;
4500 SAFE_FREE(host);
4501 return ADS_SUCCESS;
4505 * pull all token-sids from an LDAP dn
4506 * @param ads connection to ads server
4507 * @param mem_ctx TALLOC_CTX for allocating sid array
4508 * @param dn of LDAP object
4509 * @param user_sid pointer to struct dom_sid (objectSid)
4510 * @param primary_group_sid pointer to struct dom_sid (self composed)
4511 * @param sids pointer to sid array to allocate
4512 * @param num_sids counter of SIDs pulled
4513 * @return status of token query
4515 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4516 TALLOC_CTX *mem_ctx,
4517 const char *dn,
4518 struct dom_sid *user_sid,
4519 struct dom_sid *primary_group_sid,
4520 struct dom_sid **sids,
4521 size_t *num_sids)
4523 ADS_STATUS status;
4524 LDAPMessage *res = NULL;
4525 int count = 0;
4526 size_t tmp_num_sids;
4527 struct dom_sid *tmp_sids;
4528 struct dom_sid tmp_user_sid;
4529 struct dom_sid tmp_primary_group_sid;
4530 uint32_t pgid;
4531 const char *attrs[] = {
4532 "objectSid",
4533 "tokenGroups",
4534 "primaryGroupID",
4535 NULL
4538 status = ads_search_retry_dn(ads, &res, dn, attrs);
4539 if (!ADS_ERR_OK(status)) {
4540 return status;
4543 count = ads_count_replies(ads, res);
4544 if (count != 1) {
4545 ads_msgfree(ads, res);
4546 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4549 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4550 ads_msgfree(ads, res);
4551 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4554 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4555 ads_msgfree(ads, res);
4556 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4560 /* hack to compose the primary group sid without knowing the
4561 * domsid */
4563 struct dom_sid domsid;
4565 sid_copy(&domsid, &tmp_user_sid);
4567 if (!sid_split_rid(&domsid, NULL)) {
4568 ads_msgfree(ads, res);
4569 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4572 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4573 ads_msgfree(ads, res);
4574 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4578 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4580 if (tmp_num_sids == 0 || !tmp_sids) {
4581 ads_msgfree(ads, res);
4582 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4585 if (num_sids) {
4586 *num_sids = tmp_num_sids;
4589 if (sids) {
4590 *sids = tmp_sids;
4593 if (user_sid) {
4594 *user_sid = tmp_user_sid;
4597 if (primary_group_sid) {
4598 *primary_group_sid = tmp_primary_group_sid;
4601 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4603 ads_msgfree(ads, res);
4604 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4608 * Find a sAMAccountName in LDAP
4609 * @param ads connection to ads server
4610 * @param mem_ctx TALLOC_CTX for allocating sid array
4611 * @param samaccountname to search
4612 * @param uac_ret uint32_t pointer userAccountControl attribute value
4613 * @param dn_ret pointer to dn
4614 * @return status of token query
4616 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4617 TALLOC_CTX *mem_ctx,
4618 const char *samaccountname,
4619 uint32_t *uac_ret,
4620 const char **dn_ret)
4622 ADS_STATUS status;
4623 const char *attrs[] = { "userAccountControl", NULL };
4624 const char *filter;
4625 LDAPMessage *res = NULL;
4626 char *dn = NULL;
4627 uint32_t uac = 0;
4629 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4630 samaccountname);
4631 if (filter == NULL) {
4632 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4633 goto out;
4636 status = ads_do_search_all(ads, ads->config.bind_path,
4637 LDAP_SCOPE_SUBTREE,
4638 filter, attrs, &res);
4640 if (!ADS_ERR_OK(status)) {
4641 goto out;
4644 if (ads_count_replies(ads, res) != 1) {
4645 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4646 goto out;
4649 dn = ads_get_dn(ads, talloc_tos(), res);
4650 if (dn == NULL) {
4651 status = ADS_ERROR(LDAP_NO_MEMORY);
4652 goto out;
4655 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4656 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4657 goto out;
4660 if (uac_ret) {
4661 *uac_ret = uac;
4664 if (dn_ret) {
4665 *dn_ret = talloc_strdup(mem_ctx, dn);
4666 if (!*dn_ret) {
4667 status = ADS_ERROR(LDAP_NO_MEMORY);
4668 goto out;
4671 out:
4672 TALLOC_FREE(dn);
4673 ads_msgfree(ads, res);
4675 return status;
4679 * find our configuration path
4680 * @param ads connection to ads server
4681 * @param mem_ctx Pointer to talloc context
4682 * @param config_path Pointer to the config path
4683 * @return status of search
4685 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4686 TALLOC_CTX *mem_ctx,
4687 char **config_path)
4689 ADS_STATUS status;
4690 LDAPMessage *res = NULL;
4691 const char *config_context = NULL;
4692 const char *attrs[] = { "configurationNamingContext", NULL };
4694 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4695 "(objectclass=*)", attrs, &res);
4696 if (!ADS_ERR_OK(status)) {
4697 return status;
4700 config_context = ads_pull_string(ads, mem_ctx, res,
4701 "configurationNamingContext");
4702 ads_msgfree(ads, res);
4703 if (!config_context) {
4704 return ADS_ERROR(LDAP_NO_MEMORY);
4707 if (config_path) {
4708 *config_path = talloc_strdup(mem_ctx, config_context);
4709 if (!*config_path) {
4710 return ADS_ERROR(LDAP_NO_MEMORY);
4714 return ADS_ERROR(LDAP_SUCCESS);
4718 * find the displayName of an extended right
4719 * @param ads connection to ads server
4720 * @param config_path The config path
4721 * @param mem_ctx Pointer to talloc context
4722 * @param GUID struct of the rightsGUID
4723 * @return status of search
4725 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4726 const char *config_path,
4727 TALLOC_CTX *mem_ctx,
4728 const struct GUID *rights_guid)
4730 ADS_STATUS rc;
4731 LDAPMessage *res = NULL;
4732 char *expr = NULL;
4733 const char *attrs[] = { "displayName", NULL };
4734 const char *result = NULL;
4735 const char *path;
4737 if (!ads || !mem_ctx || !rights_guid) {
4738 goto done;
4741 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4742 GUID_string(mem_ctx, rights_guid));
4743 if (!expr) {
4744 goto done;
4747 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4748 if (!path) {
4749 goto done;
4752 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4753 expr, attrs, &res);
4754 if (!ADS_ERR_OK(rc)) {
4755 goto done;
4758 if (ads_count_replies(ads, res) != 1) {
4759 goto done;
4762 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4764 done:
4765 ads_msgfree(ads, res);
4766 return result;
4770 * verify or build and verify an account ou
4771 * @param mem_ctx Pointer to talloc context
4772 * @param ads connection to ads server
4773 * @param account_ou
4774 * @return status of search
4777 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4778 ADS_STRUCT *ads,
4779 const char **account_ou)
4781 char **exploded_dn;
4782 const char *name;
4783 char *ou_string;
4785 if (account_ou == NULL) {
4786 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4789 if (*account_ou != NULL) {
4790 exploded_dn = ldap_explode_dn(*account_ou, 0);
4791 if (exploded_dn) {
4792 ldap_value_free(exploded_dn);
4793 return ADS_SUCCESS;
4797 ou_string = ads_ou_string(ads, *account_ou);
4798 if (!ou_string) {
4799 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4802 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4803 ads->config.bind_path);
4804 SAFE_FREE(ou_string);
4806 if (!name) {
4807 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4810 exploded_dn = ldap_explode_dn(name, 0);
4811 if (!exploded_dn) {
4812 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4814 ldap_value_free(exploded_dn);
4816 *account_ou = name;
4817 return ADS_SUCCESS;
4820 #endif