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;
552 if ((sitename = sitename_fetch(frame, realm)) == NULL) {
554 sitename = sitename_fetch(frame, realm);
558 /* We try once with a sitename and once without
559 (unless we don't have a sitename and then we're
562 if (sitename == NULL)
565 nt_status = ads_dns_query_gcs(frame,
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_t 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)
1446 LDAPMod **modlist = (LDAPMod **) *mods;
1447 struct berval **ber_values = NULL;
1448 char **char_values = NULL;
1451 mod_op = LDAP_MOD_DELETE;
1453 if (mod_op & LDAP_MOD_BVALUES) {
1454 const struct berval **b;
1455 b = discard_const_p(const struct berval *, _invals);
1456 ber_values = ads_dup_values(ctx, b);
1459 c = discard_const_p(const char *, _invals);
1460 char_values = ads_push_strvals(ctx, c);
1464 /* find the first empty slot */
1465 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1467 if (modlist[curmod] == (LDAPMod *) -1) {
1468 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1469 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1470 return ADS_ERROR(LDAP_NO_MEMORY);
1471 memset(&modlist[curmod], 0,
1472 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1473 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1474 *mods = (ADS_MODLIST)modlist;
1477 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1478 return ADS_ERROR(LDAP_NO_MEMORY);
1479 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1480 if (mod_op & LDAP_MOD_BVALUES) {
1481 modlist[curmod]->mod_bvalues = ber_values;
1482 } else if (mod_op & LDAP_MOD_DELETE) {
1483 modlist[curmod]->mod_values = NULL;
1485 modlist[curmod]->mod_values = char_values;
1488 modlist[curmod]->mod_op = mod_op;
1489 return ADS_ERROR(LDAP_SUCCESS);
1493 * Add a single string value to a mod list
1494 * @param ctx An initialized TALLOC_CTX
1495 * @param mods An initialized ADS_MODLIST
1496 * @param name The attribute name to add
1497 * @param val The value to add - NULL means DELETE
1498 * @return ADS STATUS indicating success of add
1500 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1501 const char *name, const char *val)
1503 const char *values[2];
1509 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1510 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1514 * Add an array of string values to a mod list
1515 * @param ctx An initialized TALLOC_CTX
1516 * @param mods An initialized ADS_MODLIST
1517 * @param name The attribute name to add
1518 * @param vals The array of string values to add - NULL means DELETE
1519 * @return ADS STATUS indicating success of add
1521 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1522 const char *name, const char **vals)
1525 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1526 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1527 name, (const void **) vals);
1532 * Add a single ber-encoded value to a mod list
1533 * @param ctx An initialized TALLOC_CTX
1534 * @param mods An initialized ADS_MODLIST
1535 * @param name The attribute name to add
1536 * @param val The value to add - NULL means DELETE
1537 * @return ADS STATUS indicating success of add
1539 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1540 const char *name, const struct berval *val)
1542 const struct berval *values[2];
1547 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1548 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1549 name, (const void **) values);
1554 * Perform an ldap modify
1555 * @param ads connection to ads server
1556 * @param mod_dn DistinguishedName to modify
1557 * @param mods list of modifications to perform
1558 * @return status of modify
1560 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1563 char *utf8_dn = NULL;
1564 size_t converted_size;
1566 this control is needed to modify that contains a currently
1567 non-existent attribute (but allowable for the object) to run
1569 LDAPControl PermitModify = {
1570 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1573 LDAPControl *controls[2];
1575 controls[0] = &PermitModify;
1578 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1579 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1582 /* find the end of the list, marked by NULL or -1 */
1583 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1584 /* make sure the end of the list is NULL */
1586 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1587 (LDAPMod **) mods, controls, NULL);
1588 TALLOC_FREE(utf8_dn);
1589 return ADS_ERROR(ret);
1593 * Perform an ldap add
1594 * @param ads connection to ads server
1595 * @param new_dn DistinguishedName to add
1596 * @param mods list of attributes and values for DN
1597 * @return status of add
1599 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1602 char *utf8_dn = NULL;
1603 size_t converted_size;
1605 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1606 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1607 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1610 /* find the end of the list, marked by NULL or -1 */
1611 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1612 /* make sure the end of the list is NULL */
1615 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1616 TALLOC_FREE(utf8_dn);
1617 return ADS_ERROR(ret);
1621 * Delete a DistinguishedName
1622 * @param ads connection to ads server
1623 * @param new_dn DistinguishedName to delete
1624 * @return status of delete
1626 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1629 char *utf8_dn = NULL;
1630 size_t converted_size;
1631 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1632 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1633 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1636 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1637 TALLOC_FREE(utf8_dn);
1638 return ADS_ERROR(ret);
1642 * Build an org unit string
1643 * if org unit is Computers or blank then assume a container, otherwise
1644 * assume a / separated list of organisational units.
1645 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1646 * @param ads connection to ads server
1647 * @param org_unit Organizational unit
1648 * @return org unit string - caller must free
1650 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1654 if (!org_unit || !*org_unit) {
1656 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1658 /* samba4 might not yet respond to a wellknownobject-query */
1659 return ret ? ret : SMB_STRDUP("cn=Computers");
1662 if (strequal(org_unit, "Computers")) {
1663 return SMB_STRDUP("cn=Computers");
1666 /* jmcd: removed "\\" from the separation chars, because it is
1667 needed as an escape for chars like '#' which are valid in an
1669 return ads_build_path(org_unit, "/", "ou=", 1);
1673 * Get a org unit string for a well-known GUID
1674 * @param ads connection to ads server
1675 * @param wknguid Well known GUID
1676 * @return org unit string - caller must free
1678 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1681 LDAPMessage *res = NULL;
1682 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1683 **bind_dn_exp = NULL;
1684 const char *attrs[] = {"distinguishedName", NULL};
1685 int new_ln, wkn_ln, bind_ln, i;
1687 if (wknguid == NULL) {
1691 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1692 DEBUG(1, ("asprintf failed!\n"));
1696 status = ads_search_dn(ads, &res, base, attrs);
1697 if (!ADS_ERR_OK(status)) {
1698 DEBUG(1,("Failed while searching for: %s\n", base));
1702 if (ads_count_replies(ads, res) != 1) {
1706 /* substitute the bind-path from the well-known-guid-search result */
1707 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1712 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1717 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1722 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1724 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1727 new_ln = wkn_ln - bind_ln;
1729 ret = SMB_STRDUP(wkn_dn_exp[0]);
1734 for (i=1; i < new_ln; i++) {
1737 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1743 ret = SMB_STRDUP(s);
1752 ads_msgfree(ads, res);
1753 TALLOC_FREE(wkn_dn);
1755 ldap_value_free(wkn_dn_exp);
1758 ldap_value_free(bind_dn_exp);
1765 * Adds (appends) an item to an attribute array, rather then
1766 * replacing the whole list
1767 * @param ctx An initialized TALLOC_CTX
1768 * @param mods An initialized ADS_MODLIST
1769 * @param name name of the ldap attribute to append to
1770 * @param vals an array of values to add
1771 * @return status of addition
1774 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1775 const char *name, const char **vals)
1777 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1778 (const void *) vals);
1782 * Determines the an account's current KVNO via an LDAP lookup
1783 * @param ads An initialized ADS_STRUCT
1784 * @param account_name the NT samaccountname.
1785 * @return the kvno for the account, or -1 in case of a failure.
1788 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1790 LDAPMessage *res = NULL;
1791 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1793 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1794 char *dn_string = NULL;
1795 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1797 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1798 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1801 ret = ads_search(ads, &res, filter, attrs);
1803 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1804 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1805 ads_msgfree(ads, res);
1809 dn_string = ads_get_dn(ads, talloc_tos(), res);
1811 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1812 ads_msgfree(ads, res);
1815 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1816 TALLOC_FREE(dn_string);
1818 /* ---------------------------------------------------------
1819 * 0 is returned as a default KVNO from this point on...
1820 * This is done because Windows 2000 does not support key
1821 * version numbers. Chances are that a failure in the next
1822 * step is simply due to Windows 2000 being used for a
1823 * domain controller. */
1826 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1827 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1828 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1829 ads_msgfree(ads, res);
1834 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1835 ads_msgfree(ads, res);
1840 * Determines the computer account's current KVNO via an LDAP lookup
1841 * @param ads An initialized ADS_STRUCT
1842 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1843 * @return the kvno for the computer account, or -1 in case of a failure.
1846 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1848 char *computer_account = NULL;
1851 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1855 kvno = ads_get_kvno(ads, computer_account);
1856 free(computer_account);
1862 * This clears out all registered spn's for a given hostname
1863 * @param ads An initilaized ADS_STRUCT
1864 * @param machine_name the NetBIOS name of the computer.
1865 * @return 0 upon success, non-zero otherwise.
1868 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1871 LDAPMessage *res = NULL;
1873 const char *servicePrincipalName[1] = {NULL};
1874 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1875 char *dn_string = NULL;
1877 ret = ads_find_machine_acct(ads, &res, machine_name);
1878 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1879 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1880 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1881 ads_msgfree(ads, res);
1882 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1885 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1886 ctx = talloc_init("ads_clear_service_principal_names");
1888 ads_msgfree(ads, res);
1889 return ADS_ERROR(LDAP_NO_MEMORY);
1892 if (!(mods = ads_init_mods(ctx))) {
1893 talloc_destroy(ctx);
1894 ads_msgfree(ads, res);
1895 return ADS_ERROR(LDAP_NO_MEMORY);
1897 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1898 if (!ADS_ERR_OK(ret)) {
1899 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1900 ads_msgfree(ads, res);
1901 talloc_destroy(ctx);
1904 dn_string = ads_get_dn(ads, talloc_tos(), res);
1906 talloc_destroy(ctx);
1907 ads_msgfree(ads, res);
1908 return ADS_ERROR(LDAP_NO_MEMORY);
1910 ret = ads_gen_mod(ads, dn_string, mods);
1911 TALLOC_FREE(dn_string);
1912 if (!ADS_ERR_OK(ret)) {
1913 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1915 ads_msgfree(ads, res);
1916 talloc_destroy(ctx);
1920 ads_msgfree(ads, res);
1921 talloc_destroy(ctx);
1926 * @brief Search for an element in a string array.
1928 * @param[in] el_array The string array to search.
1930 * @param[in] num_el The number of elements in the string array.
1932 * @param[in] el The string to search.
1934 * @return True if found, false if not.
1936 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1940 if (el_array == NULL || num_el == 0 || el == NULL) {
1944 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1947 cmp = strcasecmp_m(el_array[i], el);
1957 * @brief This gets the service principal names of an existing computer account.
1959 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1961 * @param[in] ads The ADS context to use.
1963 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1964 * identify the computer account.
1966 * @param[in] spn_array A pointer to store the array for SPNs.
1968 * @param[in] num_spns The number of principals stored in the array.
1970 * @return 0 on success, or a ADS error if a failure occured.
1972 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1974 const char *machine_name,
1979 LDAPMessage *res = NULL;
1982 status = ads_find_machine_acct(ads,
1985 if (!ADS_ERR_OK(status)) {
1986 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1991 count = ads_count_replies(ads, res);
1993 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1997 *spn_array = ads_pull_strings(ads,
2000 "servicePrincipalName",
2004 ads_msgfree(ads, res);
2010 * This adds a service principal name to an existing computer account
2011 * (found by hostname) in AD.
2012 * @param ads An initialized ADS_STRUCT
2013 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2014 * @param my_fqdn The fully qualified DNS name of the machine
2015 * @param spn A string of the service principal to add, i.e. 'host'
2016 * @return 0 upon sucess, or non-zero if a failure occurs
2019 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
2020 const char *my_fqdn, const char *spn)
2024 LDAPMessage *res = NULL;
2027 char *dn_string = NULL;
2028 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
2030 ret = ads_find_machine_acct(ads, &res, machine_name);
2031 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2032 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2034 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2035 spn, machine_name, ads->config.realm));
2036 ads_msgfree(ads, res);
2037 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2040 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2041 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2042 ads_msgfree(ads, res);
2043 return ADS_ERROR(LDAP_NO_MEMORY);
2046 /* add short name spn */
2048 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2049 talloc_destroy(ctx);
2050 ads_msgfree(ads, res);
2051 return ADS_ERROR(LDAP_NO_MEMORY);
2053 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2054 ret = ADS_ERROR(LDAP_NO_MEMORY);
2057 servicePrincipalName[0] = psp1;
2059 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2060 psp1, machine_name));
2063 /* add fully qualified spn */
2065 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2066 ret = ADS_ERROR(LDAP_NO_MEMORY);
2069 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2070 ret = ADS_ERROR(LDAP_NO_MEMORY);
2073 servicePrincipalName[1] = psp2;
2075 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2076 psp2, machine_name));
2078 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2079 ret = ADS_ERROR(LDAP_NO_MEMORY);
2083 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2084 if (!ADS_ERR_OK(ret)) {
2085 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2089 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2090 ret = ADS_ERROR(LDAP_NO_MEMORY);
2094 ret = ads_gen_mod(ads, dn_string, mods);
2095 if (!ADS_ERR_OK(ret)) {
2096 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2102 ads_msgfree(ads, res);
2107 * adds a machine account to the ADS server
2108 * @param ads An intialized ADS_STRUCT
2109 * @param machine_name - the NetBIOS machine name of this account.
2110 * @param account_type A number indicating the type of account to create
2111 * @param org_unit The LDAP path in which to place this account
2112 * @return 0 upon success, or non-zero otherwise
2115 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2116 const char *org_unit)
2119 char *samAccountName, *controlstr;
2122 char *machine_escaped = NULL;
2124 const char *objectClass[] = {"top", "person", "organizationalPerson",
2125 "user", "computer", NULL};
2126 LDAPMessage *res = NULL;
2127 uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2128 UF_DONT_EXPIRE_PASSWD |\
2129 UF_ACCOUNTDISABLE );
2131 if (!(ctx = talloc_init("ads_add_machine_acct")))
2132 return ADS_ERROR(LDAP_NO_MEMORY);
2134 ret = ADS_ERROR(LDAP_NO_MEMORY);
2136 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2137 if (!machine_escaped) {
2141 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2142 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2144 if ( !new_dn || !samAccountName ) {
2148 #ifndef ENCTYPE_ARCFOUR_HMAC
2149 acct_control |= UF_USE_DES_KEY_ONLY;
2152 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2156 if (!(mods = ads_init_mods(ctx))) {
2160 ads_mod_str(ctx, &mods, "cn", machine_name);
2161 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2162 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2163 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2165 ret = ads_gen_add(ads, new_dn, mods);
2168 SAFE_FREE(machine_escaped);
2169 ads_msgfree(ads, res);
2170 talloc_destroy(ctx);
2176 * move a machine account to another OU on the ADS server
2177 * @param ads - An intialized ADS_STRUCT
2178 * @param machine_name - the NetBIOS machine name of this account.
2179 * @param org_unit - The LDAP path in which to place this account
2180 * @param moved - whether we moved the machine account (optional)
2181 * @return 0 upon success, or non-zero otherwise
2184 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2185 const char *org_unit, bool *moved)
2189 LDAPMessage *res = NULL;
2190 char *filter = NULL;
2191 char *computer_dn = NULL;
2193 char *computer_rdn = NULL;
2194 bool need_move = False;
2196 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2197 rc = ADS_ERROR(LDAP_NO_MEMORY);
2201 /* Find pre-existing machine */
2202 rc = ads_search(ads, &res, filter, NULL);
2203 if (!ADS_ERR_OK(rc)) {
2207 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2209 rc = ADS_ERROR(LDAP_NO_MEMORY);
2213 parent_dn = ads_parent_dn(computer_dn);
2214 if (strequal(parent_dn, org_unit)) {
2220 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2221 rc = ADS_ERROR(LDAP_NO_MEMORY);
2225 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2226 org_unit, 1, NULL, NULL);
2227 rc = ADS_ERROR(ldap_status);
2230 ads_msgfree(ads, res);
2232 TALLOC_FREE(computer_dn);
2233 SAFE_FREE(computer_rdn);
2235 if (!ADS_ERR_OK(rc)) {
2247 dump a binary result from ldap
2249 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2252 for (i=0; values[i]; i++) {
2253 printf("%s: ", field);
2254 for (j=0; j<values[i]->bv_len; j++) {
2255 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2261 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2264 for (i=0; values[i]; i++) {
2266 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2269 status = GUID_from_ndr_blob(&in, &guid);
2270 if (NT_STATUS_IS_OK(status)) {
2271 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2273 printf("%s: INVALID GUID\n", field);
2279 dump a sid result from ldap
2281 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2284 for (i=0; values[i]; i++) {
2287 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2290 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2295 dump ntSecurityDescriptor
2297 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2299 TALLOC_CTX *frame = talloc_stackframe();
2300 struct security_descriptor *psd;
2303 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2304 values[0]->bv_len, &psd);
2305 if (!NT_STATUS_IS_OK(status)) {
2306 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2307 nt_errstr(status)));
2313 ads_disp_sd(ads, talloc_tos(), psd);
2320 dump a string result from ldap
2322 static void dump_string(const char *field, char **values)
2325 for (i=0; values[i]; i++) {
2326 printf("%s: %s\n", field, values[i]);
2331 dump a field from LDAP on stdout
2335 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2340 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2342 {"objectGUID", False, dump_guid},
2343 {"netbootGUID", False, dump_guid},
2344 {"nTSecurityDescriptor", False, dump_sd},
2345 {"dnsRecord", False, dump_binary},
2346 {"objectSid", False, dump_sid},
2347 {"tokenGroups", False, dump_sid},
2348 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2349 {"tokengroupsGlobalandUniversal", False, dump_sid},
2350 {"mS-DS-CreatorSID", False, dump_sid},
2351 {"msExchMailboxGuid", False, dump_guid},
2356 if (!field) { /* must be end of an entry */
2361 for (i=0; handlers[i].name; i++) {
2362 if (strcasecmp_m(handlers[i].name, field) == 0) {
2363 if (!values) /* first time, indicate string or not */
2364 return handlers[i].string;
2365 handlers[i].handler(ads, field, (struct berval **) values);
2369 if (!handlers[i].name) {
2370 if (!values) /* first time, indicate string conversion */
2372 dump_string(field, (char **)values);
2378 * Dump a result from LDAP on stdout
2379 * used for debugging
2380 * @param ads connection to ads server
2381 * @param res Results to dump
2384 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2386 ads_process_results(ads, res, ads_dump_field, NULL);
2390 * Walk through results, calling a function for each entry found.
2391 * The function receives a field name, a berval * array of values,
2392 * and a data area passed through from the start. The function is
2393 * called once with null for field and values at the end of each
2395 * @param ads connection to ads server
2396 * @param res Results to process
2397 * @param fn Function for processing each result
2398 * @param data_area user-defined area to pass to function
2400 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2401 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2406 size_t converted_size;
2408 if (!(ctx = talloc_init("ads_process_results")))
2411 for (msg = ads_first_entry(ads, res); msg;
2412 msg = ads_next_entry(ads, msg)) {
2416 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2417 (LDAPMessage *)msg,&b);
2419 utf8_field=ldap_next_attribute(ads->ldap.ld,
2420 (LDAPMessage *)msg,b)) {
2421 struct berval **ber_vals;
2427 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2430 DEBUG(0,("ads_process_results: "
2431 "pull_utf8_talloc failed: %s",
2435 string = fn(ads, field, NULL, data_area);
2440 utf8_vals = ldap_get_values(ads->ldap.ld,
2441 (LDAPMessage *)msg, field);
2442 p = discard_const_p(const char *, utf8_vals);
2443 str_vals = ads_pull_strvals(ctx, p);
2444 fn(ads, field, (void **) str_vals, data_area);
2445 ldap_value_free(utf8_vals);
2447 ber_vals = ldap_get_values_len(ads->ldap.ld,
2448 (LDAPMessage *)msg, field);
2449 fn(ads, field, (void **) ber_vals, data_area);
2451 ldap_value_free_len(ber_vals);
2453 ldap_memfree(utf8_field);
2456 talloc_free_children(ctx);
2457 fn(ads, NULL, NULL, data_area); /* completed an entry */
2460 talloc_destroy(ctx);
2464 * count how many replies are in a LDAPMessage
2465 * @param ads connection to ads server
2466 * @param res Results to count
2467 * @return number of replies
2469 int ads_count_replies(ADS_STRUCT *ads, void *res)
2471 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2475 * pull the first entry from a ADS result
2476 * @param ads connection to ads server
2477 * @param res Results of search
2478 * @return first entry from result
2480 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2482 return ldap_first_entry(ads->ldap.ld, res);
2486 * pull the next entry from a ADS result
2487 * @param ads connection to ads server
2488 * @param res Results of search
2489 * @return next entry from result
2491 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2493 return ldap_next_entry(ads->ldap.ld, res);
2497 * pull the first message from a ADS result
2498 * @param ads connection to ads server
2499 * @param res Results of search
2500 * @return first message from result
2502 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2504 return ldap_first_message(ads->ldap.ld, res);
2508 * pull the next message from a ADS result
2509 * @param ads connection to ads server
2510 * @param res Results of search
2511 * @return next message from result
2513 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2515 return ldap_next_message(ads->ldap.ld, res);
2519 * pull a single string from a ADS result
2520 * @param ads connection to ads server
2521 * @param mem_ctx TALLOC_CTX to use for allocating result string
2522 * @param msg Results of search
2523 * @param field Attribute to retrieve
2524 * @return Result string in talloc context
2526 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2532 size_t converted_size;
2534 values = ldap_get_values(ads->ldap.ld, msg, field);
2538 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2543 ldap_value_free(values);
2548 * pull an array of strings from a ADS result
2549 * @param ads connection to ads server
2550 * @param mem_ctx TALLOC_CTX to use for allocating result string
2551 * @param msg Results of search
2552 * @param field Attribute to retrieve
2553 * @return Result strings in talloc context
2555 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2556 LDAPMessage *msg, const char *field,
2562 size_t converted_size;
2564 values = ldap_get_values(ads->ldap.ld, msg, field);
2568 *num_values = ldap_count_values(values);
2570 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2572 ldap_value_free(values);
2576 for (i=0;i<*num_values;i++) {
2577 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2580 ldap_value_free(values);
2586 ldap_value_free(values);
2591 * pull an array of strings from a ADS result
2592 * (handle large multivalue attributes with range retrieval)
2593 * @param ads connection to ads server
2594 * @param mem_ctx TALLOC_CTX to use for allocating result string
2595 * @param msg Results of search
2596 * @param field Attribute to retrieve
2597 * @param current_strings strings returned by a previous call to this function
2598 * @param next_attribute The next query should ask for this attribute
2599 * @param num_values How many values did we get this time?
2600 * @param more_values Are there more values to get?
2601 * @return Result strings in talloc context
2603 char **ads_pull_strings_range(ADS_STRUCT *ads,
2604 TALLOC_CTX *mem_ctx,
2605 LDAPMessage *msg, const char *field,
2606 char **current_strings,
2607 const char **next_attribute,
2608 size_t *num_strings,
2612 char *expected_range_attrib, *range_attr;
2613 BerElement *ptr = NULL;
2616 size_t num_new_strings;
2617 unsigned long int range_start;
2618 unsigned long int range_end;
2620 /* we might have been given the whole lot anyway */
2621 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2622 *more_strings = False;
2626 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2628 /* look for Range result */
2629 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2631 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2632 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2633 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2641 /* nothing here - this field is just empty */
2642 *more_strings = False;
2646 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2647 &range_start, &range_end) == 2) {
2648 *more_strings = True;
2650 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2651 &range_start) == 1) {
2652 *more_strings = False;
2654 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2656 ldap_memfree(range_attr);
2657 *more_strings = False;
2662 if ((*num_strings) != range_start) {
2663 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2664 " - aborting range retreival\n",
2665 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2666 ldap_memfree(range_attr);
2667 *more_strings = False;
2671 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2673 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2674 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2675 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2676 range_attr, (unsigned long int)range_end - range_start + 1,
2677 (unsigned long int)num_new_strings));
2678 ldap_memfree(range_attr);
2679 *more_strings = False;
2683 strings = talloc_realloc(mem_ctx, current_strings, char *,
2684 *num_strings + num_new_strings);
2686 if (strings == NULL) {
2687 ldap_memfree(range_attr);
2688 *more_strings = False;
2692 if (new_strings && num_new_strings) {
2693 memcpy(&strings[*num_strings], new_strings,
2694 sizeof(*new_strings) * num_new_strings);
2697 (*num_strings) += num_new_strings;
2699 if (*more_strings) {
2700 *next_attribute = talloc_asprintf(mem_ctx,
2705 if (!*next_attribute) {
2706 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2707 ldap_memfree(range_attr);
2708 *more_strings = False;
2713 ldap_memfree(range_attr);
2719 * pull a single uint32_t from a ADS result
2720 * @param ads connection to ads server
2721 * @param msg Results of search
2722 * @param field Attribute to retrieve
2723 * @param v Pointer to int to store result
2724 * @return boolean inidicating success
2726 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2731 values = ldap_get_values(ads->ldap.ld, msg, field);
2735 ldap_value_free(values);
2739 *v = atoi(values[0]);
2740 ldap_value_free(values);
2745 * pull a single objectGUID from an ADS result
2746 * @param ads connection to ADS server
2747 * @param msg results of search
2748 * @param guid 37-byte area to receive text guid
2749 * @return boolean indicating success
2751 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2756 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2761 status = GUID_from_ndr_blob(&blob, guid);
2762 talloc_free(blob.data);
2763 return NT_STATUS_IS_OK(status);
2768 * pull a single struct dom_sid from a ADS result
2769 * @param ads connection to ads server
2770 * @param msg Results of search
2771 * @param field Attribute to retrieve
2772 * @param sid Pointer to sid to store result
2773 * @return boolean inidicating success
2775 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2776 struct dom_sid *sid)
2778 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2782 * pull an array of struct dom_sids from a ADS result
2783 * @param ads connection to ads server
2784 * @param mem_ctx TALLOC_CTX for allocating sid array
2785 * @param msg Results of search
2786 * @param field Attribute to retrieve
2787 * @param sids pointer to sid array to allocate
2788 * @return the count of SIDs pulled
2790 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2791 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2793 struct berval **values;
2797 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2802 for (i=0; values[i]; i++)
2806 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2808 ldap_value_free_len(values);
2816 for (i=0; values[i]; i++) {
2817 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2819 DEBUG(10, ("pulling SID: %s\n",
2820 sid_string_dbg(&(*sids)[count])));
2825 ldap_value_free_len(values);
2830 * pull a struct security_descriptor from a ADS result
2831 * @param ads connection to ads server
2832 * @param mem_ctx TALLOC_CTX for allocating sid array
2833 * @param msg Results of search
2834 * @param field Attribute to retrieve
2835 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2836 * @return boolean inidicating success
2838 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2839 LDAPMessage *msg, const char *field,
2840 struct security_descriptor **sd)
2842 struct berval **values;
2845 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2847 if (!values) return false;
2851 status = unmarshall_sec_desc(mem_ctx,
2852 (uint8_t *)values[0]->bv_val,
2853 values[0]->bv_len, sd);
2854 if (!NT_STATUS_IS_OK(status)) {
2855 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2856 nt_errstr(status)));
2861 ldap_value_free_len(values);
2866 * in order to support usernames longer than 21 characters we need to
2867 * use both the sAMAccountName and the userPrincipalName attributes
2868 * It seems that not all users have the userPrincipalName attribute set
2870 * @param ads connection to ads server
2871 * @param mem_ctx TALLOC_CTX for allocating sid array
2872 * @param msg Results of search
2873 * @return the username
2875 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2881 /* lookup_name() only works on the sAMAccountName to
2882 returning the username portion of userPrincipalName
2883 breaks winbindd_getpwnam() */
2885 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2886 if (ret && (p = strchr_m(ret, '@'))) {
2891 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2896 * find the update serial number - this is the core of the ldap cache
2897 * @param ads connection to ads server
2898 * @param ads connection to ADS server
2899 * @param usn Pointer to retrieved update serial number
2900 * @return status of search
2902 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2904 const char *attrs[] = {"highestCommittedUSN", NULL};
2908 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2909 if (!ADS_ERR_OK(status))
2912 if (ads_count_replies(ads, res) != 1) {
2913 ads_msgfree(ads, res);
2914 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2917 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2918 ads_msgfree(ads, res);
2919 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2922 ads_msgfree(ads, res);
2926 /* parse a ADS timestring - typical string is
2927 '20020917091222.0Z0' which means 09:12.22 17th September
2929 static time_t ads_parse_time(const char *str)
2935 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2936 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2937 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2946 /********************************************************************
2947 ********************************************************************/
2949 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2951 const char *attrs[] = {"currentTime", NULL};
2956 ADS_STRUCT *ads_s = ads;
2958 if (!(ctx = talloc_init("ads_current_time"))) {
2959 return ADS_ERROR(LDAP_NO_MEMORY);
2962 /* establish a new ldap tcp session if necessary */
2964 if ( !ads->ldap.ld ) {
2965 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2966 ads->server.ldap_server )) == NULL )
2970 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2971 status = ads_connect( ads_s );
2972 if ( !ADS_ERR_OK(status))
2976 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2977 if (!ADS_ERR_OK(status)) {
2981 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2983 ads_msgfree(ads_s, res);
2984 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2988 /* but save the time and offset in the original ADS_STRUCT */
2990 ads->config.current_time = ads_parse_time(timestr);
2992 if (ads->config.current_time != 0) {
2993 ads->auth.time_offset = ads->config.current_time - time(NULL);
2994 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2997 ads_msgfree(ads, res);
2999 status = ADS_SUCCESS;
3002 /* free any temporary ads connections */
3003 if ( ads_s != ads ) {
3004 ads_destroy( &ads_s );
3006 talloc_destroy(ctx);
3011 /********************************************************************
3012 ********************************************************************/
3014 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3016 const char *attrs[] = {"domainFunctionality", NULL};
3019 ADS_STRUCT *ads_s = ads;
3021 *val = DS_DOMAIN_FUNCTION_2000;
3023 /* establish a new ldap tcp session if necessary */
3025 if ( !ads->ldap.ld ) {
3026 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3027 ads->server.ldap_server )) == NULL )
3029 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3032 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3033 status = ads_connect( ads_s );
3034 if ( !ADS_ERR_OK(status))
3038 /* If the attribute does not exist assume it is a Windows 2000
3039 functional domain */
3041 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3042 if (!ADS_ERR_OK(status)) {
3043 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3044 status = ADS_SUCCESS;
3049 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3050 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3052 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3055 ads_msgfree(ads, res);
3058 /* free any temporary ads connections */
3059 if ( ads_s != ads ) {
3060 ads_destroy( &ads_s );
3067 * find the domain sid for our domain
3068 * @param ads connection to ads server
3069 * @param sid Pointer to domain sid
3070 * @return status of search
3072 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3074 const char *attrs[] = {"objectSid", NULL};
3078 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3080 if (!ADS_ERR_OK(rc)) return rc;
3081 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3082 ads_msgfree(ads, res);
3083 return ADS_ERROR_SYSTEM(ENOENT);
3085 ads_msgfree(ads, res);
3091 * find our site name
3092 * @param ads connection to ads server
3093 * @param mem_ctx Pointer to talloc context
3094 * @param site_name Pointer to the sitename
3095 * @return status of search
3097 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3101 const char *dn, *service_name;
3102 const char *attrs[] = { "dsServiceName", NULL };
3104 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3105 if (!ADS_ERR_OK(status)) {
3109 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3110 if (service_name == NULL) {
3111 ads_msgfree(ads, res);
3112 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3115 ads_msgfree(ads, res);
3117 /* go up three levels */
3118 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3120 return ADS_ERROR(LDAP_NO_MEMORY);
3123 *site_name = talloc_strdup(mem_ctx, dn);
3124 if (*site_name == NULL) {
3125 return ADS_ERROR(LDAP_NO_MEMORY);
3130 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3135 * find the site dn where a machine resides
3136 * @param ads connection to ads server
3137 * @param mem_ctx Pointer to talloc context
3138 * @param computer_name name of the machine
3139 * @param site_name Pointer to the sitename
3140 * @return status of search
3142 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3146 const char *parent, *filter;
3147 char *config_context = NULL;
3150 /* shortcut a query */
3151 if (strequal(computer_name, ads->config.ldap_server_name)) {
3152 return ads_site_dn(ads, mem_ctx, site_dn);
3155 status = ads_config_path(ads, mem_ctx, &config_context);
3156 if (!ADS_ERR_OK(status)) {
3160 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3161 if (filter == NULL) {
3162 return ADS_ERROR(LDAP_NO_MEMORY);
3165 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3166 filter, NULL, &res);
3167 if (!ADS_ERR_OK(status)) {
3171 if (ads_count_replies(ads, res) != 1) {
3172 ads_msgfree(ads, res);
3173 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3176 dn = ads_get_dn(ads, mem_ctx, res);
3178 ads_msgfree(ads, res);
3179 return ADS_ERROR(LDAP_NO_MEMORY);
3182 /* go up three levels */
3183 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3184 if (parent == NULL) {
3185 ads_msgfree(ads, res);
3187 return ADS_ERROR(LDAP_NO_MEMORY);
3190 *site_dn = talloc_strdup(mem_ctx, parent);
3191 if (*site_dn == NULL) {
3192 ads_msgfree(ads, res);
3194 return ADS_ERROR(LDAP_NO_MEMORY);
3198 ads_msgfree(ads, res);
3204 * get the upn suffixes for a domain
3205 * @param ads connection to ads server
3206 * @param mem_ctx Pointer to talloc context
3207 * @param suffixes Pointer to an array of suffixes
3208 * @param num_suffixes Pointer to the number of suffixes
3209 * @return status of search
3211 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3216 char *config_context = NULL;
3217 const char *attrs[] = { "uPNSuffixes", NULL };
3219 status = ads_config_path(ads, mem_ctx, &config_context);
3220 if (!ADS_ERR_OK(status)) {
3224 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3226 return ADS_ERROR(LDAP_NO_MEMORY);
3229 status = ads_search_dn(ads, &res, base, attrs);
3230 if (!ADS_ERR_OK(status)) {
3234 if (ads_count_replies(ads, res) != 1) {
3235 ads_msgfree(ads, res);
3236 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3239 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3240 if ((*suffixes) == NULL) {
3241 ads_msgfree(ads, res);
3242 return ADS_ERROR(LDAP_NO_MEMORY);
3245 ads_msgfree(ads, res);
3251 * get the joinable ous for a domain
3252 * @param ads connection to ads server
3253 * @param mem_ctx Pointer to talloc context
3254 * @param ous Pointer to an array of ous
3255 * @param num_ous Pointer to the number of ous
3256 * @return status of search
3258 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3259 TALLOC_CTX *mem_ctx,
3264 LDAPMessage *res = NULL;
3265 LDAPMessage *msg = NULL;
3266 const char *attrs[] = { "dn", NULL };
3269 status = ads_search(ads, &res,
3270 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3272 if (!ADS_ERR_OK(status)) {
3276 count = ads_count_replies(ads, res);
3278 ads_msgfree(ads, res);
3279 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3282 for (msg = ads_first_entry(ads, res); msg;
3283 msg = ads_next_entry(ads, msg)) {
3284 const char **p = discard_const_p(const char *, *ous);
3287 dn = ads_get_dn(ads, talloc_tos(), msg);
3289 ads_msgfree(ads, res);
3290 return ADS_ERROR(LDAP_NO_MEMORY);
3293 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3295 ads_msgfree(ads, res);
3296 return ADS_ERROR(LDAP_NO_MEMORY);
3300 *ous = discard_const_p(char *, p);
3303 ads_msgfree(ads, res);
3310 * pull a struct dom_sid from an extended dn string
3311 * @param mem_ctx TALLOC_CTX
3312 * @param extended_dn string
3313 * @param flags string type of extended_dn
3314 * @param sid pointer to a struct dom_sid
3315 * @return NT_STATUS_OK on success,
3316 * NT_INVALID_PARAMETER on error,
3317 * NT_STATUS_NOT_FOUND if no SID present
3319 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3320 const char *extended_dn,
3321 enum ads_extended_dn_flags flags,
3322 struct dom_sid *sid)
3327 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3330 /* otherwise extended_dn gets stripped off */
3331 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3332 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3335 * ADS_EXTENDED_DN_HEX_STRING:
3336 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3338 * ADS_EXTENDED_DN_STRING (only with w2k3):
3339 * <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
3341 * Object with no SID, such as an Exchange Public Folder
3342 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3345 p = strchr(dn, ';');
3347 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3350 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3351 DEBUG(5,("No SID present in extended dn\n"));
3352 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3355 p += strlen(";<SID=");
3359 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3364 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3368 case ADS_EXTENDED_DN_STRING:
3369 if (!string_to_sid(sid, p)) {
3370 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3373 case ADS_EXTENDED_DN_HEX_STRING: {
3377 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3379 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3382 if (!sid_parse(buf, buf_len, sid)) {
3383 DEBUG(10,("failed to parse sid\n"));
3384 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3389 DEBUG(10,("unknown extended dn format\n"));
3390 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3393 return ADS_ERROR_NT(NT_STATUS_OK);
3396 /********************************************************************
3397 ********************************************************************/
3399 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3401 LDAPMessage *res = NULL;
3406 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3407 if (!ADS_ERR_OK(status)) {
3408 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3409 lp_netbios_name()));
3413 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3414 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3418 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3419 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3423 ads_msgfree(ads, res);
3428 /********************************************************************
3429 ********************************************************************/
3431 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3433 LDAPMessage *res = NULL;
3438 status = ads_find_machine_acct(ads, &res, machine_name);
3439 if (!ADS_ERR_OK(status)) {
3440 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3441 lp_netbios_name()));
3445 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3446 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3450 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3451 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3455 ads_msgfree(ads, res);
3460 /********************************************************************
3461 ********************************************************************/
3463 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3465 LDAPMessage *res = NULL;
3470 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3471 if (!ADS_ERR_OK(status)) {
3472 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3473 lp_netbios_name()));
3477 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3478 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3482 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3483 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3487 ads_msgfree(ads, res);
3494 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3497 * Join a machine to a realm
3498 * Creates the machine account and sets the machine password
3499 * @param ads connection to ads server
3500 * @param machine name of host to add
3501 * @param org_unit Organizational unit to place machine in
3502 * @return status of join
3504 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3505 uint32_t account_type, const char *org_unit)
3508 LDAPMessage *res = NULL;
3511 /* machine name must be lowercase */
3512 machine = SMB_STRDUP(machine_name);
3513 strlower_m(machine);
3516 status = ads_find_machine_acct(ads, (void **)&res, machine);
3517 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3518 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3519 status = ads_leave_realm(ads, machine);
3520 if (!ADS_ERR_OK(status)) {
3521 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3522 machine, ads->config.realm));
3527 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3528 if (!ADS_ERR_OK(status)) {
3529 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3534 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3535 if (!ADS_ERR_OK(status)) {
3536 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3542 ads_msgfree(ads, res);
3549 * Delete a machine from the realm
3550 * @param ads connection to ads server
3551 * @param hostname Machine to remove
3552 * @return status of delete
3554 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3559 char *hostnameDN, *host;
3561 LDAPControl ldap_control;
3562 LDAPControl * pldap_control[2] = {NULL, NULL};
3564 pldap_control[0] = &ldap_control;
3565 memset(&ldap_control, 0, sizeof(LDAPControl));
3566 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3568 /* hostname must be lowercase */
3569 host = SMB_STRDUP(hostname);
3570 if (!strlower_m(host)) {
3572 return ADS_ERROR_SYSTEM(EINVAL);
3575 status = ads_find_machine_acct(ads, &res, host);
3576 if (!ADS_ERR_OK(status)) {
3577 DEBUG(0, ("Host account for %s does not exist.\n", host));
3582 msg = ads_first_entry(ads, res);
3585 return ADS_ERROR_SYSTEM(ENOENT);
3588 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3589 if (hostnameDN == NULL) {
3591 return ADS_ERROR_SYSTEM(ENOENT);
3594 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3596 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3598 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3601 if (rc != LDAP_SUCCESS) {
3602 const char *attrs[] = { "cn", NULL };
3603 LDAPMessage *msg_sub;
3605 /* we only search with scope ONE, we do not expect any further
3606 * objects to be created deeper */
3608 status = ads_do_search_retry(ads, hostnameDN,
3609 LDAP_SCOPE_ONELEVEL,
3610 "(objectclass=*)", attrs, &res);
3612 if (!ADS_ERR_OK(status)) {
3614 TALLOC_FREE(hostnameDN);
3618 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3619 msg_sub = ads_next_entry(ads, msg_sub)) {
3623 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3625 TALLOC_FREE(hostnameDN);
3626 return ADS_ERROR(LDAP_NO_MEMORY);
3629 status = ads_del_dn(ads, dn);
3630 if (!ADS_ERR_OK(status)) {
3631 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3634 TALLOC_FREE(hostnameDN);
3641 /* there should be no subordinate objects anymore */
3642 status = ads_do_search_retry(ads, hostnameDN,
3643 LDAP_SCOPE_ONELEVEL,
3644 "(objectclass=*)", attrs, &res);
3646 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3648 TALLOC_FREE(hostnameDN);
3652 /* delete hostnameDN now */
3653 status = ads_del_dn(ads, hostnameDN);
3654 if (!ADS_ERR_OK(status)) {
3656 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3657 TALLOC_FREE(hostnameDN);
3662 TALLOC_FREE(hostnameDN);
3664 status = ads_find_machine_acct(ads, &res, host);
3665 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3666 DEBUG(3, ("Failed to remove host account.\n"));
3676 * pull all token-sids from an LDAP dn
3677 * @param ads connection to ads server
3678 * @param mem_ctx TALLOC_CTX for allocating sid array
3679 * @param dn of LDAP object
3680 * @param user_sid pointer to struct dom_sid (objectSid)
3681 * @param primary_group_sid pointer to struct dom_sid (self composed)
3682 * @param sids pointer to sid array to allocate
3683 * @param num_sids counter of SIDs pulled
3684 * @return status of token query
3686 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3687 TALLOC_CTX *mem_ctx,
3689 struct dom_sid *user_sid,
3690 struct dom_sid *primary_group_sid,
3691 struct dom_sid **sids,
3695 LDAPMessage *res = NULL;
3697 size_t tmp_num_sids;
3698 struct dom_sid *tmp_sids;
3699 struct dom_sid tmp_user_sid;
3700 struct dom_sid tmp_primary_group_sid;
3702 const char *attrs[] = {
3709 status = ads_search_retry_dn(ads, &res, dn, attrs);
3710 if (!ADS_ERR_OK(status)) {
3714 count = ads_count_replies(ads, res);
3716 ads_msgfree(ads, res);
3717 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3720 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3721 ads_msgfree(ads, res);
3722 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3725 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3726 ads_msgfree(ads, res);
3727 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3731 /* hack to compose the primary group sid without knowing the
3734 struct dom_sid domsid;
3736 sid_copy(&domsid, &tmp_user_sid);
3738 if (!sid_split_rid(&domsid, NULL)) {
3739 ads_msgfree(ads, res);
3740 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3743 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3744 ads_msgfree(ads, res);
3745 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3749 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3751 if (tmp_num_sids == 0 || !tmp_sids) {
3752 ads_msgfree(ads, res);
3753 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3757 *num_sids = tmp_num_sids;
3765 *user_sid = tmp_user_sid;
3768 if (primary_group_sid) {
3769 *primary_group_sid = tmp_primary_group_sid;
3772 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3774 ads_msgfree(ads, res);
3775 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3779 * Find a sAMAccoutName in LDAP
3780 * @param ads connection to ads server
3781 * @param mem_ctx TALLOC_CTX for allocating sid array
3782 * @param samaccountname to search
3783 * @param uac_ret uint32_t pointer userAccountControl attribute value
3784 * @param dn_ret pointer to dn
3785 * @return status of token query
3787 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3788 TALLOC_CTX *mem_ctx,
3789 const char *samaccountname,
3791 const char **dn_ret)
3794 const char *attrs[] = { "userAccountControl", NULL };
3796 LDAPMessage *res = NULL;
3800 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3802 if (filter == NULL) {
3803 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3807 status = ads_do_search_all(ads, ads->config.bind_path,
3809 filter, attrs, &res);
3811 if (!ADS_ERR_OK(status)) {
3815 if (ads_count_replies(ads, res) != 1) {
3816 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3820 dn = ads_get_dn(ads, talloc_tos(), res);
3822 status = ADS_ERROR(LDAP_NO_MEMORY);
3826 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3827 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3836 *dn_ret = talloc_strdup(mem_ctx, dn);
3838 status = ADS_ERROR(LDAP_NO_MEMORY);
3844 ads_msgfree(ads, res);
3850 * find our configuration path
3851 * @param ads connection to ads server
3852 * @param mem_ctx Pointer to talloc context
3853 * @param config_path Pointer to the config path
3854 * @return status of search
3856 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3857 TALLOC_CTX *mem_ctx,
3861 LDAPMessage *res = NULL;
3862 const char *config_context = NULL;
3863 const char *attrs[] = { "configurationNamingContext", NULL };
3865 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3866 "(objectclass=*)", attrs, &res);
3867 if (!ADS_ERR_OK(status)) {
3871 config_context = ads_pull_string(ads, mem_ctx, res,
3872 "configurationNamingContext");
3873 ads_msgfree(ads, res);
3874 if (!config_context) {
3875 return ADS_ERROR(LDAP_NO_MEMORY);
3879 *config_path = talloc_strdup(mem_ctx, config_context);
3880 if (!*config_path) {
3881 return ADS_ERROR(LDAP_NO_MEMORY);
3885 return ADS_ERROR(LDAP_SUCCESS);
3889 * find the displayName of an extended right
3890 * @param ads connection to ads server
3891 * @param config_path The config path
3892 * @param mem_ctx Pointer to talloc context
3893 * @param GUID struct of the rightsGUID
3894 * @return status of search
3896 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3897 const char *config_path,
3898 TALLOC_CTX *mem_ctx,
3899 const struct GUID *rights_guid)
3902 LDAPMessage *res = NULL;
3904 const char *attrs[] = { "displayName", NULL };
3905 const char *result = NULL;
3908 if (!ads || !mem_ctx || !rights_guid) {
3912 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3913 GUID_string(mem_ctx, rights_guid));
3918 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3923 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3925 if (!ADS_ERR_OK(rc)) {
3929 if (ads_count_replies(ads, res) != 1) {
3933 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3936 ads_msgfree(ads, res);
3941 * verify or build and verify an account ou
3942 * @param mem_ctx Pointer to talloc context
3943 * @param ads connection to ads server
3945 * @return status of search
3948 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3950 const char **account_ou)
3956 exploded_dn = ldap_explode_dn(*account_ou, 0);
3958 ldap_value_free(exploded_dn);
3962 ou_string = ads_ou_string(ads, *account_ou);
3964 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3967 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3968 ads->config.bind_path);
3969 SAFE_FREE(ou_string);
3972 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3975 exploded_dn = ldap_explode_dn(name, 0);
3977 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3979 ldap_value_free(exploded_dn);