s3:libads: Improve logging for failover scenarios
[Samba.git] / source3 / libads / ldap.c
blobcc00753ff7461ab77823167fa74b6475f50b2f6e
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"
38 #ifdef HAVE_LDAP
40 /**
41 * @file ldap.c
42 * @brief basic ldap client-side routines for ads server communications
44 * The routines contained here should do the necessary ldap calls for
45 * ads setups.
47 * Important note: attribute names passed into ads_ routines must
48 * already be in UTF-8 format. We do not convert them because in almost
49 * all cases, they are just ascii (which is represented with the same
50 * codepoints in UTF-8). This may have to change at some point
51 **/
54 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
56 static SIG_ATOMIC_T gotalarm;
58 /***************************************************************
59 Signal function to tell us we timed out.
60 ****************************************************************/
62 static void gotalarm_sig(int signum)
64 gotalarm = 1;
67 LDAP *ldap_open_with_timeout(const char *server,
68 struct sockaddr_storage *ss,
69 int port, unsigned int to)
71 LDAP *ldp = NULL;
72 int ldap_err;
73 char *uri;
75 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
76 "%u seconds\n", server, port, to));
78 if (to) {
79 /* Setup timeout */
80 gotalarm = 0;
81 CatchSignal(SIGALRM, gotalarm_sig);
82 alarm(to);
83 /* End setup timeout. */
86 if ( strchr_m(server, ':') ) {
87 /* IPv6 URI */
88 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
89 } else {
90 /* IPv4 URI */
91 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
93 if (uri == NULL) {
94 return NULL;
97 #ifdef HAVE_LDAP_INIT_FD
99 int fd = -1;
100 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
101 unsigned timeout_ms = 1000 * to;
103 status = open_socket_out(ss, port, timeout_ms, &fd);
104 if (!NT_STATUS_IS_OK(status)) {
105 DEBUG(3, ("open_socket_out: failed to open socket\n"));
106 return NULL;
109 /* define LDAP_PROTO_TCP from openldap.h if required */
110 #ifndef LDAP_PROTO_TCP
111 #define LDAP_PROTO_TCP 1
112 #endif
113 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
115 #elif defined(HAVE_LDAP_INITIALIZE)
116 ldap_err = ldap_initialize(&ldp, uri);
117 #else
118 ldp = ldap_open(server, port);
119 if (ldp != NULL) {
120 ldap_err = LDAP_SUCCESS;
121 } else {
122 ldap_err = LDAP_OTHER;
124 #endif
125 if (ldap_err != LDAP_SUCCESS) {
126 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
127 uri, ldap_err2string(ldap_err)));
128 } else {
129 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
132 if (to) {
133 /* Teardown timeout. */
134 alarm(0);
135 CatchSignal(SIGALRM, SIG_IGN);
138 return ldp;
141 static int ldap_search_with_timeout(LDAP *ld,
142 LDAP_CONST char *base,
143 int scope,
144 LDAP_CONST char *filter,
145 char **attrs,
146 int attrsonly,
147 LDAPControl **sctrls,
148 LDAPControl **cctrls,
149 int sizelimit,
150 LDAPMessage **res )
152 int to = lp_ldap_timeout();
153 struct timeval timeout;
154 struct timeval *timeout_ptr = NULL;
155 int result;
157 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
158 gotalarm = 0;
160 if (to) {
161 timeout.tv_sec = to;
162 timeout.tv_usec = 0;
163 timeout_ptr = &timeout;
165 /* Setup alarm timeout. */
166 CatchSignal(SIGALRM, gotalarm_sig);
167 /* Make the alarm time one second beyond
168 the timeout we're setting for the
169 remote search timeout, to allow that
170 to fire in preference. */
171 alarm(to+1);
172 /* End setup timeout. */
176 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
177 attrsonly, sctrls, cctrls, timeout_ptr,
178 sizelimit, res);
180 if (to) {
181 /* Teardown alarm timeout. */
182 CatchSignal(SIGALRM, SIG_IGN);
183 alarm(0);
186 if (gotalarm != 0)
187 return LDAP_TIMELIMIT_EXCEEDED;
190 * A bug in OpenLDAP means ldap_search_ext_s can return
191 * LDAP_SUCCESS but with a NULL res pointer. Cope with
192 * this. See bug #6279 for details. JRA.
195 if (*res == NULL) {
196 return LDAP_TIMELIMIT_EXCEEDED;
199 return result;
202 /**********************************************
203 Do client and server sitename match ?
204 **********************************************/
206 bool ads_sitename_match(ADS_STRUCT *ads)
208 if (ads->config.server_site_name == NULL &&
209 ads->config.client_site_name == NULL ) {
210 DEBUG(10,("ads_sitename_match: both null\n"));
211 return True;
213 if (ads->config.server_site_name &&
214 ads->config.client_site_name &&
215 strequal(ads->config.server_site_name,
216 ads->config.client_site_name)) {
217 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
218 return True;
220 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
221 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
222 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
223 return False;
226 /**********************************************
227 Is this the closest DC ?
228 **********************************************/
230 bool ads_closest_dc(ADS_STRUCT *ads)
232 if (ads->config.flags & NBT_SERVER_CLOSEST) {
233 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
234 return True;
237 /* not sure if this can ever happen */
238 if (ads_sitename_match(ads)) {
239 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
240 return True;
243 if (ads->config.client_site_name == NULL) {
244 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
245 return True;
248 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
249 ads->config.ldap_server_name));
251 return False;
254 static bool ads_fill_cldap_reply(ADS_STRUCT *ads,
255 bool gc,
256 const struct sockaddr_storage *ss,
257 const struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply)
259 TALLOC_CTX *frame = talloc_stackframe();
260 bool ret = false;
261 char addr[INET6_ADDRSTRLEN];
262 ADS_STATUS status;
263 char *dn;
265 print_sockaddr(addr, sizeof(addr), ss);
267 /* Check the CLDAP reply flags */
269 if (!(cldap_reply->server_type & NBT_SERVER_LDAP)) {
270 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
271 addr);
272 ret = false;
273 goto out;
276 /* Fill in the ads->config values */
278 ADS_TALLOC_CONST_FREE(ads->config.realm);
279 ADS_TALLOC_CONST_FREE(ads->config.bind_path);
280 ADS_TALLOC_CONST_FREE(ads->config.ldap_server_name);
281 ADS_TALLOC_CONST_FREE(ads->config.server_site_name);
282 ADS_TALLOC_CONST_FREE(ads->config.client_site_name);
283 ADS_TALLOC_CONST_FREE(ads->server.workgroup);
285 if (!check_cldap_reply_required_flags(cldap_reply->server_type,
286 ads->config.flags)) {
287 ret = false;
288 goto out;
291 ads->config.ldap_server_name = talloc_strdup(ads,
292 cldap_reply->pdc_dns_name);
293 if (ads->config.ldap_server_name == NULL) {
294 DBG_WARNING("Out of memory\n");
295 ret = false;
296 goto out;
299 ads->config.realm = talloc_asprintf_strupper_m(ads,
300 "%s",
301 cldap_reply->dns_domain);
302 if (ads->config.realm == NULL) {
303 DBG_WARNING("Out of memory\n");
304 ret = false;
305 goto out;
308 status = ads_build_dn(ads->config.realm, ads, &dn);
309 if (!ADS_ERR_OK(status)) {
310 DBG_DEBUG("Failed to build bind path: %s\n",
311 ads_errstr(status));
312 ret = false;
313 goto out;
315 ads->config.bind_path = dn;
317 if (*cldap_reply->server_site) {
318 ads->config.server_site_name =
319 talloc_strdup(ads, cldap_reply->server_site);
320 if (ads->config.server_site_name == NULL) {
321 DBG_WARNING("Out of memory\n");
322 ret = false;
323 goto out;
327 if (*cldap_reply->client_site) {
328 ads->config.client_site_name =
329 talloc_strdup(ads, cldap_reply->client_site);
330 if (ads->config.client_site_name == NULL) {
331 DBG_WARNING("Out of memory\n");
332 ret = false;
333 goto out;
337 ads->server.workgroup = talloc_strdup(ads, cldap_reply->domain_name);
338 if (ads->server.workgroup == NULL) {
339 DBG_WARNING("Out of memory\n");
340 ret = false;
341 goto out;
344 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
345 ads->ldap.ss = *ss;
347 /* Store our site name. */
348 sitename_store(cldap_reply->domain_name, cldap_reply->client_site);
349 sitename_store(cldap_reply->dns_domain, cldap_reply->client_site);
351 /* Leave this until last so that the flags are not clobbered */
352 ads->config.flags = cldap_reply->server_type;
354 ret = true;
356 out:
358 TALLOC_FREE(frame);
359 return ret;
363 try a connection to a given ldap server, returning True and setting the servers IP
364 in the ads struct if successful
366 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
367 struct sockaddr_storage *ss)
369 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply = {};
370 TALLOC_CTX *frame = talloc_stackframe();
371 bool ok;
372 char addr[INET6_ADDRSTRLEN] = { 0, };
374 if (ss == NULL) {
375 TALLOC_FREE(frame);
376 return false;
379 print_sockaddr(addr, sizeof(addr), ss);
381 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
382 addr, ads->server.realm);
384 ok = ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply);
385 if (!ok) {
386 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
387 addr, ads->server.realm);
388 TALLOC_FREE(frame);
389 return false;
392 ok = ads_fill_cldap_reply(ads, gc, ss, &cldap_reply);
393 if (!ok) {
394 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
395 addr, ads->server.realm);
396 TALLOC_FREE(frame);
397 return false;
400 TALLOC_FREE(frame);
401 return true;
404 /**********************************************************************
405 send a cldap ping to list of servers, one at a time, until one of
406 them answers it's an ldap server. Record success in the ADS_STRUCT.
407 Take note of and update negative connection cache.
408 **********************************************************************/
410 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
411 const char *domain,
412 struct samba_sockaddr *sa_list,
413 size_t count)
415 TALLOC_CTX *frame = talloc_stackframe();
416 struct timeval endtime = timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
417 uint32_t nt_version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
418 struct tsocket_address **ts_list = NULL;
419 const struct tsocket_address * const *ts_list_const = NULL;
420 struct samba_sockaddr **req_sa_list = NULL;
421 struct netlogon_samlogon_response **responses = NULL;
422 size_t num_requests = 0;
423 NTSTATUS status;
424 size_t i;
425 bool ok = false;
426 bool retry;
428 ts_list = talloc_zero_array(frame,
429 struct tsocket_address *,
430 count);
431 if (ts_list == NULL) {
432 TALLOC_FREE(frame);
433 return NT_STATUS_NO_MEMORY;
436 req_sa_list = talloc_zero_array(frame,
437 struct samba_sockaddr *,
438 count);
439 if (req_sa_list == NULL) {
440 TALLOC_FREE(frame);
441 return NT_STATUS_NO_MEMORY;
444 again:
446 * The retry loop is bound by the timeout
448 retry = false;
449 num_requests = 0;
451 for (i = 0; i < count; i++) {
452 char server[INET6_ADDRSTRLEN];
453 int ret;
455 if (is_zero_addr(&sa_list[i].u.ss)) {
456 continue;
459 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
461 status = check_negative_conn_cache(domain, server);
462 if (!NT_STATUS_IS_OK(status)) {
463 continue;
466 ret = tsocket_address_inet_from_strings(ts_list, "ip",
467 server, LDAP_PORT,
468 &ts_list[num_requests]);
469 if (ret != 0) {
470 status = map_nt_error_from_unix(errno);
471 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
472 server, nt_errstr(status));
473 TALLOC_FREE(frame);
474 return status;
477 req_sa_list[num_requests] = &sa_list[i];
478 num_requests += 1;
481 DBG_DEBUG("Try to create %zu netlogon connections for domain '%s' "
482 "(provided count of addresses was %zu).\n",
483 num_requests,
484 domain,
485 count);
487 if (num_requests == 0) {
488 status = NT_STATUS_NO_LOGON_SERVERS;
489 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
490 domain, num_requests, count, nt_errstr(status));
491 TALLOC_FREE(frame);
492 return status;
495 ts_list_const = (const struct tsocket_address * const *)ts_list;
497 status = cldap_multi_netlogon(frame,
498 ts_list_const, num_requests,
499 ads->server.realm, NULL,
500 nt_version,
501 1, endtime, &responses);
502 if (!NT_STATUS_IS_OK(status)) {
503 DBG_WARNING("cldap_multi_netlogon(realm=%s, num_requests=%zu) "
504 "for count[%zu] - %s\n",
505 ads->server.realm,
506 num_requests, count,
507 nt_errstr(status));
508 TALLOC_FREE(frame);
509 return NT_STATUS_NO_LOGON_SERVERS;
512 for (i = 0; i < num_requests; i++) {
513 struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply = NULL;
514 char server[INET6_ADDRSTRLEN];
516 if (responses[i] == NULL) {
517 continue;
520 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
522 if (responses[i]->ntver != NETLOGON_NT_VERSION_5EX) {
523 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
524 ads->server.realm,
525 responses[i]->ntver, server);
526 continue;
529 cldap_reply = &responses[i]->data.nt5_ex;
531 /* Returns ok only if it matches the correct server type */
532 ok = ads_fill_cldap_reply(ads,
533 false,
534 &req_sa_list[i]->u.ss,
535 cldap_reply);
536 if (ok) {
537 DBG_DEBUG("realm[%s]: selected %s => %s\n",
538 ads->server.realm,
539 server, cldap_reply->pdc_dns_name);
540 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
541 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
542 cldap_reply);
544 TALLOC_FREE(frame);
545 return NT_STATUS_OK;
548 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
549 ads->server.realm,
550 server, cldap_reply->pdc_dns_name);
551 if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
552 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
553 cldap_reply);
555 add_failed_connection_entry(domain, server,
556 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID);
557 retry = true;
560 if (retry) {
561 bool expired;
563 expired = timeval_expired(&endtime);
564 if (!expired) {
565 goto again;
569 /* keep track of failures as all were not suitable */
570 for (i = 0; i < num_requests; i++) {
571 char server[INET6_ADDRSTRLEN];
573 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
575 add_failed_connection_entry(domain, server,
576 NT_STATUS_UNSUCCESSFUL);
579 status = NT_STATUS_NO_LOGON_SERVERS;
580 DBG_WARNING("realm[%s] no valid response "
581 "num_requests[%zu] for count[%zu] - %s\n",
582 ads->server.realm,
583 num_requests, count, nt_errstr(status));
584 TALLOC_FREE(frame);
585 return NT_STATUS_NO_LOGON_SERVERS;
588 /***************************************************************************
589 resolve a name and perform an "ldap ping" using NetBIOS and related methods
590 ****************************************************************************/
592 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
593 const char *domain, const char *realm)
595 size_t i;
596 size_t count = 0;
597 struct samba_sockaddr *sa_list = NULL;
598 NTSTATUS status;
600 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
601 domain));
603 status = get_sorted_dc_list(talloc_tos(),
604 domain,
605 NULL,
606 &sa_list,
607 &count,
608 false);
609 if (!NT_STATUS_IS_OK(status)) {
610 return status;
613 /* remove servers which are known to be dead based on
614 the corresponding DNS method */
615 if (*realm) {
616 for (i = 0; i < count; ++i) {
617 char server[INET6_ADDRSTRLEN];
619 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
621 if(!NT_STATUS_IS_OK(
622 check_negative_conn_cache(realm, server))) {
623 /* Ensure we add the workgroup name for this
624 IP address as negative too. */
625 add_failed_connection_entry(
626 domain, server,
627 NT_STATUS_UNSUCCESSFUL);
632 status = cldap_ping_list(ads, domain, sa_list, count);
634 TALLOC_FREE(sa_list);
636 return status;
640 /**********************************************************************
641 resolve a name and perform an "ldap ping" using DNS
642 **********************************************************************/
644 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
645 const char *realm)
647 size_t count = 0;
648 struct samba_sockaddr *sa_list = NULL;
649 NTSTATUS status;
651 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
652 realm));
654 status = get_sorted_dc_list(talloc_tos(),
655 realm,
656 sitename,
657 &sa_list,
658 &count,
659 true);
660 if (!NT_STATUS_IS_OK(status)) {
661 TALLOC_FREE(sa_list);
662 return status;
665 status = cldap_ping_list(ads, realm, sa_list, count);
667 TALLOC_FREE(sa_list);
669 return status;
672 /**********************************************************************
673 Try to find an AD dc using our internal name resolution routines
674 Try the realm first and then then workgroup name if netbios is not
675 disabled
676 **********************************************************************/
678 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
680 const char *c_domain = "";
681 const char *c_realm;
682 bool use_own_domain = False;
683 char *sitename = NULL;
684 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
685 bool ok = false;
687 /* if the realm and workgroup are both empty, assume they are ours */
689 /* realm */
690 c_realm = ads->server.realm;
692 if (c_realm == NULL)
693 c_realm = "";
695 if (!*c_realm) {
696 /* special case where no realm and no workgroup means our own */
697 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
698 use_own_domain = True;
699 c_realm = lp_realm();
703 if (!lp_disable_netbios()) {
704 if (use_own_domain) {
705 c_domain = lp_workgroup();
706 } else {
707 c_domain = ads->server.workgroup;
708 if (!*c_realm && (!c_domain || !*c_domain)) {
709 c_domain = lp_workgroup();
713 if (!c_domain) {
714 c_domain = "";
718 if (!*c_realm && !*c_domain) {
719 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
720 "what to do\n"));
721 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
725 * In case of LDAP we use get_dc_name() as that
726 * creates the custom krb5.conf file
728 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
729 fstring srv_name;
730 struct sockaddr_storage ip_out;
732 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
733 " and falling back to domain '%s'\n",
734 c_realm, c_domain));
736 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
737 if (ok) {
738 if (is_zero_addr(&ip_out)) {
739 return NT_STATUS_NO_LOGON_SERVERS;
743 * we call ads_try_connect() to fill in the
744 * ads->config details
746 ok = ads_try_connect(ads, false, &ip_out);
747 if (ok) {
748 return NT_STATUS_OK;
752 return NT_STATUS_NO_LOGON_SERVERS;
755 if (*c_realm) {
756 sitename = sitename_fetch(talloc_tos(), c_realm);
757 status = resolve_and_ping_dns(ads, sitename, c_realm);
759 if (NT_STATUS_IS_OK(status)) {
760 TALLOC_FREE(sitename);
761 return status;
764 /* In case we failed to contact one of our closest DC on our
765 * site we
766 * need to try to find another DC, retry with a site-less SRV
767 * DNS query
768 * - Guenther */
770 if (sitename) {
771 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
772 "our site (%s), Trying to find another DC "
773 "for realm '%s' (domain '%s')\n",
774 sitename, c_realm, c_domain));
775 namecache_delete(c_realm, 0x1C);
776 status =
777 resolve_and_ping_dns(ads, NULL, c_realm);
779 if (NT_STATUS_IS_OK(status)) {
780 TALLOC_FREE(sitename);
781 return status;
785 TALLOC_FREE(sitename);
788 /* try netbios as fallback - if permitted,
789 or if configuration specifically requests it */
790 if (*c_domain) {
791 if (*c_realm) {
792 DEBUG(3, ("ads_find_dc: falling back to netbios "
793 "name resolution for domain '%s' (realm '%s')\n",
794 c_domain, c_realm));
797 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
798 if (NT_STATUS_IS_OK(status)) {
799 return status;
803 DEBUG(1, ("ads_find_dc: "
804 "name resolution for realm '%s' (domain '%s') failed: %s\n",
805 c_realm, c_domain, nt_errstr(status)));
806 return status;
809 * Connect to the LDAP server
810 * @param ads Pointer to an existing ADS_STRUCT
811 * @return status of connection
813 ADS_STATUS ads_connect(ADS_STRUCT *ads)
815 int version = LDAP_VERSION3;
816 ADS_STATUS status;
817 NTSTATUS ntstatus;
818 char addr[INET6_ADDRSTRLEN];
819 struct sockaddr_storage existing_ss;
821 zero_sockaddr(&existing_ss);
824 * ads_connect can be passed in a reused ADS_STRUCT
825 * with an existing non-zero ads->ldap.ss IP address
826 * that was stored by going through ads_find_dc()
827 * if ads->server.ldap_server was NULL.
829 * If ads->server.ldap_server is still NULL but
830 * the target address isn't the zero address, then
831 * store that address off off before zeroing out
832 * ads->ldap so we don't keep doing multiple calls
833 * to ads_find_dc() in the reuse case.
835 * If a caller wants a clean ADS_STRUCT they
836 * will TALLOC_FREE it and allocate a new one
837 * by calling ads_init(), which ensures
838 * ads->ldap.ss is a properly zero'ed out valid IP
839 * address.
841 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
842 /* Save off the address we previously found by ads_find_dc(). */
843 existing_ss = ads->ldap.ss;
846 ads_zero_ldap(ads);
847 ZERO_STRUCT(ads->ldap_wrap_data);
848 ads->ldap.last_attempt = time_mono(NULL);
849 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
851 /* try with a user specified server */
853 if (DEBUGLEVEL >= 11) {
854 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
855 DEBUG(11,("ads_connect: entering\n"));
856 DEBUGADD(11,("%s\n", s));
857 TALLOC_FREE(s);
860 if (ads->server.ldap_server) {
861 bool ok = false;
862 struct sockaddr_storage ss;
864 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
865 ads->server.ldap_server);
866 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
867 if (!ok) {
868 DEBUG(5,("ads_connect: unable to resolve name %s\n",
869 ads->server.ldap_server));
870 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
871 goto out;
874 if (is_zero_addr(&ss)) {
875 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
876 goto out;
879 ok = ads_try_connect(ads, ads->server.gc, &ss);
880 if (ok) {
881 goto got_connection;
884 /* The choice of which GC use is handled one level up in
885 ads_connect_gc(). If we continue on from here with
886 ads_find_dc() we will get GC searches on port 389 which
887 doesn't work. --jerry */
889 if (ads->server.gc == true) {
890 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
893 if (ads->server.no_fallback) {
894 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
895 goto out;
899 if (!is_zero_addr(&existing_ss)) {
900 /* We saved off who we should talk to. */
901 bool ok = ads_try_connect(ads,
902 ads->server.gc,
903 &existing_ss);
904 if (ok) {
905 goto got_connection;
908 * Keep trying to find a server and fall through
909 * into ads_find_dc() again.
911 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
912 "trying to find another DC.\n");
915 ntstatus = ads_find_dc(ads);
916 if (NT_STATUS_IS_OK(ntstatus)) {
917 goto got_connection;
920 status = ADS_ERROR_NT(ntstatus);
921 goto out;
923 got_connection:
925 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
926 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
928 if (!ads->auth.user_name) {
929 /* Must use the userPrincipalName value here or sAMAccountName
930 and not servicePrincipalName; found by Guenther Deschner */
931 ads->auth.user_name = talloc_asprintf(ads,
932 "%s$",
933 lp_netbios_name());
934 if (ads->auth.user_name == NULL) {
935 DBG_ERR("talloc_asprintf failed\n");
936 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
937 goto out;
941 if (ads->auth.realm == NULL) {
942 ads->auth.realm = talloc_strdup(ads, ads->config.realm);
943 if (ads->auth.realm == NULL) {
944 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
945 goto out;
949 if (!ads->auth.kdc_server) {
950 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
951 ads->auth.kdc_server = talloc_strdup(ads, addr);
952 if (ads->auth.kdc_server == NULL) {
953 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
954 goto out;
958 /* If the caller() requested no LDAP bind, then we are done */
960 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
961 status = ADS_SUCCESS;
962 goto out;
965 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
966 if (!ads->ldap_wrap_data.mem_ctx) {
967 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
968 goto out;
971 /* Otherwise setup the TCP LDAP session */
973 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
974 &ads->ldap.ss,
975 ads->ldap.port, lp_ldap_timeout());
976 if (ads->ldap.ld == NULL) {
977 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
978 goto out;
980 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
982 /* cache the successful connection for workgroup and realm */
983 if (ads_closest_dc(ads)) {
984 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
985 saf_store( ads->server.realm, ads->config.ldap_server_name);
988 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
990 /* fill in the current time and offsets */
992 status = ads_current_time( ads );
993 if ( !ADS_ERR_OK(status) ) {
994 goto out;
997 /* Now do the bind */
999 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
1000 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
1001 goto out;
1004 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
1005 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
1006 goto out;
1009 status = ads_sasl_bind(ads);
1011 out:
1012 if (DEBUGLEVEL >= 11) {
1013 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1014 DEBUG(11,("ads_connect: leaving with: %s\n",
1015 ads_errstr(status)));
1016 DEBUGADD(11,("%s\n", s));
1017 TALLOC_FREE(s);
1020 return status;
1024 * Connect to the LDAP server using given credentials
1025 * @param ads Pointer to an existing ADS_STRUCT
1026 * @return status of connection
1028 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
1030 ads->auth.flags |= ADS_AUTH_USER_CREDS;
1032 return ads_connect(ads);
1036 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1037 * @param ads Pointer to an existing ADS_STRUCT
1039 * Sets the ads->ldap.ss to a valid
1040 * zero ip address that can be detected by
1041 * our is_zero_addr() function. Otherwise
1042 * it is left as AF_UNSPEC (0).
1044 void ads_zero_ldap(ADS_STRUCT *ads)
1046 ZERO_STRUCT(ads->ldap);
1048 * Initialize the sockaddr_storage so we can use
1049 * sockaddr test functions against it.
1051 zero_sockaddr(&ads->ldap.ss);
1055 * Disconnect the LDAP server
1056 * @param ads Pointer to an existing ADS_STRUCT
1058 void ads_disconnect(ADS_STRUCT *ads)
1060 if (ads->ldap.ld) {
1061 ldap_unbind(ads->ldap.ld);
1062 ads->ldap.ld = NULL;
1064 if (ads->ldap_wrap_data.wrap_ops &&
1065 ads->ldap_wrap_data.wrap_ops->disconnect) {
1066 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1068 if (ads->ldap_wrap_data.mem_ctx) {
1069 talloc_free(ads->ldap_wrap_data.mem_ctx);
1071 ads_zero_ldap(ads);
1072 ZERO_STRUCT(ads->ldap_wrap_data);
1076 Duplicate a struct berval into talloc'ed memory
1078 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1080 struct berval *value;
1082 if (!in_val) return NULL;
1084 value = talloc_zero(ctx, struct berval);
1085 if (value == NULL)
1086 return NULL;
1087 if (in_val->bv_len == 0) return value;
1089 value->bv_len = in_val->bv_len;
1090 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1091 in_val->bv_len);
1092 return value;
1096 Make a values list out of an array of (struct berval *)
1098 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1099 const struct berval **in_vals)
1101 struct berval **values;
1102 int i;
1104 if (!in_vals) return NULL;
1105 for (i=0; in_vals[i]; i++)
1106 ; /* count values */
1107 values = talloc_zero_array(ctx, struct berval *, i+1);
1108 if (!values) return NULL;
1110 for (i=0; in_vals[i]; i++) {
1111 values[i] = dup_berval(ctx, in_vals[i]);
1113 return values;
1117 UTF8-encode a values list out of an array of (char *)
1119 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1121 char **values;
1122 int i;
1123 size_t size;
1125 if (!in_vals) return NULL;
1126 for (i=0; in_vals[i]; i++)
1127 ; /* count values */
1128 values = talloc_zero_array(ctx, char *, i+1);
1129 if (!values) return NULL;
1131 for (i=0; in_vals[i]; i++) {
1132 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1133 TALLOC_FREE(values);
1134 return NULL;
1137 return values;
1141 Pull a (char *) array out of a UTF8-encoded values list
1143 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1145 char **values;
1146 int i;
1147 size_t converted_size;
1149 if (!in_vals) return NULL;
1150 for (i=0; in_vals[i]; i++)
1151 ; /* count values */
1152 values = talloc_zero_array(ctx, char *, i+1);
1153 if (!values) return NULL;
1155 for (i=0; in_vals[i]; i++) {
1156 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1157 &converted_size)) {
1158 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1159 "%s", strerror(errno)));
1162 return values;
1166 * Do a search with paged results. cookie must be null on the first
1167 * call, and then returned on each subsequent call. It will be null
1168 * again when the entire search is complete
1169 * @param ads connection to ads server
1170 * @param bind_path Base dn for the search
1171 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1172 * @param expr Search expression - specified in local charset
1173 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1174 * @param res ** which will contain results - free res* with ads_msgfree()
1175 * @param count Number of entries retrieved on this page
1176 * @param cookie The paged results cookie to be returned on subsequent calls
1177 * @return status of search
1179 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1180 const char *bind_path,
1181 int scope, const char *expr,
1182 const char **attrs, void *args,
1183 LDAPMessage **res,
1184 int *count, struct berval **cookie)
1186 int rc, i, version;
1187 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1188 size_t converted_size;
1189 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1190 BerElement *cookie_be = NULL;
1191 struct berval *cookie_bv= NULL;
1192 BerElement *ext_be = NULL;
1193 struct berval *ext_bv= NULL;
1195 TALLOC_CTX *ctx;
1196 ads_control *external_control = (ads_control *) args;
1198 *res = NULL;
1200 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1201 return ADS_ERROR(LDAP_NO_MEMORY);
1203 /* 0 means the conversion worked but the result was empty
1204 so we only fail if it's -1. In any case, it always
1205 at least nulls out the dest */
1206 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1207 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1209 rc = LDAP_NO_MEMORY;
1210 goto done;
1213 if (!attrs || !(*attrs))
1214 search_attrs = NULL;
1215 else {
1216 /* This would be the utf8-encoded version...*/
1217 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1218 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1219 rc = LDAP_NO_MEMORY;
1220 goto done;
1224 /* Paged results only available on ldap v3 or later */
1225 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1226 if (version < LDAP_VERSION3) {
1227 rc = LDAP_NOT_SUPPORTED;
1228 goto done;
1231 cookie_be = ber_alloc_t(LBER_USE_DER);
1232 if (*cookie) {
1233 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1234 ber_bvfree(*cookie); /* don't need it from last time */
1235 *cookie = NULL;
1236 } else {
1237 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1239 ber_flatten(cookie_be, &cookie_bv);
1240 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1241 PagedResults.ldctl_iscritical = (char) 1;
1242 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1243 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1245 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1246 NoReferrals.ldctl_iscritical = (char) 0;
1247 NoReferrals.ldctl_value.bv_len = 0;
1248 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1250 if (external_control &&
1251 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1252 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1254 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1255 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1257 /* win2k does not accept a ldctl_value being passed in */
1259 if (external_control->val != 0) {
1261 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1262 rc = LDAP_NO_MEMORY;
1263 goto done;
1266 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1267 rc = LDAP_NO_MEMORY;
1268 goto done;
1270 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1271 rc = LDAP_NO_MEMORY;
1272 goto done;
1275 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1276 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1278 } else {
1279 ExternalCtrl.ldctl_value.bv_len = 0;
1280 ExternalCtrl.ldctl_value.bv_val = NULL;
1283 controls[0] = &NoReferrals;
1284 controls[1] = &PagedResults;
1285 controls[2] = &ExternalCtrl;
1286 controls[3] = NULL;
1288 } else {
1289 controls[0] = &NoReferrals;
1290 controls[1] = &PagedResults;
1291 controls[2] = NULL;
1294 /* we need to disable referrals as the openldap libs don't
1295 handle them and paged results at the same time. Using them
1296 together results in the result record containing the server
1297 page control being removed from the result list (tridge/jmcd)
1299 leaving this in despite the control that says don't generate
1300 referrals, in case the server doesn't support it (jmcd)
1302 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1304 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1305 search_attrs, 0, controls,
1306 NULL, LDAP_NO_LIMIT,
1307 (LDAPMessage **)res);
1309 ber_free(cookie_be, 1);
1310 ber_bvfree(cookie_bv);
1312 if (rc) {
1313 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1314 ldap_err2string(rc)));
1315 if (rc == LDAP_OTHER) {
1316 char *ldap_errmsg;
1317 int ret;
1319 ret = ldap_parse_result(ads->ldap.ld,
1320 *res,
1321 NULL,
1322 NULL,
1323 &ldap_errmsg,
1324 NULL,
1325 NULL,
1327 if (ret == LDAP_SUCCESS) {
1328 DEBUG(3, ("ldap_search_with_timeout(%s) "
1329 "error: %s\n", expr, ldap_errmsg));
1330 ldap_memfree(ldap_errmsg);
1333 goto done;
1336 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1337 NULL, &rcontrols, 0);
1339 if (!rcontrols) {
1340 goto done;
1343 for (i=0; rcontrols[i]; i++) {
1344 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1345 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1346 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1347 &cookie_bv);
1348 /* the berval is the cookie, but must be freed when
1349 it is all done */
1350 if (cookie_bv->bv_len) /* still more to do */
1351 *cookie=ber_bvdup(cookie_bv);
1352 else
1353 *cookie=NULL;
1354 ber_bvfree(cookie_bv);
1355 ber_free(cookie_be, 1);
1356 break;
1359 ldap_controls_free(rcontrols);
1361 done:
1362 talloc_destroy(ctx);
1364 if (ext_be) {
1365 ber_free(ext_be, 1);
1368 if (ext_bv) {
1369 ber_bvfree(ext_bv);
1372 if (rc != LDAP_SUCCESS && *res != NULL) {
1373 ads_msgfree(ads, *res);
1374 *res = NULL;
1377 /* if/when we decide to utf8-encode attrs, take out this next line */
1378 TALLOC_FREE(search_attrs);
1380 return ADS_ERROR(rc);
1383 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1384 int scope, const char *expr,
1385 const char **attrs, LDAPMessage **res,
1386 int *count, struct berval **cookie)
1388 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1393 * Get all results for a search. This uses ads_do_paged_search() to return
1394 * all entries in a large search.
1395 * @param ads connection to ads server
1396 * @param bind_path Base dn for the search
1397 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1398 * @param expr Search expression
1399 * @param attrs Attributes to retrieve
1400 * @param res ** which will contain results - free res* with ads_msgfree()
1401 * @return status of search
1403 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1404 int scope, const char *expr,
1405 const char **attrs, void *args,
1406 LDAPMessage **res)
1408 struct berval *cookie = NULL;
1409 int count = 0;
1410 ADS_STATUS status;
1412 *res = NULL;
1413 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1414 &count, &cookie);
1416 if (!ADS_ERR_OK(status))
1417 return status;
1419 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1420 while (cookie) {
1421 LDAPMessage *res2 = NULL;
1422 LDAPMessage *msg, *next;
1424 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1425 attrs, args, &res2, &count, &cookie);
1426 if (!ADS_ERR_OK(status)) {
1427 break;
1430 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1431 that this works on all ldap libs, but I have only tested with openldap */
1432 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1433 next = ads_next_message(ads, msg);
1434 ldap_add_result_entry((LDAPMessage **)res, msg);
1436 /* note that we do not free res2, as the memory is now
1437 part of the main returned list */
1439 #else
1440 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1441 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1442 #endif
1444 return status;
1447 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1448 int scope, const char *expr,
1449 const char **attrs, LDAPMessage **res)
1451 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1454 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1455 int scope, const char *expr,
1456 const char **attrs, uint32_t sd_flags,
1457 LDAPMessage **res)
1459 ads_control args;
1461 args.control = ADS_SD_FLAGS_OID;
1462 args.val = sd_flags;
1463 args.critical = True;
1465 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1470 * Run a function on all results for a search. Uses ads_do_paged_search() and
1471 * runs the function as each page is returned, using ads_process_results()
1472 * @param ads connection to ads server
1473 * @param bind_path Base dn for the search
1474 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1475 * @param expr Search expression - specified in local charset
1476 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1477 * @param fn Function which takes attr name, values list, and data_area
1478 * @param data_area Pointer which is passed to function on each call
1479 * @return status of search
1481 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1482 int scope, const char *expr, const char **attrs,
1483 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1484 void *data_area)
1486 struct berval *cookie = NULL;
1487 int count = 0;
1488 ADS_STATUS status;
1489 LDAPMessage *res;
1491 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1492 &count, &cookie);
1494 if (!ADS_ERR_OK(status)) return status;
1496 ads_process_results(ads, res, fn, data_area);
1497 ads_msgfree(ads, res);
1499 while (cookie) {
1500 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1501 &res, &count, &cookie);
1503 if (!ADS_ERR_OK(status)) break;
1505 ads_process_results(ads, res, fn, data_area);
1506 ads_msgfree(ads, res);
1509 return status;
1513 * Do a search with a timeout.
1514 * @param ads connection to ads server
1515 * @param bind_path Base dn for the search
1516 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1517 * @param expr Search expression
1518 * @param attrs Attributes to retrieve
1519 * @param res ** which will contain results - free res* with ads_msgfree()
1520 * @return status of search
1522 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1523 const char *expr,
1524 const char **attrs, LDAPMessage **res)
1526 int rc;
1527 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1528 size_t converted_size;
1529 TALLOC_CTX *ctx;
1531 *res = NULL;
1532 if (!(ctx = talloc_init("ads_do_search"))) {
1533 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1534 return ADS_ERROR(LDAP_NO_MEMORY);
1537 /* 0 means the conversion worked but the result was empty
1538 so we only fail if it's negative. In any case, it always
1539 at least nulls out the dest */
1540 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1541 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1543 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1544 rc = LDAP_NO_MEMORY;
1545 goto done;
1548 if (!attrs || !(*attrs))
1549 search_attrs = NULL;
1550 else {
1551 /* This would be the utf8-encoded version...*/
1552 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1553 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1555 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1556 rc = LDAP_NO_MEMORY;
1557 goto done;
1561 /* see the note in ads_do_paged_search - we *must* disable referrals */
1562 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1564 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1565 search_attrs, 0, NULL, NULL,
1566 LDAP_NO_LIMIT,
1567 (LDAPMessage **)res);
1569 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1570 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1571 rc = 0;
1574 done:
1575 talloc_destroy(ctx);
1576 /* if/when we decide to utf8-encode attrs, take out this next line */
1577 TALLOC_FREE(search_attrs);
1578 return ADS_ERROR(rc);
1581 * Do a general ADS search
1582 * @param ads connection to ads server
1583 * @param res ** which will contain results - free res* with ads_msgfree()
1584 * @param expr Search expression
1585 * @param attrs Attributes to retrieve
1586 * @return status of search
1588 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1589 const char *expr, const char **attrs)
1591 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1592 expr, attrs, res);
1596 * Do a search on a specific DistinguishedName
1597 * @param ads connection to ads server
1598 * @param res ** which will contain results - free res* with ads_msgfree()
1599 * @param dn DistinguishName to search
1600 * @param attrs Attributes to retrieve
1601 * @return status of search
1603 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1604 const char *dn, const char **attrs)
1606 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1607 attrs, res);
1611 * Free up memory from a ads_search
1612 * @param ads connection to ads server
1613 * @param msg Search results to free
1615 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1617 if (!msg) return;
1618 ldap_msgfree(msg);
1622 * Get a dn from search results
1623 * @param ads connection to ads server
1624 * @param msg Search result
1625 * @return dn string
1627 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1629 char *utf8_dn, *unix_dn;
1630 size_t converted_size;
1632 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1634 if (!utf8_dn) {
1635 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1636 return NULL;
1639 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1640 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1641 utf8_dn ));
1642 return NULL;
1644 ldap_memfree(utf8_dn);
1645 return unix_dn;
1649 * Get the parent from a dn
1650 * @param dn the dn to return the parent from
1651 * @return parent dn string
1653 char *ads_parent_dn(const char *dn)
1655 char *p;
1657 if (dn == NULL) {
1658 return NULL;
1661 p = strchr(dn, ',');
1663 if (p == NULL) {
1664 return NULL;
1667 return p+1;
1671 * Find a machine account given a hostname
1672 * @param ads connection to ads server
1673 * @param res ** which will contain results - free res* with ads_msgfree()
1674 * @param host Hostname to search for
1675 * @return status of search
1677 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1678 const char *machine)
1680 ADS_STATUS status;
1681 char *expr;
1682 const char *attrs[] = {
1683 /* This is how Windows checks for machine accounts */
1684 "objectClass",
1685 "SamAccountName",
1686 "userAccountControl",
1687 "DnsHostName",
1688 "ServicePrincipalName",
1689 "userPrincipalName",
1690 "unicodePwd",
1692 /* Additional attributes Samba checks */
1693 "msDS-AdditionalDnsHostName",
1694 "msDS-SupportedEncryptionTypes",
1695 "nTSecurityDescriptor",
1696 "objectSid",
1698 NULL
1700 TALLOC_CTX *frame = talloc_stackframe();
1702 *res = NULL;
1704 /* the easiest way to find a machine account anywhere in the tree
1705 is to look for hostname$ */
1706 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1707 if (expr == NULL) {
1708 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1709 goto done;
1712 status = ads_search(ads, res, expr, attrs);
1713 if (ADS_ERR_OK(status)) {
1714 if (ads_count_replies(ads, *res) != 1) {
1715 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1719 done:
1720 TALLOC_FREE(frame);
1721 return status;
1725 * Initialize a list of mods to be used in a modify request
1726 * @param ctx An initialized TALLOC_CTX
1727 * @return allocated ADS_MODLIST
1729 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1731 #define ADS_MODLIST_ALLOC_SIZE 10
1732 LDAPMod **mods;
1734 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1735 /* -1 is safety to make sure we don't go over the end.
1736 need to reset it to NULL before doing ldap modify */
1737 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1739 return (ADS_MODLIST)mods;
1744 add an attribute to the list, with values list already constructed
1746 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1747 int mod_op, const char *name,
1748 const void *_invals)
1750 int curmod;
1751 LDAPMod **modlist = (LDAPMod **) *mods;
1752 struct berval **ber_values = NULL;
1753 char **char_values = NULL;
1755 if (!_invals) {
1756 mod_op = LDAP_MOD_DELETE;
1757 } else {
1758 if (mod_op & LDAP_MOD_BVALUES) {
1759 const struct berval **b;
1760 b = discard_const_p(const struct berval *, _invals);
1761 ber_values = ads_dup_values(ctx, b);
1762 } else {
1763 const char **c;
1764 c = discard_const_p(const char *, _invals);
1765 char_values = ads_push_strvals(ctx, c);
1769 /* find the first empty slot */
1770 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1771 curmod++);
1772 if (modlist[curmod] == (LDAPMod *) -1) {
1773 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1774 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1775 return ADS_ERROR(LDAP_NO_MEMORY);
1776 memset(&modlist[curmod], 0,
1777 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1778 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1779 *mods = (ADS_MODLIST)modlist;
1782 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1783 return ADS_ERROR(LDAP_NO_MEMORY);
1784 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1785 if (mod_op & LDAP_MOD_BVALUES) {
1786 modlist[curmod]->mod_bvalues = ber_values;
1787 } else if (mod_op & LDAP_MOD_DELETE) {
1788 modlist[curmod]->mod_values = NULL;
1789 } else {
1790 modlist[curmod]->mod_values = char_values;
1793 modlist[curmod]->mod_op = mod_op;
1794 return ADS_ERROR(LDAP_SUCCESS);
1798 * Add a single string value to a mod list
1799 * @param ctx An initialized TALLOC_CTX
1800 * @param mods An initialized ADS_MODLIST
1801 * @param name The attribute name to add
1802 * @param val The value to add - NULL means DELETE
1803 * @return ADS STATUS indicating success of add
1805 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1806 const char *name, const char *val)
1808 const char *values[2];
1810 values[0] = val;
1811 values[1] = NULL;
1813 if (!val)
1814 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1815 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1819 * Add an array of string values to a mod list
1820 * @param ctx An initialized TALLOC_CTX
1821 * @param mods An initialized ADS_MODLIST
1822 * @param name The attribute name to add
1823 * @param vals The array of string values to add - NULL means DELETE
1824 * @return ADS STATUS indicating success of add
1826 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1827 const char *name, const char **vals)
1829 if (!vals)
1830 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1831 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1832 name, (const void **) vals);
1836 * Add a single ber-encoded value to a mod list
1837 * @param ctx An initialized TALLOC_CTX
1838 * @param mods An initialized ADS_MODLIST
1839 * @param name The attribute name to add
1840 * @param val The value to add - NULL means DELETE
1841 * @return ADS STATUS indicating success of add
1843 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1844 const char *name, const struct berval *val)
1846 const struct berval *values[2];
1848 values[0] = val;
1849 values[1] = NULL;
1850 if (!val)
1851 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1852 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1853 name, (const void **) values);
1856 static void ads_print_error(int ret, LDAP *ld)
1858 if (ret != 0) {
1859 char *ld_error = NULL;
1860 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1861 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1862 ret,
1863 ldap_err2string(ret),
1864 ld_error);
1865 SAFE_FREE(ld_error);
1870 * Perform an ldap modify
1871 * @param ads connection to ads server
1872 * @param mod_dn DistinguishedName to modify
1873 * @param mods list of modifications to perform
1874 * @return status of modify
1876 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1878 int ret,i;
1879 char *utf8_dn = NULL;
1880 size_t converted_size;
1882 this control is needed to modify that contains a currently
1883 non-existent attribute (but allowable for the object) to run
1885 LDAPControl PermitModify = {
1886 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1887 {0, NULL},
1888 (char) 1};
1889 LDAPControl *controls[2];
1891 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1893 controls[0] = &PermitModify;
1894 controls[1] = NULL;
1896 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1897 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1900 /* find the end of the list, marked by NULL or -1 */
1901 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1902 /* make sure the end of the list is NULL */
1903 mods[i] = NULL;
1904 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1905 (LDAPMod **) mods, controls, NULL);
1906 ads_print_error(ret, ads->ldap.ld);
1907 TALLOC_FREE(utf8_dn);
1908 return ADS_ERROR(ret);
1912 * Perform an ldap add
1913 * @param ads connection to ads server
1914 * @param new_dn DistinguishedName to add
1915 * @param mods list of attributes and values for DN
1916 * @return status of add
1918 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1920 int ret, i;
1921 char *utf8_dn = NULL;
1922 size_t converted_size;
1924 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1926 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1927 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1928 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1931 /* find the end of the list, marked by NULL or -1 */
1932 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1933 /* make sure the end of the list is NULL */
1934 mods[i] = NULL;
1936 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1937 ads_print_error(ret, ads->ldap.ld);
1938 TALLOC_FREE(utf8_dn);
1939 return ADS_ERROR(ret);
1943 * Delete a DistinguishedName
1944 * @param ads connection to ads server
1945 * @param new_dn DistinguishedName to delete
1946 * @return status of delete
1948 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1950 int ret;
1951 char *utf8_dn = NULL;
1952 size_t converted_size;
1953 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1954 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1955 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1958 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1960 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1961 ads_print_error(ret, ads->ldap.ld);
1962 TALLOC_FREE(utf8_dn);
1963 return ADS_ERROR(ret);
1967 * Build an org unit string
1968 * if org unit is Computers or blank then assume a container, otherwise
1969 * assume a / separated list of organisational units.
1970 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1971 * @param ads connection to ads server
1972 * @param org_unit Organizational unit
1973 * @return org unit string - caller must free
1975 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1977 ADS_STATUS status;
1978 char *ret = NULL;
1979 char *dn = NULL;
1981 if (!org_unit || !*org_unit) {
1983 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1985 /* samba4 might not yet respond to a wellknownobject-query */
1986 return ret ? ret : SMB_STRDUP("cn=Computers");
1989 if (strequal(org_unit, "Computers")) {
1990 return SMB_STRDUP("cn=Computers");
1993 /* jmcd: removed "\\" from the separation chars, because it is
1994 needed as an escape for chars like '#' which are valid in an
1995 OU name */
1996 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
1997 if (!ADS_ERR_OK(status)) {
1998 return NULL;
2001 return dn;
2005 * Get a org unit string for a well-known GUID
2006 * @param ads connection to ads server
2007 * @param wknguid Well known GUID
2008 * @return org unit string - caller must free
2010 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2012 ADS_STATUS status;
2013 LDAPMessage *res = NULL;
2014 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2015 **bind_dn_exp = NULL;
2016 const char *attrs[] = {"distinguishedName", NULL};
2017 int new_ln, wkn_ln, bind_ln, i;
2019 if (wknguid == NULL) {
2020 return NULL;
2023 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2024 DEBUG(1, ("asprintf failed!\n"));
2025 return NULL;
2028 status = ads_search_dn(ads, &res, base, attrs);
2029 if (!ADS_ERR_OK(status)) {
2030 DEBUG(1,("Failed while searching for: %s\n", base));
2031 goto out;
2034 if (ads_count_replies(ads, res) != 1) {
2035 goto out;
2038 /* substitute the bind-path from the well-known-guid-search result */
2039 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2040 if (!wkn_dn) {
2041 goto out;
2044 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2045 if (!wkn_dn_exp) {
2046 goto out;
2049 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2050 if (!bind_dn_exp) {
2051 goto out;
2054 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2056 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2059 new_ln = wkn_ln - bind_ln;
2061 ret = SMB_STRDUP(wkn_dn_exp[0]);
2062 if (!ret) {
2063 goto out;
2066 for (i=1; i < new_ln; i++) {
2067 char *s = NULL;
2069 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2070 SAFE_FREE(ret);
2071 goto out;
2074 SAFE_FREE(ret);
2075 ret = SMB_STRDUP(s);
2076 free(s);
2077 if (!ret) {
2078 goto out;
2082 out:
2083 SAFE_FREE(base);
2084 ads_msgfree(ads, res);
2085 TALLOC_FREE(wkn_dn);
2086 if (wkn_dn_exp) {
2087 ldap_value_free(wkn_dn_exp);
2089 if (bind_dn_exp) {
2090 ldap_value_free(bind_dn_exp);
2093 return ret;
2097 * Adds (appends) an item to an attribute array, rather then
2098 * replacing the whole list
2099 * @param ctx An initialized TALLOC_CTX
2100 * @param mods An initialized ADS_MODLIST
2101 * @param name name of the ldap attribute to append to
2102 * @param vals an array of values to add
2103 * @return status of addition
2106 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2107 const char *name, const char **vals)
2109 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2110 (const void *) vals);
2114 * Determines the an account's current KVNO via an LDAP lookup
2115 * @param ads An initialized ADS_STRUCT
2116 * @param account_name the NT samaccountname.
2117 * @return the kvno for the account, or -1 in case of a failure.
2120 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
2122 LDAPMessage *res = NULL;
2123 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
2124 char *filter;
2125 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
2126 char *dn_string = NULL;
2127 ADS_STATUS ret;
2129 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
2130 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
2131 return kvno;
2133 ret = ads_search(ads, &res, filter, attrs);
2134 SAFE_FREE(filter);
2135 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
2136 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
2137 ads_msgfree(ads, res);
2138 return kvno;
2141 dn_string = ads_get_dn(ads, talloc_tos(), res);
2142 if (!dn_string) {
2143 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2144 ads_msgfree(ads, res);
2145 return kvno;
2147 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
2148 TALLOC_FREE(dn_string);
2150 /* ---------------------------------------------------------
2151 * 0 is returned as a default KVNO from this point on...
2152 * This is done because Windows 2000 does not support key
2153 * version numbers. Chances are that a failure in the next
2154 * step is simply due to Windows 2000 being used for a
2155 * domain controller. */
2156 kvno = 0;
2158 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2159 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2160 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2161 ads_msgfree(ads, res);
2162 return kvno;
2165 /* Success */
2166 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2167 ads_msgfree(ads, res);
2168 return kvno;
2172 * Determines the computer account's current KVNO via an LDAP lookup
2173 * @param ads An initialized ADS_STRUCT
2174 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2175 * @return the kvno for the computer account, or -1 in case of a failure.
2178 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2180 char *computer_account = NULL;
2181 uint32_t kvno = -1;
2183 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2184 return kvno;
2187 kvno = ads_get_kvno(ads, computer_account);
2188 free(computer_account);
2190 return kvno;
2194 * This clears out all registered spn's for a given hostname
2195 * @param ads An initilaized ADS_STRUCT
2196 * @param machine_name the NetBIOS name of the computer.
2197 * @return 0 upon success, non-zero otherwise.
2200 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2202 TALLOC_CTX *ctx;
2203 LDAPMessage *res = NULL;
2204 ADS_MODLIST mods;
2205 const char *servicePrincipalName[1] = {NULL};
2206 ADS_STATUS ret;
2207 char *dn_string = NULL;
2209 ret = ads_find_machine_acct(ads, &res, machine_name);
2210 if (!ADS_ERR_OK(ret)) {
2211 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2212 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2213 ads_msgfree(ads, res);
2214 return ret;
2217 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2218 ctx = talloc_init("ads_clear_service_principal_names");
2219 if (!ctx) {
2220 ads_msgfree(ads, res);
2221 return ADS_ERROR(LDAP_NO_MEMORY);
2224 if (!(mods = ads_init_mods(ctx))) {
2225 talloc_destroy(ctx);
2226 ads_msgfree(ads, res);
2227 return ADS_ERROR(LDAP_NO_MEMORY);
2229 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2230 if (!ADS_ERR_OK(ret)) {
2231 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2232 ads_msgfree(ads, res);
2233 talloc_destroy(ctx);
2234 return ret;
2236 dn_string = ads_get_dn(ads, talloc_tos(), res);
2237 if (!dn_string) {
2238 talloc_destroy(ctx);
2239 ads_msgfree(ads, res);
2240 return ADS_ERROR(LDAP_NO_MEMORY);
2242 ret = ads_gen_mod(ads, dn_string, mods);
2243 TALLOC_FREE(dn_string);
2244 if (!ADS_ERR_OK(ret)) {
2245 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2246 machine_name));
2247 ads_msgfree(ads, res);
2248 talloc_destroy(ctx);
2249 return ret;
2252 ads_msgfree(ads, res);
2253 talloc_destroy(ctx);
2254 return ret;
2258 * @brief Search for an element in a string array.
2260 * @param[in] el_array The string array to search.
2262 * @param[in] num_el The number of elements in the string array.
2264 * @param[in] el The string to search.
2266 * @return True if found, false if not.
2268 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2270 size_t i;
2272 if (el_array == NULL || num_el == 0 || el == NULL) {
2273 return false;
2276 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2277 int cmp;
2279 cmp = strcasecmp_m(el_array[i], el);
2280 if (cmp == 0) {
2281 return true;
2285 return false;
2289 * @brief This gets the service principal names of an existing computer account.
2291 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2293 * @param[in] ads The ADS context to use.
2295 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2296 * identify the computer account.
2298 * @param[in] spn_array A pointer to store the array for SPNs.
2300 * @param[in] num_spns The number of principals stored in the array.
2302 * @return 0 on success, or a ADS error if a failure occurred.
2304 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2305 ADS_STRUCT *ads,
2306 const char *machine_name,
2307 char ***spn_array,
2308 size_t *num_spns)
2310 ADS_STATUS status;
2311 LDAPMessage *res = NULL;
2312 int count;
2314 status = ads_find_machine_acct(ads,
2315 &res,
2316 machine_name);
2317 if (!ADS_ERR_OK(status)) {
2318 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2319 machine_name));
2320 return status;
2323 count = ads_count_replies(ads, res);
2324 if (count != 1) {
2325 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2326 goto done;
2329 *spn_array = ads_pull_strings(ads,
2330 mem_ctx,
2331 res,
2332 "servicePrincipalName",
2333 num_spns);
2334 if (*spn_array == NULL) {
2335 DEBUG(1, ("Host account for %s does not have service principal "
2336 "names.\n",
2337 machine_name));
2338 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2339 goto done;
2342 done:
2343 ads_msgfree(ads, res);
2345 return status;
2349 * This adds a service principal name to an existing computer account
2350 * (found by hostname) in AD.
2351 * @param ads An initialized ADS_STRUCT
2352 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2353 * @param spns An array or strings for the service principals to add,
2354 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2355 * @return 0 upon success, or non-zero if a failure occurs
2358 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2359 const char *machine_name,
2360 const char **spns)
2362 ADS_STATUS ret;
2363 TALLOC_CTX *ctx;
2364 LDAPMessage *res = NULL;
2365 ADS_MODLIST mods;
2366 char *dn_string = NULL;
2367 const char **servicePrincipalName = spns;
2369 ret = ads_find_machine_acct(ads, &res, machine_name);
2370 if (!ADS_ERR_OK(ret)) {
2371 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2372 machine_name));
2373 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2374 ads_msgfree(ads, res);
2375 return ret;
2378 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2379 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2380 ads_msgfree(ads, res);
2381 return ADS_ERROR(LDAP_NO_MEMORY);
2384 DEBUG(5,("ads_add_service_principal_name: INFO: "
2385 "Adding %s to host %s\n",
2386 spns[0] ? "N/A" : spns[0], machine_name));
2389 DEBUG(5,("ads_add_service_principal_name: INFO: "
2390 "Adding %s to host %s\n",
2391 spns[1] ? "N/A" : spns[1], machine_name));
2393 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2394 ret = ADS_ERROR(LDAP_NO_MEMORY);
2395 goto out;
2398 ret = ads_add_strlist(ctx,
2399 &mods,
2400 "servicePrincipalName",
2401 servicePrincipalName);
2402 if (!ADS_ERR_OK(ret)) {
2403 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2404 goto out;
2407 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2408 ret = ADS_ERROR(LDAP_NO_MEMORY);
2409 goto out;
2412 ret = ads_gen_mod(ads, dn_string, mods);
2413 if (!ADS_ERR_OK(ret)) {
2414 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2415 goto out;
2418 out:
2419 TALLOC_FREE( ctx );
2420 ads_msgfree(ads, res);
2421 return ret;
2424 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2425 LDAPMessage *msg)
2427 uint32_t acct_ctrl = 0;
2428 bool ok;
2430 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2431 if (!ok) {
2432 return 0;
2435 return acct_ctrl;
2438 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2439 LDAPMessage *msg,
2440 const struct berval *machine_pw_val)
2442 ADS_MODLIST mods;
2443 ADS_STATUS ret;
2444 TALLOC_CTX *frame = talloc_stackframe();
2445 uint32_t acct_control;
2446 char *control_str = NULL;
2447 const char *attrs[] = {
2448 "objectSid",
2449 NULL
2451 LDAPMessage *res = NULL;
2452 char *dn = NULL;
2454 dn = ads_get_dn(ads, frame, msg);
2455 if (dn == NULL) {
2456 ret = ADS_ERROR(LDAP_NO_MEMORY);
2457 goto done;
2460 acct_control = ads_get_acct_ctrl(ads, msg);
2461 if (acct_control == 0) {
2462 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2463 goto done;
2467 * Changing the password, disables the account. So we need to change the
2468 * userAccountControl flags to enable it again.
2470 mods = ads_init_mods(frame);
2471 if (mods == NULL) {
2472 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2473 goto done;
2476 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2478 ret = ads_gen_mod(ads, dn, mods);
2479 if (!ADS_ERR_OK(ret)) {
2480 goto done;
2482 TALLOC_FREE(mods);
2485 * To activate the account, we need to disable and enable it.
2487 acct_control |= UF_ACCOUNTDISABLE;
2489 control_str = talloc_asprintf(frame, "%u", acct_control);
2490 if (control_str == NULL) {
2491 ret = ADS_ERROR(LDAP_NO_MEMORY);
2492 goto done;
2495 mods = ads_init_mods(frame);
2496 if (mods == NULL) {
2497 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2498 goto done;
2501 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2503 ret = ads_gen_mod(ads, dn, mods);
2504 if (!ADS_ERR_OK(ret)) {
2505 goto done;
2507 TALLOC_FREE(mods);
2508 TALLOC_FREE(control_str);
2511 * Enable the account again.
2513 acct_control &= ~UF_ACCOUNTDISABLE;
2515 control_str = talloc_asprintf(frame, "%u", acct_control);
2516 if (control_str == NULL) {
2517 ret = ADS_ERROR(LDAP_NO_MEMORY);
2518 goto done;
2521 mods = ads_init_mods(frame);
2522 if (mods == NULL) {
2523 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2524 goto done;
2527 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2529 ret = ads_gen_mod(ads, dn, mods);
2530 if (!ADS_ERR_OK(ret)) {
2531 goto done;
2533 TALLOC_FREE(mods);
2534 TALLOC_FREE(control_str);
2536 ret = ads_search_dn(ads, &res, dn, attrs);
2537 ads_msgfree(ads, res);
2539 done:
2540 talloc_free(frame);
2542 return ret;
2546 * adds a machine account to the ADS server
2547 * @param ads An initialized ADS_STRUCT
2548 * @param machine_name - the NetBIOS machine name of this account.
2549 * @param account_type A number indicating the type of account to create
2550 * @param org_unit The LDAP path in which to place this account
2551 * @return 0 upon success, or non-zero otherwise
2554 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2555 const char *machine_name,
2556 const char *machine_password,
2557 const char *org_unit,
2558 uint32_t etype_list,
2559 const char *dns_domain_name)
2561 ADS_STATUS ret;
2562 char *samAccountName = NULL;
2563 char *controlstr = NULL;
2564 TALLOC_CTX *ctx = NULL;
2565 ADS_MODLIST mods;
2566 char *machine_escaped = NULL;
2567 char *dns_hostname = NULL;
2568 char *new_dn = NULL;
2569 char *utf8_pw = NULL;
2570 size_t utf8_pw_len = 0;
2571 char *utf16_pw = NULL;
2572 size_t utf16_pw_len = 0;
2573 struct berval machine_pw_val;
2574 bool ok;
2575 const char **spn_array = NULL;
2576 size_t num_spns = 0;
2577 const char *spn_prefix[] = {
2578 "HOST",
2579 "RestrictedKrbHost",
2581 size_t i;
2582 LDAPMessage *res = NULL;
2583 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2585 ctx = talloc_init("ads_add_machine_acct");
2586 if (ctx == NULL) {
2587 return ADS_ERROR(LDAP_NO_MEMORY);
2590 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2591 if (machine_escaped == NULL) {
2592 ret = ADS_ERROR(LDAP_NO_MEMORY);
2593 goto done;
2596 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2597 if (utf8_pw == NULL) {
2598 ret = ADS_ERROR(LDAP_NO_MEMORY);
2599 goto done;
2601 utf8_pw_len = strlen(utf8_pw);
2603 ok = convert_string_talloc(ctx,
2604 CH_UTF8, CH_UTF16MUNGED,
2605 utf8_pw, utf8_pw_len,
2606 (void *)&utf16_pw, &utf16_pw_len);
2607 if (!ok) {
2608 ret = ADS_ERROR(LDAP_NO_MEMORY);
2609 goto done;
2612 machine_pw_val = (struct berval) {
2613 .bv_val = utf16_pw,
2614 .bv_len = utf16_pw_len,
2617 /* Check if the machine account already exists. */
2618 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2619 if (ADS_ERR_OK(ret)) {
2620 /* Change the machine account password */
2621 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2622 ads_msgfree(ads, res);
2624 goto done;
2626 ads_msgfree(ads, res);
2628 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2629 if (new_dn == NULL) {
2630 ret = ADS_ERROR(LDAP_NO_MEMORY);
2631 goto done;
2634 /* Create machine account */
2636 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2637 if (samAccountName == NULL) {
2638 ret = ADS_ERROR(LDAP_NO_MEMORY);
2639 goto done;
2642 dns_hostname = talloc_asprintf(ctx,
2643 "%s.%s",
2644 machine_name,
2645 dns_domain_name);
2646 if (dns_hostname == NULL) {
2647 ret = ADS_ERROR(LDAP_NO_MEMORY);
2648 goto done;
2651 /* Add dns_hostname SPNs */
2652 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2653 char *spn = talloc_asprintf(ctx,
2654 "%s/%s",
2655 spn_prefix[i],
2656 dns_hostname);
2657 if (spn == NULL) {
2658 ret = ADS_ERROR(LDAP_NO_MEMORY);
2659 goto done;
2662 ok = add_string_to_array(spn_array,
2663 spn,
2664 &spn_array,
2665 &num_spns);
2666 if (!ok) {
2667 ret = ADS_ERROR(LDAP_NO_MEMORY);
2668 goto done;
2672 /* Add machine_name SPNs */
2673 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2674 char *spn = talloc_asprintf(ctx,
2675 "%s/%s",
2676 spn_prefix[i],
2677 machine_name);
2678 if (spn == NULL) {
2679 ret = ADS_ERROR(LDAP_NO_MEMORY);
2680 goto done;
2683 ok = add_string_to_array(spn_array,
2684 spn,
2685 &spn_array,
2686 &num_spns);
2687 if (!ok) {
2688 ret = ADS_ERROR(LDAP_NO_MEMORY);
2689 goto done;
2693 /* Make sure to NULL terminate the array */
2694 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2695 if (spn_array == NULL) {
2696 ret = ADS_ERROR(LDAP_NO_MEMORY);
2697 goto done;
2699 spn_array[num_spns] = NULL;
2701 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2702 if (controlstr == NULL) {
2703 ret = ADS_ERROR(LDAP_NO_MEMORY);
2704 goto done;
2707 mods = ads_init_mods(ctx);
2708 if (mods == NULL) {
2709 ret = ADS_ERROR(LDAP_NO_MEMORY);
2710 goto done;
2713 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2714 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2715 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2716 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2717 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2718 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2720 ret = ads_gen_add(ads, new_dn, mods);
2722 done:
2723 SAFE_FREE(machine_escaped);
2724 talloc_destroy(ctx);
2726 return ret;
2730 * move a machine account to another OU on the ADS server
2731 * @param ads - An initialized ADS_STRUCT
2732 * @param machine_name - the NetBIOS machine name of this account.
2733 * @param org_unit - The LDAP path in which to place this account
2734 * @param moved - whether we moved the machine account (optional)
2735 * @return 0 upon success, or non-zero otherwise
2738 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2739 const char *org_unit, bool *moved)
2741 ADS_STATUS rc;
2742 int ldap_status;
2743 LDAPMessage *res = NULL;
2744 char *filter = NULL;
2745 char *computer_dn = NULL;
2746 char *parent_dn;
2747 char *computer_rdn = NULL;
2748 bool need_move = False;
2750 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2751 rc = ADS_ERROR(LDAP_NO_MEMORY);
2752 goto done;
2755 /* Find pre-existing machine */
2756 rc = ads_search(ads, &res, filter, NULL);
2757 if (!ADS_ERR_OK(rc)) {
2758 goto done;
2761 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2762 if (!computer_dn) {
2763 rc = ADS_ERROR(LDAP_NO_MEMORY);
2764 goto done;
2767 parent_dn = ads_parent_dn(computer_dn);
2768 if (strequal(parent_dn, org_unit)) {
2769 goto done;
2772 need_move = True;
2774 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2775 rc = ADS_ERROR(LDAP_NO_MEMORY);
2776 goto done;
2779 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2780 org_unit, 1, NULL, NULL);
2781 rc = ADS_ERROR(ldap_status);
2783 done:
2784 ads_msgfree(ads, res);
2785 SAFE_FREE(filter);
2786 TALLOC_FREE(computer_dn);
2787 SAFE_FREE(computer_rdn);
2789 if (!ADS_ERR_OK(rc)) {
2790 need_move = False;
2793 if (moved) {
2794 *moved = need_move;
2797 return rc;
2801 dump a binary result from ldap
2803 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2805 size_t i;
2806 for (i=0; values[i]; i++) {
2807 ber_len_t j;
2808 printf("%s: ", field);
2809 for (j=0; j<values[i]->bv_len; j++) {
2810 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2812 printf("\n");
2816 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2818 int i;
2819 for (i=0; values[i]; i++) {
2820 NTSTATUS status;
2821 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2822 struct GUID guid;
2824 status = GUID_from_ndr_blob(&in, &guid);
2825 if (NT_STATUS_IS_OK(status)) {
2826 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2827 } else {
2828 printf("%s: INVALID GUID\n", field);
2834 dump a sid result from ldap
2836 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2838 int i;
2839 for (i=0; values[i]; i++) {
2840 ssize_t ret;
2841 struct dom_sid sid;
2842 struct dom_sid_buf tmp;
2843 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2844 values[i]->bv_len, &sid);
2845 if (ret == -1) {
2846 return;
2848 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2853 dump ntSecurityDescriptor
2855 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2857 TALLOC_CTX *frame = talloc_stackframe();
2858 struct security_descriptor *psd;
2859 NTSTATUS status;
2861 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2862 values[0]->bv_len, &psd);
2863 if (!NT_STATUS_IS_OK(status)) {
2864 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2865 nt_errstr(status)));
2866 TALLOC_FREE(frame);
2867 return;
2870 if (psd) {
2871 ads_disp_sd(ads, talloc_tos(), psd);
2874 TALLOC_FREE(frame);
2878 dump a string result from ldap
2880 static void dump_string(const char *field, char **values)
2882 int i;
2883 for (i=0; values[i]; i++) {
2884 printf("%s: %s\n", field, values[i]);
2889 dump a field from LDAP on stdout
2890 used for debugging
2893 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2895 const struct {
2896 const char *name;
2897 bool string;
2898 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2899 } handlers[] = {
2900 {"objectGUID", False, dump_guid},
2901 {"netbootGUID", False, dump_guid},
2902 {"nTSecurityDescriptor", False, dump_sd},
2903 {"dnsRecord", False, dump_binary},
2904 {"objectSid", False, dump_sid},
2905 {"tokenGroups", False, dump_sid},
2906 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2907 {"tokengroupsGlobalandUniversal", False, dump_sid},
2908 {"mS-DS-CreatorSID", False, dump_sid},
2909 {"msExchMailboxGuid", False, dump_guid},
2910 {NULL, True, NULL}
2912 int i;
2914 if (!field) { /* must be end of an entry */
2915 printf("\n");
2916 return False;
2919 for (i=0; handlers[i].name; i++) {
2920 if (strcasecmp_m(handlers[i].name, field) == 0) {
2921 if (!values) /* first time, indicate string or not */
2922 return handlers[i].string;
2923 handlers[i].handler(ads, field, (struct berval **) values);
2924 break;
2927 if (!handlers[i].name) {
2928 if (!values) /* first time, indicate string conversion */
2929 return True;
2930 dump_string(field, (char **)values);
2932 return False;
2936 * Dump a result from LDAP on stdout
2937 * used for debugging
2938 * @param ads connection to ads server
2939 * @param res Results to dump
2942 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2944 ads_process_results(ads, res, ads_dump_field, NULL);
2948 * Walk through results, calling a function for each entry found.
2949 * The function receives a field name, a berval * array of values,
2950 * and a data area passed through from the start. The function is
2951 * called once with null for field and values at the end of each
2952 * entry.
2953 * @param ads connection to ads server
2954 * @param res Results to process
2955 * @param fn Function for processing each result
2956 * @param data_area user-defined area to pass to function
2958 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2959 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2960 void *data_area)
2962 LDAPMessage *msg;
2963 TALLOC_CTX *ctx;
2964 size_t converted_size;
2966 if (!(ctx = talloc_init("ads_process_results")))
2967 return;
2969 for (msg = ads_first_entry(ads, res); msg;
2970 msg = ads_next_entry(ads, msg)) {
2971 char *utf8_field;
2972 BerElement *b;
2974 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2975 (LDAPMessage *)msg,&b);
2976 utf8_field;
2977 utf8_field=ldap_next_attribute(ads->ldap.ld,
2978 (LDAPMessage *)msg,b)) {
2979 struct berval **ber_vals;
2980 char **str_vals;
2981 char **utf8_vals;
2982 char *field;
2983 bool string;
2985 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2986 &converted_size))
2988 DEBUG(0,("ads_process_results: "
2989 "pull_utf8_talloc failed: %s",
2990 strerror(errno)));
2993 string = fn(ads, field, NULL, data_area);
2995 if (string) {
2996 const char **p;
2998 utf8_vals = ldap_get_values(ads->ldap.ld,
2999 (LDAPMessage *)msg, field);
3000 p = discard_const_p(const char *, utf8_vals);
3001 str_vals = ads_pull_strvals(ctx, p);
3002 fn(ads, field, (void **) str_vals, data_area);
3003 ldap_value_free(utf8_vals);
3004 } else {
3005 ber_vals = ldap_get_values_len(ads->ldap.ld,
3006 (LDAPMessage *)msg, field);
3007 fn(ads, field, (void **) ber_vals, data_area);
3009 ldap_value_free_len(ber_vals);
3011 ldap_memfree(utf8_field);
3013 ber_free(b, 0);
3014 talloc_free_children(ctx);
3015 fn(ads, NULL, NULL, data_area); /* completed an entry */
3018 talloc_destroy(ctx);
3022 * count how many replies are in a LDAPMessage
3023 * @param ads connection to ads server
3024 * @param res Results to count
3025 * @return number of replies
3027 int ads_count_replies(ADS_STRUCT *ads, void *res)
3029 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3033 * pull the first entry from a ADS result
3034 * @param ads connection to ads server
3035 * @param res Results of search
3036 * @return first entry from result
3038 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3040 return ldap_first_entry(ads->ldap.ld, res);
3044 * pull the next entry from a ADS result
3045 * @param ads connection to ads server
3046 * @param res Results of search
3047 * @return next entry from result
3049 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3051 return ldap_next_entry(ads->ldap.ld, res);
3055 * pull the first message from a ADS result
3056 * @param ads connection to ads server
3057 * @param res Results of search
3058 * @return first message from result
3060 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3062 return ldap_first_message(ads->ldap.ld, res);
3066 * pull the next message from a ADS result
3067 * @param ads connection to ads server
3068 * @param res Results of search
3069 * @return next message from result
3071 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3073 return ldap_next_message(ads->ldap.ld, res);
3077 * pull a single string from a ADS result
3078 * @param ads connection to ads server
3079 * @param mem_ctx TALLOC_CTX to use for allocating result string
3080 * @param msg Results of search
3081 * @param field Attribute to retrieve
3082 * @return Result string in talloc context
3084 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3085 const char *field)
3087 char **values;
3088 char *ret = NULL;
3089 char *ux_string;
3090 size_t converted_size;
3092 values = ldap_get_values(ads->ldap.ld, msg, field);
3093 if (!values)
3094 return NULL;
3096 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3097 &converted_size))
3099 ret = ux_string;
3101 ldap_value_free(values);
3102 return ret;
3106 * pull an array of strings from a ADS result
3107 * @param ads connection to ads server
3108 * @param mem_ctx TALLOC_CTX to use for allocating result string
3109 * @param msg Results of search
3110 * @param field Attribute to retrieve
3111 * @return Result strings in talloc context
3113 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3114 LDAPMessage *msg, const char *field,
3115 size_t *num_values)
3117 char **values;
3118 char **ret = NULL;
3119 size_t i, converted_size;
3121 values = ldap_get_values(ads->ldap.ld, msg, field);
3122 if (!values)
3123 return NULL;
3125 *num_values = ldap_count_values(values);
3127 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3128 if (!ret) {
3129 ldap_value_free(values);
3130 return NULL;
3133 for (i=0;i<*num_values;i++) {
3134 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3135 &converted_size))
3137 ldap_value_free(values);
3138 return NULL;
3141 ret[i] = NULL;
3143 ldap_value_free(values);
3144 return ret;
3148 * pull an array of strings from a ADS result
3149 * (handle large multivalue attributes with range retrieval)
3150 * @param ads connection to ads server
3151 * @param mem_ctx TALLOC_CTX to use for allocating result string
3152 * @param msg Results of search
3153 * @param field Attribute to retrieve
3154 * @param current_strings strings returned by a previous call to this function
3155 * @param next_attribute The next query should ask for this attribute
3156 * @param num_values How many values did we get this time?
3157 * @param more_values Are there more values to get?
3158 * @return Result strings in talloc context
3160 char **ads_pull_strings_range(ADS_STRUCT *ads,
3161 TALLOC_CTX *mem_ctx,
3162 LDAPMessage *msg, const char *field,
3163 char **current_strings,
3164 const char **next_attribute,
3165 size_t *num_strings,
3166 bool *more_strings)
3168 char *attr;
3169 char *expected_range_attrib, *range_attr;
3170 BerElement *ptr = NULL;
3171 char **strings;
3172 char **new_strings;
3173 size_t num_new_strings;
3174 unsigned long int range_start;
3175 unsigned long int range_end;
3177 /* we might have been given the whole lot anyway */
3178 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3179 *more_strings = False;
3180 return strings;
3183 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3185 /* look for Range result */
3186 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3187 attr;
3188 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3189 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3190 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3191 range_attr = attr;
3192 break;
3194 ldap_memfree(attr);
3196 if (!attr) {
3197 ber_free(ptr, 0);
3198 /* nothing here - this field is just empty */
3199 *more_strings = False;
3200 return NULL;
3203 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3204 &range_start, &range_end) == 2) {
3205 *more_strings = True;
3206 } else {
3207 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3208 &range_start) == 1) {
3209 *more_strings = False;
3210 } else {
3211 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
3212 range_attr));
3213 ldap_memfree(range_attr);
3214 *more_strings = False;
3215 return NULL;
3219 if ((*num_strings) != range_start) {
3220 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3221 " - aborting range retrieval\n",
3222 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3223 ldap_memfree(range_attr);
3224 *more_strings = False;
3225 return NULL;
3228 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3230 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3231 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3232 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3233 range_attr, (unsigned long int)range_end - range_start + 1,
3234 (unsigned long int)num_new_strings));
3235 ldap_memfree(range_attr);
3236 *more_strings = False;
3237 return NULL;
3240 strings = talloc_realloc(mem_ctx, current_strings, char *,
3241 *num_strings + num_new_strings);
3243 if (strings == NULL) {
3244 ldap_memfree(range_attr);
3245 *more_strings = False;
3246 return NULL;
3249 if (new_strings && num_new_strings) {
3250 memcpy(&strings[*num_strings], new_strings,
3251 sizeof(*new_strings) * num_new_strings);
3254 (*num_strings) += num_new_strings;
3256 if (*more_strings) {
3257 *next_attribute = talloc_asprintf(mem_ctx,
3258 "%s;range=%d-*",
3259 field,
3260 (int)*num_strings);
3262 if (!*next_attribute) {
3263 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3264 ldap_memfree(range_attr);
3265 *more_strings = False;
3266 return NULL;
3270 ldap_memfree(range_attr);
3272 return strings;
3276 * pull a single uint32_t from a ADS result
3277 * @param ads connection to ads server
3278 * @param msg Results of search
3279 * @param field Attribute to retrieve
3280 * @param v Pointer to int to store result
3281 * @return boolean indicating success
3283 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3284 uint32_t *v)
3286 char **values;
3288 values = ldap_get_values(ads->ldap.ld, msg, field);
3289 if (!values)
3290 return False;
3291 if (!values[0]) {
3292 ldap_value_free(values);
3293 return False;
3296 *v = atoi(values[0]);
3297 ldap_value_free(values);
3298 return True;
3302 * pull a single objectGUID from an ADS result
3303 * @param ads connection to ADS server
3304 * @param msg results of search
3305 * @param guid 37-byte area to receive text guid
3306 * @return boolean indicating success
3308 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3310 DATA_BLOB blob;
3311 NTSTATUS status;
3313 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3314 &blob)) {
3315 return false;
3318 status = GUID_from_ndr_blob(&blob, guid);
3319 talloc_free(blob.data);
3320 return NT_STATUS_IS_OK(status);
3325 * pull a single struct dom_sid from a ADS result
3326 * @param ads connection to ads server
3327 * @param msg Results of search
3328 * @param field Attribute to retrieve
3329 * @param sid Pointer to sid to store result
3330 * @return boolean indicating success
3332 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3333 struct dom_sid *sid)
3335 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3339 * pull an array of struct dom_sids from a ADS result
3340 * @param ads connection to ads server
3341 * @param mem_ctx TALLOC_CTX for allocating sid array
3342 * @param msg Results of search
3343 * @param field Attribute to retrieve
3344 * @param sids pointer to sid array to allocate
3345 * @return the count of SIDs pulled
3347 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3348 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3350 struct berval **values;
3351 int count, i;
3353 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3355 if (!values)
3356 return 0;
3358 for (i=0; values[i]; i++)
3359 /* nop */ ;
3361 if (i) {
3362 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3363 if (!(*sids)) {
3364 ldap_value_free_len(values);
3365 return 0;
3367 } else {
3368 (*sids) = NULL;
3371 count = 0;
3372 for (i=0; values[i]; i++) {
3373 ssize_t ret;
3374 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3375 values[i]->bv_len, &(*sids)[count]);
3376 if (ret != -1) {
3377 struct dom_sid_buf buf;
3378 DBG_DEBUG("pulling SID: %s\n",
3379 dom_sid_str_buf(&(*sids)[count], &buf));
3380 count++;
3384 ldap_value_free_len(values);
3385 return count;
3389 * pull a struct security_descriptor from a ADS result
3390 * @param ads connection to ads server
3391 * @param mem_ctx TALLOC_CTX for allocating sid array
3392 * @param msg Results of search
3393 * @param field Attribute to retrieve
3394 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3395 * @return boolean indicating success
3397 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3398 LDAPMessage *msg, const char *field,
3399 struct security_descriptor **sd)
3401 struct berval **values;
3402 bool ret = true;
3404 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3406 if (!values) return false;
3408 if (values[0]) {
3409 NTSTATUS status;
3410 status = unmarshall_sec_desc(mem_ctx,
3411 (uint8_t *)values[0]->bv_val,
3412 values[0]->bv_len, sd);
3413 if (!NT_STATUS_IS_OK(status)) {
3414 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3415 nt_errstr(status)));
3416 ret = false;
3420 ldap_value_free_len(values);
3421 return ret;
3425 * in order to support usernames longer than 21 characters we need to
3426 * use both the sAMAccountName and the userPrincipalName attributes
3427 * It seems that not all users have the userPrincipalName attribute set
3429 * @param ads connection to ads server
3430 * @param mem_ctx TALLOC_CTX for allocating sid array
3431 * @param msg Results of search
3432 * @return the username
3434 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3435 LDAPMessage *msg)
3437 #if 0 /* JERRY */
3438 char *ret, *p;
3440 /* lookup_name() only works on the sAMAccountName to
3441 returning the username portion of userPrincipalName
3442 breaks winbindd_getpwnam() */
3444 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3445 if (ret && (p = strchr_m(ret, '@'))) {
3446 *p = 0;
3447 return ret;
3449 #endif
3450 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3455 * find the update serial number - this is the core of the ldap cache
3456 * @param ads connection to ads server
3457 * @param ads connection to ADS server
3458 * @param usn Pointer to retrieved update serial number
3459 * @return status of search
3461 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3463 const char *attrs[] = {"highestCommittedUSN", NULL};
3464 ADS_STATUS status;
3465 LDAPMessage *res;
3467 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3468 if (!ADS_ERR_OK(status))
3469 return status;
3471 if (ads_count_replies(ads, res) != 1) {
3472 ads_msgfree(ads, res);
3473 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3476 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3477 ads_msgfree(ads, res);
3478 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3481 ads_msgfree(ads, res);
3482 return ADS_SUCCESS;
3485 /* parse a ADS timestring - typical string is
3486 '20020917091222.0Z0' which means 09:12.22 17th September
3487 2002, timezone 0 */
3488 static time_t ads_parse_time(const char *str)
3490 struct tm tm;
3492 ZERO_STRUCT(tm);
3494 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3495 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3496 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3497 return 0;
3499 tm.tm_year -= 1900;
3500 tm.tm_mon -= 1;
3502 return timegm(&tm);
3505 /********************************************************************
3506 ********************************************************************/
3508 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3510 const char *attrs[] = {"currentTime", NULL};
3511 ADS_STATUS status;
3512 LDAPMessage *res;
3513 char *timestr;
3514 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3515 ADS_STRUCT *ads_s = ads;
3517 /* establish a new ldap tcp session if necessary */
3519 if ( !ads->ldap.ld ) {
3521 * ADS_STRUCT may be being reused after a
3522 * DC lookup, so ads->ldap.ss may already have a
3523 * good address. If not, re-initialize the passed-in
3524 * ADS_STRUCT with the given server.XXXX parameters.
3526 * Note that this doesn't depend on
3527 * ads->server.ldap_server != NULL,
3528 * as the case where ads->server.ldap_server==NULL and
3529 * ads->ldap.ss != zero_address is precisely the DC
3530 * lookup case where ads->ldap.ss was found by going
3531 * through ads_find_dc() again we want to avoid repeating.
3533 if (is_zero_addr(&ads->ldap.ss)) {
3534 ads_s = ads_init(tmp_ctx,
3535 ads->server.realm,
3536 ads->server.workgroup,
3537 ads->server.ldap_server,
3538 ADS_SASL_PLAIN );
3539 if (ads_s == NULL) {
3540 status = ADS_ERROR(LDAP_NO_MEMORY);
3541 goto done;
3546 * Reset ads->config.flags as it can contain the flags
3547 * returned by the previous CLDAP ping when reusing the struct.
3549 ads_s->config.flags = 0;
3551 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3552 status = ads_connect( ads_s );
3553 if ( !ADS_ERR_OK(status))
3554 goto done;
3557 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3558 if (!ADS_ERR_OK(status)) {
3559 goto done;
3562 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3563 if (!timestr) {
3564 ads_msgfree(ads_s, res);
3565 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3566 goto done;
3569 /* but save the time and offset in the original ADS_STRUCT */
3571 ads->config.current_time = ads_parse_time(timestr);
3573 if (ads->config.current_time != 0) {
3574 ads->auth.time_offset = ads->config.current_time - time(NULL);
3575 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3578 ads_msgfree(ads, res);
3580 status = ADS_SUCCESS;
3582 done:
3583 TALLOC_FREE(tmp_ctx);
3585 return status;
3588 /********************************************************************
3589 ********************************************************************/
3591 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3593 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3594 const char *attrs[] = {"domainFunctionality", NULL};
3595 ADS_STATUS status;
3596 LDAPMessage *res;
3597 ADS_STRUCT *ads_s = ads;
3599 *val = DS_DOMAIN_FUNCTION_2000;
3601 /* establish a new ldap tcp session if necessary */
3603 if ( !ads->ldap.ld ) {
3605 * ADS_STRUCT may be being reused after a
3606 * DC lookup, so ads->ldap.ss may already have a
3607 * good address. If not, re-initialize the passed-in
3608 * ADS_STRUCT with the given server.XXXX parameters.
3610 * Note that this doesn't depend on
3611 * ads->server.ldap_server != NULL,
3612 * as the case where ads->server.ldap_server==NULL and
3613 * ads->ldap.ss != zero_address is precisely the DC
3614 * lookup case where ads->ldap.ss was found by going
3615 * through ads_find_dc() again we want to avoid repeating.
3617 if (is_zero_addr(&ads->ldap.ss)) {
3618 ads_s = ads_init(tmp_ctx,
3619 ads->server.realm,
3620 ads->server.workgroup,
3621 ads->server.ldap_server,
3622 ADS_SASL_PLAIN );
3623 if (ads_s == NULL ) {
3624 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3625 goto done;
3630 * Reset ads->config.flags as it can contain the flags
3631 * returned by the previous CLDAP ping when reusing the struct.
3633 ads_s->config.flags = 0;
3635 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3636 status = ads_connect( ads_s );
3637 if ( !ADS_ERR_OK(status))
3638 goto done;
3641 /* If the attribute does not exist assume it is a Windows 2000
3642 functional domain */
3644 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3645 if (!ADS_ERR_OK(status)) {
3646 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3647 status = ADS_SUCCESS;
3649 goto done;
3652 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3653 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3655 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3658 ads_msgfree(ads_s, res);
3660 done:
3661 TALLOC_FREE(tmp_ctx);
3663 return status;
3667 * find the domain sid for our domain
3668 * @param ads connection to ads server
3669 * @param sid Pointer to domain sid
3670 * @return status of search
3672 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3674 const char *attrs[] = {"objectSid", NULL};
3675 LDAPMessage *res;
3676 ADS_STATUS rc;
3678 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3679 attrs, &res);
3680 if (!ADS_ERR_OK(rc)) return rc;
3681 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3682 ads_msgfree(ads, res);
3683 return ADS_ERROR_SYSTEM(ENOENT);
3685 ads_msgfree(ads, res);
3687 return ADS_SUCCESS;
3691 * find our site name
3692 * @param ads connection to ads server
3693 * @param mem_ctx Pointer to talloc context
3694 * @param site_name Pointer to the sitename
3695 * @return status of search
3697 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3699 ADS_STATUS status;
3700 LDAPMessage *res;
3701 const char *dn, *service_name;
3702 const char *attrs[] = { "dsServiceName", NULL };
3704 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3705 if (!ADS_ERR_OK(status)) {
3706 return status;
3709 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3710 if (service_name == NULL) {
3711 ads_msgfree(ads, res);
3712 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3715 ads_msgfree(ads, res);
3717 /* go up three levels */
3718 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3719 if (dn == NULL) {
3720 return ADS_ERROR(LDAP_NO_MEMORY);
3723 *site_name = talloc_strdup(mem_ctx, dn);
3724 if (*site_name == NULL) {
3725 return ADS_ERROR(LDAP_NO_MEMORY);
3728 return status;
3730 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3735 * find the site dn where a machine resides
3736 * @param ads connection to ads server
3737 * @param mem_ctx Pointer to talloc context
3738 * @param computer_name name of the machine
3739 * @param site_name Pointer to the sitename
3740 * @return status of search
3742 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3744 ADS_STATUS status;
3745 LDAPMessage *res;
3746 const char *parent, *filter;
3747 char *config_context = NULL;
3748 char *dn;
3750 /* shortcut a query */
3751 if (strequal(computer_name, ads->config.ldap_server_name)) {
3752 return ads_site_dn(ads, mem_ctx, site_dn);
3755 status = ads_config_path(ads, mem_ctx, &config_context);
3756 if (!ADS_ERR_OK(status)) {
3757 return status;
3760 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3761 if (filter == NULL) {
3762 return ADS_ERROR(LDAP_NO_MEMORY);
3765 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3766 filter, NULL, &res);
3767 if (!ADS_ERR_OK(status)) {
3768 return status;
3771 if (ads_count_replies(ads, res) != 1) {
3772 ads_msgfree(ads, res);
3773 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3776 dn = ads_get_dn(ads, mem_ctx, res);
3777 if (dn == NULL) {
3778 ads_msgfree(ads, res);
3779 return ADS_ERROR(LDAP_NO_MEMORY);
3782 /* go up three levels */
3783 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3784 if (parent == NULL) {
3785 ads_msgfree(ads, res);
3786 TALLOC_FREE(dn);
3787 return ADS_ERROR(LDAP_NO_MEMORY);
3790 *site_dn = talloc_strdup(mem_ctx, parent);
3791 if (*site_dn == NULL) {
3792 ads_msgfree(ads, res);
3793 TALLOC_FREE(dn);
3794 return ADS_ERROR(LDAP_NO_MEMORY);
3797 TALLOC_FREE(dn);
3798 ads_msgfree(ads, res);
3800 return status;
3804 * get the upn suffixes for a domain
3805 * @param ads connection to ads server
3806 * @param mem_ctx Pointer to talloc context
3807 * @param suffixes Pointer to an array of suffixes
3808 * @param num_suffixes Pointer to the number of suffixes
3809 * @return status of search
3811 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3813 ADS_STATUS status;
3814 LDAPMessage *res;
3815 const char *base;
3816 char *config_context = NULL;
3817 const char *attrs[] = { "uPNSuffixes", NULL };
3819 status = ads_config_path(ads, mem_ctx, &config_context);
3820 if (!ADS_ERR_OK(status)) {
3821 return status;
3824 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3825 if (base == NULL) {
3826 return ADS_ERROR(LDAP_NO_MEMORY);
3829 status = ads_search_dn(ads, &res, base, attrs);
3830 if (!ADS_ERR_OK(status)) {
3831 return status;
3834 if (ads_count_replies(ads, res) != 1) {
3835 ads_msgfree(ads, res);
3836 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3839 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3840 if ((*suffixes) == NULL) {
3841 ads_msgfree(ads, res);
3842 return ADS_ERROR(LDAP_NO_MEMORY);
3845 ads_msgfree(ads, res);
3847 return status;
3851 * get the joinable ous for a domain
3852 * @param ads connection to ads server
3853 * @param mem_ctx Pointer to talloc context
3854 * @param ous Pointer to an array of ous
3855 * @param num_ous Pointer to the number of ous
3856 * @return status of search
3858 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3859 TALLOC_CTX *mem_ctx,
3860 char ***ous,
3861 size_t *num_ous)
3863 ADS_STATUS status;
3864 LDAPMessage *res = NULL;
3865 LDAPMessage *msg = NULL;
3866 const char *attrs[] = { "dn", NULL };
3867 int count = 0;
3869 status = ads_search(ads, &res,
3870 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3871 attrs);
3872 if (!ADS_ERR_OK(status)) {
3873 return status;
3876 count = ads_count_replies(ads, res);
3877 if (count < 1) {
3878 ads_msgfree(ads, res);
3879 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3882 for (msg = ads_first_entry(ads, res); msg;
3883 msg = ads_next_entry(ads, msg)) {
3884 const char **p = discard_const_p(const char *, *ous);
3885 char *dn = NULL;
3887 dn = ads_get_dn(ads, talloc_tos(), msg);
3888 if (!dn) {
3889 ads_msgfree(ads, res);
3890 return ADS_ERROR(LDAP_NO_MEMORY);
3893 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3894 TALLOC_FREE(dn);
3895 ads_msgfree(ads, res);
3896 return ADS_ERROR(LDAP_NO_MEMORY);
3899 TALLOC_FREE(dn);
3900 *ous = discard_const_p(char *, p);
3903 ads_msgfree(ads, res);
3905 return status;
3910 * pull a struct dom_sid from an extended dn string
3911 * @param mem_ctx TALLOC_CTX
3912 * @param extended_dn string
3913 * @param flags string type of extended_dn
3914 * @param sid pointer to a struct dom_sid
3915 * @return NT_STATUS_OK on success,
3916 * NT_INVALID_PARAMETER on error,
3917 * NT_STATUS_NOT_FOUND if no SID present
3919 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3920 const char *extended_dn,
3921 enum ads_extended_dn_flags flags,
3922 struct dom_sid *sid)
3924 char *p, *q, *dn;
3926 if (!extended_dn) {
3927 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3930 /* otherwise extended_dn gets stripped off */
3931 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3932 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3935 * ADS_EXTENDED_DN_HEX_STRING:
3936 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3938 * ADS_EXTENDED_DN_STRING (only with w2k3):
3939 * <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
3941 * Object with no SID, such as an Exchange Public Folder
3942 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3945 p = strchr(dn, ';');
3946 if (!p) {
3947 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3950 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3951 DEBUG(5,("No SID present in extended dn\n"));
3952 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3955 p += strlen(";<SID=");
3957 q = strchr(p, '>');
3958 if (!q) {
3959 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3962 *q = '\0';
3964 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3966 switch (flags) {
3968 case ADS_EXTENDED_DN_STRING:
3969 if (!string_to_sid(sid, p)) {
3970 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3972 break;
3973 case ADS_EXTENDED_DN_HEX_STRING: {
3974 ssize_t ret;
3975 fstring buf;
3976 size_t buf_len;
3978 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3979 if (buf_len == 0) {
3980 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3983 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3984 if (ret == -1) {
3985 DEBUG(10,("failed to parse sid\n"));
3986 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3988 break;
3990 default:
3991 DEBUG(10,("unknown extended dn format\n"));
3992 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3995 return ADS_ERROR_NT(NT_STATUS_OK);
3998 /********************************************************************
3999 ********************************************************************/
4001 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4003 LDAPMessage *res = NULL;
4004 ADS_STATUS status;
4005 int count = 0;
4006 char *name = NULL;
4008 status = ads_find_machine_acct(ads, &res, machine_name);
4009 if (!ADS_ERR_OK(status)) {
4010 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4011 lp_netbios_name()));
4012 goto out;
4015 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4016 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4017 goto out;
4020 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4021 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4024 out:
4025 ads_msgfree(ads, res);
4027 return name;
4030 /********************************************************************
4031 ********************************************************************/
4033 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4034 LDAPMessage *msg, size_t *num_values)
4036 const char *field = "msDS-AdditionalDnsHostName";
4037 struct berval **values = NULL;
4038 char **ret = NULL;
4039 size_t i, converted_size;
4042 * Windows DC implicitly adds a short name for each FQDN added to
4043 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4044 * suffix "\0$" which we should ignore (see bug #14406).
4047 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4048 if (values == NULL) {
4049 return NULL;
4052 *num_values = ldap_count_values_len(values);
4054 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4055 if (ret == NULL) {
4056 ldap_value_free_len(values);
4057 return NULL;
4060 for (i = 0; i < *num_values; i++) {
4061 ret[i] = NULL;
4062 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4063 values[i]->bv_val,
4064 strnlen(values[i]->bv_val,
4065 values[i]->bv_len),
4066 &ret[i], &converted_size)) {
4067 ldap_value_free_len(values);
4068 return NULL;
4071 ret[i] = NULL;
4073 ldap_value_free_len(values);
4074 return ret;
4077 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4078 ADS_STRUCT *ads,
4079 const char *machine_name,
4080 char ***hostnames_array,
4081 size_t *num_hostnames)
4083 ADS_STATUS status;
4084 LDAPMessage *res = NULL;
4085 int count;
4087 status = ads_find_machine_acct(ads,
4088 &res,
4089 machine_name);
4090 if (!ADS_ERR_OK(status)) {
4091 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4092 machine_name));
4093 return status;
4096 count = ads_count_replies(ads, res);
4097 if (count != 1) {
4098 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4099 goto done;
4102 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4103 if (*hostnames_array == NULL) {
4104 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4105 machine_name));
4106 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4107 goto done;
4110 done:
4111 ads_msgfree(ads, res);
4113 return status;
4116 /********************************************************************
4117 ********************************************************************/
4119 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4121 LDAPMessage *res = NULL;
4122 ADS_STATUS status;
4123 int count = 0;
4124 char *name = NULL;
4126 status = ads_find_machine_acct(ads, &res, machine_name);
4127 if (!ADS_ERR_OK(status)) {
4128 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4129 lp_netbios_name()));
4130 goto out;
4133 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4134 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4135 goto out;
4138 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4139 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4142 out:
4143 ads_msgfree(ads, res);
4145 return name;
4148 /********************************************************************
4149 ********************************************************************/
4151 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4153 LDAPMessage *res = NULL;
4154 ADS_STATUS status;
4155 int count = 0;
4156 char *name = NULL;
4157 bool ok = false;
4159 status = ads_find_machine_acct(ads, &res, machine_name);
4160 if (!ADS_ERR_OK(status)) {
4161 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4162 lp_netbios_name()));
4163 goto out;
4166 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4167 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4168 goto out;
4171 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4172 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4175 out:
4176 ads_msgfree(ads, res);
4177 if (name != NULL) {
4178 ok = (strlen(name) > 0);
4180 TALLOC_FREE(name);
4181 return ok;
4184 #if 0
4186 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4189 * Join a machine to a realm
4190 * Creates the machine account and sets the machine password
4191 * @param ads connection to ads server
4192 * @param machine name of host to add
4193 * @param org_unit Organizational unit to place machine in
4194 * @return status of join
4196 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4197 uint32_t account_type, const char *org_unit)
4199 ADS_STATUS status;
4200 LDAPMessage *res = NULL;
4201 char *machine;
4203 /* machine name must be lowercase */
4204 machine = SMB_STRDUP(machine_name);
4205 strlower_m(machine);
4208 status = ads_find_machine_acct(ads, (void **)&res, machine);
4209 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4210 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4211 status = ads_leave_realm(ads, machine);
4212 if (!ADS_ERR_OK(status)) {
4213 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4214 machine, ads->config.realm));
4215 return status;
4219 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4220 if (!ADS_ERR_OK(status)) {
4221 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4222 SAFE_FREE(machine);
4223 return status;
4226 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4227 if (!ADS_ERR_OK(status)) {
4228 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4229 SAFE_FREE(machine);
4230 return status;
4233 SAFE_FREE(machine);
4234 ads_msgfree(ads, res);
4236 return status;
4238 #endif
4241 * Delete a machine from the realm
4242 * @param ads connection to ads server
4243 * @param hostname Machine to remove
4244 * @return status of delete
4246 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4248 ADS_STATUS status;
4249 void *msg;
4250 LDAPMessage *res;
4251 char *hostnameDN, *host;
4252 int rc;
4253 LDAPControl ldap_control;
4254 LDAPControl * pldap_control[2] = {NULL, NULL};
4256 pldap_control[0] = &ldap_control;
4257 memset(&ldap_control, 0, sizeof(LDAPControl));
4258 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4260 /* hostname must be lowercase */
4261 host = SMB_STRDUP(hostname);
4262 if (!strlower_m(host)) {
4263 SAFE_FREE(host);
4264 return ADS_ERROR_SYSTEM(EINVAL);
4267 status = ads_find_machine_acct(ads, &res, host);
4268 if (!ADS_ERR_OK(status)) {
4269 DEBUG(0, ("Host account for %s does not exist.\n", host));
4270 SAFE_FREE(host);
4271 return status;
4274 msg = ads_first_entry(ads, res);
4275 if (!msg) {
4276 SAFE_FREE(host);
4277 return ADS_ERROR_SYSTEM(ENOENT);
4280 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4281 if (hostnameDN == NULL) {
4282 SAFE_FREE(host);
4283 return ADS_ERROR_SYSTEM(ENOENT);
4286 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4287 if (rc) {
4288 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4289 }else {
4290 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4293 if (rc != LDAP_SUCCESS) {
4294 const char *attrs[] = { "cn", NULL };
4295 LDAPMessage *msg_sub;
4297 /* we only search with scope ONE, we do not expect any further
4298 * objects to be created deeper */
4300 status = ads_do_search_retry(ads, hostnameDN,
4301 LDAP_SCOPE_ONELEVEL,
4302 "(objectclass=*)", attrs, &res);
4304 if (!ADS_ERR_OK(status)) {
4305 SAFE_FREE(host);
4306 TALLOC_FREE(hostnameDN);
4307 return status;
4310 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4311 msg_sub = ads_next_entry(ads, msg_sub)) {
4313 char *dn = NULL;
4315 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4316 SAFE_FREE(host);
4317 TALLOC_FREE(hostnameDN);
4318 return ADS_ERROR(LDAP_NO_MEMORY);
4321 status = ads_del_dn(ads, dn);
4322 if (!ADS_ERR_OK(status)) {
4323 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4324 SAFE_FREE(host);
4325 TALLOC_FREE(dn);
4326 TALLOC_FREE(hostnameDN);
4327 return status;
4330 TALLOC_FREE(dn);
4333 /* there should be no subordinate objects anymore */
4334 status = ads_do_search_retry(ads, hostnameDN,
4335 LDAP_SCOPE_ONELEVEL,
4336 "(objectclass=*)", attrs, &res);
4338 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4339 SAFE_FREE(host);
4340 TALLOC_FREE(hostnameDN);
4341 return status;
4344 /* delete hostnameDN now */
4345 status = ads_del_dn(ads, hostnameDN);
4346 if (!ADS_ERR_OK(status)) {
4347 SAFE_FREE(host);
4348 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4349 TALLOC_FREE(hostnameDN);
4350 return status;
4354 TALLOC_FREE(hostnameDN);
4356 status = ads_find_machine_acct(ads, &res, host);
4357 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4358 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4359 DEBUG(3, ("Failed to remove host account.\n"));
4360 SAFE_FREE(host);
4361 return status;
4364 SAFE_FREE(host);
4365 return ADS_SUCCESS;
4369 * pull all token-sids from an LDAP dn
4370 * @param ads connection to ads server
4371 * @param mem_ctx TALLOC_CTX for allocating sid array
4372 * @param dn of LDAP object
4373 * @param user_sid pointer to struct dom_sid (objectSid)
4374 * @param primary_group_sid pointer to struct dom_sid (self composed)
4375 * @param sids pointer to sid array to allocate
4376 * @param num_sids counter of SIDs pulled
4377 * @return status of token query
4379 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4380 TALLOC_CTX *mem_ctx,
4381 const char *dn,
4382 struct dom_sid *user_sid,
4383 struct dom_sid *primary_group_sid,
4384 struct dom_sid **sids,
4385 size_t *num_sids)
4387 ADS_STATUS status;
4388 LDAPMessage *res = NULL;
4389 int count = 0;
4390 size_t tmp_num_sids;
4391 struct dom_sid *tmp_sids;
4392 struct dom_sid tmp_user_sid;
4393 struct dom_sid tmp_primary_group_sid;
4394 uint32_t pgid;
4395 const char *attrs[] = {
4396 "objectSid",
4397 "tokenGroups",
4398 "primaryGroupID",
4399 NULL
4402 status = ads_search_retry_dn(ads, &res, dn, attrs);
4403 if (!ADS_ERR_OK(status)) {
4404 return status;
4407 count = ads_count_replies(ads, res);
4408 if (count != 1) {
4409 ads_msgfree(ads, res);
4410 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4413 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4414 ads_msgfree(ads, res);
4415 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4418 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4419 ads_msgfree(ads, res);
4420 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4424 /* hack to compose the primary group sid without knowing the
4425 * domsid */
4427 struct dom_sid domsid;
4429 sid_copy(&domsid, &tmp_user_sid);
4431 if (!sid_split_rid(&domsid, NULL)) {
4432 ads_msgfree(ads, res);
4433 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4436 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4437 ads_msgfree(ads, res);
4438 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4442 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4444 if (tmp_num_sids == 0 || !tmp_sids) {
4445 ads_msgfree(ads, res);
4446 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4449 if (num_sids) {
4450 *num_sids = tmp_num_sids;
4453 if (sids) {
4454 *sids = tmp_sids;
4457 if (user_sid) {
4458 *user_sid = tmp_user_sid;
4461 if (primary_group_sid) {
4462 *primary_group_sid = tmp_primary_group_sid;
4465 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4467 ads_msgfree(ads, res);
4468 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4472 * Find a sAMAccoutName in LDAP
4473 * @param ads connection to ads server
4474 * @param mem_ctx TALLOC_CTX for allocating sid array
4475 * @param samaccountname to search
4476 * @param uac_ret uint32_t pointer userAccountControl attribute value
4477 * @param dn_ret pointer to dn
4478 * @return status of token query
4480 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4481 TALLOC_CTX *mem_ctx,
4482 const char *samaccountname,
4483 uint32_t *uac_ret,
4484 const char **dn_ret)
4486 ADS_STATUS status;
4487 const char *attrs[] = { "userAccountControl", NULL };
4488 const char *filter;
4489 LDAPMessage *res = NULL;
4490 char *dn = NULL;
4491 uint32_t uac = 0;
4493 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4494 samaccountname);
4495 if (filter == NULL) {
4496 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4497 goto out;
4500 status = ads_do_search_all(ads, ads->config.bind_path,
4501 LDAP_SCOPE_SUBTREE,
4502 filter, attrs, &res);
4504 if (!ADS_ERR_OK(status)) {
4505 goto out;
4508 if (ads_count_replies(ads, res) != 1) {
4509 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4510 goto out;
4513 dn = ads_get_dn(ads, talloc_tos(), res);
4514 if (dn == NULL) {
4515 status = ADS_ERROR(LDAP_NO_MEMORY);
4516 goto out;
4519 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4520 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4521 goto out;
4524 if (uac_ret) {
4525 *uac_ret = uac;
4528 if (dn_ret) {
4529 *dn_ret = talloc_strdup(mem_ctx, dn);
4530 if (!*dn_ret) {
4531 status = ADS_ERROR(LDAP_NO_MEMORY);
4532 goto out;
4535 out:
4536 TALLOC_FREE(dn);
4537 ads_msgfree(ads, res);
4539 return status;
4543 * find our configuration path
4544 * @param ads connection to ads server
4545 * @param mem_ctx Pointer to talloc context
4546 * @param config_path Pointer to the config path
4547 * @return status of search
4549 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4550 TALLOC_CTX *mem_ctx,
4551 char **config_path)
4553 ADS_STATUS status;
4554 LDAPMessage *res = NULL;
4555 const char *config_context = NULL;
4556 const char *attrs[] = { "configurationNamingContext", NULL };
4558 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4559 "(objectclass=*)", attrs, &res);
4560 if (!ADS_ERR_OK(status)) {
4561 return status;
4564 config_context = ads_pull_string(ads, mem_ctx, res,
4565 "configurationNamingContext");
4566 ads_msgfree(ads, res);
4567 if (!config_context) {
4568 return ADS_ERROR(LDAP_NO_MEMORY);
4571 if (config_path) {
4572 *config_path = talloc_strdup(mem_ctx, config_context);
4573 if (!*config_path) {
4574 return ADS_ERROR(LDAP_NO_MEMORY);
4578 return ADS_ERROR(LDAP_SUCCESS);
4582 * find the displayName of an extended right
4583 * @param ads connection to ads server
4584 * @param config_path The config path
4585 * @param mem_ctx Pointer to talloc context
4586 * @param GUID struct of the rightsGUID
4587 * @return status of search
4589 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4590 const char *config_path,
4591 TALLOC_CTX *mem_ctx,
4592 const struct GUID *rights_guid)
4594 ADS_STATUS rc;
4595 LDAPMessage *res = NULL;
4596 char *expr = NULL;
4597 const char *attrs[] = { "displayName", NULL };
4598 const char *result = NULL;
4599 const char *path;
4601 if (!ads || !mem_ctx || !rights_guid) {
4602 goto done;
4605 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4606 GUID_string(mem_ctx, rights_guid));
4607 if (!expr) {
4608 goto done;
4611 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4612 if (!path) {
4613 goto done;
4616 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4617 expr, attrs, &res);
4618 if (!ADS_ERR_OK(rc)) {
4619 goto done;
4622 if (ads_count_replies(ads, res) != 1) {
4623 goto done;
4626 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4628 done:
4629 ads_msgfree(ads, res);
4630 return result;
4634 * verify or build and verify an account ou
4635 * @param mem_ctx Pointer to talloc context
4636 * @param ads connection to ads server
4637 * @param account_ou
4638 * @return status of search
4641 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4642 ADS_STRUCT *ads,
4643 const char **account_ou)
4645 char **exploded_dn;
4646 const char *name;
4647 char *ou_string;
4649 if (account_ou == NULL) {
4650 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4653 if (*account_ou != NULL) {
4654 exploded_dn = ldap_explode_dn(*account_ou, 0);
4655 if (exploded_dn) {
4656 ldap_value_free(exploded_dn);
4657 return ADS_SUCCESS;
4661 ou_string = ads_ou_string(ads, *account_ou);
4662 if (!ou_string) {
4663 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4666 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4667 ads->config.bind_path);
4668 SAFE_FREE(ou_string);
4670 if (!name) {
4671 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4674 exploded_dn = ldap_explode_dn(name, 0);
4675 if (!exploded_dn) {
4676 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4678 ldap_value_free(exploded_dn);
4680 *account_ou = name;
4681 return ADS_SUCCESS;
4684 #endif