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/>.
25 #include "libads/sitename_cache.h"
26 #include "libads/cldap.h"
27 #include "libads/dns.h"
33 * @brief basic ldap client-side routines for ads server communications
35 * The routines contained here should do the necessary ldap calls for
38 * Important note: attribute names passed into ads_ routines must
39 * already be in UTF-8 format. We do not convert them because in almost
40 * all cases, they are just ascii (which is represented with the same
41 * codepoints in UTF-8). This may have to change at some point
45 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
47 static SIG_ATOMIC_T gotalarm;
49 /***************************************************************
50 Signal function to tell us we timed out.
51 ****************************************************************/
53 static void gotalarm_sig(int signum)
58 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
63 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
64 "%u seconds\n", server, port, to));
68 CatchSignal(SIGALRM, gotalarm_sig);
70 /* End setup timeout. */
72 ldp = ldap_open(server, port);
75 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
76 server, port, strerror(errno)));
78 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
81 /* Teardown timeout. */
82 CatchSignal(SIGALRM, SIG_IGN);
88 static int ldap_search_with_timeout(LDAP *ld,
89 LDAP_CONST char *base,
91 LDAP_CONST char *filter,
99 struct timeval timeout;
102 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
103 timeout.tv_sec = lp_ldap_timeout();
106 /* Setup alarm timeout.... Do we need both of these ? JRA. */
108 CatchSignal(SIGALRM, gotalarm_sig);
109 alarm(lp_ldap_timeout());
110 /* End setup timeout. */
112 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
113 attrsonly, sctrls, cctrls, &timeout,
116 /* Teardown timeout. */
117 CatchSignal(SIGALRM, SIG_IGN);
121 return LDAP_TIMELIMIT_EXCEEDED;
124 * A bug in OpenLDAP means ldap_search_ext_s can return
125 * LDAP_SUCCESS but with a NULL res pointer. Cope with
126 * this. See bug #6279 for details. JRA.
130 return LDAP_TIMELIMIT_EXCEEDED;
136 /**********************************************
137 Do client and server sitename match ?
138 **********************************************/
140 bool ads_sitename_match(ADS_STRUCT *ads)
142 if (ads->config.server_site_name == NULL &&
143 ads->config.client_site_name == NULL ) {
144 DEBUG(10,("ads_sitename_match: both null\n"));
147 if (ads->config.server_site_name &&
148 ads->config.client_site_name &&
149 strequal(ads->config.server_site_name,
150 ads->config.client_site_name)) {
151 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
154 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
155 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
156 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
160 /**********************************************
161 Is this the closest DC ?
162 **********************************************/
164 bool ads_closest_dc(ADS_STRUCT *ads)
166 if (ads->config.flags & NBT_SERVER_CLOSEST) {
167 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
171 /* not sure if this can ever happen */
172 if (ads_sitename_match(ads)) {
173 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
177 if (ads->config.client_site_name == NULL) {
178 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
182 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
183 ads->config.ldap_server_name));
190 try a connection to a given ldap server, returning True and setting the servers IP
191 in the ads struct if successful
193 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
196 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
197 TALLOC_CTX *frame = talloc_stackframe();
200 if (!server || !*server) {
205 if (!is_ipaddress(server)) {
206 struct sockaddr_storage ss;
207 char addr[INET6_ADDRSTRLEN];
209 if (!resolve_name(server, &ss, 0x20, true)) {
210 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
215 print_sockaddr(addr, sizeof(addr), &ss);
216 srv = talloc_strdup(frame, addr);
218 /* this copes with inet_ntoa brokenness */
219 srv = talloc_strdup(frame, server);
227 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
228 srv, ads->server.realm));
230 ZERO_STRUCT( cldap_reply );
232 if ( !ads_cldap_netlogon_5(frame, srv, ads->server.realm, &cldap_reply ) ) {
233 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
238 /* Check the CLDAP reply flags */
240 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
241 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
247 /* Fill in the ads->config values */
249 SAFE_FREE(ads->config.realm);
250 SAFE_FREE(ads->config.bind_path);
251 SAFE_FREE(ads->config.ldap_server_name);
252 SAFE_FREE(ads->config.server_site_name);
253 SAFE_FREE(ads->config.client_site_name);
254 SAFE_FREE(ads->server.workgroup);
256 ads->config.flags = cldap_reply.server_type;
257 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
258 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
259 strupper_m(ads->config.realm);
260 ads->config.bind_path = ads_build_dn(ads->config.realm);
261 if (*cldap_reply.server_site) {
262 ads->config.server_site_name =
263 SMB_STRDUP(cldap_reply.server_site);
265 if (*cldap_reply.client_site) {
266 ads->config.client_site_name =
267 SMB_STRDUP(cldap_reply.client_site);
269 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
271 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
272 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
273 DEBUG(1,("ads_try_connect: unable to convert %s "
280 /* Store our site name. */
281 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
282 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
292 /**********************************************************************
293 Try to find an AD dc using our internal name resolution routines
294 Try the realm first and then then workgroup name if netbios is not
296 **********************************************************************/
298 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
300 const char *c_domain;
303 struct ip_service *ip_list;
306 bool got_realm = False;
307 bool use_own_domain = False;
309 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
311 /* if the realm and workgroup are both empty, assume they are ours */
314 c_realm = ads->server.realm;
316 if ( !c_realm || !*c_realm ) {
317 /* special case where no realm and no workgroup means our own */
318 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
319 use_own_domain = True;
320 c_realm = lp_realm();
324 if (c_realm && *c_realm)
327 /* we need to try once with the realm name and fallback to the
328 netbios domain name if we fail (if netbios has not been disabled */
330 if ( !got_realm && !lp_disable_netbios() ) {
331 c_realm = ads->server.workgroup;
332 if (!c_realm || !*c_realm) {
333 if ( use_own_domain )
334 c_realm = lp_workgroup();
338 if ( !c_realm || !*c_realm ) {
339 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
340 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
343 if ( use_own_domain ) {
344 c_domain = lp_workgroup();
346 c_domain = ads->server.workgroup;
353 * In case of LDAP we use get_dc_name() as that
354 * creates the custom krb5.conf file
356 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
358 struct sockaddr_storage ip_out;
360 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
361 (got_realm ? "realm" : "domain"), realm));
363 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
365 * we call ads_try_connect() to fill in the
366 * ads->config details
368 if (ads_try_connect(ads, srv_name, false)) {
373 return NT_STATUS_NO_LOGON_SERVERS;
376 sitename = sitename_fetch(realm);
380 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
381 (got_realm ? "realm" : "domain"), realm));
383 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
384 if (!NT_STATUS_IS_OK(status)) {
385 /* fall back to netbios if we can */
386 if ( got_realm && !lp_disable_netbios() ) {
395 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
396 for ( i=0; i<count; i++ ) {
397 char server[INET6_ADDRSTRLEN];
399 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
401 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
405 /* realm in this case is a workgroup name. We need
406 to ignore any IP addresses in the negative connection
407 cache that match ip addresses returned in the ad realm
408 case. It sucks that I have to reproduce the logic above... */
409 c_realm = ads->server.realm;
410 if ( !c_realm || !*c_realm ) {
411 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
412 c_realm = lp_realm();
415 if (c_realm && *c_realm &&
416 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
417 /* Ensure we add the workgroup name for this
418 IP address as negative too. */
419 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
424 if ( ads_try_connect(ads, server, false) ) {
430 /* keep track of failures */
431 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
436 /* In case we failed to contact one of our closest DC on our site we
437 * need to try to find another DC, retry with a site-less SRV DNS query
441 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
442 "trying to find another DC\n", sitename));
444 namecache_delete(realm, 0x1C);
448 return NT_STATUS_NO_LOGON_SERVERS;
451 /*********************************************************************
452 *********************************************************************/
454 static NTSTATUS ads_lookup_site(void)
456 ADS_STRUCT *ads = NULL;
457 ADS_STATUS ads_status;
458 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
460 ads = ads_init(lp_realm(), NULL, NULL);
462 return NT_STATUS_NO_MEMORY;
465 /* The NO_BIND here will find a DC and set the client site
466 but not establish the TCP connection */
468 ads->auth.flags = ADS_AUTH_NO_BIND;
469 ads_status = ads_connect(ads);
470 if (!ADS_ERR_OK(ads_status)) {
471 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
472 ads_errstr(ads_status)));
474 nt_status = ads_ntstatus(ads_status);
483 /*********************************************************************
484 *********************************************************************/
486 static const char* host_dns_domain(const char *fqdn)
488 const char *p = fqdn;
490 /* go to next char following '.' */
492 if ((p = strchr_m(fqdn, '.')) != NULL) {
501 * Connect to the Global Catalog server
502 * @param ads Pointer to an existing ADS_STRUCT
503 * @return status of connection
505 * Simple wrapper around ads_connect() that fills in the
506 * GC ldap server information
509 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
511 TALLOC_CTX *frame = talloc_stackframe();
512 struct dns_rr_srv *gcs_list;
514 char *realm = ads->server.realm;
515 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
516 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
519 char *sitename = NULL;
524 if ((sitename = sitename_fetch(realm)) == NULL) {
526 sitename = sitename_fetch(realm);
530 /* We try once with a sitename and once without
531 (unless we don't have a sitename and then we're
534 if (sitename == NULL)
537 nt_status = ads_dns_query_gcs(frame, realm, sitename,
538 &gcs_list, &num_gcs);
542 if (!NT_STATUS_IS_OK(nt_status)) {
543 ads_status = ADS_ERROR_NT(nt_status);
547 /* Loop until we get a successful connection or have gone
548 through them all. When connecting a GC server, make sure that
549 the realm is the server's DNS name and not the forest root */
551 for (i=0; i<num_gcs; i++) {
552 ads->server.gc = true;
553 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
554 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
555 ads_status = ads_connect(ads);
556 if (ADS_ERR_OK(ads_status)) {
557 /* Reset the bind_dn to "". A Global Catalog server
558 may host multiple domain trees in a forest.
559 Windows 2003 GC server will accept "" as the search
560 path to imply search all domain trees in the forest */
562 SAFE_FREE(ads->config.bind_path);
563 ads->config.bind_path = SMB_STRDUP("");
568 SAFE_FREE(ads->server.ldap_server);
569 SAFE_FREE(ads->server.realm);
572 TALLOC_FREE(gcs_list);
578 talloc_destroy(frame);
585 * Connect to the LDAP server
586 * @param ads Pointer to an existing ADS_STRUCT
587 * @return status of connection
589 ADS_STATUS ads_connect(ADS_STRUCT *ads)
591 int version = LDAP_VERSION3;
594 char addr[INET6_ADDRSTRLEN];
596 ZERO_STRUCT(ads->ldap);
597 ads->ldap.last_attempt = time(NULL);
598 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
600 /* try with a user specified server */
602 if (DEBUGLEVEL >= 11) {
603 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
604 DEBUG(11,("ads_connect: entering\n"));
605 DEBUGADD(11,("%s\n", s));
609 if (ads->server.ldap_server)
611 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
615 /* The choice of which GC use is handled one level up in
616 ads_connect_gc(). If we continue on from here with
617 ads_find_dc() we will get GC searches on port 389 which
618 doesn't work. --jerry */
620 if (ads->server.gc == true) {
621 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
625 ntstatus = ads_find_dc(ads);
626 if (NT_STATUS_IS_OK(ntstatus)) {
630 status = ADS_ERROR_NT(ntstatus);
635 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
636 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
638 if (!ads->auth.user_name) {
639 /* Must use the userPrincipalName value here or sAMAccountName
640 and not servicePrincipalName; found by Guenther Deschner */
642 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
643 DEBUG(0,("ads_connect: asprintf fail.\n"));
644 ads->auth.user_name = NULL;
648 if (!ads->auth.realm) {
649 ads->auth.realm = SMB_STRDUP(ads->config.realm);
652 if (!ads->auth.kdc_server) {
653 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
654 ads->auth.kdc_server = SMB_STRDUP(addr);
658 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
659 to MIT kerberos to work (tridge) */
662 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
663 setenv(env, ads->auth.kdc_server, 1);
669 /* If the caller() requested no LDAP bind, then we are done */
671 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
672 status = ADS_SUCCESS;
676 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
677 if (!ads->ldap.mem_ctx) {
678 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
682 /* Otherwise setup the TCP LDAP session */
684 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
685 ads->ldap.port, lp_ldap_timeout());
686 if (ads->ldap.ld == NULL) {
687 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
690 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
692 /* cache the successful connection for workgroup and realm */
693 if (ads_closest_dc(ads)) {
694 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
695 saf_store( ads->server.realm, ads->config.ldap_server_name);
698 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
700 if ( lp_ldap_ssl_ads() ) {
701 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
702 if (!ADS_ERR_OK(status)) {
707 /* fill in the current time and offsets */
709 status = ads_current_time( ads );
710 if ( !ADS_ERR_OK(status) ) {
714 /* Now do the bind */
716 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
717 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
721 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
722 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
726 status = ads_sasl_bind(ads);
729 if (DEBUGLEVEL >= 11) {
730 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
731 DEBUG(11,("ads_connect: leaving with: %s\n",
732 ads_errstr(status)));
733 DEBUGADD(11,("%s\n", s));
741 * Connect to the LDAP server using given credentials
742 * @param ads Pointer to an existing ADS_STRUCT
743 * @return status of connection
745 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
747 ads->auth.flags |= ADS_AUTH_USER_CREDS;
749 return ads_connect(ads);
753 * Disconnect the LDAP server
754 * @param ads Pointer to an existing ADS_STRUCT
756 void ads_disconnect(ADS_STRUCT *ads)
759 ldap_unbind(ads->ldap.ld);
762 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
763 ads->ldap.wrap_ops->disconnect(ads);
765 if (ads->ldap.mem_ctx) {
766 talloc_free(ads->ldap.mem_ctx);
768 ZERO_STRUCT(ads->ldap);
772 Duplicate a struct berval into talloc'ed memory
774 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
776 struct berval *value;
778 if (!in_val) return NULL;
780 value = TALLOC_ZERO_P(ctx, struct berval);
783 if (in_val->bv_len == 0) return value;
785 value->bv_len = in_val->bv_len;
786 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
792 Make a values list out of an array of (struct berval *)
794 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
795 const struct berval **in_vals)
797 struct berval **values;
800 if (!in_vals) return NULL;
801 for (i=0; in_vals[i]; i++)
803 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
804 if (!values) return NULL;
806 for (i=0; in_vals[i]; i++) {
807 values[i] = dup_berval(ctx, in_vals[i]);
813 UTF8-encode a values list out of an array of (char *)
815 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
821 if (!in_vals) return NULL;
822 for (i=0; in_vals[i]; i++)
824 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
825 if (!values) return NULL;
827 for (i=0; in_vals[i]; i++) {
828 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
837 Pull a (char *) array out of a UTF8-encoded values list
839 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
843 size_t converted_size;
845 if (!in_vals) return NULL;
846 for (i=0; in_vals[i]; i++)
848 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
849 if (!values) return NULL;
851 for (i=0; in_vals[i]; i++) {
852 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
854 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
855 "%s", strerror(errno)));
862 * Do a search with paged results. cookie must be null on the first
863 * call, and then returned on each subsequent call. It will be null
864 * again when the entire search is complete
865 * @param ads connection to ads server
866 * @param bind_path Base dn for the search
867 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
868 * @param expr Search expression - specified in local charset
869 * @param attrs Attributes to retrieve - specified in utf8 or ascii
870 * @param res ** which will contain results - free res* with ads_msgfree()
871 * @param count Number of entries retrieved on this page
872 * @param cookie The paged results cookie to be returned on subsequent calls
873 * @return status of search
875 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
876 const char *bind_path,
877 int scope, const char *expr,
878 const char **attrs, void *args,
880 int *count, struct berval **cookie)
883 char *utf8_expr, *utf8_path, **search_attrs = NULL;
884 size_t converted_size;
885 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
886 BerElement *cookie_be = NULL;
887 struct berval *cookie_bv= NULL;
888 BerElement *ext_be = NULL;
889 struct berval *ext_bv= NULL;
892 ads_control *external_control = (ads_control *) args;
896 if (!(ctx = talloc_init("ads_do_paged_search_args")))
897 return ADS_ERROR(LDAP_NO_MEMORY);
899 /* 0 means the conversion worked but the result was empty
900 so we only fail if it's -1. In any case, it always
901 at least nulls out the dest */
902 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
903 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
909 if (!attrs || !(*attrs))
912 /* This would be the utf8-encoded version...*/
913 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
914 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
920 /* Paged results only available on ldap v3 or later */
921 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
922 if (version < LDAP_VERSION3) {
923 rc = LDAP_NOT_SUPPORTED;
927 cookie_be = ber_alloc_t(LBER_USE_DER);
929 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
930 ber_bvfree(*cookie); /* don't need it from last time */
933 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
935 ber_flatten(cookie_be, &cookie_bv);
936 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
937 PagedResults.ldctl_iscritical = (char) 1;
938 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
939 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
941 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
942 NoReferrals.ldctl_iscritical = (char) 0;
943 NoReferrals.ldctl_value.bv_len = 0;
944 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
946 if (external_control &&
947 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
948 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
950 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
951 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
953 /* win2k does not accept a ldctl_value beeing passed in */
955 if (external_control->val != 0) {
957 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
962 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
966 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
971 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
972 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
975 ExternalCtrl.ldctl_value.bv_len = 0;
976 ExternalCtrl.ldctl_value.bv_val = NULL;
979 controls[0] = &NoReferrals;
980 controls[1] = &PagedResults;
981 controls[2] = &ExternalCtrl;
985 controls[0] = &NoReferrals;
986 controls[1] = &PagedResults;
990 /* we need to disable referrals as the openldap libs don't
991 handle them and paged results at the same time. Using them
992 together results in the result record containing the server
993 page control being removed from the result list (tridge/jmcd)
995 leaving this in despite the control that says don't generate
996 referrals, in case the server doesn't support it (jmcd)
998 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1000 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1001 search_attrs, 0, controls,
1002 NULL, LDAP_NO_LIMIT,
1003 (LDAPMessage **)res);
1005 ber_free(cookie_be, 1);
1006 ber_bvfree(cookie_bv);
1009 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1010 ldap_err2string(rc)));
1014 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1015 NULL, &rcontrols, 0);
1021 for (i=0; rcontrols[i]; i++) {
1022 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1023 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1024 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1026 /* the berval is the cookie, but must be freed when
1028 if (cookie_bv->bv_len) /* still more to do */
1029 *cookie=ber_bvdup(cookie_bv);
1032 ber_bvfree(cookie_bv);
1033 ber_free(cookie_be, 1);
1037 ldap_controls_free(rcontrols);
1040 talloc_destroy(ctx);
1043 ber_free(ext_be, 1);
1050 /* if/when we decide to utf8-encode attrs, take out this next line */
1051 TALLOC_FREE(search_attrs);
1053 return ADS_ERROR(rc);
1056 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1057 int scope, const char *expr,
1058 const char **attrs, LDAPMessage **res,
1059 int *count, struct berval **cookie)
1061 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1066 * Get all results for a search. This uses ads_do_paged_search() to return
1067 * all entries in a large search.
1068 * @param ads connection to ads server
1069 * @param bind_path Base dn for the search
1070 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1071 * @param expr Search expression
1072 * @param attrs Attributes to retrieve
1073 * @param res ** which will contain results - free res* with ads_msgfree()
1074 * @return status of search
1076 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1077 int scope, const char *expr,
1078 const char **attrs, void *args,
1081 struct berval *cookie = NULL;
1086 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1089 if (!ADS_ERR_OK(status))
1092 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1094 LDAPMessage *res2 = NULL;
1096 LDAPMessage *msg, *next;
1098 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1099 attrs, args, &res2, &count, &cookie);
1101 if (!ADS_ERR_OK(status2)) break;
1103 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1104 that this works on all ldap libs, but I have only tested with openldap */
1105 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1106 next = ads_next_message(ads, msg);
1107 ldap_add_result_entry((LDAPMessage **)res, msg);
1109 /* note that we do not free res2, as the memory is now
1110 part of the main returned list */
1113 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1114 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1120 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1121 int scope, const char *expr,
1122 const char **attrs, LDAPMessage **res)
1124 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1127 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1128 int scope, const char *expr,
1129 const char **attrs, uint32 sd_flags,
1134 args.control = ADS_SD_FLAGS_OID;
1135 args.val = sd_flags;
1136 args.critical = True;
1138 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1143 * Run a function on all results for a search. Uses ads_do_paged_search() and
1144 * runs the function as each page is returned, using ads_process_results()
1145 * @param ads connection to ads server
1146 * @param bind_path Base dn for the search
1147 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1148 * @param expr Search expression - specified in local charset
1149 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1150 * @param fn Function which takes attr name, values list, and data_area
1151 * @param data_area Pointer which is passed to function on each call
1152 * @return status of search
1154 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1155 int scope, const char *expr, const char **attrs,
1156 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1159 struct berval *cookie = NULL;
1164 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1167 if (!ADS_ERR_OK(status)) return status;
1169 ads_process_results(ads, res, fn, data_area);
1170 ads_msgfree(ads, res);
1173 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1174 &res, &count, &cookie);
1176 if (!ADS_ERR_OK(status)) break;
1178 ads_process_results(ads, res, fn, data_area);
1179 ads_msgfree(ads, res);
1186 * Do a search with a timeout.
1187 * @param ads connection to ads server
1188 * @param bind_path Base dn for the search
1189 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1190 * @param expr Search expression
1191 * @param attrs Attributes to retrieve
1192 * @param res ** which will contain results - free res* with ads_msgfree()
1193 * @return status of search
1195 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1197 const char **attrs, LDAPMessage **res)
1200 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1201 size_t converted_size;
1205 if (!(ctx = talloc_init("ads_do_search"))) {
1206 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1207 return ADS_ERROR(LDAP_NO_MEMORY);
1210 /* 0 means the conversion worked but the result was empty
1211 so we only fail if it's negative. In any case, it always
1212 at least nulls out the dest */
1213 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1214 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1216 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1217 rc = LDAP_NO_MEMORY;
1221 if (!attrs || !(*attrs))
1222 search_attrs = NULL;
1224 /* This would be the utf8-encoded version...*/
1225 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1226 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1228 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1229 rc = LDAP_NO_MEMORY;
1234 /* see the note in ads_do_paged_search - we *must* disable referrals */
1235 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1237 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1238 search_attrs, 0, NULL, NULL,
1240 (LDAPMessage **)res);
1242 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1243 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1248 talloc_destroy(ctx);
1249 /* if/when we decide to utf8-encode attrs, take out this next line */
1250 TALLOC_FREE(search_attrs);
1251 return ADS_ERROR(rc);
1254 * Do a general ADS search
1255 * @param ads connection to ads server
1256 * @param res ** which will contain results - free res* with ads_msgfree()
1257 * @param expr Search expression
1258 * @param attrs Attributes to retrieve
1259 * @return status of search
1261 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1262 const char *expr, const char **attrs)
1264 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1269 * Do a search on a specific DistinguishedName
1270 * @param ads connection to ads server
1271 * @param res ** which will contain results - free res* with ads_msgfree()
1272 * @param dn DistinguishName to search
1273 * @param attrs Attributes to retrieve
1274 * @return status of search
1276 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1277 const char *dn, const char **attrs)
1279 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1284 * Free up memory from a ads_search
1285 * @param ads connection to ads server
1286 * @param msg Search results to free
1288 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1295 * Get a dn from search results
1296 * @param ads connection to ads server
1297 * @param msg Search result
1300 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1302 char *utf8_dn, *unix_dn;
1303 size_t converted_size;
1305 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1308 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1312 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1313 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1317 ldap_memfree(utf8_dn);
1322 * Get the parent from a dn
1323 * @param dn the dn to return the parent from
1324 * @return parent dn string
1326 char *ads_parent_dn(const char *dn)
1334 p = strchr(dn, ',');
1344 * Find a machine account given a hostname
1345 * @param ads connection to ads server
1346 * @param res ** which will contain results - free res* with ads_msgfree()
1347 * @param host Hostname to search for
1348 * @return status of search
1350 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1351 const char *machine)
1355 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1359 /* the easiest way to find a machine account anywhere in the tree
1360 is to look for hostname$ */
1361 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1362 DEBUG(1, ("asprintf failed!\n"));
1363 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1366 status = ads_search(ads, res, expr, attrs);
1372 * Initialize a list of mods to be used in a modify request
1373 * @param ctx An initialized TALLOC_CTX
1374 * @return allocated ADS_MODLIST
1376 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1378 #define ADS_MODLIST_ALLOC_SIZE 10
1381 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1382 /* -1 is safety to make sure we don't go over the end.
1383 need to reset it to NULL before doing ldap modify */
1384 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1386 return (ADS_MODLIST)mods;
1391 add an attribute to the list, with values list already constructed
1393 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1394 int mod_op, const char *name,
1395 const void *_invals)
1397 const void **invals = (const void **)_invals;
1399 LDAPMod **modlist = (LDAPMod **) *mods;
1400 struct berval **ber_values = NULL;
1401 char **char_values = NULL;
1404 mod_op = LDAP_MOD_DELETE;
1406 if (mod_op & LDAP_MOD_BVALUES)
1407 ber_values = ads_dup_values(ctx,
1408 (const struct berval **)invals);
1410 char_values = ads_push_strvals(ctx,
1411 (const char **) invals);
1414 /* find the first empty slot */
1415 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1417 if (modlist[curmod] == (LDAPMod *) -1) {
1418 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1419 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1420 return ADS_ERROR(LDAP_NO_MEMORY);
1421 memset(&modlist[curmod], 0,
1422 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1423 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1424 *mods = (ADS_MODLIST)modlist;
1427 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1428 return ADS_ERROR(LDAP_NO_MEMORY);
1429 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1430 if (mod_op & LDAP_MOD_BVALUES) {
1431 modlist[curmod]->mod_bvalues = ber_values;
1432 } else if (mod_op & LDAP_MOD_DELETE) {
1433 modlist[curmod]->mod_values = NULL;
1435 modlist[curmod]->mod_values = char_values;
1438 modlist[curmod]->mod_op = mod_op;
1439 return ADS_ERROR(LDAP_SUCCESS);
1443 * Add a single string value to a mod list
1444 * @param ctx An initialized TALLOC_CTX
1445 * @param mods An initialized ADS_MODLIST
1446 * @param name The attribute name to add
1447 * @param val The value to add - NULL means DELETE
1448 * @return ADS STATUS indicating success of add
1450 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1451 const char *name, const char *val)
1453 const char *values[2];
1459 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1460 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1464 * Add an array of string values to a mod list
1465 * @param ctx An initialized TALLOC_CTX
1466 * @param mods An initialized ADS_MODLIST
1467 * @param name The attribute name to add
1468 * @param vals The array of string values to add - NULL means DELETE
1469 * @return ADS STATUS indicating success of add
1471 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1472 const char *name, const char **vals)
1475 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1476 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1477 name, (const void **) vals);
1482 * Add a single ber-encoded value to a mod list
1483 * @param ctx An initialized TALLOC_CTX
1484 * @param mods An initialized ADS_MODLIST
1485 * @param name The attribute name to add
1486 * @param val The value to add - NULL means DELETE
1487 * @return ADS STATUS indicating success of add
1489 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1490 const char *name, const struct berval *val)
1492 const struct berval *values[2];
1497 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1498 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1499 name, (const void **) values);
1504 * Perform an ldap modify
1505 * @param ads connection to ads server
1506 * @param mod_dn DistinguishedName to modify
1507 * @param mods list of modifications to perform
1508 * @return status of modify
1510 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1513 char *utf8_dn = NULL;
1514 size_t converted_size;
1516 this control is needed to modify that contains a currently
1517 non-existent attribute (but allowable for the object) to run
1519 LDAPControl PermitModify = {
1520 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1523 LDAPControl *controls[2];
1525 controls[0] = &PermitModify;
1528 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1529 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1532 /* find the end of the list, marked by NULL or -1 */
1533 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1534 /* make sure the end of the list is NULL */
1536 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1537 (LDAPMod **) mods, controls, NULL);
1538 TALLOC_FREE(utf8_dn);
1539 return ADS_ERROR(ret);
1543 * Perform an ldap add
1544 * @param ads connection to ads server
1545 * @param new_dn DistinguishedName to add
1546 * @param mods list of attributes and values for DN
1547 * @return status of add
1549 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1552 char *utf8_dn = NULL;
1553 size_t converted_size;
1555 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1556 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1557 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1560 /* find the end of the list, marked by NULL or -1 */
1561 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1562 /* make sure the end of the list is NULL */
1565 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1566 TALLOC_FREE(utf8_dn);
1567 return ADS_ERROR(ret);
1571 * Delete a DistinguishedName
1572 * @param ads connection to ads server
1573 * @param new_dn DistinguishedName to delete
1574 * @return status of delete
1576 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1579 char *utf8_dn = NULL;
1580 size_t converted_size;
1581 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1582 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1583 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1586 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1587 TALLOC_FREE(utf8_dn);
1588 return ADS_ERROR(ret);
1592 * Build an org unit string
1593 * if org unit is Computers or blank then assume a container, otherwise
1594 * assume a / separated list of organisational units.
1595 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1596 * @param ads connection to ads server
1597 * @param org_unit Organizational unit
1598 * @return org unit string - caller must free
1600 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1604 if (!org_unit || !*org_unit) {
1606 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1608 /* samba4 might not yet respond to a wellknownobject-query */
1609 return ret ? ret : SMB_STRDUP("cn=Computers");
1612 if (strequal(org_unit, "Computers")) {
1613 return SMB_STRDUP("cn=Computers");
1616 /* jmcd: removed "\\" from the separation chars, because it is
1617 needed as an escape for chars like '#' which are valid in an
1619 return ads_build_path(org_unit, "/", "ou=", 1);
1623 * Get a org unit string for a well-known GUID
1624 * @param ads connection to ads server
1625 * @param wknguid Well known GUID
1626 * @return org unit string - caller must free
1628 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1631 LDAPMessage *res = NULL;
1632 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1633 **bind_dn_exp = NULL;
1634 const char *attrs[] = {"distinguishedName", NULL};
1635 int new_ln, wkn_ln, bind_ln, i;
1637 if (wknguid == NULL) {
1641 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1642 DEBUG(1, ("asprintf failed!\n"));
1646 status = ads_search_dn(ads, &res, base, attrs);
1647 if (!ADS_ERR_OK(status)) {
1648 DEBUG(1,("Failed while searching for: %s\n", base));
1652 if (ads_count_replies(ads, res) != 1) {
1656 /* substitute the bind-path from the well-known-guid-search result */
1657 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1662 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1667 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1672 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1674 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1677 new_ln = wkn_ln - bind_ln;
1679 ret = SMB_STRDUP(wkn_dn_exp[0]);
1684 for (i=1; i < new_ln; i++) {
1687 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1693 ret = SMB_STRDUP(s);
1702 ads_msgfree(ads, res);
1703 TALLOC_FREE(wkn_dn);
1705 ldap_value_free(wkn_dn_exp);
1708 ldap_value_free(bind_dn_exp);
1715 * Adds (appends) an item to an attribute array, rather then
1716 * replacing the whole list
1717 * @param ctx An initialized TALLOC_CTX
1718 * @param mods An initialized ADS_MODLIST
1719 * @param name name of the ldap attribute to append to
1720 * @param vals an array of values to add
1721 * @return status of addition
1724 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1725 const char *name, const char **vals)
1727 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1728 (const void *) vals);
1732 * Determines the an account's current KVNO via an LDAP lookup
1733 * @param ads An initialized ADS_STRUCT
1734 * @param account_name the NT samaccountname.
1735 * @return the kvno for the account, or -1 in case of a failure.
1738 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1740 LDAPMessage *res = NULL;
1741 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1743 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1744 char *dn_string = NULL;
1745 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1747 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1748 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1751 ret = ads_search(ads, &res, filter, attrs);
1753 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1754 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1755 ads_msgfree(ads, res);
1759 dn_string = ads_get_dn(ads, talloc_tos(), res);
1761 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1762 ads_msgfree(ads, res);
1765 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1766 TALLOC_FREE(dn_string);
1768 /* ---------------------------------------------------------
1769 * 0 is returned as a default KVNO from this point on...
1770 * This is done because Windows 2000 does not support key
1771 * version numbers. Chances are that a failure in the next
1772 * step is simply due to Windows 2000 being used for a
1773 * domain controller. */
1776 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1777 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1778 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1779 ads_msgfree(ads, res);
1784 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1785 ads_msgfree(ads, res);
1790 * Determines the computer account's current KVNO via an LDAP lookup
1791 * @param ads An initialized ADS_STRUCT
1792 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1793 * @return the kvno for the computer account, or -1 in case of a failure.
1796 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1798 char *computer_account = NULL;
1801 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1805 kvno = ads_get_kvno(ads, computer_account);
1806 free(computer_account);
1812 * This clears out all registered spn's for a given hostname
1813 * @param ads An initilaized ADS_STRUCT
1814 * @param machine_name the NetBIOS name of the computer.
1815 * @return 0 upon success, non-zero otherwise.
1818 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1821 LDAPMessage *res = NULL;
1823 const char *servicePrincipalName[1] = {NULL};
1824 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1825 char *dn_string = NULL;
1827 ret = ads_find_machine_acct(ads, &res, machine_name);
1828 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1829 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1830 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1831 ads_msgfree(ads, res);
1832 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1835 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1836 ctx = talloc_init("ads_clear_service_principal_names");
1838 ads_msgfree(ads, res);
1839 return ADS_ERROR(LDAP_NO_MEMORY);
1842 if (!(mods = ads_init_mods(ctx))) {
1843 talloc_destroy(ctx);
1844 ads_msgfree(ads, res);
1845 return ADS_ERROR(LDAP_NO_MEMORY);
1847 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1848 if (!ADS_ERR_OK(ret)) {
1849 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1850 ads_msgfree(ads, res);
1851 talloc_destroy(ctx);
1854 dn_string = ads_get_dn(ads, talloc_tos(), res);
1856 talloc_destroy(ctx);
1857 ads_msgfree(ads, res);
1858 return ADS_ERROR(LDAP_NO_MEMORY);
1860 ret = ads_gen_mod(ads, dn_string, mods);
1861 TALLOC_FREE(dn_string);
1862 if (!ADS_ERR_OK(ret)) {
1863 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1865 ads_msgfree(ads, res);
1866 talloc_destroy(ctx);
1870 ads_msgfree(ads, res);
1871 talloc_destroy(ctx);
1876 * This adds a service principal name to an existing computer account
1877 * (found by hostname) in AD.
1878 * @param ads An initialized ADS_STRUCT
1879 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1880 * @param my_fqdn The fully qualified DNS name of the machine
1881 * @param spn A string of the service principal to add, i.e. 'host'
1882 * @return 0 upon sucess, or non-zero if a failure occurs
1885 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1886 const char *my_fqdn, const char *spn)
1890 LDAPMessage *res = NULL;
1893 char *dn_string = NULL;
1894 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1896 ret = ads_find_machine_acct(ads, &res, machine_name);
1897 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1898 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1900 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1901 spn, machine_name, ads->config.realm));
1902 ads_msgfree(ads, res);
1903 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1906 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1907 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1908 ads_msgfree(ads, res);
1909 return ADS_ERROR(LDAP_NO_MEMORY);
1912 /* add short name spn */
1914 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1915 talloc_destroy(ctx);
1916 ads_msgfree(ads, res);
1917 return ADS_ERROR(LDAP_NO_MEMORY);
1920 strlower_m(&psp1[strlen(spn)]);
1921 servicePrincipalName[0] = psp1;
1923 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1924 psp1, machine_name));
1927 /* add fully qualified spn */
1929 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1930 ret = ADS_ERROR(LDAP_NO_MEMORY);
1934 strlower_m(&psp2[strlen(spn)]);
1935 servicePrincipalName[1] = psp2;
1937 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1938 psp2, machine_name));
1940 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1941 ret = ADS_ERROR(LDAP_NO_MEMORY);
1945 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1946 if (!ADS_ERR_OK(ret)) {
1947 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1951 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1952 ret = ADS_ERROR(LDAP_NO_MEMORY);
1956 ret = ads_gen_mod(ads, dn_string, mods);
1957 if (!ADS_ERR_OK(ret)) {
1958 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1964 ads_msgfree(ads, res);
1969 * adds a machine account to the ADS server
1970 * @param ads An intialized ADS_STRUCT
1971 * @param machine_name - the NetBIOS machine name of this account.
1972 * @param account_type A number indicating the type of account to create
1973 * @param org_unit The LDAP path in which to place this account
1974 * @return 0 upon success, or non-zero otherwise
1977 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1978 const char *org_unit)
1981 char *samAccountName, *controlstr;
1984 char *machine_escaped = NULL;
1986 const char *objectClass[] = {"top", "person", "organizationalPerson",
1987 "user", "computer", NULL};
1988 LDAPMessage *res = NULL;
1989 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1990 UF_DONT_EXPIRE_PASSWD |\
1991 UF_ACCOUNTDISABLE );
1993 if (!(ctx = talloc_init("ads_add_machine_acct")))
1994 return ADS_ERROR(LDAP_NO_MEMORY);
1996 ret = ADS_ERROR(LDAP_NO_MEMORY);
1998 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1999 if (!machine_escaped) {
2003 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2004 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2006 if ( !new_dn || !samAccountName ) {
2010 #ifndef ENCTYPE_ARCFOUR_HMAC
2011 acct_control |= UF_USE_DES_KEY_ONLY;
2014 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2018 if (!(mods = ads_init_mods(ctx))) {
2022 ads_mod_str(ctx, &mods, "cn", machine_name);
2023 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2024 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2025 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2027 ret = ads_gen_add(ads, new_dn, mods);
2030 SAFE_FREE(machine_escaped);
2031 ads_msgfree(ads, res);
2032 talloc_destroy(ctx);
2038 * move a machine account to another OU on the ADS server
2039 * @param ads - An intialized ADS_STRUCT
2040 * @param machine_name - the NetBIOS machine name of this account.
2041 * @param org_unit - The LDAP path in which to place this account
2042 * @param moved - whether we moved the machine account (optional)
2043 * @return 0 upon success, or non-zero otherwise
2046 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2047 const char *org_unit, bool *moved)
2051 LDAPMessage *res = NULL;
2052 char *filter = NULL;
2053 char *computer_dn = NULL;
2055 char *computer_rdn = NULL;
2056 bool need_move = False;
2058 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2059 rc = ADS_ERROR(LDAP_NO_MEMORY);
2063 /* Find pre-existing machine */
2064 rc = ads_search(ads, &res, filter, NULL);
2065 if (!ADS_ERR_OK(rc)) {
2069 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2071 rc = ADS_ERROR(LDAP_NO_MEMORY);
2075 parent_dn = ads_parent_dn(computer_dn);
2076 if (strequal(parent_dn, org_unit)) {
2082 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2083 rc = ADS_ERROR(LDAP_NO_MEMORY);
2087 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2088 org_unit, 1, NULL, NULL);
2089 rc = ADS_ERROR(ldap_status);
2092 ads_msgfree(ads, res);
2094 TALLOC_FREE(computer_dn);
2095 SAFE_FREE(computer_rdn);
2097 if (!ADS_ERR_OK(rc)) {
2109 dump a binary result from ldap
2111 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2114 for (i=0; values[i]; i++) {
2115 printf("%s: ", field);
2116 for (j=0; j<values[i]->bv_len; j++) {
2117 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2123 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2126 for (i=0; values[i]; i++) {
2131 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2132 smb_uuid_unpack(guid, &tmp);
2133 printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2138 dump a sid result from ldap
2140 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2143 for (i=0; values[i]; i++) {
2146 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2147 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2152 dump ntSecurityDescriptor
2154 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2156 TALLOC_CTX *frame = talloc_stackframe();
2157 struct security_descriptor *psd;
2160 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2161 values[0]->bv_len, &psd);
2162 if (!NT_STATUS_IS_OK(status)) {
2163 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2164 nt_errstr(status)));
2170 ads_disp_sd(ads, talloc_tos(), psd);
2177 dump a string result from ldap
2179 static void dump_string(const char *field, char **values)
2182 for (i=0; values[i]; i++) {
2183 printf("%s: %s\n", field, values[i]);
2188 dump a field from LDAP on stdout
2192 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2197 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2199 {"objectGUID", False, dump_guid},
2200 {"netbootGUID", False, dump_guid},
2201 {"nTSecurityDescriptor", False, dump_sd},
2202 {"dnsRecord", False, dump_binary},
2203 {"objectSid", False, dump_sid},
2204 {"tokenGroups", False, dump_sid},
2205 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2206 {"tokengroupsGlobalandUniversal", False, dump_sid},
2207 {"mS-DS-CreatorSID", False, dump_sid},
2208 {"msExchMailboxGuid", False, dump_guid},
2213 if (!field) { /* must be end of an entry */
2218 for (i=0; handlers[i].name; i++) {
2219 if (StrCaseCmp(handlers[i].name, field) == 0) {
2220 if (!values) /* first time, indicate string or not */
2221 return handlers[i].string;
2222 handlers[i].handler(ads, field, (struct berval **) values);
2226 if (!handlers[i].name) {
2227 if (!values) /* first time, indicate string conversion */
2229 dump_string(field, (char **)values);
2235 * Dump a result from LDAP on stdout
2236 * used for debugging
2237 * @param ads connection to ads server
2238 * @param res Results to dump
2241 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2243 ads_process_results(ads, res, ads_dump_field, NULL);
2247 * Walk through results, calling a function for each entry found.
2248 * The function receives a field name, a berval * array of values,
2249 * and a data area passed through from the start. The function is
2250 * called once with null for field and values at the end of each
2252 * @param ads connection to ads server
2253 * @param res Results to process
2254 * @param fn Function for processing each result
2255 * @param data_area user-defined area to pass to function
2257 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2258 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2263 size_t converted_size;
2265 if (!(ctx = talloc_init("ads_process_results")))
2268 for (msg = ads_first_entry(ads, res); msg;
2269 msg = ads_next_entry(ads, msg)) {
2273 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2274 (LDAPMessage *)msg,&b);
2276 utf8_field=ldap_next_attribute(ads->ldap.ld,
2277 (LDAPMessage *)msg,b)) {
2278 struct berval **ber_vals;
2279 char **str_vals, **utf8_vals;
2283 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2286 DEBUG(0,("ads_process_results: "
2287 "pull_utf8_talloc failed: %s",
2291 string = fn(ads, field, NULL, data_area);
2294 utf8_vals = ldap_get_values(ads->ldap.ld,
2295 (LDAPMessage *)msg, field);
2296 str_vals = ads_pull_strvals(ctx,
2297 (const char **) utf8_vals);
2298 fn(ads, field, (void **) str_vals, data_area);
2299 ldap_value_free(utf8_vals);
2301 ber_vals = ldap_get_values_len(ads->ldap.ld,
2302 (LDAPMessage *)msg, field);
2303 fn(ads, field, (void **) ber_vals, data_area);
2305 ldap_value_free_len(ber_vals);
2307 ldap_memfree(utf8_field);
2310 talloc_free_children(ctx);
2311 fn(ads, NULL, NULL, data_area); /* completed an entry */
2314 talloc_destroy(ctx);
2318 * count how many replies are in a LDAPMessage
2319 * @param ads connection to ads server
2320 * @param res Results to count
2321 * @return number of replies
2323 int ads_count_replies(ADS_STRUCT *ads, void *res)
2325 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2329 * pull the first entry from a ADS result
2330 * @param ads connection to ads server
2331 * @param res Results of search
2332 * @return first entry from result
2334 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2336 return ldap_first_entry(ads->ldap.ld, res);
2340 * pull the next entry from a ADS result
2341 * @param ads connection to ads server
2342 * @param res Results of search
2343 * @return next entry from result
2345 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2347 return ldap_next_entry(ads->ldap.ld, res);
2351 * pull the first message from a ADS result
2352 * @param ads connection to ads server
2353 * @param res Results of search
2354 * @return first message from result
2356 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2358 return ldap_first_message(ads->ldap.ld, res);
2362 * pull the next message from a ADS result
2363 * @param ads connection to ads server
2364 * @param res Results of search
2365 * @return next message from result
2367 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2369 return ldap_next_message(ads->ldap.ld, res);
2373 * pull a single string from a ADS result
2374 * @param ads connection to ads server
2375 * @param mem_ctx TALLOC_CTX to use for allocating result string
2376 * @param msg Results of search
2377 * @param field Attribute to retrieve
2378 * @return Result string in talloc context
2380 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2386 size_t converted_size;
2388 values = ldap_get_values(ads->ldap.ld, msg, field);
2392 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2397 ldap_value_free(values);
2402 * pull an array of strings from a ADS result
2403 * @param ads connection to ads server
2404 * @param mem_ctx TALLOC_CTX to use for allocating result string
2405 * @param msg Results of search
2406 * @param field Attribute to retrieve
2407 * @return Result strings in talloc context
2409 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2410 LDAPMessage *msg, const char *field,
2416 size_t converted_size;
2418 values = ldap_get_values(ads->ldap.ld, msg, field);
2422 *num_values = ldap_count_values(values);
2424 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2426 ldap_value_free(values);
2430 for (i=0;i<*num_values;i++) {
2431 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2434 ldap_value_free(values);
2440 ldap_value_free(values);
2445 * pull an array of strings from a ADS result
2446 * (handle large multivalue attributes with range retrieval)
2447 * @param ads connection to ads server
2448 * @param mem_ctx TALLOC_CTX to use for allocating result string
2449 * @param msg Results of search
2450 * @param field Attribute to retrieve
2451 * @param current_strings strings returned by a previous call to this function
2452 * @param next_attribute The next query should ask for this attribute
2453 * @param num_values How many values did we get this time?
2454 * @param more_values Are there more values to get?
2455 * @return Result strings in talloc context
2457 char **ads_pull_strings_range(ADS_STRUCT *ads,
2458 TALLOC_CTX *mem_ctx,
2459 LDAPMessage *msg, const char *field,
2460 char **current_strings,
2461 const char **next_attribute,
2462 size_t *num_strings,
2466 char *expected_range_attrib, *range_attr;
2467 BerElement *ptr = NULL;
2470 size_t num_new_strings;
2471 unsigned long int range_start;
2472 unsigned long int range_end;
2474 /* we might have been given the whole lot anyway */
2475 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2476 *more_strings = False;
2480 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2482 /* look for Range result */
2483 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2485 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2486 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2487 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2495 /* nothing here - this field is just empty */
2496 *more_strings = False;
2500 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2501 &range_start, &range_end) == 2) {
2502 *more_strings = True;
2504 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2505 &range_start) == 1) {
2506 *more_strings = False;
2508 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2510 ldap_memfree(range_attr);
2511 *more_strings = False;
2516 if ((*num_strings) != range_start) {
2517 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2518 " - aborting range retreival\n",
2519 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2520 ldap_memfree(range_attr);
2521 *more_strings = False;
2525 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2527 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2528 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2529 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2530 range_attr, (unsigned long int)range_end - range_start + 1,
2531 (unsigned long int)num_new_strings));
2532 ldap_memfree(range_attr);
2533 *more_strings = False;
2537 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2538 *num_strings + num_new_strings);
2540 if (strings == NULL) {
2541 ldap_memfree(range_attr);
2542 *more_strings = False;
2546 if (new_strings && num_new_strings) {
2547 memcpy(&strings[*num_strings], new_strings,
2548 sizeof(*new_strings) * num_new_strings);
2551 (*num_strings) += num_new_strings;
2553 if (*more_strings) {
2554 *next_attribute = talloc_asprintf(mem_ctx,
2559 if (!*next_attribute) {
2560 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2561 ldap_memfree(range_attr);
2562 *more_strings = False;
2567 ldap_memfree(range_attr);
2573 * pull a single uint32 from a ADS result
2574 * @param ads connection to ads server
2575 * @param msg Results of search
2576 * @param field Attribute to retrieve
2577 * @param v Pointer to int to store result
2578 * @return boolean inidicating success
2580 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2585 values = ldap_get_values(ads->ldap.ld, msg, field);
2589 ldap_value_free(values);
2593 *v = atoi(values[0]);
2594 ldap_value_free(values);
2599 * pull a single objectGUID from an ADS result
2600 * @param ads connection to ADS server
2601 * @param msg results of search
2602 * @param guid 37-byte area to receive text guid
2603 * @return boolean indicating success
2605 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2608 UUID_FLAT flat_guid;
2610 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2615 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2616 smb_uuid_unpack(flat_guid, guid);
2617 ldap_value_free(values);
2620 ldap_value_free(values);
2627 * pull a single struct dom_sid from a ADS result
2628 * @param ads connection to ads server
2629 * @param msg Results of search
2630 * @param field Attribute to retrieve
2631 * @param sid Pointer to sid to store result
2632 * @return boolean inidicating success
2634 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2635 struct dom_sid *sid)
2637 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2641 * pull an array of struct dom_sids from a ADS result
2642 * @param ads connection to ads server
2643 * @param mem_ctx TALLOC_CTX for allocating sid array
2644 * @param msg Results of search
2645 * @param field Attribute to retrieve
2646 * @param sids pointer to sid array to allocate
2647 * @return the count of SIDs pulled
2649 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2650 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2652 struct berval **values;
2656 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2661 for (i=0; values[i]; i++)
2665 (*sids) = TALLOC_ARRAY(mem_ctx, struct dom_sid, i);
2667 ldap_value_free_len(values);
2675 for (i=0; values[i]; i++) {
2676 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2678 DEBUG(10, ("pulling SID: %s\n",
2679 sid_string_dbg(&(*sids)[count])));
2684 ldap_value_free_len(values);
2689 * pull a struct security_descriptor from a ADS result
2690 * @param ads connection to ads server
2691 * @param mem_ctx TALLOC_CTX for allocating sid array
2692 * @param msg Results of search
2693 * @param field Attribute to retrieve
2694 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2695 * @return boolean inidicating success
2697 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2698 LDAPMessage *msg, const char *field,
2699 struct security_descriptor **sd)
2701 struct berval **values;
2704 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2706 if (!values) return false;
2710 status = unmarshall_sec_desc(mem_ctx,
2711 (uint8 *)values[0]->bv_val,
2712 values[0]->bv_len, sd);
2713 if (!NT_STATUS_IS_OK(status)) {
2714 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2715 nt_errstr(status)));
2720 ldap_value_free_len(values);
2725 * in order to support usernames longer than 21 characters we need to
2726 * use both the sAMAccountName and the userPrincipalName attributes
2727 * It seems that not all users have the userPrincipalName attribute set
2729 * @param ads connection to ads server
2730 * @param mem_ctx TALLOC_CTX for allocating sid array
2731 * @param msg Results of search
2732 * @return the username
2734 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2740 /* lookup_name() only works on the sAMAccountName to
2741 returning the username portion of userPrincipalName
2742 breaks winbindd_getpwnam() */
2744 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2745 if (ret && (p = strchr_m(ret, '@'))) {
2750 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2755 * find the update serial number - this is the core of the ldap cache
2756 * @param ads connection to ads server
2757 * @param ads connection to ADS server
2758 * @param usn Pointer to retrieved update serial number
2759 * @return status of search
2761 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2763 const char *attrs[] = {"highestCommittedUSN", NULL};
2767 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2768 if (!ADS_ERR_OK(status))
2771 if (ads_count_replies(ads, res) != 1) {
2772 ads_msgfree(ads, res);
2773 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2776 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2777 ads_msgfree(ads, res);
2778 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2781 ads_msgfree(ads, res);
2785 /* parse a ADS timestring - typical string is
2786 '20020917091222.0Z0' which means 09:12.22 17th September
2788 static time_t ads_parse_time(const char *str)
2794 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2795 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2796 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2805 /********************************************************************
2806 ********************************************************************/
2808 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2810 const char *attrs[] = {"currentTime", NULL};
2815 ADS_STRUCT *ads_s = ads;
2817 if (!(ctx = talloc_init("ads_current_time"))) {
2818 return ADS_ERROR(LDAP_NO_MEMORY);
2821 /* establish a new ldap tcp session if necessary */
2823 if ( !ads->ldap.ld ) {
2824 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2825 ads->server.ldap_server )) == NULL )
2829 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2830 status = ads_connect( ads_s );
2831 if ( !ADS_ERR_OK(status))
2835 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2836 if (!ADS_ERR_OK(status)) {
2840 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2842 ads_msgfree(ads_s, res);
2843 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2847 /* but save the time and offset in the original ADS_STRUCT */
2849 ads->config.current_time = ads_parse_time(timestr);
2851 if (ads->config.current_time != 0) {
2852 ads->auth.time_offset = ads->config.current_time - time(NULL);
2853 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2856 ads_msgfree(ads, res);
2858 status = ADS_SUCCESS;
2861 /* free any temporary ads connections */
2862 if ( ads_s != ads ) {
2863 ads_destroy( &ads_s );
2865 talloc_destroy(ctx);
2870 /********************************************************************
2871 ********************************************************************/
2873 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2875 const char *attrs[] = {"domainFunctionality", NULL};
2878 ADS_STRUCT *ads_s = ads;
2880 *val = DS_DOMAIN_FUNCTION_2000;
2882 /* establish a new ldap tcp session if necessary */
2884 if ( !ads->ldap.ld ) {
2885 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2886 ads->server.ldap_server )) == NULL )
2888 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2891 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2892 status = ads_connect( ads_s );
2893 if ( !ADS_ERR_OK(status))
2897 /* If the attribute does not exist assume it is a Windows 2000
2898 functional domain */
2900 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2901 if (!ADS_ERR_OK(status)) {
2902 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2903 status = ADS_SUCCESS;
2908 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2909 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2911 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2914 ads_msgfree(ads, res);
2917 /* free any temporary ads connections */
2918 if ( ads_s != ads ) {
2919 ads_destroy( &ads_s );
2926 * find the domain sid for our domain
2927 * @param ads connection to ads server
2928 * @param sid Pointer to domain sid
2929 * @return status of search
2931 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2933 const char *attrs[] = {"objectSid", NULL};
2937 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2939 if (!ADS_ERR_OK(rc)) return rc;
2940 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2941 ads_msgfree(ads, res);
2942 return ADS_ERROR_SYSTEM(ENOENT);
2944 ads_msgfree(ads, res);
2950 * find our site name
2951 * @param ads connection to ads server
2952 * @param mem_ctx Pointer to talloc context
2953 * @param site_name Pointer to the sitename
2954 * @return status of search
2956 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2960 const char *dn, *service_name;
2961 const char *attrs[] = { "dsServiceName", NULL };
2963 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2964 if (!ADS_ERR_OK(status)) {
2968 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2969 if (service_name == NULL) {
2970 ads_msgfree(ads, res);
2971 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2974 ads_msgfree(ads, res);
2976 /* go up three levels */
2977 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2979 return ADS_ERROR(LDAP_NO_MEMORY);
2982 *site_name = talloc_strdup(mem_ctx, dn);
2983 if (*site_name == NULL) {
2984 return ADS_ERROR(LDAP_NO_MEMORY);
2989 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2994 * find the site dn where a machine resides
2995 * @param ads connection to ads server
2996 * @param mem_ctx Pointer to talloc context
2997 * @param computer_name name of the machine
2998 * @param site_name Pointer to the sitename
2999 * @return status of search
3001 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3005 const char *parent, *filter;
3006 char *config_context = NULL;
3009 /* shortcut a query */
3010 if (strequal(computer_name, ads->config.ldap_server_name)) {
3011 return ads_site_dn(ads, mem_ctx, site_dn);
3014 status = ads_config_path(ads, mem_ctx, &config_context);
3015 if (!ADS_ERR_OK(status)) {
3019 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3020 if (filter == NULL) {
3021 return ADS_ERROR(LDAP_NO_MEMORY);
3024 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3025 filter, NULL, &res);
3026 if (!ADS_ERR_OK(status)) {
3030 if (ads_count_replies(ads, res) != 1) {
3031 ads_msgfree(ads, res);
3032 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3035 dn = ads_get_dn(ads, mem_ctx, res);
3037 ads_msgfree(ads, res);
3038 return ADS_ERROR(LDAP_NO_MEMORY);
3041 /* go up three levels */
3042 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3043 if (parent == NULL) {
3044 ads_msgfree(ads, res);
3046 return ADS_ERROR(LDAP_NO_MEMORY);
3049 *site_dn = talloc_strdup(mem_ctx, parent);
3050 if (*site_dn == NULL) {
3051 ads_msgfree(ads, res);
3053 return ADS_ERROR(LDAP_NO_MEMORY);
3057 ads_msgfree(ads, res);
3063 * get the upn suffixes for a domain
3064 * @param ads connection to ads server
3065 * @param mem_ctx Pointer to talloc context
3066 * @param suffixes Pointer to an array of suffixes
3067 * @param num_suffixes Pointer to the number of suffixes
3068 * @return status of search
3070 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3075 char *config_context = NULL;
3076 const char *attrs[] = { "uPNSuffixes", NULL };
3078 status = ads_config_path(ads, mem_ctx, &config_context);
3079 if (!ADS_ERR_OK(status)) {
3083 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3085 return ADS_ERROR(LDAP_NO_MEMORY);
3088 status = ads_search_dn(ads, &res, base, attrs);
3089 if (!ADS_ERR_OK(status)) {
3093 if (ads_count_replies(ads, res) != 1) {
3094 ads_msgfree(ads, res);
3095 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3098 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3099 if ((*suffixes) == NULL) {
3100 ads_msgfree(ads, res);
3101 return ADS_ERROR(LDAP_NO_MEMORY);
3104 ads_msgfree(ads, res);
3110 * get the joinable ous for a domain
3111 * @param ads connection to ads server
3112 * @param mem_ctx Pointer to talloc context
3113 * @param ous Pointer to an array of ous
3114 * @param num_ous Pointer to the number of ous
3115 * @return status of search
3117 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3118 TALLOC_CTX *mem_ctx,
3123 LDAPMessage *res = NULL;
3124 LDAPMessage *msg = NULL;
3125 const char *attrs[] = { "dn", NULL };
3128 status = ads_search(ads, &res,
3129 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3131 if (!ADS_ERR_OK(status)) {
3135 count = ads_count_replies(ads, res);
3137 ads_msgfree(ads, res);
3138 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3141 for (msg = ads_first_entry(ads, res); msg;
3142 msg = ads_next_entry(ads, msg)) {
3146 dn = ads_get_dn(ads, talloc_tos(), msg);
3148 ads_msgfree(ads, res);
3149 return ADS_ERROR(LDAP_NO_MEMORY);
3152 if (!add_string_to_array(mem_ctx, dn,
3153 (const char ***)ous,
3156 ads_msgfree(ads, res);
3157 return ADS_ERROR(LDAP_NO_MEMORY);
3163 ads_msgfree(ads, res);
3170 * pull a struct dom_sid from an extended dn string
3171 * @param mem_ctx TALLOC_CTX
3172 * @param extended_dn string
3173 * @param flags string type of extended_dn
3174 * @param sid pointer to a struct dom_sid
3175 * @return NT_STATUS_OK on success,
3176 * NT_INVALID_PARAMETER on error,
3177 * NT_STATUS_NOT_FOUND if no SID present
3179 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3180 const char *extended_dn,
3181 enum ads_extended_dn_flags flags,
3182 struct dom_sid *sid)
3187 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3190 /* otherwise extended_dn gets stripped off */
3191 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3192 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3195 * ADS_EXTENDED_DN_HEX_STRING:
3196 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3198 * ADS_EXTENDED_DN_STRING (only with w2k3):
3199 * <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
3201 * Object with no SID, such as an Exchange Public Folder
3202 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3205 p = strchr(dn, ';');
3207 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3210 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3211 DEBUG(5,("No SID present in extended dn\n"));
3212 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3215 p += strlen(";<SID=");
3219 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3224 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3228 case ADS_EXTENDED_DN_STRING:
3229 if (!string_to_sid(sid, p)) {
3230 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3233 case ADS_EXTENDED_DN_HEX_STRING: {
3237 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3239 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3242 if (!sid_parse(buf, buf_len, sid)) {
3243 DEBUG(10,("failed to parse sid\n"));
3244 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3249 DEBUG(10,("unknown extended dn format\n"));
3250 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3253 return ADS_ERROR_NT(NT_STATUS_OK);
3257 * pull an array of struct dom_sids from a ADS result
3258 * @param ads connection to ads server
3259 * @param mem_ctx TALLOC_CTX for allocating sid array
3260 * @param msg Results of search
3261 * @param field Attribute to retrieve
3262 * @param flags string type of extended_dn
3263 * @param sids pointer to sid array to allocate
3264 * @return the count of SIDs pulled
3266 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3267 TALLOC_CTX *mem_ctx,
3270 enum ads_extended_dn_flags flags,
3271 struct dom_sid **sids)
3275 size_t dn_count, ret_count = 0;
3278 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3279 &dn_count)) == NULL) {
3283 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, dn_count + 1);
3285 TALLOC_FREE(dn_strings);
3289 for (i=0; i<dn_count; i++) {
3290 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3291 flags, &(*sids)[i]);
3292 if (!ADS_ERR_OK(rc)) {
3293 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3294 NT_STATUS_NOT_FOUND)) {
3299 TALLOC_FREE(dn_strings);
3306 TALLOC_FREE(dn_strings);
3311 /********************************************************************
3312 ********************************************************************/
3314 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3316 LDAPMessage *res = NULL;
3321 status = ads_find_machine_acct(ads, &res, global_myname());
3322 if (!ADS_ERR_OK(status)) {
3323 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3328 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3329 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3333 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3334 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3338 ads_msgfree(ads, res);
3343 /********************************************************************
3344 ********************************************************************/
3346 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3348 LDAPMessage *res = NULL;
3353 status = ads_find_machine_acct(ads, &res, machine_name);
3354 if (!ADS_ERR_OK(status)) {
3355 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3360 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3361 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3365 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3366 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3370 ads_msgfree(ads, res);
3375 /********************************************************************
3376 ********************************************************************/
3378 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3380 LDAPMessage *res = NULL;
3385 status = ads_find_machine_acct(ads, &res, global_myname());
3386 if (!ADS_ERR_OK(status)) {
3387 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3392 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3393 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3397 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3398 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3402 ads_msgfree(ads, res);
3409 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3412 * Join a machine to a realm
3413 * Creates the machine account and sets the machine password
3414 * @param ads connection to ads server
3415 * @param machine name of host to add
3416 * @param org_unit Organizational unit to place machine in
3417 * @return status of join
3419 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3420 uint32 account_type, const char *org_unit)
3423 LDAPMessage *res = NULL;
3426 /* machine name must be lowercase */
3427 machine = SMB_STRDUP(machine_name);
3428 strlower_m(machine);
3431 status = ads_find_machine_acct(ads, (void **)&res, machine);
3432 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3433 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3434 status = ads_leave_realm(ads, machine);
3435 if (!ADS_ERR_OK(status)) {
3436 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3437 machine, ads->config.realm));
3442 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3443 if (!ADS_ERR_OK(status)) {
3444 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3449 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3450 if (!ADS_ERR_OK(status)) {
3451 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3457 ads_msgfree(ads, res);
3464 * Delete a machine from the realm
3465 * @param ads connection to ads server
3466 * @param hostname Machine to remove
3467 * @return status of delete
3469 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3474 char *hostnameDN, *host;
3476 LDAPControl ldap_control;
3477 LDAPControl * pldap_control[2] = {NULL, NULL};
3479 pldap_control[0] = &ldap_control;
3480 memset(&ldap_control, 0, sizeof(LDAPControl));
3481 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3483 /* hostname must be lowercase */
3484 host = SMB_STRDUP(hostname);
3487 status = ads_find_machine_acct(ads, &res, host);
3488 if (!ADS_ERR_OK(status)) {
3489 DEBUG(0, ("Host account for %s does not exist.\n", host));
3494 msg = ads_first_entry(ads, res);
3497 return ADS_ERROR_SYSTEM(ENOENT);
3500 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3502 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3504 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3506 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3509 if (rc != LDAP_SUCCESS) {
3510 const char *attrs[] = { "cn", NULL };
3511 LDAPMessage *msg_sub;
3513 /* we only search with scope ONE, we do not expect any further
3514 * objects to be created deeper */
3516 status = ads_do_search_retry(ads, hostnameDN,
3517 LDAP_SCOPE_ONELEVEL,
3518 "(objectclass=*)", attrs, &res);
3520 if (!ADS_ERR_OK(status)) {
3522 TALLOC_FREE(hostnameDN);
3526 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3527 msg_sub = ads_next_entry(ads, msg_sub)) {
3531 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3533 TALLOC_FREE(hostnameDN);
3534 return ADS_ERROR(LDAP_NO_MEMORY);
3537 status = ads_del_dn(ads, dn);
3538 if (!ADS_ERR_OK(status)) {
3539 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3542 TALLOC_FREE(hostnameDN);
3549 /* there should be no subordinate objects anymore */
3550 status = ads_do_search_retry(ads, hostnameDN,
3551 LDAP_SCOPE_ONELEVEL,
3552 "(objectclass=*)", attrs, &res);
3554 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3556 TALLOC_FREE(hostnameDN);
3560 /* delete hostnameDN now */
3561 status = ads_del_dn(ads, hostnameDN);
3562 if (!ADS_ERR_OK(status)) {
3564 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3565 TALLOC_FREE(hostnameDN);
3570 TALLOC_FREE(hostnameDN);
3572 status = ads_find_machine_acct(ads, &res, host);
3573 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3574 DEBUG(3, ("Failed to remove host account.\n"));
3584 * pull all token-sids from an LDAP dn
3585 * @param ads connection to ads server
3586 * @param mem_ctx TALLOC_CTX for allocating sid array
3587 * @param dn of LDAP object
3588 * @param user_sid pointer to struct dom_sid (objectSid)
3589 * @param primary_group_sid pointer to struct dom_sid (self composed)
3590 * @param sids pointer to sid array to allocate
3591 * @param num_sids counter of SIDs pulled
3592 * @return status of token query
3594 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3595 TALLOC_CTX *mem_ctx,
3597 struct dom_sid *user_sid,
3598 struct dom_sid *primary_group_sid,
3599 struct dom_sid **sids,
3603 LDAPMessage *res = NULL;
3605 size_t tmp_num_sids;
3606 struct dom_sid *tmp_sids;
3607 struct dom_sid tmp_user_sid;
3608 struct dom_sid tmp_primary_group_sid;
3610 const char *attrs[] = {
3617 status = ads_search_retry_dn(ads, &res, dn, attrs);
3618 if (!ADS_ERR_OK(status)) {
3622 count = ads_count_replies(ads, res);
3624 ads_msgfree(ads, res);
3625 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3628 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3629 ads_msgfree(ads, res);
3630 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3633 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3634 ads_msgfree(ads, res);
3635 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3639 /* hack to compose the primary group sid without knowing the
3642 struct dom_sid domsid;
3645 sid_copy(&domsid, &tmp_user_sid);
3647 if (!sid_split_rid(&domsid, &dummy_rid)) {
3648 ads_msgfree(ads, res);
3649 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3652 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3653 ads_msgfree(ads, res);
3654 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3658 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3660 if (tmp_num_sids == 0 || !tmp_sids) {
3661 ads_msgfree(ads, res);
3662 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3666 *num_sids = tmp_num_sids;
3674 *user_sid = tmp_user_sid;
3677 if (primary_group_sid) {
3678 *primary_group_sid = tmp_primary_group_sid;
3681 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3683 ads_msgfree(ads, res);
3684 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3688 * Find a sAMAccoutName in LDAP
3689 * @param ads connection to ads server
3690 * @param mem_ctx TALLOC_CTX for allocating sid array
3691 * @param samaccountname to search
3692 * @param uac_ret uint32 pointer userAccountControl attribute value
3693 * @param dn_ret pointer to dn
3694 * @return status of token query
3696 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3697 TALLOC_CTX *mem_ctx,
3698 const char *samaccountname,
3700 const char **dn_ret)
3703 const char *attrs[] = { "userAccountControl", NULL };
3705 LDAPMessage *res = NULL;
3709 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3711 if (filter == NULL) {
3712 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3716 status = ads_do_search_all(ads, ads->config.bind_path,
3718 filter, attrs, &res);
3720 if (!ADS_ERR_OK(status)) {
3724 if (ads_count_replies(ads, res) != 1) {
3725 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3729 dn = ads_get_dn(ads, talloc_tos(), res);
3731 status = ADS_ERROR(LDAP_NO_MEMORY);
3735 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3736 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3745 *dn_ret = talloc_strdup(mem_ctx, dn);
3747 status = ADS_ERROR(LDAP_NO_MEMORY);
3753 ads_msgfree(ads, res);
3759 * find our configuration path
3760 * @param ads connection to ads server
3761 * @param mem_ctx Pointer to talloc context
3762 * @param config_path Pointer to the config path
3763 * @return status of search
3765 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3766 TALLOC_CTX *mem_ctx,
3770 LDAPMessage *res = NULL;
3771 const char *config_context = NULL;
3772 const char *attrs[] = { "configurationNamingContext", NULL };
3774 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3775 "(objectclass=*)", attrs, &res);
3776 if (!ADS_ERR_OK(status)) {
3780 config_context = ads_pull_string(ads, mem_ctx, res,
3781 "configurationNamingContext");
3782 ads_msgfree(ads, res);
3783 if (!config_context) {
3784 return ADS_ERROR(LDAP_NO_MEMORY);
3788 *config_path = talloc_strdup(mem_ctx, config_context);
3789 if (!*config_path) {
3790 return ADS_ERROR(LDAP_NO_MEMORY);
3794 return ADS_ERROR(LDAP_SUCCESS);
3798 * find the displayName of an extended right
3799 * @param ads connection to ads server
3800 * @param config_path The config path
3801 * @param mem_ctx Pointer to talloc context
3802 * @param GUID struct of the rightsGUID
3803 * @return status of search
3805 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3806 const char *config_path,
3807 TALLOC_CTX *mem_ctx,
3808 const struct GUID *rights_guid)
3811 LDAPMessage *res = NULL;
3813 const char *attrs[] = { "displayName", NULL };
3814 const char *result = NULL;
3817 if (!ads || !mem_ctx || !rights_guid) {
3821 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3822 GUID_string(mem_ctx, rights_guid));
3827 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3832 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3834 if (!ADS_ERR_OK(rc)) {
3838 if (ads_count_replies(ads, res) != 1) {
3842 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3845 ads_msgfree(ads, res);
3850 * verify or build and verify an account ou
3851 * @param mem_ctx Pointer to talloc context
3852 * @param ads connection to ads server
3854 * @return status of search
3857 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3859 const char **account_ou)
3865 exploded_dn = ldap_explode_dn(*account_ou, 0);
3867 ldap_value_free(exploded_dn);
3871 ou_string = ads_ou_string(ads, *account_ou);
3873 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3876 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3877 ads->config.bind_path);
3878 SAFE_FREE(ou_string);
3881 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3884 exploded_dn = ldap_explode_dn(name, 0);
3886 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3888 ldap_value_free(exploded_dn);