libsmbclient: Read the file type from the server with posix enabled
[Samba.git] / source3 / libads / ldap.c
blob740bc6bb43458ff068c5e5584e75ae8aae8c11c0
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 the 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\n", 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!\n"));
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!\n"));
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!\n"));
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 DistinguishedName 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!\n"));
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!\n"));
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 initialized 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\n",
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 = NULL;
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 if (attr == NULL) {
3180 break;
3183 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3184 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3185 range_attr = attr;
3186 break;
3188 ldap_memfree(attr);
3190 if (!range_attr) {
3191 ber_free(ptr, 0);
3192 /* nothing here - this field is just empty */
3193 *more_strings = False;
3194 return NULL;
3197 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3198 &range_start, &range_end) == 2) {
3199 *more_strings = True;
3200 } else {
3201 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3202 &range_start) == 1) {
3203 *more_strings = False;
3204 } else {
3205 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3206 range_attr));
3207 ldap_memfree(range_attr);
3208 *more_strings = False;
3209 return NULL;
3213 if ((*num_strings) != range_start) {
3214 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3215 " - aborting range retrieval\n",
3216 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3217 ldap_memfree(range_attr);
3218 *more_strings = False;
3219 return NULL;
3222 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3224 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3225 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3226 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3227 range_attr, (unsigned long int)range_end - range_start + 1,
3228 (unsigned long int)num_new_strings));
3229 ldap_memfree(range_attr);
3230 *more_strings = False;
3231 return NULL;
3234 strings = talloc_realloc(mem_ctx, current_strings, char *,
3235 *num_strings + num_new_strings);
3237 if (strings == NULL) {
3238 ldap_memfree(range_attr);
3239 *more_strings = False;
3240 return NULL;
3243 if (new_strings && num_new_strings) {
3244 memcpy(&strings[*num_strings], new_strings,
3245 sizeof(*new_strings) * num_new_strings);
3248 (*num_strings) += num_new_strings;
3250 if (*more_strings) {
3251 *next_attribute = talloc_asprintf(mem_ctx,
3252 "%s;range=%d-*",
3253 field,
3254 (int)*num_strings);
3256 if (!*next_attribute) {
3257 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3258 ldap_memfree(range_attr);
3259 *more_strings = False;
3260 return NULL;
3264 ldap_memfree(range_attr);
3266 return strings;
3270 * pull a single uint32_t from a ADS result
3271 * @param ads connection to ads server
3272 * @param msg Results of search
3273 * @param field Attribute to retrieve
3274 * @param v Pointer to int to store result
3275 * @return boolean indicating success
3277 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3278 uint32_t *v)
3280 char **values;
3282 values = ldap_get_values(ads->ldap.ld, msg, field);
3283 if (!values)
3284 return False;
3285 if (!values[0]) {
3286 ldap_value_free(values);
3287 return False;
3290 *v = atoi(values[0]);
3291 ldap_value_free(values);
3292 return True;
3296 * pull a single objectGUID from an ADS result
3297 * @param ads connection to ADS server
3298 * @param msg results of search
3299 * @param guid 37-byte area to receive text guid
3300 * @return boolean indicating success
3302 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3304 DATA_BLOB blob;
3305 NTSTATUS status;
3307 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3308 &blob)) {
3309 return false;
3312 status = GUID_from_ndr_blob(&blob, guid);
3313 talloc_free(blob.data);
3314 return NT_STATUS_IS_OK(status);
3319 * pull a single struct dom_sid from a ADS result
3320 * @param ads connection to ads server
3321 * @param msg Results of search
3322 * @param field Attribute to retrieve
3323 * @param sid Pointer to sid to store result
3324 * @return boolean indicating success
3326 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3327 struct dom_sid *sid)
3329 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3333 * pull an array of struct dom_sids from a ADS result
3334 * @param ads connection to ads server
3335 * @param mem_ctx TALLOC_CTX for allocating sid array
3336 * @param msg Results of search
3337 * @param field Attribute to retrieve
3338 * @param sids pointer to sid array to allocate
3339 * @return the count of SIDs pulled
3341 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3342 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3344 struct berval **values;
3345 int count, i;
3347 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3349 if (!values)
3350 return 0;
3352 for (i=0; values[i]; i++)
3353 /* nop */ ;
3355 if (i) {
3356 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3357 if (!(*sids)) {
3358 ldap_value_free_len(values);
3359 return 0;
3361 } else {
3362 (*sids) = NULL;
3365 count = 0;
3366 for (i=0; values[i]; i++) {
3367 ssize_t ret;
3368 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3369 values[i]->bv_len, &(*sids)[count]);
3370 if (ret != -1) {
3371 struct dom_sid_buf buf;
3372 DBG_DEBUG("pulling SID: %s\n",
3373 dom_sid_str_buf(&(*sids)[count], &buf));
3374 count++;
3378 ldap_value_free_len(values);
3379 return count;
3383 * pull a struct security_descriptor from a ADS result
3384 * @param ads connection to ads server
3385 * @param mem_ctx TALLOC_CTX for allocating sid array
3386 * @param msg Results of search
3387 * @param field Attribute to retrieve
3388 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3389 * @return boolean indicating success
3391 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3392 LDAPMessage *msg, const char *field,
3393 struct security_descriptor **sd)
3395 struct berval **values;
3396 bool ret = true;
3398 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3400 if (!values) return false;
3402 if (values[0]) {
3403 NTSTATUS status;
3404 status = unmarshall_sec_desc(mem_ctx,
3405 (uint8_t *)values[0]->bv_val,
3406 values[0]->bv_len, sd);
3407 if (!NT_STATUS_IS_OK(status)) {
3408 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3409 nt_errstr(status)));
3410 ret = false;
3414 ldap_value_free_len(values);
3415 return ret;
3419 * in order to support usernames longer than 21 characters we need to
3420 * use both the sAMAccountName and the userPrincipalName attributes
3421 * It seems that not all users have the userPrincipalName attribute set
3423 * @param ads connection to ads server
3424 * @param mem_ctx TALLOC_CTX for allocating sid array
3425 * @param msg Results of search
3426 * @return the username
3428 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3429 LDAPMessage *msg)
3431 #if 0 /* JERRY */
3432 char *ret, *p;
3434 /* lookup_name() only works on the sAMAccountName to
3435 returning the username portion of userPrincipalName
3436 breaks winbindd_getpwnam() */
3438 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3439 if (ret && (p = strchr_m(ret, '@'))) {
3440 *p = 0;
3441 return ret;
3443 #endif
3444 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3449 * find the update serial number - this is the core of the ldap cache
3450 * @param ads connection to ads server
3451 * @param ads connection to ADS server
3452 * @param usn Pointer to retrieved update serial number
3453 * @return status of search
3455 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3457 const char *attrs[] = {"highestCommittedUSN", NULL};
3458 ADS_STATUS status;
3459 LDAPMessage *res;
3461 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3462 if (!ADS_ERR_OK(status))
3463 return status;
3465 if (ads_count_replies(ads, res) != 1) {
3466 ads_msgfree(ads, res);
3467 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3470 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3471 ads_msgfree(ads, res);
3472 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3475 ads_msgfree(ads, res);
3476 return ADS_SUCCESS;
3479 /* parse a ADS timestring - typical string is
3480 '20020917091222.0Z0' which means 09:12.22 17th September
3481 2002, timezone 0 */
3482 static time_t ads_parse_time(const char *str)
3484 struct tm tm;
3486 ZERO_STRUCT(tm);
3488 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3489 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3490 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3491 return 0;
3493 tm.tm_year -= 1900;
3494 tm.tm_mon -= 1;
3496 return timegm(&tm);
3499 /********************************************************************
3500 ********************************************************************/
3502 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3504 const char *attrs[] = {"currentTime", NULL};
3505 ADS_STATUS status;
3506 LDAPMessage *res;
3507 char *timestr;
3508 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3509 ADS_STRUCT *ads_s = ads;
3511 /* establish a new ldap tcp session if necessary */
3513 if ( !ads->ldap.ld ) {
3515 * ADS_STRUCT may be being reused after a
3516 * DC lookup, so ads->ldap.ss may already have a
3517 * good address. If not, re-initialize the passed-in
3518 * ADS_STRUCT with the given server.XXXX parameters.
3520 * Note that this doesn't depend on
3521 * ads->server.ldap_server != NULL,
3522 * as the case where ads->server.ldap_server==NULL and
3523 * ads->ldap.ss != zero_address is precisely the DC
3524 * lookup case where ads->ldap.ss was found by going
3525 * through ads_find_dc() again we want to avoid repeating.
3527 if (is_zero_addr(&ads->ldap.ss)) {
3528 ads_s = ads_init(tmp_ctx,
3529 ads->server.realm,
3530 ads->server.workgroup,
3531 ads->server.ldap_server,
3532 ADS_SASL_PLAIN );
3533 if (ads_s == NULL) {
3534 status = ADS_ERROR(LDAP_NO_MEMORY);
3535 goto done;
3540 * Reset ads->config.flags as it can contain the flags
3541 * returned by the previous CLDAP ping when reusing the struct.
3543 ads_s->config.flags = 0;
3545 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3546 status = ads_connect( ads_s );
3547 if ( !ADS_ERR_OK(status))
3548 goto done;
3551 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3552 if (!ADS_ERR_OK(status)) {
3553 goto done;
3556 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3557 if (!timestr) {
3558 ads_msgfree(ads_s, res);
3559 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3560 goto done;
3563 /* but save the time and offset in the original ADS_STRUCT */
3565 ads->config.current_time = ads_parse_time(timestr);
3567 if (ads->config.current_time != 0) {
3568 ads->auth.time_offset = ads->config.current_time - time(NULL);
3569 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3572 ads_msgfree(ads, res);
3574 status = ADS_SUCCESS;
3576 done:
3577 TALLOC_FREE(tmp_ctx);
3579 return status;
3582 /********************************************************************
3583 ********************************************************************/
3585 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3587 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3588 const char *attrs[] = {"domainFunctionality", NULL};
3589 ADS_STATUS status;
3590 LDAPMessage *res;
3591 ADS_STRUCT *ads_s = ads;
3593 *val = DS_DOMAIN_FUNCTION_2000;
3595 /* establish a new ldap tcp session if necessary */
3597 if ( !ads->ldap.ld ) {
3599 * ADS_STRUCT may be being reused after a
3600 * DC lookup, so ads->ldap.ss may already have a
3601 * good address. If not, re-initialize the passed-in
3602 * ADS_STRUCT with the given server.XXXX parameters.
3604 * Note that this doesn't depend on
3605 * ads->server.ldap_server != NULL,
3606 * as the case where ads->server.ldap_server==NULL and
3607 * ads->ldap.ss != zero_address is precisely the DC
3608 * lookup case where ads->ldap.ss was found by going
3609 * through ads_find_dc() again we want to avoid repeating.
3611 if (is_zero_addr(&ads->ldap.ss)) {
3612 ads_s = ads_init(tmp_ctx,
3613 ads->server.realm,
3614 ads->server.workgroup,
3615 ads->server.ldap_server,
3616 ADS_SASL_PLAIN );
3617 if (ads_s == NULL ) {
3618 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3619 goto done;
3624 * Reset ads->config.flags as it can contain the flags
3625 * returned by the previous CLDAP ping when reusing the struct.
3627 ads_s->config.flags = 0;
3629 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3630 status = ads_connect( ads_s );
3631 if ( !ADS_ERR_OK(status))
3632 goto done;
3635 /* If the attribute does not exist assume it is a Windows 2000
3636 functional domain */
3638 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3639 if (!ADS_ERR_OK(status)) {
3640 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3641 status = ADS_SUCCESS;
3643 goto done;
3646 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3647 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3649 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3652 ads_msgfree(ads_s, res);
3654 done:
3655 TALLOC_FREE(tmp_ctx);
3657 return status;
3661 * find the domain sid for our domain
3662 * @param ads connection to ads server
3663 * @param sid Pointer to domain sid
3664 * @return status of search
3666 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3668 const char *attrs[] = {"objectSid", NULL};
3669 LDAPMessage *res;
3670 ADS_STATUS rc;
3672 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3673 attrs, &res);
3674 if (!ADS_ERR_OK(rc)) return rc;
3675 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3676 ads_msgfree(ads, res);
3677 return ADS_ERROR_SYSTEM(ENOENT);
3679 ads_msgfree(ads, res);
3681 return ADS_SUCCESS;
3685 * find our site name
3686 * @param ads connection to ads server
3687 * @param mem_ctx Pointer to talloc context
3688 * @param site_name Pointer to the sitename
3689 * @return status of search
3691 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3693 ADS_STATUS status;
3694 LDAPMessage *res;
3695 const char *dn, *service_name;
3696 const char *attrs[] = { "dsServiceName", NULL };
3698 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3699 if (!ADS_ERR_OK(status)) {
3700 return status;
3703 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3704 if (service_name == NULL) {
3705 ads_msgfree(ads, res);
3706 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3709 ads_msgfree(ads, res);
3711 /* go up three levels */
3712 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3713 if (dn == NULL) {
3714 return ADS_ERROR(LDAP_NO_MEMORY);
3717 *site_name = talloc_strdup(mem_ctx, dn);
3718 if (*site_name == NULL) {
3719 return ADS_ERROR(LDAP_NO_MEMORY);
3722 return status;
3724 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3729 * find the site dn where a machine resides
3730 * @param ads connection to ads server
3731 * @param mem_ctx Pointer to talloc context
3732 * @param computer_name name of the machine
3733 * @param site_name Pointer to the sitename
3734 * @return status of search
3736 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3738 ADS_STATUS status;
3739 LDAPMessage *res;
3740 const char *parent, *filter;
3741 char *config_context = NULL;
3742 char *dn;
3744 /* shortcut a query */
3745 if (strequal(computer_name, ads->config.ldap_server_name)) {
3746 return ads_site_dn(ads, mem_ctx, site_dn);
3749 status = ads_config_path(ads, mem_ctx, &config_context);
3750 if (!ADS_ERR_OK(status)) {
3751 return status;
3754 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3755 if (filter == NULL) {
3756 return ADS_ERROR(LDAP_NO_MEMORY);
3759 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3760 filter, NULL, &res);
3761 if (!ADS_ERR_OK(status)) {
3762 return status;
3765 if (ads_count_replies(ads, res) != 1) {
3766 ads_msgfree(ads, res);
3767 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3770 dn = ads_get_dn(ads, mem_ctx, res);
3771 if (dn == NULL) {
3772 ads_msgfree(ads, res);
3773 return ADS_ERROR(LDAP_NO_MEMORY);
3776 /* go up three levels */
3777 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3778 if (parent == NULL) {
3779 ads_msgfree(ads, res);
3780 TALLOC_FREE(dn);
3781 return ADS_ERROR(LDAP_NO_MEMORY);
3784 *site_dn = talloc_strdup(mem_ctx, parent);
3785 if (*site_dn == NULL) {
3786 ads_msgfree(ads, res);
3787 TALLOC_FREE(dn);
3788 return ADS_ERROR(LDAP_NO_MEMORY);
3791 TALLOC_FREE(dn);
3792 ads_msgfree(ads, res);
3794 return status;
3798 * get the upn suffixes for a domain
3799 * @param ads connection to ads server
3800 * @param mem_ctx Pointer to talloc context
3801 * @param suffixes Pointer to an array of suffixes
3802 * @param num_suffixes Pointer to the number of suffixes
3803 * @return status of search
3805 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3807 ADS_STATUS status;
3808 LDAPMessage *res;
3809 const char *base;
3810 char *config_context = NULL;
3811 const char *attrs[] = { "uPNSuffixes", NULL };
3813 status = ads_config_path(ads, mem_ctx, &config_context);
3814 if (!ADS_ERR_OK(status)) {
3815 return status;
3818 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3819 if (base == NULL) {
3820 return ADS_ERROR(LDAP_NO_MEMORY);
3823 status = ads_search_dn(ads, &res, base, attrs);
3824 if (!ADS_ERR_OK(status)) {
3825 return status;
3828 if (ads_count_replies(ads, res) != 1) {
3829 ads_msgfree(ads, res);
3830 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3833 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3834 if ((*suffixes) == NULL) {
3835 ads_msgfree(ads, res);
3836 return ADS_ERROR(LDAP_NO_MEMORY);
3839 ads_msgfree(ads, res);
3841 return status;
3845 * get the joinable ous for a domain
3846 * @param ads connection to ads server
3847 * @param mem_ctx Pointer to talloc context
3848 * @param ous Pointer to an array of ous
3849 * @param num_ous Pointer to the number of ous
3850 * @return status of search
3852 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3853 TALLOC_CTX *mem_ctx,
3854 char ***ous,
3855 size_t *num_ous)
3857 ADS_STATUS status;
3858 LDAPMessage *res = NULL;
3859 LDAPMessage *msg = NULL;
3860 const char *attrs[] = { "dn", NULL };
3861 int count = 0;
3863 status = ads_search(ads, &res,
3864 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3865 attrs);
3866 if (!ADS_ERR_OK(status)) {
3867 return status;
3870 count = ads_count_replies(ads, res);
3871 if (count < 1) {
3872 ads_msgfree(ads, res);
3873 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3876 for (msg = ads_first_entry(ads, res); msg;
3877 msg = ads_next_entry(ads, msg)) {
3878 const char **p = discard_const_p(const char *, *ous);
3879 char *dn = NULL;
3881 dn = ads_get_dn(ads, talloc_tos(), msg);
3882 if (!dn) {
3883 ads_msgfree(ads, res);
3884 return ADS_ERROR(LDAP_NO_MEMORY);
3887 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3888 TALLOC_FREE(dn);
3889 ads_msgfree(ads, res);
3890 return ADS_ERROR(LDAP_NO_MEMORY);
3893 TALLOC_FREE(dn);
3894 *ous = discard_const_p(char *, p);
3897 ads_msgfree(ads, res);
3899 return status;
3904 * pull a struct dom_sid from an extended dn string
3905 * @param mem_ctx TALLOC_CTX
3906 * @param extended_dn string
3907 * @param flags string type of extended_dn
3908 * @param sid pointer to a struct dom_sid
3909 * @return NT_STATUS_OK on success,
3910 * NT_INVALID_PARAMETER on error,
3911 * NT_STATUS_NOT_FOUND if no SID present
3913 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3914 const char *extended_dn,
3915 enum ads_extended_dn_flags flags,
3916 struct dom_sid *sid)
3918 char *p, *q, *dn;
3920 if (!extended_dn) {
3921 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3924 /* otherwise extended_dn gets stripped off */
3925 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3926 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3929 * ADS_EXTENDED_DN_HEX_STRING:
3930 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3932 * ADS_EXTENDED_DN_STRING (only with w2k3):
3933 * <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
3935 * Object with no SID, such as an Exchange Public Folder
3936 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3939 p = strchr(dn, ';');
3940 if (!p) {
3941 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3944 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3945 DEBUG(5,("No SID present in extended dn\n"));
3946 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3949 p += strlen(";<SID=");
3951 q = strchr(p, '>');
3952 if (!q) {
3953 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3956 *q = '\0';
3958 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3960 switch (flags) {
3962 case ADS_EXTENDED_DN_STRING:
3963 if (!string_to_sid(sid, p)) {
3964 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3966 break;
3967 case ADS_EXTENDED_DN_HEX_STRING: {
3968 ssize_t ret;
3969 fstring buf;
3970 size_t buf_len;
3972 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3973 if (buf_len == 0) {
3974 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3977 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3978 if (ret == -1) {
3979 DEBUG(10,("failed to parse sid\n"));
3980 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3982 break;
3984 default:
3985 DEBUG(10,("unknown extended dn format\n"));
3986 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3989 return ADS_ERROR_NT(NT_STATUS_OK);
3992 /********************************************************************
3993 ********************************************************************/
3995 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3997 LDAPMessage *res = NULL;
3998 ADS_STATUS status;
3999 int count = 0;
4000 char *name = NULL;
4002 status = ads_find_machine_acct(ads, &res, machine_name);
4003 if (!ADS_ERR_OK(status)) {
4004 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4005 lp_netbios_name()));
4006 goto out;
4009 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4010 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4011 goto out;
4014 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4015 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4018 out:
4019 ads_msgfree(ads, res);
4021 return name;
4024 /********************************************************************
4025 ********************************************************************/
4027 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4028 LDAPMessage *msg, size_t *num_values)
4030 const char *field = "msDS-AdditionalDnsHostName";
4031 struct berval **values = NULL;
4032 char **ret = NULL;
4033 size_t i, converted_size;
4036 * Windows DC implicitly adds a short name for each FQDN added to
4037 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4038 * suffix "\0$" which we should ignore (see bug #14406).
4041 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4042 if (values == NULL) {
4043 return NULL;
4046 *num_values = ldap_count_values_len(values);
4048 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4049 if (ret == NULL) {
4050 ldap_value_free_len(values);
4051 return NULL;
4054 for (i = 0; i < *num_values; i++) {
4055 ret[i] = NULL;
4056 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4057 values[i]->bv_val,
4058 strnlen(values[i]->bv_val,
4059 values[i]->bv_len),
4060 &ret[i], &converted_size)) {
4061 ldap_value_free_len(values);
4062 return NULL;
4065 ret[i] = NULL;
4067 ldap_value_free_len(values);
4068 return ret;
4071 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4072 ADS_STRUCT *ads,
4073 const char *machine_name,
4074 char ***hostnames_array,
4075 size_t *num_hostnames)
4077 ADS_STATUS status;
4078 LDAPMessage *res = NULL;
4079 int count;
4081 status = ads_find_machine_acct(ads,
4082 &res,
4083 machine_name);
4084 if (!ADS_ERR_OK(status)) {
4085 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4086 machine_name));
4087 return status;
4090 count = ads_count_replies(ads, res);
4091 if (count != 1) {
4092 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4093 goto done;
4096 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4097 if (*hostnames_array == NULL) {
4098 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4099 machine_name));
4100 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4101 goto done;
4104 done:
4105 ads_msgfree(ads, res);
4107 return status;
4110 /********************************************************************
4111 ********************************************************************/
4113 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4115 LDAPMessage *res = NULL;
4116 ADS_STATUS status;
4117 int count = 0;
4118 char *name = NULL;
4120 status = ads_find_machine_acct(ads, &res, machine_name);
4121 if (!ADS_ERR_OK(status)) {
4122 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4123 lp_netbios_name()));
4124 goto out;
4127 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4128 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4129 goto out;
4132 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4133 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4136 out:
4137 ads_msgfree(ads, res);
4139 return name;
4142 /********************************************************************
4143 ********************************************************************/
4145 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4147 LDAPMessage *res = NULL;
4148 ADS_STATUS status;
4149 int count = 0;
4150 char *name = NULL;
4151 bool ok = false;
4153 status = ads_find_machine_acct(ads, &res, machine_name);
4154 if (!ADS_ERR_OK(status)) {
4155 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4156 lp_netbios_name()));
4157 goto out;
4160 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4161 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4162 goto out;
4165 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4166 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4169 out:
4170 ads_msgfree(ads, res);
4171 if (name != NULL) {
4172 ok = (strlen(name) > 0);
4174 TALLOC_FREE(name);
4175 return ok;
4178 #if 0
4180 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4183 * Join a machine to a realm
4184 * Creates the machine account and sets the machine password
4185 * @param ads connection to ads server
4186 * @param machine name of host to add
4187 * @param org_unit Organizational unit to place machine in
4188 * @return status of join
4190 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4191 uint32_t account_type, const char *org_unit)
4193 ADS_STATUS status;
4194 LDAPMessage *res = NULL;
4195 char *machine;
4197 /* machine name must be lowercase */
4198 machine = SMB_STRDUP(machine_name);
4199 strlower_m(machine);
4202 status = ads_find_machine_acct(ads, (void **)&res, machine);
4203 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4204 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4205 status = ads_leave_realm(ads, machine);
4206 if (!ADS_ERR_OK(status)) {
4207 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4208 machine, ads->config.realm));
4209 return status;
4213 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4214 if (!ADS_ERR_OK(status)) {
4215 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4216 SAFE_FREE(machine);
4217 return status;
4220 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4221 if (!ADS_ERR_OK(status)) {
4222 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4223 SAFE_FREE(machine);
4224 return status;
4227 SAFE_FREE(machine);
4228 ads_msgfree(ads, res);
4230 return status;
4232 #endif
4235 * Delete a machine from the realm
4236 * @param ads connection to ads server
4237 * @param hostname Machine to remove
4238 * @return status of delete
4240 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4242 ADS_STATUS status;
4243 void *msg;
4244 LDAPMessage *res;
4245 char *hostnameDN, *host;
4246 int rc;
4247 LDAPControl ldap_control;
4248 LDAPControl * pldap_control[2] = {NULL, NULL};
4250 pldap_control[0] = &ldap_control;
4251 memset(&ldap_control, 0, sizeof(LDAPControl));
4252 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4254 /* hostname must be lowercase */
4255 host = SMB_STRDUP(hostname);
4256 if (!strlower_m(host)) {
4257 SAFE_FREE(host);
4258 return ADS_ERROR_SYSTEM(EINVAL);
4261 status = ads_find_machine_acct(ads, &res, host);
4262 if (!ADS_ERR_OK(status)) {
4263 DEBUG(0, ("Host account for %s does not exist.\n", host));
4264 SAFE_FREE(host);
4265 return status;
4268 msg = ads_first_entry(ads, res);
4269 if (!msg) {
4270 SAFE_FREE(host);
4271 return ADS_ERROR_SYSTEM(ENOENT);
4274 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4275 if (hostnameDN == NULL) {
4276 SAFE_FREE(host);
4277 return ADS_ERROR_SYSTEM(ENOENT);
4280 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4281 if (rc) {
4282 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4283 }else {
4284 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4287 if (rc != LDAP_SUCCESS) {
4288 const char *attrs[] = { "cn", NULL };
4289 LDAPMessage *msg_sub;
4291 /* we only search with scope ONE, we do not expect any further
4292 * objects to be created deeper */
4294 status = ads_do_search_retry(ads, hostnameDN,
4295 LDAP_SCOPE_ONELEVEL,
4296 "(objectclass=*)", attrs, &res);
4298 if (!ADS_ERR_OK(status)) {
4299 SAFE_FREE(host);
4300 TALLOC_FREE(hostnameDN);
4301 return status;
4304 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4305 msg_sub = ads_next_entry(ads, msg_sub)) {
4307 char *dn = NULL;
4309 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4310 SAFE_FREE(host);
4311 TALLOC_FREE(hostnameDN);
4312 return ADS_ERROR(LDAP_NO_MEMORY);
4315 status = ads_del_dn(ads, dn);
4316 if (!ADS_ERR_OK(status)) {
4317 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4318 SAFE_FREE(host);
4319 TALLOC_FREE(dn);
4320 TALLOC_FREE(hostnameDN);
4321 return status;
4324 TALLOC_FREE(dn);
4327 /* there should be no subordinate objects anymore */
4328 status = ads_do_search_retry(ads, hostnameDN,
4329 LDAP_SCOPE_ONELEVEL,
4330 "(objectclass=*)", attrs, &res);
4332 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4333 SAFE_FREE(host);
4334 TALLOC_FREE(hostnameDN);
4335 return status;
4338 /* delete hostnameDN now */
4339 status = ads_del_dn(ads, hostnameDN);
4340 if (!ADS_ERR_OK(status)) {
4341 SAFE_FREE(host);
4342 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4343 TALLOC_FREE(hostnameDN);
4344 return status;
4348 TALLOC_FREE(hostnameDN);
4350 status = ads_find_machine_acct(ads, &res, host);
4351 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4352 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4353 DEBUG(3, ("Failed to remove host account.\n"));
4354 SAFE_FREE(host);
4355 return status;
4358 SAFE_FREE(host);
4359 return ADS_SUCCESS;
4363 * pull all token-sids from an LDAP dn
4364 * @param ads connection to ads server
4365 * @param mem_ctx TALLOC_CTX for allocating sid array
4366 * @param dn of LDAP object
4367 * @param user_sid pointer to struct dom_sid (objectSid)
4368 * @param primary_group_sid pointer to struct dom_sid (self composed)
4369 * @param sids pointer to sid array to allocate
4370 * @param num_sids counter of SIDs pulled
4371 * @return status of token query
4373 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4374 TALLOC_CTX *mem_ctx,
4375 const char *dn,
4376 struct dom_sid *user_sid,
4377 struct dom_sid *primary_group_sid,
4378 struct dom_sid **sids,
4379 size_t *num_sids)
4381 ADS_STATUS status;
4382 LDAPMessage *res = NULL;
4383 int count = 0;
4384 size_t tmp_num_sids;
4385 struct dom_sid *tmp_sids;
4386 struct dom_sid tmp_user_sid;
4387 struct dom_sid tmp_primary_group_sid;
4388 uint32_t pgid;
4389 const char *attrs[] = {
4390 "objectSid",
4391 "tokenGroups",
4392 "primaryGroupID",
4393 NULL
4396 status = ads_search_retry_dn(ads, &res, dn, attrs);
4397 if (!ADS_ERR_OK(status)) {
4398 return status;
4401 count = ads_count_replies(ads, res);
4402 if (count != 1) {
4403 ads_msgfree(ads, res);
4404 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4407 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4408 ads_msgfree(ads, res);
4409 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4412 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4413 ads_msgfree(ads, res);
4414 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4418 /* hack to compose the primary group sid without knowing the
4419 * domsid */
4421 struct dom_sid domsid;
4423 sid_copy(&domsid, &tmp_user_sid);
4425 if (!sid_split_rid(&domsid, NULL)) {
4426 ads_msgfree(ads, res);
4427 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4430 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4431 ads_msgfree(ads, res);
4432 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4436 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4438 if (tmp_num_sids == 0 || !tmp_sids) {
4439 ads_msgfree(ads, res);
4440 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4443 if (num_sids) {
4444 *num_sids = tmp_num_sids;
4447 if (sids) {
4448 *sids = tmp_sids;
4451 if (user_sid) {
4452 *user_sid = tmp_user_sid;
4455 if (primary_group_sid) {
4456 *primary_group_sid = tmp_primary_group_sid;
4459 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4461 ads_msgfree(ads, res);
4462 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4466 * Find a sAMAccountName in LDAP
4467 * @param ads connection to ads server
4468 * @param mem_ctx TALLOC_CTX for allocating sid array
4469 * @param samaccountname to search
4470 * @param uac_ret uint32_t pointer userAccountControl attribute value
4471 * @param dn_ret pointer to dn
4472 * @return status of token query
4474 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4475 TALLOC_CTX *mem_ctx,
4476 const char *samaccountname,
4477 uint32_t *uac_ret,
4478 const char **dn_ret)
4480 ADS_STATUS status;
4481 const char *attrs[] = { "userAccountControl", NULL };
4482 const char *filter;
4483 LDAPMessage *res = NULL;
4484 char *dn = NULL;
4485 uint32_t uac = 0;
4487 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4488 samaccountname);
4489 if (filter == NULL) {
4490 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4491 goto out;
4494 status = ads_do_search_all(ads, ads->config.bind_path,
4495 LDAP_SCOPE_SUBTREE,
4496 filter, attrs, &res);
4498 if (!ADS_ERR_OK(status)) {
4499 goto out;
4502 if (ads_count_replies(ads, res) != 1) {
4503 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4504 goto out;
4507 dn = ads_get_dn(ads, talloc_tos(), res);
4508 if (dn == NULL) {
4509 status = ADS_ERROR(LDAP_NO_MEMORY);
4510 goto out;
4513 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4514 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4515 goto out;
4518 if (uac_ret) {
4519 *uac_ret = uac;
4522 if (dn_ret) {
4523 *dn_ret = talloc_strdup(mem_ctx, dn);
4524 if (!*dn_ret) {
4525 status = ADS_ERROR(LDAP_NO_MEMORY);
4526 goto out;
4529 out:
4530 TALLOC_FREE(dn);
4531 ads_msgfree(ads, res);
4533 return status;
4537 * find our configuration path
4538 * @param ads connection to ads server
4539 * @param mem_ctx Pointer to talloc context
4540 * @param config_path Pointer to the config path
4541 * @return status of search
4543 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4544 TALLOC_CTX *mem_ctx,
4545 char **config_path)
4547 ADS_STATUS status;
4548 LDAPMessage *res = NULL;
4549 const char *config_context = NULL;
4550 const char *attrs[] = { "configurationNamingContext", NULL };
4552 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4553 "(objectclass=*)", attrs, &res);
4554 if (!ADS_ERR_OK(status)) {
4555 return status;
4558 config_context = ads_pull_string(ads, mem_ctx, res,
4559 "configurationNamingContext");
4560 ads_msgfree(ads, res);
4561 if (!config_context) {
4562 return ADS_ERROR(LDAP_NO_MEMORY);
4565 if (config_path) {
4566 *config_path = talloc_strdup(mem_ctx, config_context);
4567 if (!*config_path) {
4568 return ADS_ERROR(LDAP_NO_MEMORY);
4572 return ADS_ERROR(LDAP_SUCCESS);
4576 * find the displayName of an extended right
4577 * @param ads connection to ads server
4578 * @param config_path The config path
4579 * @param mem_ctx Pointer to talloc context
4580 * @param GUID struct of the rightsGUID
4581 * @return status of search
4583 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4584 const char *config_path,
4585 TALLOC_CTX *mem_ctx,
4586 const struct GUID *rights_guid)
4588 ADS_STATUS rc;
4589 LDAPMessage *res = NULL;
4590 char *expr = NULL;
4591 const char *attrs[] = { "displayName", NULL };
4592 const char *result = NULL;
4593 const char *path;
4595 if (!ads || !mem_ctx || !rights_guid) {
4596 goto done;
4599 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4600 GUID_string(mem_ctx, rights_guid));
4601 if (!expr) {
4602 goto done;
4605 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4606 if (!path) {
4607 goto done;
4610 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4611 expr, attrs, &res);
4612 if (!ADS_ERR_OK(rc)) {
4613 goto done;
4616 if (ads_count_replies(ads, res) != 1) {
4617 goto done;
4620 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4622 done:
4623 ads_msgfree(ads, res);
4624 return result;
4628 * verify or build and verify an account ou
4629 * @param mem_ctx Pointer to talloc context
4630 * @param ads connection to ads server
4631 * @param account_ou
4632 * @return status of search
4635 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4636 ADS_STRUCT *ads,
4637 const char **account_ou)
4639 char **exploded_dn;
4640 const char *name;
4641 char *ou_string;
4643 if (account_ou == NULL) {
4644 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4647 if (*account_ou != NULL) {
4648 exploded_dn = ldap_explode_dn(*account_ou, 0);
4649 if (exploded_dn) {
4650 ldap_value_free(exploded_dn);
4651 return ADS_SUCCESS;
4655 ou_string = ads_ou_string(ads, *account_ou);
4656 if (!ou_string) {
4657 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4660 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4661 ads->config.bind_path);
4662 SAFE_FREE(ou_string);
4664 if (!name) {
4665 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4668 exploded_dn = ldap_explode_dn(name, 0);
4669 if (!exploded_dn) {
4670 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4672 ldap_value_free(exploded_dn);
4674 *account_ou = name;
4675 return ADS_SUCCESS;
4678 #endif