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 "libads/dns.h"
29 #include "../libds/common/flags.h"
36 * @brief basic ldap client-side routines for ads server communications
38 * The routines contained here should do the necessary ldap calls for
41 * Important note: attribute names passed into ads_ routines must
42 * already be in UTF-8 format. We do not convert them because in almost
43 * all cases, they are just ascii (which is represented with the same
44 * codepoints in UTF-8). This may have to change at some point
48 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
50 static SIG_ATOMIC_T gotalarm;
52 /***************************************************************
53 Signal function to tell us we timed out.
54 ****************************************************************/
56 static void gotalarm_sig(int signum)
61 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
66 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
67 "%u seconds\n", server, port, to));
71 CatchSignal(SIGALRM, gotalarm_sig);
73 /* End setup timeout. */
75 ldp = ldap_open(server, port);
78 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
79 server, port, strerror(errno)));
81 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
84 /* Teardown timeout. */
85 CatchSignal(SIGALRM, SIG_IGN);
91 static int ldap_search_with_timeout(LDAP *ld,
92 LDAP_CONST char *base,
94 LDAP_CONST char *filter,
102 struct timeval timeout;
105 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
106 timeout.tv_sec = lp_ldap_timeout();
109 /* Setup alarm timeout.... Do we need both of these ? JRA. */
111 CatchSignal(SIGALRM, gotalarm_sig);
112 alarm(lp_ldap_timeout());
113 /* End setup timeout. */
115 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
116 attrsonly, sctrls, cctrls, &timeout,
119 /* Teardown timeout. */
120 CatchSignal(SIGALRM, SIG_IGN);
124 return LDAP_TIMELIMIT_EXCEEDED;
127 * A bug in OpenLDAP means ldap_search_ext_s can return
128 * LDAP_SUCCESS but with a NULL res pointer. Cope with
129 * this. See bug #6279 for details. JRA.
133 return LDAP_TIMELIMIT_EXCEEDED;
139 /**********************************************
140 Do client and server sitename match ?
141 **********************************************/
143 bool ads_sitename_match(ADS_STRUCT *ads)
145 if (ads->config.server_site_name == NULL &&
146 ads->config.client_site_name == NULL ) {
147 DEBUG(10,("ads_sitename_match: both null\n"));
150 if (ads->config.server_site_name &&
151 ads->config.client_site_name &&
152 strequal(ads->config.server_site_name,
153 ads->config.client_site_name)) {
154 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
157 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
158 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
159 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
163 /**********************************************
164 Is this the closest DC ?
165 **********************************************/
167 bool ads_closest_dc(ADS_STRUCT *ads)
169 if (ads->config.flags & NBT_SERVER_CLOSEST) {
170 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
174 /* not sure if this can ever happen */
175 if (ads_sitename_match(ads)) {
176 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
180 if (ads->config.client_site_name == NULL) {
181 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
185 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
186 ads->config.ldap_server_name));
193 try a connection to a given ldap server, returning True and setting the servers IP
194 in the ads struct if successful
196 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
199 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
200 TALLOC_CTX *frame = talloc_stackframe();
203 if (!server || !*server) {
208 if (!is_ipaddress(server)) {
209 struct sockaddr_storage ss;
210 char addr[INET6_ADDRSTRLEN];
212 if (!resolve_name(server, &ss, 0x20, true)) {
213 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
218 print_sockaddr(addr, sizeof(addr), &ss);
219 srv = talloc_strdup(frame, addr);
221 /* this copes with inet_ntoa brokenness */
222 srv = talloc_strdup(frame, server);
230 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
231 srv, ads->server.realm));
233 ZERO_STRUCT( cldap_reply );
235 if ( !ads_cldap_netlogon_5(frame, srv, ads->server.realm, &cldap_reply ) ) {
236 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
241 /* Check the CLDAP reply flags */
243 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
244 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
250 /* Fill in the ads->config values */
252 SAFE_FREE(ads->config.realm);
253 SAFE_FREE(ads->config.bind_path);
254 SAFE_FREE(ads->config.ldap_server_name);
255 SAFE_FREE(ads->config.server_site_name);
256 SAFE_FREE(ads->config.client_site_name);
257 SAFE_FREE(ads->server.workgroup);
259 ads->config.flags = cldap_reply.server_type;
260 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
261 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
262 strupper_m(ads->config.realm);
263 ads->config.bind_path = ads_build_dn(ads->config.realm);
264 if (*cldap_reply.server_site) {
265 ads->config.server_site_name =
266 SMB_STRDUP(cldap_reply.server_site);
268 if (*cldap_reply.client_site) {
269 ads->config.client_site_name =
270 SMB_STRDUP(cldap_reply.client_site);
272 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
274 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
275 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
276 DEBUG(1,("ads_try_connect: unable to convert %s "
283 /* Store our site name. */
284 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
285 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
295 /**********************************************************************
296 Try to find an AD dc using our internal name resolution routines
297 Try the realm first and then then workgroup name if netbios is not
299 **********************************************************************/
301 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
303 const char *c_domain;
306 struct ip_service *ip_list;
309 bool got_realm = False;
310 bool use_own_domain = False;
312 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
314 /* if the realm and workgroup are both empty, assume they are ours */
317 c_realm = ads->server.realm;
319 if ( !c_realm || !*c_realm ) {
320 /* special case where no realm and no workgroup means our own */
321 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
322 use_own_domain = True;
323 c_realm = lp_realm();
327 if (c_realm && *c_realm)
330 /* we need to try once with the realm name and fallback to the
331 netbios domain name if we fail (if netbios has not been disabled */
333 if ( !got_realm && !lp_disable_netbios() ) {
334 c_realm = ads->server.workgroup;
335 if (!c_realm || !*c_realm) {
336 if ( use_own_domain )
337 c_realm = lp_workgroup();
341 if ( !c_realm || !*c_realm ) {
342 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
343 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
346 if ( use_own_domain ) {
347 c_domain = lp_workgroup();
349 c_domain = ads->server.workgroup;
356 * In case of LDAP we use get_dc_name() as that
357 * creates the custom krb5.conf file
359 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
361 struct sockaddr_storage ip_out;
363 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
364 (got_realm ? "realm" : "domain"), realm));
366 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
368 * we call ads_try_connect() to fill in the
369 * ads->config details
371 if (ads_try_connect(ads, srv_name, false)) {
376 return NT_STATUS_NO_LOGON_SERVERS;
379 sitename = sitename_fetch(realm);
383 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
384 (got_realm ? "realm" : "domain"), realm));
386 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
387 if (!NT_STATUS_IS_OK(status)) {
388 /* fall back to netbios if we can */
389 if ( got_realm && !lp_disable_netbios() ) {
398 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
399 for ( i=0; i<count; i++ ) {
400 char server[INET6_ADDRSTRLEN];
402 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
404 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
408 /* realm in this case is a workgroup name. We need
409 to ignore any IP addresses in the negative connection
410 cache that match ip addresses returned in the ad realm
411 case. It sucks that I have to reproduce the logic above... */
412 c_realm = ads->server.realm;
413 if ( !c_realm || !*c_realm ) {
414 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
415 c_realm = lp_realm();
418 if (c_realm && *c_realm &&
419 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
420 /* Ensure we add the workgroup name for this
421 IP address as negative too. */
422 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
427 if ( ads_try_connect(ads, server, false) ) {
433 /* keep track of failures */
434 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
439 /* In case we failed to contact one of our closest DC on our site we
440 * need to try to find another DC, retry with a site-less SRV DNS query
444 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
445 "trying to find another DC\n", sitename));
447 namecache_delete(realm, 0x1C);
451 return NT_STATUS_NO_LOGON_SERVERS;
454 /*********************************************************************
455 *********************************************************************/
457 static NTSTATUS ads_lookup_site(void)
459 ADS_STRUCT *ads = NULL;
460 ADS_STATUS ads_status;
461 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
463 ads = ads_init(lp_realm(), NULL, NULL);
465 return NT_STATUS_NO_MEMORY;
468 /* The NO_BIND here will find a DC and set the client site
469 but not establish the TCP connection */
471 ads->auth.flags = ADS_AUTH_NO_BIND;
472 ads_status = ads_connect(ads);
473 if (!ADS_ERR_OK(ads_status)) {
474 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
475 ads_errstr(ads_status)));
477 nt_status = ads_ntstatus(ads_status);
486 /*********************************************************************
487 *********************************************************************/
489 static const char* host_dns_domain(const char *fqdn)
491 const char *p = fqdn;
493 /* go to next char following '.' */
495 if ((p = strchr_m(fqdn, '.')) != NULL) {
504 * Connect to the Global Catalog server
505 * @param ads Pointer to an existing ADS_STRUCT
506 * @return status of connection
508 * Simple wrapper around ads_connect() that fills in the
509 * GC ldap server information
512 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
514 TALLOC_CTX *frame = talloc_stackframe();
515 struct dns_rr_srv *gcs_list;
517 char *realm = ads->server.realm;
518 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
519 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
522 char *sitename = NULL;
527 if ((sitename = sitename_fetch(realm)) == NULL) {
529 sitename = sitename_fetch(realm);
533 /* We try once with a sitename and once without
534 (unless we don't have a sitename and then we're
537 if (sitename == NULL)
540 nt_status = ads_dns_query_gcs(frame, realm, sitename,
541 &gcs_list, &num_gcs);
545 if (!NT_STATUS_IS_OK(nt_status)) {
546 ads_status = ADS_ERROR_NT(nt_status);
550 /* Loop until we get a successful connection or have gone
551 through them all. When connecting a GC server, make sure that
552 the realm is the server's DNS name and not the forest root */
554 for (i=0; i<num_gcs; i++) {
555 ads->server.gc = true;
556 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
557 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
558 ads_status = ads_connect(ads);
559 if (ADS_ERR_OK(ads_status)) {
560 /* Reset the bind_dn to "". A Global Catalog server
561 may host multiple domain trees in a forest.
562 Windows 2003 GC server will accept "" as the search
563 path to imply search all domain trees in the forest */
565 SAFE_FREE(ads->config.bind_path);
566 ads->config.bind_path = SMB_STRDUP("");
571 SAFE_FREE(ads->server.ldap_server);
572 SAFE_FREE(ads->server.realm);
575 TALLOC_FREE(gcs_list);
581 talloc_destroy(frame);
588 * Connect to the LDAP server
589 * @param ads Pointer to an existing ADS_STRUCT
590 * @return status of connection
592 ADS_STATUS ads_connect(ADS_STRUCT *ads)
594 int version = LDAP_VERSION3;
597 char addr[INET6_ADDRSTRLEN];
599 ZERO_STRUCT(ads->ldap);
600 ads->ldap.last_attempt = time_mono(NULL);
601 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
603 /* try with a user specified server */
605 if (DEBUGLEVEL >= 11) {
606 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
607 DEBUG(11,("ads_connect: entering\n"));
608 DEBUGADD(11,("%s\n", s));
612 if (ads->server.ldap_server)
614 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
618 /* The choice of which GC use is handled one level up in
619 ads_connect_gc(). If we continue on from here with
620 ads_find_dc() we will get GC searches on port 389 which
621 doesn't work. --jerry */
623 if (ads->server.gc == true) {
624 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
628 ntstatus = ads_find_dc(ads);
629 if (NT_STATUS_IS_OK(ntstatus)) {
633 status = ADS_ERROR_NT(ntstatus);
638 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
639 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
641 if (!ads->auth.user_name) {
642 /* Must use the userPrincipalName value here or sAMAccountName
643 and not servicePrincipalName; found by Guenther Deschner */
645 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
646 DEBUG(0,("ads_connect: asprintf fail.\n"));
647 ads->auth.user_name = NULL;
651 if (!ads->auth.realm) {
652 ads->auth.realm = SMB_STRDUP(ads->config.realm);
655 if (!ads->auth.kdc_server) {
656 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
657 ads->auth.kdc_server = SMB_STRDUP(addr);
661 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
662 to MIT kerberos to work (tridge) */
665 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
666 setenv(env, ads->auth.kdc_server, 1);
672 /* If the caller() requested no LDAP bind, then we are done */
674 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
675 status = ADS_SUCCESS;
679 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
680 if (!ads->ldap.mem_ctx) {
681 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
685 /* Otherwise setup the TCP LDAP session */
687 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
688 ads->ldap.port, lp_ldap_timeout());
689 if (ads->ldap.ld == NULL) {
690 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
693 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
695 /* cache the successful connection for workgroup and realm */
696 if (ads_closest_dc(ads)) {
697 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
698 saf_store( ads->server.realm, ads->config.ldap_server_name);
701 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
703 if ( lp_ldap_ssl_ads() ) {
704 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
705 if (!ADS_ERR_OK(status)) {
710 /* fill in the current time and offsets */
712 status = ads_current_time( ads );
713 if ( !ADS_ERR_OK(status) ) {
717 /* Now do the bind */
719 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
720 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
724 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
725 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
729 status = ads_sasl_bind(ads);
732 if (DEBUGLEVEL >= 11) {
733 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
734 DEBUG(11,("ads_connect: leaving with: %s\n",
735 ads_errstr(status)));
736 DEBUGADD(11,("%s\n", s));
744 * Connect to the LDAP server using given credentials
745 * @param ads Pointer to an existing ADS_STRUCT
746 * @return status of connection
748 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
750 ads->auth.flags |= ADS_AUTH_USER_CREDS;
752 return ads_connect(ads);
756 * Disconnect the LDAP server
757 * @param ads Pointer to an existing ADS_STRUCT
759 void ads_disconnect(ADS_STRUCT *ads)
762 ldap_unbind(ads->ldap.ld);
765 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
766 ads->ldap.wrap_ops->disconnect(ads);
768 if (ads->ldap.mem_ctx) {
769 talloc_free(ads->ldap.mem_ctx);
771 ZERO_STRUCT(ads->ldap);
775 Duplicate a struct berval into talloc'ed memory
777 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
779 struct berval *value;
781 if (!in_val) return NULL;
783 value = TALLOC_ZERO_P(ctx, struct berval);
786 if (in_val->bv_len == 0) return value;
788 value->bv_len = in_val->bv_len;
789 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
795 Make a values list out of an array of (struct berval *)
797 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
798 const struct berval **in_vals)
800 struct berval **values;
803 if (!in_vals) return NULL;
804 for (i=0; in_vals[i]; i++)
806 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
807 if (!values) return NULL;
809 for (i=0; in_vals[i]; i++) {
810 values[i] = dup_berval(ctx, in_vals[i]);
816 UTF8-encode a values list out of an array of (char *)
818 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
824 if (!in_vals) return NULL;
825 for (i=0; in_vals[i]; i++)
827 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
828 if (!values) return NULL;
830 for (i=0; in_vals[i]; i++) {
831 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
840 Pull a (char *) array out of a UTF8-encoded values list
842 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
846 size_t converted_size;
848 if (!in_vals) return NULL;
849 for (i=0; in_vals[i]; i++)
851 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
852 if (!values) return NULL;
854 for (i=0; in_vals[i]; i++) {
855 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
857 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
858 "%s", strerror(errno)));
865 * Do a search with paged results. cookie must be null on the first
866 * call, and then returned on each subsequent call. It will be null
867 * again when the entire search is complete
868 * @param ads connection to ads server
869 * @param bind_path Base dn for the search
870 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
871 * @param expr Search expression - specified in local charset
872 * @param attrs Attributes to retrieve - specified in utf8 or ascii
873 * @param res ** which will contain results - free res* with ads_msgfree()
874 * @param count Number of entries retrieved on this page
875 * @param cookie The paged results cookie to be returned on subsequent calls
876 * @return status of search
878 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
879 const char *bind_path,
880 int scope, const char *expr,
881 const char **attrs, void *args,
883 int *count, struct berval **cookie)
886 char *utf8_expr, *utf8_path, **search_attrs = NULL;
887 size_t converted_size;
888 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
889 BerElement *cookie_be = NULL;
890 struct berval *cookie_bv= NULL;
891 BerElement *ext_be = NULL;
892 struct berval *ext_bv= NULL;
895 ads_control *external_control = (ads_control *) args;
899 if (!(ctx = talloc_init("ads_do_paged_search_args")))
900 return ADS_ERROR(LDAP_NO_MEMORY);
902 /* 0 means the conversion worked but the result was empty
903 so we only fail if it's -1. In any case, it always
904 at least nulls out the dest */
905 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
906 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
912 if (!attrs || !(*attrs))
915 /* This would be the utf8-encoded version...*/
916 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
917 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
923 /* Paged results only available on ldap v3 or later */
924 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
925 if (version < LDAP_VERSION3) {
926 rc = LDAP_NOT_SUPPORTED;
930 cookie_be = ber_alloc_t(LBER_USE_DER);
932 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
933 ber_bvfree(*cookie); /* don't need it from last time */
936 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
938 ber_flatten(cookie_be, &cookie_bv);
939 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
940 PagedResults.ldctl_iscritical = (char) 1;
941 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
942 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
944 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
945 NoReferrals.ldctl_iscritical = (char) 0;
946 NoReferrals.ldctl_value.bv_len = 0;
947 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
949 if (external_control &&
950 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
951 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
953 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
954 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
956 /* win2k does not accept a ldctl_value beeing passed in */
958 if (external_control->val != 0) {
960 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
965 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
969 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
974 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
975 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
978 ExternalCtrl.ldctl_value.bv_len = 0;
979 ExternalCtrl.ldctl_value.bv_val = NULL;
982 controls[0] = &NoReferrals;
983 controls[1] = &PagedResults;
984 controls[2] = &ExternalCtrl;
988 controls[0] = &NoReferrals;
989 controls[1] = &PagedResults;
993 /* we need to disable referrals as the openldap libs don't
994 handle them and paged results at the same time. Using them
995 together results in the result record containing the server
996 page control being removed from the result list (tridge/jmcd)
998 leaving this in despite the control that says don't generate
999 referrals, in case the server doesn't support it (jmcd)
1001 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1003 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1004 search_attrs, 0, controls,
1005 NULL, LDAP_NO_LIMIT,
1006 (LDAPMessage **)res);
1008 ber_free(cookie_be, 1);
1009 ber_bvfree(cookie_bv);
1012 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1013 ldap_err2string(rc)));
1017 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1018 NULL, &rcontrols, 0);
1024 for (i=0; rcontrols[i]; i++) {
1025 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1026 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1027 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1029 /* the berval is the cookie, but must be freed when
1031 if (cookie_bv->bv_len) /* still more to do */
1032 *cookie=ber_bvdup(cookie_bv);
1035 ber_bvfree(cookie_bv);
1036 ber_free(cookie_be, 1);
1040 ldap_controls_free(rcontrols);
1043 talloc_destroy(ctx);
1046 ber_free(ext_be, 1);
1053 /* if/when we decide to utf8-encode attrs, take out this next line */
1054 TALLOC_FREE(search_attrs);
1056 return ADS_ERROR(rc);
1059 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1060 int scope, const char *expr,
1061 const char **attrs, LDAPMessage **res,
1062 int *count, struct berval **cookie)
1064 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1069 * Get all results for a search. This uses ads_do_paged_search() to return
1070 * all entries in a large search.
1071 * @param ads connection to ads server
1072 * @param bind_path Base dn for the search
1073 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1074 * @param expr Search expression
1075 * @param attrs Attributes to retrieve
1076 * @param res ** which will contain results - free res* with ads_msgfree()
1077 * @return status of search
1079 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1080 int scope, const char *expr,
1081 const char **attrs, void *args,
1084 struct berval *cookie = NULL;
1089 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1092 if (!ADS_ERR_OK(status))
1095 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1097 LDAPMessage *res2 = NULL;
1099 LDAPMessage *msg, *next;
1101 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1102 attrs, args, &res2, &count, &cookie);
1104 if (!ADS_ERR_OK(status2)) break;
1106 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1107 that this works on all ldap libs, but I have only tested with openldap */
1108 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1109 next = ads_next_message(ads, msg);
1110 ldap_add_result_entry((LDAPMessage **)res, msg);
1112 /* note that we do not free res2, as the memory is now
1113 part of the main returned list */
1116 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1117 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1123 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1124 int scope, const char *expr,
1125 const char **attrs, LDAPMessage **res)
1127 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1130 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1131 int scope, const char *expr,
1132 const char **attrs, uint32 sd_flags,
1137 args.control = ADS_SD_FLAGS_OID;
1138 args.val = sd_flags;
1139 args.critical = True;
1141 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1146 * Run a function on all results for a search. Uses ads_do_paged_search() and
1147 * runs the function as each page is returned, using ads_process_results()
1148 * @param ads connection to ads server
1149 * @param bind_path Base dn for the search
1150 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1151 * @param expr Search expression - specified in local charset
1152 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1153 * @param fn Function which takes attr name, values list, and data_area
1154 * @param data_area Pointer which is passed to function on each call
1155 * @return status of search
1157 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1158 int scope, const char *expr, const char **attrs,
1159 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1162 struct berval *cookie = NULL;
1167 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1170 if (!ADS_ERR_OK(status)) return status;
1172 ads_process_results(ads, res, fn, data_area);
1173 ads_msgfree(ads, res);
1176 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1177 &res, &count, &cookie);
1179 if (!ADS_ERR_OK(status)) break;
1181 ads_process_results(ads, res, fn, data_area);
1182 ads_msgfree(ads, res);
1189 * Do a search with a timeout.
1190 * @param ads connection to ads server
1191 * @param bind_path Base dn for the search
1192 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1193 * @param expr Search expression
1194 * @param attrs Attributes to retrieve
1195 * @param res ** which will contain results - free res* with ads_msgfree()
1196 * @return status of search
1198 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1200 const char **attrs, LDAPMessage **res)
1203 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1204 size_t converted_size;
1208 if (!(ctx = talloc_init("ads_do_search"))) {
1209 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1210 return ADS_ERROR(LDAP_NO_MEMORY);
1213 /* 0 means the conversion worked but the result was empty
1214 so we only fail if it's negative. In any case, it always
1215 at least nulls out the dest */
1216 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1217 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1219 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1220 rc = LDAP_NO_MEMORY;
1224 if (!attrs || !(*attrs))
1225 search_attrs = NULL;
1227 /* This would be the utf8-encoded version...*/
1228 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1229 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1231 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1232 rc = LDAP_NO_MEMORY;
1237 /* see the note in ads_do_paged_search - we *must* disable referrals */
1238 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1240 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1241 search_attrs, 0, NULL, NULL,
1243 (LDAPMessage **)res);
1245 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1246 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1251 talloc_destroy(ctx);
1252 /* if/when we decide to utf8-encode attrs, take out this next line */
1253 TALLOC_FREE(search_attrs);
1254 return ADS_ERROR(rc);
1257 * Do a general ADS search
1258 * @param ads connection to ads server
1259 * @param res ** which will contain results - free res* with ads_msgfree()
1260 * @param expr Search expression
1261 * @param attrs Attributes to retrieve
1262 * @return status of search
1264 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1265 const char *expr, const char **attrs)
1267 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1272 * Do a search on a specific DistinguishedName
1273 * @param ads connection to ads server
1274 * @param res ** which will contain results - free res* with ads_msgfree()
1275 * @param dn DistinguishName to search
1276 * @param attrs Attributes to retrieve
1277 * @return status of search
1279 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1280 const char *dn, const char **attrs)
1282 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1287 * Free up memory from a ads_search
1288 * @param ads connection to ads server
1289 * @param msg Search results to free
1291 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1298 * Get a dn from search results
1299 * @param ads connection to ads server
1300 * @param msg Search result
1303 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1305 char *utf8_dn, *unix_dn;
1306 size_t converted_size;
1308 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1311 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1315 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1316 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1320 ldap_memfree(utf8_dn);
1325 * Get the parent from a dn
1326 * @param dn the dn to return the parent from
1327 * @return parent dn string
1329 char *ads_parent_dn(const char *dn)
1337 p = strchr(dn, ',');
1347 * Find a machine account given a hostname
1348 * @param ads connection to ads server
1349 * @param res ** which will contain results - free res* with ads_msgfree()
1350 * @param host Hostname to search for
1351 * @return status of search
1353 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1354 const char *machine)
1358 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1362 /* the easiest way to find a machine account anywhere in the tree
1363 is to look for hostname$ */
1364 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1365 DEBUG(1, ("asprintf failed!\n"));
1366 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1369 status = ads_search(ads, res, expr, attrs);
1375 * Initialize a list of mods to be used in a modify request
1376 * @param ctx An initialized TALLOC_CTX
1377 * @return allocated ADS_MODLIST
1379 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1381 #define ADS_MODLIST_ALLOC_SIZE 10
1384 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1385 /* -1 is safety to make sure we don't go over the end.
1386 need to reset it to NULL before doing ldap modify */
1387 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1389 return (ADS_MODLIST)mods;
1394 add an attribute to the list, with values list already constructed
1396 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1397 int mod_op, const char *name,
1398 const void *_invals)
1400 const void **invals = (const void **)_invals;
1402 LDAPMod **modlist = (LDAPMod **) *mods;
1403 struct berval **ber_values = NULL;
1404 char **char_values = NULL;
1407 mod_op = LDAP_MOD_DELETE;
1409 if (mod_op & LDAP_MOD_BVALUES)
1410 ber_values = ads_dup_values(ctx,
1411 (const struct berval **)invals);
1413 char_values = ads_push_strvals(ctx,
1414 (const char **) invals);
1417 /* find the first empty slot */
1418 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1420 if (modlist[curmod] == (LDAPMod *) -1) {
1421 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1422 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1423 return ADS_ERROR(LDAP_NO_MEMORY);
1424 memset(&modlist[curmod], 0,
1425 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1426 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1427 *mods = (ADS_MODLIST)modlist;
1430 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1431 return ADS_ERROR(LDAP_NO_MEMORY);
1432 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1433 if (mod_op & LDAP_MOD_BVALUES) {
1434 modlist[curmod]->mod_bvalues = ber_values;
1435 } else if (mod_op & LDAP_MOD_DELETE) {
1436 modlist[curmod]->mod_values = NULL;
1438 modlist[curmod]->mod_values = char_values;
1441 modlist[curmod]->mod_op = mod_op;
1442 return ADS_ERROR(LDAP_SUCCESS);
1446 * Add a single string value to a mod list
1447 * @param ctx An initialized TALLOC_CTX
1448 * @param mods An initialized ADS_MODLIST
1449 * @param name The attribute name to add
1450 * @param val The value to add - NULL means DELETE
1451 * @return ADS STATUS indicating success of add
1453 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1454 const char *name, const char *val)
1456 const char *values[2];
1462 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1463 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1467 * Add an array of string values to a mod list
1468 * @param ctx An initialized TALLOC_CTX
1469 * @param mods An initialized ADS_MODLIST
1470 * @param name The attribute name to add
1471 * @param vals The array of string values to add - NULL means DELETE
1472 * @return ADS STATUS indicating success of add
1474 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1475 const char *name, const char **vals)
1478 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1479 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1480 name, (const void **) vals);
1485 * Add a single ber-encoded 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 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1493 const char *name, const struct berval *val)
1495 const struct berval *values[2];
1500 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1501 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1502 name, (const void **) values);
1507 * Perform an ldap modify
1508 * @param ads connection to ads server
1509 * @param mod_dn DistinguishedName to modify
1510 * @param mods list of modifications to perform
1511 * @return status of modify
1513 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1516 char *utf8_dn = NULL;
1517 size_t converted_size;
1519 this control is needed to modify that contains a currently
1520 non-existent attribute (but allowable for the object) to run
1522 LDAPControl PermitModify = {
1523 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1526 LDAPControl *controls[2];
1528 controls[0] = &PermitModify;
1531 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1532 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1535 /* find the end of the list, marked by NULL or -1 */
1536 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1537 /* make sure the end of the list is NULL */
1539 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1540 (LDAPMod **) mods, controls, NULL);
1541 TALLOC_FREE(utf8_dn);
1542 return ADS_ERROR(ret);
1546 * Perform an ldap add
1547 * @param ads connection to ads server
1548 * @param new_dn DistinguishedName to add
1549 * @param mods list of attributes and values for DN
1550 * @return status of add
1552 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1555 char *utf8_dn = NULL;
1556 size_t converted_size;
1558 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1559 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1560 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1563 /* find the end of the list, marked by NULL or -1 */
1564 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1565 /* make sure the end of the list is NULL */
1568 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1569 TALLOC_FREE(utf8_dn);
1570 return ADS_ERROR(ret);
1574 * Delete a DistinguishedName
1575 * @param ads connection to ads server
1576 * @param new_dn DistinguishedName to delete
1577 * @return status of delete
1579 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1582 char *utf8_dn = NULL;
1583 size_t converted_size;
1584 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1585 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1586 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1589 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1590 TALLOC_FREE(utf8_dn);
1591 return ADS_ERROR(ret);
1595 * Build an org unit string
1596 * if org unit is Computers or blank then assume a container, otherwise
1597 * assume a / separated list of organisational units.
1598 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1599 * @param ads connection to ads server
1600 * @param org_unit Organizational unit
1601 * @return org unit string - caller must free
1603 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1607 if (!org_unit || !*org_unit) {
1609 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1611 /* samba4 might not yet respond to a wellknownobject-query */
1612 return ret ? ret : SMB_STRDUP("cn=Computers");
1615 if (strequal(org_unit, "Computers")) {
1616 return SMB_STRDUP("cn=Computers");
1619 /* jmcd: removed "\\" from the separation chars, because it is
1620 needed as an escape for chars like '#' which are valid in an
1622 return ads_build_path(org_unit, "/", "ou=", 1);
1626 * Get a org unit string for a well-known GUID
1627 * @param ads connection to ads server
1628 * @param wknguid Well known GUID
1629 * @return org unit string - caller must free
1631 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1634 LDAPMessage *res = NULL;
1635 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1636 **bind_dn_exp = NULL;
1637 const char *attrs[] = {"distinguishedName", NULL};
1638 int new_ln, wkn_ln, bind_ln, i;
1640 if (wknguid == NULL) {
1644 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1645 DEBUG(1, ("asprintf failed!\n"));
1649 status = ads_search_dn(ads, &res, base, attrs);
1650 if (!ADS_ERR_OK(status)) {
1651 DEBUG(1,("Failed while searching for: %s\n", base));
1655 if (ads_count_replies(ads, res) != 1) {
1659 /* substitute the bind-path from the well-known-guid-search result */
1660 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1665 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1670 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1675 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1677 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1680 new_ln = wkn_ln - bind_ln;
1682 ret = SMB_STRDUP(wkn_dn_exp[0]);
1687 for (i=1; i < new_ln; i++) {
1690 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1696 ret = SMB_STRDUP(s);
1705 ads_msgfree(ads, res);
1706 TALLOC_FREE(wkn_dn);
1708 ldap_value_free(wkn_dn_exp);
1711 ldap_value_free(bind_dn_exp);
1718 * Adds (appends) an item to an attribute array, rather then
1719 * replacing the whole list
1720 * @param ctx An initialized TALLOC_CTX
1721 * @param mods An initialized ADS_MODLIST
1722 * @param name name of the ldap attribute to append to
1723 * @param vals an array of values to add
1724 * @return status of addition
1727 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1728 const char *name, const char **vals)
1730 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1731 (const void *) vals);
1735 * Determines the an account's current KVNO via an LDAP lookup
1736 * @param ads An initialized ADS_STRUCT
1737 * @param account_name the NT samaccountname.
1738 * @return the kvno for the account, or -1 in case of a failure.
1741 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1743 LDAPMessage *res = NULL;
1744 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1746 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1747 char *dn_string = NULL;
1748 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1750 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1751 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1754 ret = ads_search(ads, &res, filter, attrs);
1756 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1757 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1758 ads_msgfree(ads, res);
1762 dn_string = ads_get_dn(ads, talloc_tos(), res);
1764 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1765 ads_msgfree(ads, res);
1768 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1769 TALLOC_FREE(dn_string);
1771 /* ---------------------------------------------------------
1772 * 0 is returned as a default KVNO from this point on...
1773 * This is done because Windows 2000 does not support key
1774 * version numbers. Chances are that a failure in the next
1775 * step is simply due to Windows 2000 being used for a
1776 * domain controller. */
1779 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1780 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1781 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1782 ads_msgfree(ads, res);
1787 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1788 ads_msgfree(ads, res);
1793 * Determines the computer account's current KVNO via an LDAP lookup
1794 * @param ads An initialized ADS_STRUCT
1795 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1796 * @return the kvno for the computer account, or -1 in case of a failure.
1799 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1801 char *computer_account = NULL;
1804 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1808 kvno = ads_get_kvno(ads, computer_account);
1809 free(computer_account);
1815 * This clears out all registered spn's for a given hostname
1816 * @param ads An initilaized ADS_STRUCT
1817 * @param machine_name the NetBIOS name of the computer.
1818 * @return 0 upon success, non-zero otherwise.
1821 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1824 LDAPMessage *res = NULL;
1826 const char *servicePrincipalName[1] = {NULL};
1827 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1828 char *dn_string = NULL;
1830 ret = ads_find_machine_acct(ads, &res, machine_name);
1831 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1832 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1833 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1834 ads_msgfree(ads, res);
1835 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1838 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1839 ctx = talloc_init("ads_clear_service_principal_names");
1841 ads_msgfree(ads, res);
1842 return ADS_ERROR(LDAP_NO_MEMORY);
1845 if (!(mods = ads_init_mods(ctx))) {
1846 talloc_destroy(ctx);
1847 ads_msgfree(ads, res);
1848 return ADS_ERROR(LDAP_NO_MEMORY);
1850 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1851 if (!ADS_ERR_OK(ret)) {
1852 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1853 ads_msgfree(ads, res);
1854 talloc_destroy(ctx);
1857 dn_string = ads_get_dn(ads, talloc_tos(), res);
1859 talloc_destroy(ctx);
1860 ads_msgfree(ads, res);
1861 return ADS_ERROR(LDAP_NO_MEMORY);
1863 ret = ads_gen_mod(ads, dn_string, mods);
1864 TALLOC_FREE(dn_string);
1865 if (!ADS_ERR_OK(ret)) {
1866 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1868 ads_msgfree(ads, res);
1869 talloc_destroy(ctx);
1873 ads_msgfree(ads, res);
1874 talloc_destroy(ctx);
1879 * This adds a service principal name to an existing computer account
1880 * (found by hostname) in AD.
1881 * @param ads An initialized ADS_STRUCT
1882 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1883 * @param my_fqdn The fully qualified DNS name of the machine
1884 * @param spn A string of the service principal to add, i.e. 'host'
1885 * @return 0 upon sucess, or non-zero if a failure occurs
1888 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1889 const char *my_fqdn, const char *spn)
1893 LDAPMessage *res = NULL;
1896 char *dn_string = NULL;
1897 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1899 ret = ads_find_machine_acct(ads, &res, machine_name);
1900 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1901 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1903 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1904 spn, machine_name, ads->config.realm));
1905 ads_msgfree(ads, res);
1906 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1909 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1910 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1911 ads_msgfree(ads, res);
1912 return ADS_ERROR(LDAP_NO_MEMORY);
1915 /* add short name spn */
1917 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1918 talloc_destroy(ctx);
1919 ads_msgfree(ads, res);
1920 return ADS_ERROR(LDAP_NO_MEMORY);
1923 strlower_m(&psp1[strlen(spn)]);
1924 servicePrincipalName[0] = psp1;
1926 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1927 psp1, machine_name));
1930 /* add fully qualified spn */
1932 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1933 ret = ADS_ERROR(LDAP_NO_MEMORY);
1937 strlower_m(&psp2[strlen(spn)]);
1938 servicePrincipalName[1] = psp2;
1940 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1941 psp2, machine_name));
1943 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1944 ret = ADS_ERROR(LDAP_NO_MEMORY);
1948 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1949 if (!ADS_ERR_OK(ret)) {
1950 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1954 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1955 ret = ADS_ERROR(LDAP_NO_MEMORY);
1959 ret = ads_gen_mod(ads, dn_string, mods);
1960 if (!ADS_ERR_OK(ret)) {
1961 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1967 ads_msgfree(ads, res);
1972 * adds a machine account to the ADS server
1973 * @param ads An intialized ADS_STRUCT
1974 * @param machine_name - the NetBIOS machine name of this account.
1975 * @param account_type A number indicating the type of account to create
1976 * @param org_unit The LDAP path in which to place this account
1977 * @return 0 upon success, or non-zero otherwise
1980 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1981 const char *org_unit)
1984 char *samAccountName, *controlstr;
1987 char *machine_escaped = NULL;
1989 const char *objectClass[] = {"top", "person", "organizationalPerson",
1990 "user", "computer", NULL};
1991 LDAPMessage *res = NULL;
1992 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1993 UF_DONT_EXPIRE_PASSWD |\
1994 UF_ACCOUNTDISABLE );
1996 if (!(ctx = talloc_init("ads_add_machine_acct")))
1997 return ADS_ERROR(LDAP_NO_MEMORY);
1999 ret = ADS_ERROR(LDAP_NO_MEMORY);
2001 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2002 if (!machine_escaped) {
2006 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2007 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2009 if ( !new_dn || !samAccountName ) {
2013 #ifndef ENCTYPE_ARCFOUR_HMAC
2014 acct_control |= UF_USE_DES_KEY_ONLY;
2017 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2021 if (!(mods = ads_init_mods(ctx))) {
2025 ads_mod_str(ctx, &mods, "cn", machine_name);
2026 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2027 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2028 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2030 ret = ads_gen_add(ads, new_dn, mods);
2033 SAFE_FREE(machine_escaped);
2034 ads_msgfree(ads, res);
2035 talloc_destroy(ctx);
2041 * move a machine account to another OU on the ADS server
2042 * @param ads - An intialized ADS_STRUCT
2043 * @param machine_name - the NetBIOS machine name of this account.
2044 * @param org_unit - The LDAP path in which to place this account
2045 * @param moved - whether we moved the machine account (optional)
2046 * @return 0 upon success, or non-zero otherwise
2049 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2050 const char *org_unit, bool *moved)
2054 LDAPMessage *res = NULL;
2055 char *filter = NULL;
2056 char *computer_dn = NULL;
2058 char *computer_rdn = NULL;
2059 bool need_move = False;
2061 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2062 rc = ADS_ERROR(LDAP_NO_MEMORY);
2066 /* Find pre-existing machine */
2067 rc = ads_search(ads, &res, filter, NULL);
2068 if (!ADS_ERR_OK(rc)) {
2072 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2074 rc = ADS_ERROR(LDAP_NO_MEMORY);
2078 parent_dn = ads_parent_dn(computer_dn);
2079 if (strequal(parent_dn, org_unit)) {
2085 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2086 rc = ADS_ERROR(LDAP_NO_MEMORY);
2090 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2091 org_unit, 1, NULL, NULL);
2092 rc = ADS_ERROR(ldap_status);
2095 ads_msgfree(ads, res);
2097 TALLOC_FREE(computer_dn);
2098 SAFE_FREE(computer_rdn);
2100 if (!ADS_ERR_OK(rc)) {
2112 dump a binary result from ldap
2114 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2117 for (i=0; values[i]; i++) {
2118 printf("%s: ", field);
2119 for (j=0; j<values[i]->bv_len; j++) {
2120 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2126 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2129 for (i=0; values[i]; i++) {
2131 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2134 status = GUID_from_ndr_blob(&in, &guid);
2135 if (NT_STATUS_IS_OK(status)) {
2136 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2138 printf("%s: INVALID GUID\n", field);
2144 dump a sid result from ldap
2146 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2149 for (i=0; values[i]; i++) {
2152 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2155 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2160 dump ntSecurityDescriptor
2162 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2164 TALLOC_CTX *frame = talloc_stackframe();
2165 struct security_descriptor *psd;
2168 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2169 values[0]->bv_len, &psd);
2170 if (!NT_STATUS_IS_OK(status)) {
2171 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2172 nt_errstr(status)));
2178 ads_disp_sd(ads, talloc_tos(), psd);
2185 dump a string result from ldap
2187 static void dump_string(const char *field, char **values)
2190 for (i=0; values[i]; i++) {
2191 printf("%s: %s\n", field, values[i]);
2196 dump a field from LDAP on stdout
2200 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2205 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2207 {"objectGUID", False, dump_guid},
2208 {"netbootGUID", False, dump_guid},
2209 {"nTSecurityDescriptor", False, dump_sd},
2210 {"dnsRecord", False, dump_binary},
2211 {"objectSid", False, dump_sid},
2212 {"tokenGroups", False, dump_sid},
2213 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2214 {"tokengroupsGlobalandUniversal", False, dump_sid},
2215 {"mS-DS-CreatorSID", False, dump_sid},
2216 {"msExchMailboxGuid", False, dump_guid},
2221 if (!field) { /* must be end of an entry */
2226 for (i=0; handlers[i].name; i++) {
2227 if (StrCaseCmp(handlers[i].name, field) == 0) {
2228 if (!values) /* first time, indicate string or not */
2229 return handlers[i].string;
2230 handlers[i].handler(ads, field, (struct berval **) values);
2234 if (!handlers[i].name) {
2235 if (!values) /* first time, indicate string conversion */
2237 dump_string(field, (char **)values);
2243 * Dump a result from LDAP on stdout
2244 * used for debugging
2245 * @param ads connection to ads server
2246 * @param res Results to dump
2249 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2251 ads_process_results(ads, res, ads_dump_field, NULL);
2255 * Walk through results, calling a function for each entry found.
2256 * The function receives a field name, a berval * array of values,
2257 * and a data area passed through from the start. The function is
2258 * called once with null for field and values at the end of each
2260 * @param ads connection to ads server
2261 * @param res Results to process
2262 * @param fn Function for processing each result
2263 * @param data_area user-defined area to pass to function
2265 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2266 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2271 size_t converted_size;
2273 if (!(ctx = talloc_init("ads_process_results")))
2276 for (msg = ads_first_entry(ads, res); msg;
2277 msg = ads_next_entry(ads, msg)) {
2281 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2282 (LDAPMessage *)msg,&b);
2284 utf8_field=ldap_next_attribute(ads->ldap.ld,
2285 (LDAPMessage *)msg,b)) {
2286 struct berval **ber_vals;
2287 char **str_vals, **utf8_vals;
2291 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2294 DEBUG(0,("ads_process_results: "
2295 "pull_utf8_talloc failed: %s",
2299 string = fn(ads, field, NULL, data_area);
2302 utf8_vals = ldap_get_values(ads->ldap.ld,
2303 (LDAPMessage *)msg, field);
2304 str_vals = ads_pull_strvals(ctx,
2305 (const char **) utf8_vals);
2306 fn(ads, field, (void **) str_vals, data_area);
2307 ldap_value_free(utf8_vals);
2309 ber_vals = ldap_get_values_len(ads->ldap.ld,
2310 (LDAPMessage *)msg, field);
2311 fn(ads, field, (void **) ber_vals, data_area);
2313 ldap_value_free_len(ber_vals);
2315 ldap_memfree(utf8_field);
2318 talloc_free_children(ctx);
2319 fn(ads, NULL, NULL, data_area); /* completed an entry */
2322 talloc_destroy(ctx);
2326 * count how many replies are in a LDAPMessage
2327 * @param ads connection to ads server
2328 * @param res Results to count
2329 * @return number of replies
2331 int ads_count_replies(ADS_STRUCT *ads, void *res)
2333 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2337 * pull the first entry from a ADS result
2338 * @param ads connection to ads server
2339 * @param res Results of search
2340 * @return first entry from result
2342 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2344 return ldap_first_entry(ads->ldap.ld, res);
2348 * pull the next entry from a ADS result
2349 * @param ads connection to ads server
2350 * @param res Results of search
2351 * @return next entry from result
2353 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2355 return ldap_next_entry(ads->ldap.ld, res);
2359 * pull the first message from a ADS result
2360 * @param ads connection to ads server
2361 * @param res Results of search
2362 * @return first message from result
2364 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2366 return ldap_first_message(ads->ldap.ld, res);
2370 * pull the next message from a ADS result
2371 * @param ads connection to ads server
2372 * @param res Results of search
2373 * @return next message from result
2375 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2377 return ldap_next_message(ads->ldap.ld, res);
2381 * pull a single string from a ADS result
2382 * @param ads connection to ads server
2383 * @param mem_ctx TALLOC_CTX to use for allocating result string
2384 * @param msg Results of search
2385 * @param field Attribute to retrieve
2386 * @return Result string in talloc context
2388 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2394 size_t converted_size;
2396 values = ldap_get_values(ads->ldap.ld, msg, field);
2400 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2405 ldap_value_free(values);
2410 * pull an array of strings from a ADS result
2411 * @param ads connection to ads server
2412 * @param mem_ctx TALLOC_CTX to use for allocating result string
2413 * @param msg Results of search
2414 * @param field Attribute to retrieve
2415 * @return Result strings in talloc context
2417 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2418 LDAPMessage *msg, const char *field,
2424 size_t converted_size;
2426 values = ldap_get_values(ads->ldap.ld, msg, field);
2430 *num_values = ldap_count_values(values);
2432 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2434 ldap_value_free(values);
2438 for (i=0;i<*num_values;i++) {
2439 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2442 ldap_value_free(values);
2448 ldap_value_free(values);
2453 * pull an array of strings from a ADS result
2454 * (handle large multivalue attributes with range retrieval)
2455 * @param ads connection to ads server
2456 * @param mem_ctx TALLOC_CTX to use for allocating result string
2457 * @param msg Results of search
2458 * @param field Attribute to retrieve
2459 * @param current_strings strings returned by a previous call to this function
2460 * @param next_attribute The next query should ask for this attribute
2461 * @param num_values How many values did we get this time?
2462 * @param more_values Are there more values to get?
2463 * @return Result strings in talloc context
2465 char **ads_pull_strings_range(ADS_STRUCT *ads,
2466 TALLOC_CTX *mem_ctx,
2467 LDAPMessage *msg, const char *field,
2468 char **current_strings,
2469 const char **next_attribute,
2470 size_t *num_strings,
2474 char *expected_range_attrib, *range_attr;
2475 BerElement *ptr = NULL;
2478 size_t num_new_strings;
2479 unsigned long int range_start;
2480 unsigned long int range_end;
2482 /* we might have been given the whole lot anyway */
2483 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2484 *more_strings = False;
2488 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2490 /* look for Range result */
2491 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2493 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2494 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2495 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2503 /* nothing here - this field is just empty */
2504 *more_strings = False;
2508 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2509 &range_start, &range_end) == 2) {
2510 *more_strings = True;
2512 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2513 &range_start) == 1) {
2514 *more_strings = False;
2516 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2518 ldap_memfree(range_attr);
2519 *more_strings = False;
2524 if ((*num_strings) != range_start) {
2525 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2526 " - aborting range retreival\n",
2527 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2528 ldap_memfree(range_attr);
2529 *more_strings = False;
2533 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2535 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2536 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2537 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2538 range_attr, (unsigned long int)range_end - range_start + 1,
2539 (unsigned long int)num_new_strings));
2540 ldap_memfree(range_attr);
2541 *more_strings = False;
2545 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2546 *num_strings + num_new_strings);
2548 if (strings == NULL) {
2549 ldap_memfree(range_attr);
2550 *more_strings = False;
2554 if (new_strings && num_new_strings) {
2555 memcpy(&strings[*num_strings], new_strings,
2556 sizeof(*new_strings) * num_new_strings);
2559 (*num_strings) += num_new_strings;
2561 if (*more_strings) {
2562 *next_attribute = talloc_asprintf(mem_ctx,
2567 if (!*next_attribute) {
2568 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2569 ldap_memfree(range_attr);
2570 *more_strings = False;
2575 ldap_memfree(range_attr);
2581 * pull a single uint32 from a ADS result
2582 * @param ads connection to ads server
2583 * @param msg Results of search
2584 * @param field Attribute to retrieve
2585 * @param v Pointer to int to store result
2586 * @return boolean inidicating success
2588 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2593 values = ldap_get_values(ads->ldap.ld, msg, field);
2597 ldap_value_free(values);
2601 *v = atoi(values[0]);
2602 ldap_value_free(values);
2607 * pull a single objectGUID from an ADS result
2608 * @param ads connection to ADS server
2609 * @param msg results of search
2610 * @param guid 37-byte area to receive text guid
2611 * @return boolean indicating success
2613 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2618 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2623 status = GUID_from_ndr_blob(&blob, guid);
2624 talloc_free(blob.data);
2625 return NT_STATUS_IS_OK(status);
2630 * pull a single struct dom_sid from a ADS result
2631 * @param ads connection to ads server
2632 * @param msg Results of search
2633 * @param field Attribute to retrieve
2634 * @param sid Pointer to sid to store result
2635 * @return boolean inidicating success
2637 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2638 struct dom_sid *sid)
2640 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2644 * pull an array of struct dom_sids from a ADS result
2645 * @param ads connection to ads server
2646 * @param mem_ctx TALLOC_CTX for allocating sid array
2647 * @param msg Results of search
2648 * @param field Attribute to retrieve
2649 * @param sids pointer to sid array to allocate
2650 * @return the count of SIDs pulled
2652 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2653 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2655 struct berval **values;
2659 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2664 for (i=0; values[i]; i++)
2668 (*sids) = TALLOC_ARRAY(mem_ctx, struct dom_sid, i);
2670 ldap_value_free_len(values);
2678 for (i=0; values[i]; i++) {
2679 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2681 DEBUG(10, ("pulling SID: %s\n",
2682 sid_string_dbg(&(*sids)[count])));
2687 ldap_value_free_len(values);
2692 * pull a struct security_descriptor from a ADS result
2693 * @param ads connection to ads server
2694 * @param mem_ctx TALLOC_CTX for allocating sid array
2695 * @param msg Results of search
2696 * @param field Attribute to retrieve
2697 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2698 * @return boolean inidicating success
2700 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2701 LDAPMessage *msg, const char *field,
2702 struct security_descriptor **sd)
2704 struct berval **values;
2707 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2709 if (!values) return false;
2713 status = unmarshall_sec_desc(mem_ctx,
2714 (uint8 *)values[0]->bv_val,
2715 values[0]->bv_len, sd);
2716 if (!NT_STATUS_IS_OK(status)) {
2717 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2718 nt_errstr(status)));
2723 ldap_value_free_len(values);
2728 * in order to support usernames longer than 21 characters we need to
2729 * use both the sAMAccountName and the userPrincipalName attributes
2730 * It seems that not all users have the userPrincipalName attribute set
2732 * @param ads connection to ads server
2733 * @param mem_ctx TALLOC_CTX for allocating sid array
2734 * @param msg Results of search
2735 * @return the username
2737 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2743 /* lookup_name() only works on the sAMAccountName to
2744 returning the username portion of userPrincipalName
2745 breaks winbindd_getpwnam() */
2747 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2748 if (ret && (p = strchr_m(ret, '@'))) {
2753 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2758 * find the update serial number - this is the core of the ldap cache
2759 * @param ads connection to ads server
2760 * @param ads connection to ADS server
2761 * @param usn Pointer to retrieved update serial number
2762 * @return status of search
2764 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2766 const char *attrs[] = {"highestCommittedUSN", NULL};
2770 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2771 if (!ADS_ERR_OK(status))
2774 if (ads_count_replies(ads, res) != 1) {
2775 ads_msgfree(ads, res);
2776 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2779 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2780 ads_msgfree(ads, res);
2781 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2784 ads_msgfree(ads, res);
2788 /* parse a ADS timestring - typical string is
2789 '20020917091222.0Z0' which means 09:12.22 17th September
2791 static time_t ads_parse_time(const char *str)
2797 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2798 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2799 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2808 /********************************************************************
2809 ********************************************************************/
2811 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2813 const char *attrs[] = {"currentTime", NULL};
2818 ADS_STRUCT *ads_s = ads;
2820 if (!(ctx = talloc_init("ads_current_time"))) {
2821 return ADS_ERROR(LDAP_NO_MEMORY);
2824 /* establish a new ldap tcp session if necessary */
2826 if ( !ads->ldap.ld ) {
2827 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2828 ads->server.ldap_server )) == NULL )
2832 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2833 status = ads_connect( ads_s );
2834 if ( !ADS_ERR_OK(status))
2838 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2839 if (!ADS_ERR_OK(status)) {
2843 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2845 ads_msgfree(ads_s, res);
2846 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2850 /* but save the time and offset in the original ADS_STRUCT */
2852 ads->config.current_time = ads_parse_time(timestr);
2854 if (ads->config.current_time != 0) {
2855 ads->auth.time_offset = ads->config.current_time - time(NULL);
2856 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2859 ads_msgfree(ads, res);
2861 status = ADS_SUCCESS;
2864 /* free any temporary ads connections */
2865 if ( ads_s != ads ) {
2866 ads_destroy( &ads_s );
2868 talloc_destroy(ctx);
2873 /********************************************************************
2874 ********************************************************************/
2876 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2878 const char *attrs[] = {"domainFunctionality", NULL};
2881 ADS_STRUCT *ads_s = ads;
2883 *val = DS_DOMAIN_FUNCTION_2000;
2885 /* establish a new ldap tcp session if necessary */
2887 if ( !ads->ldap.ld ) {
2888 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2889 ads->server.ldap_server )) == NULL )
2891 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2894 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2895 status = ads_connect( ads_s );
2896 if ( !ADS_ERR_OK(status))
2900 /* If the attribute does not exist assume it is a Windows 2000
2901 functional domain */
2903 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2904 if (!ADS_ERR_OK(status)) {
2905 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2906 status = ADS_SUCCESS;
2911 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2912 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2914 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2917 ads_msgfree(ads, res);
2920 /* free any temporary ads connections */
2921 if ( ads_s != ads ) {
2922 ads_destroy( &ads_s );
2929 * find the domain sid for our domain
2930 * @param ads connection to ads server
2931 * @param sid Pointer to domain sid
2932 * @return status of search
2934 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2936 const char *attrs[] = {"objectSid", NULL};
2940 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2942 if (!ADS_ERR_OK(rc)) return rc;
2943 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2944 ads_msgfree(ads, res);
2945 return ADS_ERROR_SYSTEM(ENOENT);
2947 ads_msgfree(ads, res);
2953 * find our site name
2954 * @param ads connection to ads server
2955 * @param mem_ctx Pointer to talloc context
2956 * @param site_name Pointer to the sitename
2957 * @return status of search
2959 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2963 const char *dn, *service_name;
2964 const char *attrs[] = { "dsServiceName", NULL };
2966 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2967 if (!ADS_ERR_OK(status)) {
2971 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2972 if (service_name == NULL) {
2973 ads_msgfree(ads, res);
2974 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2977 ads_msgfree(ads, res);
2979 /* go up three levels */
2980 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2982 return ADS_ERROR(LDAP_NO_MEMORY);
2985 *site_name = talloc_strdup(mem_ctx, dn);
2986 if (*site_name == NULL) {
2987 return ADS_ERROR(LDAP_NO_MEMORY);
2992 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2997 * find the site dn where a machine resides
2998 * @param ads connection to ads server
2999 * @param mem_ctx Pointer to talloc context
3000 * @param computer_name name of the machine
3001 * @param site_name Pointer to the sitename
3002 * @return status of search
3004 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3008 const char *parent, *filter;
3009 char *config_context = NULL;
3012 /* shortcut a query */
3013 if (strequal(computer_name, ads->config.ldap_server_name)) {
3014 return ads_site_dn(ads, mem_ctx, site_dn);
3017 status = ads_config_path(ads, mem_ctx, &config_context);
3018 if (!ADS_ERR_OK(status)) {
3022 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3023 if (filter == NULL) {
3024 return ADS_ERROR(LDAP_NO_MEMORY);
3027 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3028 filter, NULL, &res);
3029 if (!ADS_ERR_OK(status)) {
3033 if (ads_count_replies(ads, res) != 1) {
3034 ads_msgfree(ads, res);
3035 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3038 dn = ads_get_dn(ads, mem_ctx, res);
3040 ads_msgfree(ads, res);
3041 return ADS_ERROR(LDAP_NO_MEMORY);
3044 /* go up three levels */
3045 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3046 if (parent == NULL) {
3047 ads_msgfree(ads, res);
3049 return ADS_ERROR(LDAP_NO_MEMORY);
3052 *site_dn = talloc_strdup(mem_ctx, parent);
3053 if (*site_dn == NULL) {
3054 ads_msgfree(ads, res);
3056 return ADS_ERROR(LDAP_NO_MEMORY);
3060 ads_msgfree(ads, res);
3066 * get the upn suffixes for a domain
3067 * @param ads connection to ads server
3068 * @param mem_ctx Pointer to talloc context
3069 * @param suffixes Pointer to an array of suffixes
3070 * @param num_suffixes Pointer to the number of suffixes
3071 * @return status of search
3073 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3078 char *config_context = NULL;
3079 const char *attrs[] = { "uPNSuffixes", NULL };
3081 status = ads_config_path(ads, mem_ctx, &config_context);
3082 if (!ADS_ERR_OK(status)) {
3086 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3088 return ADS_ERROR(LDAP_NO_MEMORY);
3091 status = ads_search_dn(ads, &res, base, attrs);
3092 if (!ADS_ERR_OK(status)) {
3096 if (ads_count_replies(ads, res) != 1) {
3097 ads_msgfree(ads, res);
3098 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3101 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3102 if ((*suffixes) == NULL) {
3103 ads_msgfree(ads, res);
3104 return ADS_ERROR(LDAP_NO_MEMORY);
3107 ads_msgfree(ads, res);
3113 * get the joinable ous for a domain
3114 * @param ads connection to ads server
3115 * @param mem_ctx Pointer to talloc context
3116 * @param ous Pointer to an array of ous
3117 * @param num_ous Pointer to the number of ous
3118 * @return status of search
3120 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3121 TALLOC_CTX *mem_ctx,
3126 LDAPMessage *res = NULL;
3127 LDAPMessage *msg = NULL;
3128 const char *attrs[] = { "dn", NULL };
3131 status = ads_search(ads, &res,
3132 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3134 if (!ADS_ERR_OK(status)) {
3138 count = ads_count_replies(ads, res);
3140 ads_msgfree(ads, res);
3141 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3144 for (msg = ads_first_entry(ads, res); msg;
3145 msg = ads_next_entry(ads, msg)) {
3149 dn = ads_get_dn(ads, talloc_tos(), msg);
3151 ads_msgfree(ads, res);
3152 return ADS_ERROR(LDAP_NO_MEMORY);
3155 if (!add_string_to_array(mem_ctx, dn,
3156 (const char ***)ous,
3159 ads_msgfree(ads, res);
3160 return ADS_ERROR(LDAP_NO_MEMORY);
3166 ads_msgfree(ads, res);
3173 * pull a struct dom_sid from an extended dn string
3174 * @param mem_ctx TALLOC_CTX
3175 * @param extended_dn string
3176 * @param flags string type of extended_dn
3177 * @param sid pointer to a struct dom_sid
3178 * @return NT_STATUS_OK on success,
3179 * NT_INVALID_PARAMETER on error,
3180 * NT_STATUS_NOT_FOUND if no SID present
3182 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3183 const char *extended_dn,
3184 enum ads_extended_dn_flags flags,
3185 struct dom_sid *sid)
3190 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3193 /* otherwise extended_dn gets stripped off */
3194 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3195 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3198 * ADS_EXTENDED_DN_HEX_STRING:
3199 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3201 * ADS_EXTENDED_DN_STRING (only with w2k3):
3202 * <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
3204 * Object with no SID, such as an Exchange Public Folder
3205 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3208 p = strchr(dn, ';');
3210 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3213 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3214 DEBUG(5,("No SID present in extended dn\n"));
3215 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3218 p += strlen(";<SID=");
3222 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3227 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3231 case ADS_EXTENDED_DN_STRING:
3232 if (!string_to_sid(sid, p)) {
3233 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3236 case ADS_EXTENDED_DN_HEX_STRING: {
3240 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3242 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3245 if (!sid_parse(buf, buf_len, sid)) {
3246 DEBUG(10,("failed to parse sid\n"));
3247 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3252 DEBUG(10,("unknown extended dn format\n"));
3253 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3256 return ADS_ERROR_NT(NT_STATUS_OK);
3260 * pull an array of struct dom_sids from a ADS result
3261 * @param ads connection to ads server
3262 * @param mem_ctx TALLOC_CTX for allocating sid array
3263 * @param msg Results of search
3264 * @param field Attribute to retrieve
3265 * @param flags string type of extended_dn
3266 * @param sids pointer to sid array to allocate
3267 * @return the count of SIDs pulled
3269 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3270 TALLOC_CTX *mem_ctx,
3273 enum ads_extended_dn_flags flags,
3274 struct dom_sid **sids)
3278 size_t dn_count, ret_count = 0;
3281 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3282 &dn_count)) == NULL) {
3286 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, dn_count + 1);
3288 TALLOC_FREE(dn_strings);
3292 for (i=0; i<dn_count; i++) {
3293 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3294 flags, &(*sids)[i]);
3295 if (!ADS_ERR_OK(rc)) {
3296 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3297 NT_STATUS_NOT_FOUND)) {
3302 TALLOC_FREE(dn_strings);
3309 TALLOC_FREE(dn_strings);
3314 /********************************************************************
3315 ********************************************************************/
3317 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3319 LDAPMessage *res = NULL;
3324 status = ads_find_machine_acct(ads, &res, global_myname());
3325 if (!ADS_ERR_OK(status)) {
3326 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3331 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3332 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3336 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3337 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3341 ads_msgfree(ads, res);
3346 /********************************************************************
3347 ********************************************************************/
3349 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3351 LDAPMessage *res = NULL;
3356 status = ads_find_machine_acct(ads, &res, machine_name);
3357 if (!ADS_ERR_OK(status)) {
3358 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3363 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3364 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3368 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3369 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3373 ads_msgfree(ads, res);
3378 /********************************************************************
3379 ********************************************************************/
3381 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3383 LDAPMessage *res = NULL;
3388 status = ads_find_machine_acct(ads, &res, global_myname());
3389 if (!ADS_ERR_OK(status)) {
3390 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3395 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3396 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3400 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3401 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3405 ads_msgfree(ads, res);
3412 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3415 * Join a machine to a realm
3416 * Creates the machine account and sets the machine password
3417 * @param ads connection to ads server
3418 * @param machine name of host to add
3419 * @param org_unit Organizational unit to place machine in
3420 * @return status of join
3422 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3423 uint32 account_type, const char *org_unit)
3426 LDAPMessage *res = NULL;
3429 /* machine name must be lowercase */
3430 machine = SMB_STRDUP(machine_name);
3431 strlower_m(machine);
3434 status = ads_find_machine_acct(ads, (void **)&res, machine);
3435 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3436 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3437 status = ads_leave_realm(ads, machine);
3438 if (!ADS_ERR_OK(status)) {
3439 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3440 machine, ads->config.realm));
3445 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3446 if (!ADS_ERR_OK(status)) {
3447 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3452 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3453 if (!ADS_ERR_OK(status)) {
3454 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3460 ads_msgfree(ads, res);
3467 * Delete a machine from the realm
3468 * @param ads connection to ads server
3469 * @param hostname Machine to remove
3470 * @return status of delete
3472 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3477 char *hostnameDN, *host;
3479 LDAPControl ldap_control;
3480 LDAPControl * pldap_control[2] = {NULL, NULL};
3482 pldap_control[0] = &ldap_control;
3483 memset(&ldap_control, 0, sizeof(LDAPControl));
3484 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3486 /* hostname must be lowercase */
3487 host = SMB_STRDUP(hostname);
3490 status = ads_find_machine_acct(ads, &res, host);
3491 if (!ADS_ERR_OK(status)) {
3492 DEBUG(0, ("Host account for %s does not exist.\n", host));
3497 msg = ads_first_entry(ads, res);
3500 return ADS_ERROR_SYSTEM(ENOENT);
3503 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3505 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3507 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3509 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3512 if (rc != LDAP_SUCCESS) {
3513 const char *attrs[] = { "cn", NULL };
3514 LDAPMessage *msg_sub;
3516 /* we only search with scope ONE, we do not expect any further
3517 * objects to be created deeper */
3519 status = ads_do_search_retry(ads, hostnameDN,
3520 LDAP_SCOPE_ONELEVEL,
3521 "(objectclass=*)", attrs, &res);
3523 if (!ADS_ERR_OK(status)) {
3525 TALLOC_FREE(hostnameDN);
3529 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3530 msg_sub = ads_next_entry(ads, msg_sub)) {
3534 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3536 TALLOC_FREE(hostnameDN);
3537 return ADS_ERROR(LDAP_NO_MEMORY);
3540 status = ads_del_dn(ads, dn);
3541 if (!ADS_ERR_OK(status)) {
3542 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3545 TALLOC_FREE(hostnameDN);
3552 /* there should be no subordinate objects anymore */
3553 status = ads_do_search_retry(ads, hostnameDN,
3554 LDAP_SCOPE_ONELEVEL,
3555 "(objectclass=*)", attrs, &res);
3557 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3559 TALLOC_FREE(hostnameDN);
3563 /* delete hostnameDN now */
3564 status = ads_del_dn(ads, hostnameDN);
3565 if (!ADS_ERR_OK(status)) {
3567 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3568 TALLOC_FREE(hostnameDN);
3573 TALLOC_FREE(hostnameDN);
3575 status = ads_find_machine_acct(ads, &res, host);
3576 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3577 DEBUG(3, ("Failed to remove host account.\n"));
3587 * pull all token-sids from an LDAP dn
3588 * @param ads connection to ads server
3589 * @param mem_ctx TALLOC_CTX for allocating sid array
3590 * @param dn of LDAP object
3591 * @param user_sid pointer to struct dom_sid (objectSid)
3592 * @param primary_group_sid pointer to struct dom_sid (self composed)
3593 * @param sids pointer to sid array to allocate
3594 * @param num_sids counter of SIDs pulled
3595 * @return status of token query
3597 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3598 TALLOC_CTX *mem_ctx,
3600 struct dom_sid *user_sid,
3601 struct dom_sid *primary_group_sid,
3602 struct dom_sid **sids,
3606 LDAPMessage *res = NULL;
3608 size_t tmp_num_sids;
3609 struct dom_sid *tmp_sids;
3610 struct dom_sid tmp_user_sid;
3611 struct dom_sid tmp_primary_group_sid;
3613 const char *attrs[] = {
3620 status = ads_search_retry_dn(ads, &res, dn, attrs);
3621 if (!ADS_ERR_OK(status)) {
3625 count = ads_count_replies(ads, res);
3627 ads_msgfree(ads, res);
3628 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3631 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3632 ads_msgfree(ads, res);
3633 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3636 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3637 ads_msgfree(ads, res);
3638 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3642 /* hack to compose the primary group sid without knowing the
3645 struct dom_sid domsid;
3648 sid_copy(&domsid, &tmp_user_sid);
3650 if (!sid_split_rid(&domsid, &dummy_rid)) {
3651 ads_msgfree(ads, res);
3652 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3655 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3656 ads_msgfree(ads, res);
3657 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3661 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3663 if (tmp_num_sids == 0 || !tmp_sids) {
3664 ads_msgfree(ads, res);
3665 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3669 *num_sids = tmp_num_sids;
3677 *user_sid = tmp_user_sid;
3680 if (primary_group_sid) {
3681 *primary_group_sid = tmp_primary_group_sid;
3684 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3686 ads_msgfree(ads, res);
3687 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3691 * Find a sAMAccoutName in LDAP
3692 * @param ads connection to ads server
3693 * @param mem_ctx TALLOC_CTX for allocating sid array
3694 * @param samaccountname to search
3695 * @param uac_ret uint32 pointer userAccountControl attribute value
3696 * @param dn_ret pointer to dn
3697 * @return status of token query
3699 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3700 TALLOC_CTX *mem_ctx,
3701 const char *samaccountname,
3703 const char **dn_ret)
3706 const char *attrs[] = { "userAccountControl", NULL };
3708 LDAPMessage *res = NULL;
3712 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3714 if (filter == NULL) {
3715 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3719 status = ads_do_search_all(ads, ads->config.bind_path,
3721 filter, attrs, &res);
3723 if (!ADS_ERR_OK(status)) {
3727 if (ads_count_replies(ads, res) != 1) {
3728 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3732 dn = ads_get_dn(ads, talloc_tos(), res);
3734 status = ADS_ERROR(LDAP_NO_MEMORY);
3738 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3739 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3748 *dn_ret = talloc_strdup(mem_ctx, dn);
3750 status = ADS_ERROR(LDAP_NO_MEMORY);
3756 ads_msgfree(ads, res);
3762 * find our configuration path
3763 * @param ads connection to ads server
3764 * @param mem_ctx Pointer to talloc context
3765 * @param config_path Pointer to the config path
3766 * @return status of search
3768 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3769 TALLOC_CTX *mem_ctx,
3773 LDAPMessage *res = NULL;
3774 const char *config_context = NULL;
3775 const char *attrs[] = { "configurationNamingContext", NULL };
3777 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3778 "(objectclass=*)", attrs, &res);
3779 if (!ADS_ERR_OK(status)) {
3783 config_context = ads_pull_string(ads, mem_ctx, res,
3784 "configurationNamingContext");
3785 ads_msgfree(ads, res);
3786 if (!config_context) {
3787 return ADS_ERROR(LDAP_NO_MEMORY);
3791 *config_path = talloc_strdup(mem_ctx, config_context);
3792 if (!*config_path) {
3793 return ADS_ERROR(LDAP_NO_MEMORY);
3797 return ADS_ERROR(LDAP_SUCCESS);
3801 * find the displayName of an extended right
3802 * @param ads connection to ads server
3803 * @param config_path The config path
3804 * @param mem_ctx Pointer to talloc context
3805 * @param GUID struct of the rightsGUID
3806 * @return status of search
3808 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3809 const char *config_path,
3810 TALLOC_CTX *mem_ctx,
3811 const struct GUID *rights_guid)
3814 LDAPMessage *res = NULL;
3816 const char *attrs[] = { "displayName", NULL };
3817 const char *result = NULL;
3820 if (!ads || !mem_ctx || !rights_guid) {
3824 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3825 GUID_string(mem_ctx, rights_guid));
3830 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3835 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3837 if (!ADS_ERR_OK(rc)) {
3841 if (ads_count_replies(ads, res) != 1) {
3845 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3848 ads_msgfree(ads, res);
3853 * verify or build and verify an account ou
3854 * @param mem_ctx Pointer to talloc context
3855 * @param ads connection to ads server
3857 * @return status of search
3860 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3862 const char **account_ou)
3868 exploded_dn = ldap_explode_dn(*account_ou, 0);
3870 ldap_value_free(exploded_dn);
3874 ou_string = ads_ou_string(ads, *account_ou);
3876 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3879 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3880 ads->config.bind_path);
3881 SAFE_FREE(ou_string);
3884 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3887 exploded_dn = ldap_explode_dn(name, 0);
3889 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3891 ldap_value_free(exploded_dn);