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)
69 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
70 "%u seconds\n", server, port, to));
72 #if defined(HAVE_LDAP_INIT_FD) && defined(SOCKET_WRAPPER)
73 /* Only use this private LDAP function if we are in make test,
74 * as this is the best way to get the emulated TCP socket into
76 if (socket_wrapper_dir() != NULL) {
81 status = open_socket_out(ss, port, to, &fd);
83 if (!NT_STATUS_IS_OK(status)) {
87 #ifndef LDAP_PROTO_TCP
88 #define LDAP_PROTO_TCP 1
90 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
94 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
97 if (ldap_err != LDAP_SUCCESS) {
107 CatchSignal(SIGALRM, gotalarm_sig);
109 /* End setup timeout. */
112 ldp = ldap_open(server, port);
115 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
116 server, port, strerror(errno)));
118 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
122 /* Teardown timeout. */
124 CatchSignal(SIGALRM, SIG_IGN);
130 static int ldap_search_with_timeout(LDAP *ld,
131 LDAP_CONST char *base,
133 LDAP_CONST char *filter,
136 LDAPControl **sctrls,
137 LDAPControl **cctrls,
141 int to = lp_ldap_timeout();
142 struct timeval timeout;
143 struct timeval *timeout_ptr = NULL;
146 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
152 timeout_ptr = &timeout;
154 /* Setup alarm timeout. */
155 CatchSignal(SIGALRM, gotalarm_sig);
156 /* Make the alarm time one second beyond
157 the timout we're setting for the
158 remote search timeout, to allow that
159 to fire in preference. */
161 /* End setup timeout. */
165 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
166 attrsonly, sctrls, cctrls, timeout_ptr,
170 /* Teardown alarm timeout. */
171 CatchSignal(SIGALRM, SIG_IGN);
176 return LDAP_TIMELIMIT_EXCEEDED;
179 * A bug in OpenLDAP means ldap_search_ext_s can return
180 * LDAP_SUCCESS but with a NULL res pointer. Cope with
181 * this. See bug #6279 for details. JRA.
185 return LDAP_TIMELIMIT_EXCEEDED;
191 /**********************************************
192 Do client and server sitename match ?
193 **********************************************/
195 bool ads_sitename_match(ADS_STRUCT *ads)
197 if (ads->config.server_site_name == NULL &&
198 ads->config.client_site_name == NULL ) {
199 DEBUG(10,("ads_sitename_match: both null\n"));
202 if (ads->config.server_site_name &&
203 ads->config.client_site_name &&
204 strequal(ads->config.server_site_name,
205 ads->config.client_site_name)) {
206 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
209 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
210 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
211 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
215 /**********************************************
216 Is this the closest DC ?
217 **********************************************/
219 bool ads_closest_dc(ADS_STRUCT *ads)
221 if (ads->config.flags & NBT_SERVER_CLOSEST) {
222 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
226 /* not sure if this can ever happen */
227 if (ads_sitename_match(ads)) {
228 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
232 if (ads->config.client_site_name == NULL) {
233 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
237 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
238 ads->config.ldap_server_name));
245 try a connection to a given ldap server, returning True and setting the servers IP
246 in the ads struct if successful
248 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
250 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
251 TALLOC_CTX *frame = talloc_stackframe();
253 struct sockaddr_storage ss;
254 char addr[INET6_ADDRSTRLEN];
256 if (!server || !*server) {
261 if (!resolve_name(server, &ss, 0x20, true)) {
262 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
267 print_sockaddr(addr, sizeof(addr), &ss);
269 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
270 addr, ads->server.realm));
272 ZERO_STRUCT( cldap_reply );
274 if ( !ads_cldap_netlogon_5(frame, &ss, ads->server.realm, &cldap_reply ) ) {
275 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
280 /* Check the CLDAP reply flags */
282 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
283 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
289 /* Fill in the ads->config values */
291 SAFE_FREE(ads->config.realm);
292 SAFE_FREE(ads->config.bind_path);
293 SAFE_FREE(ads->config.ldap_server_name);
294 SAFE_FREE(ads->config.server_site_name);
295 SAFE_FREE(ads->config.client_site_name);
296 SAFE_FREE(ads->server.workgroup);
298 ads->config.flags = cldap_reply.server_type;
299 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
300 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
301 if (!strupper_m(ads->config.realm)) {
306 ads->config.bind_path = ads_build_dn(ads->config.realm);
307 if (*cldap_reply.server_site) {
308 ads->config.server_site_name =
309 SMB_STRDUP(cldap_reply.server_site);
311 if (*cldap_reply.client_site) {
312 ads->config.client_site_name =
313 SMB_STRDUP(cldap_reply.client_site);
315 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
317 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
320 /* Store our site name. */
321 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
322 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
332 /**********************************************************************
333 Try to find an AD dc using our internal name resolution routines
334 Try the realm first and then then workgroup name if netbios is not
336 **********************************************************************/
338 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
340 const char *c_domain;
343 struct ip_service *ip_list;
346 bool got_realm = False;
347 bool use_own_domain = False;
349 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
351 /* if the realm and workgroup are both empty, assume they are ours */
354 c_realm = ads->server.realm;
356 if ( !c_realm || !*c_realm ) {
357 /* special case where no realm and no workgroup means our own */
358 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
359 use_own_domain = True;
360 c_realm = lp_realm();
364 if (c_realm && *c_realm)
367 /* we need to try once with the realm name and fallback to the
368 netbios domain name if we fail (if netbios has not been disabled */
370 if ( !got_realm && !lp_disable_netbios() ) {
371 c_realm = ads->server.workgroup;
372 if (!c_realm || !*c_realm) {
373 if ( use_own_domain )
374 c_realm = lp_workgroup();
378 if ( !c_realm || !*c_realm ) {
379 DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know "
381 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
384 if ( use_own_domain ) {
385 c_domain = lp_workgroup();
387 c_domain = ads->server.workgroup;
394 * In case of LDAP we use get_dc_name() as that
395 * creates the custom krb5.conf file
397 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
399 struct sockaddr_storage ip_out;
401 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
402 (got_realm ? "realm" : "domain"), realm));
404 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
406 * we call ads_try_connect() to fill in the
407 * ads->config details
409 if (ads_try_connect(ads, srv_name, false)) {
414 return NT_STATUS_NO_LOGON_SERVERS;
417 sitename = sitename_fetch(realm);
421 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
422 (got_realm ? "realm" : "domain"), realm));
424 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
425 if (!NT_STATUS_IS_OK(status)) {
426 /* fall back to netbios if we can */
427 if ( got_realm && !lp_disable_netbios() ) {
436 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
437 for ( i=0; i<count; i++ ) {
438 char server[INET6_ADDRSTRLEN];
440 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
442 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
446 /* realm in this case is a workgroup name. We need
447 to ignore any IP addresses in the negative connection
448 cache that match ip addresses returned in the ad realm
449 case. It sucks that I have to reproduce the logic above... */
450 c_realm = ads->server.realm;
451 if ( !c_realm || !*c_realm ) {
452 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
453 c_realm = lp_realm();
456 if (c_realm && *c_realm &&
457 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
458 /* Ensure we add the workgroup name for this
459 IP address as negative too. */
460 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
465 if ( ads_try_connect(ads, server, false) ) {
471 /* keep track of failures */
472 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
477 /* In case we failed to contact one of our closest DC on our site we
478 * need to try to find another DC, retry with a site-less SRV DNS query
482 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
483 "trying to find another DC\n", sitename));
485 namecache_delete(realm, 0x1C);
489 return NT_STATUS_NO_LOGON_SERVERS;
492 /*********************************************************************
493 *********************************************************************/
495 static NTSTATUS ads_lookup_site(void)
497 ADS_STRUCT *ads = NULL;
498 ADS_STATUS ads_status;
499 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
501 ads = ads_init(lp_realm(), NULL, NULL);
503 return NT_STATUS_NO_MEMORY;
506 /* The NO_BIND here will find a DC and set the client site
507 but not establish the TCP connection */
509 ads->auth.flags = ADS_AUTH_NO_BIND;
510 ads_status = ads_connect(ads);
511 if (!ADS_ERR_OK(ads_status)) {
512 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
513 ads_errstr(ads_status)));
515 nt_status = ads_ntstatus(ads_status);
524 /*********************************************************************
525 *********************************************************************/
527 static const char* host_dns_domain(const char *fqdn)
529 const char *p = fqdn;
531 /* go to next char following '.' */
533 if ((p = strchr_m(fqdn, '.')) != NULL) {
542 * Connect to the Global Catalog server
543 * @param ads Pointer to an existing ADS_STRUCT
544 * @return status of connection
546 * Simple wrapper around ads_connect() that fills in the
547 * GC ldap server information
550 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
552 TALLOC_CTX *frame = talloc_stackframe();
553 struct dns_rr_srv *gcs_list;
555 const char *realm = ads->server.realm;
556 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
557 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
560 char *sitename = NULL;
561 const char *dns_hosts_file;
566 if ((sitename = sitename_fetch(realm)) == NULL) {
568 sitename = sitename_fetch(realm);
571 dns_hosts_file = lp_parm_const_string(-1, "resolv", "host file", NULL);
573 /* We try once with a sitename and once without
574 (unless we don't have a sitename and then we're
577 if (sitename == NULL)
580 nt_status = ads_dns_query_gcs(frame, dns_hosts_file,
582 &gcs_list, &num_gcs);
586 if (!NT_STATUS_IS_OK(nt_status)) {
587 ads_status = ADS_ERROR_NT(nt_status);
591 /* Loop until we get a successful connection or have gone
592 through them all. When connecting a GC server, make sure that
593 the realm is the server's DNS name and not the forest root */
595 for (i=0; i<num_gcs; i++) {
596 ads->server.gc = true;
597 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
598 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
599 ads_status = ads_connect(ads);
600 if (ADS_ERR_OK(ads_status)) {
601 /* Reset the bind_dn to "". A Global Catalog server
602 may host multiple domain trees in a forest.
603 Windows 2003 GC server will accept "" as the search
604 path to imply search all domain trees in the forest */
606 SAFE_FREE(ads->config.bind_path);
607 ads->config.bind_path = SMB_STRDUP("");
612 SAFE_FREE(ads->server.ldap_server);
613 SAFE_FREE(ads->server.realm);
616 TALLOC_FREE(gcs_list);
622 talloc_destroy(frame);
629 * Connect to the LDAP server
630 * @param ads Pointer to an existing ADS_STRUCT
631 * @return status of connection
633 ADS_STATUS ads_connect(ADS_STRUCT *ads)
635 int version = LDAP_VERSION3;
638 char addr[INET6_ADDRSTRLEN];
640 ZERO_STRUCT(ads->ldap);
641 ads->ldap.last_attempt = time_mono(NULL);
642 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
644 /* try with a user specified server */
646 if (DEBUGLEVEL >= 11) {
647 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
648 DEBUG(11,("ads_connect: entering\n"));
649 DEBUGADD(11,("%s\n", s));
653 if (ads->server.ldap_server)
655 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
659 /* The choice of which GC use is handled one level up in
660 ads_connect_gc(). If we continue on from here with
661 ads_find_dc() we will get GC searches on port 389 which
662 doesn't work. --jerry */
664 if (ads->server.gc == true) {
665 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
669 ntstatus = ads_find_dc(ads);
670 if (NT_STATUS_IS_OK(ntstatus)) {
674 status = ADS_ERROR_NT(ntstatus);
679 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
680 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
682 if (!ads->auth.user_name) {
683 /* Must use the userPrincipalName value here or sAMAccountName
684 and not servicePrincipalName; found by Guenther Deschner */
686 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
687 DEBUG(0,("ads_connect: asprintf fail.\n"));
688 ads->auth.user_name = NULL;
692 if (!ads->auth.realm) {
693 ads->auth.realm = SMB_STRDUP(ads->config.realm);
696 if (!ads->auth.kdc_server) {
697 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
698 ads->auth.kdc_server = SMB_STRDUP(addr);
701 /* If the caller() requested no LDAP bind, then we are done */
703 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
704 status = ADS_SUCCESS;
708 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
709 if (!ads->ldap.mem_ctx) {
710 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
714 /* Otherwise setup the TCP LDAP session */
716 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
718 ads->ldap.port, lp_ldap_timeout());
719 if (ads->ldap.ld == NULL) {
720 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
723 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
725 /* cache the successful connection for workgroup and realm */
726 if (ads_closest_dc(ads)) {
727 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
728 saf_store( ads->server.realm, ads->config.ldap_server_name);
731 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
733 if ( lp_ldap_ssl_ads() ) {
734 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
735 if (!ADS_ERR_OK(status)) {
740 /* fill in the current time and offsets */
742 status = ads_current_time( ads );
743 if ( !ADS_ERR_OK(status) ) {
747 /* Now do the bind */
749 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
750 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
754 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
755 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
759 status = ads_sasl_bind(ads);
762 if (DEBUGLEVEL >= 11) {
763 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
764 DEBUG(11,("ads_connect: leaving with: %s\n",
765 ads_errstr(status)));
766 DEBUGADD(11,("%s\n", s));
774 * Connect to the LDAP server using given credentials
775 * @param ads Pointer to an existing ADS_STRUCT
776 * @return status of connection
778 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
780 ads->auth.flags |= ADS_AUTH_USER_CREDS;
782 return ads_connect(ads);
786 * Disconnect the LDAP server
787 * @param ads Pointer to an existing ADS_STRUCT
789 void ads_disconnect(ADS_STRUCT *ads)
792 ldap_unbind(ads->ldap.ld);
795 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
796 ads->ldap.wrap_ops->disconnect(ads);
798 if (ads->ldap.mem_ctx) {
799 talloc_free(ads->ldap.mem_ctx);
801 ZERO_STRUCT(ads->ldap);
805 Duplicate a struct berval into talloc'ed memory
807 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
809 struct berval *value;
811 if (!in_val) return NULL;
813 value = talloc_zero(ctx, struct berval);
816 if (in_val->bv_len == 0) return value;
818 value->bv_len = in_val->bv_len;
819 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
825 Make a values list out of an array of (struct berval *)
827 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
828 const struct berval **in_vals)
830 struct berval **values;
833 if (!in_vals) return NULL;
834 for (i=0; in_vals[i]; i++)
836 values = talloc_zero_array(ctx, struct berval *, i+1);
837 if (!values) return NULL;
839 for (i=0; in_vals[i]; i++) {
840 values[i] = dup_berval(ctx, in_vals[i]);
846 UTF8-encode a values list out of an array of (char *)
848 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
854 if (!in_vals) return NULL;
855 for (i=0; in_vals[i]; i++)
857 values = talloc_zero_array(ctx, char *, i+1);
858 if (!values) return NULL;
860 for (i=0; in_vals[i]; i++) {
861 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
870 Pull a (char *) array out of a UTF8-encoded values list
872 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
876 size_t converted_size;
878 if (!in_vals) return NULL;
879 for (i=0; in_vals[i]; i++)
881 values = talloc_zero_array(ctx, char *, i+1);
882 if (!values) return NULL;
884 for (i=0; in_vals[i]; i++) {
885 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
887 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
888 "%s", strerror(errno)));
895 * Do a search with paged results. cookie must be null on the first
896 * call, and then returned on each subsequent call. It will be null
897 * again when the entire search is complete
898 * @param ads connection to ads server
899 * @param bind_path Base dn for the search
900 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
901 * @param expr Search expression - specified in local charset
902 * @param attrs Attributes to retrieve - specified in utf8 or ascii
903 * @param res ** which will contain results - free res* with ads_msgfree()
904 * @param count Number of entries retrieved on this page
905 * @param cookie The paged results cookie to be returned on subsequent calls
906 * @return status of search
908 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
909 const char *bind_path,
910 int scope, const char *expr,
911 const char **attrs, void *args,
913 int *count, struct berval **cookie)
916 char *utf8_expr, *utf8_path, **search_attrs = NULL;
917 size_t converted_size;
918 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
919 BerElement *cookie_be = NULL;
920 struct berval *cookie_bv= NULL;
921 BerElement *ext_be = NULL;
922 struct berval *ext_bv= NULL;
925 ads_control *external_control = (ads_control *) args;
929 if (!(ctx = talloc_init("ads_do_paged_search_args")))
930 return ADS_ERROR(LDAP_NO_MEMORY);
932 /* 0 means the conversion worked but the result was empty
933 so we only fail if it's -1. In any case, it always
934 at least nulls out the dest */
935 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
936 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
942 if (!attrs || !(*attrs))
945 /* This would be the utf8-encoded version...*/
946 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
947 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
953 /* Paged results only available on ldap v3 or later */
954 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
955 if (version < LDAP_VERSION3) {
956 rc = LDAP_NOT_SUPPORTED;
960 cookie_be = ber_alloc_t(LBER_USE_DER);
962 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
963 ber_bvfree(*cookie); /* don't need it from last time */
966 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
968 ber_flatten(cookie_be, &cookie_bv);
969 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
970 PagedResults.ldctl_iscritical = (char) 1;
971 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
972 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
974 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
975 NoReferrals.ldctl_iscritical = (char) 0;
976 NoReferrals.ldctl_value.bv_len = 0;
977 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
979 if (external_control &&
980 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
981 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
983 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
984 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
986 /* win2k does not accept a ldctl_value beeing passed in */
988 if (external_control->val != 0) {
990 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
995 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
999 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1000 rc = LDAP_NO_MEMORY;
1004 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1005 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1008 ExternalCtrl.ldctl_value.bv_len = 0;
1009 ExternalCtrl.ldctl_value.bv_val = NULL;
1012 controls[0] = &NoReferrals;
1013 controls[1] = &PagedResults;
1014 controls[2] = &ExternalCtrl;
1018 controls[0] = &NoReferrals;
1019 controls[1] = &PagedResults;
1023 /* we need to disable referrals as the openldap libs don't
1024 handle them and paged results at the same time. Using them
1025 together results in the result record containing the server
1026 page control being removed from the result list (tridge/jmcd)
1028 leaving this in despite the control that says don't generate
1029 referrals, in case the server doesn't support it (jmcd)
1031 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1033 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1034 search_attrs, 0, controls,
1035 NULL, LDAP_NO_LIMIT,
1036 (LDAPMessage **)res);
1038 ber_free(cookie_be, 1);
1039 ber_bvfree(cookie_bv);
1042 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1043 ldap_err2string(rc)));
1047 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1048 NULL, &rcontrols, 0);
1054 for (i=0; rcontrols[i]; i++) {
1055 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1056 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1057 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1059 /* the berval is the cookie, but must be freed when
1061 if (cookie_bv->bv_len) /* still more to do */
1062 *cookie=ber_bvdup(cookie_bv);
1065 ber_bvfree(cookie_bv);
1066 ber_free(cookie_be, 1);
1070 ldap_controls_free(rcontrols);
1073 talloc_destroy(ctx);
1076 ber_free(ext_be, 1);
1083 /* if/when we decide to utf8-encode attrs, take out this next line */
1084 TALLOC_FREE(search_attrs);
1086 return ADS_ERROR(rc);
1089 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1090 int scope, const char *expr,
1091 const char **attrs, LDAPMessage **res,
1092 int *count, struct berval **cookie)
1094 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1099 * Get all results for a search. This uses ads_do_paged_search() to return
1100 * all entries in a large search.
1101 * @param ads connection to ads server
1102 * @param bind_path Base dn for the search
1103 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1104 * @param expr Search expression
1105 * @param attrs Attributes to retrieve
1106 * @param res ** which will contain results - free res* with ads_msgfree()
1107 * @return status of search
1109 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1110 int scope, const char *expr,
1111 const char **attrs, void *args,
1114 struct berval *cookie = NULL;
1119 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1122 if (!ADS_ERR_OK(status))
1125 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1127 LDAPMessage *res2 = NULL;
1129 LDAPMessage *msg, *next;
1131 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1132 attrs, args, &res2, &count, &cookie);
1134 if (!ADS_ERR_OK(status2)) break;
1136 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1137 that this works on all ldap libs, but I have only tested with openldap */
1138 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1139 next = ads_next_message(ads, msg);
1140 ldap_add_result_entry((LDAPMessage **)res, msg);
1142 /* note that we do not free res2, as the memory is now
1143 part of the main returned list */
1146 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1147 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1153 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1154 int scope, const char *expr,
1155 const char **attrs, LDAPMessage **res)
1157 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1160 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1161 int scope, const char *expr,
1162 const char **attrs, uint32 sd_flags,
1167 args.control = ADS_SD_FLAGS_OID;
1168 args.val = sd_flags;
1169 args.critical = True;
1171 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1176 * Run a function on all results for a search. Uses ads_do_paged_search() and
1177 * runs the function as each page is returned, using ads_process_results()
1178 * @param ads connection to ads server
1179 * @param bind_path Base dn for the search
1180 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1181 * @param expr Search expression - specified in local charset
1182 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1183 * @param fn Function which takes attr name, values list, and data_area
1184 * @param data_area Pointer which is passed to function on each call
1185 * @return status of search
1187 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1188 int scope, const char *expr, const char **attrs,
1189 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1192 struct berval *cookie = NULL;
1197 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1200 if (!ADS_ERR_OK(status)) return status;
1202 ads_process_results(ads, res, fn, data_area);
1203 ads_msgfree(ads, res);
1206 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1207 &res, &count, &cookie);
1209 if (!ADS_ERR_OK(status)) break;
1211 ads_process_results(ads, res, fn, data_area);
1212 ads_msgfree(ads, res);
1219 * Do a search with a timeout.
1220 * @param ads connection to ads server
1221 * @param bind_path Base dn for the search
1222 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1223 * @param expr Search expression
1224 * @param attrs Attributes to retrieve
1225 * @param res ** which will contain results - free res* with ads_msgfree()
1226 * @return status of search
1228 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1230 const char **attrs, LDAPMessage **res)
1233 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1234 size_t converted_size;
1238 if (!(ctx = talloc_init("ads_do_search"))) {
1239 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1240 return ADS_ERROR(LDAP_NO_MEMORY);
1243 /* 0 means the conversion worked but the result was empty
1244 so we only fail if it's negative. In any case, it always
1245 at least nulls out the dest */
1246 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1247 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1249 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1250 rc = LDAP_NO_MEMORY;
1254 if (!attrs || !(*attrs))
1255 search_attrs = NULL;
1257 /* This would be the utf8-encoded version...*/
1258 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1259 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1261 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1262 rc = LDAP_NO_MEMORY;
1267 /* see the note in ads_do_paged_search - we *must* disable referrals */
1268 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1270 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1271 search_attrs, 0, NULL, NULL,
1273 (LDAPMessage **)res);
1275 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1276 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1281 talloc_destroy(ctx);
1282 /* if/when we decide to utf8-encode attrs, take out this next line */
1283 TALLOC_FREE(search_attrs);
1284 return ADS_ERROR(rc);
1287 * Do a general ADS search
1288 * @param ads connection to ads server
1289 * @param res ** which will contain results - free res* with ads_msgfree()
1290 * @param expr Search expression
1291 * @param attrs Attributes to retrieve
1292 * @return status of search
1294 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1295 const char *expr, const char **attrs)
1297 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1302 * Do a search on a specific DistinguishedName
1303 * @param ads connection to ads server
1304 * @param res ** which will contain results - free res* with ads_msgfree()
1305 * @param dn DistinguishName to search
1306 * @param attrs Attributes to retrieve
1307 * @return status of search
1309 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1310 const char *dn, const char **attrs)
1312 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1317 * Free up memory from a ads_search
1318 * @param ads connection to ads server
1319 * @param msg Search results to free
1321 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1328 * Get a dn from search results
1329 * @param ads connection to ads server
1330 * @param msg Search result
1333 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1335 char *utf8_dn, *unix_dn;
1336 size_t converted_size;
1338 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1341 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1345 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1346 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1350 ldap_memfree(utf8_dn);
1355 * Get the parent from a dn
1356 * @param dn the dn to return the parent from
1357 * @return parent dn string
1359 char *ads_parent_dn(const char *dn)
1367 p = strchr(dn, ',');
1377 * Find a machine account given a hostname
1378 * @param ads connection to ads server
1379 * @param res ** which will contain results - free res* with ads_msgfree()
1380 * @param host Hostname to search for
1381 * @return status of search
1383 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1384 const char *machine)
1388 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1392 /* the easiest way to find a machine account anywhere in the tree
1393 is to look for hostname$ */
1394 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1395 DEBUG(1, ("asprintf failed!\n"));
1396 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1399 status = ads_search(ads, res, expr, attrs);
1405 * Initialize a list of mods to be used in a modify request
1406 * @param ctx An initialized TALLOC_CTX
1407 * @return allocated ADS_MODLIST
1409 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1411 #define ADS_MODLIST_ALLOC_SIZE 10
1414 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1415 /* -1 is safety to make sure we don't go over the end.
1416 need to reset it to NULL before doing ldap modify */
1417 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1419 return (ADS_MODLIST)mods;
1424 add an attribute to the list, with values list already constructed
1426 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1427 int mod_op, const char *name,
1428 const void *_invals)
1430 const void **invals = (const void **)_invals;
1432 LDAPMod **modlist = (LDAPMod **) *mods;
1433 struct berval **ber_values = NULL;
1434 char **char_values = NULL;
1437 mod_op = LDAP_MOD_DELETE;
1439 if (mod_op & LDAP_MOD_BVALUES)
1440 ber_values = ads_dup_values(ctx,
1441 (const struct berval **)invals);
1443 char_values = ads_push_strvals(ctx,
1444 (const char **) invals);
1447 /* find the first empty slot */
1448 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1450 if (modlist[curmod] == (LDAPMod *) -1) {
1451 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1452 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1453 return ADS_ERROR(LDAP_NO_MEMORY);
1454 memset(&modlist[curmod], 0,
1455 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1456 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1457 *mods = (ADS_MODLIST)modlist;
1460 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1461 return ADS_ERROR(LDAP_NO_MEMORY);
1462 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1463 if (mod_op & LDAP_MOD_BVALUES) {
1464 modlist[curmod]->mod_bvalues = ber_values;
1465 } else if (mod_op & LDAP_MOD_DELETE) {
1466 modlist[curmod]->mod_values = NULL;
1468 modlist[curmod]->mod_values = char_values;
1471 modlist[curmod]->mod_op = mod_op;
1472 return ADS_ERROR(LDAP_SUCCESS);
1476 * Add a single string value to a mod list
1477 * @param ctx An initialized TALLOC_CTX
1478 * @param mods An initialized ADS_MODLIST
1479 * @param name The attribute name to add
1480 * @param val The value to add - NULL means DELETE
1481 * @return ADS STATUS indicating success of add
1483 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1484 const char *name, const char *val)
1486 const char *values[2];
1492 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1493 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1497 * Add an array of string values to a mod list
1498 * @param ctx An initialized TALLOC_CTX
1499 * @param mods An initialized ADS_MODLIST
1500 * @param name The attribute name to add
1501 * @param vals The array of string values to add - NULL means DELETE
1502 * @return ADS STATUS indicating success of add
1504 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1505 const char *name, const char **vals)
1508 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1509 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1510 name, (const void **) vals);
1515 * Add a single ber-encoded value to a mod list
1516 * @param ctx An initialized TALLOC_CTX
1517 * @param mods An initialized ADS_MODLIST
1518 * @param name The attribute name to add
1519 * @param val The value to add - NULL means DELETE
1520 * @return ADS STATUS indicating success of add
1522 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1523 const char *name, const struct berval *val)
1525 const struct berval *values[2];
1530 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1531 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1532 name, (const void **) values);
1537 * Perform an ldap modify
1538 * @param ads connection to ads server
1539 * @param mod_dn DistinguishedName to modify
1540 * @param mods list of modifications to perform
1541 * @return status of modify
1543 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1546 char *utf8_dn = NULL;
1547 size_t converted_size;
1549 this control is needed to modify that contains a currently
1550 non-existent attribute (but allowable for the object) to run
1552 LDAPControl PermitModify = {
1553 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1556 LDAPControl *controls[2];
1558 controls[0] = &PermitModify;
1561 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1562 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1565 /* find the end of the list, marked by NULL or -1 */
1566 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1567 /* make sure the end of the list is NULL */
1569 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1570 (LDAPMod **) mods, controls, NULL);
1571 TALLOC_FREE(utf8_dn);
1572 return ADS_ERROR(ret);
1576 * Perform an ldap add
1577 * @param ads connection to ads server
1578 * @param new_dn DistinguishedName to add
1579 * @param mods list of attributes and values for DN
1580 * @return status of add
1582 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1585 char *utf8_dn = NULL;
1586 size_t converted_size;
1588 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1589 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1590 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1593 /* find the end of the list, marked by NULL or -1 */
1594 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1595 /* make sure the end of the list is NULL */
1598 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1599 TALLOC_FREE(utf8_dn);
1600 return ADS_ERROR(ret);
1604 * Delete a DistinguishedName
1605 * @param ads connection to ads server
1606 * @param new_dn DistinguishedName to delete
1607 * @return status of delete
1609 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1612 char *utf8_dn = NULL;
1613 size_t converted_size;
1614 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1615 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1616 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1619 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1620 TALLOC_FREE(utf8_dn);
1621 return ADS_ERROR(ret);
1625 * Build an org unit string
1626 * if org unit is Computers or blank then assume a container, otherwise
1627 * assume a / separated list of organisational units.
1628 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1629 * @param ads connection to ads server
1630 * @param org_unit Organizational unit
1631 * @return org unit string - caller must free
1633 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1637 if (!org_unit || !*org_unit) {
1639 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1641 /* samba4 might not yet respond to a wellknownobject-query */
1642 return ret ? ret : SMB_STRDUP("cn=Computers");
1645 if (strequal(org_unit, "Computers")) {
1646 return SMB_STRDUP("cn=Computers");
1649 /* jmcd: removed "\\" from the separation chars, because it is
1650 needed as an escape for chars like '#' which are valid in an
1652 return ads_build_path(org_unit, "/", "ou=", 1);
1656 * Get a org unit string for a well-known GUID
1657 * @param ads connection to ads server
1658 * @param wknguid Well known GUID
1659 * @return org unit string - caller must free
1661 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1664 LDAPMessage *res = NULL;
1665 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1666 **bind_dn_exp = NULL;
1667 const char *attrs[] = {"distinguishedName", NULL};
1668 int new_ln, wkn_ln, bind_ln, i;
1670 if (wknguid == NULL) {
1674 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1675 DEBUG(1, ("asprintf failed!\n"));
1679 status = ads_search_dn(ads, &res, base, attrs);
1680 if (!ADS_ERR_OK(status)) {
1681 DEBUG(1,("Failed while searching for: %s\n", base));
1685 if (ads_count_replies(ads, res) != 1) {
1689 /* substitute the bind-path from the well-known-guid-search result */
1690 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1695 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1700 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1705 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1707 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1710 new_ln = wkn_ln - bind_ln;
1712 ret = SMB_STRDUP(wkn_dn_exp[0]);
1717 for (i=1; i < new_ln; i++) {
1720 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1726 ret = SMB_STRDUP(s);
1735 ads_msgfree(ads, res);
1736 TALLOC_FREE(wkn_dn);
1738 ldap_value_free(wkn_dn_exp);
1741 ldap_value_free(bind_dn_exp);
1748 * Adds (appends) an item to an attribute array, rather then
1749 * replacing the whole list
1750 * @param ctx An initialized TALLOC_CTX
1751 * @param mods An initialized ADS_MODLIST
1752 * @param name name of the ldap attribute to append to
1753 * @param vals an array of values to add
1754 * @return status of addition
1757 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1758 const char *name, const char **vals)
1760 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1761 (const void *) vals);
1765 * Determines the an account's current KVNO via an LDAP lookup
1766 * @param ads An initialized ADS_STRUCT
1767 * @param account_name the NT samaccountname.
1768 * @return the kvno for the account, or -1 in case of a failure.
1771 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1773 LDAPMessage *res = NULL;
1774 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1776 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1777 char *dn_string = NULL;
1778 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1780 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1781 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1784 ret = ads_search(ads, &res, filter, attrs);
1786 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1787 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1788 ads_msgfree(ads, res);
1792 dn_string = ads_get_dn(ads, talloc_tos(), res);
1794 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1795 ads_msgfree(ads, res);
1798 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1799 TALLOC_FREE(dn_string);
1801 /* ---------------------------------------------------------
1802 * 0 is returned as a default KVNO from this point on...
1803 * This is done because Windows 2000 does not support key
1804 * version numbers. Chances are that a failure in the next
1805 * step is simply due to Windows 2000 being used for a
1806 * domain controller. */
1809 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1810 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1811 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1812 ads_msgfree(ads, res);
1817 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1818 ads_msgfree(ads, res);
1823 * Determines the computer account's current KVNO via an LDAP lookup
1824 * @param ads An initialized ADS_STRUCT
1825 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1826 * @return the kvno for the computer account, or -1 in case of a failure.
1829 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1831 char *computer_account = NULL;
1834 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1838 kvno = ads_get_kvno(ads, computer_account);
1839 free(computer_account);
1845 * This clears out all registered spn's for a given hostname
1846 * @param ads An initilaized ADS_STRUCT
1847 * @param machine_name the NetBIOS name of the computer.
1848 * @return 0 upon success, non-zero otherwise.
1851 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1854 LDAPMessage *res = NULL;
1856 const char *servicePrincipalName[1] = {NULL};
1857 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1858 char *dn_string = NULL;
1860 ret = ads_find_machine_acct(ads, &res, machine_name);
1861 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1862 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1863 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1864 ads_msgfree(ads, res);
1865 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1868 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1869 ctx = talloc_init("ads_clear_service_principal_names");
1871 ads_msgfree(ads, res);
1872 return ADS_ERROR(LDAP_NO_MEMORY);
1875 if (!(mods = ads_init_mods(ctx))) {
1876 talloc_destroy(ctx);
1877 ads_msgfree(ads, res);
1878 return ADS_ERROR(LDAP_NO_MEMORY);
1880 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1881 if (!ADS_ERR_OK(ret)) {
1882 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1883 ads_msgfree(ads, res);
1884 talloc_destroy(ctx);
1887 dn_string = ads_get_dn(ads, talloc_tos(), res);
1889 talloc_destroy(ctx);
1890 ads_msgfree(ads, res);
1891 return ADS_ERROR(LDAP_NO_MEMORY);
1893 ret = ads_gen_mod(ads, dn_string, mods);
1894 TALLOC_FREE(dn_string);
1895 if (!ADS_ERR_OK(ret)) {
1896 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1898 ads_msgfree(ads, res);
1899 talloc_destroy(ctx);
1903 ads_msgfree(ads, res);
1904 talloc_destroy(ctx);
1909 * This adds a service principal name to an existing computer account
1910 * (found by hostname) in AD.
1911 * @param ads An initialized ADS_STRUCT
1912 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1913 * @param my_fqdn The fully qualified DNS name of the machine
1914 * @param spn A string of the service principal to add, i.e. 'host'
1915 * @return 0 upon sucess, or non-zero if a failure occurs
1918 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1919 const char *my_fqdn, const char *spn)
1923 LDAPMessage *res = NULL;
1926 char *dn_string = NULL;
1927 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1929 ret = ads_find_machine_acct(ads, &res, machine_name);
1930 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1931 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1933 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1934 spn, machine_name, ads->config.realm));
1935 ads_msgfree(ads, res);
1936 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1939 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1940 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1941 ads_msgfree(ads, res);
1942 return ADS_ERROR(LDAP_NO_MEMORY);
1945 /* add short name spn */
1947 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1948 talloc_destroy(ctx);
1949 ads_msgfree(ads, res);
1950 return ADS_ERROR(LDAP_NO_MEMORY);
1952 if (!strupper_m(psp1)) {
1953 ret = ADS_ERROR(LDAP_NO_MEMORY);
1957 strlower_m(&psp1[strlen(spn)]);
1958 servicePrincipalName[0] = psp1;
1960 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1961 psp1, machine_name));
1964 /* add fully qualified spn */
1966 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1967 ret = ADS_ERROR(LDAP_NO_MEMORY);
1970 if (!strupper_m(psp2)) {
1971 ret = ADS_ERROR(LDAP_NO_MEMORY);
1975 strlower_m(&psp2[strlen(spn)]);
1976 servicePrincipalName[1] = psp2;
1978 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1979 psp2, machine_name));
1981 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1982 ret = ADS_ERROR(LDAP_NO_MEMORY);
1986 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1987 if (!ADS_ERR_OK(ret)) {
1988 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1992 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1993 ret = ADS_ERROR(LDAP_NO_MEMORY);
1997 ret = ads_gen_mod(ads, dn_string, mods);
1998 if (!ADS_ERR_OK(ret)) {
1999 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2005 ads_msgfree(ads, res);
2010 * adds a machine account to the ADS server
2011 * @param ads An intialized ADS_STRUCT
2012 * @param machine_name - the NetBIOS machine name of this account.
2013 * @param account_type A number indicating the type of account to create
2014 * @param org_unit The LDAP path in which to place this account
2015 * @return 0 upon success, or non-zero otherwise
2018 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2019 const char *org_unit)
2022 char *samAccountName, *controlstr;
2025 char *machine_escaped = NULL;
2027 const char *objectClass[] = {"top", "person", "organizationalPerson",
2028 "user", "computer", NULL};
2029 LDAPMessage *res = NULL;
2030 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2031 UF_DONT_EXPIRE_PASSWD |\
2032 UF_ACCOUNTDISABLE );
2034 if (!(ctx = talloc_init("ads_add_machine_acct")))
2035 return ADS_ERROR(LDAP_NO_MEMORY);
2037 ret = ADS_ERROR(LDAP_NO_MEMORY);
2039 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2040 if (!machine_escaped) {
2044 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2045 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2047 if ( !new_dn || !samAccountName ) {
2051 #ifndef ENCTYPE_ARCFOUR_HMAC
2052 acct_control |= UF_USE_DES_KEY_ONLY;
2055 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2059 if (!(mods = ads_init_mods(ctx))) {
2063 ads_mod_str(ctx, &mods, "cn", machine_name);
2064 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2065 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2066 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2068 ret = ads_gen_add(ads, new_dn, mods);
2071 SAFE_FREE(machine_escaped);
2072 ads_msgfree(ads, res);
2073 talloc_destroy(ctx);
2079 * move a machine account to another OU on the ADS server
2080 * @param ads - An intialized ADS_STRUCT
2081 * @param machine_name - the NetBIOS machine name of this account.
2082 * @param org_unit - The LDAP path in which to place this account
2083 * @param moved - whether we moved the machine account (optional)
2084 * @return 0 upon success, or non-zero otherwise
2087 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2088 const char *org_unit, bool *moved)
2092 LDAPMessage *res = NULL;
2093 char *filter = NULL;
2094 char *computer_dn = NULL;
2096 char *computer_rdn = NULL;
2097 bool need_move = False;
2099 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2100 rc = ADS_ERROR(LDAP_NO_MEMORY);
2104 /* Find pre-existing machine */
2105 rc = ads_search(ads, &res, filter, NULL);
2106 if (!ADS_ERR_OK(rc)) {
2110 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2112 rc = ADS_ERROR(LDAP_NO_MEMORY);
2116 parent_dn = ads_parent_dn(computer_dn);
2117 if (strequal(parent_dn, org_unit)) {
2123 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2124 rc = ADS_ERROR(LDAP_NO_MEMORY);
2128 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2129 org_unit, 1, NULL, NULL);
2130 rc = ADS_ERROR(ldap_status);
2133 ads_msgfree(ads, res);
2135 TALLOC_FREE(computer_dn);
2136 SAFE_FREE(computer_rdn);
2138 if (!ADS_ERR_OK(rc)) {
2150 dump a binary result from ldap
2152 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2155 for (i=0; values[i]; i++) {
2156 printf("%s: ", field);
2157 for (j=0; j<values[i]->bv_len; j++) {
2158 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2164 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2167 for (i=0; values[i]; i++) {
2169 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2172 status = GUID_from_ndr_blob(&in, &guid);
2173 if (NT_STATUS_IS_OK(status)) {
2174 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2176 printf("%s: INVALID GUID\n", field);
2182 dump a sid result from ldap
2184 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2187 for (i=0; values[i]; i++) {
2190 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2193 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2198 dump ntSecurityDescriptor
2200 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2202 TALLOC_CTX *frame = talloc_stackframe();
2203 struct security_descriptor *psd;
2206 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2207 values[0]->bv_len, &psd);
2208 if (!NT_STATUS_IS_OK(status)) {
2209 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2210 nt_errstr(status)));
2216 ads_disp_sd(ads, talloc_tos(), psd);
2223 dump a string result from ldap
2225 static void dump_string(const char *field, char **values)
2228 for (i=0; values[i]; i++) {
2229 printf("%s: %s\n", field, values[i]);
2234 dump a field from LDAP on stdout
2238 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2243 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2245 {"objectGUID", False, dump_guid},
2246 {"netbootGUID", False, dump_guid},
2247 {"nTSecurityDescriptor", False, dump_sd},
2248 {"dnsRecord", False, dump_binary},
2249 {"objectSid", False, dump_sid},
2250 {"tokenGroups", False, dump_sid},
2251 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2252 {"tokengroupsGlobalandUniversal", False, dump_sid},
2253 {"mS-DS-CreatorSID", False, dump_sid},
2254 {"msExchMailboxGuid", False, dump_guid},
2259 if (!field) { /* must be end of an entry */
2264 for (i=0; handlers[i].name; i++) {
2265 if (strcasecmp_m(handlers[i].name, field) == 0) {
2266 if (!values) /* first time, indicate string or not */
2267 return handlers[i].string;
2268 handlers[i].handler(ads, field, (struct berval **) values);
2272 if (!handlers[i].name) {
2273 if (!values) /* first time, indicate string conversion */
2275 dump_string(field, (char **)values);
2281 * Dump a result from LDAP on stdout
2282 * used for debugging
2283 * @param ads connection to ads server
2284 * @param res Results to dump
2287 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2289 ads_process_results(ads, res, ads_dump_field, NULL);
2293 * Walk through results, calling a function for each entry found.
2294 * The function receives a field name, a berval * array of values,
2295 * and a data area passed through from the start. The function is
2296 * called once with null for field and values at the end of each
2298 * @param ads connection to ads server
2299 * @param res Results to process
2300 * @param fn Function for processing each result
2301 * @param data_area user-defined area to pass to function
2303 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2304 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2309 size_t converted_size;
2311 if (!(ctx = talloc_init("ads_process_results")))
2314 for (msg = ads_first_entry(ads, res); msg;
2315 msg = ads_next_entry(ads, msg)) {
2319 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2320 (LDAPMessage *)msg,&b);
2322 utf8_field=ldap_next_attribute(ads->ldap.ld,
2323 (LDAPMessage *)msg,b)) {
2324 struct berval **ber_vals;
2325 char **str_vals, **utf8_vals;
2329 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2332 DEBUG(0,("ads_process_results: "
2333 "pull_utf8_talloc failed: %s",
2337 string = fn(ads, field, NULL, data_area);
2340 utf8_vals = ldap_get_values(ads->ldap.ld,
2341 (LDAPMessage *)msg, field);
2342 str_vals = ads_pull_strvals(ctx,
2343 (const char **) utf8_vals);
2344 fn(ads, field, (void **) str_vals, data_area);
2345 ldap_value_free(utf8_vals);
2347 ber_vals = ldap_get_values_len(ads->ldap.ld,
2348 (LDAPMessage *)msg, field);
2349 fn(ads, field, (void **) ber_vals, data_area);
2351 ldap_value_free_len(ber_vals);
2353 ldap_memfree(utf8_field);
2356 talloc_free_children(ctx);
2357 fn(ads, NULL, NULL, data_area); /* completed an entry */
2360 talloc_destroy(ctx);
2364 * count how many replies are in a LDAPMessage
2365 * @param ads connection to ads server
2366 * @param res Results to count
2367 * @return number of replies
2369 int ads_count_replies(ADS_STRUCT *ads, void *res)
2371 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2375 * pull the first entry from a ADS result
2376 * @param ads connection to ads server
2377 * @param res Results of search
2378 * @return first entry from result
2380 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2382 return ldap_first_entry(ads->ldap.ld, res);
2386 * pull the next entry from a ADS result
2387 * @param ads connection to ads server
2388 * @param res Results of search
2389 * @return next entry from result
2391 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2393 return ldap_next_entry(ads->ldap.ld, res);
2397 * pull the first message from a ADS result
2398 * @param ads connection to ads server
2399 * @param res Results of search
2400 * @return first message from result
2402 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2404 return ldap_first_message(ads->ldap.ld, res);
2408 * pull the next message from a ADS result
2409 * @param ads connection to ads server
2410 * @param res Results of search
2411 * @return next message from result
2413 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2415 return ldap_next_message(ads->ldap.ld, res);
2419 * pull a single string from a ADS result
2420 * @param ads connection to ads server
2421 * @param mem_ctx TALLOC_CTX to use for allocating result string
2422 * @param msg Results of search
2423 * @param field Attribute to retrieve
2424 * @return Result string in talloc context
2426 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2432 size_t converted_size;
2434 values = ldap_get_values(ads->ldap.ld, msg, field);
2438 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2443 ldap_value_free(values);
2448 * pull an array of strings from a ADS result
2449 * @param ads connection to ads server
2450 * @param mem_ctx TALLOC_CTX to use for allocating result string
2451 * @param msg Results of search
2452 * @param field Attribute to retrieve
2453 * @return Result strings in talloc context
2455 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2456 LDAPMessage *msg, const char *field,
2462 size_t converted_size;
2464 values = ldap_get_values(ads->ldap.ld, msg, field);
2468 *num_values = ldap_count_values(values);
2470 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2472 ldap_value_free(values);
2476 for (i=0;i<*num_values;i++) {
2477 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2480 ldap_value_free(values);
2486 ldap_value_free(values);
2491 * pull an array of strings from a ADS result
2492 * (handle large multivalue attributes with range retrieval)
2493 * @param ads connection to ads server
2494 * @param mem_ctx TALLOC_CTX to use for allocating result string
2495 * @param msg Results of search
2496 * @param field Attribute to retrieve
2497 * @param current_strings strings returned by a previous call to this function
2498 * @param next_attribute The next query should ask for this attribute
2499 * @param num_values How many values did we get this time?
2500 * @param more_values Are there more values to get?
2501 * @return Result strings in talloc context
2503 char **ads_pull_strings_range(ADS_STRUCT *ads,
2504 TALLOC_CTX *mem_ctx,
2505 LDAPMessage *msg, const char *field,
2506 char **current_strings,
2507 const char **next_attribute,
2508 size_t *num_strings,
2512 char *expected_range_attrib, *range_attr;
2513 BerElement *ptr = NULL;
2516 size_t num_new_strings;
2517 unsigned long int range_start;
2518 unsigned long int range_end;
2520 /* we might have been given the whole lot anyway */
2521 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2522 *more_strings = False;
2526 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2528 /* look for Range result */
2529 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2531 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2532 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2533 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2541 /* nothing here - this field is just empty */
2542 *more_strings = False;
2546 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2547 &range_start, &range_end) == 2) {
2548 *more_strings = True;
2550 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2551 &range_start) == 1) {
2552 *more_strings = False;
2554 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2556 ldap_memfree(range_attr);
2557 *more_strings = False;
2562 if ((*num_strings) != range_start) {
2563 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2564 " - aborting range retreival\n",
2565 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2566 ldap_memfree(range_attr);
2567 *more_strings = False;
2571 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2573 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2574 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2575 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2576 range_attr, (unsigned long int)range_end - range_start + 1,
2577 (unsigned long int)num_new_strings));
2578 ldap_memfree(range_attr);
2579 *more_strings = False;
2583 strings = talloc_realloc(mem_ctx, current_strings, char *,
2584 *num_strings + num_new_strings);
2586 if (strings == NULL) {
2587 ldap_memfree(range_attr);
2588 *more_strings = False;
2592 if (new_strings && num_new_strings) {
2593 memcpy(&strings[*num_strings], new_strings,
2594 sizeof(*new_strings) * num_new_strings);
2597 (*num_strings) += num_new_strings;
2599 if (*more_strings) {
2600 *next_attribute = talloc_asprintf(mem_ctx,
2605 if (!*next_attribute) {
2606 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2607 ldap_memfree(range_attr);
2608 *more_strings = False;
2613 ldap_memfree(range_attr);
2619 * pull a single uint32 from a ADS result
2620 * @param ads connection to ads server
2621 * @param msg Results of search
2622 * @param field Attribute to retrieve
2623 * @param v Pointer to int to store result
2624 * @return boolean inidicating success
2626 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2631 values = ldap_get_values(ads->ldap.ld, msg, field);
2635 ldap_value_free(values);
2639 *v = atoi(values[0]);
2640 ldap_value_free(values);
2645 * pull a single objectGUID from an ADS result
2646 * @param ads connection to ADS server
2647 * @param msg results of search
2648 * @param guid 37-byte area to receive text guid
2649 * @return boolean indicating success
2651 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2656 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2661 status = GUID_from_ndr_blob(&blob, guid);
2662 talloc_free(blob.data);
2663 return NT_STATUS_IS_OK(status);
2668 * pull a single struct dom_sid from a ADS result
2669 * @param ads connection to ads server
2670 * @param msg Results of search
2671 * @param field Attribute to retrieve
2672 * @param sid Pointer to sid to store result
2673 * @return boolean inidicating success
2675 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2676 struct dom_sid *sid)
2678 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2682 * pull an array of struct dom_sids from a ADS result
2683 * @param ads connection to ads server
2684 * @param mem_ctx TALLOC_CTX for allocating sid array
2685 * @param msg Results of search
2686 * @param field Attribute to retrieve
2687 * @param sids pointer to sid array to allocate
2688 * @return the count of SIDs pulled
2690 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2691 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2693 struct berval **values;
2697 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2702 for (i=0; values[i]; i++)
2706 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2708 ldap_value_free_len(values);
2716 for (i=0; values[i]; i++) {
2717 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2719 DEBUG(10, ("pulling SID: %s\n",
2720 sid_string_dbg(&(*sids)[count])));
2725 ldap_value_free_len(values);
2730 * pull a struct security_descriptor from a ADS result
2731 * @param ads connection to ads server
2732 * @param mem_ctx TALLOC_CTX for allocating sid array
2733 * @param msg Results of search
2734 * @param field Attribute to retrieve
2735 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2736 * @return boolean inidicating success
2738 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2739 LDAPMessage *msg, const char *field,
2740 struct security_descriptor **sd)
2742 struct berval **values;
2745 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2747 if (!values) return false;
2751 status = unmarshall_sec_desc(mem_ctx,
2752 (uint8 *)values[0]->bv_val,
2753 values[0]->bv_len, sd);
2754 if (!NT_STATUS_IS_OK(status)) {
2755 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2756 nt_errstr(status)));
2761 ldap_value_free_len(values);
2766 * in order to support usernames longer than 21 characters we need to
2767 * use both the sAMAccountName and the userPrincipalName attributes
2768 * It seems that not all users have the userPrincipalName attribute set
2770 * @param ads connection to ads server
2771 * @param mem_ctx TALLOC_CTX for allocating sid array
2772 * @param msg Results of search
2773 * @return the username
2775 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2781 /* lookup_name() only works on the sAMAccountName to
2782 returning the username portion of userPrincipalName
2783 breaks winbindd_getpwnam() */
2785 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2786 if (ret && (p = strchr_m(ret, '@'))) {
2791 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2796 * find the update serial number - this is the core of the ldap cache
2797 * @param ads connection to ads server
2798 * @param ads connection to ADS server
2799 * @param usn Pointer to retrieved update serial number
2800 * @return status of search
2802 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2804 const char *attrs[] = {"highestCommittedUSN", NULL};
2808 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2809 if (!ADS_ERR_OK(status))
2812 if (ads_count_replies(ads, res) != 1) {
2813 ads_msgfree(ads, res);
2814 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2817 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2818 ads_msgfree(ads, res);
2819 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2822 ads_msgfree(ads, res);
2826 /* parse a ADS timestring - typical string is
2827 '20020917091222.0Z0' which means 09:12.22 17th September
2829 static time_t ads_parse_time(const char *str)
2835 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2836 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2837 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2846 /********************************************************************
2847 ********************************************************************/
2849 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2851 const char *attrs[] = {"currentTime", NULL};
2856 ADS_STRUCT *ads_s = ads;
2858 if (!(ctx = talloc_init("ads_current_time"))) {
2859 return ADS_ERROR(LDAP_NO_MEMORY);
2862 /* establish a new ldap tcp session if necessary */
2864 if ( !ads->ldap.ld ) {
2865 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2866 ads->server.ldap_server )) == NULL )
2870 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2871 status = ads_connect( ads_s );
2872 if ( !ADS_ERR_OK(status))
2876 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2877 if (!ADS_ERR_OK(status)) {
2881 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2883 ads_msgfree(ads_s, res);
2884 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2888 /* but save the time and offset in the original ADS_STRUCT */
2890 ads->config.current_time = ads_parse_time(timestr);
2892 if (ads->config.current_time != 0) {
2893 ads->auth.time_offset = ads->config.current_time - time(NULL);
2894 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2897 ads_msgfree(ads, res);
2899 status = ADS_SUCCESS;
2902 /* free any temporary ads connections */
2903 if ( ads_s != ads ) {
2904 ads_destroy( &ads_s );
2906 talloc_destroy(ctx);
2911 /********************************************************************
2912 ********************************************************************/
2914 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2916 const char *attrs[] = {"domainFunctionality", NULL};
2919 ADS_STRUCT *ads_s = ads;
2921 *val = DS_DOMAIN_FUNCTION_2000;
2923 /* establish a new ldap tcp session if necessary */
2925 if ( !ads->ldap.ld ) {
2926 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2927 ads->server.ldap_server )) == NULL )
2929 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2932 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2933 status = ads_connect( ads_s );
2934 if ( !ADS_ERR_OK(status))
2938 /* If the attribute does not exist assume it is a Windows 2000
2939 functional domain */
2941 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2942 if (!ADS_ERR_OK(status)) {
2943 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2944 status = ADS_SUCCESS;
2949 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2950 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2952 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2955 ads_msgfree(ads, res);
2958 /* free any temporary ads connections */
2959 if ( ads_s != ads ) {
2960 ads_destroy( &ads_s );
2967 * find the domain sid for our domain
2968 * @param ads connection to ads server
2969 * @param sid Pointer to domain sid
2970 * @return status of search
2972 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2974 const char *attrs[] = {"objectSid", NULL};
2978 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2980 if (!ADS_ERR_OK(rc)) return rc;
2981 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2982 ads_msgfree(ads, res);
2983 return ADS_ERROR_SYSTEM(ENOENT);
2985 ads_msgfree(ads, res);
2991 * find our site name
2992 * @param ads connection to ads server
2993 * @param mem_ctx Pointer to talloc context
2994 * @param site_name Pointer to the sitename
2995 * @return status of search
2997 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3001 const char *dn, *service_name;
3002 const char *attrs[] = { "dsServiceName", NULL };
3004 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3005 if (!ADS_ERR_OK(status)) {
3009 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3010 if (service_name == NULL) {
3011 ads_msgfree(ads, res);
3012 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3015 ads_msgfree(ads, res);
3017 /* go up three levels */
3018 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3020 return ADS_ERROR(LDAP_NO_MEMORY);
3023 *site_name = talloc_strdup(mem_ctx, dn);
3024 if (*site_name == NULL) {
3025 return ADS_ERROR(LDAP_NO_MEMORY);
3030 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3035 * find the site dn where a machine resides
3036 * @param ads connection to ads server
3037 * @param mem_ctx Pointer to talloc context
3038 * @param computer_name name of the machine
3039 * @param site_name Pointer to the sitename
3040 * @return status of search
3042 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3046 const char *parent, *filter;
3047 char *config_context = NULL;
3050 /* shortcut a query */
3051 if (strequal(computer_name, ads->config.ldap_server_name)) {
3052 return ads_site_dn(ads, mem_ctx, site_dn);
3055 status = ads_config_path(ads, mem_ctx, &config_context);
3056 if (!ADS_ERR_OK(status)) {
3060 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3061 if (filter == NULL) {
3062 return ADS_ERROR(LDAP_NO_MEMORY);
3065 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3066 filter, NULL, &res);
3067 if (!ADS_ERR_OK(status)) {
3071 if (ads_count_replies(ads, res) != 1) {
3072 ads_msgfree(ads, res);
3073 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3076 dn = ads_get_dn(ads, mem_ctx, res);
3078 ads_msgfree(ads, res);
3079 return ADS_ERROR(LDAP_NO_MEMORY);
3082 /* go up three levels */
3083 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3084 if (parent == NULL) {
3085 ads_msgfree(ads, res);
3087 return ADS_ERROR(LDAP_NO_MEMORY);
3090 *site_dn = talloc_strdup(mem_ctx, parent);
3091 if (*site_dn == NULL) {
3092 ads_msgfree(ads, res);
3094 return ADS_ERROR(LDAP_NO_MEMORY);
3098 ads_msgfree(ads, res);
3104 * get the upn suffixes for a domain
3105 * @param ads connection to ads server
3106 * @param mem_ctx Pointer to talloc context
3107 * @param suffixes Pointer to an array of suffixes
3108 * @param num_suffixes Pointer to the number of suffixes
3109 * @return status of search
3111 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3116 char *config_context = NULL;
3117 const char *attrs[] = { "uPNSuffixes", NULL };
3119 status = ads_config_path(ads, mem_ctx, &config_context);
3120 if (!ADS_ERR_OK(status)) {
3124 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3126 return ADS_ERROR(LDAP_NO_MEMORY);
3129 status = ads_search_dn(ads, &res, base, attrs);
3130 if (!ADS_ERR_OK(status)) {
3134 if (ads_count_replies(ads, res) != 1) {
3135 ads_msgfree(ads, res);
3136 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3139 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3140 if ((*suffixes) == NULL) {
3141 ads_msgfree(ads, res);
3142 return ADS_ERROR(LDAP_NO_MEMORY);
3145 ads_msgfree(ads, res);
3151 * get the joinable ous for a domain
3152 * @param ads connection to ads server
3153 * @param mem_ctx Pointer to talloc context
3154 * @param ous Pointer to an array of ous
3155 * @param num_ous Pointer to the number of ous
3156 * @return status of search
3158 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3159 TALLOC_CTX *mem_ctx,
3164 LDAPMessage *res = NULL;
3165 LDAPMessage *msg = NULL;
3166 const char *attrs[] = { "dn", NULL };
3169 status = ads_search(ads, &res,
3170 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3172 if (!ADS_ERR_OK(status)) {
3176 count = ads_count_replies(ads, res);
3178 ads_msgfree(ads, res);
3179 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3182 for (msg = ads_first_entry(ads, res); msg;
3183 msg = ads_next_entry(ads, msg)) {
3187 dn = ads_get_dn(ads, talloc_tos(), msg);
3189 ads_msgfree(ads, res);
3190 return ADS_ERROR(LDAP_NO_MEMORY);
3193 if (!add_string_to_array(mem_ctx, dn,
3194 (const char ***)ous,
3197 ads_msgfree(ads, res);
3198 return ADS_ERROR(LDAP_NO_MEMORY);
3204 ads_msgfree(ads, res);
3211 * pull a struct dom_sid from an extended dn string
3212 * @param mem_ctx TALLOC_CTX
3213 * @param extended_dn string
3214 * @param flags string type of extended_dn
3215 * @param sid pointer to a struct dom_sid
3216 * @return NT_STATUS_OK on success,
3217 * NT_INVALID_PARAMETER on error,
3218 * NT_STATUS_NOT_FOUND if no SID present
3220 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3221 const char *extended_dn,
3222 enum ads_extended_dn_flags flags,
3223 struct dom_sid *sid)
3228 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3231 /* otherwise extended_dn gets stripped off */
3232 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3233 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3236 * ADS_EXTENDED_DN_HEX_STRING:
3237 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3239 * ADS_EXTENDED_DN_STRING (only with w2k3):
3240 * <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
3242 * Object with no SID, such as an Exchange Public Folder
3243 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3246 p = strchr(dn, ';');
3248 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3251 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3252 DEBUG(5,("No SID present in extended dn\n"));
3253 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3256 p += strlen(";<SID=");
3260 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3265 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3269 case ADS_EXTENDED_DN_STRING:
3270 if (!string_to_sid(sid, p)) {
3271 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3274 case ADS_EXTENDED_DN_HEX_STRING: {
3278 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3280 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3283 if (!sid_parse(buf, buf_len, sid)) {
3284 DEBUG(10,("failed to parse sid\n"));
3285 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3290 DEBUG(10,("unknown extended dn format\n"));
3291 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3294 return ADS_ERROR_NT(NT_STATUS_OK);
3297 /********************************************************************
3298 ********************************************************************/
3300 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3302 LDAPMessage *res = NULL;
3307 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3308 if (!ADS_ERR_OK(status)) {
3309 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3310 lp_netbios_name()));
3314 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3315 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3319 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3320 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3324 ads_msgfree(ads, res);
3329 /********************************************************************
3330 ********************************************************************/
3332 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3334 LDAPMessage *res = NULL;
3339 status = ads_find_machine_acct(ads, &res, machine_name);
3340 if (!ADS_ERR_OK(status)) {
3341 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3342 lp_netbios_name()));
3346 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3347 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3351 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3352 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3356 ads_msgfree(ads, res);
3361 /********************************************************************
3362 ********************************************************************/
3364 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3366 LDAPMessage *res = NULL;
3371 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3372 if (!ADS_ERR_OK(status)) {
3373 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3374 lp_netbios_name()));
3378 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3379 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3383 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3384 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3388 ads_msgfree(ads, res);
3395 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3398 * Join a machine to a realm
3399 * Creates the machine account and sets the machine password
3400 * @param ads connection to ads server
3401 * @param machine name of host to add
3402 * @param org_unit Organizational unit to place machine in
3403 * @return status of join
3405 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3406 uint32 account_type, const char *org_unit)
3409 LDAPMessage *res = NULL;
3412 /* machine name must be lowercase */
3413 machine = SMB_STRDUP(machine_name);
3414 strlower_m(machine);
3417 status = ads_find_machine_acct(ads, (void **)&res, machine);
3418 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3419 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3420 status = ads_leave_realm(ads, machine);
3421 if (!ADS_ERR_OK(status)) {
3422 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3423 machine, ads->config.realm));
3428 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3429 if (!ADS_ERR_OK(status)) {
3430 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3435 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3436 if (!ADS_ERR_OK(status)) {
3437 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3443 ads_msgfree(ads, res);
3450 * Delete a machine from the realm
3451 * @param ads connection to ads server
3452 * @param hostname Machine to remove
3453 * @return status of delete
3455 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3460 char *hostnameDN, *host;
3462 LDAPControl ldap_control;
3463 LDAPControl * pldap_control[2] = {NULL, NULL};
3465 pldap_control[0] = &ldap_control;
3466 memset(&ldap_control, 0, sizeof(LDAPControl));
3467 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3469 /* hostname must be lowercase */
3470 host = SMB_STRDUP(hostname);
3473 status = ads_find_machine_acct(ads, &res, host);
3474 if (!ADS_ERR_OK(status)) {
3475 DEBUG(0, ("Host account for %s does not exist.\n", host));
3480 msg = ads_first_entry(ads, res);
3483 return ADS_ERROR_SYSTEM(ENOENT);
3486 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3487 if (hostnameDN == NULL) {
3489 return ADS_ERROR_SYSTEM(ENOENT);
3492 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3494 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3496 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3499 if (rc != LDAP_SUCCESS) {
3500 const char *attrs[] = { "cn", NULL };
3501 LDAPMessage *msg_sub;
3503 /* we only search with scope ONE, we do not expect any further
3504 * objects to be created deeper */
3506 status = ads_do_search_retry(ads, hostnameDN,
3507 LDAP_SCOPE_ONELEVEL,
3508 "(objectclass=*)", attrs, &res);
3510 if (!ADS_ERR_OK(status)) {
3512 TALLOC_FREE(hostnameDN);
3516 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3517 msg_sub = ads_next_entry(ads, msg_sub)) {
3521 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3523 TALLOC_FREE(hostnameDN);
3524 return ADS_ERROR(LDAP_NO_MEMORY);
3527 status = ads_del_dn(ads, dn);
3528 if (!ADS_ERR_OK(status)) {
3529 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3532 TALLOC_FREE(hostnameDN);
3539 /* there should be no subordinate objects anymore */
3540 status = ads_do_search_retry(ads, hostnameDN,
3541 LDAP_SCOPE_ONELEVEL,
3542 "(objectclass=*)", attrs, &res);
3544 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3546 TALLOC_FREE(hostnameDN);
3550 /* delete hostnameDN now */
3551 status = ads_del_dn(ads, hostnameDN);
3552 if (!ADS_ERR_OK(status)) {
3554 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3555 TALLOC_FREE(hostnameDN);
3560 TALLOC_FREE(hostnameDN);
3562 status = ads_find_machine_acct(ads, &res, host);
3563 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3564 DEBUG(3, ("Failed to remove host account.\n"));
3574 * pull all token-sids from an LDAP dn
3575 * @param ads connection to ads server
3576 * @param mem_ctx TALLOC_CTX for allocating sid array
3577 * @param dn of LDAP object
3578 * @param user_sid pointer to struct dom_sid (objectSid)
3579 * @param primary_group_sid pointer to struct dom_sid (self composed)
3580 * @param sids pointer to sid array to allocate
3581 * @param num_sids counter of SIDs pulled
3582 * @return status of token query
3584 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3585 TALLOC_CTX *mem_ctx,
3587 struct dom_sid *user_sid,
3588 struct dom_sid *primary_group_sid,
3589 struct dom_sid **sids,
3593 LDAPMessage *res = NULL;
3595 size_t tmp_num_sids;
3596 struct dom_sid *tmp_sids;
3597 struct dom_sid tmp_user_sid;
3598 struct dom_sid tmp_primary_group_sid;
3600 const char *attrs[] = {
3607 status = ads_search_retry_dn(ads, &res, dn, attrs);
3608 if (!ADS_ERR_OK(status)) {
3612 count = ads_count_replies(ads, res);
3614 ads_msgfree(ads, res);
3615 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3618 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3619 ads_msgfree(ads, res);
3620 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3623 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3624 ads_msgfree(ads, res);
3625 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3629 /* hack to compose the primary group sid without knowing the
3632 struct dom_sid domsid;
3634 sid_copy(&domsid, &tmp_user_sid);
3636 if (!sid_split_rid(&domsid, NULL)) {
3637 ads_msgfree(ads, res);
3638 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3641 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3642 ads_msgfree(ads, res);
3643 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3647 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3649 if (tmp_num_sids == 0 || !tmp_sids) {
3650 ads_msgfree(ads, res);
3651 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3655 *num_sids = tmp_num_sids;
3663 *user_sid = tmp_user_sid;
3666 if (primary_group_sid) {
3667 *primary_group_sid = tmp_primary_group_sid;
3670 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3672 ads_msgfree(ads, res);
3673 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3677 * Find a sAMAccoutName in LDAP
3678 * @param ads connection to ads server
3679 * @param mem_ctx TALLOC_CTX for allocating sid array
3680 * @param samaccountname to search
3681 * @param uac_ret uint32 pointer userAccountControl attribute value
3682 * @param dn_ret pointer to dn
3683 * @return status of token query
3685 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3686 TALLOC_CTX *mem_ctx,
3687 const char *samaccountname,
3689 const char **dn_ret)
3692 const char *attrs[] = { "userAccountControl", NULL };
3694 LDAPMessage *res = NULL;
3698 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3700 if (filter == NULL) {
3701 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3705 status = ads_do_search_all(ads, ads->config.bind_path,
3707 filter, attrs, &res);
3709 if (!ADS_ERR_OK(status)) {
3713 if (ads_count_replies(ads, res) != 1) {
3714 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3718 dn = ads_get_dn(ads, talloc_tos(), res);
3720 status = ADS_ERROR(LDAP_NO_MEMORY);
3724 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3725 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3734 *dn_ret = talloc_strdup(mem_ctx, dn);
3736 status = ADS_ERROR(LDAP_NO_MEMORY);
3742 ads_msgfree(ads, res);
3748 * find our configuration path
3749 * @param ads connection to ads server
3750 * @param mem_ctx Pointer to talloc context
3751 * @param config_path Pointer to the config path
3752 * @return status of search
3754 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3755 TALLOC_CTX *mem_ctx,
3759 LDAPMessage *res = NULL;
3760 const char *config_context = NULL;
3761 const char *attrs[] = { "configurationNamingContext", NULL };
3763 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3764 "(objectclass=*)", attrs, &res);
3765 if (!ADS_ERR_OK(status)) {
3769 config_context = ads_pull_string(ads, mem_ctx, res,
3770 "configurationNamingContext");
3771 ads_msgfree(ads, res);
3772 if (!config_context) {
3773 return ADS_ERROR(LDAP_NO_MEMORY);
3777 *config_path = talloc_strdup(mem_ctx, config_context);
3778 if (!*config_path) {
3779 return ADS_ERROR(LDAP_NO_MEMORY);
3783 return ADS_ERROR(LDAP_SUCCESS);
3787 * find the displayName of an extended right
3788 * @param ads connection to ads server
3789 * @param config_path The config path
3790 * @param mem_ctx Pointer to talloc context
3791 * @param GUID struct of the rightsGUID
3792 * @return status of search
3794 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3795 const char *config_path,
3796 TALLOC_CTX *mem_ctx,
3797 const struct GUID *rights_guid)
3800 LDAPMessage *res = NULL;
3802 const char *attrs[] = { "displayName", NULL };
3803 const char *result = NULL;
3806 if (!ads || !mem_ctx || !rights_guid) {
3810 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3811 GUID_string(mem_ctx, rights_guid));
3816 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3821 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3823 if (!ADS_ERR_OK(rc)) {
3827 if (ads_count_replies(ads, res) != 1) {
3831 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3834 ads_msgfree(ads, res);
3839 * verify or build and verify an account ou
3840 * @param mem_ctx Pointer to talloc context
3841 * @param ads connection to ads server
3843 * @return status of search
3846 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3848 const char **account_ou)
3854 exploded_dn = ldap_explode_dn(*account_ou, 0);
3856 ldap_value_free(exploded_dn);
3860 ou_string = ads_ou_string(ads, *account_ou);
3862 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3865 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3866 ads->config.bind_path);
3867 SAFE_FREE(ou_string);
3870 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3873 exploded_dn = ldap_explode_dn(name, 0);
3875 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3877 ldap_value_free(exploded_dn);