s3:libads: Fix code spelling
[Samba.git] / source3 / libads / ldap.c
blob2853e15dfd38f43f6c08a2dd7c21558a85de7c00
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "ads.h"
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/tsocket/tsocket.h"
29 #include "../lib/addns/dnsquery.h"
30 #include "../libds/common/flags.h"
31 #include "smbldap.h"
32 #include "../libcli/security/security.h"
33 #include "../librpc/gen_ndr/netlogon.h"
34 #include "lib/param/loadparm.h"
35 #include "libsmb/namequery.h"
36 #include "../librpc/gen_ndr/ndr_ads.h"
38 #ifdef HAVE_LDAP
40 /**
41 * @file ldap.c
42 * @brief basic ldap client-side routines for ads server communications
44 * The routines contained here should do the necessary ldap calls for
45 * ads setups.
47 * Important note: attribute names passed into ads_ routines must
48 * already be in UTF-8 format. We do not convert them because in almost
49 * all cases, they are just ascii (which is represented with the same
50 * codepoints in UTF-8). This may have to change at some point
51 **/
54 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
56 static SIG_ATOMIC_T gotalarm;
58 /***************************************************************
59 Signal function to tell us we timed out.
60 ****************************************************************/
62 static void gotalarm_sig(int signum)
64 gotalarm = 1;
67 LDAP *ldap_open_with_timeout(const char *server,
68 struct sockaddr_storage *ss,
69 int port, unsigned int to)
71 LDAP *ldp = NULL;
72 int ldap_err;
73 char *uri;
75 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
76 "%u seconds\n", server, port, to));
78 if (to) {
79 /* Setup timeout */
80 gotalarm = 0;
81 CatchSignal(SIGALRM, gotalarm_sig);
82 alarm(to);
83 /* End setup timeout. */
86 if ( strchr_m(server, ':') ) {
87 /* IPv6 URI */
88 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
89 } else {
90 /* IPv4 URI */
91 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
93 if (uri == NULL) {
94 return NULL;
97 #ifdef HAVE_LDAP_INIT_FD
99 int fd = -1;
100 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
101 unsigned timeout_ms = 1000 * to;
103 status = open_socket_out(ss, port, timeout_ms, &fd);
104 if (!NT_STATUS_IS_OK(status)) {
105 DEBUG(3, ("open_socket_out: failed to open socket\n"));
106 return NULL;
109 /* define LDAP_PROTO_TCP from openldap.h if required */
110 #ifndef LDAP_PROTO_TCP
111 #define LDAP_PROTO_TCP 1
112 #endif
113 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
115 #elif defined(HAVE_LDAP_INITIALIZE)
116 ldap_err = ldap_initialize(&ldp, uri);
117 #else
118 ldp = ldap_open(server, port);
119 if (ldp != NULL) {
120 ldap_err = LDAP_SUCCESS;
121 } else {
122 ldap_err = LDAP_OTHER;
124 #endif
125 if (ldap_err != LDAP_SUCCESS) {
126 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
127 uri, ldap_err2string(ldap_err)));
128 } else {
129 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
132 if (to) {
133 /* Teardown timeout. */
134 alarm(0);
135 CatchSignal(SIGALRM, SIG_IGN);
138 return ldp;
141 static int ldap_search_with_timeout(LDAP *ld,
142 LDAP_CONST char *base,
143 int scope,
144 LDAP_CONST char *filter,
145 char **attrs,
146 int attrsonly,
147 LDAPControl **sctrls,
148 LDAPControl **cctrls,
149 int sizelimit,
150 LDAPMessage **res )
152 int to = lp_ldap_timeout();
153 struct timeval timeout;
154 struct timeval *timeout_ptr = NULL;
155 int result;
157 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
158 gotalarm = 0;
160 if (to) {
161 timeout.tv_sec = to;
162 timeout.tv_usec = 0;
163 timeout_ptr = &timeout;
165 /* Setup alarm timeout. */
166 CatchSignal(SIGALRM, gotalarm_sig);
167 /* Make the alarm time one second beyond
168 the timeout we're setting for the
169 remote search timeout, to allow that
170 to fire in preference. */
171 alarm(to+1);
172 /* End setup timeout. */
176 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
177 attrsonly, sctrls, cctrls, timeout_ptr,
178 sizelimit, res);
180 if (to) {
181 /* Teardown alarm timeout. */
182 CatchSignal(SIGALRM, SIG_IGN);
183 alarm(0);
186 if (gotalarm != 0)
187 return LDAP_TIMELIMIT_EXCEEDED;
190 * A bug in OpenLDAP means ldap_search_ext_s can return
191 * LDAP_SUCCESS but with a NULL res pointer. Cope with
192 * this. See bug #6279 for details. JRA.
195 if (*res == NULL) {
196 return LDAP_TIMELIMIT_EXCEEDED;
199 return result;
202 /**********************************************
203 Do client and server sitename match ?
204 **********************************************/
206 bool ads_sitename_match(ADS_STRUCT *ads)
208 if (ads->config.server_site_name == NULL &&
209 ads->config.client_site_name == NULL ) {
210 DEBUG(10,("ads_sitename_match: both null\n"));
211 return True;
213 if (ads->config.server_site_name &&
214 ads->config.client_site_name &&
215 strequal(ads->config.server_site_name,
216 ads->config.client_site_name)) {
217 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
218 return True;
220 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
221 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
222 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
223 return False;
226 /**********************************************
227 Is this the closest DC ?
228 **********************************************/
230 bool ads_closest_dc(ADS_STRUCT *ads)
232 if (ads->config.flags & NBT_SERVER_CLOSEST) {
233 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
234 return True;
237 /* not sure if this can ever happen */
238 if (ads_sitename_match(ads)) {
239 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
240 return True;
243 if (ads->config.client_site_name == NULL) {
244 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
245 return True;
248 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
249 ads->config.ldap_server_name));
251 return False;
254 static bool ads_fill_cldap_reply(ADS_STRUCT *ads,
255 bool gc,
256 const struct sockaddr_storage *ss,
257 const struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply)
259 TALLOC_CTX *frame = talloc_stackframe();
260 bool ret = false;
261 char addr[INET6_ADDRSTRLEN];
262 ADS_STATUS status;
263 char *dn;
265 print_sockaddr(addr, sizeof(addr), ss);
267 /* Check the CLDAP reply flags */
269 if (!(cldap_reply->server_type & NBT_SERVER_LDAP)) {
270 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
271 addr);
272 ret = false;
273 goto out;
276 /* Fill in the ads->config values */
278 ADS_TALLOC_CONST_FREE(ads->config.realm);
279 ADS_TALLOC_CONST_FREE(ads->config.bind_path);
280 ADS_TALLOC_CONST_FREE(ads->config.ldap_server_name);
281 ADS_TALLOC_CONST_FREE(ads->config.server_site_name);
282 ADS_TALLOC_CONST_FREE(ads->config.client_site_name);
283 ADS_TALLOC_CONST_FREE(ads->server.workgroup);
285 if (!check_cldap_reply_required_flags(cldap_reply->server_type,
286 ads->config.flags)) {
287 ret = false;
288 goto out;
291 ads->config.ldap_server_name = talloc_strdup(ads,
292 cldap_reply->pdc_dns_name);
293 if (ads->config.ldap_server_name == NULL) {
294 DBG_WARNING("Out of memory\n");
295 ret = false;
296 goto out;
299 ads->config.realm = talloc_asprintf_strupper_m(ads,
300 "%s",
301 cldap_reply->dns_domain);
302 if (ads->config.realm == NULL) {
303 DBG_WARNING("Out of memory\n");
304 ret = false;
305 goto out;
308 status = ads_build_dn(ads->config.realm, ads, &dn);
309 if (!ADS_ERR_OK(status)) {
310 DBG_DEBUG("Failed to build bind path: %s\n",
311 ads_errstr(status));
312 ret = false;
313 goto out;
315 ads->config.bind_path = dn;
317 if (*cldap_reply->server_site) {
318 ads->config.server_site_name =
319 talloc_strdup(ads, cldap_reply->server_site);
320 if (ads->config.server_site_name == NULL) {
321 DBG_WARNING("Out of memory\n");
322 ret = false;
323 goto out;
327 if (*cldap_reply->client_site) {
328 ads->config.client_site_name =
329 talloc_strdup(ads, cldap_reply->client_site);
330 if (ads->config.client_site_name == NULL) {
331 DBG_WARNING("Out of memory\n");
332 ret = false;
333 goto out;
337 ads->server.workgroup = talloc_strdup(ads, cldap_reply->domain_name);
338 if (ads->server.workgroup == NULL) {
339 DBG_WARNING("Out of memory\n");
340 ret = false;
341 goto out;
344 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
345 ads->ldap.ss = *ss;
347 /* Store our site name. */
348 sitename_store(cldap_reply->domain_name, cldap_reply->client_site);
349 sitename_store(cldap_reply->dns_domain, cldap_reply->client_site);
351 /* Leave this until last so that the flags are not clobbered */
352 ads->config.flags = cldap_reply->server_type;
354 ret = true;
356 out:
358 TALLOC_FREE(frame);
359 return ret;
363 try a connection to a given ldap server, returning True and setting the servers IP
364 in the ads struct if successful
366 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
367 struct sockaddr_storage *ss)
369 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply = {};
370 TALLOC_CTX *frame = talloc_stackframe();
371 bool ok;
372 char addr[INET6_ADDRSTRLEN] = { 0, };
374 if (ss == NULL) {
375 TALLOC_FREE(frame);
376 return false;
379 print_sockaddr(addr, sizeof(addr), ss);
381 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
382 addr, ads->server.realm);
384 ok = ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply);
385 if (!ok) {
386 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
387 addr, ads->server.realm);
388 TALLOC_FREE(frame);
389 return false;
392 ok = ads_fill_cldap_reply(ads, gc, ss, &cldap_reply);
393 if (!ok) {
394 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
395 addr, ads->server.realm);
396 TALLOC_FREE(frame);
397 return false;
400 TALLOC_FREE(frame);
401 return true;
404 /**********************************************************************
405 send a cldap ping to list of servers, one at a time, until one of
406 them answers it's an ldap server. Record success in the ADS_STRUCT.
407 Take note of and update negative connection cache.
408 **********************************************************************/
410 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
411 const char *domain,
412 struct samba_sockaddr *sa_list,
413 size_t count)
415 TALLOC_CTX *frame = talloc_stackframe();
416 struct timeval endtime = timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
417 uint32_t nt_version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
418 struct tsocket_address **ts_list = NULL;
419 const struct tsocket_address * const *ts_list_const = NULL;
420 struct samba_sockaddr **req_sa_list = NULL;
421 struct netlogon_samlogon_response **responses = NULL;
422 size_t num_requests = 0;
423 NTSTATUS status;
424 size_t i;
425 bool ok = false;
426 bool retry;
428 ts_list = talloc_zero_array(frame,
429 struct tsocket_address *,
430 count);
431 if (ts_list == NULL) {
432 TALLOC_FREE(frame);
433 return NT_STATUS_NO_MEMORY;
436 req_sa_list = talloc_zero_array(frame,
437 struct samba_sockaddr *,
438 count);
439 if (req_sa_list == NULL) {
440 TALLOC_FREE(frame);
441 return NT_STATUS_NO_MEMORY;
444 again:
446 * The retry loop is bound by the timeout
448 retry = false;
449 num_requests = 0;
451 for (i = 0; i < count; i++) {
452 char server[INET6_ADDRSTRLEN];
453 int ret;
455 if (is_zero_addr(&sa_list[i].u.ss)) {
456 continue;
459 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
461 status = check_negative_conn_cache(domain, server);
462 if (!NT_STATUS_IS_OK(status)) {
463 continue;
466 ret = tsocket_address_inet_from_strings(ts_list, "ip",
467 server, LDAP_PORT,
468 &ts_list[num_requests]);
469 if (ret != 0) {
470 status = map_nt_error_from_unix(errno);
471 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
472 server, nt_errstr(status));
473 TALLOC_FREE(frame);
474 return status;
477 req_sa_list[num_requests] = &sa_list[i];
478 num_requests += 1;
481 if (num_requests == 0) {
482 status = NT_STATUS_NO_LOGON_SERVERS;
483 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
484 domain, num_requests, count, nt_errstr(status));
485 TALLOC_FREE(frame);
486 return status;
489 ts_list_const = (const struct tsocket_address * const *)ts_list;
491 status = cldap_multi_netlogon(frame,
492 ts_list_const, num_requests,
493 ads->server.realm, NULL,
494 nt_version,
495 1, endtime, &responses);
496 if (!NT_STATUS_IS_OK(status)) {
497 DBG_WARNING("cldap_multi_netlogon(realm=%s, num_requests=%zu) "
498 "for count[%zu] - %s\n",
499 ads->server.realm,
500 num_requests, count,
501 nt_errstr(status));
502 TALLOC_FREE(frame);
503 return NT_STATUS_NO_LOGON_SERVERS;
506 for (i = 0; i < num_requests; i++) {
507 struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply = NULL;
508 char server[INET6_ADDRSTRLEN];
510 if (responses[i] == NULL) {
511 continue;
514 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
516 if (responses[i]->ntver != NETLOGON_NT_VERSION_5EX) {
517 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
518 ads->server.realm,
519 responses[i]->ntver, server);
520 continue;
523 cldap_reply = &responses[i]->data.nt5_ex;
525 /* Returns ok only if it matches the correct server type */
526 ok = ads_fill_cldap_reply(ads,
527 false,
528 &req_sa_list[i]->u.ss,
529 cldap_reply);
530 if (ok) {
531 DBG_DEBUG("realm[%s]: selected %s => %s\n",
532 ads->server.realm,
533 server, cldap_reply->pdc_dns_name);
534 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
535 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
536 cldap_reply);
538 TALLOC_FREE(frame);
539 return NT_STATUS_OK;
542 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
543 ads->server.realm,
544 server, cldap_reply->pdc_dns_name);
545 if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
546 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
547 cldap_reply);
549 add_failed_connection_entry(domain, server,
550 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID);
551 retry = true;
554 if (retry) {
555 bool expired;
557 expired = timeval_expired(&endtime);
558 if (!expired) {
559 goto again;
563 /* keep track of failures as all were not suitable */
564 for (i = 0; i < num_requests; i++) {
565 char server[INET6_ADDRSTRLEN];
567 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
569 add_failed_connection_entry(domain, server,
570 NT_STATUS_UNSUCCESSFUL);
573 status = NT_STATUS_NO_LOGON_SERVERS;
574 DBG_WARNING("realm[%s] no valid response "
575 "num_requests[%zu] for count[%zu] - %s\n",
576 ads->server.realm,
577 num_requests, count, nt_errstr(status));
578 TALLOC_FREE(frame);
579 return NT_STATUS_NO_LOGON_SERVERS;
582 /***************************************************************************
583 resolve a name and perform an "ldap ping" using NetBIOS and related methods
584 ****************************************************************************/
586 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
587 const char *domain, const char *realm)
589 size_t i;
590 size_t count = 0;
591 struct samba_sockaddr *sa_list = NULL;
592 NTSTATUS status;
594 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
595 domain));
597 status = get_sorted_dc_list(talloc_tos(),
598 domain,
599 NULL,
600 &sa_list,
601 &count,
602 false);
603 if (!NT_STATUS_IS_OK(status)) {
604 return status;
607 /* remove servers which are known to be dead based on
608 the corresponding DNS method */
609 if (*realm) {
610 for (i = 0; i < count; ++i) {
611 char server[INET6_ADDRSTRLEN];
613 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
615 if(!NT_STATUS_IS_OK(
616 check_negative_conn_cache(realm, server))) {
617 /* Ensure we add the workgroup name for this
618 IP address as negative too. */
619 add_failed_connection_entry(
620 domain, server,
621 NT_STATUS_UNSUCCESSFUL);
626 status = cldap_ping_list(ads, domain, sa_list, count);
628 TALLOC_FREE(sa_list);
630 return status;
634 /**********************************************************************
635 resolve a name and perform an "ldap ping" using DNS
636 **********************************************************************/
638 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
639 const char *realm)
641 size_t count = 0;
642 struct samba_sockaddr *sa_list = NULL;
643 NTSTATUS status;
645 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
646 realm));
648 status = get_sorted_dc_list(talloc_tos(),
649 realm,
650 sitename,
651 &sa_list,
652 &count,
653 true);
654 if (!NT_STATUS_IS_OK(status)) {
655 TALLOC_FREE(sa_list);
656 return status;
659 status = cldap_ping_list(ads, realm, sa_list, count);
661 TALLOC_FREE(sa_list);
663 return status;
666 /**********************************************************************
667 Try to find an AD dc using our internal name resolution routines
668 Try the realm first and then then workgroup name if netbios is not
669 disabled
670 **********************************************************************/
672 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
674 const char *c_domain = "";
675 const char *c_realm;
676 bool use_own_domain = False;
677 char *sitename = NULL;
678 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
679 bool ok = false;
681 /* if the realm and workgroup are both empty, assume they are ours */
683 /* realm */
684 c_realm = ads->server.realm;
686 if (c_realm == NULL)
687 c_realm = "";
689 if (!*c_realm) {
690 /* special case where no realm and no workgroup means our own */
691 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
692 use_own_domain = True;
693 c_realm = lp_realm();
697 if (!lp_disable_netbios()) {
698 if (use_own_domain) {
699 c_domain = lp_workgroup();
700 } else {
701 c_domain = ads->server.workgroup;
702 if (!*c_realm && (!c_domain || !*c_domain)) {
703 c_domain = lp_workgroup();
707 if (!c_domain) {
708 c_domain = "";
712 if (!*c_realm && !*c_domain) {
713 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
714 "what to do\n"));
715 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
719 * In case of LDAP we use get_dc_name() as that
720 * creates the custom krb5.conf file
722 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
723 fstring srv_name;
724 struct sockaddr_storage ip_out;
726 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
727 " and falling back to domain '%s'\n",
728 c_realm, c_domain));
730 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
731 if (ok) {
732 if (is_zero_addr(&ip_out)) {
733 return NT_STATUS_NO_LOGON_SERVERS;
737 * we call ads_try_connect() to fill in the
738 * ads->config details
740 ok = ads_try_connect(ads, false, &ip_out);
741 if (ok) {
742 return NT_STATUS_OK;
746 return NT_STATUS_NO_LOGON_SERVERS;
749 if (*c_realm) {
750 sitename = sitename_fetch(talloc_tos(), c_realm);
751 status = resolve_and_ping_dns(ads, sitename, c_realm);
753 if (NT_STATUS_IS_OK(status)) {
754 TALLOC_FREE(sitename);
755 return status;
758 /* In case we failed to contact one of our closest DC on our
759 * site we
760 * need to try to find another DC, retry with a site-less SRV
761 * DNS query
762 * - Guenther */
764 if (sitename) {
765 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
766 "our site (%s), Trying to find another DC "
767 "for realm '%s' (domain '%s')\n",
768 sitename, c_realm, c_domain));
769 namecache_delete(c_realm, 0x1C);
770 status =
771 resolve_and_ping_dns(ads, NULL, c_realm);
773 if (NT_STATUS_IS_OK(status)) {
774 TALLOC_FREE(sitename);
775 return status;
779 TALLOC_FREE(sitename);
782 /* try netbios as fallback - if permitted,
783 or if configuration specifically requests it */
784 if (*c_domain) {
785 if (*c_realm) {
786 DEBUG(3, ("ads_find_dc: falling back to netbios "
787 "name resolution for domain '%s' (realm '%s')\n",
788 c_domain, c_realm));
791 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
792 if (NT_STATUS_IS_OK(status)) {
793 return status;
797 DEBUG(1, ("ads_find_dc: "
798 "name resolution for realm '%s' (domain '%s') failed: %s\n",
799 c_realm, c_domain, nt_errstr(status)));
800 return status;
803 * Connect to the LDAP server
804 * @param ads Pointer to an existing ADS_STRUCT
805 * @return status of connection
807 ADS_STATUS ads_connect(ADS_STRUCT *ads)
809 int version = LDAP_VERSION3;
810 ADS_STATUS status;
811 NTSTATUS ntstatus;
812 char addr[INET6_ADDRSTRLEN];
813 struct sockaddr_storage existing_ss;
815 zero_sockaddr(&existing_ss);
818 * ads_connect can be passed in a reused ADS_STRUCT
819 * with an existing non-zero ads->ldap.ss IP address
820 * that was stored by going through ads_find_dc()
821 * if ads->server.ldap_server was NULL.
823 * If ads->server.ldap_server is still NULL but
824 * the target address isn't the zero address, then
825 * store that address off off before zeroing out
826 * ads->ldap so we don't keep doing multiple calls
827 * to ads_find_dc() in the reuse case.
829 * If a caller wants a clean ADS_STRUCT they
830 * will TALLOC_FREE it and allocate a new one
831 * by calling ads_init(), which ensures
832 * ads->ldap.ss is a properly zero'ed out valid IP
833 * address.
835 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
836 /* Save off the address we previously found by ads_find_dc(). */
837 existing_ss = ads->ldap.ss;
840 ads_zero_ldap(ads);
841 ZERO_STRUCT(ads->ldap_wrap_data);
842 ads->ldap.last_attempt = time_mono(NULL);
843 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
845 /* try with a user specified server */
847 if (DEBUGLEVEL >= 11) {
848 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
849 DEBUG(11,("ads_connect: entering\n"));
850 DEBUGADD(11,("%s\n", s));
851 TALLOC_FREE(s);
854 if (ads->server.ldap_server) {
855 bool ok = false;
856 struct sockaddr_storage ss;
858 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
859 if (!ok) {
860 DEBUG(5,("ads_connect: unable to resolve name %s\n",
861 ads->server.ldap_server));
862 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
863 goto out;
866 if (is_zero_addr(&ss)) {
867 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
868 goto out;
871 ok = ads_try_connect(ads, ads->server.gc, &ss);
872 if (ok) {
873 goto got_connection;
876 /* The choice of which GC use is handled one level up in
877 ads_connect_gc(). If we continue on from here with
878 ads_find_dc() we will get GC searches on port 389 which
879 doesn't work. --jerry */
881 if (ads->server.gc == true) {
882 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
885 if (ads->server.no_fallback) {
886 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
887 goto out;
891 if (!is_zero_addr(&existing_ss)) {
892 /* We saved off who we should talk to. */
893 bool ok = ads_try_connect(ads,
894 ads->server.gc,
895 &existing_ss);
896 if (ok) {
897 goto got_connection;
900 * Keep trying to find a server and fall through
901 * into ads_find_dc() again.
905 ntstatus = ads_find_dc(ads);
906 if (NT_STATUS_IS_OK(ntstatus)) {
907 goto got_connection;
910 status = ADS_ERROR_NT(ntstatus);
911 goto out;
913 got_connection:
915 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
916 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
918 if (!ads->auth.user_name) {
919 /* Must use the userPrincipalName value here or sAMAccountName
920 and not servicePrincipalName; found by Guenther Deschner */
921 ads->auth.user_name = talloc_asprintf(ads,
922 "%s$",
923 lp_netbios_name());
924 if (ads->auth.user_name == NULL) {
925 DBG_ERR("talloc_asprintf failed\n");
926 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
927 goto out;
931 if (ads->auth.realm == NULL) {
932 ads->auth.realm = talloc_strdup(ads, ads->config.realm);
933 if (ads->auth.realm == NULL) {
934 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
935 goto out;
939 if (!ads->auth.kdc_server) {
940 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
941 ads->auth.kdc_server = talloc_strdup(ads, addr);
942 if (ads->auth.kdc_server == NULL) {
943 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
944 goto out;
948 /* If the caller() requested no LDAP bind, then we are done */
950 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
951 status = ADS_SUCCESS;
952 goto out;
955 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
956 if (!ads->ldap_wrap_data.mem_ctx) {
957 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
958 goto out;
961 /* Otherwise setup the TCP LDAP session */
963 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
964 &ads->ldap.ss,
965 ads->ldap.port, lp_ldap_timeout());
966 if (ads->ldap.ld == NULL) {
967 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
968 goto out;
970 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
972 /* cache the successful connection for workgroup and realm */
973 if (ads_closest_dc(ads)) {
974 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
975 saf_store( ads->server.realm, ads->config.ldap_server_name);
978 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
980 /* fill in the current time and offsets */
982 status = ads_current_time( ads );
983 if ( !ADS_ERR_OK(status) ) {
984 goto out;
987 /* Now do the bind */
989 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
990 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
991 goto out;
994 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
995 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
996 goto out;
999 status = ads_sasl_bind(ads);
1001 out:
1002 if (DEBUGLEVEL >= 11) {
1003 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1004 DEBUG(11,("ads_connect: leaving with: %s\n",
1005 ads_errstr(status)));
1006 DEBUGADD(11,("%s\n", s));
1007 TALLOC_FREE(s);
1010 return status;
1014 * Connect to the LDAP server using given credentials
1015 * @param ads Pointer to an existing ADS_STRUCT
1016 * @return status of connection
1018 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
1020 ads->auth.flags |= ADS_AUTH_USER_CREDS;
1022 return ads_connect(ads);
1026 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1027 * @param ads Pointer to an existing ADS_STRUCT
1029 * Sets the ads->ldap.ss to a valid
1030 * zero ip address that can be detected by
1031 * our is_zero_addr() function. Otherwise
1032 * it is left as AF_UNSPEC (0).
1034 void ads_zero_ldap(ADS_STRUCT *ads)
1036 ZERO_STRUCT(ads->ldap);
1038 * Initialize the sockaddr_storage so we can use
1039 * sockaddr test functions against it.
1041 zero_sockaddr(&ads->ldap.ss);
1045 * Disconnect the LDAP server
1046 * @param ads Pointer to an existing ADS_STRUCT
1048 void ads_disconnect(ADS_STRUCT *ads)
1050 if (ads->ldap.ld) {
1051 ldap_unbind(ads->ldap.ld);
1052 ads->ldap.ld = NULL;
1054 if (ads->ldap_wrap_data.wrap_ops &&
1055 ads->ldap_wrap_data.wrap_ops->disconnect) {
1056 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1058 if (ads->ldap_wrap_data.mem_ctx) {
1059 talloc_free(ads->ldap_wrap_data.mem_ctx);
1061 ads_zero_ldap(ads);
1062 ZERO_STRUCT(ads->ldap_wrap_data);
1066 Duplicate a struct berval into talloc'ed memory
1068 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1070 struct berval *value;
1072 if (!in_val) return NULL;
1074 value = talloc_zero(ctx, struct berval);
1075 if (value == NULL)
1076 return NULL;
1077 if (in_val->bv_len == 0) return value;
1079 value->bv_len = in_val->bv_len;
1080 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1081 in_val->bv_len);
1082 return value;
1086 Make a values list out of an array of (struct berval *)
1088 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1089 const struct berval **in_vals)
1091 struct berval **values;
1092 int i;
1094 if (!in_vals) return NULL;
1095 for (i=0; in_vals[i]; i++)
1096 ; /* count values */
1097 values = talloc_zero_array(ctx, struct berval *, i+1);
1098 if (!values) return NULL;
1100 for (i=0; in_vals[i]; i++) {
1101 values[i] = dup_berval(ctx, in_vals[i]);
1103 return values;
1107 UTF8-encode a values list out of an array of (char *)
1109 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1111 char **values;
1112 int i;
1113 size_t size;
1115 if (!in_vals) return NULL;
1116 for (i=0; in_vals[i]; i++)
1117 ; /* count values */
1118 values = talloc_zero_array(ctx, char *, i+1);
1119 if (!values) return NULL;
1121 for (i=0; in_vals[i]; i++) {
1122 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1123 TALLOC_FREE(values);
1124 return NULL;
1127 return values;
1131 Pull a (char *) array out of a UTF8-encoded values list
1133 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1135 char **values;
1136 int i;
1137 size_t converted_size;
1139 if (!in_vals) return NULL;
1140 for (i=0; in_vals[i]; i++)
1141 ; /* count values */
1142 values = talloc_zero_array(ctx, char *, i+1);
1143 if (!values) return NULL;
1145 for (i=0; in_vals[i]; i++) {
1146 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1147 &converted_size)) {
1148 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1149 "%s", strerror(errno)));
1152 return values;
1156 * Do a search with paged results. cookie must be null on the first
1157 * call, and then returned on each subsequent call. It will be null
1158 * again when the entire search is complete
1159 * @param ads connection to ads server
1160 * @param bind_path Base dn for the search
1161 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1162 * @param expr Search expression - specified in local charset
1163 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1164 * @param res ** which will contain results - free res* with ads_msgfree()
1165 * @param count Number of entries retrieved on this page
1166 * @param cookie The paged results cookie to be returned on subsequent calls
1167 * @return status of search
1169 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1170 const char *bind_path,
1171 int scope, const char *expr,
1172 const char **attrs, void *args,
1173 LDAPMessage **res,
1174 int *count, struct berval **cookie)
1176 int rc, i, version;
1177 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1178 size_t converted_size;
1179 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1180 BerElement *cookie_be = NULL;
1181 struct berval *cookie_bv= NULL;
1182 BerElement *ext_be = NULL;
1183 struct berval *ext_bv= NULL;
1185 TALLOC_CTX *ctx;
1186 ads_control *external_control = (ads_control *) args;
1188 *res = NULL;
1190 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1191 return ADS_ERROR(LDAP_NO_MEMORY);
1193 /* 0 means the conversion worked but the result was empty
1194 so we only fail if it's -1. In any case, it always
1195 at least nulls out the dest */
1196 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1197 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1199 rc = LDAP_NO_MEMORY;
1200 goto done;
1203 if (!attrs || !(*attrs))
1204 search_attrs = NULL;
1205 else {
1206 /* This would be the utf8-encoded version...*/
1207 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1208 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1209 rc = LDAP_NO_MEMORY;
1210 goto done;
1214 /* Paged results only available on ldap v3 or later */
1215 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1216 if (version < LDAP_VERSION3) {
1217 rc = LDAP_NOT_SUPPORTED;
1218 goto done;
1221 cookie_be = ber_alloc_t(LBER_USE_DER);
1222 if (*cookie) {
1223 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1224 ber_bvfree(*cookie); /* don't need it from last time */
1225 *cookie = NULL;
1226 } else {
1227 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1229 ber_flatten(cookie_be, &cookie_bv);
1230 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1231 PagedResults.ldctl_iscritical = (char) 1;
1232 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1233 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1235 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1236 NoReferrals.ldctl_iscritical = (char) 0;
1237 NoReferrals.ldctl_value.bv_len = 0;
1238 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1240 if (external_control &&
1241 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1242 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1244 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1245 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1247 /* win2k does not accept a ldctl_value being passed in */
1249 if (external_control->val != 0) {
1251 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1252 rc = LDAP_NO_MEMORY;
1253 goto done;
1256 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1257 rc = LDAP_NO_MEMORY;
1258 goto done;
1260 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1261 rc = LDAP_NO_MEMORY;
1262 goto done;
1265 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1266 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1268 } else {
1269 ExternalCtrl.ldctl_value.bv_len = 0;
1270 ExternalCtrl.ldctl_value.bv_val = NULL;
1273 controls[0] = &NoReferrals;
1274 controls[1] = &PagedResults;
1275 controls[2] = &ExternalCtrl;
1276 controls[3] = NULL;
1278 } else {
1279 controls[0] = &NoReferrals;
1280 controls[1] = &PagedResults;
1281 controls[2] = NULL;
1284 /* we need to disable referrals as the openldap libs don't
1285 handle them and paged results at the same time. Using them
1286 together results in the result record containing the server
1287 page control being removed from the result list (tridge/jmcd)
1289 leaving this in despite the control that says don't generate
1290 referrals, in case the server doesn't support it (jmcd)
1292 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1294 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1295 search_attrs, 0, controls,
1296 NULL, LDAP_NO_LIMIT,
1297 (LDAPMessage **)res);
1299 ber_free(cookie_be, 1);
1300 ber_bvfree(cookie_bv);
1302 if (rc) {
1303 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1304 ldap_err2string(rc)));
1305 if (rc == LDAP_OTHER) {
1306 char *ldap_errmsg;
1307 int ret;
1309 ret = ldap_parse_result(ads->ldap.ld,
1310 *res,
1311 NULL,
1312 NULL,
1313 &ldap_errmsg,
1314 NULL,
1315 NULL,
1317 if (ret == LDAP_SUCCESS) {
1318 DEBUG(3, ("ldap_search_with_timeout(%s) "
1319 "error: %s\n", expr, ldap_errmsg));
1320 ldap_memfree(ldap_errmsg);
1323 goto done;
1326 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1327 NULL, &rcontrols, 0);
1329 if (!rcontrols) {
1330 goto done;
1333 for (i=0; rcontrols[i]; i++) {
1334 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1335 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1336 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1337 &cookie_bv);
1338 /* the berval is the cookie, but must be freed when
1339 it is all done */
1340 if (cookie_bv->bv_len) /* still more to do */
1341 *cookie=ber_bvdup(cookie_bv);
1342 else
1343 *cookie=NULL;
1344 ber_bvfree(cookie_bv);
1345 ber_free(cookie_be, 1);
1346 break;
1349 ldap_controls_free(rcontrols);
1351 done:
1352 talloc_destroy(ctx);
1354 if (ext_be) {
1355 ber_free(ext_be, 1);
1358 if (ext_bv) {
1359 ber_bvfree(ext_bv);
1362 if (rc != LDAP_SUCCESS && *res != NULL) {
1363 ads_msgfree(ads, *res);
1364 *res = NULL;
1367 /* if/when we decide to utf8-encode attrs, take out this next line */
1368 TALLOC_FREE(search_attrs);
1370 return ADS_ERROR(rc);
1373 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1374 int scope, const char *expr,
1375 const char **attrs, LDAPMessage **res,
1376 int *count, struct berval **cookie)
1378 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1383 * Get all results for a search. This uses ads_do_paged_search() to return
1384 * all entries in a large search.
1385 * @param ads connection to ads server
1386 * @param bind_path Base dn for the search
1387 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1388 * @param expr Search expression
1389 * @param attrs Attributes to retrieve
1390 * @param res ** which will contain results - free res* with ads_msgfree()
1391 * @return status of search
1393 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1394 int scope, const char *expr,
1395 const char **attrs, void *args,
1396 LDAPMessage **res)
1398 struct berval *cookie = NULL;
1399 int count = 0;
1400 ADS_STATUS status;
1402 *res = NULL;
1403 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1404 &count, &cookie);
1406 if (!ADS_ERR_OK(status))
1407 return status;
1409 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1410 while (cookie) {
1411 LDAPMessage *res2 = NULL;
1412 LDAPMessage *msg, *next;
1414 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1415 attrs, args, &res2, &count, &cookie);
1416 if (!ADS_ERR_OK(status)) {
1417 break;
1420 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1421 that this works on all ldap libs, but I have only tested with openldap */
1422 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1423 next = ads_next_message(ads, msg);
1424 ldap_add_result_entry((LDAPMessage **)res, msg);
1426 /* note that we do not free res2, as the memory is now
1427 part of the main returned list */
1429 #else
1430 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1431 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1432 #endif
1434 return status;
1437 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1438 int scope, const char *expr,
1439 const char **attrs, LDAPMessage **res)
1441 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1444 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1445 int scope, const char *expr,
1446 const char **attrs, uint32_t sd_flags,
1447 LDAPMessage **res)
1449 ads_control args;
1451 args.control = ADS_SD_FLAGS_OID;
1452 args.val = sd_flags;
1453 args.critical = True;
1455 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1460 * Run a function on all results for a search. Uses ads_do_paged_search() and
1461 * runs the function as each page is returned, using ads_process_results()
1462 * @param ads connection to ads server
1463 * @param bind_path Base dn for the search
1464 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1465 * @param expr Search expression - specified in local charset
1466 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1467 * @param fn Function which takes attr name, values list, and data_area
1468 * @param data_area Pointer which is passed to function on each call
1469 * @return status of search
1471 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1472 int scope, const char *expr, const char **attrs,
1473 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1474 void *data_area)
1476 struct berval *cookie = NULL;
1477 int count = 0;
1478 ADS_STATUS status;
1479 LDAPMessage *res;
1481 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1482 &count, &cookie);
1484 if (!ADS_ERR_OK(status)) return status;
1486 ads_process_results(ads, res, fn, data_area);
1487 ads_msgfree(ads, res);
1489 while (cookie) {
1490 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1491 &res, &count, &cookie);
1493 if (!ADS_ERR_OK(status)) break;
1495 ads_process_results(ads, res, fn, data_area);
1496 ads_msgfree(ads, res);
1499 return status;
1503 * Do a search with a timeout.
1504 * @param ads connection to ads server
1505 * @param bind_path Base dn for the search
1506 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1507 * @param expr Search expression
1508 * @param attrs Attributes to retrieve
1509 * @param res ** which will contain results - free res* with ads_msgfree()
1510 * @return status of search
1512 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1513 const char *expr,
1514 const char **attrs, LDAPMessage **res)
1516 int rc;
1517 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1518 size_t converted_size;
1519 TALLOC_CTX *ctx;
1521 *res = NULL;
1522 if (!(ctx = talloc_init("ads_do_search"))) {
1523 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1524 return ADS_ERROR(LDAP_NO_MEMORY);
1527 /* 0 means the conversion worked but the result was empty
1528 so we only fail if it's negative. In any case, it always
1529 at least nulls out the dest */
1530 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1531 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1533 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1534 rc = LDAP_NO_MEMORY;
1535 goto done;
1538 if (!attrs || !(*attrs))
1539 search_attrs = NULL;
1540 else {
1541 /* This would be the utf8-encoded version...*/
1542 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1543 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1545 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1546 rc = LDAP_NO_MEMORY;
1547 goto done;
1551 /* see the note in ads_do_paged_search - we *must* disable referrals */
1552 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1554 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1555 search_attrs, 0, NULL, NULL,
1556 LDAP_NO_LIMIT,
1557 (LDAPMessage **)res);
1559 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1560 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1561 rc = 0;
1564 done:
1565 talloc_destroy(ctx);
1566 /* if/when we decide to utf8-encode attrs, take out this next line */
1567 TALLOC_FREE(search_attrs);
1568 return ADS_ERROR(rc);
1571 * Do a general ADS search
1572 * @param ads connection to ads server
1573 * @param res ** which will contain results - free res* with ads_msgfree()
1574 * @param expr Search expression
1575 * @param attrs Attributes to retrieve
1576 * @return status of search
1578 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1579 const char *expr, const char **attrs)
1581 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1582 expr, attrs, res);
1586 * Do a search on a specific DistinguishedName
1587 * @param ads connection to ads server
1588 * @param res ** which will contain results - free res* with ads_msgfree()
1589 * @param dn DistinguishName to search
1590 * @param attrs Attributes to retrieve
1591 * @return status of search
1593 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1594 const char *dn, const char **attrs)
1596 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1597 attrs, res);
1601 * Free up memory from a ads_search
1602 * @param ads connection to ads server
1603 * @param msg Search results to free
1605 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1607 if (!msg) return;
1608 ldap_msgfree(msg);
1612 * Get a dn from search results
1613 * @param ads connection to ads server
1614 * @param msg Search result
1615 * @return dn string
1617 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1619 char *utf8_dn, *unix_dn;
1620 size_t converted_size;
1622 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1624 if (!utf8_dn) {
1625 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1626 return NULL;
1629 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1630 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1631 utf8_dn ));
1632 return NULL;
1634 ldap_memfree(utf8_dn);
1635 return unix_dn;
1639 * Get the parent from a dn
1640 * @param dn the dn to return the parent from
1641 * @return parent dn string
1643 char *ads_parent_dn(const char *dn)
1645 char *p;
1647 if (dn == NULL) {
1648 return NULL;
1651 p = strchr(dn, ',');
1653 if (p == NULL) {
1654 return NULL;
1657 return p+1;
1661 * Find a machine account given a hostname
1662 * @param ads connection to ads server
1663 * @param res ** which will contain results - free res* with ads_msgfree()
1664 * @param host Hostname to search for
1665 * @return status of search
1667 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1668 const char *machine)
1670 ADS_STATUS status;
1671 char *expr;
1672 const char *attrs[] = {
1673 /* This is how Windows checks for machine accounts */
1674 "objectClass",
1675 "SamAccountName",
1676 "userAccountControl",
1677 "DnsHostName",
1678 "ServicePrincipalName",
1679 "userPrincipalName",
1680 "unicodePwd",
1682 /* Additional attributes Samba checks */
1683 "msDS-AdditionalDnsHostName",
1684 "msDS-SupportedEncryptionTypes",
1685 "nTSecurityDescriptor",
1686 "objectSid",
1688 NULL
1690 TALLOC_CTX *frame = talloc_stackframe();
1692 *res = NULL;
1694 /* the easiest way to find a machine account anywhere in the tree
1695 is to look for hostname$ */
1696 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1697 if (expr == NULL) {
1698 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1699 goto done;
1702 status = ads_search(ads, res, expr, attrs);
1703 if (ADS_ERR_OK(status)) {
1704 if (ads_count_replies(ads, *res) != 1) {
1705 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1709 done:
1710 TALLOC_FREE(frame);
1711 return status;
1715 * Initialize a list of mods to be used in a modify request
1716 * @param ctx An initialized TALLOC_CTX
1717 * @return allocated ADS_MODLIST
1719 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1721 #define ADS_MODLIST_ALLOC_SIZE 10
1722 LDAPMod **mods;
1724 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1725 /* -1 is safety to make sure we don't go over the end.
1726 need to reset it to NULL before doing ldap modify */
1727 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1729 return (ADS_MODLIST)mods;
1734 add an attribute to the list, with values list already constructed
1736 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1737 int mod_op, const char *name,
1738 const void *_invals)
1740 int curmod;
1741 LDAPMod **modlist = (LDAPMod **) *mods;
1742 struct berval **ber_values = NULL;
1743 char **char_values = NULL;
1745 if (!_invals) {
1746 mod_op = LDAP_MOD_DELETE;
1747 } else {
1748 if (mod_op & LDAP_MOD_BVALUES) {
1749 const struct berval **b;
1750 b = discard_const_p(const struct berval *, _invals);
1751 ber_values = ads_dup_values(ctx, b);
1752 } else {
1753 const char **c;
1754 c = discard_const_p(const char *, _invals);
1755 char_values = ads_push_strvals(ctx, c);
1759 /* find the first empty slot */
1760 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1761 curmod++);
1762 if (modlist[curmod] == (LDAPMod *) -1) {
1763 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1764 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1765 return ADS_ERROR(LDAP_NO_MEMORY);
1766 memset(&modlist[curmod], 0,
1767 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1768 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1769 *mods = (ADS_MODLIST)modlist;
1772 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1773 return ADS_ERROR(LDAP_NO_MEMORY);
1774 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1775 if (mod_op & LDAP_MOD_BVALUES) {
1776 modlist[curmod]->mod_bvalues = ber_values;
1777 } else if (mod_op & LDAP_MOD_DELETE) {
1778 modlist[curmod]->mod_values = NULL;
1779 } else {
1780 modlist[curmod]->mod_values = char_values;
1783 modlist[curmod]->mod_op = mod_op;
1784 return ADS_ERROR(LDAP_SUCCESS);
1788 * Add a single string value to a mod list
1789 * @param ctx An initialized TALLOC_CTX
1790 * @param mods An initialized ADS_MODLIST
1791 * @param name The attribute name to add
1792 * @param val The value to add - NULL means DELETE
1793 * @return ADS STATUS indicating success of add
1795 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1796 const char *name, const char *val)
1798 const char *values[2];
1800 values[0] = val;
1801 values[1] = NULL;
1803 if (!val)
1804 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1805 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1809 * Add an array of string values to a mod list
1810 * @param ctx An initialized TALLOC_CTX
1811 * @param mods An initialized ADS_MODLIST
1812 * @param name The attribute name to add
1813 * @param vals The array of string values to add - NULL means DELETE
1814 * @return ADS STATUS indicating success of add
1816 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1817 const char *name, const char **vals)
1819 if (!vals)
1820 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1821 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1822 name, (const void **) vals);
1826 * Add a single ber-encoded value to a mod list
1827 * @param ctx An initialized TALLOC_CTX
1828 * @param mods An initialized ADS_MODLIST
1829 * @param name The attribute name to add
1830 * @param val The value to add - NULL means DELETE
1831 * @return ADS STATUS indicating success of add
1833 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1834 const char *name, const struct berval *val)
1836 const struct berval *values[2];
1838 values[0] = val;
1839 values[1] = NULL;
1840 if (!val)
1841 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1842 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1843 name, (const void **) values);
1846 static void ads_print_error(int ret, LDAP *ld)
1848 if (ret != 0) {
1849 char *ld_error = NULL;
1850 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1851 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1852 ret,
1853 ldap_err2string(ret),
1854 ld_error);
1855 SAFE_FREE(ld_error);
1860 * Perform an ldap modify
1861 * @param ads connection to ads server
1862 * @param mod_dn DistinguishedName to modify
1863 * @param mods list of modifications to perform
1864 * @return status of modify
1866 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1868 int ret,i;
1869 char *utf8_dn = NULL;
1870 size_t converted_size;
1872 this control is needed to modify that contains a currently
1873 non-existent attribute (but allowable for the object) to run
1875 LDAPControl PermitModify = {
1876 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1877 {0, NULL},
1878 (char) 1};
1879 LDAPControl *controls[2];
1881 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1883 controls[0] = &PermitModify;
1884 controls[1] = NULL;
1886 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1887 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1890 /* find the end of the list, marked by NULL or -1 */
1891 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1892 /* make sure the end of the list is NULL */
1893 mods[i] = NULL;
1894 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1895 (LDAPMod **) mods, controls, NULL);
1896 ads_print_error(ret, ads->ldap.ld);
1897 TALLOC_FREE(utf8_dn);
1898 return ADS_ERROR(ret);
1902 * Perform an ldap add
1903 * @param ads connection to ads server
1904 * @param new_dn DistinguishedName to add
1905 * @param mods list of attributes and values for DN
1906 * @return status of add
1908 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1910 int ret, i;
1911 char *utf8_dn = NULL;
1912 size_t converted_size;
1914 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1916 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1917 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1918 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1921 /* find the end of the list, marked by NULL or -1 */
1922 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1923 /* make sure the end of the list is NULL */
1924 mods[i] = NULL;
1926 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1927 ads_print_error(ret, ads->ldap.ld);
1928 TALLOC_FREE(utf8_dn);
1929 return ADS_ERROR(ret);
1933 * Delete a DistinguishedName
1934 * @param ads connection to ads server
1935 * @param new_dn DistinguishedName to delete
1936 * @return status of delete
1938 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1940 int ret;
1941 char *utf8_dn = NULL;
1942 size_t converted_size;
1943 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1944 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1945 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1948 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1950 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1951 ads_print_error(ret, ads->ldap.ld);
1952 TALLOC_FREE(utf8_dn);
1953 return ADS_ERROR(ret);
1957 * Build an org unit string
1958 * if org unit is Computers or blank then assume a container, otherwise
1959 * assume a / separated list of organisational units.
1960 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1961 * @param ads connection to ads server
1962 * @param org_unit Organizational unit
1963 * @return org unit string - caller must free
1965 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1967 ADS_STATUS status;
1968 char *ret = NULL;
1969 char *dn = NULL;
1971 if (!org_unit || !*org_unit) {
1973 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1975 /* samba4 might not yet respond to a wellknownobject-query */
1976 return ret ? ret : SMB_STRDUP("cn=Computers");
1979 if (strequal(org_unit, "Computers")) {
1980 return SMB_STRDUP("cn=Computers");
1983 /* jmcd: removed "\\" from the separation chars, because it is
1984 needed as an escape for chars like '#' which are valid in an
1985 OU name */
1986 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
1987 if (!ADS_ERR_OK(status)) {
1988 return NULL;
1991 return dn;
1995 * Get a org unit string for a well-known GUID
1996 * @param ads connection to ads server
1997 * @param wknguid Well known GUID
1998 * @return org unit string - caller must free
2000 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2002 ADS_STATUS status;
2003 LDAPMessage *res = NULL;
2004 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2005 **bind_dn_exp = NULL;
2006 const char *attrs[] = {"distinguishedName", NULL};
2007 int new_ln, wkn_ln, bind_ln, i;
2009 if (wknguid == NULL) {
2010 return NULL;
2013 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2014 DEBUG(1, ("asprintf failed!\n"));
2015 return NULL;
2018 status = ads_search_dn(ads, &res, base, attrs);
2019 if (!ADS_ERR_OK(status)) {
2020 DEBUG(1,("Failed while searching for: %s\n", base));
2021 goto out;
2024 if (ads_count_replies(ads, res) != 1) {
2025 goto out;
2028 /* substitute the bind-path from the well-known-guid-search result */
2029 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2030 if (!wkn_dn) {
2031 goto out;
2034 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2035 if (!wkn_dn_exp) {
2036 goto out;
2039 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2040 if (!bind_dn_exp) {
2041 goto out;
2044 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2046 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2049 new_ln = wkn_ln - bind_ln;
2051 ret = SMB_STRDUP(wkn_dn_exp[0]);
2052 if (!ret) {
2053 goto out;
2056 for (i=1; i < new_ln; i++) {
2057 char *s = NULL;
2059 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2060 SAFE_FREE(ret);
2061 goto out;
2064 SAFE_FREE(ret);
2065 ret = SMB_STRDUP(s);
2066 free(s);
2067 if (!ret) {
2068 goto out;
2072 out:
2073 SAFE_FREE(base);
2074 ads_msgfree(ads, res);
2075 TALLOC_FREE(wkn_dn);
2076 if (wkn_dn_exp) {
2077 ldap_value_free(wkn_dn_exp);
2079 if (bind_dn_exp) {
2080 ldap_value_free(bind_dn_exp);
2083 return ret;
2087 * Adds (appends) an item to an attribute array, rather then
2088 * replacing the whole list
2089 * @param ctx An initialized TALLOC_CTX
2090 * @param mods An initialized ADS_MODLIST
2091 * @param name name of the ldap attribute to append to
2092 * @param vals an array of values to add
2093 * @return status of addition
2096 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2097 const char *name, const char **vals)
2099 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2100 (const void *) vals);
2104 * Determines the an account's current KVNO via an LDAP lookup
2105 * @param ads An initialized ADS_STRUCT
2106 * @param account_name the NT samaccountname.
2107 * @return the kvno for the account, or -1 in case of a failure.
2110 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
2112 LDAPMessage *res = NULL;
2113 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
2114 char *filter;
2115 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
2116 char *dn_string = NULL;
2117 ADS_STATUS ret;
2119 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
2120 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
2121 return kvno;
2123 ret = ads_search(ads, &res, filter, attrs);
2124 SAFE_FREE(filter);
2125 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
2126 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
2127 ads_msgfree(ads, res);
2128 return kvno;
2131 dn_string = ads_get_dn(ads, talloc_tos(), res);
2132 if (!dn_string) {
2133 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2134 ads_msgfree(ads, res);
2135 return kvno;
2137 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
2138 TALLOC_FREE(dn_string);
2140 /* ---------------------------------------------------------
2141 * 0 is returned as a default KVNO from this point on...
2142 * This is done because Windows 2000 does not support key
2143 * version numbers. Chances are that a failure in the next
2144 * step is simply due to Windows 2000 being used for a
2145 * domain controller. */
2146 kvno = 0;
2148 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2149 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2150 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2151 ads_msgfree(ads, res);
2152 return kvno;
2155 /* Success */
2156 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2157 ads_msgfree(ads, res);
2158 return kvno;
2162 * Determines the computer account's current KVNO via an LDAP lookup
2163 * @param ads An initialized ADS_STRUCT
2164 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2165 * @return the kvno for the computer account, or -1 in case of a failure.
2168 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2170 char *computer_account = NULL;
2171 uint32_t kvno = -1;
2173 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2174 return kvno;
2177 kvno = ads_get_kvno(ads, computer_account);
2178 free(computer_account);
2180 return kvno;
2184 * This clears out all registered spn's for a given hostname
2185 * @param ads An initilaized ADS_STRUCT
2186 * @param machine_name the NetBIOS name of the computer.
2187 * @return 0 upon success, non-zero otherwise.
2190 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2192 TALLOC_CTX *ctx;
2193 LDAPMessage *res = NULL;
2194 ADS_MODLIST mods;
2195 const char *servicePrincipalName[1] = {NULL};
2196 ADS_STATUS ret;
2197 char *dn_string = NULL;
2199 ret = ads_find_machine_acct(ads, &res, machine_name);
2200 if (!ADS_ERR_OK(ret)) {
2201 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2202 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2203 ads_msgfree(ads, res);
2204 return ret;
2207 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2208 ctx = talloc_init("ads_clear_service_principal_names");
2209 if (!ctx) {
2210 ads_msgfree(ads, res);
2211 return ADS_ERROR(LDAP_NO_MEMORY);
2214 if (!(mods = ads_init_mods(ctx))) {
2215 talloc_destroy(ctx);
2216 ads_msgfree(ads, res);
2217 return ADS_ERROR(LDAP_NO_MEMORY);
2219 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2220 if (!ADS_ERR_OK(ret)) {
2221 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2222 ads_msgfree(ads, res);
2223 talloc_destroy(ctx);
2224 return ret;
2226 dn_string = ads_get_dn(ads, talloc_tos(), res);
2227 if (!dn_string) {
2228 talloc_destroy(ctx);
2229 ads_msgfree(ads, res);
2230 return ADS_ERROR(LDAP_NO_MEMORY);
2232 ret = ads_gen_mod(ads, dn_string, mods);
2233 TALLOC_FREE(dn_string);
2234 if (!ADS_ERR_OK(ret)) {
2235 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2236 machine_name));
2237 ads_msgfree(ads, res);
2238 talloc_destroy(ctx);
2239 return ret;
2242 ads_msgfree(ads, res);
2243 talloc_destroy(ctx);
2244 return ret;
2248 * @brief Search for an element in a string array.
2250 * @param[in] el_array The string array to search.
2252 * @param[in] num_el The number of elements in the string array.
2254 * @param[in] el The string to search.
2256 * @return True if found, false if not.
2258 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2260 size_t i;
2262 if (el_array == NULL || num_el == 0 || el == NULL) {
2263 return false;
2266 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2267 int cmp;
2269 cmp = strcasecmp_m(el_array[i], el);
2270 if (cmp == 0) {
2271 return true;
2275 return false;
2279 * @brief This gets the service principal names of an existing computer account.
2281 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2283 * @param[in] ads The ADS context to use.
2285 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2286 * identify the computer account.
2288 * @param[in] spn_array A pointer to store the array for SPNs.
2290 * @param[in] num_spns The number of principals stored in the array.
2292 * @return 0 on success, or a ADS error if a failure occurred.
2294 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2295 ADS_STRUCT *ads,
2296 const char *machine_name,
2297 char ***spn_array,
2298 size_t *num_spns)
2300 ADS_STATUS status;
2301 LDAPMessage *res = NULL;
2302 int count;
2304 status = ads_find_machine_acct(ads,
2305 &res,
2306 machine_name);
2307 if (!ADS_ERR_OK(status)) {
2308 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2309 machine_name));
2310 return status;
2313 count = ads_count_replies(ads, res);
2314 if (count != 1) {
2315 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2316 goto done;
2319 *spn_array = ads_pull_strings(ads,
2320 mem_ctx,
2321 res,
2322 "servicePrincipalName",
2323 num_spns);
2324 if (*spn_array == NULL) {
2325 DEBUG(1, ("Host account for %s does not have service principal "
2326 "names.\n",
2327 machine_name));
2328 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2329 goto done;
2332 done:
2333 ads_msgfree(ads, res);
2335 return status;
2339 * This adds a service principal name to an existing computer account
2340 * (found by hostname) in AD.
2341 * @param ads An initialized ADS_STRUCT
2342 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2343 * @param spns An array or strings for the service principals to add,
2344 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2345 * @return 0 upon success, or non-zero if a failure occurs
2348 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2349 const char *machine_name,
2350 const char **spns)
2352 ADS_STATUS ret;
2353 TALLOC_CTX *ctx;
2354 LDAPMessage *res = NULL;
2355 ADS_MODLIST mods;
2356 char *dn_string = NULL;
2357 const char **servicePrincipalName = spns;
2359 ret = ads_find_machine_acct(ads, &res, machine_name);
2360 if (!ADS_ERR_OK(ret)) {
2361 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2362 machine_name));
2363 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2364 ads_msgfree(ads, res);
2365 return ret;
2368 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2369 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2370 ads_msgfree(ads, res);
2371 return ADS_ERROR(LDAP_NO_MEMORY);
2374 DEBUG(5,("ads_add_service_principal_name: INFO: "
2375 "Adding %s to host %s\n",
2376 spns[0] ? "N/A" : spns[0], machine_name));
2379 DEBUG(5,("ads_add_service_principal_name: INFO: "
2380 "Adding %s to host %s\n",
2381 spns[1] ? "N/A" : spns[1], machine_name));
2383 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2384 ret = ADS_ERROR(LDAP_NO_MEMORY);
2385 goto out;
2388 ret = ads_add_strlist(ctx,
2389 &mods,
2390 "servicePrincipalName",
2391 servicePrincipalName);
2392 if (!ADS_ERR_OK(ret)) {
2393 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2394 goto out;
2397 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2398 ret = ADS_ERROR(LDAP_NO_MEMORY);
2399 goto out;
2402 ret = ads_gen_mod(ads, dn_string, mods);
2403 if (!ADS_ERR_OK(ret)) {
2404 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2405 goto out;
2408 out:
2409 TALLOC_FREE( ctx );
2410 ads_msgfree(ads, res);
2411 return ret;
2414 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2415 LDAPMessage *msg)
2417 uint32_t acct_ctrl = 0;
2418 bool ok;
2420 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2421 if (!ok) {
2422 return 0;
2425 return acct_ctrl;
2428 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2429 LDAPMessage *msg,
2430 const struct berval *machine_pw_val)
2432 ADS_MODLIST mods;
2433 ADS_STATUS ret;
2434 TALLOC_CTX *frame = talloc_stackframe();
2435 uint32_t acct_control;
2436 char *control_str = NULL;
2437 const char *attrs[] = {
2438 "objectSid",
2439 NULL
2441 LDAPMessage *res = NULL;
2442 char *dn = NULL;
2444 dn = ads_get_dn(ads, frame, msg);
2445 if (dn == NULL) {
2446 ret = ADS_ERROR(LDAP_NO_MEMORY);
2447 goto done;
2450 acct_control = ads_get_acct_ctrl(ads, msg);
2451 if (acct_control == 0) {
2452 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2453 goto done;
2457 * Changing the password, disables the account. So we need to change the
2458 * userAccountControl flags to enable it again.
2460 mods = ads_init_mods(frame);
2461 if (mods == NULL) {
2462 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2463 goto done;
2466 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2468 ret = ads_gen_mod(ads, dn, mods);
2469 if (!ADS_ERR_OK(ret)) {
2470 goto done;
2472 TALLOC_FREE(mods);
2475 * To activate the account, we need to disable and enable it.
2477 acct_control |= UF_ACCOUNTDISABLE;
2479 control_str = talloc_asprintf(frame, "%u", acct_control);
2480 if (control_str == NULL) {
2481 ret = ADS_ERROR(LDAP_NO_MEMORY);
2482 goto done;
2485 mods = ads_init_mods(frame);
2486 if (mods == NULL) {
2487 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2488 goto done;
2491 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2493 ret = ads_gen_mod(ads, dn, mods);
2494 if (!ADS_ERR_OK(ret)) {
2495 goto done;
2497 TALLOC_FREE(mods);
2498 TALLOC_FREE(control_str);
2501 * Enable the account again.
2503 acct_control &= ~UF_ACCOUNTDISABLE;
2505 control_str = talloc_asprintf(frame, "%u", acct_control);
2506 if (control_str == NULL) {
2507 ret = ADS_ERROR(LDAP_NO_MEMORY);
2508 goto done;
2511 mods = ads_init_mods(frame);
2512 if (mods == NULL) {
2513 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2514 goto done;
2517 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2519 ret = ads_gen_mod(ads, dn, mods);
2520 if (!ADS_ERR_OK(ret)) {
2521 goto done;
2523 TALLOC_FREE(mods);
2524 TALLOC_FREE(control_str);
2526 ret = ads_search_dn(ads, &res, dn, attrs);
2527 ads_msgfree(ads, res);
2529 done:
2530 talloc_free(frame);
2532 return ret;
2536 * adds a machine account to the ADS server
2537 * @param ads An initialized ADS_STRUCT
2538 * @param machine_name - the NetBIOS machine name of this account.
2539 * @param account_type A number indicating the type of account to create
2540 * @param org_unit The LDAP path in which to place this account
2541 * @return 0 upon success, or non-zero otherwise
2544 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2545 const char *machine_name,
2546 const char *machine_password,
2547 const char *org_unit,
2548 uint32_t etype_list,
2549 const char *dns_domain_name)
2551 ADS_STATUS ret;
2552 char *samAccountName = NULL;
2553 char *controlstr = NULL;
2554 TALLOC_CTX *ctx = NULL;
2555 ADS_MODLIST mods;
2556 char *machine_escaped = NULL;
2557 char *dns_hostname = NULL;
2558 char *new_dn = NULL;
2559 char *utf8_pw = NULL;
2560 size_t utf8_pw_len = 0;
2561 char *utf16_pw = NULL;
2562 size_t utf16_pw_len = 0;
2563 struct berval machine_pw_val;
2564 bool ok;
2565 const char **spn_array = NULL;
2566 size_t num_spns = 0;
2567 const char *spn_prefix[] = {
2568 "HOST",
2569 "RestrictedKrbHost",
2571 size_t i;
2572 LDAPMessage *res = NULL;
2573 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2575 ctx = talloc_init("ads_add_machine_acct");
2576 if (ctx == NULL) {
2577 return ADS_ERROR(LDAP_NO_MEMORY);
2580 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2581 if (machine_escaped == NULL) {
2582 ret = ADS_ERROR(LDAP_NO_MEMORY);
2583 goto done;
2586 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2587 if (utf8_pw == NULL) {
2588 ret = ADS_ERROR(LDAP_NO_MEMORY);
2589 goto done;
2591 utf8_pw_len = strlen(utf8_pw);
2593 ok = convert_string_talloc(ctx,
2594 CH_UTF8, CH_UTF16MUNGED,
2595 utf8_pw, utf8_pw_len,
2596 (void *)&utf16_pw, &utf16_pw_len);
2597 if (!ok) {
2598 ret = ADS_ERROR(LDAP_NO_MEMORY);
2599 goto done;
2602 machine_pw_val = (struct berval) {
2603 .bv_val = utf16_pw,
2604 .bv_len = utf16_pw_len,
2607 /* Check if the machine account already exists. */
2608 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2609 if (ADS_ERR_OK(ret)) {
2610 /* Change the machine account password */
2611 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2612 ads_msgfree(ads, res);
2614 goto done;
2616 ads_msgfree(ads, res);
2618 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2619 if (new_dn == NULL) {
2620 ret = ADS_ERROR(LDAP_NO_MEMORY);
2621 goto done;
2624 /* Create machine account */
2626 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2627 if (samAccountName == NULL) {
2628 ret = ADS_ERROR(LDAP_NO_MEMORY);
2629 goto done;
2632 dns_hostname = talloc_asprintf(ctx,
2633 "%s.%s",
2634 machine_name,
2635 dns_domain_name);
2636 if (dns_hostname == NULL) {
2637 ret = ADS_ERROR(LDAP_NO_MEMORY);
2638 goto done;
2641 /* Add dns_hostname SPNs */
2642 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2643 char *spn = talloc_asprintf(ctx,
2644 "%s/%s",
2645 spn_prefix[i],
2646 dns_hostname);
2647 if (spn == NULL) {
2648 ret = ADS_ERROR(LDAP_NO_MEMORY);
2649 goto done;
2652 ok = add_string_to_array(spn_array,
2653 spn,
2654 &spn_array,
2655 &num_spns);
2656 if (!ok) {
2657 ret = ADS_ERROR(LDAP_NO_MEMORY);
2658 goto done;
2662 /* Add machine_name SPNs */
2663 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2664 char *spn = talloc_asprintf(ctx,
2665 "%s/%s",
2666 spn_prefix[i],
2667 machine_name);
2668 if (spn == NULL) {
2669 ret = ADS_ERROR(LDAP_NO_MEMORY);
2670 goto done;
2673 ok = add_string_to_array(spn_array,
2674 spn,
2675 &spn_array,
2676 &num_spns);
2677 if (!ok) {
2678 ret = ADS_ERROR(LDAP_NO_MEMORY);
2679 goto done;
2683 /* Make sure to NULL terminate the array */
2684 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2685 if (spn_array == NULL) {
2686 ret = ADS_ERROR(LDAP_NO_MEMORY);
2687 goto done;
2689 spn_array[num_spns] = NULL;
2691 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2692 if (controlstr == NULL) {
2693 ret = ADS_ERROR(LDAP_NO_MEMORY);
2694 goto done;
2697 mods = ads_init_mods(ctx);
2698 if (mods == NULL) {
2699 ret = ADS_ERROR(LDAP_NO_MEMORY);
2700 goto done;
2703 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2704 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2705 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2706 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2707 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2708 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2710 ret = ads_gen_add(ads, new_dn, mods);
2712 done:
2713 SAFE_FREE(machine_escaped);
2714 talloc_destroy(ctx);
2716 return ret;
2720 * move a machine account to another OU on the ADS server
2721 * @param ads - An initialized ADS_STRUCT
2722 * @param machine_name - the NetBIOS machine name of this account.
2723 * @param org_unit - The LDAP path in which to place this account
2724 * @param moved - whether we moved the machine account (optional)
2725 * @return 0 upon success, or non-zero otherwise
2728 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2729 const char *org_unit, bool *moved)
2731 ADS_STATUS rc;
2732 int ldap_status;
2733 LDAPMessage *res = NULL;
2734 char *filter = NULL;
2735 char *computer_dn = NULL;
2736 char *parent_dn;
2737 char *computer_rdn = NULL;
2738 bool need_move = False;
2740 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2741 rc = ADS_ERROR(LDAP_NO_MEMORY);
2742 goto done;
2745 /* Find pre-existing machine */
2746 rc = ads_search(ads, &res, filter, NULL);
2747 if (!ADS_ERR_OK(rc)) {
2748 goto done;
2751 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2752 if (!computer_dn) {
2753 rc = ADS_ERROR(LDAP_NO_MEMORY);
2754 goto done;
2757 parent_dn = ads_parent_dn(computer_dn);
2758 if (strequal(parent_dn, org_unit)) {
2759 goto done;
2762 need_move = True;
2764 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2765 rc = ADS_ERROR(LDAP_NO_MEMORY);
2766 goto done;
2769 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2770 org_unit, 1, NULL, NULL);
2771 rc = ADS_ERROR(ldap_status);
2773 done:
2774 ads_msgfree(ads, res);
2775 SAFE_FREE(filter);
2776 TALLOC_FREE(computer_dn);
2777 SAFE_FREE(computer_rdn);
2779 if (!ADS_ERR_OK(rc)) {
2780 need_move = False;
2783 if (moved) {
2784 *moved = need_move;
2787 return rc;
2791 dump a binary result from ldap
2793 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2795 size_t i;
2796 for (i=0; values[i]; i++) {
2797 ber_len_t j;
2798 printf("%s: ", field);
2799 for (j=0; j<values[i]->bv_len; j++) {
2800 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2802 printf("\n");
2806 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2808 int i;
2809 for (i=0; values[i]; i++) {
2810 NTSTATUS status;
2811 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2812 struct GUID guid;
2814 status = GUID_from_ndr_blob(&in, &guid);
2815 if (NT_STATUS_IS_OK(status)) {
2816 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2817 } else {
2818 printf("%s: INVALID GUID\n", field);
2824 dump a sid result from ldap
2826 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2828 int i;
2829 for (i=0; values[i]; i++) {
2830 ssize_t ret;
2831 struct dom_sid sid;
2832 struct dom_sid_buf tmp;
2833 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2834 values[i]->bv_len, &sid);
2835 if (ret == -1) {
2836 return;
2838 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2843 dump ntSecurityDescriptor
2845 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2847 TALLOC_CTX *frame = talloc_stackframe();
2848 struct security_descriptor *psd;
2849 NTSTATUS status;
2851 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2852 values[0]->bv_len, &psd);
2853 if (!NT_STATUS_IS_OK(status)) {
2854 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2855 nt_errstr(status)));
2856 TALLOC_FREE(frame);
2857 return;
2860 if (psd) {
2861 ads_disp_sd(ads, talloc_tos(), psd);
2864 TALLOC_FREE(frame);
2868 dump a string result from ldap
2870 static void dump_string(const char *field, char **values)
2872 int i;
2873 for (i=0; values[i]; i++) {
2874 printf("%s: %s\n", field, values[i]);
2879 dump a field from LDAP on stdout
2880 used for debugging
2883 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2885 const struct {
2886 const char *name;
2887 bool string;
2888 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2889 } handlers[] = {
2890 {"objectGUID", False, dump_guid},
2891 {"netbootGUID", False, dump_guid},
2892 {"nTSecurityDescriptor", False, dump_sd},
2893 {"dnsRecord", False, dump_binary},
2894 {"objectSid", False, dump_sid},
2895 {"tokenGroups", False, dump_sid},
2896 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2897 {"tokengroupsGlobalandUniversal", False, dump_sid},
2898 {"mS-DS-CreatorSID", False, dump_sid},
2899 {"msExchMailboxGuid", False, dump_guid},
2900 {NULL, True, NULL}
2902 int i;
2904 if (!field) { /* must be end of an entry */
2905 printf("\n");
2906 return False;
2909 for (i=0; handlers[i].name; i++) {
2910 if (strcasecmp_m(handlers[i].name, field) == 0) {
2911 if (!values) /* first time, indicate string or not */
2912 return handlers[i].string;
2913 handlers[i].handler(ads, field, (struct berval **) values);
2914 break;
2917 if (!handlers[i].name) {
2918 if (!values) /* first time, indicate string conversion */
2919 return True;
2920 dump_string(field, (char **)values);
2922 return False;
2926 * Dump a result from LDAP on stdout
2927 * used for debugging
2928 * @param ads connection to ads server
2929 * @param res Results to dump
2932 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2934 ads_process_results(ads, res, ads_dump_field, NULL);
2938 * Walk through results, calling a function for each entry found.
2939 * The function receives a field name, a berval * array of values,
2940 * and a data area passed through from the start. The function is
2941 * called once with null for field and values at the end of each
2942 * entry.
2943 * @param ads connection to ads server
2944 * @param res Results to process
2945 * @param fn Function for processing each result
2946 * @param data_area user-defined area to pass to function
2948 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2949 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2950 void *data_area)
2952 LDAPMessage *msg;
2953 TALLOC_CTX *ctx;
2954 size_t converted_size;
2956 if (!(ctx = talloc_init("ads_process_results")))
2957 return;
2959 for (msg = ads_first_entry(ads, res); msg;
2960 msg = ads_next_entry(ads, msg)) {
2961 char *utf8_field;
2962 BerElement *b;
2964 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2965 (LDAPMessage *)msg,&b);
2966 utf8_field;
2967 utf8_field=ldap_next_attribute(ads->ldap.ld,
2968 (LDAPMessage *)msg,b)) {
2969 struct berval **ber_vals;
2970 char **str_vals;
2971 char **utf8_vals;
2972 char *field;
2973 bool string;
2975 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2976 &converted_size))
2978 DEBUG(0,("ads_process_results: "
2979 "pull_utf8_talloc failed: %s",
2980 strerror(errno)));
2983 string = fn(ads, field, NULL, data_area);
2985 if (string) {
2986 const char **p;
2988 utf8_vals = ldap_get_values(ads->ldap.ld,
2989 (LDAPMessage *)msg, field);
2990 p = discard_const_p(const char *, utf8_vals);
2991 str_vals = ads_pull_strvals(ctx, p);
2992 fn(ads, field, (void **) str_vals, data_area);
2993 ldap_value_free(utf8_vals);
2994 } else {
2995 ber_vals = ldap_get_values_len(ads->ldap.ld,
2996 (LDAPMessage *)msg, field);
2997 fn(ads, field, (void **) ber_vals, data_area);
2999 ldap_value_free_len(ber_vals);
3001 ldap_memfree(utf8_field);
3003 ber_free(b, 0);
3004 talloc_free_children(ctx);
3005 fn(ads, NULL, NULL, data_area); /* completed an entry */
3008 talloc_destroy(ctx);
3012 * count how many replies are in a LDAPMessage
3013 * @param ads connection to ads server
3014 * @param res Results to count
3015 * @return number of replies
3017 int ads_count_replies(ADS_STRUCT *ads, void *res)
3019 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3023 * pull the first entry from a ADS result
3024 * @param ads connection to ads server
3025 * @param res Results of search
3026 * @return first entry from result
3028 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3030 return ldap_first_entry(ads->ldap.ld, res);
3034 * pull the next entry from a ADS result
3035 * @param ads connection to ads server
3036 * @param res Results of search
3037 * @return next entry from result
3039 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3041 return ldap_next_entry(ads->ldap.ld, res);
3045 * pull the first message from a ADS result
3046 * @param ads connection to ads server
3047 * @param res Results of search
3048 * @return first message from result
3050 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3052 return ldap_first_message(ads->ldap.ld, res);
3056 * pull the next message from a ADS result
3057 * @param ads connection to ads server
3058 * @param res Results of search
3059 * @return next message from result
3061 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3063 return ldap_next_message(ads->ldap.ld, res);
3067 * pull a single string from a ADS result
3068 * @param ads connection to ads server
3069 * @param mem_ctx TALLOC_CTX to use for allocating result string
3070 * @param msg Results of search
3071 * @param field Attribute to retrieve
3072 * @return Result string in talloc context
3074 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3075 const char *field)
3077 char **values;
3078 char *ret = NULL;
3079 char *ux_string;
3080 size_t converted_size;
3082 values = ldap_get_values(ads->ldap.ld, msg, field);
3083 if (!values)
3084 return NULL;
3086 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3087 &converted_size))
3089 ret = ux_string;
3091 ldap_value_free(values);
3092 return ret;
3096 * pull an array of strings from a ADS result
3097 * @param ads connection to ads server
3098 * @param mem_ctx TALLOC_CTX to use for allocating result string
3099 * @param msg Results of search
3100 * @param field Attribute to retrieve
3101 * @return Result strings in talloc context
3103 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3104 LDAPMessage *msg, const char *field,
3105 size_t *num_values)
3107 char **values;
3108 char **ret = NULL;
3109 size_t i, converted_size;
3111 values = ldap_get_values(ads->ldap.ld, msg, field);
3112 if (!values)
3113 return NULL;
3115 *num_values = ldap_count_values(values);
3117 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3118 if (!ret) {
3119 ldap_value_free(values);
3120 return NULL;
3123 for (i=0;i<*num_values;i++) {
3124 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3125 &converted_size))
3127 ldap_value_free(values);
3128 return NULL;
3131 ret[i] = NULL;
3133 ldap_value_free(values);
3134 return ret;
3138 * pull an array of strings from a ADS result
3139 * (handle large multivalue attributes with range retrieval)
3140 * @param ads connection to ads server
3141 * @param mem_ctx TALLOC_CTX to use for allocating result string
3142 * @param msg Results of search
3143 * @param field Attribute to retrieve
3144 * @param current_strings strings returned by a previous call to this function
3145 * @param next_attribute The next query should ask for this attribute
3146 * @param num_values How many values did we get this time?
3147 * @param more_values Are there more values to get?
3148 * @return Result strings in talloc context
3150 char **ads_pull_strings_range(ADS_STRUCT *ads,
3151 TALLOC_CTX *mem_ctx,
3152 LDAPMessage *msg, const char *field,
3153 char **current_strings,
3154 const char **next_attribute,
3155 size_t *num_strings,
3156 bool *more_strings)
3158 char *attr;
3159 char *expected_range_attrib, *range_attr;
3160 BerElement *ptr = NULL;
3161 char **strings;
3162 char **new_strings;
3163 size_t num_new_strings;
3164 unsigned long int range_start;
3165 unsigned long int range_end;
3167 /* we might have been given the whole lot anyway */
3168 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3169 *more_strings = False;
3170 return strings;
3173 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3175 /* look for Range result */
3176 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3177 attr;
3178 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3179 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3180 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3181 range_attr = attr;
3182 break;
3184 ldap_memfree(attr);
3186 if (!attr) {
3187 ber_free(ptr, 0);
3188 /* nothing here - this field is just empty */
3189 *more_strings = False;
3190 return NULL;
3193 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3194 &range_start, &range_end) == 2) {
3195 *more_strings = True;
3196 } else {
3197 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3198 &range_start) == 1) {
3199 *more_strings = False;
3200 } else {
3201 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
3202 range_attr));
3203 ldap_memfree(range_attr);
3204 *more_strings = False;
3205 return NULL;
3209 if ((*num_strings) != range_start) {
3210 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3211 " - aborting range retrieval\n",
3212 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3213 ldap_memfree(range_attr);
3214 *more_strings = False;
3215 return NULL;
3218 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3220 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3221 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3222 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3223 range_attr, (unsigned long int)range_end - range_start + 1,
3224 (unsigned long int)num_new_strings));
3225 ldap_memfree(range_attr);
3226 *more_strings = False;
3227 return NULL;
3230 strings = talloc_realloc(mem_ctx, current_strings, char *,
3231 *num_strings + num_new_strings);
3233 if (strings == NULL) {
3234 ldap_memfree(range_attr);
3235 *more_strings = False;
3236 return NULL;
3239 if (new_strings && num_new_strings) {
3240 memcpy(&strings[*num_strings], new_strings,
3241 sizeof(*new_strings) * num_new_strings);
3244 (*num_strings) += num_new_strings;
3246 if (*more_strings) {
3247 *next_attribute = talloc_asprintf(mem_ctx,
3248 "%s;range=%d-*",
3249 field,
3250 (int)*num_strings);
3252 if (!*next_attribute) {
3253 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3254 ldap_memfree(range_attr);
3255 *more_strings = False;
3256 return NULL;
3260 ldap_memfree(range_attr);
3262 return strings;
3266 * pull a single uint32_t from a ADS result
3267 * @param ads connection to ads server
3268 * @param msg Results of search
3269 * @param field Attribute to retrieve
3270 * @param v Pointer to int to store result
3271 * @return boolean indicating success
3273 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3274 uint32_t *v)
3276 char **values;
3278 values = ldap_get_values(ads->ldap.ld, msg, field);
3279 if (!values)
3280 return False;
3281 if (!values[0]) {
3282 ldap_value_free(values);
3283 return False;
3286 *v = atoi(values[0]);
3287 ldap_value_free(values);
3288 return True;
3292 * pull a single objectGUID from an ADS result
3293 * @param ads connection to ADS server
3294 * @param msg results of search
3295 * @param guid 37-byte area to receive text guid
3296 * @return boolean indicating success
3298 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3300 DATA_BLOB blob;
3301 NTSTATUS status;
3303 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3304 &blob)) {
3305 return false;
3308 status = GUID_from_ndr_blob(&blob, guid);
3309 talloc_free(blob.data);
3310 return NT_STATUS_IS_OK(status);
3315 * pull a single struct dom_sid from a ADS result
3316 * @param ads connection to ads server
3317 * @param msg Results of search
3318 * @param field Attribute to retrieve
3319 * @param sid Pointer to sid to store result
3320 * @return boolean indicating success
3322 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3323 struct dom_sid *sid)
3325 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3329 * pull an array of struct dom_sids from a ADS result
3330 * @param ads connection to ads server
3331 * @param mem_ctx TALLOC_CTX for allocating sid array
3332 * @param msg Results of search
3333 * @param field Attribute to retrieve
3334 * @param sids pointer to sid array to allocate
3335 * @return the count of SIDs pulled
3337 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3338 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3340 struct berval **values;
3341 int count, i;
3343 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3345 if (!values)
3346 return 0;
3348 for (i=0; values[i]; i++)
3349 /* nop */ ;
3351 if (i) {
3352 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3353 if (!(*sids)) {
3354 ldap_value_free_len(values);
3355 return 0;
3357 } else {
3358 (*sids) = NULL;
3361 count = 0;
3362 for (i=0; values[i]; i++) {
3363 ssize_t ret;
3364 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3365 values[i]->bv_len, &(*sids)[count]);
3366 if (ret != -1) {
3367 struct dom_sid_buf buf;
3368 DBG_DEBUG("pulling SID: %s\n",
3369 dom_sid_str_buf(&(*sids)[count], &buf));
3370 count++;
3374 ldap_value_free_len(values);
3375 return count;
3379 * pull a struct security_descriptor from a ADS result
3380 * @param ads connection to ads server
3381 * @param mem_ctx TALLOC_CTX for allocating sid array
3382 * @param msg Results of search
3383 * @param field Attribute to retrieve
3384 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3385 * @return boolean indicating success
3387 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3388 LDAPMessage *msg, const char *field,
3389 struct security_descriptor **sd)
3391 struct berval **values;
3392 bool ret = true;
3394 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3396 if (!values) return false;
3398 if (values[0]) {
3399 NTSTATUS status;
3400 status = unmarshall_sec_desc(mem_ctx,
3401 (uint8_t *)values[0]->bv_val,
3402 values[0]->bv_len, sd);
3403 if (!NT_STATUS_IS_OK(status)) {
3404 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3405 nt_errstr(status)));
3406 ret = false;
3410 ldap_value_free_len(values);
3411 return ret;
3415 * in order to support usernames longer than 21 characters we need to
3416 * use both the sAMAccountName and the userPrincipalName attributes
3417 * It seems that not all users have the userPrincipalName attribute set
3419 * @param ads connection to ads server
3420 * @param mem_ctx TALLOC_CTX for allocating sid array
3421 * @param msg Results of search
3422 * @return the username
3424 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3425 LDAPMessage *msg)
3427 #if 0 /* JERRY */
3428 char *ret, *p;
3430 /* lookup_name() only works on the sAMAccountName to
3431 returning the username portion of userPrincipalName
3432 breaks winbindd_getpwnam() */
3434 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3435 if (ret && (p = strchr_m(ret, '@'))) {
3436 *p = 0;
3437 return ret;
3439 #endif
3440 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3445 * find the update serial number - this is the core of the ldap cache
3446 * @param ads connection to ads server
3447 * @param ads connection to ADS server
3448 * @param usn Pointer to retrieved update serial number
3449 * @return status of search
3451 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3453 const char *attrs[] = {"highestCommittedUSN", NULL};
3454 ADS_STATUS status;
3455 LDAPMessage *res;
3457 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3458 if (!ADS_ERR_OK(status))
3459 return status;
3461 if (ads_count_replies(ads, res) != 1) {
3462 ads_msgfree(ads, res);
3463 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3466 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3467 ads_msgfree(ads, res);
3468 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3471 ads_msgfree(ads, res);
3472 return ADS_SUCCESS;
3475 /* parse a ADS timestring - typical string is
3476 '20020917091222.0Z0' which means 09:12.22 17th September
3477 2002, timezone 0 */
3478 static time_t ads_parse_time(const char *str)
3480 struct tm tm;
3482 ZERO_STRUCT(tm);
3484 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3485 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3486 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3487 return 0;
3489 tm.tm_year -= 1900;
3490 tm.tm_mon -= 1;
3492 return timegm(&tm);
3495 /********************************************************************
3496 ********************************************************************/
3498 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3500 const char *attrs[] = {"currentTime", NULL};
3501 ADS_STATUS status;
3502 LDAPMessage *res;
3503 char *timestr;
3504 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3505 ADS_STRUCT *ads_s = ads;
3507 /* establish a new ldap tcp session if necessary */
3509 if ( !ads->ldap.ld ) {
3511 * ADS_STRUCT may be being reused after a
3512 * DC lookup, so ads->ldap.ss may already have a
3513 * good address. If not, re-initialize the passed-in
3514 * ADS_STRUCT with the given server.XXXX parameters.
3516 * Note that this doesn't depend on
3517 * ads->server.ldap_server != NULL,
3518 * as the case where ads->server.ldap_server==NULL and
3519 * ads->ldap.ss != zero_address is precisely the DC
3520 * lookup case where ads->ldap.ss was found by going
3521 * through ads_find_dc() again we want to avoid repeating.
3523 if (is_zero_addr(&ads->ldap.ss)) {
3524 ads_s = ads_init(tmp_ctx,
3525 ads->server.realm,
3526 ads->server.workgroup,
3527 ads->server.ldap_server,
3528 ADS_SASL_PLAIN );
3529 if (ads_s == NULL) {
3530 status = ADS_ERROR(LDAP_NO_MEMORY);
3531 goto done;
3536 * Reset ads->config.flags as it can contain the flags
3537 * returned by the previous CLDAP ping when reusing the struct.
3539 ads_s->config.flags = 0;
3541 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3542 status = ads_connect( ads_s );
3543 if ( !ADS_ERR_OK(status))
3544 goto done;
3547 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3548 if (!ADS_ERR_OK(status)) {
3549 goto done;
3552 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3553 if (!timestr) {
3554 ads_msgfree(ads_s, res);
3555 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3556 goto done;
3559 /* but save the time and offset in the original ADS_STRUCT */
3561 ads->config.current_time = ads_parse_time(timestr);
3563 if (ads->config.current_time != 0) {
3564 ads->auth.time_offset = ads->config.current_time - time(NULL);
3565 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3568 ads_msgfree(ads, res);
3570 status = ADS_SUCCESS;
3572 done:
3573 TALLOC_FREE(tmp_ctx);
3575 return status;
3578 /********************************************************************
3579 ********************************************************************/
3581 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3583 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3584 const char *attrs[] = {"domainFunctionality", NULL};
3585 ADS_STATUS status;
3586 LDAPMessage *res;
3587 ADS_STRUCT *ads_s = ads;
3589 *val = DS_DOMAIN_FUNCTION_2000;
3591 /* establish a new ldap tcp session if necessary */
3593 if ( !ads->ldap.ld ) {
3595 * ADS_STRUCT may be being reused after a
3596 * DC lookup, so ads->ldap.ss may already have a
3597 * good address. If not, re-initialize the passed-in
3598 * ADS_STRUCT with the given server.XXXX parameters.
3600 * Note that this doesn't depend on
3601 * ads->server.ldap_server != NULL,
3602 * as the case where ads->server.ldap_server==NULL and
3603 * ads->ldap.ss != zero_address is precisely the DC
3604 * lookup case where ads->ldap.ss was found by going
3605 * through ads_find_dc() again we want to avoid repeating.
3607 if (is_zero_addr(&ads->ldap.ss)) {
3608 ads_s = ads_init(tmp_ctx,
3609 ads->server.realm,
3610 ads->server.workgroup,
3611 ads->server.ldap_server,
3612 ADS_SASL_PLAIN );
3613 if (ads_s == NULL ) {
3614 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3615 goto done;
3620 * Reset ads->config.flags as it can contain the flags
3621 * returned by the previous CLDAP ping when reusing the struct.
3623 ads_s->config.flags = 0;
3625 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3626 status = ads_connect( ads_s );
3627 if ( !ADS_ERR_OK(status))
3628 goto done;
3631 /* If the attribute does not exist assume it is a Windows 2000
3632 functional domain */
3634 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3635 if (!ADS_ERR_OK(status)) {
3636 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3637 status = ADS_SUCCESS;
3639 goto done;
3642 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3643 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3645 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3648 ads_msgfree(ads_s, res);
3650 done:
3651 TALLOC_FREE(tmp_ctx);
3653 return status;
3657 * find the domain sid for our domain
3658 * @param ads connection to ads server
3659 * @param sid Pointer to domain sid
3660 * @return status of search
3662 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3664 const char *attrs[] = {"objectSid", NULL};
3665 LDAPMessage *res;
3666 ADS_STATUS rc;
3668 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3669 attrs, &res);
3670 if (!ADS_ERR_OK(rc)) return rc;
3671 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3672 ads_msgfree(ads, res);
3673 return ADS_ERROR_SYSTEM(ENOENT);
3675 ads_msgfree(ads, res);
3677 return ADS_SUCCESS;
3681 * find our site name
3682 * @param ads connection to ads server
3683 * @param mem_ctx Pointer to talloc context
3684 * @param site_name Pointer to the sitename
3685 * @return status of search
3687 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3689 ADS_STATUS status;
3690 LDAPMessage *res;
3691 const char *dn, *service_name;
3692 const char *attrs[] = { "dsServiceName", NULL };
3694 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3695 if (!ADS_ERR_OK(status)) {
3696 return status;
3699 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3700 if (service_name == NULL) {
3701 ads_msgfree(ads, res);
3702 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3705 ads_msgfree(ads, res);
3707 /* go up three levels */
3708 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3709 if (dn == NULL) {
3710 return ADS_ERROR(LDAP_NO_MEMORY);
3713 *site_name = talloc_strdup(mem_ctx, dn);
3714 if (*site_name == NULL) {
3715 return ADS_ERROR(LDAP_NO_MEMORY);
3718 return status;
3720 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3725 * find the site dn where a machine resides
3726 * @param ads connection to ads server
3727 * @param mem_ctx Pointer to talloc context
3728 * @param computer_name name of the machine
3729 * @param site_name Pointer to the sitename
3730 * @return status of search
3732 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3734 ADS_STATUS status;
3735 LDAPMessage *res;
3736 const char *parent, *filter;
3737 char *config_context = NULL;
3738 char *dn;
3740 /* shortcut a query */
3741 if (strequal(computer_name, ads->config.ldap_server_name)) {
3742 return ads_site_dn(ads, mem_ctx, site_dn);
3745 status = ads_config_path(ads, mem_ctx, &config_context);
3746 if (!ADS_ERR_OK(status)) {
3747 return status;
3750 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3751 if (filter == NULL) {
3752 return ADS_ERROR(LDAP_NO_MEMORY);
3755 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3756 filter, NULL, &res);
3757 if (!ADS_ERR_OK(status)) {
3758 return status;
3761 if (ads_count_replies(ads, res) != 1) {
3762 ads_msgfree(ads, res);
3763 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3766 dn = ads_get_dn(ads, mem_ctx, res);
3767 if (dn == NULL) {
3768 ads_msgfree(ads, res);
3769 return ADS_ERROR(LDAP_NO_MEMORY);
3772 /* go up three levels */
3773 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3774 if (parent == NULL) {
3775 ads_msgfree(ads, res);
3776 TALLOC_FREE(dn);
3777 return ADS_ERROR(LDAP_NO_MEMORY);
3780 *site_dn = talloc_strdup(mem_ctx, parent);
3781 if (*site_dn == NULL) {
3782 ads_msgfree(ads, res);
3783 TALLOC_FREE(dn);
3784 return ADS_ERROR(LDAP_NO_MEMORY);
3787 TALLOC_FREE(dn);
3788 ads_msgfree(ads, res);
3790 return status;
3794 * get the upn suffixes for a domain
3795 * @param ads connection to ads server
3796 * @param mem_ctx Pointer to talloc context
3797 * @param suffixes Pointer to an array of suffixes
3798 * @param num_suffixes Pointer to the number of suffixes
3799 * @return status of search
3801 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3803 ADS_STATUS status;
3804 LDAPMessage *res;
3805 const char *base;
3806 char *config_context = NULL;
3807 const char *attrs[] = { "uPNSuffixes", NULL };
3809 status = ads_config_path(ads, mem_ctx, &config_context);
3810 if (!ADS_ERR_OK(status)) {
3811 return status;
3814 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3815 if (base == NULL) {
3816 return ADS_ERROR(LDAP_NO_MEMORY);
3819 status = ads_search_dn(ads, &res, base, attrs);
3820 if (!ADS_ERR_OK(status)) {
3821 return status;
3824 if (ads_count_replies(ads, res) != 1) {
3825 ads_msgfree(ads, res);
3826 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3829 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3830 if ((*suffixes) == NULL) {
3831 ads_msgfree(ads, res);
3832 return ADS_ERROR(LDAP_NO_MEMORY);
3835 ads_msgfree(ads, res);
3837 return status;
3841 * get the joinable ous for a domain
3842 * @param ads connection to ads server
3843 * @param mem_ctx Pointer to talloc context
3844 * @param ous Pointer to an array of ous
3845 * @param num_ous Pointer to the number of ous
3846 * @return status of search
3848 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3849 TALLOC_CTX *mem_ctx,
3850 char ***ous,
3851 size_t *num_ous)
3853 ADS_STATUS status;
3854 LDAPMessage *res = NULL;
3855 LDAPMessage *msg = NULL;
3856 const char *attrs[] = { "dn", NULL };
3857 int count = 0;
3859 status = ads_search(ads, &res,
3860 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3861 attrs);
3862 if (!ADS_ERR_OK(status)) {
3863 return status;
3866 count = ads_count_replies(ads, res);
3867 if (count < 1) {
3868 ads_msgfree(ads, res);
3869 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3872 for (msg = ads_first_entry(ads, res); msg;
3873 msg = ads_next_entry(ads, msg)) {
3874 const char **p = discard_const_p(const char *, *ous);
3875 char *dn = NULL;
3877 dn = ads_get_dn(ads, talloc_tos(), msg);
3878 if (!dn) {
3879 ads_msgfree(ads, res);
3880 return ADS_ERROR(LDAP_NO_MEMORY);
3883 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3884 TALLOC_FREE(dn);
3885 ads_msgfree(ads, res);
3886 return ADS_ERROR(LDAP_NO_MEMORY);
3889 TALLOC_FREE(dn);
3890 *ous = discard_const_p(char *, p);
3893 ads_msgfree(ads, res);
3895 return status;
3900 * pull a struct dom_sid from an extended dn string
3901 * @param mem_ctx TALLOC_CTX
3902 * @param extended_dn string
3903 * @param flags string type of extended_dn
3904 * @param sid pointer to a struct dom_sid
3905 * @return NT_STATUS_OK on success,
3906 * NT_INVALID_PARAMETER on error,
3907 * NT_STATUS_NOT_FOUND if no SID present
3909 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3910 const char *extended_dn,
3911 enum ads_extended_dn_flags flags,
3912 struct dom_sid *sid)
3914 char *p, *q, *dn;
3916 if (!extended_dn) {
3917 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3920 /* otherwise extended_dn gets stripped off */
3921 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3922 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3925 * ADS_EXTENDED_DN_HEX_STRING:
3926 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3928 * ADS_EXTENDED_DN_STRING (only with w2k3):
3929 * <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
3931 * Object with no SID, such as an Exchange Public Folder
3932 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3935 p = strchr(dn, ';');
3936 if (!p) {
3937 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3940 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3941 DEBUG(5,("No SID present in extended dn\n"));
3942 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3945 p += strlen(";<SID=");
3947 q = strchr(p, '>');
3948 if (!q) {
3949 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3952 *q = '\0';
3954 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3956 switch (flags) {
3958 case ADS_EXTENDED_DN_STRING:
3959 if (!string_to_sid(sid, p)) {
3960 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3962 break;
3963 case ADS_EXTENDED_DN_HEX_STRING: {
3964 ssize_t ret;
3965 fstring buf;
3966 size_t buf_len;
3968 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3969 if (buf_len == 0) {
3970 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3973 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3974 if (ret == -1) {
3975 DEBUG(10,("failed to parse sid\n"));
3976 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3978 break;
3980 default:
3981 DEBUG(10,("unknown extended dn format\n"));
3982 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3985 return ADS_ERROR_NT(NT_STATUS_OK);
3988 /********************************************************************
3989 ********************************************************************/
3991 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3993 LDAPMessage *res = NULL;
3994 ADS_STATUS status;
3995 int count = 0;
3996 char *name = NULL;
3998 status = ads_find_machine_acct(ads, &res, machine_name);
3999 if (!ADS_ERR_OK(status)) {
4000 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4001 lp_netbios_name()));
4002 goto out;
4005 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4006 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4007 goto out;
4010 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4011 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4014 out:
4015 ads_msgfree(ads, res);
4017 return name;
4020 /********************************************************************
4021 ********************************************************************/
4023 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4024 LDAPMessage *msg, size_t *num_values)
4026 const char *field = "msDS-AdditionalDnsHostName";
4027 struct berval **values = NULL;
4028 char **ret = NULL;
4029 size_t i, converted_size;
4032 * Windows DC implicitly adds a short name for each FQDN added to
4033 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4034 * suffix "\0$" which we should ignore (see bug #14406).
4037 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4038 if (values == NULL) {
4039 return NULL;
4042 *num_values = ldap_count_values_len(values);
4044 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4045 if (ret == NULL) {
4046 ldap_value_free_len(values);
4047 return NULL;
4050 for (i = 0; i < *num_values; i++) {
4051 ret[i] = NULL;
4052 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4053 values[i]->bv_val,
4054 strnlen(values[i]->bv_val,
4055 values[i]->bv_len),
4056 &ret[i], &converted_size)) {
4057 ldap_value_free_len(values);
4058 return NULL;
4061 ret[i] = NULL;
4063 ldap_value_free_len(values);
4064 return ret;
4067 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4068 ADS_STRUCT *ads,
4069 const char *machine_name,
4070 char ***hostnames_array,
4071 size_t *num_hostnames)
4073 ADS_STATUS status;
4074 LDAPMessage *res = NULL;
4075 int count;
4077 status = ads_find_machine_acct(ads,
4078 &res,
4079 machine_name);
4080 if (!ADS_ERR_OK(status)) {
4081 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4082 machine_name));
4083 return status;
4086 count = ads_count_replies(ads, res);
4087 if (count != 1) {
4088 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4089 goto done;
4092 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4093 if (*hostnames_array == NULL) {
4094 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4095 machine_name));
4096 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4097 goto done;
4100 done:
4101 ads_msgfree(ads, res);
4103 return status;
4106 /********************************************************************
4107 ********************************************************************/
4109 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4111 LDAPMessage *res = NULL;
4112 ADS_STATUS status;
4113 int count = 0;
4114 char *name = NULL;
4116 status = ads_find_machine_acct(ads, &res, machine_name);
4117 if (!ADS_ERR_OK(status)) {
4118 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4119 lp_netbios_name()));
4120 goto out;
4123 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4124 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4125 goto out;
4128 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4129 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4132 out:
4133 ads_msgfree(ads, res);
4135 return name;
4138 /********************************************************************
4139 ********************************************************************/
4141 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4143 LDAPMessage *res = NULL;
4144 ADS_STATUS status;
4145 int count = 0;
4146 char *name = NULL;
4147 bool ok = false;
4149 status = ads_find_machine_acct(ads, &res, machine_name);
4150 if (!ADS_ERR_OK(status)) {
4151 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4152 lp_netbios_name()));
4153 goto out;
4156 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4157 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4158 goto out;
4161 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4162 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4165 out:
4166 ads_msgfree(ads, res);
4167 if (name != NULL) {
4168 ok = (strlen(name) > 0);
4170 TALLOC_FREE(name);
4171 return ok;
4174 #if 0
4176 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4179 * Join a machine to a realm
4180 * Creates the machine account and sets the machine password
4181 * @param ads connection to ads server
4182 * @param machine name of host to add
4183 * @param org_unit Organizational unit to place machine in
4184 * @return status of join
4186 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4187 uint32_t account_type, const char *org_unit)
4189 ADS_STATUS status;
4190 LDAPMessage *res = NULL;
4191 char *machine;
4193 /* machine name must be lowercase */
4194 machine = SMB_STRDUP(machine_name);
4195 strlower_m(machine);
4198 status = ads_find_machine_acct(ads, (void **)&res, machine);
4199 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4200 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4201 status = ads_leave_realm(ads, machine);
4202 if (!ADS_ERR_OK(status)) {
4203 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4204 machine, ads->config.realm));
4205 return status;
4209 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4210 if (!ADS_ERR_OK(status)) {
4211 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4212 SAFE_FREE(machine);
4213 return status;
4216 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4217 if (!ADS_ERR_OK(status)) {
4218 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4219 SAFE_FREE(machine);
4220 return status;
4223 SAFE_FREE(machine);
4224 ads_msgfree(ads, res);
4226 return status;
4228 #endif
4231 * Delete a machine from the realm
4232 * @param ads connection to ads server
4233 * @param hostname Machine to remove
4234 * @return status of delete
4236 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4238 ADS_STATUS status;
4239 void *msg;
4240 LDAPMessage *res;
4241 char *hostnameDN, *host;
4242 int rc;
4243 LDAPControl ldap_control;
4244 LDAPControl * pldap_control[2] = {NULL, NULL};
4246 pldap_control[0] = &ldap_control;
4247 memset(&ldap_control, 0, sizeof(LDAPControl));
4248 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4250 /* hostname must be lowercase */
4251 host = SMB_STRDUP(hostname);
4252 if (!strlower_m(host)) {
4253 SAFE_FREE(host);
4254 return ADS_ERROR_SYSTEM(EINVAL);
4257 status = ads_find_machine_acct(ads, &res, host);
4258 if (!ADS_ERR_OK(status)) {
4259 DEBUG(0, ("Host account for %s does not exist.\n", host));
4260 SAFE_FREE(host);
4261 return status;
4264 msg = ads_first_entry(ads, res);
4265 if (!msg) {
4266 SAFE_FREE(host);
4267 return ADS_ERROR_SYSTEM(ENOENT);
4270 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4271 if (hostnameDN == NULL) {
4272 SAFE_FREE(host);
4273 return ADS_ERROR_SYSTEM(ENOENT);
4276 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4277 if (rc) {
4278 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4279 }else {
4280 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4283 if (rc != LDAP_SUCCESS) {
4284 const char *attrs[] = { "cn", NULL };
4285 LDAPMessage *msg_sub;
4287 /* we only search with scope ONE, we do not expect any further
4288 * objects to be created deeper */
4290 status = ads_do_search_retry(ads, hostnameDN,
4291 LDAP_SCOPE_ONELEVEL,
4292 "(objectclass=*)", attrs, &res);
4294 if (!ADS_ERR_OK(status)) {
4295 SAFE_FREE(host);
4296 TALLOC_FREE(hostnameDN);
4297 return status;
4300 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4301 msg_sub = ads_next_entry(ads, msg_sub)) {
4303 char *dn = NULL;
4305 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4306 SAFE_FREE(host);
4307 TALLOC_FREE(hostnameDN);
4308 return ADS_ERROR(LDAP_NO_MEMORY);
4311 status = ads_del_dn(ads, dn);
4312 if (!ADS_ERR_OK(status)) {
4313 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4314 SAFE_FREE(host);
4315 TALLOC_FREE(dn);
4316 TALLOC_FREE(hostnameDN);
4317 return status;
4320 TALLOC_FREE(dn);
4323 /* there should be no subordinate objects anymore */
4324 status = ads_do_search_retry(ads, hostnameDN,
4325 LDAP_SCOPE_ONELEVEL,
4326 "(objectclass=*)", attrs, &res);
4328 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4329 SAFE_FREE(host);
4330 TALLOC_FREE(hostnameDN);
4331 return status;
4334 /* delete hostnameDN now */
4335 status = ads_del_dn(ads, hostnameDN);
4336 if (!ADS_ERR_OK(status)) {
4337 SAFE_FREE(host);
4338 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4339 TALLOC_FREE(hostnameDN);
4340 return status;
4344 TALLOC_FREE(hostnameDN);
4346 status = ads_find_machine_acct(ads, &res, host);
4347 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4348 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4349 DEBUG(3, ("Failed to remove host account.\n"));
4350 SAFE_FREE(host);
4351 return status;
4354 SAFE_FREE(host);
4355 return ADS_SUCCESS;
4359 * pull all token-sids from an LDAP dn
4360 * @param ads connection to ads server
4361 * @param mem_ctx TALLOC_CTX for allocating sid array
4362 * @param dn of LDAP object
4363 * @param user_sid pointer to struct dom_sid (objectSid)
4364 * @param primary_group_sid pointer to struct dom_sid (self composed)
4365 * @param sids pointer to sid array to allocate
4366 * @param num_sids counter of SIDs pulled
4367 * @return status of token query
4369 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4370 TALLOC_CTX *mem_ctx,
4371 const char *dn,
4372 struct dom_sid *user_sid,
4373 struct dom_sid *primary_group_sid,
4374 struct dom_sid **sids,
4375 size_t *num_sids)
4377 ADS_STATUS status;
4378 LDAPMessage *res = NULL;
4379 int count = 0;
4380 size_t tmp_num_sids;
4381 struct dom_sid *tmp_sids;
4382 struct dom_sid tmp_user_sid;
4383 struct dom_sid tmp_primary_group_sid;
4384 uint32_t pgid;
4385 const char *attrs[] = {
4386 "objectSid",
4387 "tokenGroups",
4388 "primaryGroupID",
4389 NULL
4392 status = ads_search_retry_dn(ads, &res, dn, attrs);
4393 if (!ADS_ERR_OK(status)) {
4394 return status;
4397 count = ads_count_replies(ads, res);
4398 if (count != 1) {
4399 ads_msgfree(ads, res);
4400 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4403 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4404 ads_msgfree(ads, res);
4405 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4408 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4409 ads_msgfree(ads, res);
4410 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4414 /* hack to compose the primary group sid without knowing the
4415 * domsid */
4417 struct dom_sid domsid;
4419 sid_copy(&domsid, &tmp_user_sid);
4421 if (!sid_split_rid(&domsid, NULL)) {
4422 ads_msgfree(ads, res);
4423 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4426 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4427 ads_msgfree(ads, res);
4428 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4432 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4434 if (tmp_num_sids == 0 || !tmp_sids) {
4435 ads_msgfree(ads, res);
4436 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4439 if (num_sids) {
4440 *num_sids = tmp_num_sids;
4443 if (sids) {
4444 *sids = tmp_sids;
4447 if (user_sid) {
4448 *user_sid = tmp_user_sid;
4451 if (primary_group_sid) {
4452 *primary_group_sid = tmp_primary_group_sid;
4455 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4457 ads_msgfree(ads, res);
4458 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4462 * Find a sAMAccoutName in LDAP
4463 * @param ads connection to ads server
4464 * @param mem_ctx TALLOC_CTX for allocating sid array
4465 * @param samaccountname to search
4466 * @param uac_ret uint32_t pointer userAccountControl attribute value
4467 * @param dn_ret pointer to dn
4468 * @return status of token query
4470 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4471 TALLOC_CTX *mem_ctx,
4472 const char *samaccountname,
4473 uint32_t *uac_ret,
4474 const char **dn_ret)
4476 ADS_STATUS status;
4477 const char *attrs[] = { "userAccountControl", NULL };
4478 const char *filter;
4479 LDAPMessage *res = NULL;
4480 char *dn = NULL;
4481 uint32_t uac = 0;
4483 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4484 samaccountname);
4485 if (filter == NULL) {
4486 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4487 goto out;
4490 status = ads_do_search_all(ads, ads->config.bind_path,
4491 LDAP_SCOPE_SUBTREE,
4492 filter, attrs, &res);
4494 if (!ADS_ERR_OK(status)) {
4495 goto out;
4498 if (ads_count_replies(ads, res) != 1) {
4499 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4500 goto out;
4503 dn = ads_get_dn(ads, talloc_tos(), res);
4504 if (dn == NULL) {
4505 status = ADS_ERROR(LDAP_NO_MEMORY);
4506 goto out;
4509 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4510 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4511 goto out;
4514 if (uac_ret) {
4515 *uac_ret = uac;
4518 if (dn_ret) {
4519 *dn_ret = talloc_strdup(mem_ctx, dn);
4520 if (!*dn_ret) {
4521 status = ADS_ERROR(LDAP_NO_MEMORY);
4522 goto out;
4525 out:
4526 TALLOC_FREE(dn);
4527 ads_msgfree(ads, res);
4529 return status;
4533 * find our configuration path
4534 * @param ads connection to ads server
4535 * @param mem_ctx Pointer to talloc context
4536 * @param config_path Pointer to the config path
4537 * @return status of search
4539 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4540 TALLOC_CTX *mem_ctx,
4541 char **config_path)
4543 ADS_STATUS status;
4544 LDAPMessage *res = NULL;
4545 const char *config_context = NULL;
4546 const char *attrs[] = { "configurationNamingContext", NULL };
4548 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4549 "(objectclass=*)", attrs, &res);
4550 if (!ADS_ERR_OK(status)) {
4551 return status;
4554 config_context = ads_pull_string(ads, mem_ctx, res,
4555 "configurationNamingContext");
4556 ads_msgfree(ads, res);
4557 if (!config_context) {
4558 return ADS_ERROR(LDAP_NO_MEMORY);
4561 if (config_path) {
4562 *config_path = talloc_strdup(mem_ctx, config_context);
4563 if (!*config_path) {
4564 return ADS_ERROR(LDAP_NO_MEMORY);
4568 return ADS_ERROR(LDAP_SUCCESS);
4572 * find the displayName of an extended right
4573 * @param ads connection to ads server
4574 * @param config_path The config path
4575 * @param mem_ctx Pointer to talloc context
4576 * @param GUID struct of the rightsGUID
4577 * @return status of search
4579 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4580 const char *config_path,
4581 TALLOC_CTX *mem_ctx,
4582 const struct GUID *rights_guid)
4584 ADS_STATUS rc;
4585 LDAPMessage *res = NULL;
4586 char *expr = NULL;
4587 const char *attrs[] = { "displayName", NULL };
4588 const char *result = NULL;
4589 const char *path;
4591 if (!ads || !mem_ctx || !rights_guid) {
4592 goto done;
4595 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4596 GUID_string(mem_ctx, rights_guid));
4597 if (!expr) {
4598 goto done;
4601 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4602 if (!path) {
4603 goto done;
4606 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4607 expr, attrs, &res);
4608 if (!ADS_ERR_OK(rc)) {
4609 goto done;
4612 if (ads_count_replies(ads, res) != 1) {
4613 goto done;
4616 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4618 done:
4619 ads_msgfree(ads, res);
4620 return result;
4624 * verify or build and verify an account ou
4625 * @param mem_ctx Pointer to talloc context
4626 * @param ads connection to ads server
4627 * @param account_ou
4628 * @return status of search
4631 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4632 ADS_STRUCT *ads,
4633 const char **account_ou)
4635 char **exploded_dn;
4636 const char *name;
4637 char *ou_string;
4639 if (account_ou == NULL) {
4640 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4643 if (*account_ou != NULL) {
4644 exploded_dn = ldap_explode_dn(*account_ou, 0);
4645 if (exploded_dn) {
4646 ldap_value_free(exploded_dn);
4647 return ADS_SUCCESS;
4651 ou_string = ads_ou_string(ads, *account_ou);
4652 if (!ou_string) {
4653 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4656 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4657 ads->config.bind_path);
4658 SAFE_FREE(ou_string);
4660 if (!name) {
4661 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4664 exploded_dn = ldap_explode_dn(name, 0);
4665 if (!exploded_dn) {
4666 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4668 ldap_value_free(exploded_dn);
4670 *account_ou = name;
4671 return ADS_SUCCESS;
4674 #endif