libsmb: Remove unused setup_stat_from_stat_ex()
[Samba.git] / source3 / libads / ldap.c
blobe597f78597f007b08309b69348ad36ed989653ce
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 DBG_DEBUG("ldap_search: base => [%s], filter => [%s], scope => [%d]\n",
158 base,
159 filter,
160 scope);
162 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
163 gotalarm = 0;
165 if (to) {
166 timeout.tv_sec = to;
167 timeout.tv_usec = 0;
168 timeout_ptr = &timeout;
170 /* Setup alarm timeout. */
171 CatchSignal(SIGALRM, gotalarm_sig);
172 /* Make the alarm time one second beyond
173 the timeout we're setting for the
174 remote search timeout, to allow that
175 to fire in preference. */
176 alarm(to+1);
177 /* End setup timeout. */
181 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
182 attrsonly, sctrls, cctrls, timeout_ptr,
183 sizelimit, res);
185 if (to) {
186 /* Teardown alarm timeout. */
187 CatchSignal(SIGALRM, SIG_IGN);
188 alarm(0);
191 if (gotalarm != 0)
192 return LDAP_TIMELIMIT_EXCEEDED;
195 * A bug in OpenLDAP means ldap_search_ext_s can return
196 * LDAP_SUCCESS but with a NULL res pointer. Cope with
197 * this. See bug #6279 for details. JRA.
200 if (*res == NULL) {
201 return LDAP_TIMELIMIT_EXCEEDED;
204 return result;
207 /**********************************************
208 Do client and server sitename match ?
209 **********************************************/
211 bool ads_sitename_match(ADS_STRUCT *ads)
213 if (ads->config.server_site_name == NULL &&
214 ads->config.client_site_name == NULL ) {
215 DEBUG(10,("ads_sitename_match: both null\n"));
216 return True;
218 if (ads->config.server_site_name &&
219 ads->config.client_site_name &&
220 strequal(ads->config.server_site_name,
221 ads->config.client_site_name)) {
222 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
223 return True;
225 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
226 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
227 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
228 return False;
231 /**********************************************
232 Is this the closest DC ?
233 **********************************************/
235 bool ads_closest_dc(ADS_STRUCT *ads)
237 if (ads->config.flags & NBT_SERVER_CLOSEST) {
238 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
239 return True;
242 /* not sure if this can ever happen */
243 if (ads_sitename_match(ads)) {
244 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
245 return True;
248 if (ads->config.client_site_name == NULL) {
249 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
250 return True;
253 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
254 ads->config.ldap_server_name));
256 return False;
259 static bool ads_fill_cldap_reply(ADS_STRUCT *ads,
260 bool gc,
261 const struct sockaddr_storage *ss,
262 const struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply)
264 TALLOC_CTX *frame = talloc_stackframe();
265 bool ret = false;
266 char addr[INET6_ADDRSTRLEN];
267 ADS_STATUS status;
268 char *dn;
270 print_sockaddr(addr, sizeof(addr), ss);
272 /* Check the CLDAP reply flags */
274 if (!(cldap_reply->server_type & NBT_SERVER_LDAP)) {
275 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
276 addr);
277 ret = false;
278 goto out;
281 /* Fill in the ads->config values */
283 ADS_TALLOC_CONST_FREE(ads->config.workgroup);
284 ADS_TALLOC_CONST_FREE(ads->config.realm);
285 ADS_TALLOC_CONST_FREE(ads->config.bind_path);
286 ADS_TALLOC_CONST_FREE(ads->config.ldap_server_name);
287 ADS_TALLOC_CONST_FREE(ads->config.server_site_name);
288 ADS_TALLOC_CONST_FREE(ads->config.client_site_name);
290 if (!check_cldap_reply_required_flags(cldap_reply->server_type,
291 ads->config.flags)) {
292 ret = false;
293 goto out;
296 ads->config.ldap_server_name = talloc_strdup(ads,
297 cldap_reply->pdc_dns_name);
298 if (ads->config.ldap_server_name == NULL) {
299 DBG_WARNING("Out of memory\n");
300 ret = false;
301 goto out;
304 ads->config.workgroup = talloc_strdup(ads, cldap_reply->domain_name);
305 if (ads->config.workgroup == NULL) {
306 DBG_WARNING("Out of memory\n");
307 ret = false;
308 goto out;
311 ads->config.realm = talloc_asprintf_strupper_m(ads,
312 "%s",
313 cldap_reply->dns_domain);
314 if (ads->config.realm == NULL) {
315 DBG_WARNING("Out of memory\n");
316 ret = false;
317 goto out;
320 status = ads_build_dn(ads->config.realm, ads, &dn);
321 if (!ADS_ERR_OK(status)) {
322 DBG_DEBUG("Failed to build bind path: %s\n",
323 ads_errstr(status));
324 ret = false;
325 goto out;
327 ads->config.bind_path = dn;
329 if (*cldap_reply->server_site) {
330 ads->config.server_site_name =
331 talloc_strdup(ads, cldap_reply->server_site);
332 if (ads->config.server_site_name == NULL) {
333 DBG_WARNING("Out of memory\n");
334 ret = false;
335 goto out;
339 if (*cldap_reply->client_site) {
340 ads->config.client_site_name =
341 talloc_strdup(ads, cldap_reply->client_site);
342 if (ads->config.client_site_name == NULL) {
343 DBG_WARNING("Out of memory\n");
344 ret = false;
345 goto out;
349 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
350 ads->ldap.ss = *ss;
352 /* Store our site name. */
353 sitename_store(cldap_reply->domain_name, cldap_reply->client_site);
354 sitename_store(cldap_reply->dns_domain, cldap_reply->client_site);
356 /* Leave this until last so that the flags are not clobbered */
357 ads->config.flags = cldap_reply->server_type;
359 ret = true;
361 out:
363 TALLOC_FREE(frame);
364 return ret;
368 try a connection to a given ldap server, returning True and setting the servers IP
369 in the ads struct if successful
371 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
372 struct sockaddr_storage *ss)
374 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply = {};
375 TALLOC_CTX *frame = talloc_stackframe();
376 bool ok;
377 char addr[INET6_ADDRSTRLEN] = { 0, };
379 if (ss == NULL) {
380 TALLOC_FREE(frame);
381 return false;
384 print_sockaddr(addr, sizeof(addr), ss);
386 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
387 addr, ads->server.realm);
389 ok = ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply);
390 if (!ok) {
391 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
392 addr, ads->server.realm);
393 TALLOC_FREE(frame);
394 return false;
397 ok = ads_fill_cldap_reply(ads, gc, ss, &cldap_reply);
398 if (!ok) {
399 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
400 addr, ads->server.realm);
401 TALLOC_FREE(frame);
402 return false;
405 TALLOC_FREE(frame);
406 return true;
409 /**********************************************************************
410 send a cldap ping to list of servers, one at a time, until one of
411 them answers it's an ldap server. Record success in the ADS_STRUCT.
412 Take note of and update negative connection cache.
413 **********************************************************************/
415 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
416 const char *domain,
417 struct samba_sockaddr *sa_list,
418 size_t count)
420 TALLOC_CTX *frame = talloc_stackframe();
421 struct timeval endtime = timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
422 uint32_t nt_version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
423 struct tsocket_address **ts_list = NULL;
424 const struct tsocket_address * const *ts_list_const = NULL;
425 struct samba_sockaddr **req_sa_list = NULL;
426 struct netlogon_samlogon_response **responses = NULL;
427 size_t num_requests = 0;
428 NTSTATUS status;
429 size_t i;
430 bool ok = false;
431 bool retry;
433 ts_list = talloc_zero_array(frame,
434 struct tsocket_address *,
435 count);
436 if (ts_list == NULL) {
437 TALLOC_FREE(frame);
438 return NT_STATUS_NO_MEMORY;
441 req_sa_list = talloc_zero_array(frame,
442 struct samba_sockaddr *,
443 count);
444 if (req_sa_list == NULL) {
445 TALLOC_FREE(frame);
446 return NT_STATUS_NO_MEMORY;
449 again:
451 * The retry loop is bound by the timeout
453 retry = false;
454 num_requests = 0;
456 for (i = 0; i < count; i++) {
457 char server[INET6_ADDRSTRLEN];
458 int ret;
460 if (is_zero_addr(&sa_list[i].u.ss)) {
461 continue;
464 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
466 status = check_negative_conn_cache(domain, server);
467 if (!NT_STATUS_IS_OK(status)) {
468 continue;
471 ret = tsocket_address_inet_from_strings(ts_list, "ip",
472 server, LDAP_PORT,
473 &ts_list[num_requests]);
474 if (ret != 0) {
475 status = map_nt_error_from_unix(errno);
476 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
477 server, nt_errstr(status));
478 TALLOC_FREE(frame);
479 return status;
482 req_sa_list[num_requests] = &sa_list[i];
483 num_requests += 1;
486 DBG_DEBUG("Try to create %zu netlogon connections for domain '%s' "
487 "(provided count of addresses was %zu).\n",
488 num_requests,
489 domain,
490 count);
492 if (num_requests == 0) {
493 status = NT_STATUS_NO_LOGON_SERVERS;
494 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
495 domain, num_requests, count, nt_errstr(status));
496 TALLOC_FREE(frame);
497 return status;
500 ts_list_const = (const struct tsocket_address * const *)ts_list;
502 status = cldap_multi_netlogon(frame,
503 ts_list_const, num_requests,
504 ads->server.realm, NULL,
505 nt_version,
506 1, endtime, &responses);
507 if (!NT_STATUS_IS_OK(status)) {
508 DBG_WARNING("cldap_multi_netlogon(realm=%s, num_requests=%zu) "
509 "for count[%zu] - %s\n",
510 ads->server.realm,
511 num_requests, count,
512 nt_errstr(status));
513 TALLOC_FREE(frame);
514 return NT_STATUS_NO_LOGON_SERVERS;
517 for (i = 0; i < num_requests; i++) {
518 struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply = NULL;
519 char server[INET6_ADDRSTRLEN];
521 if (responses[i] == NULL) {
522 continue;
525 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
527 if (responses[i]->ntver != NETLOGON_NT_VERSION_5EX) {
528 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
529 ads->server.realm,
530 responses[i]->ntver, server);
531 continue;
534 cldap_reply = &responses[i]->data.nt5_ex;
536 /* Returns ok only if it matches the correct server type */
537 ok = ads_fill_cldap_reply(ads,
538 false,
539 &req_sa_list[i]->u.ss,
540 cldap_reply);
541 if (ok) {
542 DBG_DEBUG("realm[%s]: selected %s => %s\n",
543 ads->server.realm,
544 server, cldap_reply->pdc_dns_name);
545 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
546 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
547 cldap_reply);
549 TALLOC_FREE(frame);
550 return NT_STATUS_OK;
553 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
554 ads->server.realm,
555 server, cldap_reply->pdc_dns_name);
556 if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
557 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
558 cldap_reply);
560 add_failed_connection_entry(domain, server,
561 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID);
562 retry = true;
565 if (retry) {
566 bool expired;
568 expired = timeval_expired(&endtime);
569 if (!expired) {
570 goto again;
574 /* keep track of failures as all were not suitable */
575 for (i = 0; i < num_requests; i++) {
576 char server[INET6_ADDRSTRLEN];
578 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
580 add_failed_connection_entry(domain, server,
581 NT_STATUS_UNSUCCESSFUL);
584 status = NT_STATUS_NO_LOGON_SERVERS;
585 DBG_WARNING("realm[%s] no valid response "
586 "num_requests[%zu] for count[%zu] - %s\n",
587 ads->server.realm,
588 num_requests, count, nt_errstr(status));
589 TALLOC_FREE(frame);
590 return NT_STATUS_NO_LOGON_SERVERS;
593 /***************************************************************************
594 resolve a name and perform an "ldap ping" using NetBIOS and related methods
595 ****************************************************************************/
597 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
598 const char *domain, const char *realm)
600 size_t i;
601 size_t count = 0;
602 struct samba_sockaddr *sa_list = NULL;
603 NTSTATUS status;
605 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
606 domain));
608 status = get_sorted_dc_list(talloc_tos(),
609 domain,
610 NULL,
611 &sa_list,
612 &count,
613 false);
614 if (!NT_STATUS_IS_OK(status)) {
615 return status;
618 /* remove servers which are known to be dead based on
619 the corresponding DNS method */
620 if (*realm) {
621 for (i = 0; i < count; ++i) {
622 char server[INET6_ADDRSTRLEN];
624 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
626 if(!NT_STATUS_IS_OK(
627 check_negative_conn_cache(realm, server))) {
628 /* Ensure we add the workgroup name for this
629 IP address as negative too. */
630 add_failed_connection_entry(
631 domain, server,
632 NT_STATUS_UNSUCCESSFUL);
637 status = cldap_ping_list(ads, domain, sa_list, count);
639 TALLOC_FREE(sa_list);
641 return status;
645 /**********************************************************************
646 resolve a name and perform an "ldap ping" using DNS
647 **********************************************************************/
649 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
650 const char *realm)
652 size_t count = 0;
653 struct samba_sockaddr *sa_list = NULL;
654 NTSTATUS status;
656 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
657 realm));
659 status = get_sorted_dc_list(talloc_tos(),
660 realm,
661 sitename,
662 &sa_list,
663 &count,
664 true);
665 if (!NT_STATUS_IS_OK(status)) {
666 TALLOC_FREE(sa_list);
667 return status;
670 status = cldap_ping_list(ads, realm, sa_list, count);
672 TALLOC_FREE(sa_list);
674 return status;
677 /**********************************************************************
678 Try to find an AD dc using our internal name resolution routines
679 Try the realm first and then the workgroup name if netbios is not
680 disabled
681 **********************************************************************/
683 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
685 const char *c_domain = "";
686 const char *c_realm;
687 bool use_own_domain = False;
688 char *sitename = NULL;
689 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
690 bool ok = false;
692 /* if the realm and workgroup are both empty, assume they are ours */
694 /* realm */
695 c_realm = ads->server.realm;
697 if (c_realm == NULL)
698 c_realm = "";
700 if (!*c_realm) {
701 /* special case where no realm and no workgroup means our own */
702 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
703 use_own_domain = True;
704 c_realm = lp_realm();
708 if (!lp_disable_netbios()) {
709 if (use_own_domain) {
710 c_domain = lp_workgroup();
711 } else {
712 c_domain = ads->server.workgroup;
713 if (!*c_realm && (!c_domain || !*c_domain)) {
714 c_domain = lp_workgroup();
718 if (!c_domain) {
719 c_domain = "";
723 if (!*c_realm && !*c_domain) {
724 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
725 "what to do\n"));
726 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
730 * In case of LDAP we use get_dc_name() as that
731 * creates the custom krb5.conf file
733 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
734 fstring srv_name;
735 struct sockaddr_storage ip_out;
737 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
738 " and falling back to domain '%s'\n",
739 c_realm, c_domain));
741 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
742 if (ok) {
743 if (is_zero_addr(&ip_out)) {
744 return NT_STATUS_NO_LOGON_SERVERS;
748 * we call ads_try_connect() to fill in the
749 * ads->config details
751 ok = ads_try_connect(ads, false, &ip_out);
752 if (ok) {
753 return NT_STATUS_OK;
757 return NT_STATUS_NO_LOGON_SERVERS;
760 if (*c_realm) {
761 sitename = sitename_fetch(talloc_tos(), c_realm);
762 status = resolve_and_ping_dns(ads, sitename, c_realm);
764 if (NT_STATUS_IS_OK(status)) {
765 TALLOC_FREE(sitename);
766 return status;
769 /* In case we failed to contact one of our closest DC on our
770 * site we
771 * need to try to find another DC, retry with a site-less SRV
772 * DNS query
773 * - Guenther */
775 if (sitename) {
776 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
777 "our site (%s), Trying to find another DC "
778 "for realm '%s' (domain '%s')\n",
779 sitename, c_realm, c_domain));
780 namecache_delete(c_realm, 0x1C);
781 status =
782 resolve_and_ping_dns(ads, NULL, c_realm);
784 if (NT_STATUS_IS_OK(status)) {
785 TALLOC_FREE(sitename);
786 return status;
790 TALLOC_FREE(sitename);
793 /* try netbios as fallback - if permitted,
794 or if configuration specifically requests it */
795 if (*c_domain) {
796 if (*c_realm) {
797 DEBUG(3, ("ads_find_dc: falling back to netbios "
798 "name resolution for domain '%s' (realm '%s')\n",
799 c_domain, c_realm));
802 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
803 if (NT_STATUS_IS_OK(status)) {
804 return status;
808 DEBUG(1, ("ads_find_dc: "
809 "name resolution for realm '%s' (domain '%s') failed: %s\n",
810 c_realm, c_domain, nt_errstr(status)));
811 return status;
814 * Connect to the LDAP server
815 * @param ads Pointer to an existing ADS_STRUCT
816 * @return status of connection
818 ADS_STATUS ads_connect(ADS_STRUCT *ads)
820 int version = LDAP_VERSION3;
821 ADS_STATUS status;
822 NTSTATUS ntstatus;
823 char addr[INET6_ADDRSTRLEN];
824 struct sockaddr_storage existing_ss;
825 bool tls = false;
826 bool start_tls = false;
828 zero_sockaddr(&existing_ss);
831 * ads_connect can be passed in a reused ADS_STRUCT
832 * with an existing non-zero ads->ldap.ss IP address
833 * that was stored by going through ads_find_dc()
834 * if ads->server.ldap_server was NULL.
836 * If ads->server.ldap_server is still NULL but
837 * the target address isn't the zero address, then
838 * store that address off off before zeroing out
839 * ads->ldap so we don't keep doing multiple calls
840 * to ads_find_dc() in the reuse case.
842 * If a caller wants a clean ADS_STRUCT they
843 * will TALLOC_FREE it and allocate a new one
844 * by calling ads_init(), which ensures
845 * ads->ldap.ss is a properly zero'ed out valid IP
846 * address.
848 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
849 /* Save off the address we previously found by ads_find_dc(). */
850 existing_ss = ads->ldap.ss;
853 ads_zero_ldap(ads);
854 ZERO_STRUCT(ads->ldap_tls_data);
855 ZERO_STRUCT(ads->ldap_wrap_data);
856 ads->ldap.last_attempt = time_mono(NULL);
857 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
859 /* try with a user specified server */
861 if (DEBUGLEVEL >= 11) {
862 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
863 DEBUG(11,("ads_connect: entering\n"));
864 DEBUGADD(11,("%s\n", s));
865 TALLOC_FREE(s);
868 if (ads->server.ldap_server) {
869 bool ok = false;
870 struct sockaddr_storage ss;
872 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
873 ads->server.ldap_server);
874 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
875 if (!ok) {
876 DEBUG(5,("ads_connect: unable to resolve name %s\n",
877 ads->server.ldap_server));
878 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
879 goto out;
882 if (is_zero_addr(&ss)) {
883 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
884 goto out;
887 ok = ads_try_connect(ads, ads->server.gc, &ss);
888 if (ok) {
889 goto got_connection;
892 /* The choice of which GC use is handled one level up in
893 ads_connect_gc(). If we continue on from here with
894 ads_find_dc() we will get GC searches on port 389 which
895 doesn't work. --jerry */
897 if (ads->server.gc == true) {
898 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
901 if (ads->server.no_fallback) {
902 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
903 goto out;
907 if (!is_zero_addr(&existing_ss)) {
908 /* We saved off who we should talk to. */
909 bool ok = ads_try_connect(ads,
910 ads->server.gc,
911 &existing_ss);
912 if (ok) {
913 goto got_connection;
916 * Keep trying to find a server and fall through
917 * into ads_find_dc() again.
919 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
920 "trying to find another DC.\n");
923 ntstatus = ads_find_dc(ads);
924 if (NT_STATUS_IS_OK(ntstatus)) {
925 goto got_connection;
928 status = ADS_ERROR_NT(ntstatus);
929 goto out;
931 got_connection:
933 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
934 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
936 if (!ads->auth.user_name) {
937 /* Must use the userPrincipalName value here or sAMAccountName
938 and not servicePrincipalName; found by Guenther Deschner */
939 ads->auth.user_name = talloc_asprintf(ads,
940 "%s$",
941 lp_netbios_name());
942 if (ads->auth.user_name == NULL) {
943 DBG_ERR("talloc_asprintf failed\n");
944 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
945 goto out;
949 if (ads->auth.realm == NULL) {
950 ads->auth.realm = talloc_strdup(ads, ads->config.realm);
951 if (ads->auth.realm == NULL) {
952 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
953 goto out;
957 if (!ads->auth.kdc_server) {
958 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
959 ads->auth.kdc_server = talloc_strdup(ads, addr);
960 if (ads->auth.kdc_server == NULL) {
961 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
962 goto out;
966 /* If the caller() requested no LDAP bind, then we are done */
968 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
969 status = ADS_SUCCESS;
970 goto out;
973 ads->ldap_tls_data.mem_ctx = talloc_init("ads LDAP TLS connection memory");
974 if (!ads->ldap_tls_data.mem_ctx) {
975 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
976 goto out;
979 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
980 if (!ads->ldap_wrap_data.mem_ctx) {
981 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
982 goto out;
985 /* Otherwise setup the TCP LDAP session */
987 if (ads->auth.flags & ADS_AUTH_SASL_LDAPS) {
988 tls = true;
989 ads->ldap.port = 636;
990 } else if (ads->auth.flags & ADS_AUTH_SASL_STARTTLS) {
991 tls = true;
992 start_tls = true;
993 ads->ldap.port = 389;
994 } else {
995 ads->ldap.port = 389;
998 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
999 &ads->ldap.ss,
1000 ads->ldap.port, lp_ldap_timeout());
1001 if (ads->ldap.ld == NULL) {
1002 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
1003 goto out;
1005 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
1007 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1009 if (start_tls) {
1010 unsigned int to = lp_ldap_connection_timeout();
1011 struct berval *rspdata = NULL;
1012 char *rspoid = NULL;
1013 int rc;
1015 if (to) {
1016 /* Setup timeout */
1017 gotalarm = 0;
1018 CatchSignal(SIGALRM, gotalarm_sig);
1019 alarm(to);
1020 /* End setup timeout. */
1023 rc = ldap_extended_operation_s(ads->ldap.ld,
1024 LDAP_EXOP_START_TLS,
1025 NULL,
1026 NULL,
1027 NULL,
1028 &rspoid,
1029 &rspdata);
1030 if (gotalarm != 0 && rc == LDAP_SUCCESS) {
1031 rc = LDAP_TIMEOUT;
1034 if (to) {
1035 /* Teardown timeout. */
1036 alarm(0);
1037 CatchSignal(SIGALRM, SIG_IGN);
1040 if (rspoid != NULL) {
1041 ldap_memfree(rspoid);
1044 if (rspdata != NULL) {
1045 ber_bvfree(rspdata);
1048 if (rc != LDAP_SUCCESS) {
1049 status = ADS_ERROR_LDAP(rc);
1050 goto out;
1054 if (tls) {
1055 unsigned int to = lp_ldap_connection_timeout();
1057 if (to) {
1058 /* Setup timeout */
1059 gotalarm = 0;
1060 CatchSignal(SIGALRM, gotalarm_sig);
1061 alarm(to);
1062 /* End setup timeout. */
1065 status = ads_setup_tls_wrapping(&ads->ldap_tls_data,
1066 ads->ldap.ld,
1067 ads->config.ldap_server_name);
1069 if (to) {
1070 /* Teardown timeout. */
1071 alarm(0);
1072 CatchSignal(SIGALRM, SIG_IGN);
1075 if ( !ADS_ERR_OK(status) ) {
1076 goto out;
1080 /* cache the successful connection for workgroup and realm */
1081 if (ads_closest_dc(ads)) {
1082 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
1083 saf_store( ads->server.realm, ads->config.ldap_server_name);
1086 /* fill in the current time and offsets */
1088 status = ads_current_time( ads );
1089 if ( !ADS_ERR_OK(status) ) {
1090 goto out;
1093 /* Now do the bind */
1095 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
1096 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
1097 goto out;
1100 status = ads_sasl_bind(ads);
1102 out:
1103 if (DEBUGLEVEL >= 11) {
1104 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1105 DEBUG(11,("ads_connect: leaving with: %s\n",
1106 ads_errstr(status)));
1107 DEBUGADD(11,("%s\n", s));
1108 TALLOC_FREE(s);
1111 return status;
1115 * Connect to the LDAP server using given credentials
1116 * @param ads Pointer to an existing ADS_STRUCT
1117 * @return status of connection
1119 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
1121 ads->auth.flags |= ADS_AUTH_USER_CREDS;
1123 return ads_connect(ads);
1127 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1128 * @param ads Pointer to an existing ADS_STRUCT
1130 * Sets the ads->ldap.ss to a valid
1131 * zero ip address that can be detected by
1132 * our is_zero_addr() function. Otherwise
1133 * it is left as AF_UNSPEC (0).
1135 void ads_zero_ldap(ADS_STRUCT *ads)
1137 ZERO_STRUCT(ads->ldap);
1139 * Initialize the sockaddr_storage so we can use
1140 * sockaddr test functions against it.
1142 zero_sockaddr(&ads->ldap.ss);
1146 * Disconnect the LDAP server
1147 * @param ads Pointer to an existing ADS_STRUCT
1149 void ads_disconnect(ADS_STRUCT *ads)
1151 if (ads->ldap.ld) {
1152 ldap_unbind(ads->ldap.ld);
1153 ads->ldap.ld = NULL;
1155 if (ads->ldap_tls_data.mem_ctx) {
1156 talloc_free(ads->ldap_tls_data.mem_ctx);
1158 if (ads->ldap_wrap_data.wrap_ops &&
1159 ads->ldap_wrap_data.wrap_ops->disconnect) {
1160 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1162 if (ads->ldap_wrap_data.mem_ctx) {
1163 talloc_free(ads->ldap_wrap_data.mem_ctx);
1165 ads_zero_ldap(ads);
1166 ZERO_STRUCT(ads->ldap_tls_data);
1167 ZERO_STRUCT(ads->ldap_wrap_data);
1171 Duplicate a struct berval into talloc'ed memory
1173 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1175 struct berval *value;
1177 if (!in_val) return NULL;
1179 value = talloc_zero(ctx, struct berval);
1180 if (value == NULL)
1181 return NULL;
1182 if (in_val->bv_len == 0) return value;
1184 value->bv_len = in_val->bv_len;
1185 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1186 in_val->bv_len);
1187 return value;
1191 Make a values list out of an array of (struct berval *)
1193 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1194 const struct berval **in_vals)
1196 struct berval **values;
1197 int i;
1199 if (!in_vals) return NULL;
1200 for (i=0; in_vals[i]; i++)
1201 ; /* count values */
1202 values = talloc_zero_array(ctx, struct berval *, i+1);
1203 if (!values) return NULL;
1205 for (i=0; in_vals[i]; i++) {
1206 values[i] = dup_berval(ctx, in_vals[i]);
1208 return values;
1212 UTF8-encode a values list out of an array of (char *)
1214 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1216 char **values;
1217 int i;
1218 size_t size;
1220 if (!in_vals) return NULL;
1221 for (i=0; in_vals[i]; i++)
1222 ; /* count values */
1223 values = talloc_zero_array(ctx, char *, i+1);
1224 if (!values) return NULL;
1226 for (i=0; in_vals[i]; i++) {
1227 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1228 TALLOC_FREE(values);
1229 return NULL;
1232 return values;
1236 Pull a (char *) array out of a UTF8-encoded values list
1238 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1240 char **values;
1241 int i;
1242 size_t converted_size;
1244 if (!in_vals) return NULL;
1245 for (i=0; in_vals[i]; i++)
1246 ; /* count values */
1247 values = talloc_zero_array(ctx, char *, i+1);
1248 if (!values) return NULL;
1250 for (i=0; in_vals[i]; i++) {
1251 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1252 &converted_size)) {
1253 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1254 "%s\n", strerror(errno)));
1257 return values;
1261 * Do a search with paged results. cookie must be null on the first
1262 * call, and then returned on each subsequent call. It will be null
1263 * again when the entire search is complete
1264 * @param ads connection to ads server
1265 * @param bind_path Base dn for the search
1266 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1267 * @param expr Search expression - specified in local charset
1268 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1269 * @param res ** which will contain results - free res* with ads_msgfree()
1270 * @param count Number of entries retrieved on this page
1271 * @param cookie The paged results cookie to be returned on subsequent calls
1272 * @return status of search
1274 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1275 const char *bind_path,
1276 int scope, const char *expr,
1277 const char **attrs, void *args,
1278 LDAPMessage **res,
1279 int *count, struct berval **cookie)
1281 int rc, i, version;
1282 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1283 size_t converted_size;
1284 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1285 BerElement *cookie_be = NULL;
1286 struct berval *cookie_bv= NULL;
1287 BerElement *ext_be = NULL;
1288 struct berval *ext_bv= NULL;
1290 TALLOC_CTX *ctx;
1291 ads_control *external_control = (ads_control *) args;
1293 *res = NULL;
1295 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1296 return ADS_ERROR(LDAP_NO_MEMORY);
1298 /* 0 means the conversion worked but the result was empty
1299 so we only fail if it's -1. In any case, it always
1300 at least nulls out the dest */
1301 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1302 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1304 rc = LDAP_NO_MEMORY;
1305 goto done;
1308 if (!attrs || !(*attrs))
1309 search_attrs = NULL;
1310 else {
1311 /* This would be the utf8-encoded version...*/
1312 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1313 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1314 rc = LDAP_NO_MEMORY;
1315 goto done;
1319 /* Paged results only available on ldap v3 or later */
1320 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1321 if (version < LDAP_VERSION3) {
1322 rc = LDAP_NOT_SUPPORTED;
1323 goto done;
1326 cookie_be = ber_alloc_t(LBER_USE_DER);
1327 if (*cookie) {
1328 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1329 ber_bvfree(*cookie); /* don't need it from last time */
1330 *cookie = NULL;
1331 } else {
1332 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1334 ber_flatten(cookie_be, &cookie_bv);
1335 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1336 PagedResults.ldctl_iscritical = (char) 1;
1337 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1338 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1340 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1341 NoReferrals.ldctl_iscritical = (char) 0;
1342 NoReferrals.ldctl_value.bv_len = 0;
1343 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1345 if (external_control &&
1346 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1347 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1349 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1350 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1352 /* win2k does not accept a ldctl_value being passed in */
1354 if (external_control->val != 0) {
1356 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1357 rc = LDAP_NO_MEMORY;
1358 goto done;
1361 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1362 rc = LDAP_NO_MEMORY;
1363 goto done;
1365 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1366 rc = LDAP_NO_MEMORY;
1367 goto done;
1370 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1371 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1373 } else {
1374 ExternalCtrl.ldctl_value.bv_len = 0;
1375 ExternalCtrl.ldctl_value.bv_val = NULL;
1378 controls[0] = &NoReferrals;
1379 controls[1] = &PagedResults;
1380 controls[2] = &ExternalCtrl;
1381 controls[3] = NULL;
1383 } else {
1384 controls[0] = &NoReferrals;
1385 controls[1] = &PagedResults;
1386 controls[2] = NULL;
1389 /* we need to disable referrals as the openldap libs don't
1390 handle them and paged results at the same time. Using them
1391 together results in the result record containing the server
1392 page control being removed from the result list (tridge/jmcd)
1394 leaving this in despite the control that says don't generate
1395 referrals, in case the server doesn't support it (jmcd)
1397 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1399 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1400 search_attrs, 0, controls,
1401 NULL, LDAP_NO_LIMIT,
1402 (LDAPMessage **)res);
1404 ber_free(cookie_be, 1);
1405 ber_bvfree(cookie_bv);
1407 if (rc) {
1408 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1409 ldap_err2string(rc)));
1410 if (rc == LDAP_OTHER) {
1411 char *ldap_errmsg;
1412 int ret;
1414 ret = ldap_parse_result(ads->ldap.ld,
1415 *res,
1416 NULL,
1417 NULL,
1418 &ldap_errmsg,
1419 NULL,
1420 NULL,
1422 if (ret == LDAP_SUCCESS) {
1423 DEBUG(3, ("ldap_search_with_timeout(%s) "
1424 "error: %s\n", expr, ldap_errmsg));
1425 ldap_memfree(ldap_errmsg);
1428 goto done;
1431 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1432 NULL, &rcontrols, 0);
1434 if (!rcontrols) {
1435 goto done;
1438 for (i=0; rcontrols[i]; i++) {
1439 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1440 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1441 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1442 &cookie_bv);
1443 /* the berval is the cookie, but must be freed when
1444 it is all done */
1445 if (cookie_bv->bv_len) /* still more to do */
1446 *cookie=ber_bvdup(cookie_bv);
1447 else
1448 *cookie=NULL;
1449 ber_bvfree(cookie_bv);
1450 ber_free(cookie_be, 1);
1451 break;
1454 ldap_controls_free(rcontrols);
1456 done:
1457 talloc_destroy(ctx);
1459 if (ext_be) {
1460 ber_free(ext_be, 1);
1463 if (ext_bv) {
1464 ber_bvfree(ext_bv);
1467 if (rc != LDAP_SUCCESS && *res != NULL) {
1468 ads_msgfree(ads, *res);
1469 *res = NULL;
1472 /* if/when we decide to utf8-encode attrs, take out this next line */
1473 TALLOC_FREE(search_attrs);
1475 return ADS_ERROR(rc);
1478 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1479 int scope, const char *expr,
1480 const char **attrs, LDAPMessage **res,
1481 int *count, struct berval **cookie)
1483 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1488 * Get all results for a search. This uses ads_do_paged_search() to return
1489 * all entries in a large search.
1490 * @param ads connection to ads server
1491 * @param bind_path Base dn for the search
1492 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1493 * @param expr Search expression
1494 * @param attrs Attributes to retrieve
1495 * @param res ** which will contain results - free res* with ads_msgfree()
1496 * @return status of search
1498 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1499 int scope, const char *expr,
1500 const char **attrs, void *args,
1501 LDAPMessage **res)
1503 struct berval *cookie = NULL;
1504 int count = 0;
1505 ADS_STATUS status;
1507 *res = NULL;
1508 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1509 &count, &cookie);
1511 if (!ADS_ERR_OK(status))
1512 return status;
1514 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1515 while (cookie) {
1516 LDAPMessage *res2 = NULL;
1517 LDAPMessage *msg, *next;
1519 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1520 attrs, args, &res2, &count, &cookie);
1521 if (!ADS_ERR_OK(status)) {
1522 break;
1525 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1526 that this works on all ldap libs, but I have only tested with openldap */
1527 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1528 next = ads_next_message(ads, msg);
1529 ldap_add_result_entry((LDAPMessage **)res, msg);
1531 /* note that we do not free res2, as the memory is now
1532 part of the main returned list */
1534 #else
1535 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1536 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1537 #endif
1539 return status;
1542 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1543 int scope, const char *expr,
1544 const char **attrs, LDAPMessage **res)
1546 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1549 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1550 int scope, const char *expr,
1551 const char **attrs, uint32_t sd_flags,
1552 LDAPMessage **res)
1554 ads_control args;
1556 args.control = ADS_SD_FLAGS_OID;
1557 args.val = sd_flags;
1558 args.critical = True;
1560 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1565 * Run a function on all results for a search. Uses ads_do_paged_search() and
1566 * runs the function as each page is returned, using ads_process_results()
1567 * @param ads connection to ads server
1568 * @param bind_path Base dn for the search
1569 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1570 * @param expr Search expression - specified in local charset
1571 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1572 * @param fn Function which takes attr name, values list, and data_area
1573 * @param data_area Pointer which is passed to function on each call
1574 * @return status of search
1576 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1577 int scope, const char *expr, const char **attrs,
1578 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1579 void *data_area)
1581 struct berval *cookie = NULL;
1582 int count = 0;
1583 ADS_STATUS status;
1584 LDAPMessage *res;
1586 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1587 &count, &cookie);
1589 if (!ADS_ERR_OK(status)) return status;
1591 ads_process_results(ads, res, fn, data_area);
1592 ads_msgfree(ads, res);
1594 while (cookie) {
1595 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1596 &res, &count, &cookie);
1598 if (!ADS_ERR_OK(status)) break;
1600 ads_process_results(ads, res, fn, data_area);
1601 ads_msgfree(ads, res);
1604 return status;
1608 * Do a search with a timeout.
1609 * @param ads connection to ads server
1610 * @param bind_path Base dn for the search
1611 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1612 * @param expr Search expression
1613 * @param attrs Attributes to retrieve
1614 * @param res ** which will contain results - free res* with ads_msgfree()
1615 * @return status of search
1617 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1618 const char *expr,
1619 const char **attrs, LDAPMessage **res)
1621 int rc;
1622 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1623 size_t converted_size;
1624 TALLOC_CTX *ctx;
1626 *res = NULL;
1627 if (!(ctx = talloc_init("ads_do_search"))) {
1628 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1629 return ADS_ERROR(LDAP_NO_MEMORY);
1632 /* 0 means the conversion worked but the result was empty
1633 so we only fail if it's negative. In any case, it always
1634 at least nulls out the dest */
1635 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1636 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1638 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1639 rc = LDAP_NO_MEMORY;
1640 goto done;
1643 if (!attrs || !(*attrs))
1644 search_attrs = NULL;
1645 else {
1646 /* This would be the utf8-encoded version...*/
1647 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1648 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1650 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1651 rc = LDAP_NO_MEMORY;
1652 goto done;
1656 /* see the note in ads_do_paged_search - we *must* disable referrals */
1657 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1659 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1660 search_attrs, 0, NULL, NULL,
1661 LDAP_NO_LIMIT,
1662 (LDAPMessage **)res);
1664 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1665 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1666 rc = 0;
1669 done:
1670 talloc_destroy(ctx);
1671 /* if/when we decide to utf8-encode attrs, take out this next line */
1672 TALLOC_FREE(search_attrs);
1673 return ADS_ERROR(rc);
1676 * Do a general ADS search
1677 * @param ads connection to ads server
1678 * @param res ** which will contain results - free res* with ads_msgfree()
1679 * @param expr Search expression
1680 * @param attrs Attributes to retrieve
1681 * @return status of search
1683 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1684 const char *expr, const char **attrs)
1686 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1687 expr, attrs, res);
1691 * Do a search on a specific DistinguishedName
1692 * @param ads connection to ads server
1693 * @param res ** which will contain results - free res* with ads_msgfree()
1694 * @param dn DistinguishedName to search
1695 * @param attrs Attributes to retrieve
1696 * @return status of search
1698 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1699 const char *dn, const char **attrs)
1701 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1702 attrs, res);
1706 * Free up memory from a ads_search
1707 * @param ads connection to ads server
1708 * @param msg Search results to free
1710 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1712 if (!msg) return;
1713 ldap_msgfree(msg);
1717 * Get a dn from search results
1718 * @param ads connection to ads server
1719 * @param msg Search result
1720 * @return dn string
1722 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1724 char *utf8_dn, *unix_dn;
1725 size_t converted_size;
1727 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1729 if (!utf8_dn) {
1730 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1731 return NULL;
1734 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1735 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1736 utf8_dn ));
1737 return NULL;
1739 ldap_memfree(utf8_dn);
1740 return unix_dn;
1744 * Get the parent from a dn
1745 * @param dn the dn to return the parent from
1746 * @return parent dn string
1748 char *ads_parent_dn(const char *dn)
1750 char *p;
1752 if (dn == NULL) {
1753 return NULL;
1756 p = strchr(dn, ',');
1758 if (p == NULL) {
1759 return NULL;
1762 return p+1;
1766 * Find a machine account given a hostname
1767 * @param ads connection to ads server
1768 * @param res ** which will contain results - free res* with ads_msgfree()
1769 * @param host Hostname to search for
1770 * @return status of search
1772 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1773 const char *machine)
1775 ADS_STATUS status;
1776 char *expr;
1777 const char *attrs[] = {
1778 /* This is how Windows checks for machine accounts */
1779 "objectClass",
1780 "SamAccountName",
1781 "userAccountControl",
1782 "DnsHostName",
1783 "ServicePrincipalName",
1784 "userPrincipalName",
1786 /* Additional attributes Samba checks */
1787 "msDS-AdditionalDnsHostName",
1788 "msDS-SupportedEncryptionTypes",
1789 "nTSecurityDescriptor",
1790 "objectSid",
1792 NULL
1794 TALLOC_CTX *frame = talloc_stackframe();
1796 *res = NULL;
1798 /* the easiest way to find a machine account anywhere in the tree
1799 is to look for hostname$ */
1800 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1801 if (expr == NULL) {
1802 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1803 goto done;
1806 status = ads_search(ads, res, expr, attrs);
1807 if (ADS_ERR_OK(status)) {
1808 if (ads_count_replies(ads, *res) != 1) {
1809 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1813 done:
1814 TALLOC_FREE(frame);
1815 return status;
1819 * Initialize a list of mods to be used in a modify request
1820 * @param ctx An initialized TALLOC_CTX
1821 * @return allocated ADS_MODLIST
1823 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1825 #define ADS_MODLIST_ALLOC_SIZE 10
1826 LDAPMod **mods;
1828 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1829 /* -1 is safety to make sure we don't go over the end.
1830 need to reset it to NULL before doing ldap modify */
1831 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1833 return (ADS_MODLIST)mods;
1838 add an attribute to the list, with values list already constructed
1840 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1841 int mod_op, const char *name,
1842 const void *_invals)
1844 int curmod;
1845 LDAPMod **modlist = (LDAPMod **) *mods;
1846 struct berval **ber_values = NULL;
1847 char **char_values = NULL;
1849 if (!_invals) {
1850 mod_op = LDAP_MOD_DELETE;
1851 } else {
1852 if (mod_op & LDAP_MOD_BVALUES) {
1853 const struct berval **b;
1854 b = discard_const_p(const struct berval *, _invals);
1855 ber_values = ads_dup_values(ctx, b);
1856 } else {
1857 const char **c;
1858 c = discard_const_p(const char *, _invals);
1859 char_values = ads_push_strvals(ctx, c);
1863 /* find the first empty slot */
1864 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1865 curmod++);
1866 if (modlist[curmod] == (LDAPMod *) -1) {
1867 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1868 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1869 return ADS_ERROR(LDAP_NO_MEMORY);
1870 memset(&modlist[curmod], 0,
1871 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1872 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1873 *mods = (ADS_MODLIST)modlist;
1876 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1877 return ADS_ERROR(LDAP_NO_MEMORY);
1878 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1879 if (mod_op & LDAP_MOD_BVALUES) {
1880 modlist[curmod]->mod_bvalues = ber_values;
1881 } else if (mod_op & LDAP_MOD_DELETE) {
1882 modlist[curmod]->mod_values = NULL;
1883 } else {
1884 modlist[curmod]->mod_values = char_values;
1887 modlist[curmod]->mod_op = mod_op;
1888 return ADS_ERROR(LDAP_SUCCESS);
1892 * Add a single string value to a mod list
1893 * @param ctx An initialized TALLOC_CTX
1894 * @param mods An initialized ADS_MODLIST
1895 * @param name The attribute name to add
1896 * @param val The value to add - NULL means DELETE
1897 * @return ADS STATUS indicating success of add
1899 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1900 const char *name, const char *val)
1902 const char *values[2];
1904 values[0] = val;
1905 values[1] = NULL;
1907 if (!val)
1908 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1909 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1913 * Add an array of string values to a mod list
1914 * @param ctx An initialized TALLOC_CTX
1915 * @param mods An initialized ADS_MODLIST
1916 * @param name The attribute name to add
1917 * @param vals The array of string values to add - NULL means DELETE
1918 * @return ADS STATUS indicating success of add
1920 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1921 const char *name, const char **vals)
1923 if (!vals)
1924 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1925 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1926 name, (const void **) vals);
1930 * Add a single ber-encoded value to a mod list
1931 * @param ctx An initialized TALLOC_CTX
1932 * @param mods An initialized ADS_MODLIST
1933 * @param name The attribute name to add
1934 * @param val The value to add - NULL means DELETE
1935 * @return ADS STATUS indicating success of add
1937 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1938 const char *name, const struct berval *val)
1940 const struct berval *values[2];
1942 values[0] = val;
1943 values[1] = NULL;
1944 if (!val)
1945 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1946 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1947 name, (const void **) values);
1950 static void ads_print_error(int ret, LDAP *ld)
1952 if (ret != 0) {
1953 char *ld_error = NULL;
1954 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1955 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1956 ret,
1957 ldap_err2string(ret),
1958 ld_error);
1959 SAFE_FREE(ld_error);
1964 * Perform an ldap modify
1965 * @param ads connection to ads server
1966 * @param mod_dn DistinguishedName to modify
1967 * @param mods list of modifications to perform
1968 * @return status of modify
1970 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1972 int ret,i;
1973 char *utf8_dn = NULL;
1974 size_t converted_size;
1976 this control is needed to modify that contains a currently
1977 non-existent attribute (but allowable for the object) to run
1979 LDAPControl PermitModify = {
1980 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1981 {0, NULL},
1982 (char) 1};
1983 LDAPControl *controls[2];
1985 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1987 controls[0] = &PermitModify;
1988 controls[1] = NULL;
1990 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1991 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1994 /* find the end of the list, marked by NULL or -1 */
1995 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1996 /* make sure the end of the list is NULL */
1997 mods[i] = NULL;
1998 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1999 (LDAPMod **) mods, controls, NULL);
2000 ads_print_error(ret, ads->ldap.ld);
2001 TALLOC_FREE(utf8_dn);
2002 return ADS_ERROR(ret);
2006 * Perform an ldap add
2007 * @param ads connection to ads server
2008 * @param new_dn DistinguishedName to add
2009 * @param mods list of attributes and values for DN
2010 * @return status of add
2012 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
2014 int ret, i;
2015 char *utf8_dn = NULL;
2016 size_t converted_size;
2018 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
2020 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
2021 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2022 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2025 /* find the end of the list, marked by NULL or -1 */
2026 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2027 /* make sure the end of the list is NULL */
2028 mods[i] = NULL;
2030 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
2031 ads_print_error(ret, ads->ldap.ld);
2032 TALLOC_FREE(utf8_dn);
2033 return ADS_ERROR(ret);
2037 * Delete a DistinguishedName
2038 * @param ads connection to ads server
2039 * @param new_dn DistinguishedName to delete
2040 * @return status of delete
2042 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
2044 int ret;
2045 char *utf8_dn = NULL;
2046 size_t converted_size;
2047 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
2048 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2049 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2052 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
2054 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
2055 ads_print_error(ret, ads->ldap.ld);
2056 TALLOC_FREE(utf8_dn);
2057 return ADS_ERROR(ret);
2061 * Build an org unit string
2062 * if org unit is Computers or blank then assume a container, otherwise
2063 * assume a / separated list of organisational units.
2064 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2065 * @param ads connection to ads server
2066 * @param org_unit Organizational unit
2067 * @return org unit string - caller must free
2069 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
2071 ADS_STATUS status;
2072 char *ret = NULL;
2073 char *dn = NULL;
2075 if (!org_unit || !*org_unit) {
2077 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
2079 /* samba4 might not yet respond to a wellknownobject-query */
2080 return ret ? ret : SMB_STRDUP("cn=Computers");
2083 if (strequal(org_unit, "Computers")) {
2084 return SMB_STRDUP("cn=Computers");
2087 /* jmcd: removed "\\" from the separation chars, because it is
2088 needed as an escape for chars like '#' which are valid in an
2089 OU name */
2090 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
2091 if (!ADS_ERR_OK(status)) {
2092 return NULL;
2095 return dn;
2099 * Get a org unit string for a well-known GUID
2100 * @param ads connection to ads server
2101 * @param wknguid Well known GUID
2102 * @return org unit string - caller must free
2104 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2106 ADS_STATUS status;
2107 LDAPMessage *res = NULL;
2108 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2109 **bind_dn_exp = NULL;
2110 const char *attrs[] = {"distinguishedName", NULL};
2111 int new_ln, wkn_ln, bind_ln, i;
2113 if (wknguid == NULL) {
2114 return NULL;
2117 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2118 DEBUG(1, ("asprintf failed!\n"));
2119 return NULL;
2122 status = ads_search_dn(ads, &res, base, attrs);
2123 if (!ADS_ERR_OK(status)) {
2124 DEBUG(1,("Failed while searching for: %s\n", base));
2125 goto out;
2128 if (ads_count_replies(ads, res) != 1) {
2129 goto out;
2132 /* substitute the bind-path from the well-known-guid-search result */
2133 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2134 if (!wkn_dn) {
2135 goto out;
2138 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2139 if (!wkn_dn_exp) {
2140 goto out;
2143 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2144 if (!bind_dn_exp) {
2145 goto out;
2148 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2150 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2153 new_ln = wkn_ln - bind_ln;
2155 ret = SMB_STRDUP(wkn_dn_exp[0]);
2156 if (!ret) {
2157 goto out;
2160 for (i=1; i < new_ln; i++) {
2161 char *s = NULL;
2163 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2164 SAFE_FREE(ret);
2165 goto out;
2168 SAFE_FREE(ret);
2169 ret = SMB_STRDUP(s);
2170 free(s);
2171 if (!ret) {
2172 goto out;
2176 out:
2177 SAFE_FREE(base);
2178 ads_msgfree(ads, res);
2179 TALLOC_FREE(wkn_dn);
2180 if (wkn_dn_exp) {
2181 ldap_value_free(wkn_dn_exp);
2183 if (bind_dn_exp) {
2184 ldap_value_free(bind_dn_exp);
2187 return ret;
2191 * Adds (appends) an item to an attribute array, rather then
2192 * replacing the whole list
2193 * @param ctx An initialized TALLOC_CTX
2194 * @param mods An initialized ADS_MODLIST
2195 * @param name name of the ldap attribute to append to
2196 * @param vals an array of values to add
2197 * @return status of addition
2200 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2201 const char *name, const char **vals)
2203 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2204 (const void *) vals);
2208 * Determines the an account's current KVNO via an LDAP lookup
2209 * @param ads An initialized ADS_STRUCT
2210 * @param account_name the NT samaccountname.
2211 * @return the kvno for the account, or -1 in case of a failure.
2214 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
2216 LDAPMessage *res = NULL;
2217 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
2218 char *filter;
2219 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
2220 char *dn_string = NULL;
2221 ADS_STATUS ret;
2223 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
2224 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
2225 return kvno;
2227 ret = ads_search(ads, &res, filter, attrs);
2228 SAFE_FREE(filter);
2229 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
2230 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
2231 ads_msgfree(ads, res);
2232 return kvno;
2235 dn_string = ads_get_dn(ads, talloc_tos(), res);
2236 if (!dn_string) {
2237 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2238 ads_msgfree(ads, res);
2239 return kvno;
2241 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
2242 TALLOC_FREE(dn_string);
2244 /* ---------------------------------------------------------
2245 * 0 is returned as a default KVNO from this point on...
2246 * This is done because Windows 2000 does not support key
2247 * version numbers. Chances are that a failure in the next
2248 * step is simply due to Windows 2000 being used for a
2249 * domain controller. */
2250 kvno = 0;
2252 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2253 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2254 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2255 ads_msgfree(ads, res);
2256 return kvno;
2259 /* Success */
2260 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2261 ads_msgfree(ads, res);
2262 return kvno;
2266 * Determines the computer account's current KVNO via an LDAP lookup
2267 * @param ads An initialized ADS_STRUCT
2268 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2269 * @return the kvno for the computer account, or -1 in case of a failure.
2272 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2274 char *computer_account = NULL;
2275 uint32_t kvno = -1;
2277 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2278 return kvno;
2281 kvno = ads_get_kvno(ads, computer_account);
2282 free(computer_account);
2284 return kvno;
2288 * This clears out all registered spn's for a given hostname
2289 * @param ads An initialized ADS_STRUCT
2290 * @param machine_name the NetBIOS name of the computer.
2291 * @return 0 upon success, non-zero otherwise.
2294 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2296 TALLOC_CTX *ctx;
2297 LDAPMessage *res = NULL;
2298 ADS_MODLIST mods;
2299 const char *servicePrincipalName[1] = {NULL};
2300 ADS_STATUS ret;
2301 char *dn_string = NULL;
2303 ret = ads_find_machine_acct(ads, &res, machine_name);
2304 if (!ADS_ERR_OK(ret)) {
2305 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2306 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2307 ads_msgfree(ads, res);
2308 return ret;
2311 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2312 ctx = talloc_init("ads_clear_service_principal_names");
2313 if (!ctx) {
2314 ads_msgfree(ads, res);
2315 return ADS_ERROR(LDAP_NO_MEMORY);
2318 if (!(mods = ads_init_mods(ctx))) {
2319 talloc_destroy(ctx);
2320 ads_msgfree(ads, res);
2321 return ADS_ERROR(LDAP_NO_MEMORY);
2323 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2324 if (!ADS_ERR_OK(ret)) {
2325 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2326 ads_msgfree(ads, res);
2327 talloc_destroy(ctx);
2328 return ret;
2330 dn_string = ads_get_dn(ads, talloc_tos(), res);
2331 if (!dn_string) {
2332 talloc_destroy(ctx);
2333 ads_msgfree(ads, res);
2334 return ADS_ERROR(LDAP_NO_MEMORY);
2336 ret = ads_gen_mod(ads, dn_string, mods);
2337 TALLOC_FREE(dn_string);
2338 if (!ADS_ERR_OK(ret)) {
2339 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2340 machine_name));
2341 ads_msgfree(ads, res);
2342 talloc_destroy(ctx);
2343 return ret;
2346 ads_msgfree(ads, res);
2347 talloc_destroy(ctx);
2348 return ret;
2352 * @brief Search for an element in a string array.
2354 * @param[in] el_array The string array to search.
2356 * @param[in] num_el The number of elements in the string array.
2358 * @param[in] el The string to search.
2360 * @return True if found, false if not.
2362 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2364 size_t i;
2366 if (el_array == NULL || num_el == 0 || el == NULL) {
2367 return false;
2370 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2371 int cmp;
2373 cmp = strcasecmp_m(el_array[i], el);
2374 if (cmp == 0) {
2375 return true;
2379 return false;
2383 * @brief This gets the service principal names of an existing computer account.
2385 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2387 * @param[in] ads The ADS context to use.
2389 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2390 * identify the computer account.
2392 * @param[in] spn_array A pointer to store the array for SPNs.
2394 * @param[in] num_spns The number of principals stored in the array.
2396 * @return 0 on success, or a ADS error if a failure occurred.
2398 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2399 ADS_STRUCT *ads,
2400 const char *machine_name,
2401 char ***spn_array,
2402 size_t *num_spns)
2404 ADS_STATUS status;
2405 LDAPMessage *res = NULL;
2406 int count;
2408 status = ads_find_machine_acct(ads,
2409 &res,
2410 machine_name);
2411 if (!ADS_ERR_OK(status)) {
2412 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2413 machine_name));
2414 return status;
2417 count = ads_count_replies(ads, res);
2418 if (count != 1) {
2419 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2420 goto done;
2423 *spn_array = ads_pull_strings(ads,
2424 mem_ctx,
2425 res,
2426 "servicePrincipalName",
2427 num_spns);
2428 if (*spn_array == NULL) {
2429 DEBUG(1, ("Host account for %s does not have service principal "
2430 "names.\n",
2431 machine_name));
2432 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2433 goto done;
2436 done:
2437 ads_msgfree(ads, res);
2439 return status;
2443 * This adds a service principal name to an existing computer account
2444 * (found by hostname) in AD.
2445 * @param ads An initialized ADS_STRUCT
2446 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2447 * @param spns An array or strings for the service principals to add,
2448 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2449 * @return 0 upon success, or non-zero if a failure occurs
2452 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2453 const char *machine_name,
2454 const char **spns)
2456 ADS_STATUS ret;
2457 TALLOC_CTX *ctx;
2458 LDAPMessage *res = NULL;
2459 ADS_MODLIST mods;
2460 char *dn_string = NULL;
2461 const char **servicePrincipalName = spns;
2463 ret = ads_find_machine_acct(ads, &res, machine_name);
2464 if (!ADS_ERR_OK(ret)) {
2465 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2466 machine_name));
2467 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2468 ads_msgfree(ads, res);
2469 return ret;
2472 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2473 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2474 ads_msgfree(ads, res);
2475 return ADS_ERROR(LDAP_NO_MEMORY);
2478 DEBUG(5,("ads_add_service_principal_name: INFO: "
2479 "Adding %s to host %s\n",
2480 spns[0] ? "N/A" : spns[0], machine_name));
2483 DEBUG(5,("ads_add_service_principal_name: INFO: "
2484 "Adding %s to host %s\n",
2485 spns[1] ? "N/A" : spns[1], machine_name));
2487 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2488 ret = ADS_ERROR(LDAP_NO_MEMORY);
2489 goto out;
2492 ret = ads_add_strlist(ctx,
2493 &mods,
2494 "servicePrincipalName",
2495 servicePrincipalName);
2496 if (!ADS_ERR_OK(ret)) {
2497 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2498 goto out;
2501 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2502 ret = ADS_ERROR(LDAP_NO_MEMORY);
2503 goto out;
2506 ret = ads_gen_mod(ads, dn_string, mods);
2507 if (!ADS_ERR_OK(ret)) {
2508 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2509 goto out;
2512 out:
2513 TALLOC_FREE( ctx );
2514 ads_msgfree(ads, res);
2515 return ret;
2518 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2519 LDAPMessage *msg)
2521 uint32_t acct_ctrl = 0;
2522 bool ok;
2524 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2525 if (!ok) {
2526 return 0;
2529 return acct_ctrl;
2532 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2533 LDAPMessage *msg,
2534 const struct berval *machine_pw_val)
2536 ADS_MODLIST mods;
2537 ADS_STATUS ret;
2538 TALLOC_CTX *frame = talloc_stackframe();
2539 uint32_t acct_control;
2540 char *control_str = NULL;
2541 const char *attrs[] = {
2542 "objectSid",
2543 NULL
2545 LDAPMessage *res = NULL;
2546 char *dn = NULL;
2548 dn = ads_get_dn(ads, frame, msg);
2549 if (dn == NULL) {
2550 ret = ADS_ERROR(LDAP_NO_MEMORY);
2551 goto done;
2554 acct_control = ads_get_acct_ctrl(ads, msg);
2555 if (acct_control == 0) {
2556 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2557 goto done;
2561 * Changing the password, disables the account. So we need to change the
2562 * userAccountControl flags to enable it again.
2564 mods = ads_init_mods(frame);
2565 if (mods == NULL) {
2566 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2567 goto done;
2570 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2572 ret = ads_gen_mod(ads, dn, mods);
2573 if (!ADS_ERR_OK(ret)) {
2574 goto done;
2576 TALLOC_FREE(mods);
2579 * To activate the account, we need to disable and enable it.
2581 acct_control |= UF_ACCOUNTDISABLE;
2583 control_str = talloc_asprintf(frame, "%u", acct_control);
2584 if (control_str == NULL) {
2585 ret = ADS_ERROR(LDAP_NO_MEMORY);
2586 goto done;
2589 mods = ads_init_mods(frame);
2590 if (mods == NULL) {
2591 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2592 goto done;
2595 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2597 ret = ads_gen_mod(ads, dn, mods);
2598 if (!ADS_ERR_OK(ret)) {
2599 goto done;
2601 TALLOC_FREE(mods);
2602 TALLOC_FREE(control_str);
2605 * Enable the account again.
2607 acct_control &= ~UF_ACCOUNTDISABLE;
2609 control_str = talloc_asprintf(frame, "%u", acct_control);
2610 if (control_str == NULL) {
2611 ret = ADS_ERROR(LDAP_NO_MEMORY);
2612 goto done;
2615 mods = ads_init_mods(frame);
2616 if (mods == NULL) {
2617 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2618 goto done;
2621 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2623 ret = ads_gen_mod(ads, dn, mods);
2624 if (!ADS_ERR_OK(ret)) {
2625 goto done;
2627 TALLOC_FREE(mods);
2628 TALLOC_FREE(control_str);
2630 ret = ads_search_dn(ads, &res, dn, attrs);
2631 ads_msgfree(ads, res);
2633 done:
2634 talloc_free(frame);
2636 return ret;
2640 * adds a machine account to the ADS server
2641 * @param ads An initialized ADS_STRUCT
2642 * @param machine_name - the NetBIOS machine name of this account.
2643 * @param account_type A number indicating the type of account to create
2644 * @param org_unit The LDAP path in which to place this account
2645 * @return 0 upon success, or non-zero otherwise
2648 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2649 const char *machine_name,
2650 const char *machine_password,
2651 const char *org_unit,
2652 uint32_t etype_list,
2653 const char *dns_domain_name)
2655 ADS_STATUS ret;
2656 char *samAccountName = NULL;
2657 char *controlstr = NULL;
2658 TALLOC_CTX *ctx = NULL;
2659 ADS_MODLIST mods;
2660 char *machine_escaped = NULL;
2661 char *dns_hostname = NULL;
2662 char *new_dn = NULL;
2663 char *utf8_pw = NULL;
2664 size_t utf8_pw_len = 0;
2665 char *utf16_pw = NULL;
2666 size_t utf16_pw_len = 0;
2667 struct berval machine_pw_val;
2668 bool ok;
2669 const char **spn_array = NULL;
2670 size_t num_spns = 0;
2671 const char *spn_prefix[] = {
2672 "HOST",
2673 "RestrictedKrbHost",
2675 size_t i;
2676 LDAPMessage *res = NULL;
2677 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2679 ctx = talloc_init("ads_add_machine_acct");
2680 if (ctx == NULL) {
2681 return ADS_ERROR(LDAP_NO_MEMORY);
2684 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2685 if (machine_escaped == NULL) {
2686 ret = ADS_ERROR(LDAP_NO_MEMORY);
2687 goto done;
2690 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2691 if (utf8_pw == NULL) {
2692 ret = ADS_ERROR(LDAP_NO_MEMORY);
2693 goto done;
2695 utf8_pw_len = strlen(utf8_pw);
2697 ok = convert_string_talloc(ctx,
2698 CH_UTF8, CH_UTF16MUNGED,
2699 utf8_pw, utf8_pw_len,
2700 (void *)&utf16_pw, &utf16_pw_len);
2701 if (!ok) {
2702 ret = ADS_ERROR(LDAP_NO_MEMORY);
2703 goto done;
2706 machine_pw_val = (struct berval) {
2707 .bv_val = utf16_pw,
2708 .bv_len = utf16_pw_len,
2711 /* Check if the machine account already exists. */
2712 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2713 if (ADS_ERR_OK(ret)) {
2714 /* Change the machine account password */
2715 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2716 ads_msgfree(ads, res);
2718 goto done;
2720 ads_msgfree(ads, res);
2722 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2723 if (new_dn == NULL) {
2724 ret = ADS_ERROR(LDAP_NO_MEMORY);
2725 goto done;
2728 /* Create machine account */
2730 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2731 if (samAccountName == NULL) {
2732 ret = ADS_ERROR(LDAP_NO_MEMORY);
2733 goto done;
2736 dns_hostname = talloc_asprintf(ctx,
2737 "%s.%s",
2738 machine_name,
2739 dns_domain_name);
2740 if (dns_hostname == NULL) {
2741 ret = ADS_ERROR(LDAP_NO_MEMORY);
2742 goto done;
2745 /* Add dns_hostname SPNs */
2746 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2747 char *spn = talloc_asprintf(ctx,
2748 "%s/%s",
2749 spn_prefix[i],
2750 dns_hostname);
2751 if (spn == NULL) {
2752 ret = ADS_ERROR(LDAP_NO_MEMORY);
2753 goto done;
2756 ok = add_string_to_array(ctx,
2757 spn,
2758 &spn_array,
2759 &num_spns);
2760 if (!ok) {
2761 ret = ADS_ERROR(LDAP_NO_MEMORY);
2762 goto done;
2766 /* Add machine_name SPNs */
2767 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2768 char *spn = talloc_asprintf(ctx,
2769 "%s/%s",
2770 spn_prefix[i],
2771 machine_name);
2772 if (spn == NULL) {
2773 ret = ADS_ERROR(LDAP_NO_MEMORY);
2774 goto done;
2777 ok = add_string_to_array(ctx,
2778 spn,
2779 &spn_array,
2780 &num_spns);
2781 if (!ok) {
2782 ret = ADS_ERROR(LDAP_NO_MEMORY);
2783 goto done;
2787 /* Make sure to NULL terminate the array */
2788 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2789 if (spn_array == NULL) {
2790 ret = ADS_ERROR(LDAP_NO_MEMORY);
2791 goto done;
2793 spn_array[num_spns] = NULL;
2795 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2796 if (controlstr == NULL) {
2797 ret = ADS_ERROR(LDAP_NO_MEMORY);
2798 goto done;
2801 mods = ads_init_mods(ctx);
2802 if (mods == NULL) {
2803 ret = ADS_ERROR(LDAP_NO_MEMORY);
2804 goto done;
2807 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2808 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2809 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2810 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2811 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2812 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2814 ret = ads_gen_add(ads, new_dn, mods);
2816 done:
2817 SAFE_FREE(machine_escaped);
2818 talloc_destroy(ctx);
2820 return ret;
2824 * move a machine account to another OU on the ADS server
2825 * @param ads - An initialized ADS_STRUCT
2826 * @param machine_name - the NetBIOS machine name of this account.
2827 * @param org_unit - The LDAP path in which to place this account
2828 * @param moved - whether we moved the machine account (optional)
2829 * @return 0 upon success, or non-zero otherwise
2832 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2833 const char *org_unit, bool *moved)
2835 ADS_STATUS rc;
2836 int ldap_status;
2837 LDAPMessage *res = NULL;
2838 char *filter = NULL;
2839 char *computer_dn = NULL;
2840 char *parent_dn;
2841 char *computer_rdn = NULL;
2842 bool need_move = False;
2844 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2845 rc = ADS_ERROR(LDAP_NO_MEMORY);
2846 goto done;
2849 /* Find pre-existing machine */
2850 rc = ads_search(ads, &res, filter, NULL);
2851 if (!ADS_ERR_OK(rc)) {
2852 goto done;
2855 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2856 if (!computer_dn) {
2857 rc = ADS_ERROR(LDAP_NO_MEMORY);
2858 goto done;
2861 parent_dn = ads_parent_dn(computer_dn);
2862 if (strequal(parent_dn, org_unit)) {
2863 goto done;
2866 need_move = True;
2868 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2869 rc = ADS_ERROR(LDAP_NO_MEMORY);
2870 goto done;
2873 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2874 org_unit, 1, NULL, NULL);
2875 rc = ADS_ERROR(ldap_status);
2877 done:
2878 ads_msgfree(ads, res);
2879 SAFE_FREE(filter);
2880 TALLOC_FREE(computer_dn);
2881 SAFE_FREE(computer_rdn);
2883 if (!ADS_ERR_OK(rc)) {
2884 need_move = False;
2887 if (moved) {
2888 *moved = need_move;
2891 return rc;
2895 dump a binary result from ldap
2897 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2899 size_t i;
2900 for (i=0; values[i]; i++) {
2901 ber_len_t j;
2902 printf("%s: ", field);
2903 for (j=0; j<values[i]->bv_len; j++) {
2904 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2906 printf("\n");
2910 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2912 int i;
2913 for (i=0; values[i]; i++) {
2914 NTSTATUS status;
2915 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2916 struct GUID guid;
2918 status = GUID_from_ndr_blob(&in, &guid);
2919 if (NT_STATUS_IS_OK(status)) {
2920 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2921 } else {
2922 printf("%s: INVALID GUID\n", field);
2928 dump a sid result from ldap
2930 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2932 int i;
2933 for (i=0; values[i]; i++) {
2934 ssize_t ret;
2935 struct dom_sid sid;
2936 struct dom_sid_buf tmp;
2937 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2938 values[i]->bv_len, &sid);
2939 if (ret == -1) {
2940 return;
2942 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2947 dump ntSecurityDescriptor
2949 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2951 TALLOC_CTX *frame = talloc_stackframe();
2952 struct security_descriptor *psd;
2953 NTSTATUS status;
2955 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2956 values[0]->bv_len, &psd);
2957 if (!NT_STATUS_IS_OK(status)) {
2958 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2959 nt_errstr(status)));
2960 TALLOC_FREE(frame);
2961 return;
2964 if (psd) {
2965 ads_disp_sd(ads, talloc_tos(), psd);
2968 TALLOC_FREE(frame);
2972 dump a string result from ldap
2974 static void dump_string(const char *field, char **values)
2976 int i;
2977 for (i=0; values[i]; i++) {
2978 printf("%s: %s\n", field, values[i]);
2983 dump a field from LDAP on stdout
2984 used for debugging
2987 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2989 const struct {
2990 const char *name;
2991 bool string;
2992 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2993 } handlers[] = {
2994 {"objectGUID", False, dump_guid},
2995 {"netbootGUID", False, dump_guid},
2996 {"nTSecurityDescriptor", False, dump_sd},
2997 {"dnsRecord", False, dump_binary},
2998 {"objectSid", False, dump_sid},
2999 {"securityIdentifier", False, dump_sid},
3000 {"tokenGroups", False, dump_sid},
3001 {"tokenGroupsNoGCAcceptable", False, dump_sid},
3002 {"tokengroupsGlobalandUniversal", False, dump_sid},
3003 {"mS-DS-CreatorSID", False, dump_sid},
3004 {"msExchMailboxGuid", False, dump_guid},
3005 {"msDS-TrustForestTrustInfo", False, dump_binary},
3006 {NULL, True, NULL}
3008 int i;
3010 if (!field) { /* must be end of an entry */
3011 printf("\n");
3012 return False;
3015 for (i=0; handlers[i].name; i++) {
3016 if (strcasecmp_m(handlers[i].name, field) == 0) {
3017 if (!values) /* first time, indicate string or not */
3018 return handlers[i].string;
3019 handlers[i].handler(ads, field, (struct berval **) values);
3020 break;
3023 if (!handlers[i].name) {
3024 if (!values) /* first time, indicate string conversion */
3025 return True;
3026 dump_string(field, (char **)values);
3028 return False;
3032 * Dump a result from LDAP on stdout
3033 * used for debugging
3034 * @param ads connection to ads server
3035 * @param res Results to dump
3038 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
3040 ads_process_results(ads, res, ads_dump_field, NULL);
3044 * Walk through results, calling a function for each entry found.
3045 * The function receives a field name, a berval * array of values,
3046 * and a data area passed through from the start. The function is
3047 * called once with null for field and values at the end of each
3048 * entry.
3049 * @param ads connection to ads server
3050 * @param res Results to process
3051 * @param fn Function for processing each result
3052 * @param data_area user-defined area to pass to function
3054 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
3055 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
3056 void *data_area)
3058 LDAPMessage *msg;
3059 TALLOC_CTX *ctx;
3060 size_t converted_size;
3062 if (!(ctx = talloc_init("ads_process_results")))
3063 return;
3065 for (msg = ads_first_entry(ads, res); msg;
3066 msg = ads_next_entry(ads, msg)) {
3067 char *utf8_field;
3068 BerElement *b;
3070 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
3071 (LDAPMessage *)msg,&b);
3072 utf8_field;
3073 utf8_field=ldap_next_attribute(ads->ldap.ld,
3074 (LDAPMessage *)msg,b)) {
3075 struct berval **ber_vals;
3076 char **str_vals;
3077 char **utf8_vals;
3078 char *field;
3079 bool string;
3081 if (!pull_utf8_talloc(ctx, &field, utf8_field,
3082 &converted_size))
3084 DEBUG(0,("ads_process_results: "
3085 "pull_utf8_talloc failed: %s\n",
3086 strerror(errno)));
3089 string = fn(ads, field, NULL, data_area);
3091 if (string) {
3092 const char **p;
3094 utf8_vals = ldap_get_values(ads->ldap.ld,
3095 (LDAPMessage *)msg, field);
3096 p = discard_const_p(const char *, utf8_vals);
3097 str_vals = ads_pull_strvals(ctx, p);
3098 fn(ads, field, (void **) str_vals, data_area);
3099 ldap_value_free(utf8_vals);
3100 } else {
3101 ber_vals = ldap_get_values_len(ads->ldap.ld,
3102 (LDAPMessage *)msg, field);
3103 fn(ads, field, (void **) ber_vals, data_area);
3105 ldap_value_free_len(ber_vals);
3107 ldap_memfree(utf8_field);
3109 ber_free(b, 0);
3110 talloc_free_children(ctx);
3111 fn(ads, NULL, NULL, data_area); /* completed an entry */
3114 talloc_destroy(ctx);
3118 * count how many replies are in a LDAPMessage
3119 * @param ads connection to ads server
3120 * @param res Results to count
3121 * @return number of replies
3123 int ads_count_replies(ADS_STRUCT *ads, void *res)
3125 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3129 * pull the first entry from a ADS result
3130 * @param ads connection to ads server
3131 * @param res Results of search
3132 * @return first entry from result
3134 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3136 return ldap_first_entry(ads->ldap.ld, res);
3140 * pull the next entry from a ADS result
3141 * @param ads connection to ads server
3142 * @param res Results of search
3143 * @return next entry from result
3145 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3147 return ldap_next_entry(ads->ldap.ld, res);
3151 * pull the first message from a ADS result
3152 * @param ads connection to ads server
3153 * @param res Results of search
3154 * @return first message from result
3156 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3158 return ldap_first_message(ads->ldap.ld, res);
3162 * pull the next message from a ADS result
3163 * @param ads connection to ads server
3164 * @param res Results of search
3165 * @return next message from result
3167 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3169 return ldap_next_message(ads->ldap.ld, res);
3173 * pull a single string from a ADS result
3174 * @param ads connection to ads server
3175 * @param mem_ctx TALLOC_CTX to use for allocating result string
3176 * @param msg Results of search
3177 * @param field Attribute to retrieve
3178 * @return Result string in talloc context
3180 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3181 const char *field)
3183 char **values;
3184 char *ret = NULL;
3185 char *ux_string;
3186 size_t converted_size;
3188 values = ldap_get_values(ads->ldap.ld, msg, field);
3189 if (!values)
3190 return NULL;
3192 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3193 &converted_size))
3195 ret = ux_string;
3197 ldap_value_free(values);
3198 return ret;
3202 * pull an array of strings from a ADS result
3203 * @param ads connection to ads server
3204 * @param mem_ctx TALLOC_CTX to use for allocating result string
3205 * @param msg Results of search
3206 * @param field Attribute to retrieve
3207 * @return Result strings in talloc context
3209 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3210 LDAPMessage *msg, const char *field,
3211 size_t *num_values)
3213 char **values;
3214 char **ret = NULL;
3215 size_t i, converted_size;
3217 values = ldap_get_values(ads->ldap.ld, msg, field);
3218 if (!values)
3219 return NULL;
3221 *num_values = ldap_count_values(values);
3223 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3224 if (!ret) {
3225 ldap_value_free(values);
3226 return NULL;
3229 for (i=0;i<*num_values;i++) {
3230 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3231 &converted_size))
3233 ldap_value_free(values);
3234 return NULL;
3237 ret[i] = NULL;
3239 ldap_value_free(values);
3240 return ret;
3244 * pull an array of strings from a ADS result
3245 * (handle large multivalue attributes with range retrieval)
3246 * @param ads connection to ads server
3247 * @param mem_ctx TALLOC_CTX to use for allocating result string
3248 * @param msg Results of search
3249 * @param field Attribute to retrieve
3250 * @param current_strings strings returned by a previous call to this function
3251 * @param next_attribute The next query should ask for this attribute
3252 * @param num_values How many values did we get this time?
3253 * @param more_values Are there more values to get?
3254 * @return Result strings in talloc context
3256 char **ads_pull_strings_range(ADS_STRUCT *ads,
3257 TALLOC_CTX *mem_ctx,
3258 LDAPMessage *msg, const char *field,
3259 char **current_strings,
3260 const char **next_attribute,
3261 size_t *num_strings,
3262 bool *more_strings)
3264 char *attr;
3265 char *expected_range_attrib, *range_attr = NULL;
3266 BerElement *ptr = NULL;
3267 char **strings;
3268 char **new_strings;
3269 size_t num_new_strings;
3270 unsigned long int range_start;
3271 unsigned long int range_end;
3273 /* we might have been given the whole lot anyway */
3274 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3275 *more_strings = False;
3276 return strings;
3279 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3281 /* look for Range result */
3282 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3283 attr;
3284 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3285 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3286 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3287 range_attr = attr;
3288 break;
3290 ldap_memfree(attr);
3292 if (!range_attr) {
3293 ber_free(ptr, 0);
3294 /* nothing here - this field is just empty */
3295 *more_strings = False;
3296 return NULL;
3299 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3300 &range_start, &range_end) == 2) {
3301 *more_strings = True;
3302 } else {
3303 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3304 &range_start) == 1) {
3305 *more_strings = False;
3306 } else {
3307 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3308 range_attr));
3309 ldap_memfree(range_attr);
3310 *more_strings = False;
3311 return NULL;
3315 if ((*num_strings) != range_start) {
3316 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3317 " - aborting range retrieval\n",
3318 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3319 ldap_memfree(range_attr);
3320 *more_strings = False;
3321 return NULL;
3324 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3326 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3327 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3328 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3329 range_attr, (unsigned long int)range_end - range_start + 1,
3330 (unsigned long int)num_new_strings));
3331 ldap_memfree(range_attr);
3332 *more_strings = False;
3333 return NULL;
3336 strings = talloc_realloc(mem_ctx, current_strings, char *,
3337 *num_strings + num_new_strings);
3339 if (strings == NULL) {
3340 ldap_memfree(range_attr);
3341 *more_strings = False;
3342 return NULL;
3345 if (new_strings && num_new_strings) {
3346 memcpy(&strings[*num_strings], new_strings,
3347 sizeof(*new_strings) * num_new_strings);
3350 (*num_strings) += num_new_strings;
3352 if (*more_strings) {
3353 *next_attribute = talloc_asprintf(mem_ctx,
3354 "%s;range=%d-*",
3355 field,
3356 (int)*num_strings);
3358 if (!*next_attribute) {
3359 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3360 ldap_memfree(range_attr);
3361 *more_strings = False;
3362 return NULL;
3366 ldap_memfree(range_attr);
3368 return strings;
3372 * pull a single uint32_t from a ADS result
3373 * @param ads connection to ads server
3374 * @param msg Results of search
3375 * @param field Attribute to retrieve
3376 * @param v Pointer to int to store result
3377 * @return boolean indicating success
3379 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3380 uint32_t *v)
3382 char **values;
3384 values = ldap_get_values(ads->ldap.ld, msg, field);
3385 if (!values)
3386 return False;
3387 if (!values[0]) {
3388 ldap_value_free(values);
3389 return False;
3392 *v = atoi(values[0]);
3393 ldap_value_free(values);
3394 return True;
3398 * pull a single objectGUID from an ADS result
3399 * @param ads connection to ADS server
3400 * @param msg results of search
3401 * @param guid 37-byte area to receive text guid
3402 * @return boolean indicating success
3404 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3406 DATA_BLOB blob;
3407 NTSTATUS status;
3409 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3410 &blob)) {
3411 return false;
3414 status = GUID_from_ndr_blob(&blob, guid);
3415 talloc_free(blob.data);
3416 return NT_STATUS_IS_OK(status);
3421 * pull a single struct dom_sid from a ADS result
3422 * @param ads connection to ads server
3423 * @param msg Results of search
3424 * @param field Attribute to retrieve
3425 * @param sid Pointer to sid to store result
3426 * @return boolean indicating success
3428 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3429 struct dom_sid *sid)
3431 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3435 * pull an array of struct dom_sids from a ADS result
3436 * @param ads connection to ads server
3437 * @param mem_ctx TALLOC_CTX for allocating sid array
3438 * @param msg Results of search
3439 * @param field Attribute to retrieve
3440 * @param sids pointer to sid array to allocate
3441 * @return the count of SIDs pulled
3443 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3444 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3446 struct berval **values;
3447 int count, i;
3449 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3451 if (!values)
3452 return 0;
3454 for (i=0; values[i]; i++)
3455 /* nop */ ;
3457 if (i) {
3458 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3459 if (!(*sids)) {
3460 ldap_value_free_len(values);
3461 return 0;
3463 } else {
3464 (*sids) = NULL;
3467 count = 0;
3468 for (i=0; values[i]; i++) {
3469 ssize_t ret;
3470 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3471 values[i]->bv_len, &(*sids)[count]);
3472 if (ret != -1) {
3473 struct dom_sid_buf buf;
3474 DBG_DEBUG("pulling SID: %s\n",
3475 dom_sid_str_buf(&(*sids)[count], &buf));
3476 count++;
3480 ldap_value_free_len(values);
3481 return count;
3485 * pull a struct security_descriptor from a ADS result
3486 * @param ads connection to ads server
3487 * @param mem_ctx TALLOC_CTX for allocating sid array
3488 * @param msg Results of search
3489 * @param field Attribute to retrieve
3490 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3491 * @return boolean indicating success
3493 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3494 LDAPMessage *msg, const char *field,
3495 struct security_descriptor **sd)
3497 struct berval **values;
3498 bool ret = true;
3500 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3502 if (!values) return false;
3504 if (values[0]) {
3505 NTSTATUS status;
3506 status = unmarshall_sec_desc(mem_ctx,
3507 (uint8_t *)values[0]->bv_val,
3508 values[0]->bv_len, sd);
3509 if (!NT_STATUS_IS_OK(status)) {
3510 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3511 nt_errstr(status)));
3512 ret = false;
3516 ldap_value_free_len(values);
3517 return ret;
3521 * in order to support usernames longer than 21 characters we need to
3522 * use both the sAMAccountName and the userPrincipalName attributes
3523 * It seems that not all users have the userPrincipalName attribute set
3525 * @param ads connection to ads server
3526 * @param mem_ctx TALLOC_CTX for allocating sid array
3527 * @param msg Results of search
3528 * @return the username
3530 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3531 LDAPMessage *msg)
3533 #if 0 /* JERRY */
3534 char *ret, *p;
3536 /* lookup_name() only works on the sAMAccountName to
3537 returning the username portion of userPrincipalName
3538 breaks winbindd_getpwnam() */
3540 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3541 if (ret && (p = strchr_m(ret, '@'))) {
3542 *p = 0;
3543 return ret;
3545 #endif
3546 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3551 * find the update serial number - this is the core of the ldap cache
3552 * @param ads connection to ads server
3553 * @param ads connection to ADS server
3554 * @param usn Pointer to retrieved update serial number
3555 * @return status of search
3557 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3559 const char *attrs[] = {"highestCommittedUSN", NULL};
3560 ADS_STATUS status;
3561 LDAPMessage *res;
3563 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3564 if (!ADS_ERR_OK(status))
3565 return status;
3567 if (ads_count_replies(ads, res) != 1) {
3568 ads_msgfree(ads, res);
3569 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3572 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3573 ads_msgfree(ads, res);
3574 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3577 ads_msgfree(ads, res);
3578 return ADS_SUCCESS;
3581 /* parse a ADS timestring - typical string is
3582 '20020917091222.0Z0' which means 09:12.22 17th September
3583 2002, timezone 0 */
3584 static time_t ads_parse_time(const char *str)
3586 struct tm tm;
3588 ZERO_STRUCT(tm);
3590 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3591 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3592 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3593 return 0;
3595 tm.tm_year -= 1900;
3596 tm.tm_mon -= 1;
3598 return timegm(&tm);
3601 /********************************************************************
3602 ********************************************************************/
3604 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3606 const char *attrs[] = {"currentTime", NULL};
3607 ADS_STATUS status;
3608 LDAPMessage *res;
3609 char *timestr;
3610 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3611 ADS_STRUCT *ads_s = ads;
3613 /* establish a new ldap tcp session if necessary */
3615 if ( !ads->ldap.ld ) {
3617 * ADS_STRUCT may be being reused after a
3618 * DC lookup, so ads->ldap.ss may already have a
3619 * good address. If not, re-initialize the passed-in
3620 * ADS_STRUCT with the given server.XXXX parameters.
3622 * Note that this doesn't depend on
3623 * ads->server.ldap_server != NULL,
3624 * as the case where ads->server.ldap_server==NULL and
3625 * ads->ldap.ss != zero_address is precisely the DC
3626 * lookup case where ads->ldap.ss was found by going
3627 * through ads_find_dc() again we want to avoid repeating.
3629 if (is_zero_addr(&ads->ldap.ss)) {
3630 ads_s = ads_init(tmp_ctx,
3631 ads->server.realm,
3632 ads->server.workgroup,
3633 ads->server.ldap_server,
3634 ADS_SASL_PLAIN );
3635 if (ads_s == NULL) {
3636 status = ADS_ERROR(LDAP_NO_MEMORY);
3637 goto done;
3642 * Reset ads->config.flags as it can contain the flags
3643 * returned by the previous CLDAP ping when reusing the struct.
3645 ads_s->config.flags = 0;
3647 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3648 status = ads_connect( ads_s );
3649 if ( !ADS_ERR_OK(status))
3650 goto done;
3653 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3654 if (!ADS_ERR_OK(status)) {
3655 goto done;
3658 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3659 if (!timestr) {
3660 ads_msgfree(ads_s, res);
3661 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3662 goto done;
3665 /* but save the time and offset in the original ADS_STRUCT */
3667 ads->config.current_time = ads_parse_time(timestr);
3669 if (ads->config.current_time != 0) {
3670 ads->auth.time_offset = ads->config.current_time - time(NULL);
3671 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3674 ads_msgfree(ads, res);
3676 status = ADS_SUCCESS;
3678 done:
3679 TALLOC_FREE(tmp_ctx);
3681 return status;
3684 /********************************************************************
3685 ********************************************************************/
3687 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3689 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3690 const char *attrs[] = {"domainFunctionality", NULL};
3691 ADS_STATUS status;
3692 LDAPMessage *res;
3693 ADS_STRUCT *ads_s = ads;
3695 *val = DS_DOMAIN_FUNCTION_2000;
3697 /* establish a new ldap tcp session if necessary */
3699 if ( !ads->ldap.ld ) {
3701 * ADS_STRUCT may be being reused after a
3702 * DC lookup, so ads->ldap.ss may already have a
3703 * good address. If not, re-initialize the passed-in
3704 * ADS_STRUCT with the given server.XXXX parameters.
3706 * Note that this doesn't depend on
3707 * ads->server.ldap_server != NULL,
3708 * as the case where ads->server.ldap_server==NULL and
3709 * ads->ldap.ss != zero_address is precisely the DC
3710 * lookup case where ads->ldap.ss was found by going
3711 * through ads_find_dc() again we want to avoid repeating.
3713 if (is_zero_addr(&ads->ldap.ss)) {
3714 ads_s = ads_init(tmp_ctx,
3715 ads->server.realm,
3716 ads->server.workgroup,
3717 ads->server.ldap_server,
3718 ADS_SASL_PLAIN );
3719 if (ads_s == NULL ) {
3720 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3721 goto done;
3726 * Reset ads->config.flags as it can contain the flags
3727 * returned by the previous CLDAP ping when reusing the struct.
3729 ads_s->config.flags = 0;
3731 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3732 status = ads_connect( ads_s );
3733 if ( !ADS_ERR_OK(status))
3734 goto done;
3737 /* If the attribute does not exist assume it is a Windows 2000
3738 functional domain */
3740 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3741 if (!ADS_ERR_OK(status)) {
3742 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3743 status = ADS_SUCCESS;
3745 goto done;
3748 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3749 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3751 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3754 ads_msgfree(ads_s, res);
3756 done:
3757 TALLOC_FREE(tmp_ctx);
3759 return status;
3763 * find the domain sid for our domain
3764 * @param ads connection to ads server
3765 * @param sid Pointer to domain sid
3766 * @return status of search
3768 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3770 const char *attrs[] = {"objectSid", NULL};
3771 LDAPMessage *res;
3772 ADS_STATUS rc;
3774 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3775 attrs, &res);
3776 if (!ADS_ERR_OK(rc)) return rc;
3777 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3778 ads_msgfree(ads, res);
3779 return ADS_ERROR_SYSTEM(ENOENT);
3781 ads_msgfree(ads, res);
3783 return ADS_SUCCESS;
3787 * find our site name
3788 * @param ads connection to ads server
3789 * @param mem_ctx Pointer to talloc context
3790 * @param site_name Pointer to the sitename
3791 * @return status of search
3793 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3795 ADS_STATUS status;
3796 LDAPMessage *res;
3797 const char *dn, *service_name;
3798 const char *attrs[] = { "dsServiceName", NULL };
3800 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3801 if (!ADS_ERR_OK(status)) {
3802 return status;
3805 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3806 if (service_name == NULL) {
3807 ads_msgfree(ads, res);
3808 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3811 ads_msgfree(ads, res);
3813 /* go up three levels */
3814 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3815 if (dn == NULL) {
3816 return ADS_ERROR(LDAP_NO_MEMORY);
3819 *site_name = talloc_strdup(mem_ctx, dn);
3820 if (*site_name == NULL) {
3821 return ADS_ERROR(LDAP_NO_MEMORY);
3824 return status;
3826 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3831 * find the site dn where a machine resides
3832 * @param ads connection to ads server
3833 * @param mem_ctx Pointer to talloc context
3834 * @param computer_name name of the machine
3835 * @param site_name Pointer to the sitename
3836 * @return status of search
3838 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3840 ADS_STATUS status;
3841 LDAPMessage *res;
3842 const char *parent, *filter;
3843 char *config_context = NULL;
3844 char *dn;
3846 /* shortcut a query */
3847 if (strequal(computer_name, ads->config.ldap_server_name)) {
3848 return ads_site_dn(ads, mem_ctx, site_dn);
3851 status = ads_config_path(ads, mem_ctx, &config_context);
3852 if (!ADS_ERR_OK(status)) {
3853 return status;
3856 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3857 if (filter == NULL) {
3858 return ADS_ERROR(LDAP_NO_MEMORY);
3861 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3862 filter, NULL, &res);
3863 if (!ADS_ERR_OK(status)) {
3864 return status;
3867 if (ads_count_replies(ads, res) != 1) {
3868 ads_msgfree(ads, res);
3869 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3872 dn = ads_get_dn(ads, mem_ctx, res);
3873 if (dn == NULL) {
3874 ads_msgfree(ads, res);
3875 return ADS_ERROR(LDAP_NO_MEMORY);
3878 /* go up three levels */
3879 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3880 if (parent == NULL) {
3881 ads_msgfree(ads, res);
3882 TALLOC_FREE(dn);
3883 return ADS_ERROR(LDAP_NO_MEMORY);
3886 *site_dn = talloc_strdup(mem_ctx, parent);
3887 if (*site_dn == NULL) {
3888 ads_msgfree(ads, res);
3889 TALLOC_FREE(dn);
3890 return ADS_ERROR(LDAP_NO_MEMORY);
3893 TALLOC_FREE(dn);
3894 ads_msgfree(ads, res);
3896 return status;
3900 * get the upn suffixes for a domain
3901 * @param ads connection to ads server
3902 * @param mem_ctx Pointer to talloc context
3903 * @param suffixes Pointer to an array of suffixes
3904 * @param num_suffixes Pointer to the number of suffixes
3905 * @return status of search
3907 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3909 ADS_STATUS status;
3910 LDAPMessage *res;
3911 const char *base;
3912 char *config_context = NULL;
3913 const char *attrs[] = { "uPNSuffixes", NULL };
3915 status = ads_config_path(ads, mem_ctx, &config_context);
3916 if (!ADS_ERR_OK(status)) {
3917 return status;
3920 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3921 if (base == NULL) {
3922 return ADS_ERROR(LDAP_NO_MEMORY);
3925 status = ads_search_dn(ads, &res, base, attrs);
3926 if (!ADS_ERR_OK(status)) {
3927 return status;
3930 if (ads_count_replies(ads, res) != 1) {
3931 ads_msgfree(ads, res);
3932 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3935 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3936 if ((*suffixes) == NULL) {
3937 ads_msgfree(ads, res);
3938 return ADS_ERROR(LDAP_NO_MEMORY);
3941 ads_msgfree(ads, res);
3943 return status;
3947 * get the joinable ous for a domain
3948 * @param ads connection to ads server
3949 * @param mem_ctx Pointer to talloc context
3950 * @param ous Pointer to an array of ous
3951 * @param num_ous Pointer to the number of ous
3952 * @return status of search
3954 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3955 TALLOC_CTX *mem_ctx,
3956 char ***ous,
3957 size_t *num_ous)
3959 ADS_STATUS status;
3960 LDAPMessage *res = NULL;
3961 LDAPMessage *msg = NULL;
3962 const char *attrs[] = { "dn", NULL };
3963 int count = 0;
3965 status = ads_search(ads, &res,
3966 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3967 attrs);
3968 if (!ADS_ERR_OK(status)) {
3969 return status;
3972 count = ads_count_replies(ads, res);
3973 if (count < 1) {
3974 ads_msgfree(ads, res);
3975 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3978 for (msg = ads_first_entry(ads, res); msg;
3979 msg = ads_next_entry(ads, msg)) {
3980 const char **p = discard_const_p(const char *, *ous);
3981 char *dn = NULL;
3983 dn = ads_get_dn(ads, talloc_tos(), msg);
3984 if (!dn) {
3985 ads_msgfree(ads, res);
3986 return ADS_ERROR(LDAP_NO_MEMORY);
3989 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3990 TALLOC_FREE(dn);
3991 ads_msgfree(ads, res);
3992 return ADS_ERROR(LDAP_NO_MEMORY);
3995 TALLOC_FREE(dn);
3996 *ous = discard_const_p(char *, p);
3999 ads_msgfree(ads, res);
4001 return status;
4006 * pull a struct dom_sid from an extended dn string
4007 * @param mem_ctx TALLOC_CTX
4008 * @param extended_dn string
4009 * @param flags string type of extended_dn
4010 * @param sid pointer to a struct dom_sid
4011 * @return NT_STATUS_OK on success,
4012 * NT_INVALID_PARAMETER on error,
4013 * NT_STATUS_NOT_FOUND if no SID present
4015 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
4016 const char *extended_dn,
4017 enum ads_extended_dn_flags flags,
4018 struct dom_sid *sid)
4020 char *p, *q, *dn;
4022 if (!extended_dn) {
4023 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4026 /* otherwise extended_dn gets stripped off */
4027 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
4028 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4031 * ADS_EXTENDED_DN_HEX_STRING:
4032 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4034 * ADS_EXTENDED_DN_STRING (only with w2k3):
4035 * <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
4037 * Object with no SID, such as an Exchange Public Folder
4038 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4041 p = strchr(dn, ';');
4042 if (!p) {
4043 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4046 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
4047 DEBUG(5,("No SID present in extended dn\n"));
4048 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
4051 p += strlen(";<SID=");
4053 q = strchr(p, '>');
4054 if (!q) {
4055 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4058 *q = '\0';
4060 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
4062 switch (flags) {
4064 case ADS_EXTENDED_DN_STRING:
4065 if (!string_to_sid(sid, p)) {
4066 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4068 break;
4069 case ADS_EXTENDED_DN_HEX_STRING: {
4070 ssize_t ret;
4071 fstring buf;
4072 size_t buf_len;
4074 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
4075 if (buf_len == 0) {
4076 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4079 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
4080 if (ret == -1) {
4081 DEBUG(10,("failed to parse sid\n"));
4082 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4084 break;
4086 default:
4087 DEBUG(10,("unknown extended dn format\n"));
4088 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4091 return ADS_ERROR_NT(NT_STATUS_OK);
4094 /********************************************************************
4095 ********************************************************************/
4097 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4099 LDAPMessage *res = NULL;
4100 ADS_STATUS status;
4101 int count = 0;
4102 char *name = NULL;
4104 status = ads_find_machine_acct(ads, &res, machine_name);
4105 if (!ADS_ERR_OK(status)) {
4106 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4107 lp_netbios_name()));
4108 goto out;
4111 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4112 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4113 goto out;
4116 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4117 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4120 out:
4121 ads_msgfree(ads, res);
4123 return name;
4126 /********************************************************************
4127 ********************************************************************/
4129 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4130 LDAPMessage *msg, size_t *num_values)
4132 const char *field = "msDS-AdditionalDnsHostName";
4133 struct berval **values = NULL;
4134 char **ret = NULL;
4135 size_t i, converted_size;
4138 * Windows DC implicitly adds a short name for each FQDN added to
4139 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4140 * suffix "\0$" which we should ignore (see bug #14406).
4143 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4144 if (values == NULL) {
4145 return NULL;
4148 *num_values = ldap_count_values_len(values);
4150 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4151 if (ret == NULL) {
4152 ldap_value_free_len(values);
4153 return NULL;
4156 for (i = 0; i < *num_values; i++) {
4157 ret[i] = NULL;
4158 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4159 values[i]->bv_val,
4160 strnlen(values[i]->bv_val,
4161 values[i]->bv_len),
4162 &ret[i], &converted_size)) {
4163 ldap_value_free_len(values);
4164 return NULL;
4167 ret[i] = NULL;
4169 ldap_value_free_len(values);
4170 return ret;
4173 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4174 ADS_STRUCT *ads,
4175 const char *machine_name,
4176 char ***hostnames_array,
4177 size_t *num_hostnames)
4179 ADS_STATUS status;
4180 LDAPMessage *res = NULL;
4181 int count;
4183 status = ads_find_machine_acct(ads,
4184 &res,
4185 machine_name);
4186 if (!ADS_ERR_OK(status)) {
4187 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4188 machine_name));
4189 return status;
4192 count = ads_count_replies(ads, res);
4193 if (count != 1) {
4194 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4195 goto done;
4198 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4199 if (*hostnames_array == NULL) {
4200 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4201 machine_name));
4202 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4203 goto done;
4206 done:
4207 ads_msgfree(ads, res);
4209 return status;
4212 /********************************************************************
4213 ********************************************************************/
4215 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4217 LDAPMessage *res = NULL;
4218 ADS_STATUS status;
4219 int count = 0;
4220 char *name = NULL;
4222 status = ads_find_machine_acct(ads, &res, machine_name);
4223 if (!ADS_ERR_OK(status)) {
4224 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4225 lp_netbios_name()));
4226 goto out;
4229 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4230 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4231 goto out;
4234 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4235 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4238 out:
4239 ads_msgfree(ads, res);
4241 return name;
4244 /********************************************************************
4245 ********************************************************************/
4247 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4249 LDAPMessage *res = NULL;
4250 ADS_STATUS status;
4251 int count = 0;
4252 char *name = NULL;
4253 bool ok = false;
4255 status = ads_find_machine_acct(ads, &res, machine_name);
4256 if (!ADS_ERR_OK(status)) {
4257 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4258 lp_netbios_name()));
4259 goto out;
4262 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4263 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4264 goto out;
4267 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4268 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4271 out:
4272 ads_msgfree(ads, res);
4273 if (name != NULL) {
4274 ok = (strlen(name) > 0);
4276 TALLOC_FREE(name);
4277 return ok;
4280 #if 0
4282 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4285 * Join a machine to a realm
4286 * Creates the machine account and sets the machine password
4287 * @param ads connection to ads server
4288 * @param machine name of host to add
4289 * @param org_unit Organizational unit to place machine in
4290 * @return status of join
4292 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4293 uint32_t account_type, const char *org_unit)
4295 ADS_STATUS status;
4296 LDAPMessage *res = NULL;
4297 char *machine;
4299 /* machine name must be lowercase */
4300 machine = SMB_STRDUP(machine_name);
4301 strlower_m(machine);
4304 status = ads_find_machine_acct(ads, (void **)&res, machine);
4305 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4306 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4307 status = ads_leave_realm(ads, machine);
4308 if (!ADS_ERR_OK(status)) {
4309 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4310 machine, ads->config.realm));
4311 return status;
4315 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4316 if (!ADS_ERR_OK(status)) {
4317 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4318 SAFE_FREE(machine);
4319 return status;
4322 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4323 if (!ADS_ERR_OK(status)) {
4324 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4325 SAFE_FREE(machine);
4326 return status;
4329 SAFE_FREE(machine);
4330 ads_msgfree(ads, res);
4332 return status;
4334 #endif
4337 * Delete a machine from the realm
4338 * @param ads connection to ads server
4339 * @param hostname Machine to remove
4340 * @return status of delete
4342 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4344 ADS_STATUS status;
4345 void *msg;
4346 LDAPMessage *res;
4347 char *hostnameDN, *host;
4348 int rc;
4349 LDAPControl ldap_control;
4350 LDAPControl * pldap_control[2] = {NULL, NULL};
4352 pldap_control[0] = &ldap_control;
4353 memset(&ldap_control, 0, sizeof(LDAPControl));
4354 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4356 /* hostname must be lowercase */
4357 host = SMB_STRDUP(hostname);
4358 if (!strlower_m(host)) {
4359 SAFE_FREE(host);
4360 return ADS_ERROR_SYSTEM(EINVAL);
4363 status = ads_find_machine_acct(ads, &res, host);
4364 if (!ADS_ERR_OK(status)) {
4365 DEBUG(0, ("Host account for %s does not exist.\n", host));
4366 SAFE_FREE(host);
4367 return status;
4370 msg = ads_first_entry(ads, res);
4371 if (!msg) {
4372 SAFE_FREE(host);
4373 return ADS_ERROR_SYSTEM(ENOENT);
4376 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4377 if (hostnameDN == NULL) {
4378 SAFE_FREE(host);
4379 return ADS_ERROR_SYSTEM(ENOENT);
4382 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4383 if (rc) {
4384 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4385 }else {
4386 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4389 if (rc != LDAP_SUCCESS) {
4390 const char *attrs[] = { "cn", NULL };
4391 LDAPMessage *msg_sub;
4393 /* we only search with scope ONE, we do not expect any further
4394 * objects to be created deeper */
4396 status = ads_do_search_retry(ads, hostnameDN,
4397 LDAP_SCOPE_ONELEVEL,
4398 "(objectclass=*)", attrs, &res);
4400 if (!ADS_ERR_OK(status)) {
4401 SAFE_FREE(host);
4402 TALLOC_FREE(hostnameDN);
4403 return status;
4406 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4407 msg_sub = ads_next_entry(ads, msg_sub)) {
4409 char *dn = NULL;
4411 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4412 SAFE_FREE(host);
4413 TALLOC_FREE(hostnameDN);
4414 return ADS_ERROR(LDAP_NO_MEMORY);
4417 status = ads_del_dn(ads, dn);
4418 if (!ADS_ERR_OK(status)) {
4419 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4420 SAFE_FREE(host);
4421 TALLOC_FREE(dn);
4422 TALLOC_FREE(hostnameDN);
4423 return status;
4426 TALLOC_FREE(dn);
4429 /* there should be no subordinate objects anymore */
4430 status = ads_do_search_retry(ads, hostnameDN,
4431 LDAP_SCOPE_ONELEVEL,
4432 "(objectclass=*)", attrs, &res);
4434 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4435 SAFE_FREE(host);
4436 TALLOC_FREE(hostnameDN);
4437 return status;
4440 /* delete hostnameDN now */
4441 status = ads_del_dn(ads, hostnameDN);
4442 if (!ADS_ERR_OK(status)) {
4443 SAFE_FREE(host);
4444 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4445 TALLOC_FREE(hostnameDN);
4446 return status;
4450 TALLOC_FREE(hostnameDN);
4452 status = ads_find_machine_acct(ads, &res, host);
4453 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4454 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4455 DEBUG(3, ("Failed to remove host account.\n"));
4456 SAFE_FREE(host);
4457 return status;
4460 SAFE_FREE(host);
4461 return ADS_SUCCESS;
4465 * pull all token-sids from an LDAP dn
4466 * @param ads connection to ads server
4467 * @param mem_ctx TALLOC_CTX for allocating sid array
4468 * @param dn of LDAP object
4469 * @param user_sid pointer to struct dom_sid (objectSid)
4470 * @param primary_group_sid pointer to struct dom_sid (self composed)
4471 * @param sids pointer to sid array to allocate
4472 * @param num_sids counter of SIDs pulled
4473 * @return status of token query
4475 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4476 TALLOC_CTX *mem_ctx,
4477 const char *dn,
4478 struct dom_sid *user_sid,
4479 struct dom_sid *primary_group_sid,
4480 struct dom_sid **sids,
4481 size_t *num_sids)
4483 ADS_STATUS status;
4484 LDAPMessage *res = NULL;
4485 int count = 0;
4486 size_t tmp_num_sids;
4487 struct dom_sid *tmp_sids;
4488 struct dom_sid tmp_user_sid;
4489 struct dom_sid tmp_primary_group_sid;
4490 uint32_t pgid;
4491 const char *attrs[] = {
4492 "objectSid",
4493 "tokenGroups",
4494 "primaryGroupID",
4495 NULL
4498 status = ads_search_retry_dn(ads, &res, dn, attrs);
4499 if (!ADS_ERR_OK(status)) {
4500 return status;
4503 count = ads_count_replies(ads, res);
4504 if (count != 1) {
4505 ads_msgfree(ads, res);
4506 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4509 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4510 ads_msgfree(ads, res);
4511 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4514 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4515 ads_msgfree(ads, res);
4516 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4520 /* hack to compose the primary group sid without knowing the
4521 * domsid */
4523 struct dom_sid domsid;
4525 sid_copy(&domsid, &tmp_user_sid);
4527 if (!sid_split_rid(&domsid, NULL)) {
4528 ads_msgfree(ads, res);
4529 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4532 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4533 ads_msgfree(ads, res);
4534 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4538 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4540 if (tmp_num_sids == 0 || !tmp_sids) {
4541 ads_msgfree(ads, res);
4542 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4545 if (num_sids) {
4546 *num_sids = tmp_num_sids;
4549 if (sids) {
4550 *sids = tmp_sids;
4553 if (user_sid) {
4554 *user_sid = tmp_user_sid;
4557 if (primary_group_sid) {
4558 *primary_group_sid = tmp_primary_group_sid;
4561 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4563 ads_msgfree(ads, res);
4564 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4568 * Find a sAMAccountName in LDAP
4569 * @param ads connection to ads server
4570 * @param mem_ctx TALLOC_CTX for allocating sid array
4571 * @param samaccountname to search
4572 * @param uac_ret uint32_t pointer userAccountControl attribute value
4573 * @param dn_ret pointer to dn
4574 * @return status of token query
4576 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4577 TALLOC_CTX *mem_ctx,
4578 const char *samaccountname,
4579 uint32_t *uac_ret,
4580 const char **dn_ret)
4582 ADS_STATUS status;
4583 const char *attrs[] = { "userAccountControl", NULL };
4584 const char *filter;
4585 LDAPMessage *res = NULL;
4586 char *dn = NULL;
4587 uint32_t uac = 0;
4589 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4590 samaccountname);
4591 if (filter == NULL) {
4592 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4593 goto out;
4596 status = ads_do_search_all(ads, ads->config.bind_path,
4597 LDAP_SCOPE_SUBTREE,
4598 filter, attrs, &res);
4600 if (!ADS_ERR_OK(status)) {
4601 goto out;
4604 if (ads_count_replies(ads, res) != 1) {
4605 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4606 goto out;
4609 dn = ads_get_dn(ads, talloc_tos(), res);
4610 if (dn == NULL) {
4611 status = ADS_ERROR(LDAP_NO_MEMORY);
4612 goto out;
4615 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4616 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4617 goto out;
4620 if (uac_ret) {
4621 *uac_ret = uac;
4624 if (dn_ret) {
4625 *dn_ret = talloc_strdup(mem_ctx, dn);
4626 if (!*dn_ret) {
4627 status = ADS_ERROR(LDAP_NO_MEMORY);
4628 goto out;
4631 out:
4632 TALLOC_FREE(dn);
4633 ads_msgfree(ads, res);
4635 return status;
4639 * find our configuration path
4640 * @param ads connection to ads server
4641 * @param mem_ctx Pointer to talloc context
4642 * @param config_path Pointer to the config path
4643 * @return status of search
4645 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4646 TALLOC_CTX *mem_ctx,
4647 char **config_path)
4649 ADS_STATUS status;
4650 LDAPMessage *res = NULL;
4651 const char *config_context = NULL;
4652 const char *attrs[] = { "configurationNamingContext", NULL };
4654 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4655 "(objectclass=*)", attrs, &res);
4656 if (!ADS_ERR_OK(status)) {
4657 return status;
4660 config_context = ads_pull_string(ads, mem_ctx, res,
4661 "configurationNamingContext");
4662 ads_msgfree(ads, res);
4663 if (!config_context) {
4664 return ADS_ERROR(LDAP_NO_MEMORY);
4667 if (config_path) {
4668 *config_path = talloc_strdup(mem_ctx, config_context);
4669 if (!*config_path) {
4670 return ADS_ERROR(LDAP_NO_MEMORY);
4674 return ADS_ERROR(LDAP_SUCCESS);
4678 * find the displayName of an extended right
4679 * @param ads connection to ads server
4680 * @param config_path The config path
4681 * @param mem_ctx Pointer to talloc context
4682 * @param GUID struct of the rightsGUID
4683 * @return status of search
4685 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4686 const char *config_path,
4687 TALLOC_CTX *mem_ctx,
4688 const struct GUID *rights_guid)
4690 ADS_STATUS rc;
4691 LDAPMessage *res = NULL;
4692 char *expr = NULL;
4693 const char *attrs[] = { "displayName", NULL };
4694 const char *result = NULL;
4695 const char *path;
4697 if (!ads || !mem_ctx || !rights_guid) {
4698 goto done;
4701 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4702 GUID_string(mem_ctx, rights_guid));
4703 if (!expr) {
4704 goto done;
4707 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4708 if (!path) {
4709 goto done;
4712 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4713 expr, attrs, &res);
4714 if (!ADS_ERR_OK(rc)) {
4715 goto done;
4718 if (ads_count_replies(ads, res) != 1) {
4719 goto done;
4722 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4724 done:
4725 ads_msgfree(ads, res);
4726 return result;
4730 * verify or build and verify an account ou
4731 * @param mem_ctx Pointer to talloc context
4732 * @param ads connection to ads server
4733 * @param account_ou
4734 * @return status of search
4737 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4738 ADS_STRUCT *ads,
4739 const char **account_ou)
4741 char **exploded_dn;
4742 const char *name;
4743 char *ou_string;
4745 if (account_ou == NULL) {
4746 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4749 if (*account_ou != NULL) {
4750 exploded_dn = ldap_explode_dn(*account_ou, 0);
4751 if (exploded_dn) {
4752 ldap_value_free(exploded_dn);
4753 return ADS_SUCCESS;
4757 ou_string = ads_ou_string(ads, *account_ou);
4758 if (!ou_string) {
4759 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4762 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4763 ads->config.bind_path);
4764 SAFE_FREE(ou_string);
4766 if (!name) {
4767 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4770 exploded_dn = ldap_explode_dn(name, 0);
4771 if (!exploded_dn) {
4772 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4774 ldap_value_free(exploded_dn);
4776 *account_ou = name;
4777 return ADS_SUCCESS;
4780 #endif