s4:auth: Enforce machine authentication policy for NTLM authentication
[Samba.git] / source3 / libads / ldap.c
bloba01b1193013821a51579f3c3c43bb6a2d0f4b8a9
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 timout 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;
450 for (i = 0; i < count; i++) {
451 char server[INET6_ADDRSTRLEN];
452 int ret;
454 if (is_zero_addr(&sa_list[i].u.ss)) {
455 continue;
458 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
460 status = check_negative_conn_cache(domain, server);
461 if (!NT_STATUS_IS_OK(status)) {
462 continue;
465 ret = tsocket_address_inet_from_strings(ts_list, "ip",
466 server, LDAP_PORT,
467 &ts_list[num_requests]);
468 if (ret != 0) {
469 status = map_nt_error_from_unix(errno);
470 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
471 server, nt_errstr(status));
472 TALLOC_FREE(frame);
473 return status;
476 req_sa_list[num_requests] = &sa_list[i];
477 num_requests += 1;
480 if (num_requests == 0) {
481 status = NT_STATUS_NO_LOGON_SERVERS;
482 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
483 domain, num_requests, count, nt_errstr(status));
484 TALLOC_FREE(frame);
485 return status;
488 ts_list_const = (const struct tsocket_address * const *)ts_list;
490 status = cldap_multi_netlogon(frame,
491 ts_list_const, num_requests,
492 ads->server.realm, NULL,
493 nt_version,
494 1, endtime, &responses);
495 if (!NT_STATUS_IS_OK(status)) {
496 DBG_WARNING("cldap_multi_netlogon(realm=%s, num_requests=%zu) "
497 "for count[%zu] - %s\n",
498 ads->server.realm,
499 num_requests, count,
500 nt_errstr(status));
501 TALLOC_FREE(frame);
502 return NT_STATUS_NO_LOGON_SERVERS;
505 for (i = 0; i < num_requests; i++) {
506 struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply = NULL;
507 char server[INET6_ADDRSTRLEN];
509 if (responses[i] == NULL) {
510 continue;
513 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
515 if (responses[i]->ntver != NETLOGON_NT_VERSION_5EX) {
516 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
517 ads->server.realm,
518 responses[i]->ntver, server);
519 continue;
522 cldap_reply = &responses[i]->data.nt5_ex;
524 /* Returns ok only if it matches the correct server type */
525 ok = ads_fill_cldap_reply(ads,
526 false,
527 &req_sa_list[i]->u.ss,
528 cldap_reply);
529 if (ok) {
530 DBG_DEBUG("realm[%s]: selected %s => %s\n",
531 ads->server.realm,
532 server, cldap_reply->pdc_dns_name);
533 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
534 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
535 cldap_reply);
537 TALLOC_FREE(frame);
538 return NT_STATUS_OK;
541 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
542 ads->server.realm,
543 server, cldap_reply->pdc_dns_name);
544 if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
545 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
546 cldap_reply);
548 add_failed_connection_entry(domain, server,
549 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID);
550 retry = true;
553 if (retry) {
554 bool expired;
556 expired = timeval_expired(&endtime);
557 if (!expired) {
558 goto again;
562 /* keep track of failures as all were not suitable */
563 for (i = 0; i < num_requests; i++) {
564 char server[INET6_ADDRSTRLEN];
566 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
568 add_failed_connection_entry(domain, server,
569 NT_STATUS_UNSUCCESSFUL);
572 status = NT_STATUS_NO_LOGON_SERVERS;
573 DBG_WARNING("realm[%s] no valid response "
574 "num_requests[%zu] for count[%zu] - %s\n",
575 ads->server.realm,
576 num_requests, count, nt_errstr(status));
577 TALLOC_FREE(frame);
578 return NT_STATUS_NO_LOGON_SERVERS;
581 /***************************************************************************
582 resolve a name and perform an "ldap ping" using NetBIOS and related methods
583 ****************************************************************************/
585 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
586 const char *domain, const char *realm)
588 size_t i;
589 size_t count = 0;
590 struct samba_sockaddr *sa_list = NULL;
591 NTSTATUS status;
593 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
594 domain));
596 status = get_sorted_dc_list(talloc_tos(),
597 domain,
598 NULL,
599 &sa_list,
600 &count,
601 false);
602 if (!NT_STATUS_IS_OK(status)) {
603 return status;
606 /* remove servers which are known to be dead based on
607 the corresponding DNS method */
608 if (*realm) {
609 for (i = 0; i < count; ++i) {
610 char server[INET6_ADDRSTRLEN];
612 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
614 if(!NT_STATUS_IS_OK(
615 check_negative_conn_cache(realm, server))) {
616 /* Ensure we add the workgroup name for this
617 IP address as negative too. */
618 add_failed_connection_entry(
619 domain, server,
620 NT_STATUS_UNSUCCESSFUL);
625 status = cldap_ping_list(ads, domain, sa_list, count);
627 TALLOC_FREE(sa_list);
629 return status;
633 /**********************************************************************
634 resolve a name and perform an "ldap ping" using DNS
635 **********************************************************************/
637 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
638 const char *realm)
640 size_t count = 0;
641 struct samba_sockaddr *sa_list = NULL;
642 NTSTATUS status;
644 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
645 realm));
647 status = get_sorted_dc_list(talloc_tos(),
648 realm,
649 sitename,
650 &sa_list,
651 &count,
652 true);
653 if (!NT_STATUS_IS_OK(status)) {
654 TALLOC_FREE(sa_list);
655 return status;
658 status = cldap_ping_list(ads, realm, sa_list, count);
660 TALLOC_FREE(sa_list);
662 return status;
665 /**********************************************************************
666 Try to find an AD dc using our internal name resolution routines
667 Try the realm first and then then workgroup name if netbios is not
668 disabled
669 **********************************************************************/
671 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
673 const char *c_domain = "";
674 const char *c_realm;
675 bool use_own_domain = False;
676 char *sitename = NULL;
677 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
678 bool ok = false;
680 /* if the realm and workgroup are both empty, assume they are ours */
682 /* realm */
683 c_realm = ads->server.realm;
685 if (c_realm == NULL)
686 c_realm = "";
688 if (!*c_realm) {
689 /* special case where no realm and no workgroup means our own */
690 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
691 use_own_domain = True;
692 c_realm = lp_realm();
696 if (!lp_disable_netbios()) {
697 if (use_own_domain) {
698 c_domain = lp_workgroup();
699 } else {
700 c_domain = ads->server.workgroup;
701 if (!*c_realm && (!c_domain || !*c_domain)) {
702 c_domain = lp_workgroup();
706 if (!c_domain) {
707 c_domain = "";
711 if (!*c_realm && !*c_domain) {
712 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
713 "what to do\n"));
714 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
718 * In case of LDAP we use get_dc_name() as that
719 * creates the custom krb5.conf file
721 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
722 fstring srv_name;
723 struct sockaddr_storage ip_out;
725 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
726 " and falling back to domain '%s'\n",
727 c_realm, c_domain));
729 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
730 if (ok) {
731 if (is_zero_addr(&ip_out)) {
732 return NT_STATUS_NO_LOGON_SERVERS;
736 * we call ads_try_connect() to fill in the
737 * ads->config details
739 ok = ads_try_connect(ads, false, &ip_out);
740 if (ok) {
741 return NT_STATUS_OK;
745 return NT_STATUS_NO_LOGON_SERVERS;
748 if (*c_realm) {
749 sitename = sitename_fetch(talloc_tos(), c_realm);
750 status = resolve_and_ping_dns(ads, sitename, c_realm);
752 if (NT_STATUS_IS_OK(status)) {
753 TALLOC_FREE(sitename);
754 return status;
757 /* In case we failed to contact one of our closest DC on our
758 * site we
759 * need to try to find another DC, retry with a site-less SRV
760 * DNS query
761 * - Guenther */
763 if (sitename) {
764 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
765 "our site (%s), Trying to find another DC "
766 "for realm '%s' (domain '%s')\n",
767 sitename, c_realm, c_domain));
768 namecache_delete(c_realm, 0x1C);
769 status =
770 resolve_and_ping_dns(ads, NULL, c_realm);
772 if (NT_STATUS_IS_OK(status)) {
773 TALLOC_FREE(sitename);
774 return status;
778 TALLOC_FREE(sitename);
781 /* try netbios as fallback - if permitted,
782 or if configuration specifically requests it */
783 if (*c_domain) {
784 if (*c_realm) {
785 DEBUG(3, ("ads_find_dc: falling back to netbios "
786 "name resolution for domain '%s' (realm '%s')\n",
787 c_domain, c_realm));
790 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
791 if (NT_STATUS_IS_OK(status)) {
792 return status;
796 DEBUG(1, ("ads_find_dc: "
797 "name resolution for realm '%s' (domain '%s') failed: %s\n",
798 c_realm, c_domain, nt_errstr(status)));
799 return status;
802 * Connect to the LDAP server
803 * @param ads Pointer to an existing ADS_STRUCT
804 * @return status of connection
806 ADS_STATUS ads_connect(ADS_STRUCT *ads)
808 int version = LDAP_VERSION3;
809 ADS_STATUS status;
810 NTSTATUS ntstatus;
811 char addr[INET6_ADDRSTRLEN];
812 struct sockaddr_storage existing_ss;
814 zero_sockaddr(&existing_ss);
817 * ads_connect can be passed in a reused ADS_STRUCT
818 * with an existing non-zero ads->ldap.ss IP address
819 * that was stored by going through ads_find_dc()
820 * if ads->server.ldap_server was NULL.
822 * If ads->server.ldap_server is still NULL but
823 * the target address isn't the zero address, then
824 * store that address off off before zeroing out
825 * ads->ldap so we don't keep doing multiple calls
826 * to ads_find_dc() in the reuse case.
828 * If a caller wants a clean ADS_STRUCT they
829 * will TALLOC_FREE it and allocate a new one
830 * by calling ads_init(), which ensures
831 * ads->ldap.ss is a properly zero'ed out valid IP
832 * address.
834 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
835 /* Save off the address we previously found by ads_find_dc(). */
836 existing_ss = ads->ldap.ss;
839 ads_zero_ldap(ads);
840 ZERO_STRUCT(ads->ldap_wrap_data);
841 ads->ldap.last_attempt = time_mono(NULL);
842 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
844 /* try with a user specified server */
846 if (DEBUGLEVEL >= 11) {
847 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
848 DEBUG(11,("ads_connect: entering\n"));
849 DEBUGADD(11,("%s\n", s));
850 TALLOC_FREE(s);
853 if (ads->server.ldap_server) {
854 bool ok = false;
855 struct sockaddr_storage ss;
857 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
858 if (!ok) {
859 DEBUG(5,("ads_connect: unable to resolve name %s\n",
860 ads->server.ldap_server));
861 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
862 goto out;
865 if (is_zero_addr(&ss)) {
866 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
867 goto out;
870 ok = ads_try_connect(ads, ads->server.gc, &ss);
871 if (ok) {
872 goto got_connection;
875 /* The choice of which GC use is handled one level up in
876 ads_connect_gc(). If we continue on from here with
877 ads_find_dc() we will get GC searches on port 389 which
878 doesn't work. --jerry */
880 if (ads->server.gc == true) {
881 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
884 if (ads->server.no_fallback) {
885 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
886 goto out;
890 if (!is_zero_addr(&existing_ss)) {
891 /* We saved off who we should talk to. */
892 bool ok = ads_try_connect(ads,
893 ads->server.gc,
894 &existing_ss);
895 if (ok) {
896 goto got_connection;
899 * Keep trying to find a server and fall through
900 * into ads_find_dc() again.
904 ntstatus = ads_find_dc(ads);
905 if (NT_STATUS_IS_OK(ntstatus)) {
906 goto got_connection;
909 status = ADS_ERROR_NT(ntstatus);
910 goto out;
912 got_connection:
914 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
915 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
917 if (!ads->auth.user_name) {
918 /* Must use the userPrincipalName value here or sAMAccountName
919 and not servicePrincipalName; found by Guenther Deschner */
920 ads->auth.user_name = talloc_asprintf(ads,
921 "%s$",
922 lp_netbios_name());
923 if (ads->auth.user_name == NULL) {
924 DBG_ERR("talloc_asprintf failed\n");
925 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
926 goto out;
930 if (ads->auth.realm == NULL) {
931 ads->auth.realm = talloc_strdup(ads, ads->config.realm);
932 if (ads->auth.realm == NULL) {
933 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
934 goto out;
938 if (!ads->auth.kdc_server) {
939 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
940 ads->auth.kdc_server = talloc_strdup(ads, addr);
941 if (ads->auth.kdc_server == NULL) {
942 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
943 goto out;
947 /* If the caller() requested no LDAP bind, then we are done */
949 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
950 status = ADS_SUCCESS;
951 goto out;
954 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
955 if (!ads->ldap_wrap_data.mem_ctx) {
956 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
957 goto out;
960 /* Otherwise setup the TCP LDAP session */
962 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
963 &ads->ldap.ss,
964 ads->ldap.port, lp_ldap_timeout());
965 if (ads->ldap.ld == NULL) {
966 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
967 goto out;
969 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
971 /* cache the successful connection for workgroup and realm */
972 if (ads_closest_dc(ads)) {
973 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
974 saf_store( ads->server.realm, ads->config.ldap_server_name);
977 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
979 /* fill in the current time and offsets */
981 status = ads_current_time( ads );
982 if ( !ADS_ERR_OK(status) ) {
983 goto out;
986 /* Now do the bind */
988 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
989 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
990 goto out;
993 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
994 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
995 goto out;
998 status = ads_sasl_bind(ads);
1000 out:
1001 if (DEBUGLEVEL >= 11) {
1002 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1003 DEBUG(11,("ads_connect: leaving with: %s\n",
1004 ads_errstr(status)));
1005 DEBUGADD(11,("%s\n", s));
1006 TALLOC_FREE(s);
1009 return status;
1013 * Connect to the LDAP server using given credentials
1014 * @param ads Pointer to an existing ADS_STRUCT
1015 * @return status of connection
1017 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
1019 ads->auth.flags |= ADS_AUTH_USER_CREDS;
1021 return ads_connect(ads);
1025 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1026 * @param ads Pointer to an existing ADS_STRUCT
1028 * Sets the ads->ldap.ss to a valid
1029 * zero ip address that can be detected by
1030 * our is_zero_addr() function. Otherwise
1031 * it is left as AF_UNSPEC (0).
1033 void ads_zero_ldap(ADS_STRUCT *ads)
1035 ZERO_STRUCT(ads->ldap);
1037 * Initialize the sockaddr_storage so we can use
1038 * sockaddr test functions against it.
1040 zero_sockaddr(&ads->ldap.ss);
1044 * Disconnect the LDAP server
1045 * @param ads Pointer to an existing ADS_STRUCT
1047 void ads_disconnect(ADS_STRUCT *ads)
1049 if (ads->ldap.ld) {
1050 ldap_unbind(ads->ldap.ld);
1051 ads->ldap.ld = NULL;
1053 if (ads->ldap_wrap_data.wrap_ops &&
1054 ads->ldap_wrap_data.wrap_ops->disconnect) {
1055 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1057 if (ads->ldap_wrap_data.mem_ctx) {
1058 talloc_free(ads->ldap_wrap_data.mem_ctx);
1060 ads_zero_ldap(ads);
1061 ZERO_STRUCT(ads->ldap_wrap_data);
1065 Duplicate a struct berval into talloc'ed memory
1067 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1069 struct berval *value;
1071 if (!in_val) return NULL;
1073 value = talloc_zero(ctx, struct berval);
1074 if (value == NULL)
1075 return NULL;
1076 if (in_val->bv_len == 0) return value;
1078 value->bv_len = in_val->bv_len;
1079 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1080 in_val->bv_len);
1081 return value;
1085 Make a values list out of an array of (struct berval *)
1087 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1088 const struct berval **in_vals)
1090 struct berval **values;
1091 int i;
1093 if (!in_vals) return NULL;
1094 for (i=0; in_vals[i]; i++)
1095 ; /* count values */
1096 values = talloc_zero_array(ctx, struct berval *, i+1);
1097 if (!values) return NULL;
1099 for (i=0; in_vals[i]; i++) {
1100 values[i] = dup_berval(ctx, in_vals[i]);
1102 return values;
1106 UTF8-encode a values list out of an array of (char *)
1108 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1110 char **values;
1111 int i;
1112 size_t size;
1114 if (!in_vals) return NULL;
1115 for (i=0; in_vals[i]; i++)
1116 ; /* count values */
1117 values = talloc_zero_array(ctx, char *, i+1);
1118 if (!values) return NULL;
1120 for (i=0; in_vals[i]; i++) {
1121 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1122 TALLOC_FREE(values);
1123 return NULL;
1126 return values;
1130 Pull a (char *) array out of a UTF8-encoded values list
1132 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1134 char **values;
1135 int i;
1136 size_t converted_size;
1138 if (!in_vals) return NULL;
1139 for (i=0; in_vals[i]; i++)
1140 ; /* count values */
1141 values = talloc_zero_array(ctx, char *, i+1);
1142 if (!values) return NULL;
1144 for (i=0; in_vals[i]; i++) {
1145 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1146 &converted_size)) {
1147 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1148 "%s", strerror(errno)));
1151 return values;
1155 * Do a search with paged results. cookie must be null on the first
1156 * call, and then returned on each subsequent call. It will be null
1157 * again when the entire search is complete
1158 * @param ads connection to ads server
1159 * @param bind_path Base dn for the search
1160 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1161 * @param expr Search expression - specified in local charset
1162 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1163 * @param res ** which will contain results - free res* with ads_msgfree()
1164 * @param count Number of entries retrieved on this page
1165 * @param cookie The paged results cookie to be returned on subsequent calls
1166 * @return status of search
1168 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1169 const char *bind_path,
1170 int scope, const char *expr,
1171 const char **attrs, void *args,
1172 LDAPMessage **res,
1173 int *count, struct berval **cookie)
1175 int rc, i, version;
1176 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1177 size_t converted_size;
1178 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1179 BerElement *cookie_be = NULL;
1180 struct berval *cookie_bv= NULL;
1181 BerElement *ext_be = NULL;
1182 struct berval *ext_bv= NULL;
1184 TALLOC_CTX *ctx;
1185 ads_control *external_control = (ads_control *) args;
1187 *res = NULL;
1189 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1190 return ADS_ERROR(LDAP_NO_MEMORY);
1192 /* 0 means the conversion worked but the result was empty
1193 so we only fail if it's -1. In any case, it always
1194 at least nulls out the dest */
1195 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1196 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1198 rc = LDAP_NO_MEMORY;
1199 goto done;
1202 if (!attrs || !(*attrs))
1203 search_attrs = NULL;
1204 else {
1205 /* This would be the utf8-encoded version...*/
1206 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1207 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1208 rc = LDAP_NO_MEMORY;
1209 goto done;
1213 /* Paged results only available on ldap v3 or later */
1214 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1215 if (version < LDAP_VERSION3) {
1216 rc = LDAP_NOT_SUPPORTED;
1217 goto done;
1220 cookie_be = ber_alloc_t(LBER_USE_DER);
1221 if (*cookie) {
1222 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1223 ber_bvfree(*cookie); /* don't need it from last time */
1224 *cookie = NULL;
1225 } else {
1226 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1228 ber_flatten(cookie_be, &cookie_bv);
1229 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1230 PagedResults.ldctl_iscritical = (char) 1;
1231 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1232 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1234 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1235 NoReferrals.ldctl_iscritical = (char) 0;
1236 NoReferrals.ldctl_value.bv_len = 0;
1237 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1239 if (external_control &&
1240 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1241 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1243 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1244 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1246 /* win2k does not accept a ldctl_value beeing passed in */
1248 if (external_control->val != 0) {
1250 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1251 rc = LDAP_NO_MEMORY;
1252 goto done;
1255 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1256 rc = LDAP_NO_MEMORY;
1257 goto done;
1259 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1260 rc = LDAP_NO_MEMORY;
1261 goto done;
1264 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1265 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1267 } else {
1268 ExternalCtrl.ldctl_value.bv_len = 0;
1269 ExternalCtrl.ldctl_value.bv_val = NULL;
1272 controls[0] = &NoReferrals;
1273 controls[1] = &PagedResults;
1274 controls[2] = &ExternalCtrl;
1275 controls[3] = NULL;
1277 } else {
1278 controls[0] = &NoReferrals;
1279 controls[1] = &PagedResults;
1280 controls[2] = NULL;
1283 /* we need to disable referrals as the openldap libs don't
1284 handle them and paged results at the same time. Using them
1285 together results in the result record containing the server
1286 page control being removed from the result list (tridge/jmcd)
1288 leaving this in despite the control that says don't generate
1289 referrals, in case the server doesn't support it (jmcd)
1291 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1293 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1294 search_attrs, 0, controls,
1295 NULL, LDAP_NO_LIMIT,
1296 (LDAPMessage **)res);
1298 ber_free(cookie_be, 1);
1299 ber_bvfree(cookie_bv);
1301 if (rc) {
1302 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1303 ldap_err2string(rc)));
1304 if (rc == LDAP_OTHER) {
1305 char *ldap_errmsg;
1306 int ret;
1308 ret = ldap_parse_result(ads->ldap.ld,
1309 *res,
1310 NULL,
1311 NULL,
1312 &ldap_errmsg,
1313 NULL,
1314 NULL,
1316 if (ret == LDAP_SUCCESS) {
1317 DEBUG(3, ("ldap_search_with_timeout(%s) "
1318 "error: %s\n", expr, ldap_errmsg));
1319 ldap_memfree(ldap_errmsg);
1322 goto done;
1325 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1326 NULL, &rcontrols, 0);
1328 if (!rcontrols) {
1329 goto done;
1332 for (i=0; rcontrols[i]; i++) {
1333 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1334 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1335 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1336 &cookie_bv);
1337 /* the berval is the cookie, but must be freed when
1338 it is all done */
1339 if (cookie_bv->bv_len) /* still more to do */
1340 *cookie=ber_bvdup(cookie_bv);
1341 else
1342 *cookie=NULL;
1343 ber_bvfree(cookie_bv);
1344 ber_free(cookie_be, 1);
1345 break;
1348 ldap_controls_free(rcontrols);
1350 done:
1351 talloc_destroy(ctx);
1353 if (ext_be) {
1354 ber_free(ext_be, 1);
1357 if (ext_bv) {
1358 ber_bvfree(ext_bv);
1361 if (rc != LDAP_SUCCESS && *res != NULL) {
1362 ads_msgfree(ads, *res);
1363 *res = NULL;
1366 /* if/when we decide to utf8-encode attrs, take out this next line */
1367 TALLOC_FREE(search_attrs);
1369 return ADS_ERROR(rc);
1372 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1373 int scope, const char *expr,
1374 const char **attrs, LDAPMessage **res,
1375 int *count, struct berval **cookie)
1377 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1382 * Get all results for a search. This uses ads_do_paged_search() to return
1383 * all entries in a large search.
1384 * @param ads connection to ads server
1385 * @param bind_path Base dn for the search
1386 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1387 * @param expr Search expression
1388 * @param attrs Attributes to retrieve
1389 * @param res ** which will contain results - free res* with ads_msgfree()
1390 * @return status of search
1392 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1393 int scope, const char *expr,
1394 const char **attrs, void *args,
1395 LDAPMessage **res)
1397 struct berval *cookie = NULL;
1398 int count = 0;
1399 ADS_STATUS status;
1401 *res = NULL;
1402 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1403 &count, &cookie);
1405 if (!ADS_ERR_OK(status))
1406 return status;
1408 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1409 while (cookie) {
1410 LDAPMessage *res2 = NULL;
1411 LDAPMessage *msg, *next;
1413 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1414 attrs, args, &res2, &count, &cookie);
1415 if (!ADS_ERR_OK(status)) {
1416 break;
1419 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1420 that this works on all ldap libs, but I have only tested with openldap */
1421 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1422 next = ads_next_message(ads, msg);
1423 ldap_add_result_entry((LDAPMessage **)res, msg);
1425 /* note that we do not free res2, as the memory is now
1426 part of the main returned list */
1428 #else
1429 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1430 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1431 #endif
1433 return status;
1436 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1437 int scope, const char *expr,
1438 const char **attrs, LDAPMessage **res)
1440 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1443 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1444 int scope, const char *expr,
1445 const char **attrs, uint32_t sd_flags,
1446 LDAPMessage **res)
1448 ads_control args;
1450 args.control = ADS_SD_FLAGS_OID;
1451 args.val = sd_flags;
1452 args.critical = True;
1454 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1459 * Run a function on all results for a search. Uses ads_do_paged_search() and
1460 * runs the function as each page is returned, using ads_process_results()
1461 * @param ads connection to ads server
1462 * @param bind_path Base dn for the search
1463 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1464 * @param expr Search expression - specified in local charset
1465 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1466 * @param fn Function which takes attr name, values list, and data_area
1467 * @param data_area Pointer which is passed to function on each call
1468 * @return status of search
1470 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1471 int scope, const char *expr, const char **attrs,
1472 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1473 void *data_area)
1475 struct berval *cookie = NULL;
1476 int count = 0;
1477 ADS_STATUS status;
1478 LDAPMessage *res;
1480 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1481 &count, &cookie);
1483 if (!ADS_ERR_OK(status)) return status;
1485 ads_process_results(ads, res, fn, data_area);
1486 ads_msgfree(ads, res);
1488 while (cookie) {
1489 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1490 &res, &count, &cookie);
1492 if (!ADS_ERR_OK(status)) break;
1494 ads_process_results(ads, res, fn, data_area);
1495 ads_msgfree(ads, res);
1498 return status;
1502 * Do a search with a timeout.
1503 * @param ads connection to ads server
1504 * @param bind_path Base dn for the search
1505 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1506 * @param expr Search expression
1507 * @param attrs Attributes to retrieve
1508 * @param res ** which will contain results - free res* with ads_msgfree()
1509 * @return status of search
1511 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1512 const char *expr,
1513 const char **attrs, LDAPMessage **res)
1515 int rc;
1516 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1517 size_t converted_size;
1518 TALLOC_CTX *ctx;
1520 *res = NULL;
1521 if (!(ctx = talloc_init("ads_do_search"))) {
1522 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1523 return ADS_ERROR(LDAP_NO_MEMORY);
1526 /* 0 means the conversion worked but the result was empty
1527 so we only fail if it's negative. In any case, it always
1528 at least nulls out the dest */
1529 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1530 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1532 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1533 rc = LDAP_NO_MEMORY;
1534 goto done;
1537 if (!attrs || !(*attrs))
1538 search_attrs = NULL;
1539 else {
1540 /* This would be the utf8-encoded version...*/
1541 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1542 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1544 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1545 rc = LDAP_NO_MEMORY;
1546 goto done;
1550 /* see the note in ads_do_paged_search - we *must* disable referrals */
1551 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1553 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1554 search_attrs, 0, NULL, NULL,
1555 LDAP_NO_LIMIT,
1556 (LDAPMessage **)res);
1558 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1559 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1560 rc = 0;
1563 done:
1564 talloc_destroy(ctx);
1565 /* if/when we decide to utf8-encode attrs, take out this next line */
1566 TALLOC_FREE(search_attrs);
1567 return ADS_ERROR(rc);
1570 * Do a general ADS search
1571 * @param ads connection to ads server
1572 * @param res ** which will contain results - free res* with ads_msgfree()
1573 * @param expr Search expression
1574 * @param attrs Attributes to retrieve
1575 * @return status of search
1577 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1578 const char *expr, const char **attrs)
1580 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1581 expr, attrs, res);
1585 * Do a search on a specific DistinguishedName
1586 * @param ads connection to ads server
1587 * @param res ** which will contain results - free res* with ads_msgfree()
1588 * @param dn DistinguishName to search
1589 * @param attrs Attributes to retrieve
1590 * @return status of search
1592 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1593 const char *dn, const char **attrs)
1595 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1596 attrs, res);
1600 * Free up memory from a ads_search
1601 * @param ads connection to ads server
1602 * @param msg Search results to free
1604 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1606 if (!msg) return;
1607 ldap_msgfree(msg);
1611 * Get a dn from search results
1612 * @param ads connection to ads server
1613 * @param msg Search result
1614 * @return dn string
1616 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1618 char *utf8_dn, *unix_dn;
1619 size_t converted_size;
1621 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1623 if (!utf8_dn) {
1624 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1625 return NULL;
1628 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1629 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1630 utf8_dn ));
1631 return NULL;
1633 ldap_memfree(utf8_dn);
1634 return unix_dn;
1638 * Get the parent from a dn
1639 * @param dn the dn to return the parent from
1640 * @return parent dn string
1642 char *ads_parent_dn(const char *dn)
1644 char *p;
1646 if (dn == NULL) {
1647 return NULL;
1650 p = strchr(dn, ',');
1652 if (p == NULL) {
1653 return NULL;
1656 return p+1;
1660 * Find a machine account given a hostname
1661 * @param ads connection to ads server
1662 * @param res ** which will contain results - free res* with ads_msgfree()
1663 * @param host Hostname to search for
1664 * @return status of search
1666 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1667 const char *machine)
1669 ADS_STATUS status;
1670 char *expr;
1671 const char *attrs[] = {
1672 /* This is how Windows checks for machine accounts */
1673 "objectClass",
1674 "SamAccountName",
1675 "userAccountControl",
1676 "DnsHostName",
1677 "ServicePrincipalName",
1678 "userPrincipalName",
1679 "unicodePwd",
1681 /* Additional attributes Samba checks */
1682 "msDS-AdditionalDnsHostName",
1683 "msDS-SupportedEncryptionTypes",
1684 "nTSecurityDescriptor",
1685 "objectSid",
1687 NULL
1689 TALLOC_CTX *frame = talloc_stackframe();
1691 *res = NULL;
1693 /* the easiest way to find a machine account anywhere in the tree
1694 is to look for hostname$ */
1695 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1696 if (expr == NULL) {
1697 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1698 goto done;
1701 status = ads_search(ads, res, expr, attrs);
1702 if (ADS_ERR_OK(status)) {
1703 if (ads_count_replies(ads, *res) != 1) {
1704 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1708 done:
1709 TALLOC_FREE(frame);
1710 return status;
1714 * Initialize a list of mods to be used in a modify request
1715 * @param ctx An initialized TALLOC_CTX
1716 * @return allocated ADS_MODLIST
1718 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1720 #define ADS_MODLIST_ALLOC_SIZE 10
1721 LDAPMod **mods;
1723 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1724 /* -1 is safety to make sure we don't go over the end.
1725 need to reset it to NULL before doing ldap modify */
1726 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1728 return (ADS_MODLIST)mods;
1733 add an attribute to the list, with values list already constructed
1735 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1736 int mod_op, const char *name,
1737 const void *_invals)
1739 int curmod;
1740 LDAPMod **modlist = (LDAPMod **) *mods;
1741 struct berval **ber_values = NULL;
1742 char **char_values = NULL;
1744 if (!_invals) {
1745 mod_op = LDAP_MOD_DELETE;
1746 } else {
1747 if (mod_op & LDAP_MOD_BVALUES) {
1748 const struct berval **b;
1749 b = discard_const_p(const struct berval *, _invals);
1750 ber_values = ads_dup_values(ctx, b);
1751 } else {
1752 const char **c;
1753 c = discard_const_p(const char *, _invals);
1754 char_values = ads_push_strvals(ctx, c);
1758 /* find the first empty slot */
1759 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1760 curmod++);
1761 if (modlist[curmod] == (LDAPMod *) -1) {
1762 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1763 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1764 return ADS_ERROR(LDAP_NO_MEMORY);
1765 memset(&modlist[curmod], 0,
1766 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1767 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1768 *mods = (ADS_MODLIST)modlist;
1771 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1772 return ADS_ERROR(LDAP_NO_MEMORY);
1773 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1774 if (mod_op & LDAP_MOD_BVALUES) {
1775 modlist[curmod]->mod_bvalues = ber_values;
1776 } else if (mod_op & LDAP_MOD_DELETE) {
1777 modlist[curmod]->mod_values = NULL;
1778 } else {
1779 modlist[curmod]->mod_values = char_values;
1782 modlist[curmod]->mod_op = mod_op;
1783 return ADS_ERROR(LDAP_SUCCESS);
1787 * Add a single string value to a mod list
1788 * @param ctx An initialized TALLOC_CTX
1789 * @param mods An initialized ADS_MODLIST
1790 * @param name The attribute name to add
1791 * @param val The value to add - NULL means DELETE
1792 * @return ADS STATUS indicating success of add
1794 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1795 const char *name, const char *val)
1797 const char *values[2];
1799 values[0] = val;
1800 values[1] = NULL;
1802 if (!val)
1803 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1804 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1808 * Add an array of string values to a mod list
1809 * @param ctx An initialized TALLOC_CTX
1810 * @param mods An initialized ADS_MODLIST
1811 * @param name The attribute name to add
1812 * @param vals The array of string values to add - NULL means DELETE
1813 * @return ADS STATUS indicating success of add
1815 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1816 const char *name, const char **vals)
1818 if (!vals)
1819 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1820 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1821 name, (const void **) vals);
1825 * Add a single ber-encoded value to a mod list
1826 * @param ctx An initialized TALLOC_CTX
1827 * @param mods An initialized ADS_MODLIST
1828 * @param name The attribute name to add
1829 * @param val The value to add - NULL means DELETE
1830 * @return ADS STATUS indicating success of add
1832 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1833 const char *name, const struct berval *val)
1835 const struct berval *values[2];
1837 values[0] = val;
1838 values[1] = NULL;
1839 if (!val)
1840 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1841 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1842 name, (const void **) values);
1845 static void ads_print_error(int ret, LDAP *ld)
1847 if (ret != 0) {
1848 char *ld_error = NULL;
1849 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1850 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1851 ret,
1852 ldap_err2string(ret),
1853 ld_error);
1854 SAFE_FREE(ld_error);
1859 * Perform an ldap modify
1860 * @param ads connection to ads server
1861 * @param mod_dn DistinguishedName to modify
1862 * @param mods list of modifications to perform
1863 * @return status of modify
1865 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1867 int ret,i;
1868 char *utf8_dn = NULL;
1869 size_t converted_size;
1871 this control is needed to modify that contains a currently
1872 non-existent attribute (but allowable for the object) to run
1874 LDAPControl PermitModify = {
1875 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1876 {0, NULL},
1877 (char) 1};
1878 LDAPControl *controls[2];
1880 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1882 controls[0] = &PermitModify;
1883 controls[1] = NULL;
1885 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1886 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1889 /* find the end of the list, marked by NULL or -1 */
1890 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1891 /* make sure the end of the list is NULL */
1892 mods[i] = NULL;
1893 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1894 (LDAPMod **) mods, controls, NULL);
1895 ads_print_error(ret, ads->ldap.ld);
1896 TALLOC_FREE(utf8_dn);
1897 return ADS_ERROR(ret);
1901 * Perform an ldap add
1902 * @param ads connection to ads server
1903 * @param new_dn DistinguishedName to add
1904 * @param mods list of attributes and values for DN
1905 * @return status of add
1907 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1909 int ret, i;
1910 char *utf8_dn = NULL;
1911 size_t converted_size;
1913 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1915 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1916 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1917 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1920 /* find the end of the list, marked by NULL or -1 */
1921 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1922 /* make sure the end of the list is NULL */
1923 mods[i] = NULL;
1925 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1926 ads_print_error(ret, ads->ldap.ld);
1927 TALLOC_FREE(utf8_dn);
1928 return ADS_ERROR(ret);
1932 * Delete a DistinguishedName
1933 * @param ads connection to ads server
1934 * @param new_dn DistinguishedName to delete
1935 * @return status of delete
1937 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1939 int ret;
1940 char *utf8_dn = NULL;
1941 size_t converted_size;
1942 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1943 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1944 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1947 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1949 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1950 ads_print_error(ret, ads->ldap.ld);
1951 TALLOC_FREE(utf8_dn);
1952 return ADS_ERROR(ret);
1956 * Build an org unit string
1957 * if org unit is Computers or blank then assume a container, otherwise
1958 * assume a / separated list of organisational units.
1959 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1960 * @param ads connection to ads server
1961 * @param org_unit Organizational unit
1962 * @return org unit string - caller must free
1964 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1966 ADS_STATUS status;
1967 char *ret = NULL;
1968 char *dn = NULL;
1970 if (!org_unit || !*org_unit) {
1972 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1974 /* samba4 might not yet respond to a wellknownobject-query */
1975 return ret ? ret : SMB_STRDUP("cn=Computers");
1978 if (strequal(org_unit, "Computers")) {
1979 return SMB_STRDUP("cn=Computers");
1982 /* jmcd: removed "\\" from the separation chars, because it is
1983 needed as an escape for chars like '#' which are valid in an
1984 OU name */
1985 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
1986 if (!ADS_ERR_OK(status)) {
1987 return NULL;
1990 return dn;
1994 * Get a org unit string for a well-known GUID
1995 * @param ads connection to ads server
1996 * @param wknguid Well known GUID
1997 * @return org unit string - caller must free
1999 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2001 ADS_STATUS status;
2002 LDAPMessage *res = NULL;
2003 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2004 **bind_dn_exp = NULL;
2005 const char *attrs[] = {"distinguishedName", NULL};
2006 int new_ln, wkn_ln, bind_ln, i;
2008 if (wknguid == NULL) {
2009 return NULL;
2012 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2013 DEBUG(1, ("asprintf failed!\n"));
2014 return NULL;
2017 status = ads_search_dn(ads, &res, base, attrs);
2018 if (!ADS_ERR_OK(status)) {
2019 DEBUG(1,("Failed while searching for: %s\n", base));
2020 goto out;
2023 if (ads_count_replies(ads, res) != 1) {
2024 goto out;
2027 /* substitute the bind-path from the well-known-guid-search result */
2028 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2029 if (!wkn_dn) {
2030 goto out;
2033 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2034 if (!wkn_dn_exp) {
2035 goto out;
2038 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2039 if (!bind_dn_exp) {
2040 goto out;
2043 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2045 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2048 new_ln = wkn_ln - bind_ln;
2050 ret = SMB_STRDUP(wkn_dn_exp[0]);
2051 if (!ret) {
2052 goto out;
2055 for (i=1; i < new_ln; i++) {
2056 char *s = NULL;
2058 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2059 SAFE_FREE(ret);
2060 goto out;
2063 SAFE_FREE(ret);
2064 ret = SMB_STRDUP(s);
2065 free(s);
2066 if (!ret) {
2067 goto out;
2071 out:
2072 SAFE_FREE(base);
2073 ads_msgfree(ads, res);
2074 TALLOC_FREE(wkn_dn);
2075 if (wkn_dn_exp) {
2076 ldap_value_free(wkn_dn_exp);
2078 if (bind_dn_exp) {
2079 ldap_value_free(bind_dn_exp);
2082 return ret;
2086 * Adds (appends) an item to an attribute array, rather then
2087 * replacing the whole list
2088 * @param ctx An initialized TALLOC_CTX
2089 * @param mods An initialized ADS_MODLIST
2090 * @param name name of the ldap attribute to append to
2091 * @param vals an array of values to add
2092 * @return status of addition
2095 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2096 const char *name, const char **vals)
2098 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2099 (const void *) vals);
2103 * Determines the an account's current KVNO via an LDAP lookup
2104 * @param ads An initialized ADS_STRUCT
2105 * @param account_name the NT samaccountname.
2106 * @return the kvno for the account, or -1 in case of a failure.
2109 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
2111 LDAPMessage *res = NULL;
2112 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
2113 char *filter;
2114 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
2115 char *dn_string = NULL;
2116 ADS_STATUS ret;
2118 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
2119 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
2120 return kvno;
2122 ret = ads_search(ads, &res, filter, attrs);
2123 SAFE_FREE(filter);
2124 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
2125 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
2126 ads_msgfree(ads, res);
2127 return kvno;
2130 dn_string = ads_get_dn(ads, talloc_tos(), res);
2131 if (!dn_string) {
2132 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2133 ads_msgfree(ads, res);
2134 return kvno;
2136 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
2137 TALLOC_FREE(dn_string);
2139 /* ---------------------------------------------------------
2140 * 0 is returned as a default KVNO from this point on...
2141 * This is done because Windows 2000 does not support key
2142 * version numbers. Chances are that a failure in the next
2143 * step is simply due to Windows 2000 being used for a
2144 * domain controller. */
2145 kvno = 0;
2147 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2148 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2149 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2150 ads_msgfree(ads, res);
2151 return kvno;
2154 /* Success */
2155 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2156 ads_msgfree(ads, res);
2157 return kvno;
2161 * Determines the computer account's current KVNO via an LDAP lookup
2162 * @param ads An initialized ADS_STRUCT
2163 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2164 * @return the kvno for the computer account, or -1 in case of a failure.
2167 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2169 char *computer_account = NULL;
2170 uint32_t kvno = -1;
2172 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2173 return kvno;
2176 kvno = ads_get_kvno(ads, computer_account);
2177 free(computer_account);
2179 return kvno;
2183 * This clears out all registered spn's for a given hostname
2184 * @param ads An initilaized ADS_STRUCT
2185 * @param machine_name the NetBIOS name of the computer.
2186 * @return 0 upon success, non-zero otherwise.
2189 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2191 TALLOC_CTX *ctx;
2192 LDAPMessage *res = NULL;
2193 ADS_MODLIST mods;
2194 const char *servicePrincipalName[1] = {NULL};
2195 ADS_STATUS ret;
2196 char *dn_string = NULL;
2198 ret = ads_find_machine_acct(ads, &res, machine_name);
2199 if (!ADS_ERR_OK(ret)) {
2200 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2201 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2202 ads_msgfree(ads, res);
2203 return ret;
2206 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2207 ctx = talloc_init("ads_clear_service_principal_names");
2208 if (!ctx) {
2209 ads_msgfree(ads, res);
2210 return ADS_ERROR(LDAP_NO_MEMORY);
2213 if (!(mods = ads_init_mods(ctx))) {
2214 talloc_destroy(ctx);
2215 ads_msgfree(ads, res);
2216 return ADS_ERROR(LDAP_NO_MEMORY);
2218 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2219 if (!ADS_ERR_OK(ret)) {
2220 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2221 ads_msgfree(ads, res);
2222 talloc_destroy(ctx);
2223 return ret;
2225 dn_string = ads_get_dn(ads, talloc_tos(), res);
2226 if (!dn_string) {
2227 talloc_destroy(ctx);
2228 ads_msgfree(ads, res);
2229 return ADS_ERROR(LDAP_NO_MEMORY);
2231 ret = ads_gen_mod(ads, dn_string, mods);
2232 TALLOC_FREE(dn_string);
2233 if (!ADS_ERR_OK(ret)) {
2234 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2235 machine_name));
2236 ads_msgfree(ads, res);
2237 talloc_destroy(ctx);
2238 return ret;
2241 ads_msgfree(ads, res);
2242 talloc_destroy(ctx);
2243 return ret;
2247 * @brief Search for an element in a string array.
2249 * @param[in] el_array The string array to search.
2251 * @param[in] num_el The number of elements in the string array.
2253 * @param[in] el The string to search.
2255 * @return True if found, false if not.
2257 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2259 size_t i;
2261 if (el_array == NULL || num_el == 0 || el == NULL) {
2262 return false;
2265 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2266 int cmp;
2268 cmp = strcasecmp_m(el_array[i], el);
2269 if (cmp == 0) {
2270 return true;
2274 return false;
2278 * @brief This gets the service principal names of an existing computer account.
2280 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2282 * @param[in] ads The ADS context to use.
2284 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2285 * identify the computer account.
2287 * @param[in] spn_array A pointer to store the array for SPNs.
2289 * @param[in] num_spns The number of principals stored in the array.
2291 * @return 0 on success, or a ADS error if a failure occurred.
2293 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2294 ADS_STRUCT *ads,
2295 const char *machine_name,
2296 char ***spn_array,
2297 size_t *num_spns)
2299 ADS_STATUS status;
2300 LDAPMessage *res = NULL;
2301 int count;
2303 status = ads_find_machine_acct(ads,
2304 &res,
2305 machine_name);
2306 if (!ADS_ERR_OK(status)) {
2307 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2308 machine_name));
2309 return status;
2312 count = ads_count_replies(ads, res);
2313 if (count != 1) {
2314 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2315 goto done;
2318 *spn_array = ads_pull_strings(ads,
2319 mem_ctx,
2320 res,
2321 "servicePrincipalName",
2322 num_spns);
2323 if (*spn_array == NULL) {
2324 DEBUG(1, ("Host account for %s does not have service principal "
2325 "names.\n",
2326 machine_name));
2327 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2328 goto done;
2331 done:
2332 ads_msgfree(ads, res);
2334 return status;
2338 * This adds a service principal name to an existing computer account
2339 * (found by hostname) in AD.
2340 * @param ads An initialized ADS_STRUCT
2341 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2342 * @param spns An array or strings for the service principals to add,
2343 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2344 * @return 0 upon sucess, or non-zero if a failure occurs
2347 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2348 const char *machine_name,
2349 const char **spns)
2351 ADS_STATUS ret;
2352 TALLOC_CTX *ctx;
2353 LDAPMessage *res = NULL;
2354 ADS_MODLIST mods;
2355 char *dn_string = NULL;
2356 const char **servicePrincipalName = spns;
2358 ret = ads_find_machine_acct(ads, &res, machine_name);
2359 if (!ADS_ERR_OK(ret)) {
2360 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2361 machine_name));
2362 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2363 ads_msgfree(ads, res);
2364 return ret;
2367 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2368 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2369 ads_msgfree(ads, res);
2370 return ADS_ERROR(LDAP_NO_MEMORY);
2373 DEBUG(5,("ads_add_service_principal_name: INFO: "
2374 "Adding %s to host %s\n",
2375 spns[0] ? "N/A" : spns[0], machine_name));
2378 DEBUG(5,("ads_add_service_principal_name: INFO: "
2379 "Adding %s to host %s\n",
2380 spns[1] ? "N/A" : spns[1], machine_name));
2382 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2383 ret = ADS_ERROR(LDAP_NO_MEMORY);
2384 goto out;
2387 ret = ads_add_strlist(ctx,
2388 &mods,
2389 "servicePrincipalName",
2390 servicePrincipalName);
2391 if (!ADS_ERR_OK(ret)) {
2392 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2393 goto out;
2396 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2397 ret = ADS_ERROR(LDAP_NO_MEMORY);
2398 goto out;
2401 ret = ads_gen_mod(ads, dn_string, mods);
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 out:
2408 TALLOC_FREE( ctx );
2409 ads_msgfree(ads, res);
2410 return ret;
2413 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2414 LDAPMessage *msg)
2416 uint32_t acct_ctrl = 0;
2417 bool ok;
2419 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2420 if (!ok) {
2421 return 0;
2424 return acct_ctrl;
2427 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2428 LDAPMessage *msg,
2429 const struct berval *machine_pw_val)
2431 ADS_MODLIST mods;
2432 ADS_STATUS ret;
2433 TALLOC_CTX *frame = talloc_stackframe();
2434 uint32_t acct_control;
2435 char *control_str = NULL;
2436 const char *attrs[] = {
2437 "objectSid",
2438 NULL
2440 LDAPMessage *res = NULL;
2441 char *dn = NULL;
2443 dn = ads_get_dn(ads, frame, msg);
2444 if (dn == NULL) {
2445 ret = ADS_ERROR(LDAP_NO_MEMORY);
2446 goto done;
2449 acct_control = ads_get_acct_ctrl(ads, msg);
2450 if (acct_control == 0) {
2451 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2452 goto done;
2456 * Changing the password, disables the account. So we need to change the
2457 * userAccountControl flags to enable it again.
2459 mods = ads_init_mods(frame);
2460 if (mods == NULL) {
2461 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2462 goto done;
2465 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2467 ret = ads_gen_mod(ads, dn, mods);
2468 if (!ADS_ERR_OK(ret)) {
2469 goto done;
2471 TALLOC_FREE(mods);
2474 * To activate the account, we need to disable and enable it.
2476 acct_control |= UF_ACCOUNTDISABLE;
2478 control_str = talloc_asprintf(frame, "%u", acct_control);
2479 if (control_str == NULL) {
2480 ret = ADS_ERROR(LDAP_NO_MEMORY);
2481 goto done;
2484 mods = ads_init_mods(frame);
2485 if (mods == NULL) {
2486 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2487 goto done;
2490 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2492 ret = ads_gen_mod(ads, dn, mods);
2493 if (!ADS_ERR_OK(ret)) {
2494 goto done;
2496 TALLOC_FREE(mods);
2497 TALLOC_FREE(control_str);
2500 * Enable the account again.
2502 acct_control &= ~UF_ACCOUNTDISABLE;
2504 control_str = talloc_asprintf(frame, "%u", acct_control);
2505 if (control_str == NULL) {
2506 ret = ADS_ERROR(LDAP_NO_MEMORY);
2507 goto done;
2510 mods = ads_init_mods(frame);
2511 if (mods == NULL) {
2512 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2513 goto done;
2516 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2518 ret = ads_gen_mod(ads, dn, mods);
2519 if (!ADS_ERR_OK(ret)) {
2520 goto done;
2522 TALLOC_FREE(mods);
2523 TALLOC_FREE(control_str);
2525 ret = ads_search_dn(ads, &res, dn, attrs);
2526 ads_msgfree(ads, res);
2528 done:
2529 talloc_free(frame);
2531 return ret;
2535 * adds a machine account to the ADS server
2536 * @param ads An intialized ADS_STRUCT
2537 * @param machine_name - the NetBIOS machine name of this account.
2538 * @param account_type A number indicating the type of account to create
2539 * @param org_unit The LDAP path in which to place this account
2540 * @return 0 upon success, or non-zero otherwise
2543 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2544 const char *machine_name,
2545 const char *machine_password,
2546 const char *org_unit,
2547 uint32_t etype_list,
2548 const char *dns_domain_name)
2550 ADS_STATUS ret;
2551 char *samAccountName = NULL;
2552 char *controlstr = NULL;
2553 TALLOC_CTX *ctx = NULL;
2554 ADS_MODLIST mods;
2555 char *machine_escaped = NULL;
2556 char *dns_hostname = NULL;
2557 char *new_dn = NULL;
2558 char *utf8_pw = NULL;
2559 size_t utf8_pw_len = 0;
2560 char *utf16_pw = NULL;
2561 size_t utf16_pw_len = 0;
2562 struct berval machine_pw_val;
2563 bool ok;
2564 const char **spn_array = NULL;
2565 size_t num_spns = 0;
2566 const char *spn_prefix[] = {
2567 "HOST",
2568 "RestrictedKrbHost",
2570 size_t i;
2571 LDAPMessage *res = NULL;
2572 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2574 ctx = talloc_init("ads_add_machine_acct");
2575 if (ctx == NULL) {
2576 return ADS_ERROR(LDAP_NO_MEMORY);
2579 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2580 if (machine_escaped == NULL) {
2581 ret = ADS_ERROR(LDAP_NO_MEMORY);
2582 goto done;
2585 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2586 if (utf8_pw == NULL) {
2587 ret = ADS_ERROR(LDAP_NO_MEMORY);
2588 goto done;
2590 utf8_pw_len = strlen(utf8_pw);
2592 ok = convert_string_talloc(ctx,
2593 CH_UTF8, CH_UTF16MUNGED,
2594 utf8_pw, utf8_pw_len,
2595 (void *)&utf16_pw, &utf16_pw_len);
2596 if (!ok) {
2597 ret = ADS_ERROR(LDAP_NO_MEMORY);
2598 goto done;
2601 machine_pw_val = (struct berval) {
2602 .bv_val = utf16_pw,
2603 .bv_len = utf16_pw_len,
2606 /* Check if the machine account already exists. */
2607 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2608 if (ADS_ERR_OK(ret)) {
2609 /* Change the machine account password */
2610 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2611 ads_msgfree(ads, res);
2613 goto done;
2615 ads_msgfree(ads, res);
2617 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2618 if (new_dn == NULL) {
2619 ret = ADS_ERROR(LDAP_NO_MEMORY);
2620 goto done;
2623 /* Create machine account */
2625 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2626 if (samAccountName == NULL) {
2627 ret = ADS_ERROR(LDAP_NO_MEMORY);
2628 goto done;
2631 dns_hostname = talloc_asprintf(ctx,
2632 "%s.%s",
2633 machine_name,
2634 dns_domain_name);
2635 if (dns_hostname == NULL) {
2636 ret = ADS_ERROR(LDAP_NO_MEMORY);
2637 goto done;
2640 /* Add dns_hostname SPNs */
2641 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2642 char *spn = talloc_asprintf(ctx,
2643 "%s/%s",
2644 spn_prefix[i],
2645 dns_hostname);
2646 if (spn == NULL) {
2647 ret = ADS_ERROR(LDAP_NO_MEMORY);
2648 goto done;
2651 ok = add_string_to_array(spn_array,
2652 spn,
2653 &spn_array,
2654 &num_spns);
2655 if (!ok) {
2656 ret = ADS_ERROR(LDAP_NO_MEMORY);
2657 goto done;
2661 /* Add machine_name SPNs */
2662 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2663 char *spn = talloc_asprintf(ctx,
2664 "%s/%s",
2665 spn_prefix[i],
2666 machine_name);
2667 if (spn == NULL) {
2668 ret = ADS_ERROR(LDAP_NO_MEMORY);
2669 goto done;
2672 ok = add_string_to_array(spn_array,
2673 spn,
2674 &spn_array,
2675 &num_spns);
2676 if (!ok) {
2677 ret = ADS_ERROR(LDAP_NO_MEMORY);
2678 goto done;
2682 /* Make sure to NULL terminate the array */
2683 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2684 if (spn_array == NULL) {
2685 ret = ADS_ERROR(LDAP_NO_MEMORY);
2686 goto done;
2688 spn_array[num_spns] = NULL;
2690 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2691 if (controlstr == NULL) {
2692 ret = ADS_ERROR(LDAP_NO_MEMORY);
2693 goto done;
2696 mods = ads_init_mods(ctx);
2697 if (mods == NULL) {
2698 ret = ADS_ERROR(LDAP_NO_MEMORY);
2699 goto done;
2702 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2703 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2704 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2705 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2706 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2707 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2709 ret = ads_gen_add(ads, new_dn, mods);
2711 done:
2712 SAFE_FREE(machine_escaped);
2713 talloc_destroy(ctx);
2715 return ret;
2719 * move a machine account to another OU on the ADS server
2720 * @param ads - An intialized ADS_STRUCT
2721 * @param machine_name - the NetBIOS machine name of this account.
2722 * @param org_unit - The LDAP path in which to place this account
2723 * @param moved - whether we moved the machine account (optional)
2724 * @return 0 upon success, or non-zero otherwise
2727 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2728 const char *org_unit, bool *moved)
2730 ADS_STATUS rc;
2731 int ldap_status;
2732 LDAPMessage *res = NULL;
2733 char *filter = NULL;
2734 char *computer_dn = NULL;
2735 char *parent_dn;
2736 char *computer_rdn = NULL;
2737 bool need_move = False;
2739 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2740 rc = ADS_ERROR(LDAP_NO_MEMORY);
2741 goto done;
2744 /* Find pre-existing machine */
2745 rc = ads_search(ads, &res, filter, NULL);
2746 if (!ADS_ERR_OK(rc)) {
2747 goto done;
2750 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2751 if (!computer_dn) {
2752 rc = ADS_ERROR(LDAP_NO_MEMORY);
2753 goto done;
2756 parent_dn = ads_parent_dn(computer_dn);
2757 if (strequal(parent_dn, org_unit)) {
2758 goto done;
2761 need_move = True;
2763 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2764 rc = ADS_ERROR(LDAP_NO_MEMORY);
2765 goto done;
2768 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2769 org_unit, 1, NULL, NULL);
2770 rc = ADS_ERROR(ldap_status);
2772 done:
2773 ads_msgfree(ads, res);
2774 SAFE_FREE(filter);
2775 TALLOC_FREE(computer_dn);
2776 SAFE_FREE(computer_rdn);
2778 if (!ADS_ERR_OK(rc)) {
2779 need_move = False;
2782 if (moved) {
2783 *moved = need_move;
2786 return rc;
2790 dump a binary result from ldap
2792 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2794 size_t i;
2795 for (i=0; values[i]; i++) {
2796 ber_len_t j;
2797 printf("%s: ", field);
2798 for (j=0; j<values[i]->bv_len; j++) {
2799 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2801 printf("\n");
2805 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2807 int i;
2808 for (i=0; values[i]; i++) {
2809 NTSTATUS status;
2810 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2811 struct GUID guid;
2813 status = GUID_from_ndr_blob(&in, &guid);
2814 if (NT_STATUS_IS_OK(status)) {
2815 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2816 } else {
2817 printf("%s: INVALID GUID\n", field);
2823 dump a sid result from ldap
2825 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2827 int i;
2828 for (i=0; values[i]; i++) {
2829 ssize_t ret;
2830 struct dom_sid sid;
2831 struct dom_sid_buf tmp;
2832 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2833 values[i]->bv_len, &sid);
2834 if (ret == -1) {
2835 return;
2837 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2842 dump ntSecurityDescriptor
2844 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2846 TALLOC_CTX *frame = talloc_stackframe();
2847 struct security_descriptor *psd;
2848 NTSTATUS status;
2850 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2851 values[0]->bv_len, &psd);
2852 if (!NT_STATUS_IS_OK(status)) {
2853 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2854 nt_errstr(status)));
2855 TALLOC_FREE(frame);
2856 return;
2859 if (psd) {
2860 ads_disp_sd(ads, talloc_tos(), psd);
2863 TALLOC_FREE(frame);
2867 dump a string result from ldap
2869 static void dump_string(const char *field, char **values)
2871 int i;
2872 for (i=0; values[i]; i++) {
2873 printf("%s: %s\n", field, values[i]);
2878 dump a field from LDAP on stdout
2879 used for debugging
2882 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2884 const struct {
2885 const char *name;
2886 bool string;
2887 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2888 } handlers[] = {
2889 {"objectGUID", False, dump_guid},
2890 {"netbootGUID", False, dump_guid},
2891 {"nTSecurityDescriptor", False, dump_sd},
2892 {"dnsRecord", False, dump_binary},
2893 {"objectSid", False, dump_sid},
2894 {"tokenGroups", False, dump_sid},
2895 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2896 {"tokengroupsGlobalandUniversal", False, dump_sid},
2897 {"mS-DS-CreatorSID", False, dump_sid},
2898 {"msExchMailboxGuid", False, dump_guid},
2899 {NULL, True, NULL}
2901 int i;
2903 if (!field) { /* must be end of an entry */
2904 printf("\n");
2905 return False;
2908 for (i=0; handlers[i].name; i++) {
2909 if (strcasecmp_m(handlers[i].name, field) == 0) {
2910 if (!values) /* first time, indicate string or not */
2911 return handlers[i].string;
2912 handlers[i].handler(ads, field, (struct berval **) values);
2913 break;
2916 if (!handlers[i].name) {
2917 if (!values) /* first time, indicate string conversion */
2918 return True;
2919 dump_string(field, (char **)values);
2921 return False;
2925 * Dump a result from LDAP on stdout
2926 * used for debugging
2927 * @param ads connection to ads server
2928 * @param res Results to dump
2931 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2933 ads_process_results(ads, res, ads_dump_field, NULL);
2937 * Walk through results, calling a function for each entry found.
2938 * The function receives a field name, a berval * array of values,
2939 * and a data area passed through from the start. The function is
2940 * called once with null for field and values at the end of each
2941 * entry.
2942 * @param ads connection to ads server
2943 * @param res Results to process
2944 * @param fn Function for processing each result
2945 * @param data_area user-defined area to pass to function
2947 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2948 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2949 void *data_area)
2951 LDAPMessage *msg;
2952 TALLOC_CTX *ctx;
2953 size_t converted_size;
2955 if (!(ctx = talloc_init("ads_process_results")))
2956 return;
2958 for (msg = ads_first_entry(ads, res); msg;
2959 msg = ads_next_entry(ads, msg)) {
2960 char *utf8_field;
2961 BerElement *b;
2963 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2964 (LDAPMessage *)msg,&b);
2965 utf8_field;
2966 utf8_field=ldap_next_attribute(ads->ldap.ld,
2967 (LDAPMessage *)msg,b)) {
2968 struct berval **ber_vals;
2969 char **str_vals;
2970 char **utf8_vals;
2971 char *field;
2972 bool string;
2974 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2975 &converted_size))
2977 DEBUG(0,("ads_process_results: "
2978 "pull_utf8_talloc failed: %s",
2979 strerror(errno)));
2982 string = fn(ads, field, NULL, data_area);
2984 if (string) {
2985 const char **p;
2987 utf8_vals = ldap_get_values(ads->ldap.ld,
2988 (LDAPMessage *)msg, field);
2989 p = discard_const_p(const char *, utf8_vals);
2990 str_vals = ads_pull_strvals(ctx, p);
2991 fn(ads, field, (void **) str_vals, data_area);
2992 ldap_value_free(utf8_vals);
2993 } else {
2994 ber_vals = ldap_get_values_len(ads->ldap.ld,
2995 (LDAPMessage *)msg, field);
2996 fn(ads, field, (void **) ber_vals, data_area);
2998 ldap_value_free_len(ber_vals);
3000 ldap_memfree(utf8_field);
3002 ber_free(b, 0);
3003 talloc_free_children(ctx);
3004 fn(ads, NULL, NULL, data_area); /* completed an entry */
3007 talloc_destroy(ctx);
3011 * count how many replies are in a LDAPMessage
3012 * @param ads connection to ads server
3013 * @param res Results to count
3014 * @return number of replies
3016 int ads_count_replies(ADS_STRUCT *ads, void *res)
3018 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3022 * pull the first entry from a ADS result
3023 * @param ads connection to ads server
3024 * @param res Results of search
3025 * @return first entry from result
3027 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3029 return ldap_first_entry(ads->ldap.ld, res);
3033 * pull the next entry from a ADS result
3034 * @param ads connection to ads server
3035 * @param res Results of search
3036 * @return next entry from result
3038 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3040 return ldap_next_entry(ads->ldap.ld, res);
3044 * pull the first message from a ADS result
3045 * @param ads connection to ads server
3046 * @param res Results of search
3047 * @return first message from result
3049 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3051 return ldap_first_message(ads->ldap.ld, res);
3055 * pull the next message from a ADS result
3056 * @param ads connection to ads server
3057 * @param res Results of search
3058 * @return next message from result
3060 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3062 return ldap_next_message(ads->ldap.ld, res);
3066 * pull a single string from a ADS result
3067 * @param ads connection to ads server
3068 * @param mem_ctx TALLOC_CTX to use for allocating result string
3069 * @param msg Results of search
3070 * @param field Attribute to retrieve
3071 * @return Result string in talloc context
3073 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3074 const char *field)
3076 char **values;
3077 char *ret = NULL;
3078 char *ux_string;
3079 size_t converted_size;
3081 values = ldap_get_values(ads->ldap.ld, msg, field);
3082 if (!values)
3083 return NULL;
3085 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3086 &converted_size))
3088 ret = ux_string;
3090 ldap_value_free(values);
3091 return ret;
3095 * pull an array of strings from a ADS result
3096 * @param ads connection to ads server
3097 * @param mem_ctx TALLOC_CTX to use for allocating result string
3098 * @param msg Results of search
3099 * @param field Attribute to retrieve
3100 * @return Result strings in talloc context
3102 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3103 LDAPMessage *msg, const char *field,
3104 size_t *num_values)
3106 char **values;
3107 char **ret = NULL;
3108 size_t i, converted_size;
3110 values = ldap_get_values(ads->ldap.ld, msg, field);
3111 if (!values)
3112 return NULL;
3114 *num_values = ldap_count_values(values);
3116 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3117 if (!ret) {
3118 ldap_value_free(values);
3119 return NULL;
3122 for (i=0;i<*num_values;i++) {
3123 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3124 &converted_size))
3126 ldap_value_free(values);
3127 return NULL;
3130 ret[i] = NULL;
3132 ldap_value_free(values);
3133 return ret;
3137 * pull an array of strings from a ADS result
3138 * (handle large multivalue attributes with range retrieval)
3139 * @param ads connection to ads server
3140 * @param mem_ctx TALLOC_CTX to use for allocating result string
3141 * @param msg Results of search
3142 * @param field Attribute to retrieve
3143 * @param current_strings strings returned by a previous call to this function
3144 * @param next_attribute The next query should ask for this attribute
3145 * @param num_values How many values did we get this time?
3146 * @param more_values Are there more values to get?
3147 * @return Result strings in talloc context
3149 char **ads_pull_strings_range(ADS_STRUCT *ads,
3150 TALLOC_CTX *mem_ctx,
3151 LDAPMessage *msg, const char *field,
3152 char **current_strings,
3153 const char **next_attribute,
3154 size_t *num_strings,
3155 bool *more_strings)
3157 char *attr;
3158 char *expected_range_attrib, *range_attr;
3159 BerElement *ptr = NULL;
3160 char **strings;
3161 char **new_strings;
3162 size_t num_new_strings;
3163 unsigned long int range_start;
3164 unsigned long int range_end;
3166 /* we might have been given the whole lot anyway */
3167 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3168 *more_strings = False;
3169 return strings;
3172 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3174 /* look for Range result */
3175 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3176 attr;
3177 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3178 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3179 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3180 range_attr = attr;
3181 break;
3183 ldap_memfree(attr);
3185 if (!attr) {
3186 ber_free(ptr, 0);
3187 /* nothing here - this field is just empty */
3188 *more_strings = False;
3189 return NULL;
3192 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3193 &range_start, &range_end) == 2) {
3194 *more_strings = True;
3195 } else {
3196 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3197 &range_start) == 1) {
3198 *more_strings = False;
3199 } else {
3200 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
3201 range_attr));
3202 ldap_memfree(range_attr);
3203 *more_strings = False;
3204 return NULL;
3208 if ((*num_strings) != range_start) {
3209 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3210 " - aborting range retreival\n",
3211 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3212 ldap_memfree(range_attr);
3213 *more_strings = False;
3214 return NULL;
3217 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3219 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3220 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3221 "strings in this bunch, but we only got %lu - aborting range retreival\n",
3222 range_attr, (unsigned long int)range_end - range_start + 1,
3223 (unsigned long int)num_new_strings));
3224 ldap_memfree(range_attr);
3225 *more_strings = False;
3226 return NULL;
3229 strings = talloc_realloc(mem_ctx, current_strings, char *,
3230 *num_strings + num_new_strings);
3232 if (strings == NULL) {
3233 ldap_memfree(range_attr);
3234 *more_strings = False;
3235 return NULL;
3238 if (new_strings && num_new_strings) {
3239 memcpy(&strings[*num_strings], new_strings,
3240 sizeof(*new_strings) * num_new_strings);
3243 (*num_strings) += num_new_strings;
3245 if (*more_strings) {
3246 *next_attribute = talloc_asprintf(mem_ctx,
3247 "%s;range=%d-*",
3248 field,
3249 (int)*num_strings);
3251 if (!*next_attribute) {
3252 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3253 ldap_memfree(range_attr);
3254 *more_strings = False;
3255 return NULL;
3259 ldap_memfree(range_attr);
3261 return strings;
3265 * pull a single uint32_t from a ADS result
3266 * @param ads connection to ads server
3267 * @param msg Results of search
3268 * @param field Attribute to retrieve
3269 * @param v Pointer to int to store result
3270 * @return boolean inidicating success
3272 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3273 uint32_t *v)
3275 char **values;
3277 values = ldap_get_values(ads->ldap.ld, msg, field);
3278 if (!values)
3279 return False;
3280 if (!values[0]) {
3281 ldap_value_free(values);
3282 return False;
3285 *v = atoi(values[0]);
3286 ldap_value_free(values);
3287 return True;
3291 * pull a single objectGUID from an ADS result
3292 * @param ads connection to ADS server
3293 * @param msg results of search
3294 * @param guid 37-byte area to receive text guid
3295 * @return boolean indicating success
3297 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3299 DATA_BLOB blob;
3300 NTSTATUS status;
3302 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3303 &blob)) {
3304 return false;
3307 status = GUID_from_ndr_blob(&blob, guid);
3308 talloc_free(blob.data);
3309 return NT_STATUS_IS_OK(status);
3314 * pull a single struct dom_sid from a ADS result
3315 * @param ads connection to ads server
3316 * @param msg Results of search
3317 * @param field Attribute to retrieve
3318 * @param sid Pointer to sid to store result
3319 * @return boolean inidicating success
3321 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3322 struct dom_sid *sid)
3324 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3328 * pull an array of struct dom_sids from a ADS result
3329 * @param ads connection to ads server
3330 * @param mem_ctx TALLOC_CTX for allocating sid array
3331 * @param msg Results of search
3332 * @param field Attribute to retrieve
3333 * @param sids pointer to sid array to allocate
3334 * @return the count of SIDs pulled
3336 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3337 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3339 struct berval **values;
3340 int count, i;
3342 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3344 if (!values)
3345 return 0;
3347 for (i=0; values[i]; i++)
3348 /* nop */ ;
3350 if (i) {
3351 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3352 if (!(*sids)) {
3353 ldap_value_free_len(values);
3354 return 0;
3356 } else {
3357 (*sids) = NULL;
3360 count = 0;
3361 for (i=0; values[i]; i++) {
3362 ssize_t ret;
3363 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3364 values[i]->bv_len, &(*sids)[count]);
3365 if (ret != -1) {
3366 struct dom_sid_buf buf;
3367 DBG_DEBUG("pulling SID: %s\n",
3368 dom_sid_str_buf(&(*sids)[count], &buf));
3369 count++;
3373 ldap_value_free_len(values);
3374 return count;
3378 * pull a struct security_descriptor from a ADS result
3379 * @param ads connection to ads server
3380 * @param mem_ctx TALLOC_CTX for allocating sid array
3381 * @param msg Results of search
3382 * @param field Attribute to retrieve
3383 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3384 * @return boolean inidicating success
3386 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3387 LDAPMessage *msg, const char *field,
3388 struct security_descriptor **sd)
3390 struct berval **values;
3391 bool ret = true;
3393 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3395 if (!values) return false;
3397 if (values[0]) {
3398 NTSTATUS status;
3399 status = unmarshall_sec_desc(mem_ctx,
3400 (uint8_t *)values[0]->bv_val,
3401 values[0]->bv_len, sd);
3402 if (!NT_STATUS_IS_OK(status)) {
3403 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3404 nt_errstr(status)));
3405 ret = false;
3409 ldap_value_free_len(values);
3410 return ret;
3414 * in order to support usernames longer than 21 characters we need to
3415 * use both the sAMAccountName and the userPrincipalName attributes
3416 * It seems that not all users have the userPrincipalName attribute set
3418 * @param ads connection to ads server
3419 * @param mem_ctx TALLOC_CTX for allocating sid array
3420 * @param msg Results of search
3421 * @return the username
3423 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3424 LDAPMessage *msg)
3426 #if 0 /* JERRY */
3427 char *ret, *p;
3429 /* lookup_name() only works on the sAMAccountName to
3430 returning the username portion of userPrincipalName
3431 breaks winbindd_getpwnam() */
3433 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3434 if (ret && (p = strchr_m(ret, '@'))) {
3435 *p = 0;
3436 return ret;
3438 #endif
3439 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3444 * find the update serial number - this is the core of the ldap cache
3445 * @param ads connection to ads server
3446 * @param ads connection to ADS server
3447 * @param usn Pointer to retrieved update serial number
3448 * @return status of search
3450 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3452 const char *attrs[] = {"highestCommittedUSN", NULL};
3453 ADS_STATUS status;
3454 LDAPMessage *res;
3456 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3457 if (!ADS_ERR_OK(status))
3458 return status;
3460 if (ads_count_replies(ads, res) != 1) {
3461 ads_msgfree(ads, res);
3462 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3465 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3466 ads_msgfree(ads, res);
3467 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3470 ads_msgfree(ads, res);
3471 return ADS_SUCCESS;
3474 /* parse a ADS timestring - typical string is
3475 '20020917091222.0Z0' which means 09:12.22 17th September
3476 2002, timezone 0 */
3477 static time_t ads_parse_time(const char *str)
3479 struct tm tm;
3481 ZERO_STRUCT(tm);
3483 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3484 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3485 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3486 return 0;
3488 tm.tm_year -= 1900;
3489 tm.tm_mon -= 1;
3491 return timegm(&tm);
3494 /********************************************************************
3495 ********************************************************************/
3497 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3499 const char *attrs[] = {"currentTime", NULL};
3500 ADS_STATUS status;
3501 LDAPMessage *res;
3502 char *timestr;
3503 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3504 ADS_STRUCT *ads_s = ads;
3506 /* establish a new ldap tcp session if necessary */
3508 if ( !ads->ldap.ld ) {
3510 * ADS_STRUCT may be being reused after a
3511 * DC lookup, so ads->ldap.ss may already have a
3512 * good address. If not, re-initialize the passed-in
3513 * ADS_STRUCT with the given server.XXXX parameters.
3515 * Note that this doesn't depend on
3516 * ads->server.ldap_server != NULL,
3517 * as the case where ads->server.ldap_server==NULL and
3518 * ads->ldap.ss != zero_address is precisely the DC
3519 * lookup case where ads->ldap.ss was found by going
3520 * through ads_find_dc() again we want to avoid repeating.
3522 if (is_zero_addr(&ads->ldap.ss)) {
3523 ads_s = ads_init(tmp_ctx,
3524 ads->server.realm,
3525 ads->server.workgroup,
3526 ads->server.ldap_server,
3527 ADS_SASL_PLAIN );
3528 if (ads_s == NULL) {
3529 status = ADS_ERROR(LDAP_NO_MEMORY);
3530 goto done;
3535 * Reset ads->config.flags as it can contain the flags
3536 * returned by the previous CLDAP ping when reusing the struct.
3538 ads_s->config.flags = 0;
3540 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3541 status = ads_connect( ads_s );
3542 if ( !ADS_ERR_OK(status))
3543 goto done;
3546 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3547 if (!ADS_ERR_OK(status)) {
3548 goto done;
3551 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3552 if (!timestr) {
3553 ads_msgfree(ads_s, res);
3554 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3555 goto done;
3558 /* but save the time and offset in the original ADS_STRUCT */
3560 ads->config.current_time = ads_parse_time(timestr);
3562 if (ads->config.current_time != 0) {
3563 ads->auth.time_offset = ads->config.current_time - time(NULL);
3564 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3567 ads_msgfree(ads, res);
3569 status = ADS_SUCCESS;
3571 done:
3572 TALLOC_FREE(tmp_ctx);
3574 return status;
3577 /********************************************************************
3578 ********************************************************************/
3580 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3582 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3583 const char *attrs[] = {"domainFunctionality", NULL};
3584 ADS_STATUS status;
3585 LDAPMessage *res;
3586 ADS_STRUCT *ads_s = ads;
3588 *val = DS_DOMAIN_FUNCTION_2000;
3590 /* establish a new ldap tcp session if necessary */
3592 if ( !ads->ldap.ld ) {
3594 * ADS_STRUCT may be being reused after a
3595 * DC lookup, so ads->ldap.ss may already have a
3596 * good address. If not, re-initialize the passed-in
3597 * ADS_STRUCT with the given server.XXXX parameters.
3599 * Note that this doesn't depend on
3600 * ads->server.ldap_server != NULL,
3601 * as the case where ads->server.ldap_server==NULL and
3602 * ads->ldap.ss != zero_address is precisely the DC
3603 * lookup case where ads->ldap.ss was found by going
3604 * through ads_find_dc() again we want to avoid repeating.
3606 if (is_zero_addr(&ads->ldap.ss)) {
3607 ads_s = ads_init(tmp_ctx,
3608 ads->server.realm,
3609 ads->server.workgroup,
3610 ads->server.ldap_server,
3611 ADS_SASL_PLAIN );
3612 if (ads_s == NULL ) {
3613 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3614 goto done;
3619 * Reset ads->config.flags as it can contain the flags
3620 * returned by the previous CLDAP ping when reusing the struct.
3622 ads_s->config.flags = 0;
3624 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3625 status = ads_connect( ads_s );
3626 if ( !ADS_ERR_OK(status))
3627 goto done;
3630 /* If the attribute does not exist assume it is a Windows 2000
3631 functional domain */
3633 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3634 if (!ADS_ERR_OK(status)) {
3635 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3636 status = ADS_SUCCESS;
3638 goto done;
3641 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3642 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3644 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3647 ads_msgfree(ads_s, res);
3649 done:
3650 TALLOC_FREE(tmp_ctx);
3652 return status;
3656 * find the domain sid for our domain
3657 * @param ads connection to ads server
3658 * @param sid Pointer to domain sid
3659 * @return status of search
3661 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3663 const char *attrs[] = {"objectSid", NULL};
3664 LDAPMessage *res;
3665 ADS_STATUS rc;
3667 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3668 attrs, &res);
3669 if (!ADS_ERR_OK(rc)) return rc;
3670 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3671 ads_msgfree(ads, res);
3672 return ADS_ERROR_SYSTEM(ENOENT);
3674 ads_msgfree(ads, res);
3676 return ADS_SUCCESS;
3680 * find our site name
3681 * @param ads connection to ads server
3682 * @param mem_ctx Pointer to talloc context
3683 * @param site_name Pointer to the sitename
3684 * @return status of search
3686 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3688 ADS_STATUS status;
3689 LDAPMessage *res;
3690 const char *dn, *service_name;
3691 const char *attrs[] = { "dsServiceName", NULL };
3693 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3694 if (!ADS_ERR_OK(status)) {
3695 return status;
3698 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3699 if (service_name == NULL) {
3700 ads_msgfree(ads, res);
3701 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3704 ads_msgfree(ads, res);
3706 /* go up three levels */
3707 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3708 if (dn == NULL) {
3709 return ADS_ERROR(LDAP_NO_MEMORY);
3712 *site_name = talloc_strdup(mem_ctx, dn);
3713 if (*site_name == NULL) {
3714 return ADS_ERROR(LDAP_NO_MEMORY);
3717 return status;
3719 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3724 * find the site dn where a machine resides
3725 * @param ads connection to ads server
3726 * @param mem_ctx Pointer to talloc context
3727 * @param computer_name name of the machine
3728 * @param site_name Pointer to the sitename
3729 * @return status of search
3731 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3733 ADS_STATUS status;
3734 LDAPMessage *res;
3735 const char *parent, *filter;
3736 char *config_context = NULL;
3737 char *dn;
3739 /* shortcut a query */
3740 if (strequal(computer_name, ads->config.ldap_server_name)) {
3741 return ads_site_dn(ads, mem_ctx, site_dn);
3744 status = ads_config_path(ads, mem_ctx, &config_context);
3745 if (!ADS_ERR_OK(status)) {
3746 return status;
3749 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3750 if (filter == NULL) {
3751 return ADS_ERROR(LDAP_NO_MEMORY);
3754 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3755 filter, NULL, &res);
3756 if (!ADS_ERR_OK(status)) {
3757 return status;
3760 if (ads_count_replies(ads, res) != 1) {
3761 ads_msgfree(ads, res);
3762 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3765 dn = ads_get_dn(ads, mem_ctx, res);
3766 if (dn == NULL) {
3767 ads_msgfree(ads, res);
3768 return ADS_ERROR(LDAP_NO_MEMORY);
3771 /* go up three levels */
3772 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3773 if (parent == NULL) {
3774 ads_msgfree(ads, res);
3775 TALLOC_FREE(dn);
3776 return ADS_ERROR(LDAP_NO_MEMORY);
3779 *site_dn = talloc_strdup(mem_ctx, parent);
3780 if (*site_dn == NULL) {
3781 ads_msgfree(ads, res);
3782 TALLOC_FREE(dn);
3783 return ADS_ERROR(LDAP_NO_MEMORY);
3786 TALLOC_FREE(dn);
3787 ads_msgfree(ads, res);
3789 return status;
3793 * get the upn suffixes for a domain
3794 * @param ads connection to ads server
3795 * @param mem_ctx Pointer to talloc context
3796 * @param suffixes Pointer to an array of suffixes
3797 * @param num_suffixes Pointer to the number of suffixes
3798 * @return status of search
3800 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3802 ADS_STATUS status;
3803 LDAPMessage *res;
3804 const char *base;
3805 char *config_context = NULL;
3806 const char *attrs[] = { "uPNSuffixes", NULL };
3808 status = ads_config_path(ads, mem_ctx, &config_context);
3809 if (!ADS_ERR_OK(status)) {
3810 return status;
3813 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3814 if (base == NULL) {
3815 return ADS_ERROR(LDAP_NO_MEMORY);
3818 status = ads_search_dn(ads, &res, base, attrs);
3819 if (!ADS_ERR_OK(status)) {
3820 return status;
3823 if (ads_count_replies(ads, res) != 1) {
3824 ads_msgfree(ads, res);
3825 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3828 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3829 if ((*suffixes) == NULL) {
3830 ads_msgfree(ads, res);
3831 return ADS_ERROR(LDAP_NO_MEMORY);
3834 ads_msgfree(ads, res);
3836 return status;
3840 * get the joinable ous for a domain
3841 * @param ads connection to ads server
3842 * @param mem_ctx Pointer to talloc context
3843 * @param ous Pointer to an array of ous
3844 * @param num_ous Pointer to the number of ous
3845 * @return status of search
3847 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3848 TALLOC_CTX *mem_ctx,
3849 char ***ous,
3850 size_t *num_ous)
3852 ADS_STATUS status;
3853 LDAPMessage *res = NULL;
3854 LDAPMessage *msg = NULL;
3855 const char *attrs[] = { "dn", NULL };
3856 int count = 0;
3858 status = ads_search(ads, &res,
3859 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3860 attrs);
3861 if (!ADS_ERR_OK(status)) {
3862 return status;
3865 count = ads_count_replies(ads, res);
3866 if (count < 1) {
3867 ads_msgfree(ads, res);
3868 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3871 for (msg = ads_first_entry(ads, res); msg;
3872 msg = ads_next_entry(ads, msg)) {
3873 const char **p = discard_const_p(const char *, *ous);
3874 char *dn = NULL;
3876 dn = ads_get_dn(ads, talloc_tos(), msg);
3877 if (!dn) {
3878 ads_msgfree(ads, res);
3879 return ADS_ERROR(LDAP_NO_MEMORY);
3882 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3883 TALLOC_FREE(dn);
3884 ads_msgfree(ads, res);
3885 return ADS_ERROR(LDAP_NO_MEMORY);
3888 TALLOC_FREE(dn);
3889 *ous = discard_const_p(char *, p);
3892 ads_msgfree(ads, res);
3894 return status;
3899 * pull a struct dom_sid from an extended dn string
3900 * @param mem_ctx TALLOC_CTX
3901 * @param extended_dn string
3902 * @param flags string type of extended_dn
3903 * @param sid pointer to a struct dom_sid
3904 * @return NT_STATUS_OK on success,
3905 * NT_INVALID_PARAMETER on error,
3906 * NT_STATUS_NOT_FOUND if no SID present
3908 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3909 const char *extended_dn,
3910 enum ads_extended_dn_flags flags,
3911 struct dom_sid *sid)
3913 char *p, *q, *dn;
3915 if (!extended_dn) {
3916 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3919 /* otherwise extended_dn gets stripped off */
3920 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3921 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3924 * ADS_EXTENDED_DN_HEX_STRING:
3925 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3927 * ADS_EXTENDED_DN_STRING (only with w2k3):
3928 * <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
3930 * Object with no SID, such as an Exchange Public Folder
3931 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3934 p = strchr(dn, ';');
3935 if (!p) {
3936 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3939 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3940 DEBUG(5,("No SID present in extended dn\n"));
3941 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3944 p += strlen(";<SID=");
3946 q = strchr(p, '>');
3947 if (!q) {
3948 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3951 *q = '\0';
3953 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3955 switch (flags) {
3957 case ADS_EXTENDED_DN_STRING:
3958 if (!string_to_sid(sid, p)) {
3959 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3961 break;
3962 case ADS_EXTENDED_DN_HEX_STRING: {
3963 ssize_t ret;
3964 fstring buf;
3965 size_t buf_len;
3967 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3968 if (buf_len == 0) {
3969 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3972 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3973 if (ret == -1) {
3974 DEBUG(10,("failed to parse sid\n"));
3975 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3977 break;
3979 default:
3980 DEBUG(10,("unknown extended dn format\n"));
3981 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3984 return ADS_ERROR_NT(NT_STATUS_OK);
3987 /********************************************************************
3988 ********************************************************************/
3990 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3992 LDAPMessage *res = NULL;
3993 ADS_STATUS status;
3994 int count = 0;
3995 char *name = NULL;
3997 status = ads_find_machine_acct(ads, &res, machine_name);
3998 if (!ADS_ERR_OK(status)) {
3999 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4000 lp_netbios_name()));
4001 goto out;
4004 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4005 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4006 goto out;
4009 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4010 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4013 out:
4014 ads_msgfree(ads, res);
4016 return name;
4019 /********************************************************************
4020 ********************************************************************/
4022 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4023 LDAPMessage *msg, size_t *num_values)
4025 const char *field = "msDS-AdditionalDnsHostName";
4026 struct berval **values = NULL;
4027 char **ret = NULL;
4028 size_t i, converted_size;
4031 * Windows DC implicitly adds a short name for each FQDN added to
4032 * msDS-AdditionalDnsHostName, but it comes with a strage binary
4033 * suffix "\0$" which we should ignore (see bug #14406).
4036 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4037 if (values == NULL) {
4038 return NULL;
4041 *num_values = ldap_count_values_len(values);
4043 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4044 if (ret == NULL) {
4045 ldap_value_free_len(values);
4046 return NULL;
4049 for (i = 0; i < *num_values; i++) {
4050 ret[i] = NULL;
4051 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4052 values[i]->bv_val,
4053 strnlen(values[i]->bv_val,
4054 values[i]->bv_len),
4055 &ret[i], &converted_size)) {
4056 ldap_value_free_len(values);
4057 return NULL;
4060 ret[i] = NULL;
4062 ldap_value_free_len(values);
4063 return ret;
4066 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4067 ADS_STRUCT *ads,
4068 const char *machine_name,
4069 char ***hostnames_array,
4070 size_t *num_hostnames)
4072 ADS_STATUS status;
4073 LDAPMessage *res = NULL;
4074 int count;
4076 status = ads_find_machine_acct(ads,
4077 &res,
4078 machine_name);
4079 if (!ADS_ERR_OK(status)) {
4080 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4081 machine_name));
4082 return status;
4085 count = ads_count_replies(ads, res);
4086 if (count != 1) {
4087 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4088 goto done;
4091 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4092 if (*hostnames_array == NULL) {
4093 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4094 machine_name));
4095 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4096 goto done;
4099 done:
4100 ads_msgfree(ads, res);
4102 return status;
4105 /********************************************************************
4106 ********************************************************************/
4108 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4110 LDAPMessage *res = NULL;
4111 ADS_STATUS status;
4112 int count = 0;
4113 char *name = NULL;
4115 status = ads_find_machine_acct(ads, &res, machine_name);
4116 if (!ADS_ERR_OK(status)) {
4117 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4118 lp_netbios_name()));
4119 goto out;
4122 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4123 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4124 goto out;
4127 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4128 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4131 out:
4132 ads_msgfree(ads, res);
4134 return name;
4137 /********************************************************************
4138 ********************************************************************/
4140 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4142 LDAPMessage *res = NULL;
4143 ADS_STATUS status;
4144 int count = 0;
4145 char *name = NULL;
4146 bool ok = false;
4148 status = ads_find_machine_acct(ads, &res, machine_name);
4149 if (!ADS_ERR_OK(status)) {
4150 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4151 lp_netbios_name()));
4152 goto out;
4155 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4156 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4157 goto out;
4160 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4161 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4164 out:
4165 ads_msgfree(ads, res);
4166 if (name != NULL) {
4167 ok = (strlen(name) > 0);
4169 TALLOC_FREE(name);
4170 return ok;
4173 #if 0
4175 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4178 * Join a machine to a realm
4179 * Creates the machine account and sets the machine password
4180 * @param ads connection to ads server
4181 * @param machine name of host to add
4182 * @param org_unit Organizational unit to place machine in
4183 * @return status of join
4185 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4186 uint32_t account_type, const char *org_unit)
4188 ADS_STATUS status;
4189 LDAPMessage *res = NULL;
4190 char *machine;
4192 /* machine name must be lowercase */
4193 machine = SMB_STRDUP(machine_name);
4194 strlower_m(machine);
4197 status = ads_find_machine_acct(ads, (void **)&res, machine);
4198 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4199 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4200 status = ads_leave_realm(ads, machine);
4201 if (!ADS_ERR_OK(status)) {
4202 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4203 machine, ads->config.realm));
4204 return status;
4208 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4209 if (!ADS_ERR_OK(status)) {
4210 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4211 SAFE_FREE(machine);
4212 return status;
4215 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4216 if (!ADS_ERR_OK(status)) {
4217 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4218 SAFE_FREE(machine);
4219 return status;
4222 SAFE_FREE(machine);
4223 ads_msgfree(ads, res);
4225 return status;
4227 #endif
4230 * Delete a machine from the realm
4231 * @param ads connection to ads server
4232 * @param hostname Machine to remove
4233 * @return status of delete
4235 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4237 ADS_STATUS status;
4238 void *msg;
4239 LDAPMessage *res;
4240 char *hostnameDN, *host;
4241 int rc;
4242 LDAPControl ldap_control;
4243 LDAPControl * pldap_control[2] = {NULL, NULL};
4245 pldap_control[0] = &ldap_control;
4246 memset(&ldap_control, 0, sizeof(LDAPControl));
4247 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4249 /* hostname must be lowercase */
4250 host = SMB_STRDUP(hostname);
4251 if (!strlower_m(host)) {
4252 SAFE_FREE(host);
4253 return ADS_ERROR_SYSTEM(EINVAL);
4256 status = ads_find_machine_acct(ads, &res, host);
4257 if (!ADS_ERR_OK(status)) {
4258 DEBUG(0, ("Host account for %s does not exist.\n", host));
4259 SAFE_FREE(host);
4260 return status;
4263 msg = ads_first_entry(ads, res);
4264 if (!msg) {
4265 SAFE_FREE(host);
4266 return ADS_ERROR_SYSTEM(ENOENT);
4269 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4270 if (hostnameDN == NULL) {
4271 SAFE_FREE(host);
4272 return ADS_ERROR_SYSTEM(ENOENT);
4275 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4276 if (rc) {
4277 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4278 }else {
4279 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4282 if (rc != LDAP_SUCCESS) {
4283 const char *attrs[] = { "cn", NULL };
4284 LDAPMessage *msg_sub;
4286 /* we only search with scope ONE, we do not expect any further
4287 * objects to be created deeper */
4289 status = ads_do_search_retry(ads, hostnameDN,
4290 LDAP_SCOPE_ONELEVEL,
4291 "(objectclass=*)", attrs, &res);
4293 if (!ADS_ERR_OK(status)) {
4294 SAFE_FREE(host);
4295 TALLOC_FREE(hostnameDN);
4296 return status;
4299 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4300 msg_sub = ads_next_entry(ads, msg_sub)) {
4302 char *dn = NULL;
4304 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4305 SAFE_FREE(host);
4306 TALLOC_FREE(hostnameDN);
4307 return ADS_ERROR(LDAP_NO_MEMORY);
4310 status = ads_del_dn(ads, dn);
4311 if (!ADS_ERR_OK(status)) {
4312 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4313 SAFE_FREE(host);
4314 TALLOC_FREE(dn);
4315 TALLOC_FREE(hostnameDN);
4316 return status;
4319 TALLOC_FREE(dn);
4322 /* there should be no subordinate objects anymore */
4323 status = ads_do_search_retry(ads, hostnameDN,
4324 LDAP_SCOPE_ONELEVEL,
4325 "(objectclass=*)", attrs, &res);
4327 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4328 SAFE_FREE(host);
4329 TALLOC_FREE(hostnameDN);
4330 return status;
4333 /* delete hostnameDN now */
4334 status = ads_del_dn(ads, hostnameDN);
4335 if (!ADS_ERR_OK(status)) {
4336 SAFE_FREE(host);
4337 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4338 TALLOC_FREE(hostnameDN);
4339 return status;
4343 TALLOC_FREE(hostnameDN);
4345 status = ads_find_machine_acct(ads, &res, host);
4346 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4347 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4348 DEBUG(3, ("Failed to remove host account.\n"));
4349 SAFE_FREE(host);
4350 return status;
4353 SAFE_FREE(host);
4354 return ADS_SUCCESS;
4358 * pull all token-sids from an LDAP dn
4359 * @param ads connection to ads server
4360 * @param mem_ctx TALLOC_CTX for allocating sid array
4361 * @param dn of LDAP object
4362 * @param user_sid pointer to struct dom_sid (objectSid)
4363 * @param primary_group_sid pointer to struct dom_sid (self composed)
4364 * @param sids pointer to sid array to allocate
4365 * @param num_sids counter of SIDs pulled
4366 * @return status of token query
4368 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4369 TALLOC_CTX *mem_ctx,
4370 const char *dn,
4371 struct dom_sid *user_sid,
4372 struct dom_sid *primary_group_sid,
4373 struct dom_sid **sids,
4374 size_t *num_sids)
4376 ADS_STATUS status;
4377 LDAPMessage *res = NULL;
4378 int count = 0;
4379 size_t tmp_num_sids;
4380 struct dom_sid *tmp_sids;
4381 struct dom_sid tmp_user_sid;
4382 struct dom_sid tmp_primary_group_sid;
4383 uint32_t pgid;
4384 const char *attrs[] = {
4385 "objectSid",
4386 "tokenGroups",
4387 "primaryGroupID",
4388 NULL
4391 status = ads_search_retry_dn(ads, &res, dn, attrs);
4392 if (!ADS_ERR_OK(status)) {
4393 return status;
4396 count = ads_count_replies(ads, res);
4397 if (count != 1) {
4398 ads_msgfree(ads, res);
4399 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4402 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4403 ads_msgfree(ads, res);
4404 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4407 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4408 ads_msgfree(ads, res);
4409 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4413 /* hack to compose the primary group sid without knowing the
4414 * domsid */
4416 struct dom_sid domsid;
4418 sid_copy(&domsid, &tmp_user_sid);
4420 if (!sid_split_rid(&domsid, NULL)) {
4421 ads_msgfree(ads, res);
4422 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4425 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4426 ads_msgfree(ads, res);
4427 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4431 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4433 if (tmp_num_sids == 0 || !tmp_sids) {
4434 ads_msgfree(ads, res);
4435 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4438 if (num_sids) {
4439 *num_sids = tmp_num_sids;
4442 if (sids) {
4443 *sids = tmp_sids;
4446 if (user_sid) {
4447 *user_sid = tmp_user_sid;
4450 if (primary_group_sid) {
4451 *primary_group_sid = tmp_primary_group_sid;
4454 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4456 ads_msgfree(ads, res);
4457 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4461 * Find a sAMAccoutName in LDAP
4462 * @param ads connection to ads server
4463 * @param mem_ctx TALLOC_CTX for allocating sid array
4464 * @param samaccountname to search
4465 * @param uac_ret uint32_t pointer userAccountControl attribute value
4466 * @param dn_ret pointer to dn
4467 * @return status of token query
4469 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4470 TALLOC_CTX *mem_ctx,
4471 const char *samaccountname,
4472 uint32_t *uac_ret,
4473 const char **dn_ret)
4475 ADS_STATUS status;
4476 const char *attrs[] = { "userAccountControl", NULL };
4477 const char *filter;
4478 LDAPMessage *res = NULL;
4479 char *dn = NULL;
4480 uint32_t uac = 0;
4482 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4483 samaccountname);
4484 if (filter == NULL) {
4485 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4486 goto out;
4489 status = ads_do_search_all(ads, ads->config.bind_path,
4490 LDAP_SCOPE_SUBTREE,
4491 filter, attrs, &res);
4493 if (!ADS_ERR_OK(status)) {
4494 goto out;
4497 if (ads_count_replies(ads, res) != 1) {
4498 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4499 goto out;
4502 dn = ads_get_dn(ads, talloc_tos(), res);
4503 if (dn == NULL) {
4504 status = ADS_ERROR(LDAP_NO_MEMORY);
4505 goto out;
4508 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4509 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4510 goto out;
4513 if (uac_ret) {
4514 *uac_ret = uac;
4517 if (dn_ret) {
4518 *dn_ret = talloc_strdup(mem_ctx, dn);
4519 if (!*dn_ret) {
4520 status = ADS_ERROR(LDAP_NO_MEMORY);
4521 goto out;
4524 out:
4525 TALLOC_FREE(dn);
4526 ads_msgfree(ads, res);
4528 return status;
4532 * find our configuration path
4533 * @param ads connection to ads server
4534 * @param mem_ctx Pointer to talloc context
4535 * @param config_path Pointer to the config path
4536 * @return status of search
4538 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4539 TALLOC_CTX *mem_ctx,
4540 char **config_path)
4542 ADS_STATUS status;
4543 LDAPMessage *res = NULL;
4544 const char *config_context = NULL;
4545 const char *attrs[] = { "configurationNamingContext", NULL };
4547 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4548 "(objectclass=*)", attrs, &res);
4549 if (!ADS_ERR_OK(status)) {
4550 return status;
4553 config_context = ads_pull_string(ads, mem_ctx, res,
4554 "configurationNamingContext");
4555 ads_msgfree(ads, res);
4556 if (!config_context) {
4557 return ADS_ERROR(LDAP_NO_MEMORY);
4560 if (config_path) {
4561 *config_path = talloc_strdup(mem_ctx, config_context);
4562 if (!*config_path) {
4563 return ADS_ERROR(LDAP_NO_MEMORY);
4567 return ADS_ERROR(LDAP_SUCCESS);
4571 * find the displayName of an extended right
4572 * @param ads connection to ads server
4573 * @param config_path The config path
4574 * @param mem_ctx Pointer to talloc context
4575 * @param GUID struct of the rightsGUID
4576 * @return status of search
4578 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4579 const char *config_path,
4580 TALLOC_CTX *mem_ctx,
4581 const struct GUID *rights_guid)
4583 ADS_STATUS rc;
4584 LDAPMessage *res = NULL;
4585 char *expr = NULL;
4586 const char *attrs[] = { "displayName", NULL };
4587 const char *result = NULL;
4588 const char *path;
4590 if (!ads || !mem_ctx || !rights_guid) {
4591 goto done;
4594 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4595 GUID_string(mem_ctx, rights_guid));
4596 if (!expr) {
4597 goto done;
4600 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4601 if (!path) {
4602 goto done;
4605 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4606 expr, attrs, &res);
4607 if (!ADS_ERR_OK(rc)) {
4608 goto done;
4611 if (ads_count_replies(ads, res) != 1) {
4612 goto done;
4615 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4617 done:
4618 ads_msgfree(ads, res);
4619 return result;
4623 * verify or build and verify an account ou
4624 * @param mem_ctx Pointer to talloc context
4625 * @param ads connection to ads server
4626 * @param account_ou
4627 * @return status of search
4630 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4631 ADS_STRUCT *ads,
4632 const char **account_ou)
4634 char **exploded_dn;
4635 const char *name;
4636 char *ou_string;
4638 if (account_ou == NULL) {
4639 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4642 if (*account_ou != NULL) {
4643 exploded_dn = ldap_explode_dn(*account_ou, 0);
4644 if (exploded_dn) {
4645 ldap_value_free(exploded_dn);
4646 return ADS_SUCCESS;
4650 ou_string = ads_ou_string(ads, *account_ou);
4651 if (!ou_string) {
4652 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4655 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4656 ads->config.bind_path);
4657 SAFE_FREE(ou_string);
4659 if (!name) {
4660 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4663 exploded_dn = ldap_explode_dn(name, 0);
4664 if (!exploded_dn) {
4665 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4667 ldap_value_free(exploded_dn);
4669 *account_ou = name;
4670 return ADS_SUCCESS;
4673 #endif