s3:libads: split out ads_legacy_creds()
[Samba.git] / source3 / libads / ldap.c
blobb5bf5634086e71b7f9a858c1391c61a4c7ba9dc6
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "ads.h"
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/tsocket/tsocket.h"
29 #include "../lib/addns/dnsquery.h"
30 #include "../libds/common/flags.h"
31 #include "smbldap.h"
32 #include "../libcli/security/security.h"
33 #include "../librpc/gen_ndr/netlogon.h"
34 #include "lib/param/loadparm.h"
35 #include "libsmb/namequery.h"
36 #include "../librpc/gen_ndr/ndr_ads.h"
38 #ifdef HAVE_LDAP
40 /**
41 * @file ldap.c
42 * @brief basic ldap client-side routines for ads server communications
44 * The routines contained here should do the necessary ldap calls for
45 * ads setups.
47 * Important note: attribute names passed into ads_ routines must
48 * already be in UTF-8 format. We do not convert them because in almost
49 * all cases, they are just ascii (which is represented with the same
50 * codepoints in UTF-8). This may have to change at some point
51 **/
54 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
56 static SIG_ATOMIC_T gotalarm;
58 /***************************************************************
59 Signal function to tell us we timed out.
60 ****************************************************************/
62 static void gotalarm_sig(int signum)
64 gotalarm = 1;
67 LDAP *ldap_open_with_timeout(const char *server,
68 struct sockaddr_storage *ss,
69 int port, unsigned int to)
71 LDAP *ldp = NULL;
72 int ldap_err;
73 char *uri;
75 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
76 "%u seconds\n", server, port, to));
78 if (to) {
79 /* Setup timeout */
80 gotalarm = 0;
81 CatchSignal(SIGALRM, gotalarm_sig);
82 alarm(to);
83 /* End setup timeout. */
86 if ( strchr_m(server, ':') ) {
87 /* IPv6 URI */
88 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
89 } else {
90 /* IPv4 URI */
91 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
93 if (uri == NULL) {
94 return NULL;
97 #ifdef HAVE_LDAP_INIT_FD
99 int fd = -1;
100 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
101 unsigned timeout_ms = 1000 * to;
103 status = open_socket_out(ss, port, timeout_ms, &fd);
104 if (!NT_STATUS_IS_OK(status)) {
105 DEBUG(3, ("open_socket_out: failed to open socket\n"));
106 return NULL;
109 /* define LDAP_PROTO_TCP from openldap.h if required */
110 #ifndef LDAP_PROTO_TCP
111 #define LDAP_PROTO_TCP 1
112 #endif
113 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
115 #elif defined(HAVE_LDAP_INITIALIZE)
116 ldap_err = ldap_initialize(&ldp, uri);
117 #else
118 ldp = ldap_open(server, port);
119 if (ldp != NULL) {
120 ldap_err = LDAP_SUCCESS;
121 } else {
122 ldap_err = LDAP_OTHER;
124 #endif
125 if (ldap_err != LDAP_SUCCESS) {
126 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
127 uri, ldap_err2string(ldap_err)));
128 } else {
129 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
132 if (to) {
133 /* Teardown timeout. */
134 alarm(0);
135 CatchSignal(SIGALRM, SIG_IGN);
138 return ldp;
141 static int ldap_search_with_timeout(LDAP *ld,
142 LDAP_CONST char *base,
143 int scope,
144 LDAP_CONST char *filter,
145 char **attrs,
146 int attrsonly,
147 LDAPControl **sctrls,
148 LDAPControl **cctrls,
149 int sizelimit,
150 LDAPMessage **res )
152 int to = lp_ldap_timeout();
153 struct timeval timeout;
154 struct timeval *timeout_ptr = NULL;
155 int result;
157 DBG_DEBUG("ldap_search: base => [%s], filter => [%s], scope => [%d]\n",
158 base,
159 filter,
160 scope);
162 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
163 gotalarm = 0;
165 if (to) {
166 timeout.tv_sec = to;
167 timeout.tv_usec = 0;
168 timeout_ptr = &timeout;
170 /* Setup alarm timeout. */
171 CatchSignal(SIGALRM, gotalarm_sig);
172 /* Make the alarm time one second beyond
173 the timeout we're setting for the
174 remote search timeout, to allow that
175 to fire in preference. */
176 alarm(to+1);
177 /* End setup timeout. */
181 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
182 attrsonly, sctrls, cctrls, timeout_ptr,
183 sizelimit, res);
185 if (to) {
186 /* Teardown alarm timeout. */
187 CatchSignal(SIGALRM, SIG_IGN);
188 alarm(0);
191 if (gotalarm != 0)
192 return LDAP_TIMELIMIT_EXCEEDED;
195 * A bug in OpenLDAP means ldap_search_ext_s can return
196 * LDAP_SUCCESS but with a NULL res pointer. Cope with
197 * this. See bug #6279 for details. JRA.
200 if (*res == NULL) {
201 return LDAP_TIMELIMIT_EXCEEDED;
204 return result;
207 /**********************************************
208 Do client and server sitename match ?
209 **********************************************/
211 bool ads_sitename_match(ADS_STRUCT *ads)
213 if (ads->config.server_site_name == NULL &&
214 ads->config.client_site_name == NULL ) {
215 DEBUG(10,("ads_sitename_match: both null\n"));
216 return True;
218 if (ads->config.server_site_name &&
219 ads->config.client_site_name &&
220 strequal(ads->config.server_site_name,
221 ads->config.client_site_name)) {
222 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
223 return True;
225 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
226 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
227 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
228 return False;
231 /**********************************************
232 Is this the closest DC ?
233 **********************************************/
235 bool ads_closest_dc(ADS_STRUCT *ads)
237 if (ads->config.flags & NBT_SERVER_CLOSEST) {
238 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
239 return True;
242 /* not sure if this can ever happen */
243 if (ads_sitename_match(ads)) {
244 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
245 return True;
248 if (ads->config.client_site_name == NULL) {
249 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
250 return True;
253 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
254 ads->config.ldap_server_name));
256 return False;
259 static bool ads_fill_cldap_reply(ADS_STRUCT *ads,
260 bool gc,
261 const struct sockaddr_storage *ss,
262 const struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply)
264 TALLOC_CTX *frame = talloc_stackframe();
265 bool ret = false;
266 char addr[INET6_ADDRSTRLEN];
267 ADS_STATUS status;
268 char *dn;
270 print_sockaddr(addr, sizeof(addr), ss);
272 /* Check the CLDAP reply flags */
274 if (!(cldap_reply->server_type & NBT_SERVER_LDAP)) {
275 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
276 addr);
277 ret = false;
278 goto out;
281 /* Fill in the ads->config values */
283 ADS_TALLOC_CONST_FREE(ads->config.workgroup);
284 ADS_TALLOC_CONST_FREE(ads->config.realm);
285 ADS_TALLOC_CONST_FREE(ads->config.bind_path);
286 ADS_TALLOC_CONST_FREE(ads->config.ldap_server_name);
287 ADS_TALLOC_CONST_FREE(ads->config.server_site_name);
288 ADS_TALLOC_CONST_FREE(ads->config.client_site_name);
290 if (!check_cldap_reply_required_flags(cldap_reply->server_type,
291 ads->config.flags)) {
292 ret = false;
293 goto out;
296 ads->config.ldap_server_name = talloc_strdup(ads,
297 cldap_reply->pdc_dns_name);
298 if (ads->config.ldap_server_name == NULL) {
299 DBG_WARNING("Out of memory\n");
300 ret = false;
301 goto out;
304 ads->config.workgroup = talloc_strdup(ads, cldap_reply->domain_name);
305 if (ads->config.workgroup == NULL) {
306 DBG_WARNING("Out of memory\n");
307 ret = false;
308 goto out;
311 ads->config.realm = talloc_asprintf_strupper_m(ads,
312 "%s",
313 cldap_reply->dns_domain);
314 if (ads->config.realm == NULL) {
315 DBG_WARNING("Out of memory\n");
316 ret = false;
317 goto out;
320 status = ads_build_dn(ads->config.realm, ads, &dn);
321 if (!ADS_ERR_OK(status)) {
322 DBG_DEBUG("Failed to build bind path: %s\n",
323 ads_errstr(status));
324 ret = false;
325 goto out;
327 ads->config.bind_path = dn;
329 if (*cldap_reply->server_site) {
330 ads->config.server_site_name =
331 talloc_strdup(ads, cldap_reply->server_site);
332 if (ads->config.server_site_name == NULL) {
333 DBG_WARNING("Out of memory\n");
334 ret = false;
335 goto out;
339 if (*cldap_reply->client_site) {
340 ads->config.client_site_name =
341 talloc_strdup(ads, cldap_reply->client_site);
342 if (ads->config.client_site_name == NULL) {
343 DBG_WARNING("Out of memory\n");
344 ret = false;
345 goto out;
349 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
350 ads->ldap.ss = *ss;
352 /* Store our site name. */
353 sitename_store(cldap_reply->domain_name, cldap_reply->client_site);
354 sitename_store(cldap_reply->dns_domain, cldap_reply->client_site);
356 /* Leave this until last so that the flags are not clobbered */
357 ads->config.flags = cldap_reply->server_type;
359 ret = true;
361 out:
363 TALLOC_FREE(frame);
364 return ret;
368 try a connection to a given ldap server, returning True and setting the servers IP
369 in the ads struct if successful
371 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
372 struct sockaddr_storage *ss)
374 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply = {};
375 TALLOC_CTX *frame = talloc_stackframe();
376 bool ok;
377 char addr[INET6_ADDRSTRLEN] = { 0, };
379 if (ss == NULL) {
380 TALLOC_FREE(frame);
381 return false;
384 print_sockaddr(addr, sizeof(addr), ss);
386 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
387 addr, ads->server.realm);
389 ok = ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply);
390 if (!ok) {
391 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
392 addr, ads->server.realm);
393 TALLOC_FREE(frame);
394 return false;
397 ok = ads_fill_cldap_reply(ads, gc, ss, &cldap_reply);
398 if (!ok) {
399 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
400 addr, ads->server.realm);
401 TALLOC_FREE(frame);
402 return false;
405 TALLOC_FREE(frame);
406 return true;
409 /**********************************************************************
410 send a cldap ping to list of servers, one at a time, until one of
411 them answers it's an ldap server. Record success in the ADS_STRUCT.
412 Take note of and update negative connection cache.
413 **********************************************************************/
415 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
416 const char *domain,
417 struct samba_sockaddr *sa_list,
418 size_t count)
420 TALLOC_CTX *frame = talloc_stackframe();
421 struct timeval endtime = timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
422 uint32_t nt_version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
423 struct tsocket_address **ts_list = NULL;
424 const struct tsocket_address * const *ts_list_const = NULL;
425 struct samba_sockaddr **req_sa_list = NULL;
426 struct netlogon_samlogon_response **responses = NULL;
427 size_t num_requests = 0;
428 NTSTATUS status;
429 size_t i;
430 bool ok = false;
431 bool retry;
433 ts_list = talloc_zero_array(frame,
434 struct tsocket_address *,
435 count);
436 if (ts_list == NULL) {
437 TALLOC_FREE(frame);
438 return NT_STATUS_NO_MEMORY;
441 req_sa_list = talloc_zero_array(frame,
442 struct samba_sockaddr *,
443 count);
444 if (req_sa_list == NULL) {
445 TALLOC_FREE(frame);
446 return NT_STATUS_NO_MEMORY;
449 again:
451 * The retry loop is bound by the timeout
453 retry = false;
454 num_requests = 0;
456 for (i = 0; i < count; i++) {
457 char server[INET6_ADDRSTRLEN];
458 int ret;
460 if (is_zero_addr(&sa_list[i].u.ss)) {
461 continue;
464 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
466 status = check_negative_conn_cache(domain, server);
467 if (!NT_STATUS_IS_OK(status)) {
468 continue;
471 ret = tsocket_address_inet_from_strings(ts_list, "ip",
472 server, LDAP_PORT,
473 &ts_list[num_requests]);
474 if (ret != 0) {
475 status = map_nt_error_from_unix(errno);
476 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
477 server, nt_errstr(status));
478 TALLOC_FREE(frame);
479 return status;
482 req_sa_list[num_requests] = &sa_list[i];
483 num_requests += 1;
486 DBG_DEBUG("Try to create %zu netlogon connections for domain '%s' "
487 "(provided count of addresses was %zu).\n",
488 num_requests,
489 domain,
490 count);
492 if (num_requests == 0) {
493 status = NT_STATUS_NO_LOGON_SERVERS;
494 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
495 domain, num_requests, count, nt_errstr(status));
496 TALLOC_FREE(frame);
497 return status;
500 ts_list_const = (const struct tsocket_address * const *)ts_list;
502 status = cldap_multi_netlogon(frame,
503 ts_list_const, num_requests,
504 ads->server.realm, NULL,
505 nt_version,
506 1, endtime, &responses);
507 if (!NT_STATUS_IS_OK(status)) {
508 DBG_WARNING("cldap_multi_netlogon(realm=%s, num_requests=%zu) "
509 "for count[%zu] - %s\n",
510 ads->server.realm,
511 num_requests, count,
512 nt_errstr(status));
513 TALLOC_FREE(frame);
514 return NT_STATUS_NO_LOGON_SERVERS;
517 for (i = 0; i < num_requests; i++) {
518 struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply = NULL;
519 char server[INET6_ADDRSTRLEN];
521 if (responses[i] == NULL) {
522 continue;
525 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
527 if (responses[i]->ntver != NETLOGON_NT_VERSION_5EX) {
528 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
529 ads->server.realm,
530 responses[i]->ntver, server);
531 continue;
534 cldap_reply = &responses[i]->data.nt5_ex;
536 /* Returns ok only if it matches the correct server type */
537 ok = ads_fill_cldap_reply(ads,
538 false,
539 &req_sa_list[i]->u.ss,
540 cldap_reply);
541 if (ok) {
542 DBG_DEBUG("realm[%s]: selected %s => %s\n",
543 ads->server.realm,
544 server, cldap_reply->pdc_dns_name);
545 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
546 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
547 cldap_reply);
549 TALLOC_FREE(frame);
550 return NT_STATUS_OK;
553 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
554 ads->server.realm,
555 server, cldap_reply->pdc_dns_name);
556 if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
557 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
558 cldap_reply);
560 add_failed_connection_entry(domain, server,
561 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID);
562 retry = true;
565 if (retry) {
566 bool expired;
568 expired = timeval_expired(&endtime);
569 if (!expired) {
570 goto again;
574 /* keep track of failures as all were not suitable */
575 for (i = 0; i < num_requests; i++) {
576 char server[INET6_ADDRSTRLEN];
578 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
580 add_failed_connection_entry(domain, server,
581 NT_STATUS_UNSUCCESSFUL);
584 status = NT_STATUS_NO_LOGON_SERVERS;
585 DBG_WARNING("realm[%s] no valid response "
586 "num_requests[%zu] for count[%zu] - %s\n",
587 ads->server.realm,
588 num_requests, count, nt_errstr(status));
589 TALLOC_FREE(frame);
590 return NT_STATUS_NO_LOGON_SERVERS;
593 /***************************************************************************
594 resolve a name and perform an "ldap ping" using NetBIOS and related methods
595 ****************************************************************************/
597 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
598 const char *domain, const char *realm)
600 size_t i;
601 size_t count = 0;
602 struct samba_sockaddr *sa_list = NULL;
603 NTSTATUS status;
605 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
606 domain));
608 status = get_sorted_dc_list(talloc_tos(),
609 domain,
610 NULL,
611 &sa_list,
612 &count,
613 false);
614 if (!NT_STATUS_IS_OK(status)) {
615 return status;
618 /* remove servers which are known to be dead based on
619 the corresponding DNS method */
620 if (*realm) {
621 for (i = 0; i < count; ++i) {
622 char server[INET6_ADDRSTRLEN];
624 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
626 if(!NT_STATUS_IS_OK(
627 check_negative_conn_cache(realm, server))) {
628 /* Ensure we add the workgroup name for this
629 IP address as negative too. */
630 add_failed_connection_entry(
631 domain, server,
632 NT_STATUS_UNSUCCESSFUL);
637 status = cldap_ping_list(ads, domain, sa_list, count);
639 TALLOC_FREE(sa_list);
641 return status;
645 /**********************************************************************
646 resolve a name and perform an "ldap ping" using DNS
647 **********************************************************************/
649 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
650 const char *realm)
652 size_t count = 0;
653 struct samba_sockaddr *sa_list = NULL;
654 NTSTATUS status;
656 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
657 realm));
659 status = get_sorted_dc_list(talloc_tos(),
660 realm,
661 sitename,
662 &sa_list,
663 &count,
664 true);
665 if (!NT_STATUS_IS_OK(status)) {
666 TALLOC_FREE(sa_list);
667 return status;
670 status = cldap_ping_list(ads, realm, sa_list, count);
672 TALLOC_FREE(sa_list);
674 return status;
677 /**********************************************************************
678 Try to find an AD dc using our internal name resolution routines
679 Try the realm first and then the workgroup name if netbios is not
680 disabled
681 **********************************************************************/
683 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
685 const char *c_domain = "";
686 const char *c_realm;
687 bool use_own_domain = False;
688 char *sitename = NULL;
689 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
690 bool ok = false;
692 /* if the realm and workgroup are both empty, assume they are ours */
694 /* realm */
695 c_realm = ads->server.realm;
697 if (c_realm == NULL)
698 c_realm = "";
700 if (!*c_realm) {
701 /* special case where no realm and no workgroup means our own */
702 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
703 use_own_domain = True;
704 c_realm = lp_realm();
708 if (!lp_disable_netbios()) {
709 if (use_own_domain) {
710 c_domain = lp_workgroup();
711 } else {
712 c_domain = ads->server.workgroup;
713 if (!*c_realm && (!c_domain || !*c_domain)) {
714 c_domain = lp_workgroup();
718 if (!c_domain) {
719 c_domain = "";
723 if (!*c_realm && !*c_domain) {
724 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
725 "what to do\n"));
726 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
730 * In case of LDAP we use get_dc_name() as that
731 * creates the custom krb5.conf file
733 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
734 fstring srv_name;
735 struct sockaddr_storage ip_out;
737 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
738 " and falling back to domain '%s'\n",
739 c_realm, c_domain));
741 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
742 if (ok) {
743 if (is_zero_addr(&ip_out)) {
744 return NT_STATUS_NO_LOGON_SERVERS;
748 * we call ads_try_connect() to fill in the
749 * ads->config details
751 ok = ads_try_connect(ads, false, &ip_out);
752 if (ok) {
753 return NT_STATUS_OK;
757 return NT_STATUS_NO_LOGON_SERVERS;
760 if (*c_realm) {
761 sitename = sitename_fetch(talloc_tos(), c_realm);
762 status = resolve_and_ping_dns(ads, sitename, c_realm);
764 if (NT_STATUS_IS_OK(status)) {
765 TALLOC_FREE(sitename);
766 return status;
769 /* In case we failed to contact one of our closest DC on our
770 * site we
771 * need to try to find another DC, retry with a site-less SRV
772 * DNS query
773 * - Guenther */
775 if (sitename) {
776 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
777 "our site (%s), Trying to find another DC "
778 "for realm '%s' (domain '%s')\n",
779 sitename, c_realm, c_domain));
780 namecache_delete(c_realm, 0x1C);
781 status =
782 resolve_and_ping_dns(ads, NULL, c_realm);
784 if (NT_STATUS_IS_OK(status)) {
785 TALLOC_FREE(sitename);
786 return status;
790 TALLOC_FREE(sitename);
793 /* try netbios as fallback - if permitted,
794 or if configuration specifically requests it */
795 if (*c_domain) {
796 if (*c_realm) {
797 DEBUG(3, ("ads_find_dc: falling back to netbios "
798 "name resolution for domain '%s' (realm '%s')\n",
799 c_domain, c_realm));
802 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
803 if (NT_STATUS_IS_OK(status)) {
804 return status;
808 DEBUG(1, ("ads_find_dc: "
809 "name resolution for realm '%s' (domain '%s') failed: %s\n",
810 c_realm, c_domain, nt_errstr(status)));
811 return status;
814 * Connect to the LDAP server
815 * @param ads Pointer to an existing ADS_STRUCT
816 * @return status of connection
818 ADS_STATUS ads_connect(ADS_STRUCT *ads)
820 int version = LDAP_VERSION3;
821 ADS_STATUS status;
822 NTSTATUS ntstatus;
823 char addr[INET6_ADDRSTRLEN];
824 struct sockaddr_storage existing_ss;
825 bool tls = false;
826 bool start_tls = false;
828 zero_sockaddr(&existing_ss);
831 * ads_connect can be passed in a reused ADS_STRUCT
832 * with an existing non-zero ads->ldap.ss IP address
833 * that was stored by going through ads_find_dc()
834 * if ads->server.ldap_server was NULL.
836 * If ads->server.ldap_server is still NULL but
837 * the target address isn't the zero address, then
838 * store that address off off before zeroing out
839 * ads->ldap so we don't keep doing multiple calls
840 * to ads_find_dc() in the reuse case.
842 * If a caller wants a clean ADS_STRUCT they
843 * will TALLOC_FREE it and allocate a new one
844 * by calling ads_init(), which ensures
845 * ads->ldap.ss is a properly zero'ed out valid IP
846 * address.
848 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
849 /* Save off the address we previously found by ads_find_dc(). */
850 existing_ss = ads->ldap.ss;
853 ads_zero_ldap(ads);
854 ZERO_STRUCT(ads->ldap_tls_data);
855 ZERO_STRUCT(ads->ldap_wrap_data);
856 ads->ldap.last_attempt = time_mono(NULL);
857 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
859 /* try with a user specified server */
861 if (DEBUGLEVEL >= 11) {
862 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
863 DEBUG(11,("ads_connect: entering\n"));
864 DEBUGADD(11,("%s\n", s));
865 TALLOC_FREE(s);
868 if (ads->server.ldap_server) {
869 bool ok = false;
870 struct sockaddr_storage ss;
872 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
873 ads->server.ldap_server);
874 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
875 if (!ok) {
876 DEBUG(5,("ads_connect: unable to resolve name %s\n",
877 ads->server.ldap_server));
878 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
879 goto out;
882 if (is_zero_addr(&ss)) {
883 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
884 goto out;
887 ok = ads_try_connect(ads, ads->server.gc, &ss);
888 if (ok) {
889 goto got_connection;
892 /* The choice of which GC use is handled one level up in
893 ads_connect_gc(). If we continue on from here with
894 ads_find_dc() we will get GC searches on port 389 which
895 doesn't work. --jerry */
897 if (ads->server.gc == true) {
898 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
901 if (ads->server.no_fallback) {
902 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
903 goto out;
907 if (!is_zero_addr(&existing_ss)) {
908 /* We saved off who we should talk to. */
909 bool ok = ads_try_connect(ads,
910 ads->server.gc,
911 &existing_ss);
912 if (ok) {
913 goto got_connection;
916 * Keep trying to find a server and fall through
917 * into ads_find_dc() again.
919 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
920 "trying to find another DC.\n");
923 ntstatus = ads_find_dc(ads);
924 if (NT_STATUS_IS_OK(ntstatus)) {
925 goto got_connection;
928 status = ADS_ERROR_NT(ntstatus);
929 goto out;
931 got_connection:
933 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
934 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
936 if (!ads->auth.kdc_server) {
937 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
938 ads->auth.kdc_server = talloc_strdup(ads, addr);
939 if (ads->auth.kdc_server == NULL) {
940 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
941 goto out;
945 /* If the caller() requested no LDAP bind, then we are done */
947 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
948 status = ADS_SUCCESS;
949 goto out;
952 ads->ldap_tls_data.mem_ctx = talloc_init("ads LDAP TLS connection memory");
953 if (!ads->ldap_tls_data.mem_ctx) {
954 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
955 goto out;
958 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
959 if (!ads->ldap_wrap_data.mem_ctx) {
960 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
961 goto out;
964 /* Otherwise setup the TCP LDAP session */
966 if (ads->auth.flags & ADS_AUTH_SASL_LDAPS) {
967 tls = true;
968 ads->ldap.port = 636;
969 } else if (ads->auth.flags & ADS_AUTH_SASL_STARTTLS) {
970 tls = true;
971 start_tls = true;
972 ads->ldap.port = 389;
973 } else {
974 ads->ldap.port = 389;
977 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
978 &ads->ldap.ss,
979 ads->ldap.port, lp_ldap_timeout());
980 if (ads->ldap.ld == NULL) {
981 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
982 goto out;
984 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
986 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
988 if (start_tls) {
989 unsigned int to = lp_ldap_connection_timeout();
990 struct berval *rspdata = NULL;
991 char *rspoid = NULL;
992 int rc;
994 if (to) {
995 /* Setup timeout */
996 gotalarm = 0;
997 CatchSignal(SIGALRM, gotalarm_sig);
998 alarm(to);
999 /* End setup timeout. */
1002 rc = ldap_extended_operation_s(ads->ldap.ld,
1003 LDAP_EXOP_START_TLS,
1004 NULL,
1005 NULL,
1006 NULL,
1007 &rspoid,
1008 &rspdata);
1009 if (gotalarm != 0 && rc == LDAP_SUCCESS) {
1010 rc = LDAP_TIMEOUT;
1013 if (to) {
1014 /* Teardown timeout. */
1015 alarm(0);
1016 CatchSignal(SIGALRM, SIG_IGN);
1019 if (rspoid != NULL) {
1020 ldap_memfree(rspoid);
1023 if (rspdata != NULL) {
1024 ber_bvfree(rspdata);
1027 if (rc != LDAP_SUCCESS) {
1028 status = ADS_ERROR_LDAP(rc);
1029 goto out;
1033 if (tls) {
1034 unsigned int to = lp_ldap_connection_timeout();
1036 if (to) {
1037 /* Setup timeout */
1038 gotalarm = 0;
1039 CatchSignal(SIGALRM, gotalarm_sig);
1040 alarm(to);
1041 /* End setup timeout. */
1044 status = ads_setup_tls_wrapping(&ads->ldap_tls_data,
1045 ads->ldap.ld,
1046 ads->config.ldap_server_name);
1048 if (to) {
1049 /* Teardown timeout. */
1050 alarm(0);
1051 CatchSignal(SIGALRM, SIG_IGN);
1054 if ( !ADS_ERR_OK(status) ) {
1055 goto out;
1059 /* cache the successful connection for workgroup and realm */
1060 if (ads_closest_dc(ads)) {
1061 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
1062 saf_store( ads->server.realm, ads->config.ldap_server_name);
1065 /* fill in the current time and offsets */
1067 status = ads_current_time( ads );
1068 if ( !ADS_ERR_OK(status) ) {
1069 goto out;
1072 /* Now do the bind */
1074 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
1075 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
1076 goto out;
1079 status = ads_sasl_bind(ads);
1081 out:
1082 if (DEBUGLEVEL >= 11) {
1083 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1084 DEBUG(11,("ads_connect: leaving with: %s\n",
1085 ads_errstr(status)));
1086 DEBUGADD(11,("%s\n", s));
1087 TALLOC_FREE(s);
1090 return status;
1094 * Connect to the LDAP server using given credentials
1095 * @param ads Pointer to an existing ADS_STRUCT
1096 * @return status of connection
1098 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
1100 ads->auth.flags |= ADS_AUTH_USER_CREDS;
1102 return ads_connect(ads);
1106 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1107 * @param ads Pointer to an existing ADS_STRUCT
1109 * Sets the ads->ldap.ss to a valid
1110 * zero ip address that can be detected by
1111 * our is_zero_addr() function. Otherwise
1112 * it is left as AF_UNSPEC (0).
1114 void ads_zero_ldap(ADS_STRUCT *ads)
1116 ZERO_STRUCT(ads->ldap);
1118 * Initialize the sockaddr_storage so we can use
1119 * sockaddr test functions against it.
1121 zero_sockaddr(&ads->ldap.ss);
1125 * Disconnect the LDAP server
1126 * @param ads Pointer to an existing ADS_STRUCT
1128 void ads_disconnect(ADS_STRUCT *ads)
1130 if (ads->ldap.ld) {
1131 ldap_unbind(ads->ldap.ld);
1132 ads->ldap.ld = NULL;
1134 if (ads->ldap_tls_data.mem_ctx) {
1135 talloc_free(ads->ldap_tls_data.mem_ctx);
1137 if (ads->ldap_wrap_data.wrap_ops &&
1138 ads->ldap_wrap_data.wrap_ops->disconnect) {
1139 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1141 if (ads->ldap_wrap_data.mem_ctx) {
1142 talloc_free(ads->ldap_wrap_data.mem_ctx);
1144 ads_zero_ldap(ads);
1145 ZERO_STRUCT(ads->ldap_tls_data);
1146 ZERO_STRUCT(ads->ldap_wrap_data);
1150 Duplicate a struct berval into talloc'ed memory
1152 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1154 struct berval *value;
1156 if (!in_val) return NULL;
1158 value = talloc_zero(ctx, struct berval);
1159 if (value == NULL)
1160 return NULL;
1161 if (in_val->bv_len == 0) return value;
1163 value->bv_len = in_val->bv_len;
1164 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1165 in_val->bv_len);
1166 return value;
1170 Make a values list out of an array of (struct berval *)
1172 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1173 const struct berval **in_vals)
1175 struct berval **values;
1176 int i;
1178 if (!in_vals) return NULL;
1179 for (i=0; in_vals[i]; i++)
1180 ; /* count values */
1181 values = talloc_zero_array(ctx, struct berval *, i+1);
1182 if (!values) return NULL;
1184 for (i=0; in_vals[i]; i++) {
1185 values[i] = dup_berval(ctx, in_vals[i]);
1187 return values;
1191 UTF8-encode a values list out of an array of (char *)
1193 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1195 char **values;
1196 int i;
1197 size_t size;
1199 if (!in_vals) return NULL;
1200 for (i=0; in_vals[i]; i++)
1201 ; /* count values */
1202 values = talloc_zero_array(ctx, char *, i+1);
1203 if (!values) return NULL;
1205 for (i=0; in_vals[i]; i++) {
1206 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1207 TALLOC_FREE(values);
1208 return NULL;
1211 return values;
1215 Pull a (char *) array out of a UTF8-encoded values list
1217 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1219 char **values;
1220 int i;
1221 size_t converted_size;
1223 if (!in_vals) return NULL;
1224 for (i=0; in_vals[i]; i++)
1225 ; /* count values */
1226 values = talloc_zero_array(ctx, char *, i+1);
1227 if (!values) return NULL;
1229 for (i=0; in_vals[i]; i++) {
1230 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1231 &converted_size)) {
1232 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1233 "%s\n", strerror(errno)));
1236 return values;
1240 * Do a search with paged results. cookie must be null on the first
1241 * call, and then returned on each subsequent call. It will be null
1242 * again when the entire search is complete
1243 * @param ads connection to ads server
1244 * @param bind_path Base dn for the search
1245 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1246 * @param expr Search expression - specified in local charset
1247 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1248 * @param res ** which will contain results - free res* with ads_msgfree()
1249 * @param count Number of entries retrieved on this page
1250 * @param cookie The paged results cookie to be returned on subsequent calls
1251 * @return status of search
1253 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1254 const char *bind_path,
1255 int scope, const char *expr,
1256 const char **attrs, void *args,
1257 LDAPMessage **res,
1258 int *count, struct berval **cookie)
1260 int rc, i, version;
1261 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1262 size_t converted_size;
1263 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1264 BerElement *cookie_be = NULL;
1265 struct berval *cookie_bv= NULL;
1266 BerElement *ext_be = NULL;
1267 struct berval *ext_bv= NULL;
1269 TALLOC_CTX *ctx;
1270 ads_control *external_control = (ads_control *) args;
1272 *res = NULL;
1274 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1275 return ADS_ERROR(LDAP_NO_MEMORY);
1277 /* 0 means the conversion worked but the result was empty
1278 so we only fail if it's -1. In any case, it always
1279 at least nulls out the dest */
1280 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1281 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1283 rc = LDAP_NO_MEMORY;
1284 goto done;
1287 if (!attrs || !(*attrs))
1288 search_attrs = NULL;
1289 else {
1290 /* This would be the utf8-encoded version...*/
1291 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1292 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1293 rc = LDAP_NO_MEMORY;
1294 goto done;
1298 /* Paged results only available on ldap v3 or later */
1299 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1300 if (version < LDAP_VERSION3) {
1301 rc = LDAP_NOT_SUPPORTED;
1302 goto done;
1305 cookie_be = ber_alloc_t(LBER_USE_DER);
1306 if (*cookie) {
1307 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1308 ber_bvfree(*cookie); /* don't need it from last time */
1309 *cookie = NULL;
1310 } else {
1311 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1313 ber_flatten(cookie_be, &cookie_bv);
1314 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1315 PagedResults.ldctl_iscritical = (char) 1;
1316 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1317 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1319 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1320 NoReferrals.ldctl_iscritical = (char) 0;
1321 NoReferrals.ldctl_value.bv_len = 0;
1322 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1324 if (external_control &&
1325 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1326 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1328 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1329 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1331 /* win2k does not accept a ldctl_value being passed in */
1333 if (external_control->val != 0) {
1335 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1336 rc = LDAP_NO_MEMORY;
1337 goto done;
1340 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1341 rc = LDAP_NO_MEMORY;
1342 goto done;
1344 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1345 rc = LDAP_NO_MEMORY;
1346 goto done;
1349 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1350 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1352 } else {
1353 ExternalCtrl.ldctl_value.bv_len = 0;
1354 ExternalCtrl.ldctl_value.bv_val = NULL;
1357 controls[0] = &NoReferrals;
1358 controls[1] = &PagedResults;
1359 controls[2] = &ExternalCtrl;
1360 controls[3] = NULL;
1362 } else {
1363 controls[0] = &NoReferrals;
1364 controls[1] = &PagedResults;
1365 controls[2] = NULL;
1368 /* we need to disable referrals as the openldap libs don't
1369 handle them and paged results at the same time. Using them
1370 together results in the result record containing the server
1371 page control being removed from the result list (tridge/jmcd)
1373 leaving this in despite the control that says don't generate
1374 referrals, in case the server doesn't support it (jmcd)
1376 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1378 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1379 search_attrs, 0, controls,
1380 NULL, LDAP_NO_LIMIT,
1381 (LDAPMessage **)res);
1383 ber_free(cookie_be, 1);
1384 ber_bvfree(cookie_bv);
1386 if (rc) {
1387 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1388 ldap_err2string(rc)));
1389 if (rc == LDAP_OTHER) {
1390 char *ldap_errmsg;
1391 int ret;
1393 ret = ldap_parse_result(ads->ldap.ld,
1394 *res,
1395 NULL,
1396 NULL,
1397 &ldap_errmsg,
1398 NULL,
1399 NULL,
1401 if (ret == LDAP_SUCCESS) {
1402 DEBUG(3, ("ldap_search_with_timeout(%s) "
1403 "error: %s\n", expr, ldap_errmsg));
1404 ldap_memfree(ldap_errmsg);
1407 goto done;
1410 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1411 NULL, &rcontrols, 0);
1413 if (!rcontrols) {
1414 goto done;
1417 for (i=0; rcontrols[i]; i++) {
1418 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1419 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1420 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1421 &cookie_bv);
1422 /* the berval is the cookie, but must be freed when
1423 it is all done */
1424 if (cookie_bv->bv_len) /* still more to do */
1425 *cookie=ber_bvdup(cookie_bv);
1426 else
1427 *cookie=NULL;
1428 ber_bvfree(cookie_bv);
1429 ber_free(cookie_be, 1);
1430 break;
1433 ldap_controls_free(rcontrols);
1435 done:
1436 talloc_destroy(ctx);
1438 if (ext_be) {
1439 ber_free(ext_be, 1);
1442 if (ext_bv) {
1443 ber_bvfree(ext_bv);
1446 if (rc != LDAP_SUCCESS && *res != NULL) {
1447 ads_msgfree(ads, *res);
1448 *res = NULL;
1451 /* if/when we decide to utf8-encode attrs, take out this next line */
1452 TALLOC_FREE(search_attrs);
1454 return ADS_ERROR(rc);
1457 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1458 int scope, const char *expr,
1459 const char **attrs, LDAPMessage **res,
1460 int *count, struct berval **cookie)
1462 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1467 * Get all results for a search. This uses ads_do_paged_search() to return
1468 * all entries in a large search.
1469 * @param ads connection to ads server
1470 * @param bind_path Base dn for the search
1471 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1472 * @param expr Search expression
1473 * @param attrs Attributes to retrieve
1474 * @param res ** which will contain results - free res* with ads_msgfree()
1475 * @return status of search
1477 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1478 int scope, const char *expr,
1479 const char **attrs, void *args,
1480 LDAPMessage **res)
1482 struct berval *cookie = NULL;
1483 int count = 0;
1484 ADS_STATUS status;
1486 *res = NULL;
1487 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1488 &count, &cookie);
1490 if (!ADS_ERR_OK(status))
1491 return status;
1493 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1494 while (cookie) {
1495 LDAPMessage *res2 = NULL;
1496 LDAPMessage *msg, *next;
1498 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1499 attrs, args, &res2, &count, &cookie);
1500 if (!ADS_ERR_OK(status)) {
1501 break;
1504 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1505 that this works on all ldap libs, but I have only tested with openldap */
1506 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1507 next = ads_next_message(ads, msg);
1508 ldap_add_result_entry((LDAPMessage **)res, msg);
1510 /* note that we do not free res2, as the memory is now
1511 part of the main returned list */
1513 #else
1514 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1515 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1516 #endif
1518 return status;
1521 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1522 int scope, const char *expr,
1523 const char **attrs, LDAPMessage **res)
1525 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1528 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1529 int scope, const char *expr,
1530 const char **attrs, uint32_t sd_flags,
1531 LDAPMessage **res)
1533 ads_control args;
1535 args.control = ADS_SD_FLAGS_OID;
1536 args.val = sd_flags;
1537 args.critical = True;
1539 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1544 * Run a function on all results for a search. Uses ads_do_paged_search() and
1545 * runs the function as each page is returned, using ads_process_results()
1546 * @param ads connection to ads server
1547 * @param bind_path Base dn for the search
1548 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1549 * @param expr Search expression - specified in local charset
1550 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1551 * @param fn Function which takes attr name, values list, and data_area
1552 * @param data_area Pointer which is passed to function on each call
1553 * @return status of search
1555 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1556 int scope, const char *expr, const char **attrs,
1557 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1558 void *data_area)
1560 struct berval *cookie = NULL;
1561 int count = 0;
1562 ADS_STATUS status;
1563 LDAPMessage *res;
1565 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1566 &count, &cookie);
1568 if (!ADS_ERR_OK(status)) return status;
1570 ads_process_results(ads, res, fn, data_area);
1571 ads_msgfree(ads, res);
1573 while (cookie) {
1574 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1575 &res, &count, &cookie);
1577 if (!ADS_ERR_OK(status)) break;
1579 ads_process_results(ads, res, fn, data_area);
1580 ads_msgfree(ads, res);
1583 return status;
1587 * Do a search with a timeout.
1588 * @param ads connection to ads server
1589 * @param bind_path Base dn for the search
1590 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1591 * @param expr Search expression
1592 * @param attrs Attributes to retrieve
1593 * @param res ** which will contain results - free res* with ads_msgfree()
1594 * @return status of search
1596 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1597 const char *expr,
1598 const char **attrs, LDAPMessage **res)
1600 int rc;
1601 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1602 size_t converted_size;
1603 TALLOC_CTX *ctx;
1605 *res = NULL;
1606 if (!(ctx = talloc_init("ads_do_search"))) {
1607 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1608 return ADS_ERROR(LDAP_NO_MEMORY);
1611 /* 0 means the conversion worked but the result was empty
1612 so we only fail if it's negative. In any case, it always
1613 at least nulls out the dest */
1614 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1615 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1617 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1618 rc = LDAP_NO_MEMORY;
1619 goto done;
1622 if (!attrs || !(*attrs))
1623 search_attrs = NULL;
1624 else {
1625 /* This would be the utf8-encoded version...*/
1626 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1627 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1629 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1630 rc = LDAP_NO_MEMORY;
1631 goto done;
1635 /* see the note in ads_do_paged_search - we *must* disable referrals */
1636 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1638 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1639 search_attrs, 0, NULL, NULL,
1640 LDAP_NO_LIMIT,
1641 (LDAPMessage **)res);
1643 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1644 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1645 rc = 0;
1648 done:
1649 talloc_destroy(ctx);
1650 /* if/when we decide to utf8-encode attrs, take out this next line */
1651 TALLOC_FREE(search_attrs);
1652 return ADS_ERROR(rc);
1655 * Do a general ADS search
1656 * @param ads connection to ads server
1657 * @param res ** which will contain results - free res* with ads_msgfree()
1658 * @param expr Search expression
1659 * @param attrs Attributes to retrieve
1660 * @return status of search
1662 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1663 const char *expr, const char **attrs)
1665 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1666 expr, attrs, res);
1670 * Do a search on a specific DistinguishedName
1671 * @param ads connection to ads server
1672 * @param res ** which will contain results - free res* with ads_msgfree()
1673 * @param dn DistinguishedName to search
1674 * @param attrs Attributes to retrieve
1675 * @return status of search
1677 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1678 const char *dn, const char **attrs)
1680 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1681 attrs, res);
1685 * Free up memory from a ads_search
1686 * @param ads connection to ads server
1687 * @param msg Search results to free
1689 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1691 if (!msg) return;
1692 ldap_msgfree(msg);
1696 * Get a dn from search results
1697 * @param ads connection to ads server
1698 * @param msg Search result
1699 * @return dn string
1701 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1703 char *utf8_dn, *unix_dn;
1704 size_t converted_size;
1706 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1708 if (!utf8_dn) {
1709 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1710 return NULL;
1713 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1714 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1715 utf8_dn ));
1716 return NULL;
1718 ldap_memfree(utf8_dn);
1719 return unix_dn;
1723 * Get the parent from a dn
1724 * @param dn the dn to return the parent from
1725 * @return parent dn string
1727 char *ads_parent_dn(const char *dn)
1729 char *p;
1731 if (dn == NULL) {
1732 return NULL;
1735 p = strchr(dn, ',');
1737 if (p == NULL) {
1738 return NULL;
1741 return p+1;
1745 * Find a machine account given a hostname
1746 * @param ads connection to ads server
1747 * @param res ** which will contain results - free res* with ads_msgfree()
1748 * @param host Hostname to search for
1749 * @return status of search
1751 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1752 const char *machine)
1754 ADS_STATUS status;
1755 char *expr;
1756 const char *attrs[] = {
1757 /* This is how Windows checks for machine accounts */
1758 "objectClass",
1759 "SamAccountName",
1760 "userAccountControl",
1761 "DnsHostName",
1762 "ServicePrincipalName",
1763 "userPrincipalName",
1765 /* Additional attributes Samba checks */
1766 "msDS-AdditionalDnsHostName",
1767 "msDS-SupportedEncryptionTypes",
1768 "nTSecurityDescriptor",
1769 "objectSid",
1771 NULL
1773 TALLOC_CTX *frame = talloc_stackframe();
1775 *res = NULL;
1777 /* the easiest way to find a machine account anywhere in the tree
1778 is to look for hostname$ */
1779 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1780 if (expr == NULL) {
1781 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1782 goto done;
1785 status = ads_search(ads, res, expr, attrs);
1786 if (ADS_ERR_OK(status)) {
1787 if (ads_count_replies(ads, *res) != 1) {
1788 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1792 done:
1793 TALLOC_FREE(frame);
1794 return status;
1798 * Initialize a list of mods to be used in a modify request
1799 * @param ctx An initialized TALLOC_CTX
1800 * @return allocated ADS_MODLIST
1802 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1804 #define ADS_MODLIST_ALLOC_SIZE 10
1805 LDAPMod **mods;
1807 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1808 /* -1 is safety to make sure we don't go over the end.
1809 need to reset it to NULL before doing ldap modify */
1810 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1812 return (ADS_MODLIST)mods;
1817 add an attribute to the list, with values list already constructed
1819 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1820 int mod_op, const char *name,
1821 const void *_invals)
1823 int curmod;
1824 LDAPMod **modlist = (LDAPMod **) *mods;
1825 struct berval **ber_values = NULL;
1826 char **char_values = NULL;
1828 if (!_invals) {
1829 mod_op = LDAP_MOD_DELETE;
1830 } else {
1831 if (mod_op & LDAP_MOD_BVALUES) {
1832 const struct berval **b;
1833 b = discard_const_p(const struct berval *, _invals);
1834 ber_values = ads_dup_values(ctx, b);
1835 } else {
1836 const char **c;
1837 c = discard_const_p(const char *, _invals);
1838 char_values = ads_push_strvals(ctx, c);
1842 /* find the first empty slot */
1843 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1844 curmod++);
1845 if (modlist[curmod] == (LDAPMod *) -1) {
1846 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1847 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1848 return ADS_ERROR(LDAP_NO_MEMORY);
1849 memset(&modlist[curmod], 0,
1850 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1851 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1852 *mods = (ADS_MODLIST)modlist;
1855 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1856 return ADS_ERROR(LDAP_NO_MEMORY);
1857 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1858 if (mod_op & LDAP_MOD_BVALUES) {
1859 modlist[curmod]->mod_bvalues = ber_values;
1860 } else if (mod_op & LDAP_MOD_DELETE) {
1861 modlist[curmod]->mod_values = NULL;
1862 } else {
1863 modlist[curmod]->mod_values = char_values;
1866 modlist[curmod]->mod_op = mod_op;
1867 return ADS_ERROR(LDAP_SUCCESS);
1871 * Add a single string value to a mod list
1872 * @param ctx An initialized TALLOC_CTX
1873 * @param mods An initialized ADS_MODLIST
1874 * @param name The attribute name to add
1875 * @param val The value to add - NULL means DELETE
1876 * @return ADS STATUS indicating success of add
1878 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1879 const char *name, const char *val)
1881 const char *values[2];
1883 values[0] = val;
1884 values[1] = NULL;
1886 if (!val)
1887 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1888 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1892 * Add an array of string values to a mod list
1893 * @param ctx An initialized TALLOC_CTX
1894 * @param mods An initialized ADS_MODLIST
1895 * @param name The attribute name to add
1896 * @param vals The array of string values to add - NULL means DELETE
1897 * @return ADS STATUS indicating success of add
1899 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1900 const char *name, const char **vals)
1902 if (!vals)
1903 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1904 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1905 name, (const void **) vals);
1909 * Add a single ber-encoded value to a mod list
1910 * @param ctx An initialized TALLOC_CTX
1911 * @param mods An initialized ADS_MODLIST
1912 * @param name The attribute name to add
1913 * @param val The value to add - NULL means DELETE
1914 * @return ADS STATUS indicating success of add
1916 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1917 const char *name, const struct berval *val)
1919 const struct berval *values[2];
1921 values[0] = val;
1922 values[1] = NULL;
1923 if (!val)
1924 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1925 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1926 name, (const void **) values);
1929 static void ads_print_error(int ret, LDAP *ld)
1931 if (ret != 0) {
1932 char *ld_error = NULL;
1933 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1934 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1935 ret,
1936 ldap_err2string(ret),
1937 ld_error);
1938 SAFE_FREE(ld_error);
1943 * Perform an ldap modify
1944 * @param ads connection to ads server
1945 * @param mod_dn DistinguishedName to modify
1946 * @param mods list of modifications to perform
1947 * @return status of modify
1949 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1951 int ret,i;
1952 char *utf8_dn = NULL;
1953 size_t converted_size;
1955 this control is needed to modify that contains a currently
1956 non-existent attribute (but allowable for the object) to run
1958 LDAPControl PermitModify = {
1959 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1960 {0, NULL},
1961 (char) 1};
1962 LDAPControl *controls[2];
1964 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1966 controls[0] = &PermitModify;
1967 controls[1] = NULL;
1969 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1970 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1973 /* find the end of the list, marked by NULL or -1 */
1974 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1975 /* make sure the end of the list is NULL */
1976 mods[i] = NULL;
1977 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1978 (LDAPMod **) mods, controls, NULL);
1979 ads_print_error(ret, ads->ldap.ld);
1980 TALLOC_FREE(utf8_dn);
1981 return ADS_ERROR(ret);
1985 * Perform an ldap add
1986 * @param ads connection to ads server
1987 * @param new_dn DistinguishedName to add
1988 * @param mods list of attributes and values for DN
1989 * @return status of add
1991 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1993 int ret, i;
1994 char *utf8_dn = NULL;
1995 size_t converted_size;
1997 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1999 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
2000 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2001 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2004 /* find the end of the list, marked by NULL or -1 */
2005 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2006 /* make sure the end of the list is NULL */
2007 mods[i] = NULL;
2009 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
2010 ads_print_error(ret, ads->ldap.ld);
2011 TALLOC_FREE(utf8_dn);
2012 return ADS_ERROR(ret);
2016 * Delete a DistinguishedName
2017 * @param ads connection to ads server
2018 * @param new_dn DistinguishedName to delete
2019 * @return status of delete
2021 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
2023 int ret;
2024 char *utf8_dn = NULL;
2025 size_t converted_size;
2026 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
2027 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2028 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2031 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
2033 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
2034 ads_print_error(ret, ads->ldap.ld);
2035 TALLOC_FREE(utf8_dn);
2036 return ADS_ERROR(ret);
2040 * Build an org unit string
2041 * if org unit is Computers or blank then assume a container, otherwise
2042 * assume a / separated list of organisational units.
2043 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2044 * @param ads connection to ads server
2045 * @param org_unit Organizational unit
2046 * @return org unit string - caller must free
2048 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
2050 ADS_STATUS status;
2051 char *ret = NULL;
2052 char *dn = NULL;
2054 if (!org_unit || !*org_unit) {
2056 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
2058 /* samba4 might not yet respond to a wellknownobject-query */
2059 return ret ? ret : SMB_STRDUP("cn=Computers");
2062 if (strequal(org_unit, "Computers")) {
2063 return SMB_STRDUP("cn=Computers");
2066 /* jmcd: removed "\\" from the separation chars, because it is
2067 needed as an escape for chars like '#' which are valid in an
2068 OU name */
2069 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
2070 if (!ADS_ERR_OK(status)) {
2071 return NULL;
2074 return dn;
2078 * Get a org unit string for a well-known GUID
2079 * @param ads connection to ads server
2080 * @param wknguid Well known GUID
2081 * @return org unit string - caller must free
2083 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2085 ADS_STATUS status;
2086 LDAPMessage *res = NULL;
2087 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2088 **bind_dn_exp = NULL;
2089 const char *attrs[] = {"distinguishedName", NULL};
2090 int new_ln, wkn_ln, bind_ln, i;
2092 if (wknguid == NULL) {
2093 return NULL;
2096 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2097 DEBUG(1, ("asprintf failed!\n"));
2098 return NULL;
2101 status = ads_search_dn(ads, &res, base, attrs);
2102 if (!ADS_ERR_OK(status)) {
2103 DEBUG(1,("Failed while searching for: %s\n", base));
2104 goto out;
2107 if (ads_count_replies(ads, res) != 1) {
2108 goto out;
2111 /* substitute the bind-path from the well-known-guid-search result */
2112 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2113 if (!wkn_dn) {
2114 goto out;
2117 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2118 if (!wkn_dn_exp) {
2119 goto out;
2122 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2123 if (!bind_dn_exp) {
2124 goto out;
2127 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2129 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2132 new_ln = wkn_ln - bind_ln;
2134 ret = SMB_STRDUP(wkn_dn_exp[0]);
2135 if (!ret) {
2136 goto out;
2139 for (i=1; i < new_ln; i++) {
2140 char *s = NULL;
2142 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2143 SAFE_FREE(ret);
2144 goto out;
2147 SAFE_FREE(ret);
2148 ret = SMB_STRDUP(s);
2149 free(s);
2150 if (!ret) {
2151 goto out;
2155 out:
2156 SAFE_FREE(base);
2157 ads_msgfree(ads, res);
2158 TALLOC_FREE(wkn_dn);
2159 if (wkn_dn_exp) {
2160 ldap_value_free(wkn_dn_exp);
2162 if (bind_dn_exp) {
2163 ldap_value_free(bind_dn_exp);
2166 return ret;
2170 * Adds (appends) an item to an attribute array, rather then
2171 * replacing the whole list
2172 * @param ctx An initialized TALLOC_CTX
2173 * @param mods An initialized ADS_MODLIST
2174 * @param name name of the ldap attribute to append to
2175 * @param vals an array of values to add
2176 * @return status of addition
2179 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2180 const char *name, const char **vals)
2182 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2183 (const void *) vals);
2187 * Determines the an account's current KVNO via an LDAP lookup
2188 * @param ads An initialized ADS_STRUCT
2189 * @param account_name the NT samaccountname.
2190 * @return the kvno for the account, or -1 in case of a failure.
2193 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
2195 LDAPMessage *res = NULL;
2196 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
2197 char *filter;
2198 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
2199 char *dn_string = NULL;
2200 ADS_STATUS ret;
2202 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
2203 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
2204 return kvno;
2206 ret = ads_search(ads, &res, filter, attrs);
2207 SAFE_FREE(filter);
2208 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
2209 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
2210 ads_msgfree(ads, res);
2211 return kvno;
2214 dn_string = ads_get_dn(ads, talloc_tos(), res);
2215 if (!dn_string) {
2216 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2217 ads_msgfree(ads, res);
2218 return kvno;
2220 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
2221 TALLOC_FREE(dn_string);
2223 /* ---------------------------------------------------------
2224 * 0 is returned as a default KVNO from this point on...
2225 * This is done because Windows 2000 does not support key
2226 * version numbers. Chances are that a failure in the next
2227 * step is simply due to Windows 2000 being used for a
2228 * domain controller. */
2229 kvno = 0;
2231 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2232 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2233 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2234 ads_msgfree(ads, res);
2235 return kvno;
2238 /* Success */
2239 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2240 ads_msgfree(ads, res);
2241 return kvno;
2245 * Determines the computer account's current KVNO via an LDAP lookup
2246 * @param ads An initialized ADS_STRUCT
2247 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2248 * @return the kvno for the computer account, or -1 in case of a failure.
2251 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2253 char *computer_account = NULL;
2254 uint32_t kvno = -1;
2256 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2257 return kvno;
2260 kvno = ads_get_kvno(ads, computer_account);
2261 free(computer_account);
2263 return kvno;
2267 * This clears out all registered spn's for a given hostname
2268 * @param ads An initialized ADS_STRUCT
2269 * @param machine_name the NetBIOS name of the computer.
2270 * @return 0 upon success, non-zero otherwise.
2273 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2275 TALLOC_CTX *ctx;
2276 LDAPMessage *res = NULL;
2277 ADS_MODLIST mods;
2278 const char *servicePrincipalName[1] = {NULL};
2279 ADS_STATUS ret;
2280 char *dn_string = NULL;
2282 ret = ads_find_machine_acct(ads, &res, machine_name);
2283 if (!ADS_ERR_OK(ret)) {
2284 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2285 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2286 ads_msgfree(ads, res);
2287 return ret;
2290 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2291 ctx = talloc_init("ads_clear_service_principal_names");
2292 if (!ctx) {
2293 ads_msgfree(ads, res);
2294 return ADS_ERROR(LDAP_NO_MEMORY);
2297 if (!(mods = ads_init_mods(ctx))) {
2298 talloc_destroy(ctx);
2299 ads_msgfree(ads, res);
2300 return ADS_ERROR(LDAP_NO_MEMORY);
2302 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2303 if (!ADS_ERR_OK(ret)) {
2304 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2305 ads_msgfree(ads, res);
2306 talloc_destroy(ctx);
2307 return ret;
2309 dn_string = ads_get_dn(ads, talloc_tos(), res);
2310 if (!dn_string) {
2311 talloc_destroy(ctx);
2312 ads_msgfree(ads, res);
2313 return ADS_ERROR(LDAP_NO_MEMORY);
2315 ret = ads_gen_mod(ads, dn_string, mods);
2316 TALLOC_FREE(dn_string);
2317 if (!ADS_ERR_OK(ret)) {
2318 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2319 machine_name));
2320 ads_msgfree(ads, res);
2321 talloc_destroy(ctx);
2322 return ret;
2325 ads_msgfree(ads, res);
2326 talloc_destroy(ctx);
2327 return ret;
2331 * @brief Search for an element in a string array.
2333 * @param[in] el_array The string array to search.
2335 * @param[in] num_el The number of elements in the string array.
2337 * @param[in] el The string to search.
2339 * @return True if found, false if not.
2341 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2343 size_t i;
2345 if (el_array == NULL || num_el == 0 || el == NULL) {
2346 return false;
2349 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2350 int cmp;
2352 cmp = strcasecmp_m(el_array[i], el);
2353 if (cmp == 0) {
2354 return true;
2358 return false;
2362 * @brief This gets the service principal names of an existing computer account.
2364 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2366 * @param[in] ads The ADS context to use.
2368 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2369 * identify the computer account.
2371 * @param[in] spn_array A pointer to store the array for SPNs.
2373 * @param[in] num_spns The number of principals stored in the array.
2375 * @return 0 on success, or a ADS error if a failure occurred.
2377 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2378 ADS_STRUCT *ads,
2379 const char *machine_name,
2380 char ***spn_array,
2381 size_t *num_spns)
2383 ADS_STATUS status;
2384 LDAPMessage *res = NULL;
2385 int count;
2387 status = ads_find_machine_acct(ads,
2388 &res,
2389 machine_name);
2390 if (!ADS_ERR_OK(status)) {
2391 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2392 machine_name));
2393 return status;
2396 count = ads_count_replies(ads, res);
2397 if (count != 1) {
2398 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2399 goto done;
2402 *spn_array = ads_pull_strings(ads,
2403 mem_ctx,
2404 res,
2405 "servicePrincipalName",
2406 num_spns);
2407 if (*spn_array == NULL) {
2408 DEBUG(1, ("Host account for %s does not have service principal "
2409 "names.\n",
2410 machine_name));
2411 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2412 goto done;
2415 done:
2416 ads_msgfree(ads, res);
2418 return status;
2422 * This adds a service principal name to an existing computer account
2423 * (found by hostname) in AD.
2424 * @param ads An initialized ADS_STRUCT
2425 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2426 * @param spns An array or strings for the service principals to add,
2427 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2428 * @return 0 upon success, or non-zero if a failure occurs
2431 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2432 const char *machine_name,
2433 const char **spns)
2435 ADS_STATUS ret;
2436 TALLOC_CTX *ctx;
2437 LDAPMessage *res = NULL;
2438 ADS_MODLIST mods;
2439 char *dn_string = NULL;
2440 const char **servicePrincipalName = spns;
2442 ret = ads_find_machine_acct(ads, &res, machine_name);
2443 if (!ADS_ERR_OK(ret)) {
2444 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2445 machine_name));
2446 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2447 ads_msgfree(ads, res);
2448 return ret;
2451 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2452 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2453 ads_msgfree(ads, res);
2454 return ADS_ERROR(LDAP_NO_MEMORY);
2457 DEBUG(5,("ads_add_service_principal_name: INFO: "
2458 "Adding %s to host %s\n",
2459 spns[0] ? "N/A" : spns[0], machine_name));
2462 DEBUG(5,("ads_add_service_principal_name: INFO: "
2463 "Adding %s to host %s\n",
2464 spns[1] ? "N/A" : spns[1], machine_name));
2466 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2467 ret = ADS_ERROR(LDAP_NO_MEMORY);
2468 goto out;
2471 ret = ads_add_strlist(ctx,
2472 &mods,
2473 "servicePrincipalName",
2474 servicePrincipalName);
2475 if (!ADS_ERR_OK(ret)) {
2476 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2477 goto out;
2480 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2481 ret = ADS_ERROR(LDAP_NO_MEMORY);
2482 goto out;
2485 ret = ads_gen_mod(ads, dn_string, mods);
2486 if (!ADS_ERR_OK(ret)) {
2487 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2488 goto out;
2491 out:
2492 TALLOC_FREE( ctx );
2493 ads_msgfree(ads, res);
2494 return ret;
2497 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2498 LDAPMessage *msg)
2500 uint32_t acct_ctrl = 0;
2501 bool ok;
2503 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2504 if (!ok) {
2505 return 0;
2508 return acct_ctrl;
2511 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2512 LDAPMessage *msg,
2513 const struct berval *machine_pw_val)
2515 ADS_MODLIST mods;
2516 ADS_STATUS ret;
2517 TALLOC_CTX *frame = talloc_stackframe();
2518 uint32_t acct_control;
2519 char *control_str = NULL;
2520 const char *attrs[] = {
2521 "objectSid",
2522 NULL
2524 LDAPMessage *res = NULL;
2525 char *dn = NULL;
2527 dn = ads_get_dn(ads, frame, msg);
2528 if (dn == NULL) {
2529 ret = ADS_ERROR(LDAP_NO_MEMORY);
2530 goto done;
2533 acct_control = ads_get_acct_ctrl(ads, msg);
2534 if (acct_control == 0) {
2535 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2536 goto done;
2540 * Changing the password, disables the account. So we need to change the
2541 * userAccountControl flags to enable it again.
2543 mods = ads_init_mods(frame);
2544 if (mods == NULL) {
2545 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2546 goto done;
2549 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2551 ret = ads_gen_mod(ads, dn, mods);
2552 if (!ADS_ERR_OK(ret)) {
2553 goto done;
2555 TALLOC_FREE(mods);
2558 * To activate the account, we need to disable and enable it.
2560 acct_control |= UF_ACCOUNTDISABLE;
2562 control_str = talloc_asprintf(frame, "%u", acct_control);
2563 if (control_str == NULL) {
2564 ret = ADS_ERROR(LDAP_NO_MEMORY);
2565 goto done;
2568 mods = ads_init_mods(frame);
2569 if (mods == NULL) {
2570 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2571 goto done;
2574 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2576 ret = ads_gen_mod(ads, dn, mods);
2577 if (!ADS_ERR_OK(ret)) {
2578 goto done;
2580 TALLOC_FREE(mods);
2581 TALLOC_FREE(control_str);
2584 * Enable the account again.
2586 acct_control &= ~UF_ACCOUNTDISABLE;
2588 control_str = talloc_asprintf(frame, "%u", acct_control);
2589 if (control_str == NULL) {
2590 ret = ADS_ERROR(LDAP_NO_MEMORY);
2591 goto done;
2594 mods = ads_init_mods(frame);
2595 if (mods == NULL) {
2596 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2597 goto done;
2600 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2602 ret = ads_gen_mod(ads, dn, mods);
2603 if (!ADS_ERR_OK(ret)) {
2604 goto done;
2606 TALLOC_FREE(mods);
2607 TALLOC_FREE(control_str);
2609 ret = ads_search_dn(ads, &res, dn, attrs);
2610 ads_msgfree(ads, res);
2612 done:
2613 talloc_free(frame);
2615 return ret;
2619 * adds a machine account to the ADS server
2620 * @param ads An initialized ADS_STRUCT
2621 * @param machine_name - the NetBIOS machine name of this account.
2622 * @param account_type A number indicating the type of account to create
2623 * @param org_unit The LDAP path in which to place this account
2624 * @return 0 upon success, or non-zero otherwise
2627 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2628 const char *machine_name,
2629 const char *machine_password,
2630 const char *org_unit,
2631 uint32_t etype_list,
2632 const char *dns_domain_name)
2634 ADS_STATUS ret;
2635 char *samAccountName = NULL;
2636 char *controlstr = NULL;
2637 TALLOC_CTX *ctx = NULL;
2638 ADS_MODLIST mods;
2639 char *machine_escaped = NULL;
2640 char *dns_hostname = NULL;
2641 char *new_dn = NULL;
2642 char *utf8_pw = NULL;
2643 size_t utf8_pw_len = 0;
2644 char *utf16_pw = NULL;
2645 size_t utf16_pw_len = 0;
2646 struct berval machine_pw_val;
2647 bool ok;
2648 const char **spn_array = NULL;
2649 size_t num_spns = 0;
2650 const char *spn_prefix[] = {
2651 "HOST",
2652 "RestrictedKrbHost",
2654 size_t i;
2655 LDAPMessage *res = NULL;
2656 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2658 ctx = talloc_init("ads_add_machine_acct");
2659 if (ctx == NULL) {
2660 return ADS_ERROR(LDAP_NO_MEMORY);
2663 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2664 if (machine_escaped == NULL) {
2665 ret = ADS_ERROR(LDAP_NO_MEMORY);
2666 goto done;
2669 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2670 if (utf8_pw == NULL) {
2671 ret = ADS_ERROR(LDAP_NO_MEMORY);
2672 goto done;
2674 utf8_pw_len = strlen(utf8_pw);
2676 ok = convert_string_talloc(ctx,
2677 CH_UTF8, CH_UTF16MUNGED,
2678 utf8_pw, utf8_pw_len,
2679 (void *)&utf16_pw, &utf16_pw_len);
2680 if (!ok) {
2681 ret = ADS_ERROR(LDAP_NO_MEMORY);
2682 goto done;
2685 machine_pw_val = (struct berval) {
2686 .bv_val = utf16_pw,
2687 .bv_len = utf16_pw_len,
2690 /* Check if the machine account already exists. */
2691 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2692 if (ADS_ERR_OK(ret)) {
2693 /* Change the machine account password */
2694 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2695 ads_msgfree(ads, res);
2697 goto done;
2699 ads_msgfree(ads, res);
2701 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2702 if (new_dn == NULL) {
2703 ret = ADS_ERROR(LDAP_NO_MEMORY);
2704 goto done;
2707 /* Create machine account */
2709 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2710 if (samAccountName == NULL) {
2711 ret = ADS_ERROR(LDAP_NO_MEMORY);
2712 goto done;
2715 dns_hostname = talloc_asprintf(ctx,
2716 "%s.%s",
2717 machine_name,
2718 dns_domain_name);
2719 if (dns_hostname == NULL) {
2720 ret = ADS_ERROR(LDAP_NO_MEMORY);
2721 goto done;
2724 /* Add dns_hostname SPNs */
2725 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2726 char *spn = talloc_asprintf(ctx,
2727 "%s/%s",
2728 spn_prefix[i],
2729 dns_hostname);
2730 if (spn == NULL) {
2731 ret = ADS_ERROR(LDAP_NO_MEMORY);
2732 goto done;
2735 ok = add_string_to_array(ctx,
2736 spn,
2737 &spn_array,
2738 &num_spns);
2739 if (!ok) {
2740 ret = ADS_ERROR(LDAP_NO_MEMORY);
2741 goto done;
2745 /* Add machine_name SPNs */
2746 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2747 char *spn = talloc_asprintf(ctx,
2748 "%s/%s",
2749 spn_prefix[i],
2750 machine_name);
2751 if (spn == NULL) {
2752 ret = ADS_ERROR(LDAP_NO_MEMORY);
2753 goto done;
2756 ok = add_string_to_array(ctx,
2757 spn,
2758 &spn_array,
2759 &num_spns);
2760 if (!ok) {
2761 ret = ADS_ERROR(LDAP_NO_MEMORY);
2762 goto done;
2766 /* Make sure to NULL terminate the array */
2767 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2768 if (spn_array == NULL) {
2769 ret = ADS_ERROR(LDAP_NO_MEMORY);
2770 goto done;
2772 spn_array[num_spns] = NULL;
2774 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2775 if (controlstr == NULL) {
2776 ret = ADS_ERROR(LDAP_NO_MEMORY);
2777 goto done;
2780 mods = ads_init_mods(ctx);
2781 if (mods == NULL) {
2782 ret = ADS_ERROR(LDAP_NO_MEMORY);
2783 goto done;
2786 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2787 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2788 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2789 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2790 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2791 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2793 ret = ads_gen_add(ads, new_dn, mods);
2795 done:
2796 SAFE_FREE(machine_escaped);
2797 talloc_destroy(ctx);
2799 return ret;
2803 * move a machine account to another OU on the ADS server
2804 * @param ads - An initialized ADS_STRUCT
2805 * @param machine_name - the NetBIOS machine name of this account.
2806 * @param org_unit - The LDAP path in which to place this account
2807 * @param moved - whether we moved the machine account (optional)
2808 * @return 0 upon success, or non-zero otherwise
2811 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2812 const char *org_unit, bool *moved)
2814 ADS_STATUS rc;
2815 int ldap_status;
2816 LDAPMessage *res = NULL;
2817 char *filter = NULL;
2818 char *computer_dn = NULL;
2819 char *parent_dn;
2820 char *computer_rdn = NULL;
2821 bool need_move = False;
2823 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2824 rc = ADS_ERROR(LDAP_NO_MEMORY);
2825 goto done;
2828 /* Find pre-existing machine */
2829 rc = ads_search(ads, &res, filter, NULL);
2830 if (!ADS_ERR_OK(rc)) {
2831 goto done;
2834 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2835 if (!computer_dn) {
2836 rc = ADS_ERROR(LDAP_NO_MEMORY);
2837 goto done;
2840 parent_dn = ads_parent_dn(computer_dn);
2841 if (strequal(parent_dn, org_unit)) {
2842 goto done;
2845 need_move = True;
2847 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2848 rc = ADS_ERROR(LDAP_NO_MEMORY);
2849 goto done;
2852 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2853 org_unit, 1, NULL, NULL);
2854 rc = ADS_ERROR(ldap_status);
2856 done:
2857 ads_msgfree(ads, res);
2858 SAFE_FREE(filter);
2859 TALLOC_FREE(computer_dn);
2860 SAFE_FREE(computer_rdn);
2862 if (!ADS_ERR_OK(rc)) {
2863 need_move = False;
2866 if (moved) {
2867 *moved = need_move;
2870 return rc;
2874 dump a binary result from ldap
2876 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2878 size_t i;
2879 for (i=0; values[i]; i++) {
2880 ber_len_t j;
2881 printf("%s: ", field);
2882 for (j=0; j<values[i]->bv_len; j++) {
2883 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2885 printf("\n");
2889 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2891 int i;
2892 for (i=0; values[i]; i++) {
2893 NTSTATUS status;
2894 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2895 struct GUID guid;
2897 status = GUID_from_ndr_blob(&in, &guid);
2898 if (NT_STATUS_IS_OK(status)) {
2899 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2900 } else {
2901 printf("%s: INVALID GUID\n", field);
2907 dump a sid result from ldap
2909 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2911 int i;
2912 for (i=0; values[i]; i++) {
2913 ssize_t ret;
2914 struct dom_sid sid;
2915 struct dom_sid_buf tmp;
2916 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2917 values[i]->bv_len, &sid);
2918 if (ret == -1) {
2919 return;
2921 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2926 dump ntSecurityDescriptor
2928 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2930 TALLOC_CTX *frame = talloc_stackframe();
2931 struct security_descriptor *psd;
2932 NTSTATUS status;
2934 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2935 values[0]->bv_len, &psd);
2936 if (!NT_STATUS_IS_OK(status)) {
2937 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2938 nt_errstr(status)));
2939 TALLOC_FREE(frame);
2940 return;
2943 if (psd) {
2944 ads_disp_sd(ads, talloc_tos(), psd);
2947 TALLOC_FREE(frame);
2951 dump a string result from ldap
2953 static void dump_string(const char *field, char **values)
2955 int i;
2956 for (i=0; values[i]; i++) {
2957 printf("%s: %s\n", field, values[i]);
2962 dump a field from LDAP on stdout
2963 used for debugging
2966 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2968 const struct {
2969 const char *name;
2970 bool string;
2971 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2972 } handlers[] = {
2973 {"objectGUID", False, dump_guid},
2974 {"netbootGUID", False, dump_guid},
2975 {"nTSecurityDescriptor", False, dump_sd},
2976 {"dnsRecord", False, dump_binary},
2977 {"objectSid", False, dump_sid},
2978 {"securityIdentifier", False, dump_sid},
2979 {"tokenGroups", False, dump_sid},
2980 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2981 {"tokengroupsGlobalandUniversal", False, dump_sid},
2982 {"mS-DS-CreatorSID", False, dump_sid},
2983 {"msExchMailboxGuid", False, dump_guid},
2984 {"msDS-TrustForestTrustInfo", False, dump_binary},
2985 {NULL, True, NULL}
2987 int i;
2989 if (!field) { /* must be end of an entry */
2990 printf("\n");
2991 return False;
2994 for (i=0; handlers[i].name; i++) {
2995 if (strcasecmp_m(handlers[i].name, field) == 0) {
2996 if (!values) /* first time, indicate string or not */
2997 return handlers[i].string;
2998 handlers[i].handler(ads, field, (struct berval **) values);
2999 break;
3002 if (!handlers[i].name) {
3003 if (!values) /* first time, indicate string conversion */
3004 return True;
3005 dump_string(field, (char **)values);
3007 return False;
3011 * Dump a result from LDAP on stdout
3012 * used for debugging
3013 * @param ads connection to ads server
3014 * @param res Results to dump
3017 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
3019 ads_process_results(ads, res, ads_dump_field, NULL);
3023 * Walk through results, calling a function for each entry found.
3024 * The function receives a field name, a berval * array of values,
3025 * and a data area passed through from the start. The function is
3026 * called once with null for field and values at the end of each
3027 * entry.
3028 * @param ads connection to ads server
3029 * @param res Results to process
3030 * @param fn Function for processing each result
3031 * @param data_area user-defined area to pass to function
3033 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
3034 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
3035 void *data_area)
3037 LDAPMessage *msg;
3038 TALLOC_CTX *ctx;
3039 size_t converted_size;
3041 if (!(ctx = talloc_init("ads_process_results")))
3042 return;
3044 for (msg = ads_first_entry(ads, res); msg;
3045 msg = ads_next_entry(ads, msg)) {
3046 char *utf8_field;
3047 BerElement *b;
3049 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
3050 (LDAPMessage *)msg,&b);
3051 utf8_field;
3052 utf8_field=ldap_next_attribute(ads->ldap.ld,
3053 (LDAPMessage *)msg,b)) {
3054 struct berval **ber_vals;
3055 char **str_vals;
3056 char **utf8_vals;
3057 char *field;
3058 bool string;
3060 if (!pull_utf8_talloc(ctx, &field, utf8_field,
3061 &converted_size))
3063 DEBUG(0,("ads_process_results: "
3064 "pull_utf8_talloc failed: %s\n",
3065 strerror(errno)));
3068 string = fn(ads, field, NULL, data_area);
3070 if (string) {
3071 const char **p;
3073 utf8_vals = ldap_get_values(ads->ldap.ld,
3074 (LDAPMessage *)msg, field);
3075 p = discard_const_p(const char *, utf8_vals);
3076 str_vals = ads_pull_strvals(ctx, p);
3077 fn(ads, field, (void **) str_vals, data_area);
3078 ldap_value_free(utf8_vals);
3079 } else {
3080 ber_vals = ldap_get_values_len(ads->ldap.ld,
3081 (LDAPMessage *)msg, field);
3082 fn(ads, field, (void **) ber_vals, data_area);
3084 ldap_value_free_len(ber_vals);
3086 ldap_memfree(utf8_field);
3088 ber_free(b, 0);
3089 talloc_free_children(ctx);
3090 fn(ads, NULL, NULL, data_area); /* completed an entry */
3093 talloc_destroy(ctx);
3097 * count how many replies are in a LDAPMessage
3098 * @param ads connection to ads server
3099 * @param res Results to count
3100 * @return number of replies
3102 int ads_count_replies(ADS_STRUCT *ads, void *res)
3104 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3108 * pull the first entry from a ADS result
3109 * @param ads connection to ads server
3110 * @param res Results of search
3111 * @return first entry from result
3113 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3115 return ldap_first_entry(ads->ldap.ld, res);
3119 * pull the next entry from a ADS result
3120 * @param ads connection to ads server
3121 * @param res Results of search
3122 * @return next entry from result
3124 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3126 return ldap_next_entry(ads->ldap.ld, res);
3130 * pull the first message from a ADS result
3131 * @param ads connection to ads server
3132 * @param res Results of search
3133 * @return first message from result
3135 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3137 return ldap_first_message(ads->ldap.ld, res);
3141 * pull the next message from a ADS result
3142 * @param ads connection to ads server
3143 * @param res Results of search
3144 * @return next message from result
3146 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3148 return ldap_next_message(ads->ldap.ld, res);
3152 * pull a single string from a ADS result
3153 * @param ads connection to ads server
3154 * @param mem_ctx TALLOC_CTX to use for allocating result string
3155 * @param msg Results of search
3156 * @param field Attribute to retrieve
3157 * @return Result string in talloc context
3159 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3160 const char *field)
3162 char **values;
3163 char *ret = NULL;
3164 char *ux_string;
3165 size_t converted_size;
3167 values = ldap_get_values(ads->ldap.ld, msg, field);
3168 if (!values)
3169 return NULL;
3171 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3172 &converted_size))
3174 ret = ux_string;
3176 ldap_value_free(values);
3177 return ret;
3181 * pull an array of strings from a ADS result
3182 * @param ads connection to ads server
3183 * @param mem_ctx TALLOC_CTX to use for allocating result string
3184 * @param msg Results of search
3185 * @param field Attribute to retrieve
3186 * @return Result strings in talloc context
3188 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3189 LDAPMessage *msg, const char *field,
3190 size_t *num_values)
3192 char **values;
3193 char **ret = NULL;
3194 size_t i, converted_size;
3196 values = ldap_get_values(ads->ldap.ld, msg, field);
3197 if (!values)
3198 return NULL;
3200 *num_values = ldap_count_values(values);
3202 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3203 if (!ret) {
3204 ldap_value_free(values);
3205 return NULL;
3208 for (i=0;i<*num_values;i++) {
3209 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3210 &converted_size))
3212 ldap_value_free(values);
3213 return NULL;
3216 ret[i] = NULL;
3218 ldap_value_free(values);
3219 return ret;
3223 * pull an array of strings from a ADS result
3224 * (handle large multivalue attributes with range retrieval)
3225 * @param ads connection to ads server
3226 * @param mem_ctx TALLOC_CTX to use for allocating result string
3227 * @param msg Results of search
3228 * @param field Attribute to retrieve
3229 * @param current_strings strings returned by a previous call to this function
3230 * @param next_attribute The next query should ask for this attribute
3231 * @param num_values How many values did we get this time?
3232 * @param more_values Are there more values to get?
3233 * @return Result strings in talloc context
3235 char **ads_pull_strings_range(ADS_STRUCT *ads,
3236 TALLOC_CTX *mem_ctx,
3237 LDAPMessage *msg, const char *field,
3238 char **current_strings,
3239 const char **next_attribute,
3240 size_t *num_strings,
3241 bool *more_strings)
3243 char *attr;
3244 char *expected_range_attrib, *range_attr = NULL;
3245 BerElement *ptr = NULL;
3246 char **strings;
3247 char **new_strings;
3248 size_t num_new_strings;
3249 unsigned long int range_start;
3250 unsigned long int range_end;
3252 /* we might have been given the whole lot anyway */
3253 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3254 *more_strings = False;
3255 return strings;
3258 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3260 /* look for Range result */
3261 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3262 attr;
3263 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3264 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3265 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3266 range_attr = attr;
3267 break;
3269 ldap_memfree(attr);
3271 if (!range_attr) {
3272 ber_free(ptr, 0);
3273 /* nothing here - this field is just empty */
3274 *more_strings = False;
3275 return NULL;
3278 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3279 &range_start, &range_end) == 2) {
3280 *more_strings = True;
3281 } else {
3282 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3283 &range_start) == 1) {
3284 *more_strings = False;
3285 } else {
3286 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3287 range_attr));
3288 ldap_memfree(range_attr);
3289 *more_strings = False;
3290 return NULL;
3294 if ((*num_strings) != range_start) {
3295 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3296 " - aborting range retrieval\n",
3297 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3298 ldap_memfree(range_attr);
3299 *more_strings = False;
3300 return NULL;
3303 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3305 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3306 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3307 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3308 range_attr, (unsigned long int)range_end - range_start + 1,
3309 (unsigned long int)num_new_strings));
3310 ldap_memfree(range_attr);
3311 *more_strings = False;
3312 return NULL;
3315 strings = talloc_realloc(mem_ctx, current_strings, char *,
3316 *num_strings + num_new_strings);
3318 if (strings == NULL) {
3319 ldap_memfree(range_attr);
3320 *more_strings = False;
3321 return NULL;
3324 if (new_strings && num_new_strings) {
3325 memcpy(&strings[*num_strings], new_strings,
3326 sizeof(*new_strings) * num_new_strings);
3329 (*num_strings) += num_new_strings;
3331 if (*more_strings) {
3332 *next_attribute = talloc_asprintf(mem_ctx,
3333 "%s;range=%d-*",
3334 field,
3335 (int)*num_strings);
3337 if (!*next_attribute) {
3338 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3339 ldap_memfree(range_attr);
3340 *more_strings = False;
3341 return NULL;
3345 ldap_memfree(range_attr);
3347 return strings;
3351 * pull a single uint32_t from a ADS result
3352 * @param ads connection to ads server
3353 * @param msg Results of search
3354 * @param field Attribute to retrieve
3355 * @param v Pointer to int to store result
3356 * @return boolean indicating success
3358 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3359 uint32_t *v)
3361 char **values;
3363 values = ldap_get_values(ads->ldap.ld, msg, field);
3364 if (!values)
3365 return False;
3366 if (!values[0]) {
3367 ldap_value_free(values);
3368 return False;
3371 *v = atoi(values[0]);
3372 ldap_value_free(values);
3373 return True;
3377 * pull a single objectGUID from an ADS result
3378 * @param ads connection to ADS server
3379 * @param msg results of search
3380 * @param guid 37-byte area to receive text guid
3381 * @return boolean indicating success
3383 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3385 DATA_BLOB blob;
3386 NTSTATUS status;
3388 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3389 &blob)) {
3390 return false;
3393 status = GUID_from_ndr_blob(&blob, guid);
3394 talloc_free(blob.data);
3395 return NT_STATUS_IS_OK(status);
3400 * pull a single struct dom_sid from a ADS result
3401 * @param ads connection to ads server
3402 * @param msg Results of search
3403 * @param field Attribute to retrieve
3404 * @param sid Pointer to sid to store result
3405 * @return boolean indicating success
3407 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3408 struct dom_sid *sid)
3410 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3414 * pull an array of struct dom_sids from a ADS result
3415 * @param ads connection to ads server
3416 * @param mem_ctx TALLOC_CTX for allocating sid array
3417 * @param msg Results of search
3418 * @param field Attribute to retrieve
3419 * @param sids pointer to sid array to allocate
3420 * @return the count of SIDs pulled
3422 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3423 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3425 struct berval **values;
3426 int count, i;
3428 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3430 if (!values)
3431 return 0;
3433 for (i=0; values[i]; i++)
3434 /* nop */ ;
3436 if (i) {
3437 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3438 if (!(*sids)) {
3439 ldap_value_free_len(values);
3440 return 0;
3442 } else {
3443 (*sids) = NULL;
3446 count = 0;
3447 for (i=0; values[i]; i++) {
3448 ssize_t ret;
3449 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3450 values[i]->bv_len, &(*sids)[count]);
3451 if (ret != -1) {
3452 struct dom_sid_buf buf;
3453 DBG_DEBUG("pulling SID: %s\n",
3454 dom_sid_str_buf(&(*sids)[count], &buf));
3455 count++;
3459 ldap_value_free_len(values);
3460 return count;
3464 * pull a struct security_descriptor from a ADS result
3465 * @param ads connection to ads server
3466 * @param mem_ctx TALLOC_CTX for allocating sid array
3467 * @param msg Results of search
3468 * @param field Attribute to retrieve
3469 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3470 * @return boolean indicating success
3472 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3473 LDAPMessage *msg, const char *field,
3474 struct security_descriptor **sd)
3476 struct berval **values;
3477 bool ret = true;
3479 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3481 if (!values) return false;
3483 if (values[0]) {
3484 NTSTATUS status;
3485 status = unmarshall_sec_desc(mem_ctx,
3486 (uint8_t *)values[0]->bv_val,
3487 values[0]->bv_len, sd);
3488 if (!NT_STATUS_IS_OK(status)) {
3489 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3490 nt_errstr(status)));
3491 ret = false;
3495 ldap_value_free_len(values);
3496 return ret;
3500 * in order to support usernames longer than 21 characters we need to
3501 * use both the sAMAccountName and the userPrincipalName attributes
3502 * It seems that not all users have the userPrincipalName attribute set
3504 * @param ads connection to ads server
3505 * @param mem_ctx TALLOC_CTX for allocating sid array
3506 * @param msg Results of search
3507 * @return the username
3509 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3510 LDAPMessage *msg)
3512 #if 0 /* JERRY */
3513 char *ret, *p;
3515 /* lookup_name() only works on the sAMAccountName to
3516 returning the username portion of userPrincipalName
3517 breaks winbindd_getpwnam() */
3519 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3520 if (ret && (p = strchr_m(ret, '@'))) {
3521 *p = 0;
3522 return ret;
3524 #endif
3525 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3530 * find the update serial number - this is the core of the ldap cache
3531 * @param ads connection to ads server
3532 * @param ads connection to ADS server
3533 * @param usn Pointer to retrieved update serial number
3534 * @return status of search
3536 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3538 const char *attrs[] = {"highestCommittedUSN", NULL};
3539 ADS_STATUS status;
3540 LDAPMessage *res;
3542 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3543 if (!ADS_ERR_OK(status))
3544 return status;
3546 if (ads_count_replies(ads, res) != 1) {
3547 ads_msgfree(ads, res);
3548 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3551 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3552 ads_msgfree(ads, res);
3553 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3556 ads_msgfree(ads, res);
3557 return ADS_SUCCESS;
3560 /* parse a ADS timestring - typical string is
3561 '20020917091222.0Z0' which means 09:12.22 17th September
3562 2002, timezone 0 */
3563 static time_t ads_parse_time(const char *str)
3565 struct tm tm;
3567 ZERO_STRUCT(tm);
3569 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3570 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3571 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3572 return 0;
3574 tm.tm_year -= 1900;
3575 tm.tm_mon -= 1;
3577 return timegm(&tm);
3580 /********************************************************************
3581 ********************************************************************/
3583 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3585 const char *attrs[] = {"currentTime", NULL};
3586 ADS_STATUS status;
3587 LDAPMessage *res;
3588 char *timestr;
3589 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3590 ADS_STRUCT *ads_s = ads;
3592 /* establish a new ldap tcp session if necessary */
3594 if ( !ads->ldap.ld ) {
3596 * ADS_STRUCT may be being reused after a
3597 * DC lookup, so ads->ldap.ss may already have a
3598 * good address. If not, re-initialize the passed-in
3599 * ADS_STRUCT with the given server.XXXX parameters.
3601 * Note that this doesn't depend on
3602 * ads->server.ldap_server != NULL,
3603 * as the case where ads->server.ldap_server==NULL and
3604 * ads->ldap.ss != zero_address is precisely the DC
3605 * lookup case where ads->ldap.ss was found by going
3606 * through ads_find_dc() again we want to avoid repeating.
3608 if (is_zero_addr(&ads->ldap.ss)) {
3609 ads_s = ads_init(tmp_ctx,
3610 ads->server.realm,
3611 ads->server.workgroup,
3612 ads->server.ldap_server,
3613 ADS_SASL_PLAIN );
3614 if (ads_s == NULL) {
3615 status = ADS_ERROR(LDAP_NO_MEMORY);
3616 goto done;
3621 * Reset ads->config.flags as it can contain the flags
3622 * returned by the previous CLDAP ping when reusing the struct.
3624 ads_s->config.flags = 0;
3626 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3627 status = ads_connect( ads_s );
3628 if ( !ADS_ERR_OK(status))
3629 goto done;
3632 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3633 if (!ADS_ERR_OK(status)) {
3634 goto done;
3637 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3638 if (!timestr) {
3639 ads_msgfree(ads_s, res);
3640 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3641 goto done;
3644 /* but save the time and offset in the original ADS_STRUCT */
3646 ads->config.current_time = ads_parse_time(timestr);
3648 if (ads->config.current_time != 0) {
3649 ads->auth.time_offset = ads->config.current_time - time(NULL);
3650 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3653 ads_msgfree(ads, res);
3655 status = ADS_SUCCESS;
3657 done:
3658 TALLOC_FREE(tmp_ctx);
3660 return status;
3663 /********************************************************************
3664 ********************************************************************/
3666 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3668 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3669 const char *attrs[] = {"domainFunctionality", NULL};
3670 ADS_STATUS status;
3671 LDAPMessage *res;
3672 ADS_STRUCT *ads_s = ads;
3674 *val = DS_DOMAIN_FUNCTION_2000;
3676 /* establish a new ldap tcp session if necessary */
3678 if ( !ads->ldap.ld ) {
3680 * ADS_STRUCT may be being reused after a
3681 * DC lookup, so ads->ldap.ss may already have a
3682 * good address. If not, re-initialize the passed-in
3683 * ADS_STRUCT with the given server.XXXX parameters.
3685 * Note that this doesn't depend on
3686 * ads->server.ldap_server != NULL,
3687 * as the case where ads->server.ldap_server==NULL and
3688 * ads->ldap.ss != zero_address is precisely the DC
3689 * lookup case where ads->ldap.ss was found by going
3690 * through ads_find_dc() again we want to avoid repeating.
3692 if (is_zero_addr(&ads->ldap.ss)) {
3693 ads_s = ads_init(tmp_ctx,
3694 ads->server.realm,
3695 ads->server.workgroup,
3696 ads->server.ldap_server,
3697 ADS_SASL_PLAIN );
3698 if (ads_s == NULL ) {
3699 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3700 goto done;
3705 * Reset ads->config.flags as it can contain the flags
3706 * returned by the previous CLDAP ping when reusing the struct.
3708 ads_s->config.flags = 0;
3710 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3711 status = ads_connect( ads_s );
3712 if ( !ADS_ERR_OK(status))
3713 goto done;
3716 /* If the attribute does not exist assume it is a Windows 2000
3717 functional domain */
3719 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3720 if (!ADS_ERR_OK(status)) {
3721 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3722 status = ADS_SUCCESS;
3724 goto done;
3727 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3728 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3730 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3733 ads_msgfree(ads_s, res);
3735 done:
3736 TALLOC_FREE(tmp_ctx);
3738 return status;
3742 * find the domain sid for our domain
3743 * @param ads connection to ads server
3744 * @param sid Pointer to domain sid
3745 * @return status of search
3747 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3749 const char *attrs[] = {"objectSid", NULL};
3750 LDAPMessage *res;
3751 ADS_STATUS rc;
3753 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3754 attrs, &res);
3755 if (!ADS_ERR_OK(rc)) return rc;
3756 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3757 ads_msgfree(ads, res);
3758 return ADS_ERROR_SYSTEM(ENOENT);
3760 ads_msgfree(ads, res);
3762 return ADS_SUCCESS;
3766 * find our site name
3767 * @param ads connection to ads server
3768 * @param mem_ctx Pointer to talloc context
3769 * @param site_name Pointer to the sitename
3770 * @return status of search
3772 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3774 ADS_STATUS status;
3775 LDAPMessage *res;
3776 const char *dn, *service_name;
3777 const char *attrs[] = { "dsServiceName", NULL };
3779 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3780 if (!ADS_ERR_OK(status)) {
3781 return status;
3784 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3785 if (service_name == NULL) {
3786 ads_msgfree(ads, res);
3787 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3790 ads_msgfree(ads, res);
3792 /* go up three levels */
3793 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3794 if (dn == NULL) {
3795 return ADS_ERROR(LDAP_NO_MEMORY);
3798 *site_name = talloc_strdup(mem_ctx, dn);
3799 if (*site_name == NULL) {
3800 return ADS_ERROR(LDAP_NO_MEMORY);
3803 return status;
3805 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3810 * find the site dn where a machine resides
3811 * @param ads connection to ads server
3812 * @param mem_ctx Pointer to talloc context
3813 * @param computer_name name of the machine
3814 * @param site_name Pointer to the sitename
3815 * @return status of search
3817 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3819 ADS_STATUS status;
3820 LDAPMessage *res;
3821 const char *parent, *filter;
3822 char *config_context = NULL;
3823 char *dn;
3825 /* shortcut a query */
3826 if (strequal(computer_name, ads->config.ldap_server_name)) {
3827 return ads_site_dn(ads, mem_ctx, site_dn);
3830 status = ads_config_path(ads, mem_ctx, &config_context);
3831 if (!ADS_ERR_OK(status)) {
3832 return status;
3835 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3836 if (filter == NULL) {
3837 return ADS_ERROR(LDAP_NO_MEMORY);
3840 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3841 filter, NULL, &res);
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 dn = ads_get_dn(ads, mem_ctx, res);
3852 if (dn == NULL) {
3853 ads_msgfree(ads, res);
3854 return ADS_ERROR(LDAP_NO_MEMORY);
3857 /* go up three levels */
3858 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3859 if (parent == NULL) {
3860 ads_msgfree(ads, res);
3861 TALLOC_FREE(dn);
3862 return ADS_ERROR(LDAP_NO_MEMORY);
3865 *site_dn = talloc_strdup(mem_ctx, parent);
3866 if (*site_dn == NULL) {
3867 ads_msgfree(ads, res);
3868 TALLOC_FREE(dn);
3869 return ADS_ERROR(LDAP_NO_MEMORY);
3872 TALLOC_FREE(dn);
3873 ads_msgfree(ads, res);
3875 return status;
3879 * get the upn suffixes for a domain
3880 * @param ads connection to ads server
3881 * @param mem_ctx Pointer to talloc context
3882 * @param suffixes Pointer to an array of suffixes
3883 * @param num_suffixes Pointer to the number of suffixes
3884 * @return status of search
3886 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3888 ADS_STATUS status;
3889 LDAPMessage *res;
3890 const char *base;
3891 char *config_context = NULL;
3892 const char *attrs[] = { "uPNSuffixes", NULL };
3894 status = ads_config_path(ads, mem_ctx, &config_context);
3895 if (!ADS_ERR_OK(status)) {
3896 return status;
3899 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3900 if (base == NULL) {
3901 return ADS_ERROR(LDAP_NO_MEMORY);
3904 status = ads_search_dn(ads, &res, base, attrs);
3905 if (!ADS_ERR_OK(status)) {
3906 return status;
3909 if (ads_count_replies(ads, res) != 1) {
3910 ads_msgfree(ads, res);
3911 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3914 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3915 if ((*suffixes) == NULL) {
3916 ads_msgfree(ads, res);
3917 return ADS_ERROR(LDAP_NO_MEMORY);
3920 ads_msgfree(ads, res);
3922 return status;
3926 * get the joinable ous for a domain
3927 * @param ads connection to ads server
3928 * @param mem_ctx Pointer to talloc context
3929 * @param ous Pointer to an array of ous
3930 * @param num_ous Pointer to the number of ous
3931 * @return status of search
3933 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3934 TALLOC_CTX *mem_ctx,
3935 char ***ous,
3936 size_t *num_ous)
3938 ADS_STATUS status;
3939 LDAPMessage *res = NULL;
3940 LDAPMessage *msg = NULL;
3941 const char *attrs[] = { "dn", NULL };
3942 int count = 0;
3944 status = ads_search(ads, &res,
3945 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3946 attrs);
3947 if (!ADS_ERR_OK(status)) {
3948 return status;
3951 count = ads_count_replies(ads, res);
3952 if (count < 1) {
3953 ads_msgfree(ads, res);
3954 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3957 for (msg = ads_first_entry(ads, res); msg;
3958 msg = ads_next_entry(ads, msg)) {
3959 const char **p = discard_const_p(const char *, *ous);
3960 char *dn = NULL;
3962 dn = ads_get_dn(ads, talloc_tos(), msg);
3963 if (!dn) {
3964 ads_msgfree(ads, res);
3965 return ADS_ERROR(LDAP_NO_MEMORY);
3968 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3969 TALLOC_FREE(dn);
3970 ads_msgfree(ads, res);
3971 return ADS_ERROR(LDAP_NO_MEMORY);
3974 TALLOC_FREE(dn);
3975 *ous = discard_const_p(char *, p);
3978 ads_msgfree(ads, res);
3980 return status;
3985 * pull a struct dom_sid from an extended dn string
3986 * @param mem_ctx TALLOC_CTX
3987 * @param extended_dn string
3988 * @param flags string type of extended_dn
3989 * @param sid pointer to a struct dom_sid
3990 * @return NT_STATUS_OK on success,
3991 * NT_INVALID_PARAMETER on error,
3992 * NT_STATUS_NOT_FOUND if no SID present
3994 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3995 const char *extended_dn,
3996 enum ads_extended_dn_flags flags,
3997 struct dom_sid *sid)
3999 char *p, *q, *dn;
4001 if (!extended_dn) {
4002 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4005 /* otherwise extended_dn gets stripped off */
4006 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
4007 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4010 * ADS_EXTENDED_DN_HEX_STRING:
4011 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4013 * ADS_EXTENDED_DN_STRING (only with w2k3):
4014 * <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
4016 * Object with no SID, such as an Exchange Public Folder
4017 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4020 p = strchr(dn, ';');
4021 if (!p) {
4022 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4025 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
4026 DEBUG(5,("No SID present in extended dn\n"));
4027 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
4030 p += strlen(";<SID=");
4032 q = strchr(p, '>');
4033 if (!q) {
4034 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4037 *q = '\0';
4039 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
4041 switch (flags) {
4043 case ADS_EXTENDED_DN_STRING:
4044 if (!string_to_sid(sid, p)) {
4045 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4047 break;
4048 case ADS_EXTENDED_DN_HEX_STRING: {
4049 ssize_t ret;
4050 fstring buf;
4051 size_t buf_len;
4053 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
4054 if (buf_len == 0) {
4055 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4058 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
4059 if (ret == -1) {
4060 DEBUG(10,("failed to parse sid\n"));
4061 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4063 break;
4065 default:
4066 DEBUG(10,("unknown extended dn format\n"));
4067 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4070 return ADS_ERROR_NT(NT_STATUS_OK);
4073 /********************************************************************
4074 ********************************************************************/
4076 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4078 LDAPMessage *res = NULL;
4079 ADS_STATUS status;
4080 int count = 0;
4081 char *name = NULL;
4083 status = ads_find_machine_acct(ads, &res, machine_name);
4084 if (!ADS_ERR_OK(status)) {
4085 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4086 lp_netbios_name()));
4087 goto out;
4090 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4091 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4092 goto out;
4095 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4096 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4099 out:
4100 ads_msgfree(ads, res);
4102 return name;
4105 /********************************************************************
4106 ********************************************************************/
4108 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4109 LDAPMessage *msg, size_t *num_values)
4111 const char *field = "msDS-AdditionalDnsHostName";
4112 struct berval **values = NULL;
4113 char **ret = NULL;
4114 size_t i, converted_size;
4117 * Windows DC implicitly adds a short name for each FQDN added to
4118 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4119 * suffix "\0$" which we should ignore (see bug #14406).
4122 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4123 if (values == NULL) {
4124 return NULL;
4127 *num_values = ldap_count_values_len(values);
4129 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4130 if (ret == NULL) {
4131 ldap_value_free_len(values);
4132 return NULL;
4135 for (i = 0; i < *num_values; i++) {
4136 ret[i] = NULL;
4137 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4138 values[i]->bv_val,
4139 strnlen(values[i]->bv_val,
4140 values[i]->bv_len),
4141 &ret[i], &converted_size)) {
4142 ldap_value_free_len(values);
4143 return NULL;
4146 ret[i] = NULL;
4148 ldap_value_free_len(values);
4149 return ret;
4152 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4153 ADS_STRUCT *ads,
4154 const char *machine_name,
4155 char ***hostnames_array,
4156 size_t *num_hostnames)
4158 ADS_STATUS status;
4159 LDAPMessage *res = NULL;
4160 int count;
4162 status = ads_find_machine_acct(ads,
4163 &res,
4164 machine_name);
4165 if (!ADS_ERR_OK(status)) {
4166 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4167 machine_name));
4168 return status;
4171 count = ads_count_replies(ads, res);
4172 if (count != 1) {
4173 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4174 goto done;
4177 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4178 if (*hostnames_array == NULL) {
4179 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4180 machine_name));
4181 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4182 goto done;
4185 done:
4186 ads_msgfree(ads, res);
4188 return status;
4191 /********************************************************************
4192 ********************************************************************/
4194 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4196 LDAPMessage *res = NULL;
4197 ADS_STATUS status;
4198 int count = 0;
4199 char *name = NULL;
4201 status = ads_find_machine_acct(ads, &res, machine_name);
4202 if (!ADS_ERR_OK(status)) {
4203 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4204 lp_netbios_name()));
4205 goto out;
4208 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4209 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4210 goto out;
4213 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4214 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4217 out:
4218 ads_msgfree(ads, res);
4220 return name;
4223 /********************************************************************
4224 ********************************************************************/
4226 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4228 LDAPMessage *res = NULL;
4229 ADS_STATUS status;
4230 int count = 0;
4231 char *name = NULL;
4232 bool ok = false;
4234 status = ads_find_machine_acct(ads, &res, machine_name);
4235 if (!ADS_ERR_OK(status)) {
4236 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4237 lp_netbios_name()));
4238 goto out;
4241 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4242 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4243 goto out;
4246 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4247 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4250 out:
4251 ads_msgfree(ads, res);
4252 if (name != NULL) {
4253 ok = (strlen(name) > 0);
4255 TALLOC_FREE(name);
4256 return ok;
4259 #if 0
4261 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4264 * Join a machine to a realm
4265 * Creates the machine account and sets the machine password
4266 * @param ads connection to ads server
4267 * @param machine name of host to add
4268 * @param org_unit Organizational unit to place machine in
4269 * @return status of join
4271 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4272 uint32_t account_type, const char *org_unit)
4274 ADS_STATUS status;
4275 LDAPMessage *res = NULL;
4276 char *machine;
4278 /* machine name must be lowercase */
4279 machine = SMB_STRDUP(machine_name);
4280 strlower_m(machine);
4283 status = ads_find_machine_acct(ads, (void **)&res, machine);
4284 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4285 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4286 status = ads_leave_realm(ads, machine);
4287 if (!ADS_ERR_OK(status)) {
4288 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4289 machine, ads->config.realm));
4290 return status;
4294 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4295 if (!ADS_ERR_OK(status)) {
4296 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4297 SAFE_FREE(machine);
4298 return status;
4301 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4302 if (!ADS_ERR_OK(status)) {
4303 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4304 SAFE_FREE(machine);
4305 return status;
4308 SAFE_FREE(machine);
4309 ads_msgfree(ads, res);
4311 return status;
4313 #endif
4316 * Delete a machine from the realm
4317 * @param ads connection to ads server
4318 * @param hostname Machine to remove
4319 * @return status of delete
4321 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4323 ADS_STATUS status;
4324 void *msg;
4325 LDAPMessage *res;
4326 char *hostnameDN, *host;
4327 int rc;
4328 LDAPControl ldap_control;
4329 LDAPControl * pldap_control[2] = {NULL, NULL};
4331 pldap_control[0] = &ldap_control;
4332 memset(&ldap_control, 0, sizeof(LDAPControl));
4333 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4335 /* hostname must be lowercase */
4336 host = SMB_STRDUP(hostname);
4337 if (!strlower_m(host)) {
4338 SAFE_FREE(host);
4339 return ADS_ERROR_SYSTEM(EINVAL);
4342 status = ads_find_machine_acct(ads, &res, host);
4343 if (!ADS_ERR_OK(status)) {
4344 DEBUG(0, ("Host account for %s does not exist.\n", host));
4345 SAFE_FREE(host);
4346 return status;
4349 msg = ads_first_entry(ads, res);
4350 if (!msg) {
4351 SAFE_FREE(host);
4352 return ADS_ERROR_SYSTEM(ENOENT);
4355 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4356 if (hostnameDN == NULL) {
4357 SAFE_FREE(host);
4358 return ADS_ERROR_SYSTEM(ENOENT);
4361 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4362 if (rc) {
4363 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4364 }else {
4365 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4368 if (rc != LDAP_SUCCESS) {
4369 const char *attrs[] = { "cn", NULL };
4370 LDAPMessage *msg_sub;
4372 /* we only search with scope ONE, we do not expect any further
4373 * objects to be created deeper */
4375 status = ads_do_search_retry(ads, hostnameDN,
4376 LDAP_SCOPE_ONELEVEL,
4377 "(objectclass=*)", attrs, &res);
4379 if (!ADS_ERR_OK(status)) {
4380 SAFE_FREE(host);
4381 TALLOC_FREE(hostnameDN);
4382 return status;
4385 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4386 msg_sub = ads_next_entry(ads, msg_sub)) {
4388 char *dn = NULL;
4390 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4391 SAFE_FREE(host);
4392 TALLOC_FREE(hostnameDN);
4393 return ADS_ERROR(LDAP_NO_MEMORY);
4396 status = ads_del_dn(ads, dn);
4397 if (!ADS_ERR_OK(status)) {
4398 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4399 SAFE_FREE(host);
4400 TALLOC_FREE(dn);
4401 TALLOC_FREE(hostnameDN);
4402 return status;
4405 TALLOC_FREE(dn);
4408 /* there should be no subordinate objects anymore */
4409 status = ads_do_search_retry(ads, hostnameDN,
4410 LDAP_SCOPE_ONELEVEL,
4411 "(objectclass=*)", attrs, &res);
4413 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4414 SAFE_FREE(host);
4415 TALLOC_FREE(hostnameDN);
4416 return status;
4419 /* delete hostnameDN now */
4420 status = ads_del_dn(ads, hostnameDN);
4421 if (!ADS_ERR_OK(status)) {
4422 SAFE_FREE(host);
4423 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4424 TALLOC_FREE(hostnameDN);
4425 return status;
4429 TALLOC_FREE(hostnameDN);
4431 status = ads_find_machine_acct(ads, &res, host);
4432 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4433 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4434 DEBUG(3, ("Failed to remove host account.\n"));
4435 SAFE_FREE(host);
4436 return status;
4439 SAFE_FREE(host);
4440 return ADS_SUCCESS;
4444 * pull all token-sids from an LDAP dn
4445 * @param ads connection to ads server
4446 * @param mem_ctx TALLOC_CTX for allocating sid array
4447 * @param dn of LDAP object
4448 * @param user_sid pointer to struct dom_sid (objectSid)
4449 * @param primary_group_sid pointer to struct dom_sid (self composed)
4450 * @param sids pointer to sid array to allocate
4451 * @param num_sids counter of SIDs pulled
4452 * @return status of token query
4454 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4455 TALLOC_CTX *mem_ctx,
4456 const char *dn,
4457 struct dom_sid *user_sid,
4458 struct dom_sid *primary_group_sid,
4459 struct dom_sid **sids,
4460 size_t *num_sids)
4462 ADS_STATUS status;
4463 LDAPMessage *res = NULL;
4464 int count = 0;
4465 size_t tmp_num_sids;
4466 struct dom_sid *tmp_sids;
4467 struct dom_sid tmp_user_sid;
4468 struct dom_sid tmp_primary_group_sid;
4469 uint32_t pgid;
4470 const char *attrs[] = {
4471 "objectSid",
4472 "tokenGroups",
4473 "primaryGroupID",
4474 NULL
4477 status = ads_search_retry_dn(ads, &res, dn, attrs);
4478 if (!ADS_ERR_OK(status)) {
4479 return status;
4482 count = ads_count_replies(ads, res);
4483 if (count != 1) {
4484 ads_msgfree(ads, res);
4485 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4488 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4489 ads_msgfree(ads, res);
4490 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4493 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4494 ads_msgfree(ads, res);
4495 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4499 /* hack to compose the primary group sid without knowing the
4500 * domsid */
4502 struct dom_sid domsid;
4504 sid_copy(&domsid, &tmp_user_sid);
4506 if (!sid_split_rid(&domsid, NULL)) {
4507 ads_msgfree(ads, res);
4508 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4511 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4512 ads_msgfree(ads, res);
4513 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4517 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4519 if (tmp_num_sids == 0 || !tmp_sids) {
4520 ads_msgfree(ads, res);
4521 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4524 if (num_sids) {
4525 *num_sids = tmp_num_sids;
4528 if (sids) {
4529 *sids = tmp_sids;
4532 if (user_sid) {
4533 *user_sid = tmp_user_sid;
4536 if (primary_group_sid) {
4537 *primary_group_sid = tmp_primary_group_sid;
4540 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4542 ads_msgfree(ads, res);
4543 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4547 * Find a sAMAccountName in LDAP
4548 * @param ads connection to ads server
4549 * @param mem_ctx TALLOC_CTX for allocating sid array
4550 * @param samaccountname to search
4551 * @param uac_ret uint32_t pointer userAccountControl attribute value
4552 * @param dn_ret pointer to dn
4553 * @return status of token query
4555 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4556 TALLOC_CTX *mem_ctx,
4557 const char *samaccountname,
4558 uint32_t *uac_ret,
4559 const char **dn_ret)
4561 ADS_STATUS status;
4562 const char *attrs[] = { "userAccountControl", NULL };
4563 const char *filter;
4564 LDAPMessage *res = NULL;
4565 char *dn = NULL;
4566 uint32_t uac = 0;
4568 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4569 samaccountname);
4570 if (filter == NULL) {
4571 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4572 goto out;
4575 status = ads_do_search_all(ads, ads->config.bind_path,
4576 LDAP_SCOPE_SUBTREE,
4577 filter, attrs, &res);
4579 if (!ADS_ERR_OK(status)) {
4580 goto out;
4583 if (ads_count_replies(ads, res) != 1) {
4584 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4585 goto out;
4588 dn = ads_get_dn(ads, talloc_tos(), res);
4589 if (dn == NULL) {
4590 status = ADS_ERROR(LDAP_NO_MEMORY);
4591 goto out;
4594 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4595 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4596 goto out;
4599 if (uac_ret) {
4600 *uac_ret = uac;
4603 if (dn_ret) {
4604 *dn_ret = talloc_strdup(mem_ctx, dn);
4605 if (!*dn_ret) {
4606 status = ADS_ERROR(LDAP_NO_MEMORY);
4607 goto out;
4610 out:
4611 TALLOC_FREE(dn);
4612 ads_msgfree(ads, res);
4614 return status;
4618 * find our configuration path
4619 * @param ads connection to ads server
4620 * @param mem_ctx Pointer to talloc context
4621 * @param config_path Pointer to the config path
4622 * @return status of search
4624 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4625 TALLOC_CTX *mem_ctx,
4626 char **config_path)
4628 ADS_STATUS status;
4629 LDAPMessage *res = NULL;
4630 const char *config_context = NULL;
4631 const char *attrs[] = { "configurationNamingContext", NULL };
4633 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4634 "(objectclass=*)", attrs, &res);
4635 if (!ADS_ERR_OK(status)) {
4636 return status;
4639 config_context = ads_pull_string(ads, mem_ctx, res,
4640 "configurationNamingContext");
4641 ads_msgfree(ads, res);
4642 if (!config_context) {
4643 return ADS_ERROR(LDAP_NO_MEMORY);
4646 if (config_path) {
4647 *config_path = talloc_strdup(mem_ctx, config_context);
4648 if (!*config_path) {
4649 return ADS_ERROR(LDAP_NO_MEMORY);
4653 return ADS_ERROR(LDAP_SUCCESS);
4657 * find the displayName of an extended right
4658 * @param ads connection to ads server
4659 * @param config_path The config path
4660 * @param mem_ctx Pointer to talloc context
4661 * @param GUID struct of the rightsGUID
4662 * @return status of search
4664 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4665 const char *config_path,
4666 TALLOC_CTX *mem_ctx,
4667 const struct GUID *rights_guid)
4669 ADS_STATUS rc;
4670 LDAPMessage *res = NULL;
4671 char *expr = NULL;
4672 const char *attrs[] = { "displayName", NULL };
4673 const char *result = NULL;
4674 const char *path;
4676 if (!ads || !mem_ctx || !rights_guid) {
4677 goto done;
4680 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4681 GUID_string(mem_ctx, rights_guid));
4682 if (!expr) {
4683 goto done;
4686 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4687 if (!path) {
4688 goto done;
4691 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4692 expr, attrs, &res);
4693 if (!ADS_ERR_OK(rc)) {
4694 goto done;
4697 if (ads_count_replies(ads, res) != 1) {
4698 goto done;
4701 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4703 done:
4704 ads_msgfree(ads, res);
4705 return result;
4709 * verify or build and verify an account ou
4710 * @param mem_ctx Pointer to talloc context
4711 * @param ads connection to ads server
4712 * @param account_ou
4713 * @return status of search
4716 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4717 ADS_STRUCT *ads,
4718 const char **account_ou)
4720 char **exploded_dn;
4721 const char *name;
4722 char *ou_string;
4724 if (account_ou == NULL) {
4725 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4728 if (*account_ou != NULL) {
4729 exploded_dn = ldap_explode_dn(*account_ou, 0);
4730 if (exploded_dn) {
4731 ldap_value_free(exploded_dn);
4732 return ADS_SUCCESS;
4736 ou_string = ads_ou_string(ads, *account_ou);
4737 if (!ou_string) {
4738 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4741 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4742 ads->config.bind_path);
4743 SAFE_FREE(ou_string);
4745 if (!name) {
4746 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4749 exploded_dn = ldap_explode_dn(name, 0);
4750 if (!exploded_dn) {
4751 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4753 ldap_value_free(exploded_dn);
4755 *account_ou = name;
4756 return ADS_SUCCESS;
4759 #endif