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/>.
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/addns/dnsquery.h"
29 #include "../libds/common/flags.h"
31 #include "../libcli/security/security.h"
32 #include "lib/param/loadparm.h"
38 * @brief basic ldap client-side routines for ads server communications
40 * The routines contained here should do the necessary ldap calls for
43 * Important note: attribute names passed into ads_ routines must
44 * already be in UTF-8 format. We do not convert them because in almost
45 * all cases, they are just ascii (which is represented with the same
46 * codepoints in UTF-8). This may have to change at some point
50 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
52 static SIG_ATOMIC_T gotalarm;
54 /***************************************************************
55 Signal function to tell us we timed out.
56 ****************************************************************/
58 static void gotalarm_sig(int signum)
63 LDAP *ldap_open_with_timeout(const char *server,
64 struct sockaddr_storage *ss,
65 int port, unsigned int to)
71 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
72 "%u seconds\n", server, port, to));
77 CatchSignal(SIGALRM, gotalarm_sig);
79 /* End setup timeout. */
82 if ( strchr_m(server, ':') ) {
84 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
87 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
93 #ifdef HAVE_LDAP_INITIALIZE
94 ldap_err = ldap_initialize(&ldp, uri);
96 ldp = ldap_open(server, port);
98 ldap_err = LDAP_SUCCESS;
100 ldap_err = LDAP_OTHER;
103 if (ldap_err != LDAP_SUCCESS) {
104 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
105 uri, ldap_err2string(ldap_err)));
107 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
111 /* Teardown timeout. */
113 CatchSignal(SIGALRM, SIG_IGN);
119 static int ldap_search_with_timeout(LDAP *ld,
120 LDAP_CONST char *base,
122 LDAP_CONST char *filter,
125 LDAPControl **sctrls,
126 LDAPControl **cctrls,
130 int to = lp_ldap_timeout();
131 struct timeval timeout;
132 struct timeval *timeout_ptr = NULL;
135 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
141 timeout_ptr = &timeout;
143 /* Setup alarm timeout. */
144 CatchSignal(SIGALRM, gotalarm_sig);
145 /* Make the alarm time one second beyond
146 the timout we're setting for the
147 remote search timeout, to allow that
148 to fire in preference. */
150 /* End setup timeout. */
154 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
155 attrsonly, sctrls, cctrls, timeout_ptr,
159 /* Teardown alarm timeout. */
160 CatchSignal(SIGALRM, SIG_IGN);
165 return LDAP_TIMELIMIT_EXCEEDED;
168 * A bug in OpenLDAP means ldap_search_ext_s can return
169 * LDAP_SUCCESS but with a NULL res pointer. Cope with
170 * this. See bug #6279 for details. JRA.
174 return LDAP_TIMELIMIT_EXCEEDED;
180 /**********************************************
181 Do client and server sitename match ?
182 **********************************************/
184 bool ads_sitename_match(ADS_STRUCT *ads)
186 if (ads->config.server_site_name == NULL &&
187 ads->config.client_site_name == NULL ) {
188 DEBUG(10,("ads_sitename_match: both null\n"));
191 if (ads->config.server_site_name &&
192 ads->config.client_site_name &&
193 strequal(ads->config.server_site_name,
194 ads->config.client_site_name)) {
195 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
198 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
199 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
200 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
204 /**********************************************
205 Is this the closest DC ?
206 **********************************************/
208 bool ads_closest_dc(ADS_STRUCT *ads)
210 if (ads->config.flags & NBT_SERVER_CLOSEST) {
211 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
215 /* not sure if this can ever happen */
216 if (ads_sitename_match(ads)) {
217 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
221 if (ads->config.client_site_name == NULL) {
222 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
226 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
227 ads->config.ldap_server_name));
234 try a connection to a given ldap server, returning True and setting the servers IP
235 in the ads struct if successful
237 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
238 struct sockaddr_storage *ss)
240 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
241 TALLOC_CTX *frame = talloc_stackframe();
243 char addr[INET6_ADDRSTRLEN];
250 print_sockaddr(addr, sizeof(addr), ss);
252 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
253 addr, ads->server.realm));
255 ZERO_STRUCT( cldap_reply );
257 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
258 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
263 /* Check the CLDAP reply flags */
265 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
266 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
272 /* Fill in the ads->config values */
274 SAFE_FREE(ads->config.realm);
275 SAFE_FREE(ads->config.bind_path);
276 SAFE_FREE(ads->config.ldap_server_name);
277 SAFE_FREE(ads->config.server_site_name);
278 SAFE_FREE(ads->config.client_site_name);
279 SAFE_FREE(ads->server.workgroup);
281 ads->config.flags = cldap_reply.server_type;
282 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
283 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
284 if (!strupper_m(ads->config.realm)) {
289 ads->config.bind_path = ads_build_dn(ads->config.realm);
290 if (*cldap_reply.server_site) {
291 ads->config.server_site_name =
292 SMB_STRDUP(cldap_reply.server_site);
294 if (*cldap_reply.client_site) {
295 ads->config.client_site_name =
296 SMB_STRDUP(cldap_reply.client_site);
298 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
300 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
303 /* Store our site name. */
304 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
305 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
315 /**********************************************************************
316 Try to find an AD dc using our internal name resolution routines
317 Try the realm first and then then workgroup name if netbios is not
319 **********************************************************************/
321 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
323 const char *c_domain;
326 struct ip_service *ip_list;
329 bool got_realm = False;
330 bool use_own_domain = False;
332 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
335 /* if the realm and workgroup are both empty, assume they are ours */
338 c_realm = ads->server.realm;
340 if ( !c_realm || !*c_realm ) {
341 /* special case where no realm and no workgroup means our own */
342 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
343 use_own_domain = True;
344 c_realm = lp_realm();
348 if (c_realm && *c_realm)
351 /* we need to try once with the realm name and fallback to the
352 netbios domain name if we fail (if netbios has not been disabled */
354 if ( !got_realm && !lp_disable_netbios() ) {
355 c_realm = ads->server.workgroup;
356 if (!c_realm || !*c_realm) {
357 if ( use_own_domain )
358 c_realm = lp_workgroup();
362 if ( !c_realm || !*c_realm ) {
363 DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know "
365 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
368 if ( use_own_domain ) {
369 c_domain = lp_workgroup();
371 c_domain = ads->server.workgroup;
378 * In case of LDAP we use get_dc_name() as that
379 * creates the custom krb5.conf file
381 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
383 struct sockaddr_storage ip_out;
385 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
386 (got_realm ? "realm" : "domain"), realm));
388 ok = get_dc_name(domain, realm, srv_name, &ip_out);
391 * we call ads_try_connect() to fill in the
392 * ads->config details
394 ok = ads_try_connect(ads, false, &ip_out);
400 return NT_STATUS_NO_LOGON_SERVERS;
403 sitename = sitename_fetch(talloc_tos(), realm);
407 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
408 (got_realm ? "realm" : "domain"), realm));
410 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
411 if (!NT_STATUS_IS_OK(status)) {
412 /* fall back to netbios if we can */
413 if ( got_realm && !lp_disable_netbios() ) {
418 TALLOC_FREE(sitename);
422 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
423 for ( i=0; i<count; i++ ) {
424 char server[INET6_ADDRSTRLEN];
426 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
428 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
432 /* realm in this case is a workgroup name. We need
433 to ignore any IP addresses in the negative connection
434 cache that match ip addresses returned in the ad realm
435 case. It sucks that I have to reproduce the logic above... */
436 c_realm = ads->server.realm;
437 if ( !c_realm || !*c_realm ) {
438 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
439 c_realm = lp_realm();
442 if (c_realm && *c_realm &&
443 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
444 /* Ensure we add the workgroup name for this
445 IP address as negative too. */
446 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
451 ok = ads_try_connect(ads, false, &ip_list[i].ss);
454 TALLOC_FREE(sitename);
458 /* keep track of failures */
459 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
464 /* In case we failed to contact one of our closest DC on our site we
465 * need to try to find another DC, retry with a site-less SRV DNS query
469 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
470 "trying to find another DC\n", sitename));
471 TALLOC_FREE(sitename);
472 namecache_delete(realm, 0x1C);
476 return NT_STATUS_NO_LOGON_SERVERS;
479 /*********************************************************************
480 *********************************************************************/
482 static NTSTATUS ads_lookup_site(void)
484 ADS_STRUCT *ads = NULL;
485 ADS_STATUS ads_status;
486 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
488 ads = ads_init(lp_realm(), NULL, NULL);
490 return NT_STATUS_NO_MEMORY;
493 /* The NO_BIND here will find a DC and set the client site
494 but not establish the TCP connection */
496 ads->auth.flags = ADS_AUTH_NO_BIND;
497 ads_status = ads_connect(ads);
498 if (!ADS_ERR_OK(ads_status)) {
499 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
500 ads_errstr(ads_status)));
502 nt_status = ads_ntstatus(ads_status);
511 /*********************************************************************
512 *********************************************************************/
514 static const char* host_dns_domain(const char *fqdn)
516 const char *p = fqdn;
518 /* go to next char following '.' */
520 if ((p = strchr_m(fqdn, '.')) != NULL) {
529 * Connect to the Global Catalog server
530 * @param ads Pointer to an existing ADS_STRUCT
531 * @return status of connection
533 * Simple wrapper around ads_connect() that fills in the
534 * GC ldap server information
537 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
539 TALLOC_CTX *frame = talloc_stackframe();
540 struct dns_rr_srv *gcs_list;
542 const char *realm = ads->server.realm;
543 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
544 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
547 char *sitename = NULL;
548 const char *dns_hosts_file;
553 if ((sitename = sitename_fetch(frame, realm)) == NULL) {
555 sitename = sitename_fetch(frame, realm);
558 dns_hosts_file = lp_parm_const_string(-1, "resolv", "host file", NULL);
560 /* We try once with a sitename and once without
561 (unless we don't have a sitename and then we're
564 if (sitename == NULL)
567 nt_status = ads_dns_query_gcs(frame, dns_hosts_file,
569 &gcs_list, &num_gcs);
571 if (!NT_STATUS_IS_OK(nt_status)) {
572 ads_status = ADS_ERROR_NT(nt_status);
576 /* Loop until we get a successful connection or have gone
577 through them all. When connecting a GC server, make sure that
578 the realm is the server's DNS name and not the forest root */
580 for (i=0; i<num_gcs; i++) {
581 ads->server.gc = true;
582 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
583 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
584 ads_status = ads_connect(ads);
585 if (ADS_ERR_OK(ads_status)) {
586 /* Reset the bind_dn to "". A Global Catalog server
587 may host multiple domain trees in a forest.
588 Windows 2003 GC server will accept "" as the search
589 path to imply search all domain trees in the forest */
591 SAFE_FREE(ads->config.bind_path);
592 ads->config.bind_path = SMB_STRDUP("");
597 SAFE_FREE(ads->server.ldap_server);
598 SAFE_FREE(ads->server.realm);
601 TALLOC_FREE(gcs_list);
606 talloc_destroy(frame);
613 * Connect to the LDAP server
614 * @param ads Pointer to an existing ADS_STRUCT
615 * @return status of connection
617 ADS_STATUS ads_connect(ADS_STRUCT *ads)
619 int version = LDAP_VERSION3;
622 char addr[INET6_ADDRSTRLEN];
624 ZERO_STRUCT(ads->ldap);
625 ads->ldap.last_attempt = time_mono(NULL);
626 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
628 /* try with a user specified server */
630 if (DEBUGLEVEL >= 11) {
631 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
632 DEBUG(11,("ads_connect: entering\n"));
633 DEBUGADD(11,("%s\n", s));
637 if (ads->server.ldap_server) {
639 struct sockaddr_storage ss;
641 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
643 DEBUG(5,("ads_connect: unable to resolve name %s\n",
644 ads->server.ldap_server));
645 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
648 ok = ads_try_connect(ads, ads->server.gc, &ss);
653 /* The choice of which GC use is handled one level up in
654 ads_connect_gc(). If we continue on from here with
655 ads_find_dc() we will get GC searches on port 389 which
656 doesn't work. --jerry */
658 if (ads->server.gc == true) {
659 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
663 ntstatus = ads_find_dc(ads);
664 if (NT_STATUS_IS_OK(ntstatus)) {
668 status = ADS_ERROR_NT(ntstatus);
673 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
674 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
676 if (!ads->auth.user_name) {
677 /* Must use the userPrincipalName value here or sAMAccountName
678 and not servicePrincipalName; found by Guenther Deschner */
680 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
681 DEBUG(0,("ads_connect: asprintf fail.\n"));
682 ads->auth.user_name = NULL;
686 if (!ads->auth.realm) {
687 ads->auth.realm = SMB_STRDUP(ads->config.realm);
690 if (!ads->auth.kdc_server) {
691 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
692 ads->auth.kdc_server = SMB_STRDUP(addr);
695 /* If the caller() requested no LDAP bind, then we are done */
697 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
698 status = ADS_SUCCESS;
702 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
703 if (!ads->ldap.mem_ctx) {
704 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
708 /* Otherwise setup the TCP LDAP session */
710 ads->ldap.ld = ldap_open_with_timeout(addr,
712 ads->ldap.port, lp_ldap_timeout());
713 if (ads->ldap.ld == NULL) {
714 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
717 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
719 /* cache the successful connection for workgroup and realm */
720 if (ads_closest_dc(ads)) {
721 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
722 saf_store( ads->server.realm, ads->config.ldap_server_name);
725 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
727 if ( lp_ldap_ssl_ads() ) {
728 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
729 if (!ADS_ERR_OK(status)) {
734 /* fill in the current time and offsets */
736 status = ads_current_time( ads );
737 if ( !ADS_ERR_OK(status) ) {
741 /* Now do the bind */
743 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
744 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
748 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
749 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
753 status = ads_sasl_bind(ads);
756 if (DEBUGLEVEL >= 11) {
757 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
758 DEBUG(11,("ads_connect: leaving with: %s\n",
759 ads_errstr(status)));
760 DEBUGADD(11,("%s\n", s));
768 * Connect to the LDAP server using given credentials
769 * @param ads Pointer to an existing ADS_STRUCT
770 * @return status of connection
772 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
774 ads->auth.flags |= ADS_AUTH_USER_CREDS;
776 return ads_connect(ads);
780 * Disconnect the LDAP server
781 * @param ads Pointer to an existing ADS_STRUCT
783 void ads_disconnect(ADS_STRUCT *ads)
786 ldap_unbind(ads->ldap.ld);
789 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
790 ads->ldap.wrap_ops->disconnect(ads);
792 if (ads->ldap.mem_ctx) {
793 talloc_free(ads->ldap.mem_ctx);
795 ZERO_STRUCT(ads->ldap);
799 Duplicate a struct berval into talloc'ed memory
801 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
803 struct berval *value;
805 if (!in_val) return NULL;
807 value = talloc_zero(ctx, struct berval);
810 if (in_val->bv_len == 0) return value;
812 value->bv_len = in_val->bv_len;
813 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
819 Make a values list out of an array of (struct berval *)
821 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
822 const struct berval **in_vals)
824 struct berval **values;
827 if (!in_vals) return NULL;
828 for (i=0; in_vals[i]; i++)
830 values = talloc_zero_array(ctx, struct berval *, i+1);
831 if (!values) return NULL;
833 for (i=0; in_vals[i]; i++) {
834 values[i] = dup_berval(ctx, in_vals[i]);
840 UTF8-encode a values list out of an array of (char *)
842 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
848 if (!in_vals) return NULL;
849 for (i=0; in_vals[i]; i++)
851 values = talloc_zero_array(ctx, char *, i+1);
852 if (!values) return NULL;
854 for (i=0; in_vals[i]; i++) {
855 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
864 Pull a (char *) array out of a UTF8-encoded values list
866 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
870 size_t converted_size;
872 if (!in_vals) return NULL;
873 for (i=0; in_vals[i]; i++)
875 values = talloc_zero_array(ctx, char *, i+1);
876 if (!values) return NULL;
878 for (i=0; in_vals[i]; i++) {
879 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
881 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
882 "%s", strerror(errno)));
889 * Do a search with paged results. cookie must be null on the first
890 * call, and then returned on each subsequent call. It will be null
891 * again when the entire search is complete
892 * @param ads connection to ads server
893 * @param bind_path Base dn for the search
894 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
895 * @param expr Search expression - specified in local charset
896 * @param attrs Attributes to retrieve - specified in utf8 or ascii
897 * @param res ** which will contain results - free res* with ads_msgfree()
898 * @param count Number of entries retrieved on this page
899 * @param cookie The paged results cookie to be returned on subsequent calls
900 * @return status of search
902 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
903 const char *bind_path,
904 int scope, const char *expr,
905 const char **attrs, void *args,
907 int *count, struct berval **cookie)
910 char *utf8_expr, *utf8_path, **search_attrs = NULL;
911 size_t converted_size;
912 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
913 BerElement *cookie_be = NULL;
914 struct berval *cookie_bv= NULL;
915 BerElement *ext_be = NULL;
916 struct berval *ext_bv= NULL;
919 ads_control *external_control = (ads_control *) args;
923 if (!(ctx = talloc_init("ads_do_paged_search_args")))
924 return ADS_ERROR(LDAP_NO_MEMORY);
926 /* 0 means the conversion worked but the result was empty
927 so we only fail if it's -1. In any case, it always
928 at least nulls out the dest */
929 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
930 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
936 if (!attrs || !(*attrs))
939 /* This would be the utf8-encoded version...*/
940 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
941 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
947 /* Paged results only available on ldap v3 or later */
948 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
949 if (version < LDAP_VERSION3) {
950 rc = LDAP_NOT_SUPPORTED;
954 cookie_be = ber_alloc_t(LBER_USE_DER);
956 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
957 ber_bvfree(*cookie); /* don't need it from last time */
960 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
962 ber_flatten(cookie_be, &cookie_bv);
963 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
964 PagedResults.ldctl_iscritical = (char) 1;
965 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
966 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
968 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
969 NoReferrals.ldctl_iscritical = (char) 0;
970 NoReferrals.ldctl_value.bv_len = 0;
971 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
973 if (external_control &&
974 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
975 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
977 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
978 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
980 /* win2k does not accept a ldctl_value beeing passed in */
982 if (external_control->val != 0) {
984 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
989 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
993 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
998 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
999 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1002 ExternalCtrl.ldctl_value.bv_len = 0;
1003 ExternalCtrl.ldctl_value.bv_val = NULL;
1006 controls[0] = &NoReferrals;
1007 controls[1] = &PagedResults;
1008 controls[2] = &ExternalCtrl;
1012 controls[0] = &NoReferrals;
1013 controls[1] = &PagedResults;
1017 /* we need to disable referrals as the openldap libs don't
1018 handle them and paged results at the same time. Using them
1019 together results in the result record containing the server
1020 page control being removed from the result list (tridge/jmcd)
1022 leaving this in despite the control that says don't generate
1023 referrals, in case the server doesn't support it (jmcd)
1025 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1027 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1028 search_attrs, 0, controls,
1029 NULL, LDAP_NO_LIMIT,
1030 (LDAPMessage **)res);
1032 ber_free(cookie_be, 1);
1033 ber_bvfree(cookie_bv);
1036 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1037 ldap_err2string(rc)));
1038 if (rc == LDAP_OTHER) {
1042 ret = ldap_parse_result(ads->ldap.ld,
1050 if (ret == LDAP_SUCCESS) {
1051 DEBUG(3, ("ldap_search_with_timeout(%s) "
1052 "error: %s\n", expr, ldap_errmsg));
1053 ldap_memfree(ldap_errmsg);
1059 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1060 NULL, &rcontrols, 0);
1066 for (i=0; rcontrols[i]; i++) {
1067 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1068 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1069 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1071 /* the berval is the cookie, but must be freed when
1073 if (cookie_bv->bv_len) /* still more to do */
1074 *cookie=ber_bvdup(cookie_bv);
1077 ber_bvfree(cookie_bv);
1078 ber_free(cookie_be, 1);
1082 ldap_controls_free(rcontrols);
1085 talloc_destroy(ctx);
1088 ber_free(ext_be, 1);
1095 /* if/when we decide to utf8-encode attrs, take out this next line */
1096 TALLOC_FREE(search_attrs);
1098 return ADS_ERROR(rc);
1101 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1102 int scope, const char *expr,
1103 const char **attrs, LDAPMessage **res,
1104 int *count, struct berval **cookie)
1106 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1111 * Get all results for a search. This uses ads_do_paged_search() to return
1112 * all entries in a large search.
1113 * @param ads connection to ads server
1114 * @param bind_path Base dn for the search
1115 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1116 * @param expr Search expression
1117 * @param attrs Attributes to retrieve
1118 * @param res ** which will contain results - free res* with ads_msgfree()
1119 * @return status of search
1121 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1122 int scope, const char *expr,
1123 const char **attrs, void *args,
1126 struct berval *cookie = NULL;
1131 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1134 if (!ADS_ERR_OK(status))
1137 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1139 LDAPMessage *res2 = NULL;
1140 LDAPMessage *msg, *next;
1142 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1143 attrs, args, &res2, &count, &cookie);
1144 if (!ADS_ERR_OK(status)) {
1145 /* Ensure we free all collected results */
1146 ads_msgfree(ads, *res);
1151 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1152 that this works on all ldap libs, but I have only tested with openldap */
1153 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1154 next = ads_next_message(ads, msg);
1155 ldap_add_result_entry((LDAPMessage **)res, msg);
1157 /* note that we do not free res2, as the memory is now
1158 part of the main returned list */
1161 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1162 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1168 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1169 int scope, const char *expr,
1170 const char **attrs, LDAPMessage **res)
1172 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1175 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1176 int scope, const char *expr,
1177 const char **attrs, uint32 sd_flags,
1182 args.control = ADS_SD_FLAGS_OID;
1183 args.val = sd_flags;
1184 args.critical = True;
1186 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1191 * Run a function on all results for a search. Uses ads_do_paged_search() and
1192 * runs the function as each page is returned, using ads_process_results()
1193 * @param ads connection to ads server
1194 * @param bind_path Base dn for the search
1195 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1196 * @param expr Search expression - specified in local charset
1197 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1198 * @param fn Function which takes attr name, values list, and data_area
1199 * @param data_area Pointer which is passed to function on each call
1200 * @return status of search
1202 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1203 int scope, const char *expr, const char **attrs,
1204 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1207 struct berval *cookie = NULL;
1212 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1215 if (!ADS_ERR_OK(status)) return status;
1217 ads_process_results(ads, res, fn, data_area);
1218 ads_msgfree(ads, res);
1221 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1222 &res, &count, &cookie);
1224 if (!ADS_ERR_OK(status)) break;
1226 ads_process_results(ads, res, fn, data_area);
1227 ads_msgfree(ads, res);
1234 * Do a search with a timeout.
1235 * @param ads connection to ads server
1236 * @param bind_path Base dn for the search
1237 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1238 * @param expr Search expression
1239 * @param attrs Attributes to retrieve
1240 * @param res ** which will contain results - free res* with ads_msgfree()
1241 * @return status of search
1243 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1245 const char **attrs, LDAPMessage **res)
1248 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1249 size_t converted_size;
1253 if (!(ctx = talloc_init("ads_do_search"))) {
1254 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1255 return ADS_ERROR(LDAP_NO_MEMORY);
1258 /* 0 means the conversion worked but the result was empty
1259 so we only fail if it's negative. In any case, it always
1260 at least nulls out the dest */
1261 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1262 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1264 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1265 rc = LDAP_NO_MEMORY;
1269 if (!attrs || !(*attrs))
1270 search_attrs = NULL;
1272 /* This would be the utf8-encoded version...*/
1273 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1274 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1276 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1277 rc = LDAP_NO_MEMORY;
1282 /* see the note in ads_do_paged_search - we *must* disable referrals */
1283 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1285 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1286 search_attrs, 0, NULL, NULL,
1288 (LDAPMessage **)res);
1290 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1291 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1296 talloc_destroy(ctx);
1297 /* if/when we decide to utf8-encode attrs, take out this next line */
1298 TALLOC_FREE(search_attrs);
1299 return ADS_ERROR(rc);
1302 * Do a general ADS search
1303 * @param ads connection to ads server
1304 * @param res ** which will contain results - free res* with ads_msgfree()
1305 * @param expr Search expression
1306 * @param attrs Attributes to retrieve
1307 * @return status of search
1309 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1310 const char *expr, const char **attrs)
1312 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1317 * Do a search on a specific DistinguishedName
1318 * @param ads connection to ads server
1319 * @param res ** which will contain results - free res* with ads_msgfree()
1320 * @param dn DistinguishName to search
1321 * @param attrs Attributes to retrieve
1322 * @return status of search
1324 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1325 const char *dn, const char **attrs)
1327 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1332 * Free up memory from a ads_search
1333 * @param ads connection to ads server
1334 * @param msg Search results to free
1336 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1343 * Get a dn from search results
1344 * @param ads connection to ads server
1345 * @param msg Search result
1348 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1350 char *utf8_dn, *unix_dn;
1351 size_t converted_size;
1353 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1356 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1360 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1361 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1365 ldap_memfree(utf8_dn);
1370 * Get the parent from a dn
1371 * @param dn the dn to return the parent from
1372 * @return parent dn string
1374 char *ads_parent_dn(const char *dn)
1382 p = strchr(dn, ',');
1392 * Find a machine account given a hostname
1393 * @param ads connection to ads server
1394 * @param res ** which will contain results - free res* with ads_msgfree()
1395 * @param host Hostname to search for
1396 * @return status of search
1398 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1399 const char *machine)
1403 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1407 /* the easiest way to find a machine account anywhere in the tree
1408 is to look for hostname$ */
1409 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1410 DEBUG(1, ("asprintf failed!\n"));
1411 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1414 status = ads_search(ads, res, expr, attrs);
1420 * Initialize a list of mods to be used in a modify request
1421 * @param ctx An initialized TALLOC_CTX
1422 * @return allocated ADS_MODLIST
1424 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1426 #define ADS_MODLIST_ALLOC_SIZE 10
1429 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1430 /* -1 is safety to make sure we don't go over the end.
1431 need to reset it to NULL before doing ldap modify */
1432 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1434 return (ADS_MODLIST)mods;
1439 add an attribute to the list, with values list already constructed
1441 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1442 int mod_op, const char *name,
1443 const void *_invals)
1445 const void **invals = (const void **)_invals;
1447 LDAPMod **modlist = (LDAPMod **) *mods;
1448 struct berval **ber_values = NULL;
1449 char **char_values = NULL;
1452 mod_op = LDAP_MOD_DELETE;
1454 if (mod_op & LDAP_MOD_BVALUES)
1455 ber_values = ads_dup_values(ctx,
1456 (const struct berval **)invals);
1458 char_values = ads_push_strvals(ctx,
1459 (const char **) invals);
1462 /* find the first empty slot */
1463 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1465 if (modlist[curmod] == (LDAPMod *) -1) {
1466 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1467 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1468 return ADS_ERROR(LDAP_NO_MEMORY);
1469 memset(&modlist[curmod], 0,
1470 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1471 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1472 *mods = (ADS_MODLIST)modlist;
1475 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1476 return ADS_ERROR(LDAP_NO_MEMORY);
1477 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1478 if (mod_op & LDAP_MOD_BVALUES) {
1479 modlist[curmod]->mod_bvalues = ber_values;
1480 } else if (mod_op & LDAP_MOD_DELETE) {
1481 modlist[curmod]->mod_values = NULL;
1483 modlist[curmod]->mod_values = char_values;
1486 modlist[curmod]->mod_op = mod_op;
1487 return ADS_ERROR(LDAP_SUCCESS);
1491 * Add a single string value to a mod list
1492 * @param ctx An initialized TALLOC_CTX
1493 * @param mods An initialized ADS_MODLIST
1494 * @param name The attribute name to add
1495 * @param val The value to add - NULL means DELETE
1496 * @return ADS STATUS indicating success of add
1498 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1499 const char *name, const char *val)
1501 const char *values[2];
1507 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1508 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1512 * Add an array of string values to a mod list
1513 * @param ctx An initialized TALLOC_CTX
1514 * @param mods An initialized ADS_MODLIST
1515 * @param name The attribute name to add
1516 * @param vals The array of string values to add - NULL means DELETE
1517 * @return ADS STATUS indicating success of add
1519 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1520 const char *name, const char **vals)
1523 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1524 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1525 name, (const void **) vals);
1530 * Add a single ber-encoded value to a mod list
1531 * @param ctx An initialized TALLOC_CTX
1532 * @param mods An initialized ADS_MODLIST
1533 * @param name The attribute name to add
1534 * @param val The value to add - NULL means DELETE
1535 * @return ADS STATUS indicating success of add
1537 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1538 const char *name, const struct berval *val)
1540 const struct berval *values[2];
1545 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1546 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1547 name, (const void **) values);
1552 * Perform an ldap modify
1553 * @param ads connection to ads server
1554 * @param mod_dn DistinguishedName to modify
1555 * @param mods list of modifications to perform
1556 * @return status of modify
1558 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1561 char *utf8_dn = NULL;
1562 size_t converted_size;
1564 this control is needed to modify that contains a currently
1565 non-existent attribute (but allowable for the object) to run
1567 LDAPControl PermitModify = {
1568 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1571 LDAPControl *controls[2];
1573 controls[0] = &PermitModify;
1576 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1577 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1580 /* find the end of the list, marked by NULL or -1 */
1581 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1582 /* make sure the end of the list is NULL */
1584 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1585 (LDAPMod **) mods, controls, NULL);
1586 TALLOC_FREE(utf8_dn);
1587 return ADS_ERROR(ret);
1591 * Perform an ldap add
1592 * @param ads connection to ads server
1593 * @param new_dn DistinguishedName to add
1594 * @param mods list of attributes and values for DN
1595 * @return status of add
1597 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1600 char *utf8_dn = NULL;
1601 size_t converted_size;
1603 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1604 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1605 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1608 /* find the end of the list, marked by NULL or -1 */
1609 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1610 /* make sure the end of the list is NULL */
1613 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1614 TALLOC_FREE(utf8_dn);
1615 return ADS_ERROR(ret);
1619 * Delete a DistinguishedName
1620 * @param ads connection to ads server
1621 * @param new_dn DistinguishedName to delete
1622 * @return status of delete
1624 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1627 char *utf8_dn = NULL;
1628 size_t converted_size;
1629 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1630 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1631 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1634 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1635 TALLOC_FREE(utf8_dn);
1636 return ADS_ERROR(ret);
1640 * Build an org unit string
1641 * if org unit is Computers or blank then assume a container, otherwise
1642 * assume a / separated list of organisational units.
1643 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1644 * @param ads connection to ads server
1645 * @param org_unit Organizational unit
1646 * @return org unit string - caller must free
1648 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1652 if (!org_unit || !*org_unit) {
1654 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1656 /* samba4 might not yet respond to a wellknownobject-query */
1657 return ret ? ret : SMB_STRDUP("cn=Computers");
1660 if (strequal(org_unit, "Computers")) {
1661 return SMB_STRDUP("cn=Computers");
1664 /* jmcd: removed "\\" from the separation chars, because it is
1665 needed as an escape for chars like '#' which are valid in an
1667 return ads_build_path(org_unit, "/", "ou=", 1);
1671 * Get a org unit string for a well-known GUID
1672 * @param ads connection to ads server
1673 * @param wknguid Well known GUID
1674 * @return org unit string - caller must free
1676 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1679 LDAPMessage *res = NULL;
1680 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1681 **bind_dn_exp = NULL;
1682 const char *attrs[] = {"distinguishedName", NULL};
1683 int new_ln, wkn_ln, bind_ln, i;
1685 if (wknguid == NULL) {
1689 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1690 DEBUG(1, ("asprintf failed!\n"));
1694 status = ads_search_dn(ads, &res, base, attrs);
1695 if (!ADS_ERR_OK(status)) {
1696 DEBUG(1,("Failed while searching for: %s\n", base));
1700 if (ads_count_replies(ads, res) != 1) {
1704 /* substitute the bind-path from the well-known-guid-search result */
1705 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1710 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1715 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1720 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1722 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1725 new_ln = wkn_ln - bind_ln;
1727 ret = SMB_STRDUP(wkn_dn_exp[0]);
1732 for (i=1; i < new_ln; i++) {
1735 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1741 ret = SMB_STRDUP(s);
1750 ads_msgfree(ads, res);
1751 TALLOC_FREE(wkn_dn);
1753 ldap_value_free(wkn_dn_exp);
1756 ldap_value_free(bind_dn_exp);
1763 * Adds (appends) an item to an attribute array, rather then
1764 * replacing the whole list
1765 * @param ctx An initialized TALLOC_CTX
1766 * @param mods An initialized ADS_MODLIST
1767 * @param name name of the ldap attribute to append to
1768 * @param vals an array of values to add
1769 * @return status of addition
1772 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1773 const char *name, const char **vals)
1775 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1776 (const void *) vals);
1780 * Determines the an account's current KVNO via an LDAP lookup
1781 * @param ads An initialized ADS_STRUCT
1782 * @param account_name the NT samaccountname.
1783 * @return the kvno for the account, or -1 in case of a failure.
1786 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1788 LDAPMessage *res = NULL;
1789 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1791 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1792 char *dn_string = NULL;
1793 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1795 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1796 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1799 ret = ads_search(ads, &res, filter, attrs);
1801 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1802 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1803 ads_msgfree(ads, res);
1807 dn_string = ads_get_dn(ads, talloc_tos(), res);
1809 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1810 ads_msgfree(ads, res);
1813 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1814 TALLOC_FREE(dn_string);
1816 /* ---------------------------------------------------------
1817 * 0 is returned as a default KVNO from this point on...
1818 * This is done because Windows 2000 does not support key
1819 * version numbers. Chances are that a failure in the next
1820 * step is simply due to Windows 2000 being used for a
1821 * domain controller. */
1824 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1825 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1826 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1827 ads_msgfree(ads, res);
1832 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1833 ads_msgfree(ads, res);
1838 * Determines the computer account's current KVNO via an LDAP lookup
1839 * @param ads An initialized ADS_STRUCT
1840 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1841 * @return the kvno for the computer account, or -1 in case of a failure.
1844 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1846 char *computer_account = NULL;
1849 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1853 kvno = ads_get_kvno(ads, computer_account);
1854 free(computer_account);
1860 * This clears out all registered spn's for a given hostname
1861 * @param ads An initilaized ADS_STRUCT
1862 * @param machine_name the NetBIOS name of the computer.
1863 * @return 0 upon success, non-zero otherwise.
1866 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1869 LDAPMessage *res = NULL;
1871 const char *servicePrincipalName[1] = {NULL};
1872 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1873 char *dn_string = NULL;
1875 ret = ads_find_machine_acct(ads, &res, machine_name);
1876 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1877 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1878 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1879 ads_msgfree(ads, res);
1880 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1883 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1884 ctx = talloc_init("ads_clear_service_principal_names");
1886 ads_msgfree(ads, res);
1887 return ADS_ERROR(LDAP_NO_MEMORY);
1890 if (!(mods = ads_init_mods(ctx))) {
1891 talloc_destroy(ctx);
1892 ads_msgfree(ads, res);
1893 return ADS_ERROR(LDAP_NO_MEMORY);
1895 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1896 if (!ADS_ERR_OK(ret)) {
1897 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1898 ads_msgfree(ads, res);
1899 talloc_destroy(ctx);
1902 dn_string = ads_get_dn(ads, talloc_tos(), res);
1904 talloc_destroy(ctx);
1905 ads_msgfree(ads, res);
1906 return ADS_ERROR(LDAP_NO_MEMORY);
1908 ret = ads_gen_mod(ads, dn_string, mods);
1909 TALLOC_FREE(dn_string);
1910 if (!ADS_ERR_OK(ret)) {
1911 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1913 ads_msgfree(ads, res);
1914 talloc_destroy(ctx);
1918 ads_msgfree(ads, res);
1919 talloc_destroy(ctx);
1924 * @brief Search for an element in a string array.
1926 * @param[in] el_array The string array to search.
1928 * @param[in] num_el The number of elements in the string array.
1930 * @param[in] el The string to search.
1932 * @return True if found, false if not.
1934 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1938 if (el_array == NULL || num_el == 0 || el == NULL) {
1942 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1945 cmp = strcasecmp_m(el_array[i], el);
1955 * @brief This gets the service principal names of an existing computer account.
1957 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1959 * @param[in] ads The ADS context to use.
1961 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1962 * identify the computer account.
1964 * @param[in] spn_array A pointer to store the array for SPNs.
1966 * @param[in] num_spns The number of principals stored in the array.
1968 * @return 0 on success, or a ADS error if a failure occured.
1970 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1972 const char *machine_name,
1977 LDAPMessage *res = NULL;
1981 status = ads_find_machine_acct(ads,
1984 if (!ADS_ERR_OK(status)) {
1985 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1990 count = ads_count_replies(ads, res);
1992 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1996 dn = ads_get_dn(ads, mem_ctx, res);
1998 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2002 *spn_array = ads_pull_strings(ads,
2005 "servicePrincipalName",
2007 if (*spn_array == NULL) {
2008 DEBUG(1, ("Host account for %s does not have service principal "
2011 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2016 ads_msgfree(ads, res);
2022 * This adds a service principal name to an existing computer account
2023 * (found by hostname) in AD.
2024 * @param ads An initialized ADS_STRUCT
2025 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2026 * @param my_fqdn The fully qualified DNS name of the machine
2027 * @param spn A string of the service principal to add, i.e. 'host'
2028 * @return 0 upon sucess, or non-zero if a failure occurs
2031 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
2032 const char *my_fqdn, const char *spn)
2036 LDAPMessage *res = NULL;
2039 char *dn_string = NULL;
2040 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
2042 ret = ads_find_machine_acct(ads, &res, machine_name);
2043 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2044 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2046 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2047 spn, machine_name, ads->config.realm));
2048 ads_msgfree(ads, res);
2049 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2052 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2053 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2054 ads_msgfree(ads, res);
2055 return ADS_ERROR(LDAP_NO_MEMORY);
2058 /* add short name spn */
2060 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2061 talloc_destroy(ctx);
2062 ads_msgfree(ads, res);
2063 return ADS_ERROR(LDAP_NO_MEMORY);
2065 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2066 ret = ADS_ERROR(LDAP_NO_MEMORY);
2069 servicePrincipalName[0] = psp1;
2071 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2072 psp1, machine_name));
2075 /* add fully qualified spn */
2077 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2078 ret = ADS_ERROR(LDAP_NO_MEMORY);
2081 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2082 ret = ADS_ERROR(LDAP_NO_MEMORY);
2085 servicePrincipalName[1] = psp2;
2087 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2088 psp2, machine_name));
2090 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2091 ret = ADS_ERROR(LDAP_NO_MEMORY);
2095 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2096 if (!ADS_ERR_OK(ret)) {
2097 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2101 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2102 ret = ADS_ERROR(LDAP_NO_MEMORY);
2106 ret = ads_gen_mod(ads, dn_string, mods);
2107 if (!ADS_ERR_OK(ret)) {
2108 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2114 ads_msgfree(ads, res);
2119 * adds a machine account to the ADS server
2120 * @param ads An intialized ADS_STRUCT
2121 * @param machine_name - the NetBIOS machine name of this account.
2122 * @param account_type A number indicating the type of account to create
2123 * @param org_unit The LDAP path in which to place this account
2124 * @return 0 upon success, or non-zero otherwise
2127 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2128 const char *org_unit)
2131 char *samAccountName, *controlstr;
2134 char *machine_escaped = NULL;
2136 const char *objectClass[] = {"top", "person", "organizationalPerson",
2137 "user", "computer", NULL};
2138 LDAPMessage *res = NULL;
2139 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2140 UF_DONT_EXPIRE_PASSWD |\
2141 UF_ACCOUNTDISABLE );
2143 if (!(ctx = talloc_init("ads_add_machine_acct")))
2144 return ADS_ERROR(LDAP_NO_MEMORY);
2146 ret = ADS_ERROR(LDAP_NO_MEMORY);
2148 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2149 if (!machine_escaped) {
2153 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2154 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2156 if ( !new_dn || !samAccountName ) {
2160 #ifndef ENCTYPE_ARCFOUR_HMAC
2161 acct_control |= UF_USE_DES_KEY_ONLY;
2164 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2168 if (!(mods = ads_init_mods(ctx))) {
2172 ads_mod_str(ctx, &mods, "cn", machine_name);
2173 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2174 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2175 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2177 ret = ads_gen_add(ads, new_dn, mods);
2180 SAFE_FREE(machine_escaped);
2181 ads_msgfree(ads, res);
2182 talloc_destroy(ctx);
2188 * move a machine account to another OU on the ADS server
2189 * @param ads - An intialized ADS_STRUCT
2190 * @param machine_name - the NetBIOS machine name of this account.
2191 * @param org_unit - The LDAP path in which to place this account
2192 * @param moved - whether we moved the machine account (optional)
2193 * @return 0 upon success, or non-zero otherwise
2196 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2197 const char *org_unit, bool *moved)
2201 LDAPMessage *res = NULL;
2202 char *filter = NULL;
2203 char *computer_dn = NULL;
2205 char *computer_rdn = NULL;
2206 bool need_move = False;
2208 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2209 rc = ADS_ERROR(LDAP_NO_MEMORY);
2213 /* Find pre-existing machine */
2214 rc = ads_search(ads, &res, filter, NULL);
2215 if (!ADS_ERR_OK(rc)) {
2219 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2221 rc = ADS_ERROR(LDAP_NO_MEMORY);
2225 parent_dn = ads_parent_dn(computer_dn);
2226 if (strequal(parent_dn, org_unit)) {
2232 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2233 rc = ADS_ERROR(LDAP_NO_MEMORY);
2237 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2238 org_unit, 1, NULL, NULL);
2239 rc = ADS_ERROR(ldap_status);
2242 ads_msgfree(ads, res);
2244 TALLOC_FREE(computer_dn);
2245 SAFE_FREE(computer_rdn);
2247 if (!ADS_ERR_OK(rc)) {
2259 dump a binary result from ldap
2261 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2264 for (i=0; values[i]; i++) {
2265 printf("%s: ", field);
2266 for (j=0; j<values[i]->bv_len; j++) {
2267 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2273 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2276 for (i=0; values[i]; i++) {
2278 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2281 status = GUID_from_ndr_blob(&in, &guid);
2282 if (NT_STATUS_IS_OK(status)) {
2283 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2285 printf("%s: INVALID GUID\n", field);
2291 dump a sid result from ldap
2293 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2296 for (i=0; values[i]; i++) {
2299 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2302 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2307 dump ntSecurityDescriptor
2309 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2311 TALLOC_CTX *frame = talloc_stackframe();
2312 struct security_descriptor *psd;
2315 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2316 values[0]->bv_len, &psd);
2317 if (!NT_STATUS_IS_OK(status)) {
2318 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2319 nt_errstr(status)));
2325 ads_disp_sd(ads, talloc_tos(), psd);
2332 dump a string result from ldap
2334 static void dump_string(const char *field, char **values)
2337 for (i=0; values[i]; i++) {
2338 printf("%s: %s\n", field, values[i]);
2343 dump a field from LDAP on stdout
2347 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2352 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2354 {"objectGUID", False, dump_guid},
2355 {"netbootGUID", False, dump_guid},
2356 {"nTSecurityDescriptor", False, dump_sd},
2357 {"dnsRecord", False, dump_binary},
2358 {"objectSid", False, dump_sid},
2359 {"tokenGroups", False, dump_sid},
2360 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2361 {"tokengroupsGlobalandUniversal", False, dump_sid},
2362 {"mS-DS-CreatorSID", False, dump_sid},
2363 {"msExchMailboxGuid", False, dump_guid},
2368 if (!field) { /* must be end of an entry */
2373 for (i=0; handlers[i].name; i++) {
2374 if (strcasecmp_m(handlers[i].name, field) == 0) {
2375 if (!values) /* first time, indicate string or not */
2376 return handlers[i].string;
2377 handlers[i].handler(ads, field, (struct berval **) values);
2381 if (!handlers[i].name) {
2382 if (!values) /* first time, indicate string conversion */
2384 dump_string(field, (char **)values);
2390 * Dump a result from LDAP on stdout
2391 * used for debugging
2392 * @param ads connection to ads server
2393 * @param res Results to dump
2396 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2398 ads_process_results(ads, res, ads_dump_field, NULL);
2402 * Walk through results, calling a function for each entry found.
2403 * The function receives a field name, a berval * array of values,
2404 * and a data area passed through from the start. The function is
2405 * called once with null for field and values at the end of each
2407 * @param ads connection to ads server
2408 * @param res Results to process
2409 * @param fn Function for processing each result
2410 * @param data_area user-defined area to pass to function
2412 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2413 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2418 size_t converted_size;
2420 if (!(ctx = talloc_init("ads_process_results")))
2423 for (msg = ads_first_entry(ads, res); msg;
2424 msg = ads_next_entry(ads, msg)) {
2428 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2429 (LDAPMessage *)msg,&b);
2431 utf8_field=ldap_next_attribute(ads->ldap.ld,
2432 (LDAPMessage *)msg,b)) {
2433 struct berval **ber_vals;
2434 char **str_vals, **utf8_vals;
2438 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2441 DEBUG(0,("ads_process_results: "
2442 "pull_utf8_talloc failed: %s",
2446 string = fn(ads, field, NULL, data_area);
2449 utf8_vals = ldap_get_values(ads->ldap.ld,
2450 (LDAPMessage *)msg, field);
2451 str_vals = ads_pull_strvals(ctx,
2452 (const char **) utf8_vals);
2453 fn(ads, field, (void **) str_vals, data_area);
2454 ldap_value_free(utf8_vals);
2456 ber_vals = ldap_get_values_len(ads->ldap.ld,
2457 (LDAPMessage *)msg, field);
2458 fn(ads, field, (void **) ber_vals, data_area);
2460 ldap_value_free_len(ber_vals);
2462 ldap_memfree(utf8_field);
2465 talloc_free_children(ctx);
2466 fn(ads, NULL, NULL, data_area); /* completed an entry */
2469 talloc_destroy(ctx);
2473 * count how many replies are in a LDAPMessage
2474 * @param ads connection to ads server
2475 * @param res Results to count
2476 * @return number of replies
2478 int ads_count_replies(ADS_STRUCT *ads, void *res)
2480 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2484 * pull the first entry from a ADS result
2485 * @param ads connection to ads server
2486 * @param res Results of search
2487 * @return first entry from result
2489 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2491 return ldap_first_entry(ads->ldap.ld, res);
2495 * pull the next entry from a ADS result
2496 * @param ads connection to ads server
2497 * @param res Results of search
2498 * @return next entry from result
2500 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2502 return ldap_next_entry(ads->ldap.ld, res);
2506 * pull the first message from a ADS result
2507 * @param ads connection to ads server
2508 * @param res Results of search
2509 * @return first message from result
2511 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2513 return ldap_first_message(ads->ldap.ld, res);
2517 * pull the next message from a ADS result
2518 * @param ads connection to ads server
2519 * @param res Results of search
2520 * @return next message from result
2522 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2524 return ldap_next_message(ads->ldap.ld, res);
2528 * pull a single string from a ADS result
2529 * @param ads connection to ads server
2530 * @param mem_ctx TALLOC_CTX to use for allocating result string
2531 * @param msg Results of search
2532 * @param field Attribute to retrieve
2533 * @return Result string in talloc context
2535 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2541 size_t converted_size;
2543 values = ldap_get_values(ads->ldap.ld, msg, field);
2547 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2552 ldap_value_free(values);
2557 * pull an array of strings from a ADS result
2558 * @param ads connection to ads server
2559 * @param mem_ctx TALLOC_CTX to use for allocating result string
2560 * @param msg Results of search
2561 * @param field Attribute to retrieve
2562 * @return Result strings in talloc context
2564 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2565 LDAPMessage *msg, const char *field,
2571 size_t converted_size;
2573 values = ldap_get_values(ads->ldap.ld, msg, field);
2577 *num_values = ldap_count_values(values);
2579 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2581 ldap_value_free(values);
2585 for (i=0;i<*num_values;i++) {
2586 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2589 ldap_value_free(values);
2595 ldap_value_free(values);
2600 * pull an array of strings from a ADS result
2601 * (handle large multivalue attributes with range retrieval)
2602 * @param ads connection to ads server
2603 * @param mem_ctx TALLOC_CTX to use for allocating result string
2604 * @param msg Results of search
2605 * @param field Attribute to retrieve
2606 * @param current_strings strings returned by a previous call to this function
2607 * @param next_attribute The next query should ask for this attribute
2608 * @param num_values How many values did we get this time?
2609 * @param more_values Are there more values to get?
2610 * @return Result strings in talloc context
2612 char **ads_pull_strings_range(ADS_STRUCT *ads,
2613 TALLOC_CTX *mem_ctx,
2614 LDAPMessage *msg, const char *field,
2615 char **current_strings,
2616 const char **next_attribute,
2617 size_t *num_strings,
2621 char *expected_range_attrib, *range_attr;
2622 BerElement *ptr = NULL;
2625 size_t num_new_strings;
2626 unsigned long int range_start;
2627 unsigned long int range_end;
2629 /* we might have been given the whole lot anyway */
2630 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2631 *more_strings = False;
2635 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2637 /* look for Range result */
2638 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2640 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2641 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2642 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2650 /* nothing here - this field is just empty */
2651 *more_strings = False;
2655 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2656 &range_start, &range_end) == 2) {
2657 *more_strings = True;
2659 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2660 &range_start) == 1) {
2661 *more_strings = False;
2663 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2665 ldap_memfree(range_attr);
2666 *more_strings = False;
2671 if ((*num_strings) != range_start) {
2672 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2673 " - aborting range retreival\n",
2674 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2675 ldap_memfree(range_attr);
2676 *more_strings = False;
2680 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2682 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2683 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2684 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2685 range_attr, (unsigned long int)range_end - range_start + 1,
2686 (unsigned long int)num_new_strings));
2687 ldap_memfree(range_attr);
2688 *more_strings = False;
2692 strings = talloc_realloc(mem_ctx, current_strings, char *,
2693 *num_strings + num_new_strings);
2695 if (strings == NULL) {
2696 ldap_memfree(range_attr);
2697 *more_strings = False;
2701 if (new_strings && num_new_strings) {
2702 memcpy(&strings[*num_strings], new_strings,
2703 sizeof(*new_strings) * num_new_strings);
2706 (*num_strings) += num_new_strings;
2708 if (*more_strings) {
2709 *next_attribute = talloc_asprintf(mem_ctx,
2714 if (!*next_attribute) {
2715 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2716 ldap_memfree(range_attr);
2717 *more_strings = False;
2722 ldap_memfree(range_attr);
2728 * pull a single uint32 from a ADS result
2729 * @param ads connection to ads server
2730 * @param msg Results of search
2731 * @param field Attribute to retrieve
2732 * @param v Pointer to int to store result
2733 * @return boolean inidicating success
2735 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2740 values = ldap_get_values(ads->ldap.ld, msg, field);
2744 ldap_value_free(values);
2748 *v = atoi(values[0]);
2749 ldap_value_free(values);
2754 * pull a single objectGUID from an ADS result
2755 * @param ads connection to ADS server
2756 * @param msg results of search
2757 * @param guid 37-byte area to receive text guid
2758 * @return boolean indicating success
2760 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2765 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2770 status = GUID_from_ndr_blob(&blob, guid);
2771 talloc_free(blob.data);
2772 return NT_STATUS_IS_OK(status);
2777 * pull a single struct dom_sid from a ADS result
2778 * @param ads connection to ads server
2779 * @param msg Results of search
2780 * @param field Attribute to retrieve
2781 * @param sid Pointer to sid to store result
2782 * @return boolean inidicating success
2784 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2785 struct dom_sid *sid)
2787 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2791 * pull an array of struct dom_sids from a ADS result
2792 * @param ads connection to ads server
2793 * @param mem_ctx TALLOC_CTX for allocating sid array
2794 * @param msg Results of search
2795 * @param field Attribute to retrieve
2796 * @param sids pointer to sid array to allocate
2797 * @return the count of SIDs pulled
2799 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2800 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2802 struct berval **values;
2806 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2811 for (i=0; values[i]; i++)
2815 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2817 ldap_value_free_len(values);
2825 for (i=0; values[i]; i++) {
2826 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2828 DEBUG(10, ("pulling SID: %s\n",
2829 sid_string_dbg(&(*sids)[count])));
2834 ldap_value_free_len(values);
2839 * pull a struct security_descriptor from a ADS result
2840 * @param ads connection to ads server
2841 * @param mem_ctx TALLOC_CTX for allocating sid array
2842 * @param msg Results of search
2843 * @param field Attribute to retrieve
2844 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2845 * @return boolean inidicating success
2847 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2848 LDAPMessage *msg, const char *field,
2849 struct security_descriptor **sd)
2851 struct berval **values;
2854 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2856 if (!values) return false;
2860 status = unmarshall_sec_desc(mem_ctx,
2861 (uint8 *)values[0]->bv_val,
2862 values[0]->bv_len, sd);
2863 if (!NT_STATUS_IS_OK(status)) {
2864 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2865 nt_errstr(status)));
2870 ldap_value_free_len(values);
2875 * in order to support usernames longer than 21 characters we need to
2876 * use both the sAMAccountName and the userPrincipalName attributes
2877 * It seems that not all users have the userPrincipalName attribute set
2879 * @param ads connection to ads server
2880 * @param mem_ctx TALLOC_CTX for allocating sid array
2881 * @param msg Results of search
2882 * @return the username
2884 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2890 /* lookup_name() only works on the sAMAccountName to
2891 returning the username portion of userPrincipalName
2892 breaks winbindd_getpwnam() */
2894 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2895 if (ret && (p = strchr_m(ret, '@'))) {
2900 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2905 * find the update serial number - this is the core of the ldap cache
2906 * @param ads connection to ads server
2907 * @param ads connection to ADS server
2908 * @param usn Pointer to retrieved update serial number
2909 * @return status of search
2911 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2913 const char *attrs[] = {"highestCommittedUSN", NULL};
2917 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2918 if (!ADS_ERR_OK(status))
2921 if (ads_count_replies(ads, res) != 1) {
2922 ads_msgfree(ads, res);
2923 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2926 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2927 ads_msgfree(ads, res);
2928 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2931 ads_msgfree(ads, res);
2935 /* parse a ADS timestring - typical string is
2936 '20020917091222.0Z0' which means 09:12.22 17th September
2938 static time_t ads_parse_time(const char *str)
2944 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2945 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2946 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2955 /********************************************************************
2956 ********************************************************************/
2958 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2960 const char *attrs[] = {"currentTime", NULL};
2965 ADS_STRUCT *ads_s = ads;
2967 if (!(ctx = talloc_init("ads_current_time"))) {
2968 return ADS_ERROR(LDAP_NO_MEMORY);
2971 /* establish a new ldap tcp session if necessary */
2973 if ( !ads->ldap.ld ) {
2974 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2975 ads->server.ldap_server )) == NULL )
2979 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2980 status = ads_connect( ads_s );
2981 if ( !ADS_ERR_OK(status))
2985 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2986 if (!ADS_ERR_OK(status)) {
2990 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2992 ads_msgfree(ads_s, res);
2993 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2997 /* but save the time and offset in the original ADS_STRUCT */
2999 ads->config.current_time = ads_parse_time(timestr);
3001 if (ads->config.current_time != 0) {
3002 ads->auth.time_offset = ads->config.current_time - time(NULL);
3003 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3006 ads_msgfree(ads, res);
3008 status = ADS_SUCCESS;
3011 /* free any temporary ads connections */
3012 if ( ads_s != ads ) {
3013 ads_destroy( &ads_s );
3015 talloc_destroy(ctx);
3020 /********************************************************************
3021 ********************************************************************/
3023 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
3025 const char *attrs[] = {"domainFunctionality", NULL};
3028 ADS_STRUCT *ads_s = ads;
3030 *val = DS_DOMAIN_FUNCTION_2000;
3032 /* establish a new ldap tcp session if necessary */
3034 if ( !ads->ldap.ld ) {
3035 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3036 ads->server.ldap_server )) == NULL )
3038 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3041 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3042 status = ads_connect( ads_s );
3043 if ( !ADS_ERR_OK(status))
3047 /* If the attribute does not exist assume it is a Windows 2000
3048 functional domain */
3050 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3051 if (!ADS_ERR_OK(status)) {
3052 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3053 status = ADS_SUCCESS;
3058 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3059 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3061 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3064 ads_msgfree(ads, res);
3067 /* free any temporary ads connections */
3068 if ( ads_s != ads ) {
3069 ads_destroy( &ads_s );
3076 * find the domain sid for our domain
3077 * @param ads connection to ads server
3078 * @param sid Pointer to domain sid
3079 * @return status of search
3081 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3083 const char *attrs[] = {"objectSid", NULL};
3087 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3089 if (!ADS_ERR_OK(rc)) return rc;
3090 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3091 ads_msgfree(ads, res);
3092 return ADS_ERROR_SYSTEM(ENOENT);
3094 ads_msgfree(ads, res);
3100 * find our site name
3101 * @param ads connection to ads server
3102 * @param mem_ctx Pointer to talloc context
3103 * @param site_name Pointer to the sitename
3104 * @return status of search
3106 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3110 const char *dn, *service_name;
3111 const char *attrs[] = { "dsServiceName", NULL };
3113 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3114 if (!ADS_ERR_OK(status)) {
3118 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3119 if (service_name == NULL) {
3120 ads_msgfree(ads, res);
3121 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3124 ads_msgfree(ads, res);
3126 /* go up three levels */
3127 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3129 return ADS_ERROR(LDAP_NO_MEMORY);
3132 *site_name = talloc_strdup(mem_ctx, dn);
3133 if (*site_name == NULL) {
3134 return ADS_ERROR(LDAP_NO_MEMORY);
3139 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3144 * find the site dn where a machine resides
3145 * @param ads connection to ads server
3146 * @param mem_ctx Pointer to talloc context
3147 * @param computer_name name of the machine
3148 * @param site_name Pointer to the sitename
3149 * @return status of search
3151 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3155 const char *parent, *filter;
3156 char *config_context = NULL;
3159 /* shortcut a query */
3160 if (strequal(computer_name, ads->config.ldap_server_name)) {
3161 return ads_site_dn(ads, mem_ctx, site_dn);
3164 status = ads_config_path(ads, mem_ctx, &config_context);
3165 if (!ADS_ERR_OK(status)) {
3169 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3170 if (filter == NULL) {
3171 return ADS_ERROR(LDAP_NO_MEMORY);
3174 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3175 filter, NULL, &res);
3176 if (!ADS_ERR_OK(status)) {
3180 if (ads_count_replies(ads, res) != 1) {
3181 ads_msgfree(ads, res);
3182 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3185 dn = ads_get_dn(ads, mem_ctx, res);
3187 ads_msgfree(ads, res);
3188 return ADS_ERROR(LDAP_NO_MEMORY);
3191 /* go up three levels */
3192 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3193 if (parent == NULL) {
3194 ads_msgfree(ads, res);
3196 return ADS_ERROR(LDAP_NO_MEMORY);
3199 *site_dn = talloc_strdup(mem_ctx, parent);
3200 if (*site_dn == NULL) {
3201 ads_msgfree(ads, res);
3203 return ADS_ERROR(LDAP_NO_MEMORY);
3207 ads_msgfree(ads, res);
3213 * get the upn suffixes for a domain
3214 * @param ads connection to ads server
3215 * @param mem_ctx Pointer to talloc context
3216 * @param suffixes Pointer to an array of suffixes
3217 * @param num_suffixes Pointer to the number of suffixes
3218 * @return status of search
3220 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3225 char *config_context = NULL;
3226 const char *attrs[] = { "uPNSuffixes", NULL };
3228 status = ads_config_path(ads, mem_ctx, &config_context);
3229 if (!ADS_ERR_OK(status)) {
3233 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3235 return ADS_ERROR(LDAP_NO_MEMORY);
3238 status = ads_search_dn(ads, &res, base, attrs);
3239 if (!ADS_ERR_OK(status)) {
3243 if (ads_count_replies(ads, res) != 1) {
3244 ads_msgfree(ads, res);
3245 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3248 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3249 if ((*suffixes) == NULL) {
3250 ads_msgfree(ads, res);
3251 return ADS_ERROR(LDAP_NO_MEMORY);
3254 ads_msgfree(ads, res);
3260 * get the joinable ous for a domain
3261 * @param ads connection to ads server
3262 * @param mem_ctx Pointer to talloc context
3263 * @param ous Pointer to an array of ous
3264 * @param num_ous Pointer to the number of ous
3265 * @return status of search
3267 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3268 TALLOC_CTX *mem_ctx,
3273 LDAPMessage *res = NULL;
3274 LDAPMessage *msg = NULL;
3275 const char *attrs[] = { "dn", NULL };
3278 status = ads_search(ads, &res,
3279 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3281 if (!ADS_ERR_OK(status)) {
3285 count = ads_count_replies(ads, res);
3287 ads_msgfree(ads, res);
3288 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3291 for (msg = ads_first_entry(ads, res); msg;
3292 msg = ads_next_entry(ads, msg)) {
3296 dn = ads_get_dn(ads, talloc_tos(), msg);
3298 ads_msgfree(ads, res);
3299 return ADS_ERROR(LDAP_NO_MEMORY);
3302 if (!add_string_to_array(mem_ctx, dn,
3303 (const char ***)ous,
3306 ads_msgfree(ads, res);
3307 return ADS_ERROR(LDAP_NO_MEMORY);
3313 ads_msgfree(ads, res);
3320 * pull a struct dom_sid from an extended dn string
3321 * @param mem_ctx TALLOC_CTX
3322 * @param extended_dn string
3323 * @param flags string type of extended_dn
3324 * @param sid pointer to a struct dom_sid
3325 * @return NT_STATUS_OK on success,
3326 * NT_INVALID_PARAMETER on error,
3327 * NT_STATUS_NOT_FOUND if no SID present
3329 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3330 const char *extended_dn,
3331 enum ads_extended_dn_flags flags,
3332 struct dom_sid *sid)
3337 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3340 /* otherwise extended_dn gets stripped off */
3341 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3342 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3345 * ADS_EXTENDED_DN_HEX_STRING:
3346 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3348 * ADS_EXTENDED_DN_STRING (only with w2k3):
3349 * <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
3351 * Object with no SID, such as an Exchange Public Folder
3352 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3355 p = strchr(dn, ';');
3357 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3360 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3361 DEBUG(5,("No SID present in extended dn\n"));
3362 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3365 p += strlen(";<SID=");
3369 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3374 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3378 case ADS_EXTENDED_DN_STRING:
3379 if (!string_to_sid(sid, p)) {
3380 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3383 case ADS_EXTENDED_DN_HEX_STRING: {
3387 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3389 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3392 if (!sid_parse(buf, buf_len, sid)) {
3393 DEBUG(10,("failed to parse sid\n"));
3394 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3399 DEBUG(10,("unknown extended dn format\n"));
3400 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3403 return ADS_ERROR_NT(NT_STATUS_OK);
3406 /********************************************************************
3407 ********************************************************************/
3409 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3411 LDAPMessage *res = NULL;
3416 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3417 if (!ADS_ERR_OK(status)) {
3418 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3419 lp_netbios_name()));
3423 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3424 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3428 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3429 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3433 ads_msgfree(ads, res);
3438 /********************************************************************
3439 ********************************************************************/
3441 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3443 LDAPMessage *res = NULL;
3448 status = ads_find_machine_acct(ads, &res, machine_name);
3449 if (!ADS_ERR_OK(status)) {
3450 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3451 lp_netbios_name()));
3455 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3456 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3460 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3461 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3465 ads_msgfree(ads, res);
3470 /********************************************************************
3471 ********************************************************************/
3473 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3475 LDAPMessage *res = NULL;
3480 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3481 if (!ADS_ERR_OK(status)) {
3482 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3483 lp_netbios_name()));
3487 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3488 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3492 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3493 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3497 ads_msgfree(ads, res);
3504 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3507 * Join a machine to a realm
3508 * Creates the machine account and sets the machine password
3509 * @param ads connection to ads server
3510 * @param machine name of host to add
3511 * @param org_unit Organizational unit to place machine in
3512 * @return status of join
3514 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3515 uint32 account_type, const char *org_unit)
3518 LDAPMessage *res = NULL;
3521 /* machine name must be lowercase */
3522 machine = SMB_STRDUP(machine_name);
3523 strlower_m(machine);
3526 status = ads_find_machine_acct(ads, (void **)&res, machine);
3527 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3528 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3529 status = ads_leave_realm(ads, machine);
3530 if (!ADS_ERR_OK(status)) {
3531 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3532 machine, ads->config.realm));
3537 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3538 if (!ADS_ERR_OK(status)) {
3539 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3544 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3545 if (!ADS_ERR_OK(status)) {
3546 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3552 ads_msgfree(ads, res);
3559 * Delete a machine from the realm
3560 * @param ads connection to ads server
3561 * @param hostname Machine to remove
3562 * @return status of delete
3564 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3569 char *hostnameDN, *host;
3571 LDAPControl ldap_control;
3572 LDAPControl * pldap_control[2] = {NULL, NULL};
3574 pldap_control[0] = &ldap_control;
3575 memset(&ldap_control, 0, sizeof(LDAPControl));
3576 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3578 /* hostname must be lowercase */
3579 host = SMB_STRDUP(hostname);
3580 if (!strlower_m(host)) {
3582 return ADS_ERROR_SYSTEM(EINVAL);
3585 status = ads_find_machine_acct(ads, &res, host);
3586 if (!ADS_ERR_OK(status)) {
3587 DEBUG(0, ("Host account for %s does not exist.\n", host));
3592 msg = ads_first_entry(ads, res);
3595 return ADS_ERROR_SYSTEM(ENOENT);
3598 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3599 if (hostnameDN == NULL) {
3601 return ADS_ERROR_SYSTEM(ENOENT);
3604 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3606 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3608 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3611 if (rc != LDAP_SUCCESS) {
3612 const char *attrs[] = { "cn", NULL };
3613 LDAPMessage *msg_sub;
3615 /* we only search with scope ONE, we do not expect any further
3616 * objects to be created deeper */
3618 status = ads_do_search_retry(ads, hostnameDN,
3619 LDAP_SCOPE_ONELEVEL,
3620 "(objectclass=*)", attrs, &res);
3622 if (!ADS_ERR_OK(status)) {
3624 TALLOC_FREE(hostnameDN);
3628 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3629 msg_sub = ads_next_entry(ads, msg_sub)) {
3633 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3635 TALLOC_FREE(hostnameDN);
3636 return ADS_ERROR(LDAP_NO_MEMORY);
3639 status = ads_del_dn(ads, dn);
3640 if (!ADS_ERR_OK(status)) {
3641 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3644 TALLOC_FREE(hostnameDN);
3651 /* there should be no subordinate objects anymore */
3652 status = ads_do_search_retry(ads, hostnameDN,
3653 LDAP_SCOPE_ONELEVEL,
3654 "(objectclass=*)", attrs, &res);
3656 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3658 TALLOC_FREE(hostnameDN);
3662 /* delete hostnameDN now */
3663 status = ads_del_dn(ads, hostnameDN);
3664 if (!ADS_ERR_OK(status)) {
3666 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3667 TALLOC_FREE(hostnameDN);
3672 TALLOC_FREE(hostnameDN);
3674 status = ads_find_machine_acct(ads, &res, host);
3675 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3676 DEBUG(3, ("Failed to remove host account.\n"));
3686 * pull all token-sids from an LDAP dn
3687 * @param ads connection to ads server
3688 * @param mem_ctx TALLOC_CTX for allocating sid array
3689 * @param dn of LDAP object
3690 * @param user_sid pointer to struct dom_sid (objectSid)
3691 * @param primary_group_sid pointer to struct dom_sid (self composed)
3692 * @param sids pointer to sid array to allocate
3693 * @param num_sids counter of SIDs pulled
3694 * @return status of token query
3696 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3697 TALLOC_CTX *mem_ctx,
3699 struct dom_sid *user_sid,
3700 struct dom_sid *primary_group_sid,
3701 struct dom_sid **sids,
3705 LDAPMessage *res = NULL;
3707 size_t tmp_num_sids;
3708 struct dom_sid *tmp_sids;
3709 struct dom_sid tmp_user_sid;
3710 struct dom_sid tmp_primary_group_sid;
3712 const char *attrs[] = {
3719 status = ads_search_retry_dn(ads, &res, dn, attrs);
3720 if (!ADS_ERR_OK(status)) {
3724 count = ads_count_replies(ads, res);
3726 ads_msgfree(ads, res);
3727 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3730 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3731 ads_msgfree(ads, res);
3732 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3735 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3736 ads_msgfree(ads, res);
3737 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3741 /* hack to compose the primary group sid without knowing the
3744 struct dom_sid domsid;
3746 sid_copy(&domsid, &tmp_user_sid);
3748 if (!sid_split_rid(&domsid, NULL)) {
3749 ads_msgfree(ads, res);
3750 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3753 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3754 ads_msgfree(ads, res);
3755 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3759 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3761 if (tmp_num_sids == 0 || !tmp_sids) {
3762 ads_msgfree(ads, res);
3763 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3767 *num_sids = tmp_num_sids;
3775 *user_sid = tmp_user_sid;
3778 if (primary_group_sid) {
3779 *primary_group_sid = tmp_primary_group_sid;
3782 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3784 ads_msgfree(ads, res);
3785 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3789 * Find a sAMAccoutName in LDAP
3790 * @param ads connection to ads server
3791 * @param mem_ctx TALLOC_CTX for allocating sid array
3792 * @param samaccountname to search
3793 * @param uac_ret uint32 pointer userAccountControl attribute value
3794 * @param dn_ret pointer to dn
3795 * @return status of token query
3797 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3798 TALLOC_CTX *mem_ctx,
3799 const char *samaccountname,
3801 const char **dn_ret)
3804 const char *attrs[] = { "userAccountControl", NULL };
3806 LDAPMessage *res = NULL;
3810 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3812 if (filter == NULL) {
3813 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3817 status = ads_do_search_all(ads, ads->config.bind_path,
3819 filter, attrs, &res);
3821 if (!ADS_ERR_OK(status)) {
3825 if (ads_count_replies(ads, res) != 1) {
3826 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3830 dn = ads_get_dn(ads, talloc_tos(), res);
3832 status = ADS_ERROR(LDAP_NO_MEMORY);
3836 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3837 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3846 *dn_ret = talloc_strdup(mem_ctx, dn);
3848 status = ADS_ERROR(LDAP_NO_MEMORY);
3854 ads_msgfree(ads, res);
3860 * find our configuration path
3861 * @param ads connection to ads server
3862 * @param mem_ctx Pointer to talloc context
3863 * @param config_path Pointer to the config path
3864 * @return status of search
3866 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3867 TALLOC_CTX *mem_ctx,
3871 LDAPMessage *res = NULL;
3872 const char *config_context = NULL;
3873 const char *attrs[] = { "configurationNamingContext", NULL };
3875 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3876 "(objectclass=*)", attrs, &res);
3877 if (!ADS_ERR_OK(status)) {
3881 config_context = ads_pull_string(ads, mem_ctx, res,
3882 "configurationNamingContext");
3883 ads_msgfree(ads, res);
3884 if (!config_context) {
3885 return ADS_ERROR(LDAP_NO_MEMORY);
3889 *config_path = talloc_strdup(mem_ctx, config_context);
3890 if (!*config_path) {
3891 return ADS_ERROR(LDAP_NO_MEMORY);
3895 return ADS_ERROR(LDAP_SUCCESS);
3899 * find the displayName of an extended right
3900 * @param ads connection to ads server
3901 * @param config_path The config path
3902 * @param mem_ctx Pointer to talloc context
3903 * @param GUID struct of the rightsGUID
3904 * @return status of search
3906 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3907 const char *config_path,
3908 TALLOC_CTX *mem_ctx,
3909 const struct GUID *rights_guid)
3912 LDAPMessage *res = NULL;
3914 const char *attrs[] = { "displayName", NULL };
3915 const char *result = NULL;
3918 if (!ads || !mem_ctx || !rights_guid) {
3922 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3923 GUID_string(mem_ctx, rights_guid));
3928 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3933 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3935 if (!ADS_ERR_OK(rc)) {
3939 if (ads_count_replies(ads, res) != 1) {
3943 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3946 ads_msgfree(ads, res);
3951 * verify or build and verify an account ou
3952 * @param mem_ctx Pointer to talloc context
3953 * @param ads connection to ads server
3955 * @return status of search
3958 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3960 const char **account_ou)
3966 exploded_dn = ldap_explode_dn(*account_ou, 0);
3968 ldap_value_free(exploded_dn);
3972 ou_string = ads_ou_string(ads, *account_ou);
3974 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3977 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3978 ads->config.bind_path);
3979 SAFE_FREE(ou_string);
3982 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3985 exploded_dn = ldap_explode_dn(name, 0);
3987 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3989 ldap_value_free(exploded_dn);