s3:libads: call ldap_set_option(LDAP_OPT_PROTOCOL_VERSION) as soon as possible
[samba.git] / source3 / libads / ldap.c
blob211a18472a8397d5e5866e2d9c3bb4fd3e77c984
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;
826 zero_sockaddr(&existing_ss);
829 * ads_connect can be passed in a reused ADS_STRUCT
830 * with an existing non-zero ads->ldap.ss IP address
831 * that was stored by going through ads_find_dc()
832 * if ads->server.ldap_server was NULL.
834 * If ads->server.ldap_server is still NULL but
835 * the target address isn't the zero address, then
836 * store that address off off before zeroing out
837 * ads->ldap so we don't keep doing multiple calls
838 * to ads_find_dc() in the reuse case.
840 * If a caller wants a clean ADS_STRUCT they
841 * will TALLOC_FREE it and allocate a new one
842 * by calling ads_init(), which ensures
843 * ads->ldap.ss is a properly zero'ed out valid IP
844 * address.
846 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
847 /* Save off the address we previously found by ads_find_dc(). */
848 existing_ss = ads->ldap.ss;
851 ads_zero_ldap(ads);
852 ZERO_STRUCT(ads->ldap_tls_data);
853 ZERO_STRUCT(ads->ldap_wrap_data);
854 ads->ldap.last_attempt = time_mono(NULL);
855 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
857 /* try with a user specified server */
859 if (DEBUGLEVEL >= 11) {
860 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
861 DEBUG(11,("ads_connect: entering\n"));
862 DEBUGADD(11,("%s\n", s));
863 TALLOC_FREE(s);
866 if (ads->server.ldap_server) {
867 bool ok = false;
868 struct sockaddr_storage ss;
870 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
871 ads->server.ldap_server);
872 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
873 if (!ok) {
874 DEBUG(5,("ads_connect: unable to resolve name %s\n",
875 ads->server.ldap_server));
876 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
877 goto out;
880 if (is_zero_addr(&ss)) {
881 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
882 goto out;
885 ok = ads_try_connect(ads, ads->server.gc, &ss);
886 if (ok) {
887 goto got_connection;
890 /* The choice of which GC use is handled one level up in
891 ads_connect_gc(). If we continue on from here with
892 ads_find_dc() we will get GC searches on port 389 which
893 doesn't work. --jerry */
895 if (ads->server.gc == true) {
896 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
899 if (ads->server.no_fallback) {
900 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
901 goto out;
905 if (!is_zero_addr(&existing_ss)) {
906 /* We saved off who we should talk to. */
907 bool ok = ads_try_connect(ads,
908 ads->server.gc,
909 &existing_ss);
910 if (ok) {
911 goto got_connection;
914 * Keep trying to find a server and fall through
915 * into ads_find_dc() again.
917 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
918 "trying to find another DC.\n");
921 ntstatus = ads_find_dc(ads);
922 if (NT_STATUS_IS_OK(ntstatus)) {
923 goto got_connection;
926 status = ADS_ERROR_NT(ntstatus);
927 goto out;
929 got_connection:
931 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
932 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
934 if (!ads->auth.user_name) {
935 /* Must use the userPrincipalName value here or sAMAccountName
936 and not servicePrincipalName; found by Guenther Deschner */
937 ads->auth.user_name = talloc_asprintf(ads,
938 "%s$",
939 lp_netbios_name());
940 if (ads->auth.user_name == NULL) {
941 DBG_ERR("talloc_asprintf failed\n");
942 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
943 goto out;
947 if (ads->auth.realm == NULL) {
948 ads->auth.realm = talloc_strdup(ads, ads->config.realm);
949 if (ads->auth.realm == NULL) {
950 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
951 goto out;
955 if (!ads->auth.kdc_server) {
956 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
957 ads->auth.kdc_server = talloc_strdup(ads, addr);
958 if (ads->auth.kdc_server == NULL) {
959 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
960 goto out;
964 /* If the caller() requested no LDAP bind, then we are done */
966 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
967 status = ADS_SUCCESS;
968 goto out;
971 ads->ldap_tls_data.mem_ctx = talloc_init("ads LDAP TLS connection memory");
972 if (!ads->ldap_tls_data.mem_ctx) {
973 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
974 goto out;
977 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
978 if (!ads->ldap_wrap_data.mem_ctx) {
979 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
980 goto out;
983 /* Otherwise setup the TCP LDAP session */
985 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
986 &ads->ldap.ss,
987 ads->ldap.port, lp_ldap_timeout());
988 if (ads->ldap.ld == NULL) {
989 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
990 goto out;
992 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
994 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
996 /* cache the successful connection for workgroup and realm */
997 if (ads_closest_dc(ads)) {
998 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
999 saf_store( ads->server.realm, ads->config.ldap_server_name);
1002 /* fill in the current time and offsets */
1004 status = ads_current_time( ads );
1005 if ( !ADS_ERR_OK(status) ) {
1006 goto out;
1009 /* Now do the bind */
1011 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
1012 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
1013 goto out;
1016 status = ads_sasl_bind(ads);
1018 out:
1019 if (DEBUGLEVEL >= 11) {
1020 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1021 DEBUG(11,("ads_connect: leaving with: %s\n",
1022 ads_errstr(status)));
1023 DEBUGADD(11,("%s\n", s));
1024 TALLOC_FREE(s);
1027 return status;
1031 * Connect to the LDAP server using given credentials
1032 * @param ads Pointer to an existing ADS_STRUCT
1033 * @return status of connection
1035 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
1037 ads->auth.flags |= ADS_AUTH_USER_CREDS;
1039 return ads_connect(ads);
1043 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1044 * @param ads Pointer to an existing ADS_STRUCT
1046 * Sets the ads->ldap.ss to a valid
1047 * zero ip address that can be detected by
1048 * our is_zero_addr() function. Otherwise
1049 * it is left as AF_UNSPEC (0).
1051 void ads_zero_ldap(ADS_STRUCT *ads)
1053 ZERO_STRUCT(ads->ldap);
1055 * Initialize the sockaddr_storage so we can use
1056 * sockaddr test functions against it.
1058 zero_sockaddr(&ads->ldap.ss);
1062 * Disconnect the LDAP server
1063 * @param ads Pointer to an existing ADS_STRUCT
1065 void ads_disconnect(ADS_STRUCT *ads)
1067 if (ads->ldap.ld) {
1068 ldap_unbind(ads->ldap.ld);
1069 ads->ldap.ld = NULL;
1071 if (ads->ldap_tls_data.mem_ctx) {
1072 talloc_free(ads->ldap_tls_data.mem_ctx);
1074 if (ads->ldap_wrap_data.wrap_ops &&
1075 ads->ldap_wrap_data.wrap_ops->disconnect) {
1076 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1078 if (ads->ldap_wrap_data.mem_ctx) {
1079 talloc_free(ads->ldap_wrap_data.mem_ctx);
1081 ads_zero_ldap(ads);
1082 ZERO_STRUCT(ads->ldap_tls_data);
1083 ZERO_STRUCT(ads->ldap_wrap_data);
1087 Duplicate a struct berval into talloc'ed memory
1089 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1091 struct berval *value;
1093 if (!in_val) return NULL;
1095 value = talloc_zero(ctx, struct berval);
1096 if (value == NULL)
1097 return NULL;
1098 if (in_val->bv_len == 0) return value;
1100 value->bv_len = in_val->bv_len;
1101 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1102 in_val->bv_len);
1103 return value;
1107 Make a values list out of an array of (struct berval *)
1109 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1110 const struct berval **in_vals)
1112 struct berval **values;
1113 int i;
1115 if (!in_vals) return NULL;
1116 for (i=0; in_vals[i]; i++)
1117 ; /* count values */
1118 values = talloc_zero_array(ctx, struct berval *, i+1);
1119 if (!values) return NULL;
1121 for (i=0; in_vals[i]; i++) {
1122 values[i] = dup_berval(ctx, in_vals[i]);
1124 return values;
1128 UTF8-encode a values list out of an array of (char *)
1130 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1132 char **values;
1133 int i;
1134 size_t size;
1136 if (!in_vals) return NULL;
1137 for (i=0; in_vals[i]; i++)
1138 ; /* count values */
1139 values = talloc_zero_array(ctx, char *, i+1);
1140 if (!values) return NULL;
1142 for (i=0; in_vals[i]; i++) {
1143 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1144 TALLOC_FREE(values);
1145 return NULL;
1148 return values;
1152 Pull a (char *) array out of a UTF8-encoded values list
1154 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1156 char **values;
1157 int i;
1158 size_t converted_size;
1160 if (!in_vals) return NULL;
1161 for (i=0; in_vals[i]; i++)
1162 ; /* count values */
1163 values = talloc_zero_array(ctx, char *, i+1);
1164 if (!values) return NULL;
1166 for (i=0; in_vals[i]; i++) {
1167 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1168 &converted_size)) {
1169 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1170 "%s\n", strerror(errno)));
1173 return values;
1177 * Do a search with paged results. cookie must be null on the first
1178 * call, and then returned on each subsequent call. It will be null
1179 * again when the entire search is complete
1180 * @param ads connection to ads server
1181 * @param bind_path Base dn for the search
1182 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1183 * @param expr Search expression - specified in local charset
1184 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1185 * @param res ** which will contain results - free res* with ads_msgfree()
1186 * @param count Number of entries retrieved on this page
1187 * @param cookie The paged results cookie to be returned on subsequent calls
1188 * @return status of search
1190 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1191 const char *bind_path,
1192 int scope, const char *expr,
1193 const char **attrs, void *args,
1194 LDAPMessage **res,
1195 int *count, struct berval **cookie)
1197 int rc, i, version;
1198 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1199 size_t converted_size;
1200 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1201 BerElement *cookie_be = NULL;
1202 struct berval *cookie_bv= NULL;
1203 BerElement *ext_be = NULL;
1204 struct berval *ext_bv= NULL;
1206 TALLOC_CTX *ctx;
1207 ads_control *external_control = (ads_control *) args;
1209 *res = NULL;
1211 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1212 return ADS_ERROR(LDAP_NO_MEMORY);
1214 /* 0 means the conversion worked but the result was empty
1215 so we only fail if it's -1. In any case, it always
1216 at least nulls out the dest */
1217 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1218 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1220 rc = LDAP_NO_MEMORY;
1221 goto done;
1224 if (!attrs || !(*attrs))
1225 search_attrs = NULL;
1226 else {
1227 /* This would be the utf8-encoded version...*/
1228 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1229 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1230 rc = LDAP_NO_MEMORY;
1231 goto done;
1235 /* Paged results only available on ldap v3 or later */
1236 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1237 if (version < LDAP_VERSION3) {
1238 rc = LDAP_NOT_SUPPORTED;
1239 goto done;
1242 cookie_be = ber_alloc_t(LBER_USE_DER);
1243 if (*cookie) {
1244 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1245 ber_bvfree(*cookie); /* don't need it from last time */
1246 *cookie = NULL;
1247 } else {
1248 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1250 ber_flatten(cookie_be, &cookie_bv);
1251 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1252 PagedResults.ldctl_iscritical = (char) 1;
1253 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1254 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1256 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1257 NoReferrals.ldctl_iscritical = (char) 0;
1258 NoReferrals.ldctl_value.bv_len = 0;
1259 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1261 if (external_control &&
1262 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1263 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1265 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1266 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1268 /* win2k does not accept a ldctl_value being passed in */
1270 if (external_control->val != 0) {
1272 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1273 rc = LDAP_NO_MEMORY;
1274 goto done;
1277 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1278 rc = LDAP_NO_MEMORY;
1279 goto done;
1281 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1282 rc = LDAP_NO_MEMORY;
1283 goto done;
1286 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1287 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1289 } else {
1290 ExternalCtrl.ldctl_value.bv_len = 0;
1291 ExternalCtrl.ldctl_value.bv_val = NULL;
1294 controls[0] = &NoReferrals;
1295 controls[1] = &PagedResults;
1296 controls[2] = &ExternalCtrl;
1297 controls[3] = NULL;
1299 } else {
1300 controls[0] = &NoReferrals;
1301 controls[1] = &PagedResults;
1302 controls[2] = NULL;
1305 /* we need to disable referrals as the openldap libs don't
1306 handle them and paged results at the same time. Using them
1307 together results in the result record containing the server
1308 page control being removed from the result list (tridge/jmcd)
1310 leaving this in despite the control that says don't generate
1311 referrals, in case the server doesn't support it (jmcd)
1313 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1315 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1316 search_attrs, 0, controls,
1317 NULL, LDAP_NO_LIMIT,
1318 (LDAPMessage **)res);
1320 ber_free(cookie_be, 1);
1321 ber_bvfree(cookie_bv);
1323 if (rc) {
1324 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1325 ldap_err2string(rc)));
1326 if (rc == LDAP_OTHER) {
1327 char *ldap_errmsg;
1328 int ret;
1330 ret = ldap_parse_result(ads->ldap.ld,
1331 *res,
1332 NULL,
1333 NULL,
1334 &ldap_errmsg,
1335 NULL,
1336 NULL,
1338 if (ret == LDAP_SUCCESS) {
1339 DEBUG(3, ("ldap_search_with_timeout(%s) "
1340 "error: %s\n", expr, ldap_errmsg));
1341 ldap_memfree(ldap_errmsg);
1344 goto done;
1347 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1348 NULL, &rcontrols, 0);
1350 if (!rcontrols) {
1351 goto done;
1354 for (i=0; rcontrols[i]; i++) {
1355 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1356 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1357 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1358 &cookie_bv);
1359 /* the berval is the cookie, but must be freed when
1360 it is all done */
1361 if (cookie_bv->bv_len) /* still more to do */
1362 *cookie=ber_bvdup(cookie_bv);
1363 else
1364 *cookie=NULL;
1365 ber_bvfree(cookie_bv);
1366 ber_free(cookie_be, 1);
1367 break;
1370 ldap_controls_free(rcontrols);
1372 done:
1373 talloc_destroy(ctx);
1375 if (ext_be) {
1376 ber_free(ext_be, 1);
1379 if (ext_bv) {
1380 ber_bvfree(ext_bv);
1383 if (rc != LDAP_SUCCESS && *res != NULL) {
1384 ads_msgfree(ads, *res);
1385 *res = NULL;
1388 /* if/when we decide to utf8-encode attrs, take out this next line */
1389 TALLOC_FREE(search_attrs);
1391 return ADS_ERROR(rc);
1394 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1395 int scope, const char *expr,
1396 const char **attrs, LDAPMessage **res,
1397 int *count, struct berval **cookie)
1399 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1404 * Get all results for a search. This uses ads_do_paged_search() to return
1405 * all entries in a large search.
1406 * @param ads connection to ads server
1407 * @param bind_path Base dn for the search
1408 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1409 * @param expr Search expression
1410 * @param attrs Attributes to retrieve
1411 * @param res ** which will contain results - free res* with ads_msgfree()
1412 * @return status of search
1414 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1415 int scope, const char *expr,
1416 const char **attrs, void *args,
1417 LDAPMessage **res)
1419 struct berval *cookie = NULL;
1420 int count = 0;
1421 ADS_STATUS status;
1423 *res = NULL;
1424 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1425 &count, &cookie);
1427 if (!ADS_ERR_OK(status))
1428 return status;
1430 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1431 while (cookie) {
1432 LDAPMessage *res2 = NULL;
1433 LDAPMessage *msg, *next;
1435 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1436 attrs, args, &res2, &count, &cookie);
1437 if (!ADS_ERR_OK(status)) {
1438 break;
1441 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1442 that this works on all ldap libs, but I have only tested with openldap */
1443 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1444 next = ads_next_message(ads, msg);
1445 ldap_add_result_entry((LDAPMessage **)res, msg);
1447 /* note that we do not free res2, as the memory is now
1448 part of the main returned list */
1450 #else
1451 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1452 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1453 #endif
1455 return status;
1458 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1459 int scope, const char *expr,
1460 const char **attrs, LDAPMessage **res)
1462 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1465 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1466 int scope, const char *expr,
1467 const char **attrs, uint32_t sd_flags,
1468 LDAPMessage **res)
1470 ads_control args;
1472 args.control = ADS_SD_FLAGS_OID;
1473 args.val = sd_flags;
1474 args.critical = True;
1476 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1481 * Run a function on all results for a search. Uses ads_do_paged_search() and
1482 * runs the function as each page is returned, using ads_process_results()
1483 * @param ads connection to ads server
1484 * @param bind_path Base dn for the search
1485 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1486 * @param expr Search expression - specified in local charset
1487 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1488 * @param fn Function which takes attr name, values list, and data_area
1489 * @param data_area Pointer which is passed to function on each call
1490 * @return status of search
1492 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1493 int scope, const char *expr, const char **attrs,
1494 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1495 void *data_area)
1497 struct berval *cookie = NULL;
1498 int count = 0;
1499 ADS_STATUS status;
1500 LDAPMessage *res;
1502 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1503 &count, &cookie);
1505 if (!ADS_ERR_OK(status)) return status;
1507 ads_process_results(ads, res, fn, data_area);
1508 ads_msgfree(ads, res);
1510 while (cookie) {
1511 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1512 &res, &count, &cookie);
1514 if (!ADS_ERR_OK(status)) break;
1516 ads_process_results(ads, res, fn, data_area);
1517 ads_msgfree(ads, res);
1520 return status;
1524 * Do a search with a timeout.
1525 * @param ads connection to ads server
1526 * @param bind_path Base dn for the search
1527 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1528 * @param expr Search expression
1529 * @param attrs Attributes to retrieve
1530 * @param res ** which will contain results - free res* with ads_msgfree()
1531 * @return status of search
1533 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1534 const char *expr,
1535 const char **attrs, LDAPMessage **res)
1537 int rc;
1538 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1539 size_t converted_size;
1540 TALLOC_CTX *ctx;
1542 *res = NULL;
1543 if (!(ctx = talloc_init("ads_do_search"))) {
1544 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1545 return ADS_ERROR(LDAP_NO_MEMORY);
1548 /* 0 means the conversion worked but the result was empty
1549 so we only fail if it's negative. In any case, it always
1550 at least nulls out the dest */
1551 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1552 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1554 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1555 rc = LDAP_NO_MEMORY;
1556 goto done;
1559 if (!attrs || !(*attrs))
1560 search_attrs = NULL;
1561 else {
1562 /* This would be the utf8-encoded version...*/
1563 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1564 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1566 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1567 rc = LDAP_NO_MEMORY;
1568 goto done;
1572 /* see the note in ads_do_paged_search - we *must* disable referrals */
1573 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1575 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1576 search_attrs, 0, NULL, NULL,
1577 LDAP_NO_LIMIT,
1578 (LDAPMessage **)res);
1580 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1581 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1582 rc = 0;
1585 done:
1586 talloc_destroy(ctx);
1587 /* if/when we decide to utf8-encode attrs, take out this next line */
1588 TALLOC_FREE(search_attrs);
1589 return ADS_ERROR(rc);
1592 * Do a general ADS search
1593 * @param ads connection to ads server
1594 * @param res ** which will contain results - free res* with ads_msgfree()
1595 * @param expr Search expression
1596 * @param attrs Attributes to retrieve
1597 * @return status of search
1599 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1600 const char *expr, const char **attrs)
1602 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1603 expr, attrs, res);
1607 * Do a search on a specific DistinguishedName
1608 * @param ads connection to ads server
1609 * @param res ** which will contain results - free res* with ads_msgfree()
1610 * @param dn DistinguishedName to search
1611 * @param attrs Attributes to retrieve
1612 * @return status of search
1614 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1615 const char *dn, const char **attrs)
1617 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1618 attrs, res);
1622 * Free up memory from a ads_search
1623 * @param ads connection to ads server
1624 * @param msg Search results to free
1626 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1628 if (!msg) return;
1629 ldap_msgfree(msg);
1633 * Get a dn from search results
1634 * @param ads connection to ads server
1635 * @param msg Search result
1636 * @return dn string
1638 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1640 char *utf8_dn, *unix_dn;
1641 size_t converted_size;
1643 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1645 if (!utf8_dn) {
1646 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1647 return NULL;
1650 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1651 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1652 utf8_dn ));
1653 return NULL;
1655 ldap_memfree(utf8_dn);
1656 return unix_dn;
1660 * Get the parent from a dn
1661 * @param dn the dn to return the parent from
1662 * @return parent dn string
1664 char *ads_parent_dn(const char *dn)
1666 char *p;
1668 if (dn == NULL) {
1669 return NULL;
1672 p = strchr(dn, ',');
1674 if (p == NULL) {
1675 return NULL;
1678 return p+1;
1682 * Find a machine account given a hostname
1683 * @param ads connection to ads server
1684 * @param res ** which will contain results - free res* with ads_msgfree()
1685 * @param host Hostname to search for
1686 * @return status of search
1688 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1689 const char *machine)
1691 ADS_STATUS status;
1692 char *expr;
1693 const char *attrs[] = {
1694 /* This is how Windows checks for machine accounts */
1695 "objectClass",
1696 "SamAccountName",
1697 "userAccountControl",
1698 "DnsHostName",
1699 "ServicePrincipalName",
1700 "userPrincipalName",
1702 /* Additional attributes Samba checks */
1703 "msDS-AdditionalDnsHostName",
1704 "msDS-SupportedEncryptionTypes",
1705 "nTSecurityDescriptor",
1706 "objectSid",
1708 NULL
1710 TALLOC_CTX *frame = talloc_stackframe();
1712 *res = NULL;
1714 /* the easiest way to find a machine account anywhere in the tree
1715 is to look for hostname$ */
1716 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1717 if (expr == NULL) {
1718 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1719 goto done;
1722 status = ads_search(ads, res, expr, attrs);
1723 if (ADS_ERR_OK(status)) {
1724 if (ads_count_replies(ads, *res) != 1) {
1725 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1729 done:
1730 TALLOC_FREE(frame);
1731 return status;
1735 * Initialize a list of mods to be used in a modify request
1736 * @param ctx An initialized TALLOC_CTX
1737 * @return allocated ADS_MODLIST
1739 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1741 #define ADS_MODLIST_ALLOC_SIZE 10
1742 LDAPMod **mods;
1744 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1745 /* -1 is safety to make sure we don't go over the end.
1746 need to reset it to NULL before doing ldap modify */
1747 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1749 return (ADS_MODLIST)mods;
1754 add an attribute to the list, with values list already constructed
1756 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1757 int mod_op, const char *name,
1758 const void *_invals)
1760 int curmod;
1761 LDAPMod **modlist = (LDAPMod **) *mods;
1762 struct berval **ber_values = NULL;
1763 char **char_values = NULL;
1765 if (!_invals) {
1766 mod_op = LDAP_MOD_DELETE;
1767 } else {
1768 if (mod_op & LDAP_MOD_BVALUES) {
1769 const struct berval **b;
1770 b = discard_const_p(const struct berval *, _invals);
1771 ber_values = ads_dup_values(ctx, b);
1772 } else {
1773 const char **c;
1774 c = discard_const_p(const char *, _invals);
1775 char_values = ads_push_strvals(ctx, c);
1779 /* find the first empty slot */
1780 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1781 curmod++);
1782 if (modlist[curmod] == (LDAPMod *) -1) {
1783 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1784 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1785 return ADS_ERROR(LDAP_NO_MEMORY);
1786 memset(&modlist[curmod], 0,
1787 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1788 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1789 *mods = (ADS_MODLIST)modlist;
1792 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1793 return ADS_ERROR(LDAP_NO_MEMORY);
1794 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1795 if (mod_op & LDAP_MOD_BVALUES) {
1796 modlist[curmod]->mod_bvalues = ber_values;
1797 } else if (mod_op & LDAP_MOD_DELETE) {
1798 modlist[curmod]->mod_values = NULL;
1799 } else {
1800 modlist[curmod]->mod_values = char_values;
1803 modlist[curmod]->mod_op = mod_op;
1804 return ADS_ERROR(LDAP_SUCCESS);
1808 * Add a single string value to a mod list
1809 * @param ctx An initialized TALLOC_CTX
1810 * @param mods An initialized ADS_MODLIST
1811 * @param name The attribute name to add
1812 * @param val The value to add - NULL means DELETE
1813 * @return ADS STATUS indicating success of add
1815 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1816 const char *name, const char *val)
1818 const char *values[2];
1820 values[0] = val;
1821 values[1] = NULL;
1823 if (!val)
1824 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1825 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1829 * Add an array of string values to a mod list
1830 * @param ctx An initialized TALLOC_CTX
1831 * @param mods An initialized ADS_MODLIST
1832 * @param name The attribute name to add
1833 * @param vals The array of string values to add - NULL means DELETE
1834 * @return ADS STATUS indicating success of add
1836 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1837 const char *name, const char **vals)
1839 if (!vals)
1840 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1841 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1842 name, (const void **) vals);
1846 * Add a single ber-encoded value to a mod list
1847 * @param ctx An initialized TALLOC_CTX
1848 * @param mods An initialized ADS_MODLIST
1849 * @param name The attribute name to add
1850 * @param val The value to add - NULL means DELETE
1851 * @return ADS STATUS indicating success of add
1853 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1854 const char *name, const struct berval *val)
1856 const struct berval *values[2];
1858 values[0] = val;
1859 values[1] = NULL;
1860 if (!val)
1861 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1862 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1863 name, (const void **) values);
1866 static void ads_print_error(int ret, LDAP *ld)
1868 if (ret != 0) {
1869 char *ld_error = NULL;
1870 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1871 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1872 ret,
1873 ldap_err2string(ret),
1874 ld_error);
1875 SAFE_FREE(ld_error);
1880 * Perform an ldap modify
1881 * @param ads connection to ads server
1882 * @param mod_dn DistinguishedName to modify
1883 * @param mods list of modifications to perform
1884 * @return status of modify
1886 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1888 int ret,i;
1889 char *utf8_dn = NULL;
1890 size_t converted_size;
1892 this control is needed to modify that contains a currently
1893 non-existent attribute (but allowable for the object) to run
1895 LDAPControl PermitModify = {
1896 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1897 {0, NULL},
1898 (char) 1};
1899 LDAPControl *controls[2];
1901 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1903 controls[0] = &PermitModify;
1904 controls[1] = NULL;
1906 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1907 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1910 /* find the end of the list, marked by NULL or -1 */
1911 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1912 /* make sure the end of the list is NULL */
1913 mods[i] = NULL;
1914 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1915 (LDAPMod **) mods, controls, NULL);
1916 ads_print_error(ret, ads->ldap.ld);
1917 TALLOC_FREE(utf8_dn);
1918 return ADS_ERROR(ret);
1922 * Perform an ldap add
1923 * @param ads connection to ads server
1924 * @param new_dn DistinguishedName to add
1925 * @param mods list of attributes and values for DN
1926 * @return status of add
1928 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1930 int ret, i;
1931 char *utf8_dn = NULL;
1932 size_t converted_size;
1934 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1936 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1937 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
1938 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1941 /* find the end of the list, marked by NULL or -1 */
1942 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1943 /* make sure the end of the list is NULL */
1944 mods[i] = NULL;
1946 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1947 ads_print_error(ret, ads->ldap.ld);
1948 TALLOC_FREE(utf8_dn);
1949 return ADS_ERROR(ret);
1953 * Delete a DistinguishedName
1954 * @param ads connection to ads server
1955 * @param new_dn DistinguishedName to delete
1956 * @return status of delete
1958 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1960 int ret;
1961 char *utf8_dn = NULL;
1962 size_t converted_size;
1963 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1964 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
1965 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1968 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1970 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1971 ads_print_error(ret, ads->ldap.ld);
1972 TALLOC_FREE(utf8_dn);
1973 return ADS_ERROR(ret);
1977 * Build an org unit string
1978 * if org unit is Computers or blank then assume a container, otherwise
1979 * assume a / separated list of organisational units.
1980 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1981 * @param ads connection to ads server
1982 * @param org_unit Organizational unit
1983 * @return org unit string - caller must free
1985 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1987 ADS_STATUS status;
1988 char *ret = NULL;
1989 char *dn = NULL;
1991 if (!org_unit || !*org_unit) {
1993 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1995 /* samba4 might not yet respond to a wellknownobject-query */
1996 return ret ? ret : SMB_STRDUP("cn=Computers");
1999 if (strequal(org_unit, "Computers")) {
2000 return SMB_STRDUP("cn=Computers");
2003 /* jmcd: removed "\\" from the separation chars, because it is
2004 needed as an escape for chars like '#' which are valid in an
2005 OU name */
2006 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
2007 if (!ADS_ERR_OK(status)) {
2008 return NULL;
2011 return dn;
2015 * Get a org unit string for a well-known GUID
2016 * @param ads connection to ads server
2017 * @param wknguid Well known GUID
2018 * @return org unit string - caller must free
2020 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2022 ADS_STATUS status;
2023 LDAPMessage *res = NULL;
2024 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2025 **bind_dn_exp = NULL;
2026 const char *attrs[] = {"distinguishedName", NULL};
2027 int new_ln, wkn_ln, bind_ln, i;
2029 if (wknguid == NULL) {
2030 return NULL;
2033 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2034 DEBUG(1, ("asprintf failed!\n"));
2035 return NULL;
2038 status = ads_search_dn(ads, &res, base, attrs);
2039 if (!ADS_ERR_OK(status)) {
2040 DEBUG(1,("Failed while searching for: %s\n", base));
2041 goto out;
2044 if (ads_count_replies(ads, res) != 1) {
2045 goto out;
2048 /* substitute the bind-path from the well-known-guid-search result */
2049 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2050 if (!wkn_dn) {
2051 goto out;
2054 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2055 if (!wkn_dn_exp) {
2056 goto out;
2059 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2060 if (!bind_dn_exp) {
2061 goto out;
2064 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2066 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2069 new_ln = wkn_ln - bind_ln;
2071 ret = SMB_STRDUP(wkn_dn_exp[0]);
2072 if (!ret) {
2073 goto out;
2076 for (i=1; i < new_ln; i++) {
2077 char *s = NULL;
2079 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2080 SAFE_FREE(ret);
2081 goto out;
2084 SAFE_FREE(ret);
2085 ret = SMB_STRDUP(s);
2086 free(s);
2087 if (!ret) {
2088 goto out;
2092 out:
2093 SAFE_FREE(base);
2094 ads_msgfree(ads, res);
2095 TALLOC_FREE(wkn_dn);
2096 if (wkn_dn_exp) {
2097 ldap_value_free(wkn_dn_exp);
2099 if (bind_dn_exp) {
2100 ldap_value_free(bind_dn_exp);
2103 return ret;
2107 * Adds (appends) an item to an attribute array, rather then
2108 * replacing the whole list
2109 * @param ctx An initialized TALLOC_CTX
2110 * @param mods An initialized ADS_MODLIST
2111 * @param name name of the ldap attribute to append to
2112 * @param vals an array of values to add
2113 * @return status of addition
2116 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2117 const char *name, const char **vals)
2119 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2120 (const void *) vals);
2124 * Determines the an account's current KVNO via an LDAP lookup
2125 * @param ads An initialized ADS_STRUCT
2126 * @param account_name the NT samaccountname.
2127 * @return the kvno for the account, or -1 in case of a failure.
2130 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
2132 LDAPMessage *res = NULL;
2133 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
2134 char *filter;
2135 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
2136 char *dn_string = NULL;
2137 ADS_STATUS ret;
2139 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
2140 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
2141 return kvno;
2143 ret = ads_search(ads, &res, filter, attrs);
2144 SAFE_FREE(filter);
2145 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
2146 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
2147 ads_msgfree(ads, res);
2148 return kvno;
2151 dn_string = ads_get_dn(ads, talloc_tos(), res);
2152 if (!dn_string) {
2153 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2154 ads_msgfree(ads, res);
2155 return kvno;
2157 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
2158 TALLOC_FREE(dn_string);
2160 /* ---------------------------------------------------------
2161 * 0 is returned as a default KVNO from this point on...
2162 * This is done because Windows 2000 does not support key
2163 * version numbers. Chances are that a failure in the next
2164 * step is simply due to Windows 2000 being used for a
2165 * domain controller. */
2166 kvno = 0;
2168 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2169 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2170 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2171 ads_msgfree(ads, res);
2172 return kvno;
2175 /* Success */
2176 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2177 ads_msgfree(ads, res);
2178 return kvno;
2182 * Determines the computer account's current KVNO via an LDAP lookup
2183 * @param ads An initialized ADS_STRUCT
2184 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2185 * @return the kvno for the computer account, or -1 in case of a failure.
2188 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2190 char *computer_account = NULL;
2191 uint32_t kvno = -1;
2193 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2194 return kvno;
2197 kvno = ads_get_kvno(ads, computer_account);
2198 free(computer_account);
2200 return kvno;
2204 * This clears out all registered spn's for a given hostname
2205 * @param ads An initialized ADS_STRUCT
2206 * @param machine_name the NetBIOS name of the computer.
2207 * @return 0 upon success, non-zero otherwise.
2210 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2212 TALLOC_CTX *ctx;
2213 LDAPMessage *res = NULL;
2214 ADS_MODLIST mods;
2215 const char *servicePrincipalName[1] = {NULL};
2216 ADS_STATUS ret;
2217 char *dn_string = NULL;
2219 ret = ads_find_machine_acct(ads, &res, machine_name);
2220 if (!ADS_ERR_OK(ret)) {
2221 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2222 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2223 ads_msgfree(ads, res);
2224 return ret;
2227 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2228 ctx = talloc_init("ads_clear_service_principal_names");
2229 if (!ctx) {
2230 ads_msgfree(ads, res);
2231 return ADS_ERROR(LDAP_NO_MEMORY);
2234 if (!(mods = ads_init_mods(ctx))) {
2235 talloc_destroy(ctx);
2236 ads_msgfree(ads, res);
2237 return ADS_ERROR(LDAP_NO_MEMORY);
2239 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2240 if (!ADS_ERR_OK(ret)) {
2241 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2242 ads_msgfree(ads, res);
2243 talloc_destroy(ctx);
2244 return ret;
2246 dn_string = ads_get_dn(ads, talloc_tos(), res);
2247 if (!dn_string) {
2248 talloc_destroy(ctx);
2249 ads_msgfree(ads, res);
2250 return ADS_ERROR(LDAP_NO_MEMORY);
2252 ret = ads_gen_mod(ads, dn_string, mods);
2253 TALLOC_FREE(dn_string);
2254 if (!ADS_ERR_OK(ret)) {
2255 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2256 machine_name));
2257 ads_msgfree(ads, res);
2258 talloc_destroy(ctx);
2259 return ret;
2262 ads_msgfree(ads, res);
2263 talloc_destroy(ctx);
2264 return ret;
2268 * @brief Search for an element in a string array.
2270 * @param[in] el_array The string array to search.
2272 * @param[in] num_el The number of elements in the string array.
2274 * @param[in] el The string to search.
2276 * @return True if found, false if not.
2278 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2280 size_t i;
2282 if (el_array == NULL || num_el == 0 || el == NULL) {
2283 return false;
2286 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2287 int cmp;
2289 cmp = strcasecmp_m(el_array[i], el);
2290 if (cmp == 0) {
2291 return true;
2295 return false;
2299 * @brief This gets the service principal names of an existing computer account.
2301 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2303 * @param[in] ads The ADS context to use.
2305 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2306 * identify the computer account.
2308 * @param[in] spn_array A pointer to store the array for SPNs.
2310 * @param[in] num_spns The number of principals stored in the array.
2312 * @return 0 on success, or a ADS error if a failure occurred.
2314 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2315 ADS_STRUCT *ads,
2316 const char *machine_name,
2317 char ***spn_array,
2318 size_t *num_spns)
2320 ADS_STATUS status;
2321 LDAPMessage *res = NULL;
2322 int count;
2324 status = ads_find_machine_acct(ads,
2325 &res,
2326 machine_name);
2327 if (!ADS_ERR_OK(status)) {
2328 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2329 machine_name));
2330 return status;
2333 count = ads_count_replies(ads, res);
2334 if (count != 1) {
2335 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2336 goto done;
2339 *spn_array = ads_pull_strings(ads,
2340 mem_ctx,
2341 res,
2342 "servicePrincipalName",
2343 num_spns);
2344 if (*spn_array == NULL) {
2345 DEBUG(1, ("Host account for %s does not have service principal "
2346 "names.\n",
2347 machine_name));
2348 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2349 goto done;
2352 done:
2353 ads_msgfree(ads, res);
2355 return status;
2359 * This adds a service principal name to an existing computer account
2360 * (found by hostname) in AD.
2361 * @param ads An initialized ADS_STRUCT
2362 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2363 * @param spns An array or strings for the service principals to add,
2364 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2365 * @return 0 upon success, or non-zero if a failure occurs
2368 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2369 const char *machine_name,
2370 const char **spns)
2372 ADS_STATUS ret;
2373 TALLOC_CTX *ctx;
2374 LDAPMessage *res = NULL;
2375 ADS_MODLIST mods;
2376 char *dn_string = NULL;
2377 const char **servicePrincipalName = spns;
2379 ret = ads_find_machine_acct(ads, &res, machine_name);
2380 if (!ADS_ERR_OK(ret)) {
2381 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2382 machine_name));
2383 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2384 ads_msgfree(ads, res);
2385 return ret;
2388 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2389 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2390 ads_msgfree(ads, res);
2391 return ADS_ERROR(LDAP_NO_MEMORY);
2394 DEBUG(5,("ads_add_service_principal_name: INFO: "
2395 "Adding %s to host %s\n",
2396 spns[0] ? "N/A" : spns[0], machine_name));
2399 DEBUG(5,("ads_add_service_principal_name: INFO: "
2400 "Adding %s to host %s\n",
2401 spns[1] ? "N/A" : spns[1], machine_name));
2403 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2404 ret = ADS_ERROR(LDAP_NO_MEMORY);
2405 goto out;
2408 ret = ads_add_strlist(ctx,
2409 &mods,
2410 "servicePrincipalName",
2411 servicePrincipalName);
2412 if (!ADS_ERR_OK(ret)) {
2413 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2414 goto out;
2417 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2418 ret = ADS_ERROR(LDAP_NO_MEMORY);
2419 goto out;
2422 ret = ads_gen_mod(ads, dn_string, mods);
2423 if (!ADS_ERR_OK(ret)) {
2424 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2425 goto out;
2428 out:
2429 TALLOC_FREE( ctx );
2430 ads_msgfree(ads, res);
2431 return ret;
2434 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2435 LDAPMessage *msg)
2437 uint32_t acct_ctrl = 0;
2438 bool ok;
2440 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2441 if (!ok) {
2442 return 0;
2445 return acct_ctrl;
2448 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2449 LDAPMessage *msg,
2450 const struct berval *machine_pw_val)
2452 ADS_MODLIST mods;
2453 ADS_STATUS ret;
2454 TALLOC_CTX *frame = talloc_stackframe();
2455 uint32_t acct_control;
2456 char *control_str = NULL;
2457 const char *attrs[] = {
2458 "objectSid",
2459 NULL
2461 LDAPMessage *res = NULL;
2462 char *dn = NULL;
2464 dn = ads_get_dn(ads, frame, msg);
2465 if (dn == NULL) {
2466 ret = ADS_ERROR(LDAP_NO_MEMORY);
2467 goto done;
2470 acct_control = ads_get_acct_ctrl(ads, msg);
2471 if (acct_control == 0) {
2472 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2473 goto done;
2477 * Changing the password, disables the account. So we need to change the
2478 * userAccountControl flags to enable it again.
2480 mods = ads_init_mods(frame);
2481 if (mods == NULL) {
2482 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2483 goto done;
2486 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2488 ret = ads_gen_mod(ads, dn, mods);
2489 if (!ADS_ERR_OK(ret)) {
2490 goto done;
2492 TALLOC_FREE(mods);
2495 * To activate the account, we need to disable and enable it.
2497 acct_control |= UF_ACCOUNTDISABLE;
2499 control_str = talloc_asprintf(frame, "%u", acct_control);
2500 if (control_str == NULL) {
2501 ret = ADS_ERROR(LDAP_NO_MEMORY);
2502 goto done;
2505 mods = ads_init_mods(frame);
2506 if (mods == NULL) {
2507 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2508 goto done;
2511 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2513 ret = ads_gen_mod(ads, dn, mods);
2514 if (!ADS_ERR_OK(ret)) {
2515 goto done;
2517 TALLOC_FREE(mods);
2518 TALLOC_FREE(control_str);
2521 * Enable the account again.
2523 acct_control &= ~UF_ACCOUNTDISABLE;
2525 control_str = talloc_asprintf(frame, "%u", acct_control);
2526 if (control_str == NULL) {
2527 ret = ADS_ERROR(LDAP_NO_MEMORY);
2528 goto done;
2531 mods = ads_init_mods(frame);
2532 if (mods == NULL) {
2533 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2534 goto done;
2537 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2539 ret = ads_gen_mod(ads, dn, mods);
2540 if (!ADS_ERR_OK(ret)) {
2541 goto done;
2543 TALLOC_FREE(mods);
2544 TALLOC_FREE(control_str);
2546 ret = ads_search_dn(ads, &res, dn, attrs);
2547 ads_msgfree(ads, res);
2549 done:
2550 talloc_free(frame);
2552 return ret;
2556 * adds a machine account to the ADS server
2557 * @param ads An initialized ADS_STRUCT
2558 * @param machine_name - the NetBIOS machine name of this account.
2559 * @param account_type A number indicating the type of account to create
2560 * @param org_unit The LDAP path in which to place this account
2561 * @return 0 upon success, or non-zero otherwise
2564 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2565 const char *machine_name,
2566 const char *machine_password,
2567 const char *org_unit,
2568 uint32_t etype_list,
2569 const char *dns_domain_name)
2571 ADS_STATUS ret;
2572 char *samAccountName = NULL;
2573 char *controlstr = NULL;
2574 TALLOC_CTX *ctx = NULL;
2575 ADS_MODLIST mods;
2576 char *machine_escaped = NULL;
2577 char *dns_hostname = NULL;
2578 char *new_dn = NULL;
2579 char *utf8_pw = NULL;
2580 size_t utf8_pw_len = 0;
2581 char *utf16_pw = NULL;
2582 size_t utf16_pw_len = 0;
2583 struct berval machine_pw_val;
2584 bool ok;
2585 const char **spn_array = NULL;
2586 size_t num_spns = 0;
2587 const char *spn_prefix[] = {
2588 "HOST",
2589 "RestrictedKrbHost",
2591 size_t i;
2592 LDAPMessage *res = NULL;
2593 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2595 ctx = talloc_init("ads_add_machine_acct");
2596 if (ctx == NULL) {
2597 return ADS_ERROR(LDAP_NO_MEMORY);
2600 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2601 if (machine_escaped == NULL) {
2602 ret = ADS_ERROR(LDAP_NO_MEMORY);
2603 goto done;
2606 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2607 if (utf8_pw == NULL) {
2608 ret = ADS_ERROR(LDAP_NO_MEMORY);
2609 goto done;
2611 utf8_pw_len = strlen(utf8_pw);
2613 ok = convert_string_talloc(ctx,
2614 CH_UTF8, CH_UTF16MUNGED,
2615 utf8_pw, utf8_pw_len,
2616 (void *)&utf16_pw, &utf16_pw_len);
2617 if (!ok) {
2618 ret = ADS_ERROR(LDAP_NO_MEMORY);
2619 goto done;
2622 machine_pw_val = (struct berval) {
2623 .bv_val = utf16_pw,
2624 .bv_len = utf16_pw_len,
2627 /* Check if the machine account already exists. */
2628 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2629 if (ADS_ERR_OK(ret)) {
2630 /* Change the machine account password */
2631 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2632 ads_msgfree(ads, res);
2634 goto done;
2636 ads_msgfree(ads, res);
2638 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2639 if (new_dn == NULL) {
2640 ret = ADS_ERROR(LDAP_NO_MEMORY);
2641 goto done;
2644 /* Create machine account */
2646 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2647 if (samAccountName == NULL) {
2648 ret = ADS_ERROR(LDAP_NO_MEMORY);
2649 goto done;
2652 dns_hostname = talloc_asprintf(ctx,
2653 "%s.%s",
2654 machine_name,
2655 dns_domain_name);
2656 if (dns_hostname == NULL) {
2657 ret = ADS_ERROR(LDAP_NO_MEMORY);
2658 goto done;
2661 /* Add dns_hostname SPNs */
2662 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2663 char *spn = talloc_asprintf(ctx,
2664 "%s/%s",
2665 spn_prefix[i],
2666 dns_hostname);
2667 if (spn == NULL) {
2668 ret = ADS_ERROR(LDAP_NO_MEMORY);
2669 goto done;
2672 ok = add_string_to_array(ctx,
2673 spn,
2674 &spn_array,
2675 &num_spns);
2676 if (!ok) {
2677 ret = ADS_ERROR(LDAP_NO_MEMORY);
2678 goto done;
2682 /* Add machine_name SPNs */
2683 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2684 char *spn = talloc_asprintf(ctx,
2685 "%s/%s",
2686 spn_prefix[i],
2687 machine_name);
2688 if (spn == NULL) {
2689 ret = ADS_ERROR(LDAP_NO_MEMORY);
2690 goto done;
2693 ok = add_string_to_array(ctx,
2694 spn,
2695 &spn_array,
2696 &num_spns);
2697 if (!ok) {
2698 ret = ADS_ERROR(LDAP_NO_MEMORY);
2699 goto done;
2703 /* Make sure to NULL terminate the array */
2704 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2705 if (spn_array == NULL) {
2706 ret = ADS_ERROR(LDAP_NO_MEMORY);
2707 goto done;
2709 spn_array[num_spns] = NULL;
2711 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2712 if (controlstr == NULL) {
2713 ret = ADS_ERROR(LDAP_NO_MEMORY);
2714 goto done;
2717 mods = ads_init_mods(ctx);
2718 if (mods == NULL) {
2719 ret = ADS_ERROR(LDAP_NO_MEMORY);
2720 goto done;
2723 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2724 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2725 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2726 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2727 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2728 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2730 ret = ads_gen_add(ads, new_dn, mods);
2732 done:
2733 SAFE_FREE(machine_escaped);
2734 talloc_destroy(ctx);
2736 return ret;
2740 * move a machine account to another OU on the ADS server
2741 * @param ads - An initialized ADS_STRUCT
2742 * @param machine_name - the NetBIOS machine name of this account.
2743 * @param org_unit - The LDAP path in which to place this account
2744 * @param moved - whether we moved the machine account (optional)
2745 * @return 0 upon success, or non-zero otherwise
2748 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2749 const char *org_unit, bool *moved)
2751 ADS_STATUS rc;
2752 int ldap_status;
2753 LDAPMessage *res = NULL;
2754 char *filter = NULL;
2755 char *computer_dn = NULL;
2756 char *parent_dn;
2757 char *computer_rdn = NULL;
2758 bool need_move = False;
2760 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2761 rc = ADS_ERROR(LDAP_NO_MEMORY);
2762 goto done;
2765 /* Find pre-existing machine */
2766 rc = ads_search(ads, &res, filter, NULL);
2767 if (!ADS_ERR_OK(rc)) {
2768 goto done;
2771 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2772 if (!computer_dn) {
2773 rc = ADS_ERROR(LDAP_NO_MEMORY);
2774 goto done;
2777 parent_dn = ads_parent_dn(computer_dn);
2778 if (strequal(parent_dn, org_unit)) {
2779 goto done;
2782 need_move = True;
2784 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2785 rc = ADS_ERROR(LDAP_NO_MEMORY);
2786 goto done;
2789 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2790 org_unit, 1, NULL, NULL);
2791 rc = ADS_ERROR(ldap_status);
2793 done:
2794 ads_msgfree(ads, res);
2795 SAFE_FREE(filter);
2796 TALLOC_FREE(computer_dn);
2797 SAFE_FREE(computer_rdn);
2799 if (!ADS_ERR_OK(rc)) {
2800 need_move = False;
2803 if (moved) {
2804 *moved = need_move;
2807 return rc;
2811 dump a binary result from ldap
2813 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2815 size_t i;
2816 for (i=0; values[i]; i++) {
2817 ber_len_t j;
2818 printf("%s: ", field);
2819 for (j=0; j<values[i]->bv_len; j++) {
2820 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2822 printf("\n");
2826 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2828 int i;
2829 for (i=0; values[i]; i++) {
2830 NTSTATUS status;
2831 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2832 struct GUID guid;
2834 status = GUID_from_ndr_blob(&in, &guid);
2835 if (NT_STATUS_IS_OK(status)) {
2836 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2837 } else {
2838 printf("%s: INVALID GUID\n", field);
2844 dump a sid result from ldap
2846 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2848 int i;
2849 for (i=0; values[i]; i++) {
2850 ssize_t ret;
2851 struct dom_sid sid;
2852 struct dom_sid_buf tmp;
2853 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2854 values[i]->bv_len, &sid);
2855 if (ret == -1) {
2856 return;
2858 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2863 dump ntSecurityDescriptor
2865 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2867 TALLOC_CTX *frame = talloc_stackframe();
2868 struct security_descriptor *psd;
2869 NTSTATUS status;
2871 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2872 values[0]->bv_len, &psd);
2873 if (!NT_STATUS_IS_OK(status)) {
2874 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2875 nt_errstr(status)));
2876 TALLOC_FREE(frame);
2877 return;
2880 if (psd) {
2881 ads_disp_sd(ads, talloc_tos(), psd);
2884 TALLOC_FREE(frame);
2888 dump a string result from ldap
2890 static void dump_string(const char *field, char **values)
2892 int i;
2893 for (i=0; values[i]; i++) {
2894 printf("%s: %s\n", field, values[i]);
2899 dump a field from LDAP on stdout
2900 used for debugging
2903 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2905 const struct {
2906 const char *name;
2907 bool string;
2908 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2909 } handlers[] = {
2910 {"objectGUID", False, dump_guid},
2911 {"netbootGUID", False, dump_guid},
2912 {"nTSecurityDescriptor", False, dump_sd},
2913 {"dnsRecord", False, dump_binary},
2914 {"objectSid", False, dump_sid},
2915 {"securityIdentifier", False, dump_sid},
2916 {"tokenGroups", False, dump_sid},
2917 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2918 {"tokengroupsGlobalandUniversal", False, dump_sid},
2919 {"mS-DS-CreatorSID", False, dump_sid},
2920 {"msExchMailboxGuid", False, dump_guid},
2921 {"msDS-TrustForestTrustInfo", False, dump_binary},
2922 {NULL, True, NULL}
2924 int i;
2926 if (!field) { /* must be end of an entry */
2927 printf("\n");
2928 return False;
2931 for (i=0; handlers[i].name; i++) {
2932 if (strcasecmp_m(handlers[i].name, field) == 0) {
2933 if (!values) /* first time, indicate string or not */
2934 return handlers[i].string;
2935 handlers[i].handler(ads, field, (struct berval **) values);
2936 break;
2939 if (!handlers[i].name) {
2940 if (!values) /* first time, indicate string conversion */
2941 return True;
2942 dump_string(field, (char **)values);
2944 return False;
2948 * Dump a result from LDAP on stdout
2949 * used for debugging
2950 * @param ads connection to ads server
2951 * @param res Results to dump
2954 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2956 ads_process_results(ads, res, ads_dump_field, NULL);
2960 * Walk through results, calling a function for each entry found.
2961 * The function receives a field name, a berval * array of values,
2962 * and a data area passed through from the start. The function is
2963 * called once with null for field and values at the end of each
2964 * entry.
2965 * @param ads connection to ads server
2966 * @param res Results to process
2967 * @param fn Function for processing each result
2968 * @param data_area user-defined area to pass to function
2970 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2971 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2972 void *data_area)
2974 LDAPMessage *msg;
2975 TALLOC_CTX *ctx;
2976 size_t converted_size;
2978 if (!(ctx = talloc_init("ads_process_results")))
2979 return;
2981 for (msg = ads_first_entry(ads, res); msg;
2982 msg = ads_next_entry(ads, msg)) {
2983 char *utf8_field;
2984 BerElement *b;
2986 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2987 (LDAPMessage *)msg,&b);
2988 utf8_field;
2989 utf8_field=ldap_next_attribute(ads->ldap.ld,
2990 (LDAPMessage *)msg,b)) {
2991 struct berval **ber_vals;
2992 char **str_vals;
2993 char **utf8_vals;
2994 char *field;
2995 bool string;
2997 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2998 &converted_size))
3000 DEBUG(0,("ads_process_results: "
3001 "pull_utf8_talloc failed: %s\n",
3002 strerror(errno)));
3005 string = fn(ads, field, NULL, data_area);
3007 if (string) {
3008 const char **p;
3010 utf8_vals = ldap_get_values(ads->ldap.ld,
3011 (LDAPMessage *)msg, field);
3012 p = discard_const_p(const char *, utf8_vals);
3013 str_vals = ads_pull_strvals(ctx, p);
3014 fn(ads, field, (void **) str_vals, data_area);
3015 ldap_value_free(utf8_vals);
3016 } else {
3017 ber_vals = ldap_get_values_len(ads->ldap.ld,
3018 (LDAPMessage *)msg, field);
3019 fn(ads, field, (void **) ber_vals, data_area);
3021 ldap_value_free_len(ber_vals);
3023 ldap_memfree(utf8_field);
3025 ber_free(b, 0);
3026 talloc_free_children(ctx);
3027 fn(ads, NULL, NULL, data_area); /* completed an entry */
3030 talloc_destroy(ctx);
3034 * count how many replies are in a LDAPMessage
3035 * @param ads connection to ads server
3036 * @param res Results to count
3037 * @return number of replies
3039 int ads_count_replies(ADS_STRUCT *ads, void *res)
3041 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3045 * pull the first entry from a ADS result
3046 * @param ads connection to ads server
3047 * @param res Results of search
3048 * @return first entry from result
3050 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3052 return ldap_first_entry(ads->ldap.ld, res);
3056 * pull the next entry from a ADS result
3057 * @param ads connection to ads server
3058 * @param res Results of search
3059 * @return next entry from result
3061 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3063 return ldap_next_entry(ads->ldap.ld, res);
3067 * pull the first message from a ADS result
3068 * @param ads connection to ads server
3069 * @param res Results of search
3070 * @return first message from result
3072 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3074 return ldap_first_message(ads->ldap.ld, res);
3078 * pull the next message from a ADS result
3079 * @param ads connection to ads server
3080 * @param res Results of search
3081 * @return next message from result
3083 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3085 return ldap_next_message(ads->ldap.ld, res);
3089 * pull a single string from a ADS result
3090 * @param ads connection to ads server
3091 * @param mem_ctx TALLOC_CTX to use for allocating result string
3092 * @param msg Results of search
3093 * @param field Attribute to retrieve
3094 * @return Result string in talloc context
3096 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3097 const char *field)
3099 char **values;
3100 char *ret = NULL;
3101 char *ux_string;
3102 size_t converted_size;
3104 values = ldap_get_values(ads->ldap.ld, msg, field);
3105 if (!values)
3106 return NULL;
3108 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3109 &converted_size))
3111 ret = ux_string;
3113 ldap_value_free(values);
3114 return ret;
3118 * pull an array of strings from a ADS result
3119 * @param ads connection to ads server
3120 * @param mem_ctx TALLOC_CTX to use for allocating result string
3121 * @param msg Results of search
3122 * @param field Attribute to retrieve
3123 * @return Result strings in talloc context
3125 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3126 LDAPMessage *msg, const char *field,
3127 size_t *num_values)
3129 char **values;
3130 char **ret = NULL;
3131 size_t i, converted_size;
3133 values = ldap_get_values(ads->ldap.ld, msg, field);
3134 if (!values)
3135 return NULL;
3137 *num_values = ldap_count_values(values);
3139 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3140 if (!ret) {
3141 ldap_value_free(values);
3142 return NULL;
3145 for (i=0;i<*num_values;i++) {
3146 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3147 &converted_size))
3149 ldap_value_free(values);
3150 return NULL;
3153 ret[i] = NULL;
3155 ldap_value_free(values);
3156 return ret;
3160 * pull an array of strings from a ADS result
3161 * (handle large multivalue attributes with range retrieval)
3162 * @param ads connection to ads server
3163 * @param mem_ctx TALLOC_CTX to use for allocating result string
3164 * @param msg Results of search
3165 * @param field Attribute to retrieve
3166 * @param current_strings strings returned by a previous call to this function
3167 * @param next_attribute The next query should ask for this attribute
3168 * @param num_values How many values did we get this time?
3169 * @param more_values Are there more values to get?
3170 * @return Result strings in talloc context
3172 char **ads_pull_strings_range(ADS_STRUCT *ads,
3173 TALLOC_CTX *mem_ctx,
3174 LDAPMessage *msg, const char *field,
3175 char **current_strings,
3176 const char **next_attribute,
3177 size_t *num_strings,
3178 bool *more_strings)
3180 char *attr;
3181 char *expected_range_attrib, *range_attr = NULL;
3182 BerElement *ptr = NULL;
3183 char **strings;
3184 char **new_strings;
3185 size_t num_new_strings;
3186 unsigned long int range_start;
3187 unsigned long int range_end;
3189 /* we might have been given the whole lot anyway */
3190 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3191 *more_strings = False;
3192 return strings;
3195 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3197 /* look for Range result */
3198 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3199 attr;
3200 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3201 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3202 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3203 range_attr = attr;
3204 break;
3206 ldap_memfree(attr);
3208 if (!range_attr) {
3209 ber_free(ptr, 0);
3210 /* nothing here - this field is just empty */
3211 *more_strings = False;
3212 return NULL;
3215 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3216 &range_start, &range_end) == 2) {
3217 *more_strings = True;
3218 } else {
3219 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3220 &range_start) == 1) {
3221 *more_strings = False;
3222 } else {
3223 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3224 range_attr));
3225 ldap_memfree(range_attr);
3226 *more_strings = False;
3227 return NULL;
3231 if ((*num_strings) != range_start) {
3232 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3233 " - aborting range retrieval\n",
3234 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3235 ldap_memfree(range_attr);
3236 *more_strings = False;
3237 return NULL;
3240 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3242 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3243 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3244 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3245 range_attr, (unsigned long int)range_end - range_start + 1,
3246 (unsigned long int)num_new_strings));
3247 ldap_memfree(range_attr);
3248 *more_strings = False;
3249 return NULL;
3252 strings = talloc_realloc(mem_ctx, current_strings, char *,
3253 *num_strings + num_new_strings);
3255 if (strings == NULL) {
3256 ldap_memfree(range_attr);
3257 *more_strings = False;
3258 return NULL;
3261 if (new_strings && num_new_strings) {
3262 memcpy(&strings[*num_strings], new_strings,
3263 sizeof(*new_strings) * num_new_strings);
3266 (*num_strings) += num_new_strings;
3268 if (*more_strings) {
3269 *next_attribute = talloc_asprintf(mem_ctx,
3270 "%s;range=%d-*",
3271 field,
3272 (int)*num_strings);
3274 if (!*next_attribute) {
3275 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3276 ldap_memfree(range_attr);
3277 *more_strings = False;
3278 return NULL;
3282 ldap_memfree(range_attr);
3284 return strings;
3288 * pull a single uint32_t from a ADS result
3289 * @param ads connection to ads server
3290 * @param msg Results of search
3291 * @param field Attribute to retrieve
3292 * @param v Pointer to int to store result
3293 * @return boolean indicating success
3295 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3296 uint32_t *v)
3298 char **values;
3300 values = ldap_get_values(ads->ldap.ld, msg, field);
3301 if (!values)
3302 return False;
3303 if (!values[0]) {
3304 ldap_value_free(values);
3305 return False;
3308 *v = atoi(values[0]);
3309 ldap_value_free(values);
3310 return True;
3314 * pull a single objectGUID from an ADS result
3315 * @param ads connection to ADS server
3316 * @param msg results of search
3317 * @param guid 37-byte area to receive text guid
3318 * @return boolean indicating success
3320 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3322 DATA_BLOB blob;
3323 NTSTATUS status;
3325 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3326 &blob)) {
3327 return false;
3330 status = GUID_from_ndr_blob(&blob, guid);
3331 talloc_free(blob.data);
3332 return NT_STATUS_IS_OK(status);
3337 * pull a single struct dom_sid from a ADS result
3338 * @param ads connection to ads server
3339 * @param msg Results of search
3340 * @param field Attribute to retrieve
3341 * @param sid Pointer to sid to store result
3342 * @return boolean indicating success
3344 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3345 struct dom_sid *sid)
3347 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3351 * pull an array of struct dom_sids from a ADS result
3352 * @param ads connection to ads server
3353 * @param mem_ctx TALLOC_CTX for allocating sid array
3354 * @param msg Results of search
3355 * @param field Attribute to retrieve
3356 * @param sids pointer to sid array to allocate
3357 * @return the count of SIDs pulled
3359 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3360 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3362 struct berval **values;
3363 int count, i;
3365 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3367 if (!values)
3368 return 0;
3370 for (i=0; values[i]; i++)
3371 /* nop */ ;
3373 if (i) {
3374 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3375 if (!(*sids)) {
3376 ldap_value_free_len(values);
3377 return 0;
3379 } else {
3380 (*sids) = NULL;
3383 count = 0;
3384 for (i=0; values[i]; i++) {
3385 ssize_t ret;
3386 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3387 values[i]->bv_len, &(*sids)[count]);
3388 if (ret != -1) {
3389 struct dom_sid_buf buf;
3390 DBG_DEBUG("pulling SID: %s\n",
3391 dom_sid_str_buf(&(*sids)[count], &buf));
3392 count++;
3396 ldap_value_free_len(values);
3397 return count;
3401 * pull a struct security_descriptor from a ADS result
3402 * @param ads connection to ads server
3403 * @param mem_ctx TALLOC_CTX for allocating sid array
3404 * @param msg Results of search
3405 * @param field Attribute to retrieve
3406 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3407 * @return boolean indicating success
3409 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3410 LDAPMessage *msg, const char *field,
3411 struct security_descriptor **sd)
3413 struct berval **values;
3414 bool ret = true;
3416 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3418 if (!values) return false;
3420 if (values[0]) {
3421 NTSTATUS status;
3422 status = unmarshall_sec_desc(mem_ctx,
3423 (uint8_t *)values[0]->bv_val,
3424 values[0]->bv_len, sd);
3425 if (!NT_STATUS_IS_OK(status)) {
3426 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3427 nt_errstr(status)));
3428 ret = false;
3432 ldap_value_free_len(values);
3433 return ret;
3437 * in order to support usernames longer than 21 characters we need to
3438 * use both the sAMAccountName and the userPrincipalName attributes
3439 * It seems that not all users have the userPrincipalName attribute set
3441 * @param ads connection to ads server
3442 * @param mem_ctx TALLOC_CTX for allocating sid array
3443 * @param msg Results of search
3444 * @return the username
3446 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3447 LDAPMessage *msg)
3449 #if 0 /* JERRY */
3450 char *ret, *p;
3452 /* lookup_name() only works on the sAMAccountName to
3453 returning the username portion of userPrincipalName
3454 breaks winbindd_getpwnam() */
3456 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3457 if (ret && (p = strchr_m(ret, '@'))) {
3458 *p = 0;
3459 return ret;
3461 #endif
3462 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3467 * find the update serial number - this is the core of the ldap cache
3468 * @param ads connection to ads server
3469 * @param ads connection to ADS server
3470 * @param usn Pointer to retrieved update serial number
3471 * @return status of search
3473 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3475 const char *attrs[] = {"highestCommittedUSN", NULL};
3476 ADS_STATUS status;
3477 LDAPMessage *res;
3479 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3480 if (!ADS_ERR_OK(status))
3481 return status;
3483 if (ads_count_replies(ads, res) != 1) {
3484 ads_msgfree(ads, res);
3485 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3488 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3489 ads_msgfree(ads, res);
3490 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3493 ads_msgfree(ads, res);
3494 return ADS_SUCCESS;
3497 /* parse a ADS timestring - typical string is
3498 '20020917091222.0Z0' which means 09:12.22 17th September
3499 2002, timezone 0 */
3500 static time_t ads_parse_time(const char *str)
3502 struct tm tm;
3504 ZERO_STRUCT(tm);
3506 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3507 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3508 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3509 return 0;
3511 tm.tm_year -= 1900;
3512 tm.tm_mon -= 1;
3514 return timegm(&tm);
3517 /********************************************************************
3518 ********************************************************************/
3520 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3522 const char *attrs[] = {"currentTime", NULL};
3523 ADS_STATUS status;
3524 LDAPMessage *res;
3525 char *timestr;
3526 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3527 ADS_STRUCT *ads_s = ads;
3529 /* establish a new ldap tcp session if necessary */
3531 if ( !ads->ldap.ld ) {
3533 * ADS_STRUCT may be being reused after a
3534 * DC lookup, so ads->ldap.ss may already have a
3535 * good address. If not, re-initialize the passed-in
3536 * ADS_STRUCT with the given server.XXXX parameters.
3538 * Note that this doesn't depend on
3539 * ads->server.ldap_server != NULL,
3540 * as the case where ads->server.ldap_server==NULL and
3541 * ads->ldap.ss != zero_address is precisely the DC
3542 * lookup case where ads->ldap.ss was found by going
3543 * through ads_find_dc() again we want to avoid repeating.
3545 if (is_zero_addr(&ads->ldap.ss)) {
3546 ads_s = ads_init(tmp_ctx,
3547 ads->server.realm,
3548 ads->server.workgroup,
3549 ads->server.ldap_server,
3550 ADS_SASL_PLAIN );
3551 if (ads_s == NULL) {
3552 status = ADS_ERROR(LDAP_NO_MEMORY);
3553 goto done;
3558 * Reset ads->config.flags as it can contain the flags
3559 * returned by the previous CLDAP ping when reusing the struct.
3561 ads_s->config.flags = 0;
3563 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3564 status = ads_connect( ads_s );
3565 if ( !ADS_ERR_OK(status))
3566 goto done;
3569 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3570 if (!ADS_ERR_OK(status)) {
3571 goto done;
3574 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3575 if (!timestr) {
3576 ads_msgfree(ads_s, res);
3577 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3578 goto done;
3581 /* but save the time and offset in the original ADS_STRUCT */
3583 ads->config.current_time = ads_parse_time(timestr);
3585 if (ads->config.current_time != 0) {
3586 ads->auth.time_offset = ads->config.current_time - time(NULL);
3587 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3590 ads_msgfree(ads, res);
3592 status = ADS_SUCCESS;
3594 done:
3595 TALLOC_FREE(tmp_ctx);
3597 return status;
3600 /********************************************************************
3601 ********************************************************************/
3603 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3605 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3606 const char *attrs[] = {"domainFunctionality", NULL};
3607 ADS_STATUS status;
3608 LDAPMessage *res;
3609 ADS_STRUCT *ads_s = ads;
3611 *val = DS_DOMAIN_FUNCTION_2000;
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_NT(NT_STATUS_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 /* If the attribute does not exist assume it is a Windows 2000
3654 functional domain */
3656 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3657 if (!ADS_ERR_OK(status)) {
3658 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3659 status = ADS_SUCCESS;
3661 goto done;
3664 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3665 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3667 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3670 ads_msgfree(ads_s, res);
3672 done:
3673 TALLOC_FREE(tmp_ctx);
3675 return status;
3679 * find the domain sid for our domain
3680 * @param ads connection to ads server
3681 * @param sid Pointer to domain sid
3682 * @return status of search
3684 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3686 const char *attrs[] = {"objectSid", NULL};
3687 LDAPMessage *res;
3688 ADS_STATUS rc;
3690 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3691 attrs, &res);
3692 if (!ADS_ERR_OK(rc)) return rc;
3693 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3694 ads_msgfree(ads, res);
3695 return ADS_ERROR_SYSTEM(ENOENT);
3697 ads_msgfree(ads, res);
3699 return ADS_SUCCESS;
3703 * find our site name
3704 * @param ads connection to ads server
3705 * @param mem_ctx Pointer to talloc context
3706 * @param site_name Pointer to the sitename
3707 * @return status of search
3709 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3711 ADS_STATUS status;
3712 LDAPMessage *res;
3713 const char *dn, *service_name;
3714 const char *attrs[] = { "dsServiceName", NULL };
3716 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3717 if (!ADS_ERR_OK(status)) {
3718 return status;
3721 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3722 if (service_name == NULL) {
3723 ads_msgfree(ads, res);
3724 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3727 ads_msgfree(ads, res);
3729 /* go up three levels */
3730 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3731 if (dn == NULL) {
3732 return ADS_ERROR(LDAP_NO_MEMORY);
3735 *site_name = talloc_strdup(mem_ctx, dn);
3736 if (*site_name == NULL) {
3737 return ADS_ERROR(LDAP_NO_MEMORY);
3740 return status;
3742 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3747 * find the site dn where a machine resides
3748 * @param ads connection to ads server
3749 * @param mem_ctx Pointer to talloc context
3750 * @param computer_name name of the machine
3751 * @param site_name Pointer to the sitename
3752 * @return status of search
3754 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3756 ADS_STATUS status;
3757 LDAPMessage *res;
3758 const char *parent, *filter;
3759 char *config_context = NULL;
3760 char *dn;
3762 /* shortcut a query */
3763 if (strequal(computer_name, ads->config.ldap_server_name)) {
3764 return ads_site_dn(ads, mem_ctx, site_dn);
3767 status = ads_config_path(ads, mem_ctx, &config_context);
3768 if (!ADS_ERR_OK(status)) {
3769 return status;
3772 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3773 if (filter == NULL) {
3774 return ADS_ERROR(LDAP_NO_MEMORY);
3777 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3778 filter, NULL, &res);
3779 if (!ADS_ERR_OK(status)) {
3780 return status;
3783 if (ads_count_replies(ads, res) != 1) {
3784 ads_msgfree(ads, res);
3785 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3788 dn = ads_get_dn(ads, mem_ctx, res);
3789 if (dn == NULL) {
3790 ads_msgfree(ads, res);
3791 return ADS_ERROR(LDAP_NO_MEMORY);
3794 /* go up three levels */
3795 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3796 if (parent == NULL) {
3797 ads_msgfree(ads, res);
3798 TALLOC_FREE(dn);
3799 return ADS_ERROR(LDAP_NO_MEMORY);
3802 *site_dn = talloc_strdup(mem_ctx, parent);
3803 if (*site_dn == NULL) {
3804 ads_msgfree(ads, res);
3805 TALLOC_FREE(dn);
3806 return ADS_ERROR(LDAP_NO_MEMORY);
3809 TALLOC_FREE(dn);
3810 ads_msgfree(ads, res);
3812 return status;
3816 * get the upn suffixes for a domain
3817 * @param ads connection to ads server
3818 * @param mem_ctx Pointer to talloc context
3819 * @param suffixes Pointer to an array of suffixes
3820 * @param num_suffixes Pointer to the number of suffixes
3821 * @return status of search
3823 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3825 ADS_STATUS status;
3826 LDAPMessage *res;
3827 const char *base;
3828 char *config_context = NULL;
3829 const char *attrs[] = { "uPNSuffixes", NULL };
3831 status = ads_config_path(ads, mem_ctx, &config_context);
3832 if (!ADS_ERR_OK(status)) {
3833 return status;
3836 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3837 if (base == NULL) {
3838 return ADS_ERROR(LDAP_NO_MEMORY);
3841 status = ads_search_dn(ads, &res, base, attrs);
3842 if (!ADS_ERR_OK(status)) {
3843 return status;
3846 if (ads_count_replies(ads, res) != 1) {
3847 ads_msgfree(ads, res);
3848 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3851 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3852 if ((*suffixes) == NULL) {
3853 ads_msgfree(ads, res);
3854 return ADS_ERROR(LDAP_NO_MEMORY);
3857 ads_msgfree(ads, res);
3859 return status;
3863 * get the joinable ous for a domain
3864 * @param ads connection to ads server
3865 * @param mem_ctx Pointer to talloc context
3866 * @param ous Pointer to an array of ous
3867 * @param num_ous Pointer to the number of ous
3868 * @return status of search
3870 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3871 TALLOC_CTX *mem_ctx,
3872 char ***ous,
3873 size_t *num_ous)
3875 ADS_STATUS status;
3876 LDAPMessage *res = NULL;
3877 LDAPMessage *msg = NULL;
3878 const char *attrs[] = { "dn", NULL };
3879 int count = 0;
3881 status = ads_search(ads, &res,
3882 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3883 attrs);
3884 if (!ADS_ERR_OK(status)) {
3885 return status;
3888 count = ads_count_replies(ads, res);
3889 if (count < 1) {
3890 ads_msgfree(ads, res);
3891 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3894 for (msg = ads_first_entry(ads, res); msg;
3895 msg = ads_next_entry(ads, msg)) {
3896 const char **p = discard_const_p(const char *, *ous);
3897 char *dn = NULL;
3899 dn = ads_get_dn(ads, talloc_tos(), msg);
3900 if (!dn) {
3901 ads_msgfree(ads, res);
3902 return ADS_ERROR(LDAP_NO_MEMORY);
3905 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3906 TALLOC_FREE(dn);
3907 ads_msgfree(ads, res);
3908 return ADS_ERROR(LDAP_NO_MEMORY);
3911 TALLOC_FREE(dn);
3912 *ous = discard_const_p(char *, p);
3915 ads_msgfree(ads, res);
3917 return status;
3922 * pull a struct dom_sid from an extended dn string
3923 * @param mem_ctx TALLOC_CTX
3924 * @param extended_dn string
3925 * @param flags string type of extended_dn
3926 * @param sid pointer to a struct dom_sid
3927 * @return NT_STATUS_OK on success,
3928 * NT_INVALID_PARAMETER on error,
3929 * NT_STATUS_NOT_FOUND if no SID present
3931 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3932 const char *extended_dn,
3933 enum ads_extended_dn_flags flags,
3934 struct dom_sid *sid)
3936 char *p, *q, *dn;
3938 if (!extended_dn) {
3939 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3942 /* otherwise extended_dn gets stripped off */
3943 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3944 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3947 * ADS_EXTENDED_DN_HEX_STRING:
3948 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3950 * ADS_EXTENDED_DN_STRING (only with w2k3):
3951 * <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
3953 * Object with no SID, such as an Exchange Public Folder
3954 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3957 p = strchr(dn, ';');
3958 if (!p) {
3959 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3962 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3963 DEBUG(5,("No SID present in extended dn\n"));
3964 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3967 p += strlen(";<SID=");
3969 q = strchr(p, '>');
3970 if (!q) {
3971 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3974 *q = '\0';
3976 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3978 switch (flags) {
3980 case ADS_EXTENDED_DN_STRING:
3981 if (!string_to_sid(sid, p)) {
3982 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3984 break;
3985 case ADS_EXTENDED_DN_HEX_STRING: {
3986 ssize_t ret;
3987 fstring buf;
3988 size_t buf_len;
3990 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3991 if (buf_len == 0) {
3992 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3995 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3996 if (ret == -1) {
3997 DEBUG(10,("failed to parse sid\n"));
3998 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4000 break;
4002 default:
4003 DEBUG(10,("unknown extended dn format\n"));
4004 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4007 return ADS_ERROR_NT(NT_STATUS_OK);
4010 /********************************************************************
4011 ********************************************************************/
4013 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4015 LDAPMessage *res = NULL;
4016 ADS_STATUS status;
4017 int count = 0;
4018 char *name = NULL;
4020 status = ads_find_machine_acct(ads, &res, machine_name);
4021 if (!ADS_ERR_OK(status)) {
4022 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4023 lp_netbios_name()));
4024 goto out;
4027 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4028 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4029 goto out;
4032 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4033 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4036 out:
4037 ads_msgfree(ads, res);
4039 return name;
4042 /********************************************************************
4043 ********************************************************************/
4045 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4046 LDAPMessage *msg, size_t *num_values)
4048 const char *field = "msDS-AdditionalDnsHostName";
4049 struct berval **values = NULL;
4050 char **ret = NULL;
4051 size_t i, converted_size;
4054 * Windows DC implicitly adds a short name for each FQDN added to
4055 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4056 * suffix "\0$" which we should ignore (see bug #14406).
4059 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4060 if (values == NULL) {
4061 return NULL;
4064 *num_values = ldap_count_values_len(values);
4066 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4067 if (ret == NULL) {
4068 ldap_value_free_len(values);
4069 return NULL;
4072 for (i = 0; i < *num_values; i++) {
4073 ret[i] = NULL;
4074 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4075 values[i]->bv_val,
4076 strnlen(values[i]->bv_val,
4077 values[i]->bv_len),
4078 &ret[i], &converted_size)) {
4079 ldap_value_free_len(values);
4080 return NULL;
4083 ret[i] = NULL;
4085 ldap_value_free_len(values);
4086 return ret;
4089 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4090 ADS_STRUCT *ads,
4091 const char *machine_name,
4092 char ***hostnames_array,
4093 size_t *num_hostnames)
4095 ADS_STATUS status;
4096 LDAPMessage *res = NULL;
4097 int count;
4099 status = ads_find_machine_acct(ads,
4100 &res,
4101 machine_name);
4102 if (!ADS_ERR_OK(status)) {
4103 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4104 machine_name));
4105 return status;
4108 count = ads_count_replies(ads, res);
4109 if (count != 1) {
4110 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4111 goto done;
4114 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4115 if (*hostnames_array == NULL) {
4116 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4117 machine_name));
4118 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4119 goto done;
4122 done:
4123 ads_msgfree(ads, res);
4125 return status;
4128 /********************************************************************
4129 ********************************************************************/
4131 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4133 LDAPMessage *res = NULL;
4134 ADS_STATUS status;
4135 int count = 0;
4136 char *name = NULL;
4138 status = ads_find_machine_acct(ads, &res, machine_name);
4139 if (!ADS_ERR_OK(status)) {
4140 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4141 lp_netbios_name()));
4142 goto out;
4145 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4146 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4147 goto out;
4150 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4151 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4154 out:
4155 ads_msgfree(ads, res);
4157 return name;
4160 /********************************************************************
4161 ********************************************************************/
4163 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4165 LDAPMessage *res = NULL;
4166 ADS_STATUS status;
4167 int count = 0;
4168 char *name = NULL;
4169 bool ok = false;
4171 status = ads_find_machine_acct(ads, &res, machine_name);
4172 if (!ADS_ERR_OK(status)) {
4173 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4174 lp_netbios_name()));
4175 goto out;
4178 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4179 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4180 goto out;
4183 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4184 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4187 out:
4188 ads_msgfree(ads, res);
4189 if (name != NULL) {
4190 ok = (strlen(name) > 0);
4192 TALLOC_FREE(name);
4193 return ok;
4196 #if 0
4198 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4201 * Join a machine to a realm
4202 * Creates the machine account and sets the machine password
4203 * @param ads connection to ads server
4204 * @param machine name of host to add
4205 * @param org_unit Organizational unit to place machine in
4206 * @return status of join
4208 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4209 uint32_t account_type, const char *org_unit)
4211 ADS_STATUS status;
4212 LDAPMessage *res = NULL;
4213 char *machine;
4215 /* machine name must be lowercase */
4216 machine = SMB_STRDUP(machine_name);
4217 strlower_m(machine);
4220 status = ads_find_machine_acct(ads, (void **)&res, machine);
4221 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4222 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4223 status = ads_leave_realm(ads, machine);
4224 if (!ADS_ERR_OK(status)) {
4225 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4226 machine, ads->config.realm));
4227 return status;
4231 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4232 if (!ADS_ERR_OK(status)) {
4233 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4234 SAFE_FREE(machine);
4235 return status;
4238 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4239 if (!ADS_ERR_OK(status)) {
4240 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4241 SAFE_FREE(machine);
4242 return status;
4245 SAFE_FREE(machine);
4246 ads_msgfree(ads, res);
4248 return status;
4250 #endif
4253 * Delete a machine from the realm
4254 * @param ads connection to ads server
4255 * @param hostname Machine to remove
4256 * @return status of delete
4258 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4260 ADS_STATUS status;
4261 void *msg;
4262 LDAPMessage *res;
4263 char *hostnameDN, *host;
4264 int rc;
4265 LDAPControl ldap_control;
4266 LDAPControl * pldap_control[2] = {NULL, NULL};
4268 pldap_control[0] = &ldap_control;
4269 memset(&ldap_control, 0, sizeof(LDAPControl));
4270 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4272 /* hostname must be lowercase */
4273 host = SMB_STRDUP(hostname);
4274 if (!strlower_m(host)) {
4275 SAFE_FREE(host);
4276 return ADS_ERROR_SYSTEM(EINVAL);
4279 status = ads_find_machine_acct(ads, &res, host);
4280 if (!ADS_ERR_OK(status)) {
4281 DEBUG(0, ("Host account for %s does not exist.\n", host));
4282 SAFE_FREE(host);
4283 return status;
4286 msg = ads_first_entry(ads, res);
4287 if (!msg) {
4288 SAFE_FREE(host);
4289 return ADS_ERROR_SYSTEM(ENOENT);
4292 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4293 if (hostnameDN == NULL) {
4294 SAFE_FREE(host);
4295 return ADS_ERROR_SYSTEM(ENOENT);
4298 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4299 if (rc) {
4300 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4301 }else {
4302 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4305 if (rc != LDAP_SUCCESS) {
4306 const char *attrs[] = { "cn", NULL };
4307 LDAPMessage *msg_sub;
4309 /* we only search with scope ONE, we do not expect any further
4310 * objects to be created deeper */
4312 status = ads_do_search_retry(ads, hostnameDN,
4313 LDAP_SCOPE_ONELEVEL,
4314 "(objectclass=*)", attrs, &res);
4316 if (!ADS_ERR_OK(status)) {
4317 SAFE_FREE(host);
4318 TALLOC_FREE(hostnameDN);
4319 return status;
4322 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4323 msg_sub = ads_next_entry(ads, msg_sub)) {
4325 char *dn = NULL;
4327 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4328 SAFE_FREE(host);
4329 TALLOC_FREE(hostnameDN);
4330 return ADS_ERROR(LDAP_NO_MEMORY);
4333 status = ads_del_dn(ads, dn);
4334 if (!ADS_ERR_OK(status)) {
4335 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4336 SAFE_FREE(host);
4337 TALLOC_FREE(dn);
4338 TALLOC_FREE(hostnameDN);
4339 return status;
4342 TALLOC_FREE(dn);
4345 /* there should be no subordinate objects anymore */
4346 status = ads_do_search_retry(ads, hostnameDN,
4347 LDAP_SCOPE_ONELEVEL,
4348 "(objectclass=*)", attrs, &res);
4350 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4351 SAFE_FREE(host);
4352 TALLOC_FREE(hostnameDN);
4353 return status;
4356 /* delete hostnameDN now */
4357 status = ads_del_dn(ads, hostnameDN);
4358 if (!ADS_ERR_OK(status)) {
4359 SAFE_FREE(host);
4360 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4361 TALLOC_FREE(hostnameDN);
4362 return status;
4366 TALLOC_FREE(hostnameDN);
4368 status = ads_find_machine_acct(ads, &res, host);
4369 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4370 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4371 DEBUG(3, ("Failed to remove host account.\n"));
4372 SAFE_FREE(host);
4373 return status;
4376 SAFE_FREE(host);
4377 return ADS_SUCCESS;
4381 * pull all token-sids from an LDAP dn
4382 * @param ads connection to ads server
4383 * @param mem_ctx TALLOC_CTX for allocating sid array
4384 * @param dn of LDAP object
4385 * @param user_sid pointer to struct dom_sid (objectSid)
4386 * @param primary_group_sid pointer to struct dom_sid (self composed)
4387 * @param sids pointer to sid array to allocate
4388 * @param num_sids counter of SIDs pulled
4389 * @return status of token query
4391 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4392 TALLOC_CTX *mem_ctx,
4393 const char *dn,
4394 struct dom_sid *user_sid,
4395 struct dom_sid *primary_group_sid,
4396 struct dom_sid **sids,
4397 size_t *num_sids)
4399 ADS_STATUS status;
4400 LDAPMessage *res = NULL;
4401 int count = 0;
4402 size_t tmp_num_sids;
4403 struct dom_sid *tmp_sids;
4404 struct dom_sid tmp_user_sid;
4405 struct dom_sid tmp_primary_group_sid;
4406 uint32_t pgid;
4407 const char *attrs[] = {
4408 "objectSid",
4409 "tokenGroups",
4410 "primaryGroupID",
4411 NULL
4414 status = ads_search_retry_dn(ads, &res, dn, attrs);
4415 if (!ADS_ERR_OK(status)) {
4416 return status;
4419 count = ads_count_replies(ads, res);
4420 if (count != 1) {
4421 ads_msgfree(ads, res);
4422 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4425 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4426 ads_msgfree(ads, res);
4427 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4430 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4431 ads_msgfree(ads, res);
4432 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4436 /* hack to compose the primary group sid without knowing the
4437 * domsid */
4439 struct dom_sid domsid;
4441 sid_copy(&domsid, &tmp_user_sid);
4443 if (!sid_split_rid(&domsid, NULL)) {
4444 ads_msgfree(ads, res);
4445 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4448 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4449 ads_msgfree(ads, res);
4450 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4454 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4456 if (tmp_num_sids == 0 || !tmp_sids) {
4457 ads_msgfree(ads, res);
4458 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4461 if (num_sids) {
4462 *num_sids = tmp_num_sids;
4465 if (sids) {
4466 *sids = tmp_sids;
4469 if (user_sid) {
4470 *user_sid = tmp_user_sid;
4473 if (primary_group_sid) {
4474 *primary_group_sid = tmp_primary_group_sid;
4477 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4479 ads_msgfree(ads, res);
4480 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4484 * Find a sAMAccountName in LDAP
4485 * @param ads connection to ads server
4486 * @param mem_ctx TALLOC_CTX for allocating sid array
4487 * @param samaccountname to search
4488 * @param uac_ret uint32_t pointer userAccountControl attribute value
4489 * @param dn_ret pointer to dn
4490 * @return status of token query
4492 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4493 TALLOC_CTX *mem_ctx,
4494 const char *samaccountname,
4495 uint32_t *uac_ret,
4496 const char **dn_ret)
4498 ADS_STATUS status;
4499 const char *attrs[] = { "userAccountControl", NULL };
4500 const char *filter;
4501 LDAPMessage *res = NULL;
4502 char *dn = NULL;
4503 uint32_t uac = 0;
4505 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4506 samaccountname);
4507 if (filter == NULL) {
4508 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4509 goto out;
4512 status = ads_do_search_all(ads, ads->config.bind_path,
4513 LDAP_SCOPE_SUBTREE,
4514 filter, attrs, &res);
4516 if (!ADS_ERR_OK(status)) {
4517 goto out;
4520 if (ads_count_replies(ads, res) != 1) {
4521 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4522 goto out;
4525 dn = ads_get_dn(ads, talloc_tos(), res);
4526 if (dn == NULL) {
4527 status = ADS_ERROR(LDAP_NO_MEMORY);
4528 goto out;
4531 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4532 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4533 goto out;
4536 if (uac_ret) {
4537 *uac_ret = uac;
4540 if (dn_ret) {
4541 *dn_ret = talloc_strdup(mem_ctx, dn);
4542 if (!*dn_ret) {
4543 status = ADS_ERROR(LDAP_NO_MEMORY);
4544 goto out;
4547 out:
4548 TALLOC_FREE(dn);
4549 ads_msgfree(ads, res);
4551 return status;
4555 * find our configuration path
4556 * @param ads connection to ads server
4557 * @param mem_ctx Pointer to talloc context
4558 * @param config_path Pointer to the config path
4559 * @return status of search
4561 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4562 TALLOC_CTX *mem_ctx,
4563 char **config_path)
4565 ADS_STATUS status;
4566 LDAPMessage *res = NULL;
4567 const char *config_context = NULL;
4568 const char *attrs[] = { "configurationNamingContext", NULL };
4570 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4571 "(objectclass=*)", attrs, &res);
4572 if (!ADS_ERR_OK(status)) {
4573 return status;
4576 config_context = ads_pull_string(ads, mem_ctx, res,
4577 "configurationNamingContext");
4578 ads_msgfree(ads, res);
4579 if (!config_context) {
4580 return ADS_ERROR(LDAP_NO_MEMORY);
4583 if (config_path) {
4584 *config_path = talloc_strdup(mem_ctx, config_context);
4585 if (!*config_path) {
4586 return ADS_ERROR(LDAP_NO_MEMORY);
4590 return ADS_ERROR(LDAP_SUCCESS);
4594 * find the displayName of an extended right
4595 * @param ads connection to ads server
4596 * @param config_path The config path
4597 * @param mem_ctx Pointer to talloc context
4598 * @param GUID struct of the rightsGUID
4599 * @return status of search
4601 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4602 const char *config_path,
4603 TALLOC_CTX *mem_ctx,
4604 const struct GUID *rights_guid)
4606 ADS_STATUS rc;
4607 LDAPMessage *res = NULL;
4608 char *expr = NULL;
4609 const char *attrs[] = { "displayName", NULL };
4610 const char *result = NULL;
4611 const char *path;
4613 if (!ads || !mem_ctx || !rights_guid) {
4614 goto done;
4617 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4618 GUID_string(mem_ctx, rights_guid));
4619 if (!expr) {
4620 goto done;
4623 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4624 if (!path) {
4625 goto done;
4628 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4629 expr, attrs, &res);
4630 if (!ADS_ERR_OK(rc)) {
4631 goto done;
4634 if (ads_count_replies(ads, res) != 1) {
4635 goto done;
4638 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4640 done:
4641 ads_msgfree(ads, res);
4642 return result;
4646 * verify or build and verify an account ou
4647 * @param mem_ctx Pointer to talloc context
4648 * @param ads connection to ads server
4649 * @param account_ou
4650 * @return status of search
4653 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4654 ADS_STRUCT *ads,
4655 const char **account_ou)
4657 char **exploded_dn;
4658 const char *name;
4659 char *ou_string;
4661 if (account_ou == NULL) {
4662 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4665 if (*account_ou != NULL) {
4666 exploded_dn = ldap_explode_dn(*account_ou, 0);
4667 if (exploded_dn) {
4668 ldap_value_free(exploded_dn);
4669 return ADS_SUCCESS;
4673 ou_string = ads_ou_string(ads, *account_ou);
4674 if (!ou_string) {
4675 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4678 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4679 ads->config.bind_path);
4680 SAFE_FREE(ou_string);
4682 if (!name) {
4683 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4686 exploded_dn = ldap_explode_dn(name, 0);
4687 if (!exploded_dn) {
4688 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4690 ldap_value_free(exploded_dn);
4692 *account_ou = name;
4693 return ADS_SUCCESS;
4696 #endif