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 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
87 #ifdef HAVE_LDAP_INITIALIZE
88 ldap_err = ldap_initialize(&ldp, uri);
90 ldp = ldap_open(server, port);
92 ldap_err = LDAP_SUCCESS;
94 ldap_err = LDAP_OTHER;
97 if (ldap_err != LDAP_SUCCESS) {
98 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
99 uri, ldap_err2string(ldap_err)));
101 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
105 /* Teardown timeout. */
107 CatchSignal(SIGALRM, SIG_IGN);
113 static int ldap_search_with_timeout(LDAP *ld,
114 LDAP_CONST char *base,
116 LDAP_CONST char *filter,
119 LDAPControl **sctrls,
120 LDAPControl **cctrls,
124 int to = lp_ldap_timeout();
125 struct timeval timeout;
126 struct timeval *timeout_ptr = NULL;
129 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
135 timeout_ptr = &timeout;
137 /* Setup alarm timeout. */
138 CatchSignal(SIGALRM, gotalarm_sig);
139 /* Make the alarm time one second beyond
140 the timout we're setting for the
141 remote search timeout, to allow that
142 to fire in preference. */
144 /* End setup timeout. */
148 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
149 attrsonly, sctrls, cctrls, timeout_ptr,
153 /* Teardown alarm timeout. */
154 CatchSignal(SIGALRM, SIG_IGN);
159 return LDAP_TIMELIMIT_EXCEEDED;
162 * A bug in OpenLDAP means ldap_search_ext_s can return
163 * LDAP_SUCCESS but with a NULL res pointer. Cope with
164 * this. See bug #6279 for details. JRA.
168 return LDAP_TIMELIMIT_EXCEEDED;
174 /**********************************************
175 Do client and server sitename match ?
176 **********************************************/
178 bool ads_sitename_match(ADS_STRUCT *ads)
180 if (ads->config.server_site_name == NULL &&
181 ads->config.client_site_name == NULL ) {
182 DEBUG(10,("ads_sitename_match: both null\n"));
185 if (ads->config.server_site_name &&
186 ads->config.client_site_name &&
187 strequal(ads->config.server_site_name,
188 ads->config.client_site_name)) {
189 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
192 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
193 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
194 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
198 /**********************************************
199 Is this the closest DC ?
200 **********************************************/
202 bool ads_closest_dc(ADS_STRUCT *ads)
204 if (ads->config.flags & NBT_SERVER_CLOSEST) {
205 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
209 /* not sure if this can ever happen */
210 if (ads_sitename_match(ads)) {
211 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
215 if (ads->config.client_site_name == NULL) {
216 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
220 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
221 ads->config.ldap_server_name));
228 try a connection to a given ldap server, returning True and setting the servers IP
229 in the ads struct if successful
231 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
232 struct sockaddr_storage *ss)
234 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
235 TALLOC_CTX *frame = talloc_stackframe();
237 char addr[INET6_ADDRSTRLEN];
244 print_sockaddr(addr, sizeof(addr), ss);
246 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
247 addr, ads->server.realm));
249 ZERO_STRUCT( cldap_reply );
251 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
252 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
257 /* Check the CLDAP reply flags */
259 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
260 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
266 /* Fill in the ads->config values */
268 SAFE_FREE(ads->config.realm);
269 SAFE_FREE(ads->config.bind_path);
270 SAFE_FREE(ads->config.ldap_server_name);
271 SAFE_FREE(ads->config.server_site_name);
272 SAFE_FREE(ads->config.client_site_name);
273 SAFE_FREE(ads->server.workgroup);
275 ads->config.flags = cldap_reply.server_type;
276 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
277 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
278 if (!strupper_m(ads->config.realm)) {
283 ads->config.bind_path = ads_build_dn(ads->config.realm);
284 if (*cldap_reply.server_site) {
285 ads->config.server_site_name =
286 SMB_STRDUP(cldap_reply.server_site);
288 if (*cldap_reply.client_site) {
289 ads->config.client_site_name =
290 SMB_STRDUP(cldap_reply.client_site);
292 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
294 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
297 /* Store our site name. */
298 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
299 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
309 /**********************************************************************
310 Try to find an AD dc using our internal name resolution routines
311 Try the realm first and then then workgroup name if netbios is not
313 **********************************************************************/
315 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
317 const char *c_domain;
320 struct ip_service *ip_list;
323 bool got_realm = False;
324 bool use_own_domain = False;
326 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
329 /* if the realm and workgroup are both empty, assume they are ours */
332 c_realm = ads->server.realm;
334 if ( !c_realm || !*c_realm ) {
335 /* special case where no realm and no workgroup means our own */
336 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
337 use_own_domain = True;
338 c_realm = lp_realm();
342 if (c_realm && *c_realm)
345 /* we need to try once with the realm name and fallback to the
346 netbios domain name if we fail (if netbios has not been disabled */
348 if ( !got_realm && !lp_disable_netbios() ) {
349 c_realm = ads->server.workgroup;
350 if (!c_realm || !*c_realm) {
351 if ( use_own_domain )
352 c_realm = lp_workgroup();
356 if ( !c_realm || !*c_realm ) {
357 DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know "
359 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
362 if ( use_own_domain ) {
363 c_domain = lp_workgroup();
365 c_domain = ads->server.workgroup;
372 * In case of LDAP we use get_dc_name() as that
373 * creates the custom krb5.conf file
375 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
377 struct sockaddr_storage ip_out;
379 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
380 (got_realm ? "realm" : "domain"), realm));
382 ok = get_dc_name(domain, realm, srv_name, &ip_out);
385 * we call ads_try_connect() to fill in the
386 * ads->config details
388 ok = ads_try_connect(ads, false, &ip_out);
394 return NT_STATUS_NO_LOGON_SERVERS;
397 sitename = sitename_fetch(talloc_tos(), realm);
401 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
402 (got_realm ? "realm" : "domain"), realm));
404 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
405 if (!NT_STATUS_IS_OK(status)) {
406 /* fall back to netbios if we can */
407 if ( got_realm && !lp_disable_netbios() ) {
412 TALLOC_FREE(sitename);
416 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
417 for ( i=0; i<count; i++ ) {
418 char server[INET6_ADDRSTRLEN];
420 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
422 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
426 /* realm in this case is a workgroup name. We need
427 to ignore any IP addresses in the negative connection
428 cache that match ip addresses returned in the ad realm
429 case. It sucks that I have to reproduce the logic above... */
430 c_realm = ads->server.realm;
431 if ( !c_realm || !*c_realm ) {
432 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
433 c_realm = lp_realm();
436 if (c_realm && *c_realm &&
437 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
438 /* Ensure we add the workgroup name for this
439 IP address as negative too. */
440 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
445 ok = ads_try_connect(ads, false, &ip_list[i].ss);
448 TALLOC_FREE(sitename);
452 /* keep track of failures */
453 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
458 /* In case we failed to contact one of our closest DC on our site we
459 * need to try to find another DC, retry with a site-less SRV DNS query
463 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
464 "trying to find another DC\n", sitename));
465 TALLOC_FREE(sitename);
466 namecache_delete(realm, 0x1C);
470 return NT_STATUS_NO_LOGON_SERVERS;
473 /*********************************************************************
474 *********************************************************************/
476 static NTSTATUS ads_lookup_site(void)
478 ADS_STRUCT *ads = NULL;
479 ADS_STATUS ads_status;
480 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
482 ads = ads_init(lp_realm(), NULL, NULL);
484 return NT_STATUS_NO_MEMORY;
487 /* The NO_BIND here will find a DC and set the client site
488 but not establish the TCP connection */
490 ads->auth.flags = ADS_AUTH_NO_BIND;
491 ads_status = ads_connect(ads);
492 if (!ADS_ERR_OK(ads_status)) {
493 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
494 ads_errstr(ads_status)));
496 nt_status = ads_ntstatus(ads_status);
505 /*********************************************************************
506 *********************************************************************/
508 static const char* host_dns_domain(const char *fqdn)
510 const char *p = fqdn;
512 /* go to next char following '.' */
514 if ((p = strchr_m(fqdn, '.')) != NULL) {
523 * Connect to the Global Catalog server
524 * @param ads Pointer to an existing ADS_STRUCT
525 * @return status of connection
527 * Simple wrapper around ads_connect() that fills in the
528 * GC ldap server information
531 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
533 TALLOC_CTX *frame = talloc_stackframe();
534 struct dns_rr_srv *gcs_list;
536 const char *realm = ads->server.realm;
537 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
538 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
541 char *sitename = NULL;
542 const char *dns_hosts_file;
547 if ((sitename = sitename_fetch(frame, realm)) == NULL) {
549 sitename = sitename_fetch(frame, realm);
552 dns_hosts_file = lp_parm_const_string(-1, "resolv", "host file", NULL);
554 /* We try once with a sitename and once without
555 (unless we don't have a sitename and then we're
558 if (sitename == NULL)
561 nt_status = ads_dns_query_gcs(frame, dns_hosts_file,
563 &gcs_list, &num_gcs);
565 if (!NT_STATUS_IS_OK(nt_status)) {
566 ads_status = ADS_ERROR_NT(nt_status);
570 /* Loop until we get a successful connection or have gone
571 through them all. When connecting a GC server, make sure that
572 the realm is the server's DNS name and not the forest root */
574 for (i=0; i<num_gcs; i++) {
575 ads->server.gc = true;
576 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
577 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
578 ads_status = ads_connect(ads);
579 if (ADS_ERR_OK(ads_status)) {
580 /* Reset the bind_dn to "". A Global Catalog server
581 may host multiple domain trees in a forest.
582 Windows 2003 GC server will accept "" as the search
583 path to imply search all domain trees in the forest */
585 SAFE_FREE(ads->config.bind_path);
586 ads->config.bind_path = SMB_STRDUP("");
591 SAFE_FREE(ads->server.ldap_server);
592 SAFE_FREE(ads->server.realm);
595 TALLOC_FREE(gcs_list);
600 talloc_destroy(frame);
607 * Connect to the LDAP server
608 * @param ads Pointer to an existing ADS_STRUCT
609 * @return status of connection
611 ADS_STATUS ads_connect(ADS_STRUCT *ads)
613 int version = LDAP_VERSION3;
616 char addr[INET6_ADDRSTRLEN];
618 ZERO_STRUCT(ads->ldap);
619 ads->ldap.last_attempt = time_mono(NULL);
620 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
622 /* try with a user specified server */
624 if (DEBUGLEVEL >= 11) {
625 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
626 DEBUG(11,("ads_connect: entering\n"));
627 DEBUGADD(11,("%s\n", s));
631 if (ads->server.ldap_server) {
633 struct sockaddr_storage ss;
635 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
637 DEBUG(5,("ads_connect: unable to resolve name %s\n",
638 ads->server.ldap_server));
639 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
642 ok = ads_try_connect(ads, ads->server.gc, &ss);
647 /* The choice of which GC use is handled one level up in
648 ads_connect_gc(). If we continue on from here with
649 ads_find_dc() we will get GC searches on port 389 which
650 doesn't work. --jerry */
652 if (ads->server.gc == true) {
653 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
657 ntstatus = ads_find_dc(ads);
658 if (NT_STATUS_IS_OK(ntstatus)) {
662 status = ADS_ERROR_NT(ntstatus);
667 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
668 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
670 if (!ads->auth.user_name) {
671 /* Must use the userPrincipalName value here or sAMAccountName
672 and not servicePrincipalName; found by Guenther Deschner */
674 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
675 DEBUG(0,("ads_connect: asprintf fail.\n"));
676 ads->auth.user_name = NULL;
680 if (!ads->auth.realm) {
681 ads->auth.realm = SMB_STRDUP(ads->config.realm);
684 if (!ads->auth.kdc_server) {
685 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
686 ads->auth.kdc_server = SMB_STRDUP(addr);
689 /* If the caller() requested no LDAP bind, then we are done */
691 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
692 status = ADS_SUCCESS;
696 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
697 if (!ads->ldap.mem_ctx) {
698 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
702 /* Otherwise setup the TCP LDAP session */
704 ads->ldap.ld = ldap_open_with_timeout(addr,
706 ads->ldap.port, lp_ldap_timeout());
707 if (ads->ldap.ld == NULL) {
708 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
711 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
713 /* cache the successful connection for workgroup and realm */
714 if (ads_closest_dc(ads)) {
715 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
716 saf_store( ads->server.realm, ads->config.ldap_server_name);
719 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
721 if ( lp_ldap_ssl_ads() ) {
722 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
723 if (!ADS_ERR_OK(status)) {
728 /* fill in the current time and offsets */
730 status = ads_current_time( ads );
731 if ( !ADS_ERR_OK(status) ) {
735 /* Now do the bind */
737 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
738 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
742 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
743 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
747 status = ads_sasl_bind(ads);
750 if (DEBUGLEVEL >= 11) {
751 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
752 DEBUG(11,("ads_connect: leaving with: %s\n",
753 ads_errstr(status)));
754 DEBUGADD(11,("%s\n", s));
762 * Connect to the LDAP server using given credentials
763 * @param ads Pointer to an existing ADS_STRUCT
764 * @return status of connection
766 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
768 ads->auth.flags |= ADS_AUTH_USER_CREDS;
770 return ads_connect(ads);
774 * Disconnect the LDAP server
775 * @param ads Pointer to an existing ADS_STRUCT
777 void ads_disconnect(ADS_STRUCT *ads)
780 ldap_unbind(ads->ldap.ld);
783 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
784 ads->ldap.wrap_ops->disconnect(ads);
786 if (ads->ldap.mem_ctx) {
787 talloc_free(ads->ldap.mem_ctx);
789 ZERO_STRUCT(ads->ldap);
793 Duplicate a struct berval into talloc'ed memory
795 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
797 struct berval *value;
799 if (!in_val) return NULL;
801 value = talloc_zero(ctx, struct berval);
804 if (in_val->bv_len == 0) return value;
806 value->bv_len = in_val->bv_len;
807 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
813 Make a values list out of an array of (struct berval *)
815 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
816 const struct berval **in_vals)
818 struct berval **values;
821 if (!in_vals) return NULL;
822 for (i=0; in_vals[i]; i++)
824 values = talloc_zero_array(ctx, struct berval *, i+1);
825 if (!values) return NULL;
827 for (i=0; in_vals[i]; i++) {
828 values[i] = dup_berval(ctx, in_vals[i]);
834 UTF8-encode a values list out of an array of (char *)
836 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
842 if (!in_vals) return NULL;
843 for (i=0; in_vals[i]; i++)
845 values = talloc_zero_array(ctx, char *, i+1);
846 if (!values) return NULL;
848 for (i=0; in_vals[i]; i++) {
849 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
858 Pull a (char *) array out of a UTF8-encoded values list
860 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
864 size_t converted_size;
866 if (!in_vals) return NULL;
867 for (i=0; in_vals[i]; i++)
869 values = talloc_zero_array(ctx, char *, i+1);
870 if (!values) return NULL;
872 for (i=0; in_vals[i]; i++) {
873 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
875 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
876 "%s", strerror(errno)));
883 * Do a search with paged results. cookie must be null on the first
884 * call, and then returned on each subsequent call. It will be null
885 * again when the entire search is complete
886 * @param ads connection to ads server
887 * @param bind_path Base dn for the search
888 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
889 * @param expr Search expression - specified in local charset
890 * @param attrs Attributes to retrieve - specified in utf8 or ascii
891 * @param res ** which will contain results - free res* with ads_msgfree()
892 * @param count Number of entries retrieved on this page
893 * @param cookie The paged results cookie to be returned on subsequent calls
894 * @return status of search
896 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
897 const char *bind_path,
898 int scope, const char *expr,
899 const char **attrs, void *args,
901 int *count, struct berval **cookie)
904 char *utf8_expr, *utf8_path, **search_attrs = NULL;
905 size_t converted_size;
906 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
907 BerElement *cookie_be = NULL;
908 struct berval *cookie_bv= NULL;
909 BerElement *ext_be = NULL;
910 struct berval *ext_bv= NULL;
913 ads_control *external_control = (ads_control *) args;
917 if (!(ctx = talloc_init("ads_do_paged_search_args")))
918 return ADS_ERROR(LDAP_NO_MEMORY);
920 /* 0 means the conversion worked but the result was empty
921 so we only fail if it's -1. In any case, it always
922 at least nulls out the dest */
923 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
924 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
930 if (!attrs || !(*attrs))
933 /* This would be the utf8-encoded version...*/
934 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
935 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
941 /* Paged results only available on ldap v3 or later */
942 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
943 if (version < LDAP_VERSION3) {
944 rc = LDAP_NOT_SUPPORTED;
948 cookie_be = ber_alloc_t(LBER_USE_DER);
950 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
951 ber_bvfree(*cookie); /* don't need it from last time */
954 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
956 ber_flatten(cookie_be, &cookie_bv);
957 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
958 PagedResults.ldctl_iscritical = (char) 1;
959 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
960 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
962 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
963 NoReferrals.ldctl_iscritical = (char) 0;
964 NoReferrals.ldctl_value.bv_len = 0;
965 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
967 if (external_control &&
968 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
969 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
971 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
972 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
974 /* win2k does not accept a ldctl_value beeing passed in */
976 if (external_control->val != 0) {
978 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
983 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
987 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
992 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
993 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
996 ExternalCtrl.ldctl_value.bv_len = 0;
997 ExternalCtrl.ldctl_value.bv_val = NULL;
1000 controls[0] = &NoReferrals;
1001 controls[1] = &PagedResults;
1002 controls[2] = &ExternalCtrl;
1006 controls[0] = &NoReferrals;
1007 controls[1] = &PagedResults;
1011 /* we need to disable referrals as the openldap libs don't
1012 handle them and paged results at the same time. Using them
1013 together results in the result record containing the server
1014 page control being removed from the result list (tridge/jmcd)
1016 leaving this in despite the control that says don't generate
1017 referrals, in case the server doesn't support it (jmcd)
1019 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1021 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1022 search_attrs, 0, controls,
1023 NULL, LDAP_NO_LIMIT,
1024 (LDAPMessage **)res);
1026 ber_free(cookie_be, 1);
1027 ber_bvfree(cookie_bv);
1030 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1031 ldap_err2string(rc)));
1032 if (rc == LDAP_OTHER) {
1036 ret = ldap_parse_result(ads->ldap.ld,
1044 if (ret == LDAP_SUCCESS) {
1045 DEBUG(3, ("ldap_search_with_timeout(%s) "
1046 "error: %s\n", expr, ldap_errmsg));
1047 ldap_memfree(ldap_errmsg);
1053 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1054 NULL, &rcontrols, 0);
1060 for (i=0; rcontrols[i]; i++) {
1061 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1062 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1063 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1065 /* the berval is the cookie, but must be freed when
1067 if (cookie_bv->bv_len) /* still more to do */
1068 *cookie=ber_bvdup(cookie_bv);
1071 ber_bvfree(cookie_bv);
1072 ber_free(cookie_be, 1);
1076 ldap_controls_free(rcontrols);
1079 talloc_destroy(ctx);
1082 ber_free(ext_be, 1);
1089 /* if/when we decide to utf8-encode attrs, take out this next line */
1090 TALLOC_FREE(search_attrs);
1092 return ADS_ERROR(rc);
1095 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1096 int scope, const char *expr,
1097 const char **attrs, LDAPMessage **res,
1098 int *count, struct berval **cookie)
1100 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1105 * Get all results for a search. This uses ads_do_paged_search() to return
1106 * all entries in a large search.
1107 * @param ads connection to ads server
1108 * @param bind_path Base dn for the search
1109 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1110 * @param expr Search expression
1111 * @param attrs Attributes to retrieve
1112 * @param res ** which will contain results - free res* with ads_msgfree()
1113 * @return status of search
1115 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1116 int scope, const char *expr,
1117 const char **attrs, void *args,
1120 struct berval *cookie = NULL;
1125 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1128 if (!ADS_ERR_OK(status))
1131 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1133 LDAPMessage *res2 = NULL;
1134 LDAPMessage *msg, *next;
1136 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1137 attrs, args, &res2, &count, &cookie);
1138 if (!ADS_ERR_OK(status)) {
1139 /* Ensure we free all collected results */
1140 ads_msgfree(ads, *res);
1145 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1146 that this works on all ldap libs, but I have only tested with openldap */
1147 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1148 next = ads_next_message(ads, msg);
1149 ldap_add_result_entry((LDAPMessage **)res, msg);
1151 /* note that we do not free res2, as the memory is now
1152 part of the main returned list */
1155 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1156 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1162 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1163 int scope, const char *expr,
1164 const char **attrs, LDAPMessage **res)
1166 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1169 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1170 int scope, const char *expr,
1171 const char **attrs, uint32 sd_flags,
1176 args.control = ADS_SD_FLAGS_OID;
1177 args.val = sd_flags;
1178 args.critical = True;
1180 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1185 * Run a function on all results for a search. Uses ads_do_paged_search() and
1186 * runs the function as each page is returned, using ads_process_results()
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 - specified in local charset
1191 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1192 * @param fn Function which takes attr name, values list, and data_area
1193 * @param data_area Pointer which is passed to function on each call
1194 * @return status of search
1196 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1197 int scope, const char *expr, const char **attrs,
1198 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1201 struct berval *cookie = NULL;
1206 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1209 if (!ADS_ERR_OK(status)) return status;
1211 ads_process_results(ads, res, fn, data_area);
1212 ads_msgfree(ads, res);
1215 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1216 &res, &count, &cookie);
1218 if (!ADS_ERR_OK(status)) break;
1220 ads_process_results(ads, res, fn, data_area);
1221 ads_msgfree(ads, res);
1228 * Do a search with a timeout.
1229 * @param ads connection to ads server
1230 * @param bind_path Base dn for the search
1231 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1232 * @param expr Search expression
1233 * @param attrs Attributes to retrieve
1234 * @param res ** which will contain results - free res* with ads_msgfree()
1235 * @return status of search
1237 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1239 const char **attrs, LDAPMessage **res)
1242 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1243 size_t converted_size;
1247 if (!(ctx = talloc_init("ads_do_search"))) {
1248 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1249 return ADS_ERROR(LDAP_NO_MEMORY);
1252 /* 0 means the conversion worked but the result was empty
1253 so we only fail if it's negative. In any case, it always
1254 at least nulls out the dest */
1255 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1256 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1258 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1259 rc = LDAP_NO_MEMORY;
1263 if (!attrs || !(*attrs))
1264 search_attrs = NULL;
1266 /* This would be the utf8-encoded version...*/
1267 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1268 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1270 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1271 rc = LDAP_NO_MEMORY;
1276 /* see the note in ads_do_paged_search - we *must* disable referrals */
1277 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1279 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1280 search_attrs, 0, NULL, NULL,
1282 (LDAPMessage **)res);
1284 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1285 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1290 talloc_destroy(ctx);
1291 /* if/when we decide to utf8-encode attrs, take out this next line */
1292 TALLOC_FREE(search_attrs);
1293 return ADS_ERROR(rc);
1296 * Do a general ADS search
1297 * @param ads connection to ads server
1298 * @param res ** which will contain results - free res* with ads_msgfree()
1299 * @param expr Search expression
1300 * @param attrs Attributes to retrieve
1301 * @return status of search
1303 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1304 const char *expr, const char **attrs)
1306 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1311 * Do a search on a specific DistinguishedName
1312 * @param ads connection to ads server
1313 * @param res ** which will contain results - free res* with ads_msgfree()
1314 * @param dn DistinguishName to search
1315 * @param attrs Attributes to retrieve
1316 * @return status of search
1318 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1319 const char *dn, const char **attrs)
1321 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1326 * Free up memory from a ads_search
1327 * @param ads connection to ads server
1328 * @param msg Search results to free
1330 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1337 * Get a dn from search results
1338 * @param ads connection to ads server
1339 * @param msg Search result
1342 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1344 char *utf8_dn, *unix_dn;
1345 size_t converted_size;
1347 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1350 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1354 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1355 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1359 ldap_memfree(utf8_dn);
1364 * Get the parent from a dn
1365 * @param dn the dn to return the parent from
1366 * @return parent dn string
1368 char *ads_parent_dn(const char *dn)
1376 p = strchr(dn, ',');
1386 * Find a machine account given a hostname
1387 * @param ads connection to ads server
1388 * @param res ** which will contain results - free res* with ads_msgfree()
1389 * @param host Hostname to search for
1390 * @return status of search
1392 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1393 const char *machine)
1397 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1401 /* the easiest way to find a machine account anywhere in the tree
1402 is to look for hostname$ */
1403 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1404 DEBUG(1, ("asprintf failed!\n"));
1405 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1408 status = ads_search(ads, res, expr, attrs);
1414 * Initialize a list of mods to be used in a modify request
1415 * @param ctx An initialized TALLOC_CTX
1416 * @return allocated ADS_MODLIST
1418 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1420 #define ADS_MODLIST_ALLOC_SIZE 10
1423 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1424 /* -1 is safety to make sure we don't go over the end.
1425 need to reset it to NULL before doing ldap modify */
1426 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1428 return (ADS_MODLIST)mods;
1433 add an attribute to the list, with values list already constructed
1435 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1436 int mod_op, const char *name,
1437 const void *_invals)
1439 const void **invals = (const void **)_invals;
1441 LDAPMod **modlist = (LDAPMod **) *mods;
1442 struct berval **ber_values = NULL;
1443 char **char_values = NULL;
1446 mod_op = LDAP_MOD_DELETE;
1448 if (mod_op & LDAP_MOD_BVALUES)
1449 ber_values = ads_dup_values(ctx,
1450 (const struct berval **)invals);
1452 char_values = ads_push_strvals(ctx,
1453 (const char **) invals);
1456 /* find the first empty slot */
1457 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1459 if (modlist[curmod] == (LDAPMod *) -1) {
1460 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1461 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1462 return ADS_ERROR(LDAP_NO_MEMORY);
1463 memset(&modlist[curmod], 0,
1464 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1465 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1466 *mods = (ADS_MODLIST)modlist;
1469 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1470 return ADS_ERROR(LDAP_NO_MEMORY);
1471 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1472 if (mod_op & LDAP_MOD_BVALUES) {
1473 modlist[curmod]->mod_bvalues = ber_values;
1474 } else if (mod_op & LDAP_MOD_DELETE) {
1475 modlist[curmod]->mod_values = NULL;
1477 modlist[curmod]->mod_values = char_values;
1480 modlist[curmod]->mod_op = mod_op;
1481 return ADS_ERROR(LDAP_SUCCESS);
1485 * Add a single string value to a mod list
1486 * @param ctx An initialized TALLOC_CTX
1487 * @param mods An initialized ADS_MODLIST
1488 * @param name The attribute name to add
1489 * @param val The value to add - NULL means DELETE
1490 * @return ADS STATUS indicating success of add
1492 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1493 const char *name, const char *val)
1495 const char *values[2];
1501 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1502 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1506 * Add an array of string values to a mod list
1507 * @param ctx An initialized TALLOC_CTX
1508 * @param mods An initialized ADS_MODLIST
1509 * @param name The attribute name to add
1510 * @param vals The array of string values to add - NULL means DELETE
1511 * @return ADS STATUS indicating success of add
1513 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1514 const char *name, const char **vals)
1517 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1518 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1519 name, (const void **) vals);
1524 * Add a single ber-encoded value to a mod list
1525 * @param ctx An initialized TALLOC_CTX
1526 * @param mods An initialized ADS_MODLIST
1527 * @param name The attribute name to add
1528 * @param val The value to add - NULL means DELETE
1529 * @return ADS STATUS indicating success of add
1531 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1532 const char *name, const struct berval *val)
1534 const struct berval *values[2];
1539 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1540 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1541 name, (const void **) values);
1546 * Perform an ldap modify
1547 * @param ads connection to ads server
1548 * @param mod_dn DistinguishedName to modify
1549 * @param mods list of modifications to perform
1550 * @return status of modify
1552 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1555 char *utf8_dn = NULL;
1556 size_t converted_size;
1558 this control is needed to modify that contains a currently
1559 non-existent attribute (but allowable for the object) to run
1561 LDAPControl PermitModify = {
1562 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1565 LDAPControl *controls[2];
1567 controls[0] = &PermitModify;
1570 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1571 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1574 /* find the end of the list, marked by NULL or -1 */
1575 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1576 /* make sure the end of the list is NULL */
1578 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1579 (LDAPMod **) mods, controls, NULL);
1580 TALLOC_FREE(utf8_dn);
1581 return ADS_ERROR(ret);
1585 * Perform an ldap add
1586 * @param ads connection to ads server
1587 * @param new_dn DistinguishedName to add
1588 * @param mods list of attributes and values for DN
1589 * @return status of add
1591 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1594 char *utf8_dn = NULL;
1595 size_t converted_size;
1597 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1598 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1599 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1602 /* find the end of the list, marked by NULL or -1 */
1603 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1604 /* make sure the end of the list is NULL */
1607 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1608 TALLOC_FREE(utf8_dn);
1609 return ADS_ERROR(ret);
1613 * Delete a DistinguishedName
1614 * @param ads connection to ads server
1615 * @param new_dn DistinguishedName to delete
1616 * @return status of delete
1618 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1621 char *utf8_dn = NULL;
1622 size_t converted_size;
1623 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1624 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1625 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1628 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1629 TALLOC_FREE(utf8_dn);
1630 return ADS_ERROR(ret);
1634 * Build an org unit string
1635 * if org unit is Computers or blank then assume a container, otherwise
1636 * assume a / separated list of organisational units.
1637 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1638 * @param ads connection to ads server
1639 * @param org_unit Organizational unit
1640 * @return org unit string - caller must free
1642 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1646 if (!org_unit || !*org_unit) {
1648 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1650 /* samba4 might not yet respond to a wellknownobject-query */
1651 return ret ? ret : SMB_STRDUP("cn=Computers");
1654 if (strequal(org_unit, "Computers")) {
1655 return SMB_STRDUP("cn=Computers");
1658 /* jmcd: removed "\\" from the separation chars, because it is
1659 needed as an escape for chars like '#' which are valid in an
1661 return ads_build_path(org_unit, "/", "ou=", 1);
1665 * Get a org unit string for a well-known GUID
1666 * @param ads connection to ads server
1667 * @param wknguid Well known GUID
1668 * @return org unit string - caller must free
1670 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1673 LDAPMessage *res = NULL;
1674 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1675 **bind_dn_exp = NULL;
1676 const char *attrs[] = {"distinguishedName", NULL};
1677 int new_ln, wkn_ln, bind_ln, i;
1679 if (wknguid == NULL) {
1683 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1684 DEBUG(1, ("asprintf failed!\n"));
1688 status = ads_search_dn(ads, &res, base, attrs);
1689 if (!ADS_ERR_OK(status)) {
1690 DEBUG(1,("Failed while searching for: %s\n", base));
1694 if (ads_count_replies(ads, res) != 1) {
1698 /* substitute the bind-path from the well-known-guid-search result */
1699 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1704 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1709 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1714 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1716 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1719 new_ln = wkn_ln - bind_ln;
1721 ret = SMB_STRDUP(wkn_dn_exp[0]);
1726 for (i=1; i < new_ln; i++) {
1729 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1735 ret = SMB_STRDUP(s);
1744 ads_msgfree(ads, res);
1745 TALLOC_FREE(wkn_dn);
1747 ldap_value_free(wkn_dn_exp);
1750 ldap_value_free(bind_dn_exp);
1757 * Adds (appends) an item to an attribute array, rather then
1758 * replacing the whole list
1759 * @param ctx An initialized TALLOC_CTX
1760 * @param mods An initialized ADS_MODLIST
1761 * @param name name of the ldap attribute to append to
1762 * @param vals an array of values to add
1763 * @return status of addition
1766 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1767 const char *name, const char **vals)
1769 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1770 (const void *) vals);
1774 * Determines the an account's current KVNO via an LDAP lookup
1775 * @param ads An initialized ADS_STRUCT
1776 * @param account_name the NT samaccountname.
1777 * @return the kvno for the account, or -1 in case of a failure.
1780 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1782 LDAPMessage *res = NULL;
1783 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1785 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1786 char *dn_string = NULL;
1787 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1789 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1790 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1793 ret = ads_search(ads, &res, filter, attrs);
1795 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1796 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1797 ads_msgfree(ads, res);
1801 dn_string = ads_get_dn(ads, talloc_tos(), res);
1803 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1804 ads_msgfree(ads, res);
1807 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1808 TALLOC_FREE(dn_string);
1810 /* ---------------------------------------------------------
1811 * 0 is returned as a default KVNO from this point on...
1812 * This is done because Windows 2000 does not support key
1813 * version numbers. Chances are that a failure in the next
1814 * step is simply due to Windows 2000 being used for a
1815 * domain controller. */
1818 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1819 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1820 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1821 ads_msgfree(ads, res);
1826 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1827 ads_msgfree(ads, res);
1832 * Determines the computer account's current KVNO via an LDAP lookup
1833 * @param ads An initialized ADS_STRUCT
1834 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1835 * @return the kvno for the computer account, or -1 in case of a failure.
1838 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1840 char *computer_account = NULL;
1843 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1847 kvno = ads_get_kvno(ads, computer_account);
1848 free(computer_account);
1854 * This clears out all registered spn's for a given hostname
1855 * @param ads An initilaized ADS_STRUCT
1856 * @param machine_name the NetBIOS name of the computer.
1857 * @return 0 upon success, non-zero otherwise.
1860 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1863 LDAPMessage *res = NULL;
1865 const char *servicePrincipalName[1] = {NULL};
1866 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1867 char *dn_string = NULL;
1869 ret = ads_find_machine_acct(ads, &res, machine_name);
1870 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1871 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1872 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1873 ads_msgfree(ads, res);
1874 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1877 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1878 ctx = talloc_init("ads_clear_service_principal_names");
1880 ads_msgfree(ads, res);
1881 return ADS_ERROR(LDAP_NO_MEMORY);
1884 if (!(mods = ads_init_mods(ctx))) {
1885 talloc_destroy(ctx);
1886 ads_msgfree(ads, res);
1887 return ADS_ERROR(LDAP_NO_MEMORY);
1889 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1890 if (!ADS_ERR_OK(ret)) {
1891 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1892 ads_msgfree(ads, res);
1893 talloc_destroy(ctx);
1896 dn_string = ads_get_dn(ads, talloc_tos(), res);
1898 talloc_destroy(ctx);
1899 ads_msgfree(ads, res);
1900 return ADS_ERROR(LDAP_NO_MEMORY);
1902 ret = ads_gen_mod(ads, dn_string, mods);
1903 TALLOC_FREE(dn_string);
1904 if (!ADS_ERR_OK(ret)) {
1905 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1907 ads_msgfree(ads, res);
1908 talloc_destroy(ctx);
1912 ads_msgfree(ads, res);
1913 talloc_destroy(ctx);
1918 * @brief Search for an element in a string array.
1920 * @param[in] el_array The string array to search.
1922 * @param[in] num_el The number of elements in the string array.
1924 * @param[in] el The string to search.
1926 * @return True if found, false if not.
1928 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1932 if (el_array == NULL || num_el == 0 || el == NULL) {
1936 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1939 cmp = strcasecmp_m(el_array[i], el);
1949 * @brief This gets the service principal names of an existing computer account.
1951 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1953 * @param[in] ads The ADS context to use.
1955 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1956 * identify the computer account.
1958 * @param[in] spn_array A pointer to store the array for SPNs.
1960 * @param[in] num_spns The number of principals stored in the array.
1962 * @return 0 on success, or a ADS error if a failure occured.
1964 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1966 const char *machine_name,
1971 LDAPMessage *res = NULL;
1975 status = ads_find_machine_acct(ads,
1978 if (!ADS_ERR_OK(status)) {
1979 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1984 count = ads_count_replies(ads, res);
1986 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1990 dn = ads_get_dn(ads, mem_ctx, res);
1992 status = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
1996 *spn_array = ads_pull_strings(ads,
1999 "servicePrincipalName",
2003 ads_msgfree(ads, res);
2009 * This adds a service principal name to an existing computer account
2010 * (found by hostname) in AD.
2011 * @param ads An initialized ADS_STRUCT
2012 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2013 * @param my_fqdn The fully qualified DNS name of the machine
2014 * @param spn A string of the service principal to add, i.e. 'host'
2015 * @return 0 upon sucess, or non-zero if a failure occurs
2018 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
2019 const char *my_fqdn, const char *spn)
2023 LDAPMessage *res = NULL;
2026 char *dn_string = NULL;
2027 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
2029 ret = ads_find_machine_acct(ads, &res, machine_name);
2030 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2031 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2033 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2034 spn, machine_name, ads->config.realm));
2035 ads_msgfree(ads, res);
2036 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2039 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2040 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2041 ads_msgfree(ads, res);
2042 return ADS_ERROR(LDAP_NO_MEMORY);
2045 /* add short name spn */
2047 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2048 talloc_destroy(ctx);
2049 ads_msgfree(ads, res);
2050 return ADS_ERROR(LDAP_NO_MEMORY);
2052 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2053 ret = ADS_ERROR(LDAP_NO_MEMORY);
2056 servicePrincipalName[0] = psp1;
2058 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2059 psp1, machine_name));
2062 /* add fully qualified spn */
2064 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2065 ret = ADS_ERROR(LDAP_NO_MEMORY);
2068 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2069 ret = ADS_ERROR(LDAP_NO_MEMORY);
2072 servicePrincipalName[1] = psp2;
2074 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2075 psp2, machine_name));
2077 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2078 ret = ADS_ERROR(LDAP_NO_MEMORY);
2082 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2083 if (!ADS_ERR_OK(ret)) {
2084 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2088 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2089 ret = ADS_ERROR(LDAP_NO_MEMORY);
2093 ret = ads_gen_mod(ads, dn_string, mods);
2094 if (!ADS_ERR_OK(ret)) {
2095 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2101 ads_msgfree(ads, res);
2106 * adds a machine account to the ADS server
2107 * @param ads An intialized ADS_STRUCT
2108 * @param machine_name - the NetBIOS machine name of this account.
2109 * @param account_type A number indicating the type of account to create
2110 * @param org_unit The LDAP path in which to place this account
2111 * @return 0 upon success, or non-zero otherwise
2114 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2115 const char *org_unit)
2118 char *samAccountName, *controlstr;
2121 char *machine_escaped = NULL;
2123 const char *objectClass[] = {"top", "person", "organizationalPerson",
2124 "user", "computer", NULL};
2125 LDAPMessage *res = NULL;
2126 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2127 UF_DONT_EXPIRE_PASSWD |\
2128 UF_ACCOUNTDISABLE );
2130 if (!(ctx = talloc_init("ads_add_machine_acct")))
2131 return ADS_ERROR(LDAP_NO_MEMORY);
2133 ret = ADS_ERROR(LDAP_NO_MEMORY);
2135 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2136 if (!machine_escaped) {
2140 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2141 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2143 if ( !new_dn || !samAccountName ) {
2147 #ifndef ENCTYPE_ARCFOUR_HMAC
2148 acct_control |= UF_USE_DES_KEY_ONLY;
2151 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2155 if (!(mods = ads_init_mods(ctx))) {
2159 ads_mod_str(ctx, &mods, "cn", machine_name);
2160 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2161 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2162 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2164 ret = ads_gen_add(ads, new_dn, mods);
2167 SAFE_FREE(machine_escaped);
2168 ads_msgfree(ads, res);
2169 talloc_destroy(ctx);
2175 * move a machine account to another OU on the ADS server
2176 * @param ads - An intialized ADS_STRUCT
2177 * @param machine_name - the NetBIOS machine name of this account.
2178 * @param org_unit - The LDAP path in which to place this account
2179 * @param moved - whether we moved the machine account (optional)
2180 * @return 0 upon success, or non-zero otherwise
2183 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2184 const char *org_unit, bool *moved)
2188 LDAPMessage *res = NULL;
2189 char *filter = NULL;
2190 char *computer_dn = NULL;
2192 char *computer_rdn = NULL;
2193 bool need_move = False;
2195 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2196 rc = ADS_ERROR(LDAP_NO_MEMORY);
2200 /* Find pre-existing machine */
2201 rc = ads_search(ads, &res, filter, NULL);
2202 if (!ADS_ERR_OK(rc)) {
2206 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2208 rc = ADS_ERROR(LDAP_NO_MEMORY);
2212 parent_dn = ads_parent_dn(computer_dn);
2213 if (strequal(parent_dn, org_unit)) {
2219 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2220 rc = ADS_ERROR(LDAP_NO_MEMORY);
2224 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2225 org_unit, 1, NULL, NULL);
2226 rc = ADS_ERROR(ldap_status);
2229 ads_msgfree(ads, res);
2231 TALLOC_FREE(computer_dn);
2232 SAFE_FREE(computer_rdn);
2234 if (!ADS_ERR_OK(rc)) {
2246 dump a binary result from ldap
2248 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2251 for (i=0; values[i]; i++) {
2252 printf("%s: ", field);
2253 for (j=0; j<values[i]->bv_len; j++) {
2254 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2260 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2263 for (i=0; values[i]; i++) {
2265 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2268 status = GUID_from_ndr_blob(&in, &guid);
2269 if (NT_STATUS_IS_OK(status)) {
2270 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2272 printf("%s: INVALID GUID\n", field);
2278 dump a sid result from ldap
2280 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2283 for (i=0; values[i]; i++) {
2286 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2289 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2294 dump ntSecurityDescriptor
2296 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2298 TALLOC_CTX *frame = talloc_stackframe();
2299 struct security_descriptor *psd;
2302 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2303 values[0]->bv_len, &psd);
2304 if (!NT_STATUS_IS_OK(status)) {
2305 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2306 nt_errstr(status)));
2312 ads_disp_sd(ads, talloc_tos(), psd);
2319 dump a string result from ldap
2321 static void dump_string(const char *field, char **values)
2324 for (i=0; values[i]; i++) {
2325 printf("%s: %s\n", field, values[i]);
2330 dump a field from LDAP on stdout
2334 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2339 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2341 {"objectGUID", False, dump_guid},
2342 {"netbootGUID", False, dump_guid},
2343 {"nTSecurityDescriptor", False, dump_sd},
2344 {"dnsRecord", False, dump_binary},
2345 {"objectSid", False, dump_sid},
2346 {"tokenGroups", False, dump_sid},
2347 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2348 {"tokengroupsGlobalandUniversal", False, dump_sid},
2349 {"mS-DS-CreatorSID", False, dump_sid},
2350 {"msExchMailboxGuid", False, dump_guid},
2355 if (!field) { /* must be end of an entry */
2360 for (i=0; handlers[i].name; i++) {
2361 if (strcasecmp_m(handlers[i].name, field) == 0) {
2362 if (!values) /* first time, indicate string or not */
2363 return handlers[i].string;
2364 handlers[i].handler(ads, field, (struct berval **) values);
2368 if (!handlers[i].name) {
2369 if (!values) /* first time, indicate string conversion */
2371 dump_string(field, (char **)values);
2377 * Dump a result from LDAP on stdout
2378 * used for debugging
2379 * @param ads connection to ads server
2380 * @param res Results to dump
2383 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2385 ads_process_results(ads, res, ads_dump_field, NULL);
2389 * Walk through results, calling a function for each entry found.
2390 * The function receives a field name, a berval * array of values,
2391 * and a data area passed through from the start. The function is
2392 * called once with null for field and values at the end of each
2394 * @param ads connection to ads server
2395 * @param res Results to process
2396 * @param fn Function for processing each result
2397 * @param data_area user-defined area to pass to function
2399 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2400 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2405 size_t converted_size;
2407 if (!(ctx = talloc_init("ads_process_results")))
2410 for (msg = ads_first_entry(ads, res); msg;
2411 msg = ads_next_entry(ads, msg)) {
2415 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2416 (LDAPMessage *)msg,&b);
2418 utf8_field=ldap_next_attribute(ads->ldap.ld,
2419 (LDAPMessage *)msg,b)) {
2420 struct berval **ber_vals;
2421 char **str_vals, **utf8_vals;
2425 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2428 DEBUG(0,("ads_process_results: "
2429 "pull_utf8_talloc failed: %s",
2433 string = fn(ads, field, NULL, data_area);
2436 utf8_vals = ldap_get_values(ads->ldap.ld,
2437 (LDAPMessage *)msg, field);
2438 str_vals = ads_pull_strvals(ctx,
2439 (const char **) utf8_vals);
2440 fn(ads, field, (void **) str_vals, data_area);
2441 ldap_value_free(utf8_vals);
2443 ber_vals = ldap_get_values_len(ads->ldap.ld,
2444 (LDAPMessage *)msg, field);
2445 fn(ads, field, (void **) ber_vals, data_area);
2447 ldap_value_free_len(ber_vals);
2449 ldap_memfree(utf8_field);
2452 talloc_free_children(ctx);
2453 fn(ads, NULL, NULL, data_area); /* completed an entry */
2456 talloc_destroy(ctx);
2460 * count how many replies are in a LDAPMessage
2461 * @param ads connection to ads server
2462 * @param res Results to count
2463 * @return number of replies
2465 int ads_count_replies(ADS_STRUCT *ads, void *res)
2467 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2471 * pull the first entry from a ADS result
2472 * @param ads connection to ads server
2473 * @param res Results of search
2474 * @return first entry from result
2476 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2478 return ldap_first_entry(ads->ldap.ld, res);
2482 * pull the next entry from a ADS result
2483 * @param ads connection to ads server
2484 * @param res Results of search
2485 * @return next entry from result
2487 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2489 return ldap_next_entry(ads->ldap.ld, res);
2493 * pull the first message from a ADS result
2494 * @param ads connection to ads server
2495 * @param res Results of search
2496 * @return first message from result
2498 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2500 return ldap_first_message(ads->ldap.ld, res);
2504 * pull the next message from a ADS result
2505 * @param ads connection to ads server
2506 * @param res Results of search
2507 * @return next message from result
2509 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2511 return ldap_next_message(ads->ldap.ld, res);
2515 * pull a single string from a ADS result
2516 * @param ads connection to ads server
2517 * @param mem_ctx TALLOC_CTX to use for allocating result string
2518 * @param msg Results of search
2519 * @param field Attribute to retrieve
2520 * @return Result string in talloc context
2522 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2528 size_t converted_size;
2530 values = ldap_get_values(ads->ldap.ld, msg, field);
2534 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2539 ldap_value_free(values);
2544 * pull an array of strings from a ADS result
2545 * @param ads connection to ads server
2546 * @param mem_ctx TALLOC_CTX to use for allocating result string
2547 * @param msg Results of search
2548 * @param field Attribute to retrieve
2549 * @return Result strings in talloc context
2551 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2552 LDAPMessage *msg, const char *field,
2558 size_t converted_size;
2560 values = ldap_get_values(ads->ldap.ld, msg, field);
2564 *num_values = ldap_count_values(values);
2566 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2568 ldap_value_free(values);
2572 for (i=0;i<*num_values;i++) {
2573 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2576 ldap_value_free(values);
2582 ldap_value_free(values);
2587 * pull an array of strings from a ADS result
2588 * (handle large multivalue attributes with range retrieval)
2589 * @param ads connection to ads server
2590 * @param mem_ctx TALLOC_CTX to use for allocating result string
2591 * @param msg Results of search
2592 * @param field Attribute to retrieve
2593 * @param current_strings strings returned by a previous call to this function
2594 * @param next_attribute The next query should ask for this attribute
2595 * @param num_values How many values did we get this time?
2596 * @param more_values Are there more values to get?
2597 * @return Result strings in talloc context
2599 char **ads_pull_strings_range(ADS_STRUCT *ads,
2600 TALLOC_CTX *mem_ctx,
2601 LDAPMessage *msg, const char *field,
2602 char **current_strings,
2603 const char **next_attribute,
2604 size_t *num_strings,
2608 char *expected_range_attrib, *range_attr;
2609 BerElement *ptr = NULL;
2612 size_t num_new_strings;
2613 unsigned long int range_start;
2614 unsigned long int range_end;
2616 /* we might have been given the whole lot anyway */
2617 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2618 *more_strings = False;
2622 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2624 /* look for Range result */
2625 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2627 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2628 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2629 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2637 /* nothing here - this field is just empty */
2638 *more_strings = False;
2642 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2643 &range_start, &range_end) == 2) {
2644 *more_strings = True;
2646 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2647 &range_start) == 1) {
2648 *more_strings = False;
2650 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2652 ldap_memfree(range_attr);
2653 *more_strings = False;
2658 if ((*num_strings) != range_start) {
2659 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2660 " - aborting range retreival\n",
2661 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2662 ldap_memfree(range_attr);
2663 *more_strings = False;
2667 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2669 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2670 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2671 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2672 range_attr, (unsigned long int)range_end - range_start + 1,
2673 (unsigned long int)num_new_strings));
2674 ldap_memfree(range_attr);
2675 *more_strings = False;
2679 strings = talloc_realloc(mem_ctx, current_strings, char *,
2680 *num_strings + num_new_strings);
2682 if (strings == NULL) {
2683 ldap_memfree(range_attr);
2684 *more_strings = False;
2688 if (new_strings && num_new_strings) {
2689 memcpy(&strings[*num_strings], new_strings,
2690 sizeof(*new_strings) * num_new_strings);
2693 (*num_strings) += num_new_strings;
2695 if (*more_strings) {
2696 *next_attribute = talloc_asprintf(mem_ctx,
2701 if (!*next_attribute) {
2702 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2703 ldap_memfree(range_attr);
2704 *more_strings = False;
2709 ldap_memfree(range_attr);
2715 * pull a single uint32 from a ADS result
2716 * @param ads connection to ads server
2717 * @param msg Results of search
2718 * @param field Attribute to retrieve
2719 * @param v Pointer to int to store result
2720 * @return boolean inidicating success
2722 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2727 values = ldap_get_values(ads->ldap.ld, msg, field);
2731 ldap_value_free(values);
2735 *v = atoi(values[0]);
2736 ldap_value_free(values);
2741 * pull a single objectGUID from an ADS result
2742 * @param ads connection to ADS server
2743 * @param msg results of search
2744 * @param guid 37-byte area to receive text guid
2745 * @return boolean indicating success
2747 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2752 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2757 status = GUID_from_ndr_blob(&blob, guid);
2758 talloc_free(blob.data);
2759 return NT_STATUS_IS_OK(status);
2764 * pull a single struct dom_sid from a ADS result
2765 * @param ads connection to ads server
2766 * @param msg Results of search
2767 * @param field Attribute to retrieve
2768 * @param sid Pointer to sid to store result
2769 * @return boolean inidicating success
2771 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2772 struct dom_sid *sid)
2774 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2778 * pull an array of struct dom_sids from a ADS result
2779 * @param ads connection to ads server
2780 * @param mem_ctx TALLOC_CTX for allocating sid array
2781 * @param msg Results of search
2782 * @param field Attribute to retrieve
2783 * @param sids pointer to sid array to allocate
2784 * @return the count of SIDs pulled
2786 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2787 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2789 struct berval **values;
2793 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2798 for (i=0; values[i]; i++)
2802 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2804 ldap_value_free_len(values);
2812 for (i=0; values[i]; i++) {
2813 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2815 DEBUG(10, ("pulling SID: %s\n",
2816 sid_string_dbg(&(*sids)[count])));
2821 ldap_value_free_len(values);
2826 * pull a struct security_descriptor from a ADS result
2827 * @param ads connection to ads server
2828 * @param mem_ctx TALLOC_CTX for allocating sid array
2829 * @param msg Results of search
2830 * @param field Attribute to retrieve
2831 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2832 * @return boolean inidicating success
2834 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2835 LDAPMessage *msg, const char *field,
2836 struct security_descriptor **sd)
2838 struct berval **values;
2841 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2843 if (!values) return false;
2847 status = unmarshall_sec_desc(mem_ctx,
2848 (uint8 *)values[0]->bv_val,
2849 values[0]->bv_len, sd);
2850 if (!NT_STATUS_IS_OK(status)) {
2851 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2852 nt_errstr(status)));
2857 ldap_value_free_len(values);
2862 * in order to support usernames longer than 21 characters we need to
2863 * use both the sAMAccountName and the userPrincipalName attributes
2864 * It seems that not all users have the userPrincipalName attribute set
2866 * @param ads connection to ads server
2867 * @param mem_ctx TALLOC_CTX for allocating sid array
2868 * @param msg Results of search
2869 * @return the username
2871 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2877 /* lookup_name() only works on the sAMAccountName to
2878 returning the username portion of userPrincipalName
2879 breaks winbindd_getpwnam() */
2881 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2882 if (ret && (p = strchr_m(ret, '@'))) {
2887 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2892 * find the update serial number - this is the core of the ldap cache
2893 * @param ads connection to ads server
2894 * @param ads connection to ADS server
2895 * @param usn Pointer to retrieved update serial number
2896 * @return status of search
2898 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2900 const char *attrs[] = {"highestCommittedUSN", NULL};
2904 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2905 if (!ADS_ERR_OK(status))
2908 if (ads_count_replies(ads, res) != 1) {
2909 ads_msgfree(ads, res);
2910 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2913 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2914 ads_msgfree(ads, res);
2915 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2918 ads_msgfree(ads, res);
2922 /* parse a ADS timestring - typical string is
2923 '20020917091222.0Z0' which means 09:12.22 17th September
2925 static time_t ads_parse_time(const char *str)
2931 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2932 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2933 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2942 /********************************************************************
2943 ********************************************************************/
2945 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2947 const char *attrs[] = {"currentTime", NULL};
2952 ADS_STRUCT *ads_s = ads;
2954 if (!(ctx = talloc_init("ads_current_time"))) {
2955 return ADS_ERROR(LDAP_NO_MEMORY);
2958 /* establish a new ldap tcp session if necessary */
2960 if ( !ads->ldap.ld ) {
2961 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2962 ads->server.ldap_server )) == NULL )
2966 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2967 status = ads_connect( ads_s );
2968 if ( !ADS_ERR_OK(status))
2972 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2973 if (!ADS_ERR_OK(status)) {
2977 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2979 ads_msgfree(ads_s, res);
2980 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2984 /* but save the time and offset in the original ADS_STRUCT */
2986 ads->config.current_time = ads_parse_time(timestr);
2988 if (ads->config.current_time != 0) {
2989 ads->auth.time_offset = ads->config.current_time - time(NULL);
2990 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2993 ads_msgfree(ads, res);
2995 status = ADS_SUCCESS;
2998 /* free any temporary ads connections */
2999 if ( ads_s != ads ) {
3000 ads_destroy( &ads_s );
3002 talloc_destroy(ctx);
3007 /********************************************************************
3008 ********************************************************************/
3010 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
3012 const char *attrs[] = {"domainFunctionality", NULL};
3015 ADS_STRUCT *ads_s = ads;
3017 *val = DS_DOMAIN_FUNCTION_2000;
3019 /* establish a new ldap tcp session if necessary */
3021 if ( !ads->ldap.ld ) {
3022 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3023 ads->server.ldap_server )) == NULL )
3025 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3028 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3029 status = ads_connect( ads_s );
3030 if ( !ADS_ERR_OK(status))
3034 /* If the attribute does not exist assume it is a Windows 2000
3035 functional domain */
3037 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3038 if (!ADS_ERR_OK(status)) {
3039 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3040 status = ADS_SUCCESS;
3045 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3046 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3048 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3051 ads_msgfree(ads, res);
3054 /* free any temporary ads connections */
3055 if ( ads_s != ads ) {
3056 ads_destroy( &ads_s );
3063 * find the domain sid for our domain
3064 * @param ads connection to ads server
3065 * @param sid Pointer to domain sid
3066 * @return status of search
3068 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3070 const char *attrs[] = {"objectSid", NULL};
3074 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3076 if (!ADS_ERR_OK(rc)) return rc;
3077 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3078 ads_msgfree(ads, res);
3079 return ADS_ERROR_SYSTEM(ENOENT);
3081 ads_msgfree(ads, res);
3087 * find our site name
3088 * @param ads connection to ads server
3089 * @param mem_ctx Pointer to talloc context
3090 * @param site_name Pointer to the sitename
3091 * @return status of search
3093 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3097 const char *dn, *service_name;
3098 const char *attrs[] = { "dsServiceName", NULL };
3100 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3101 if (!ADS_ERR_OK(status)) {
3105 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3106 if (service_name == NULL) {
3107 ads_msgfree(ads, res);
3108 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3111 ads_msgfree(ads, res);
3113 /* go up three levels */
3114 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3116 return ADS_ERROR(LDAP_NO_MEMORY);
3119 *site_name = talloc_strdup(mem_ctx, dn);
3120 if (*site_name == NULL) {
3121 return ADS_ERROR(LDAP_NO_MEMORY);
3126 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3131 * find the site dn where a machine resides
3132 * @param ads connection to ads server
3133 * @param mem_ctx Pointer to talloc context
3134 * @param computer_name name of the machine
3135 * @param site_name Pointer to the sitename
3136 * @return status of search
3138 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3142 const char *parent, *filter;
3143 char *config_context = NULL;
3146 /* shortcut a query */
3147 if (strequal(computer_name, ads->config.ldap_server_name)) {
3148 return ads_site_dn(ads, mem_ctx, site_dn);
3151 status = ads_config_path(ads, mem_ctx, &config_context);
3152 if (!ADS_ERR_OK(status)) {
3156 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3157 if (filter == NULL) {
3158 return ADS_ERROR(LDAP_NO_MEMORY);
3161 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3162 filter, NULL, &res);
3163 if (!ADS_ERR_OK(status)) {
3167 if (ads_count_replies(ads, res) != 1) {
3168 ads_msgfree(ads, res);
3169 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3172 dn = ads_get_dn(ads, mem_ctx, res);
3174 ads_msgfree(ads, res);
3175 return ADS_ERROR(LDAP_NO_MEMORY);
3178 /* go up three levels */
3179 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3180 if (parent == NULL) {
3181 ads_msgfree(ads, res);
3183 return ADS_ERROR(LDAP_NO_MEMORY);
3186 *site_dn = talloc_strdup(mem_ctx, parent);
3187 if (*site_dn == NULL) {
3188 ads_msgfree(ads, res);
3190 return ADS_ERROR(LDAP_NO_MEMORY);
3194 ads_msgfree(ads, res);
3200 * get the upn suffixes for a domain
3201 * @param ads connection to ads server
3202 * @param mem_ctx Pointer to talloc context
3203 * @param suffixes Pointer to an array of suffixes
3204 * @param num_suffixes Pointer to the number of suffixes
3205 * @return status of search
3207 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3212 char *config_context = NULL;
3213 const char *attrs[] = { "uPNSuffixes", NULL };
3215 status = ads_config_path(ads, mem_ctx, &config_context);
3216 if (!ADS_ERR_OK(status)) {
3220 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3222 return ADS_ERROR(LDAP_NO_MEMORY);
3225 status = ads_search_dn(ads, &res, base, attrs);
3226 if (!ADS_ERR_OK(status)) {
3230 if (ads_count_replies(ads, res) != 1) {
3231 ads_msgfree(ads, res);
3232 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3235 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3236 if ((*suffixes) == NULL) {
3237 ads_msgfree(ads, res);
3238 return ADS_ERROR(LDAP_NO_MEMORY);
3241 ads_msgfree(ads, res);
3247 * get the joinable ous for a domain
3248 * @param ads connection to ads server
3249 * @param mem_ctx Pointer to talloc context
3250 * @param ous Pointer to an array of ous
3251 * @param num_ous Pointer to the number of ous
3252 * @return status of search
3254 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3255 TALLOC_CTX *mem_ctx,
3260 LDAPMessage *res = NULL;
3261 LDAPMessage *msg = NULL;
3262 const char *attrs[] = { "dn", NULL };
3265 status = ads_search(ads, &res,
3266 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3268 if (!ADS_ERR_OK(status)) {
3272 count = ads_count_replies(ads, res);
3274 ads_msgfree(ads, res);
3275 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3278 for (msg = ads_first_entry(ads, res); msg;
3279 msg = ads_next_entry(ads, msg)) {
3283 dn = ads_get_dn(ads, talloc_tos(), msg);
3285 ads_msgfree(ads, res);
3286 return ADS_ERROR(LDAP_NO_MEMORY);
3289 if (!add_string_to_array(mem_ctx, dn,
3290 (const char ***)ous,
3293 ads_msgfree(ads, res);
3294 return ADS_ERROR(LDAP_NO_MEMORY);
3300 ads_msgfree(ads, res);
3307 * pull a struct dom_sid from an extended dn string
3308 * @param mem_ctx TALLOC_CTX
3309 * @param extended_dn string
3310 * @param flags string type of extended_dn
3311 * @param sid pointer to a struct dom_sid
3312 * @return NT_STATUS_OK on success,
3313 * NT_INVALID_PARAMETER on error,
3314 * NT_STATUS_NOT_FOUND if no SID present
3316 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3317 const char *extended_dn,
3318 enum ads_extended_dn_flags flags,
3319 struct dom_sid *sid)
3324 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3327 /* otherwise extended_dn gets stripped off */
3328 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3329 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3332 * ADS_EXTENDED_DN_HEX_STRING:
3333 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3335 * ADS_EXTENDED_DN_STRING (only with w2k3):
3336 * <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
3338 * Object with no SID, such as an Exchange Public Folder
3339 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3342 p = strchr(dn, ';');
3344 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3347 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3348 DEBUG(5,("No SID present in extended dn\n"));
3349 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3352 p += strlen(";<SID=");
3356 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3361 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3365 case ADS_EXTENDED_DN_STRING:
3366 if (!string_to_sid(sid, p)) {
3367 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3370 case ADS_EXTENDED_DN_HEX_STRING: {
3374 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3376 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3379 if (!sid_parse(buf, buf_len, sid)) {
3380 DEBUG(10,("failed to parse sid\n"));
3381 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3386 DEBUG(10,("unknown extended dn format\n"));
3387 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3390 return ADS_ERROR_NT(NT_STATUS_OK);
3393 /********************************************************************
3394 ********************************************************************/
3396 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3398 LDAPMessage *res = NULL;
3403 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3404 if (!ADS_ERR_OK(status)) {
3405 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3406 lp_netbios_name()));
3410 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3411 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3415 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3416 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3420 ads_msgfree(ads, res);
3425 /********************************************************************
3426 ********************************************************************/
3428 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3430 LDAPMessage *res = NULL;
3435 status = ads_find_machine_acct(ads, &res, machine_name);
3436 if (!ADS_ERR_OK(status)) {
3437 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3438 lp_netbios_name()));
3442 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3443 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3447 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3448 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3452 ads_msgfree(ads, res);
3457 /********************************************************************
3458 ********************************************************************/
3460 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3462 LDAPMessage *res = NULL;
3467 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3468 if (!ADS_ERR_OK(status)) {
3469 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3470 lp_netbios_name()));
3474 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3475 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3479 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3480 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3484 ads_msgfree(ads, res);
3491 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3494 * Join a machine to a realm
3495 * Creates the machine account and sets the machine password
3496 * @param ads connection to ads server
3497 * @param machine name of host to add
3498 * @param org_unit Organizational unit to place machine in
3499 * @return status of join
3501 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3502 uint32 account_type, const char *org_unit)
3505 LDAPMessage *res = NULL;
3508 /* machine name must be lowercase */
3509 machine = SMB_STRDUP(machine_name);
3510 strlower_m(machine);
3513 status = ads_find_machine_acct(ads, (void **)&res, machine);
3514 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3515 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3516 status = ads_leave_realm(ads, machine);
3517 if (!ADS_ERR_OK(status)) {
3518 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3519 machine, ads->config.realm));
3524 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3525 if (!ADS_ERR_OK(status)) {
3526 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3531 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3532 if (!ADS_ERR_OK(status)) {
3533 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3539 ads_msgfree(ads, res);
3546 * Delete a machine from the realm
3547 * @param ads connection to ads server
3548 * @param hostname Machine to remove
3549 * @return status of delete
3551 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3556 char *hostnameDN, *host;
3558 LDAPControl ldap_control;
3559 LDAPControl * pldap_control[2] = {NULL, NULL};
3561 pldap_control[0] = &ldap_control;
3562 memset(&ldap_control, 0, sizeof(LDAPControl));
3563 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3565 /* hostname must be lowercase */
3566 host = SMB_STRDUP(hostname);
3567 if (!strlower_m(host)) {
3569 return ADS_ERROR_SYSTEM(EINVAL);
3572 status = ads_find_machine_acct(ads, &res, host);
3573 if (!ADS_ERR_OK(status)) {
3574 DEBUG(0, ("Host account for %s does not exist.\n", host));
3579 msg = ads_first_entry(ads, res);
3582 return ADS_ERROR_SYSTEM(ENOENT);
3585 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3586 if (hostnameDN == NULL) {
3588 return ADS_ERROR_SYSTEM(ENOENT);
3591 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3593 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3595 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3598 if (rc != LDAP_SUCCESS) {
3599 const char *attrs[] = { "cn", NULL };
3600 LDAPMessage *msg_sub;
3602 /* we only search with scope ONE, we do not expect any further
3603 * objects to be created deeper */
3605 status = ads_do_search_retry(ads, hostnameDN,
3606 LDAP_SCOPE_ONELEVEL,
3607 "(objectclass=*)", attrs, &res);
3609 if (!ADS_ERR_OK(status)) {
3611 TALLOC_FREE(hostnameDN);
3615 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3616 msg_sub = ads_next_entry(ads, msg_sub)) {
3620 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3622 TALLOC_FREE(hostnameDN);
3623 return ADS_ERROR(LDAP_NO_MEMORY);
3626 status = ads_del_dn(ads, dn);
3627 if (!ADS_ERR_OK(status)) {
3628 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3631 TALLOC_FREE(hostnameDN);
3638 /* there should be no subordinate objects anymore */
3639 status = ads_do_search_retry(ads, hostnameDN,
3640 LDAP_SCOPE_ONELEVEL,
3641 "(objectclass=*)", attrs, &res);
3643 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3645 TALLOC_FREE(hostnameDN);
3649 /* delete hostnameDN now */
3650 status = ads_del_dn(ads, hostnameDN);
3651 if (!ADS_ERR_OK(status)) {
3653 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3654 TALLOC_FREE(hostnameDN);
3659 TALLOC_FREE(hostnameDN);
3661 status = ads_find_machine_acct(ads, &res, host);
3662 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3663 DEBUG(3, ("Failed to remove host account.\n"));
3673 * pull all token-sids from an LDAP dn
3674 * @param ads connection to ads server
3675 * @param mem_ctx TALLOC_CTX for allocating sid array
3676 * @param dn of LDAP object
3677 * @param user_sid pointer to struct dom_sid (objectSid)
3678 * @param primary_group_sid pointer to struct dom_sid (self composed)
3679 * @param sids pointer to sid array to allocate
3680 * @param num_sids counter of SIDs pulled
3681 * @return status of token query
3683 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3684 TALLOC_CTX *mem_ctx,
3686 struct dom_sid *user_sid,
3687 struct dom_sid *primary_group_sid,
3688 struct dom_sid **sids,
3692 LDAPMessage *res = NULL;
3694 size_t tmp_num_sids;
3695 struct dom_sid *tmp_sids;
3696 struct dom_sid tmp_user_sid;
3697 struct dom_sid tmp_primary_group_sid;
3699 const char *attrs[] = {
3706 status = ads_search_retry_dn(ads, &res, dn, attrs);
3707 if (!ADS_ERR_OK(status)) {
3711 count = ads_count_replies(ads, res);
3713 ads_msgfree(ads, res);
3714 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3717 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3718 ads_msgfree(ads, res);
3719 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3722 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3723 ads_msgfree(ads, res);
3724 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3728 /* hack to compose the primary group sid without knowing the
3731 struct dom_sid domsid;
3733 sid_copy(&domsid, &tmp_user_sid);
3735 if (!sid_split_rid(&domsid, NULL)) {
3736 ads_msgfree(ads, res);
3737 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3740 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3741 ads_msgfree(ads, res);
3742 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3746 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3748 if (tmp_num_sids == 0 || !tmp_sids) {
3749 ads_msgfree(ads, res);
3750 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3754 *num_sids = tmp_num_sids;
3762 *user_sid = tmp_user_sid;
3765 if (primary_group_sid) {
3766 *primary_group_sid = tmp_primary_group_sid;
3769 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3771 ads_msgfree(ads, res);
3772 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3776 * Find a sAMAccoutName in LDAP
3777 * @param ads connection to ads server
3778 * @param mem_ctx TALLOC_CTX for allocating sid array
3779 * @param samaccountname to search
3780 * @param uac_ret uint32 pointer userAccountControl attribute value
3781 * @param dn_ret pointer to dn
3782 * @return status of token query
3784 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3785 TALLOC_CTX *mem_ctx,
3786 const char *samaccountname,
3788 const char **dn_ret)
3791 const char *attrs[] = { "userAccountControl", NULL };
3793 LDAPMessage *res = NULL;
3797 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3799 if (filter == NULL) {
3800 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3804 status = ads_do_search_all(ads, ads->config.bind_path,
3806 filter, attrs, &res);
3808 if (!ADS_ERR_OK(status)) {
3812 if (ads_count_replies(ads, res) != 1) {
3813 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3817 dn = ads_get_dn(ads, talloc_tos(), res);
3819 status = ADS_ERROR(LDAP_NO_MEMORY);
3823 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3824 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3833 *dn_ret = talloc_strdup(mem_ctx, dn);
3835 status = ADS_ERROR(LDAP_NO_MEMORY);
3841 ads_msgfree(ads, res);
3847 * find our configuration path
3848 * @param ads connection to ads server
3849 * @param mem_ctx Pointer to talloc context
3850 * @param config_path Pointer to the config path
3851 * @return status of search
3853 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3854 TALLOC_CTX *mem_ctx,
3858 LDAPMessage *res = NULL;
3859 const char *config_context = NULL;
3860 const char *attrs[] = { "configurationNamingContext", NULL };
3862 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3863 "(objectclass=*)", attrs, &res);
3864 if (!ADS_ERR_OK(status)) {
3868 config_context = ads_pull_string(ads, mem_ctx, res,
3869 "configurationNamingContext");
3870 ads_msgfree(ads, res);
3871 if (!config_context) {
3872 return ADS_ERROR(LDAP_NO_MEMORY);
3876 *config_path = talloc_strdup(mem_ctx, config_context);
3877 if (!*config_path) {
3878 return ADS_ERROR(LDAP_NO_MEMORY);
3882 return ADS_ERROR(LDAP_SUCCESS);
3886 * find the displayName of an extended right
3887 * @param ads connection to ads server
3888 * @param config_path The config path
3889 * @param mem_ctx Pointer to talloc context
3890 * @param GUID struct of the rightsGUID
3891 * @return status of search
3893 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3894 const char *config_path,
3895 TALLOC_CTX *mem_ctx,
3896 const struct GUID *rights_guid)
3899 LDAPMessage *res = NULL;
3901 const char *attrs[] = { "displayName", NULL };
3902 const char *result = NULL;
3905 if (!ads || !mem_ctx || !rights_guid) {
3909 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3910 GUID_string(mem_ctx, rights_guid));
3915 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3920 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3922 if (!ADS_ERR_OK(rc)) {
3926 if (ads_count_replies(ads, res) != 1) {
3930 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3933 ads_msgfree(ads, res);
3938 * verify or build and verify an account ou
3939 * @param mem_ctx Pointer to talloc context
3940 * @param ads connection to ads server
3942 * @return status of search
3945 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3947 const char **account_ou)
3953 exploded_dn = ldap_explode_dn(*account_ou, 0);
3955 ldap_value_free(exploded_dn);
3959 ou_string = ads_ou_string(ads, *account_ou);
3961 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3964 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3965 ads->config.bind_path);
3966 SAFE_FREE(ou_string);
3969 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3972 exploded_dn = ldap_explode_dn(name, 0);
3974 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3976 ldap_value_free(exploded_dn);