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 "../librpc/gen_ndr/netlogon.h"
33 #include "lib/param/loadparm.h"
39 * @brief basic ldap client-side routines for ads server communications
41 * The routines contained here should do the necessary ldap calls for
44 * Important note: attribute names passed into ads_ routines must
45 * already be in UTF-8 format. We do not convert them because in almost
46 * all cases, they are just ascii (which is represented with the same
47 * codepoints in UTF-8). This may have to change at some point
51 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
53 static SIG_ATOMIC_T gotalarm;
55 /***************************************************************
56 Signal function to tell us we timed out.
57 ****************************************************************/
59 static void gotalarm_sig(int signum)
64 LDAP *ldap_open_with_timeout(const char *server,
65 struct sockaddr_storage *ss,
66 int port, unsigned int to)
72 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
73 "%u seconds\n", server, port, to));
78 CatchSignal(SIGALRM, gotalarm_sig);
80 /* End setup timeout. */
83 if ( strchr_m(server, ':') ) {
85 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
88 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
94 #ifdef HAVE_LDAP_INITIALIZE
95 ldap_err = ldap_initialize(&ldp, uri);
97 ldp = ldap_open(server, port);
99 ldap_err = LDAP_SUCCESS;
101 ldap_err = LDAP_OTHER;
104 if (ldap_err != LDAP_SUCCESS) {
105 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
106 uri, ldap_err2string(ldap_err)));
108 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
112 /* Teardown timeout. */
114 CatchSignal(SIGALRM, SIG_IGN);
120 static int ldap_search_with_timeout(LDAP *ld,
121 LDAP_CONST char *base,
123 LDAP_CONST char *filter,
126 LDAPControl **sctrls,
127 LDAPControl **cctrls,
131 int to = lp_ldap_timeout();
132 struct timeval timeout;
133 struct timeval *timeout_ptr = NULL;
136 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
142 timeout_ptr = &timeout;
144 /* Setup alarm timeout. */
145 CatchSignal(SIGALRM, gotalarm_sig);
146 /* Make the alarm time one second beyond
147 the timout we're setting for the
148 remote search timeout, to allow that
149 to fire in preference. */
151 /* End setup timeout. */
155 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
156 attrsonly, sctrls, cctrls, timeout_ptr,
160 /* Teardown alarm timeout. */
161 CatchSignal(SIGALRM, SIG_IGN);
166 return LDAP_TIMELIMIT_EXCEEDED;
169 * A bug in OpenLDAP means ldap_search_ext_s can return
170 * LDAP_SUCCESS but with a NULL res pointer. Cope with
171 * this. See bug #6279 for details. JRA.
175 return LDAP_TIMELIMIT_EXCEEDED;
181 /**********************************************
182 Do client and server sitename match ?
183 **********************************************/
185 bool ads_sitename_match(ADS_STRUCT *ads)
187 if (ads->config.server_site_name == NULL &&
188 ads->config.client_site_name == NULL ) {
189 DEBUG(10,("ads_sitename_match: both null\n"));
192 if (ads->config.server_site_name &&
193 ads->config.client_site_name &&
194 strequal(ads->config.server_site_name,
195 ads->config.client_site_name)) {
196 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
199 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
200 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
201 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
205 /**********************************************
206 Is this the closest DC ?
207 **********************************************/
209 bool ads_closest_dc(ADS_STRUCT *ads)
211 if (ads->config.flags & NBT_SERVER_CLOSEST) {
212 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
216 /* not sure if this can ever happen */
217 if (ads_sitename_match(ads)) {
218 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
222 if (ads->config.client_site_name == NULL) {
223 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
227 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
228 ads->config.ldap_server_name));
235 try a connection to a given ldap server, returning True and setting the servers IP
236 in the ads struct if successful
238 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
239 struct sockaddr_storage *ss)
241 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
242 TALLOC_CTX *frame = talloc_stackframe();
244 char addr[INET6_ADDRSTRLEN];
251 print_sockaddr(addr, sizeof(addr), ss);
253 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
254 addr, ads->server.realm));
256 ZERO_STRUCT( cldap_reply );
258 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
259 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
264 /* Check the CLDAP reply flags */
266 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
267 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
273 /* Fill in the ads->config values */
275 SAFE_FREE(ads->config.realm);
276 SAFE_FREE(ads->config.bind_path);
277 SAFE_FREE(ads->config.ldap_server_name);
278 SAFE_FREE(ads->config.server_site_name);
279 SAFE_FREE(ads->config.client_site_name);
280 SAFE_FREE(ads->server.workgroup);
282 if (!check_cldap_reply_required_flags(cldap_reply.server_type,
283 ads->config.flags)) {
288 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
289 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
290 if (!strupper_m(ads->config.realm)) {
295 ads->config.bind_path = ads_build_dn(ads->config.realm);
296 if (*cldap_reply.server_site) {
297 ads->config.server_site_name =
298 SMB_STRDUP(cldap_reply.server_site);
300 if (*cldap_reply.client_site) {
301 ads->config.client_site_name =
302 SMB_STRDUP(cldap_reply.client_site);
304 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
306 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
309 /* Store our site name. */
310 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
311 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
313 /* Leave this until last so that the flags are not clobbered */
314 ads->config.flags = cldap_reply.server_type;
324 /**********************************************************************
325 send a cldap ping to list of servers, one at a time, until one of
326 them answers it's an ldap server. Record success in the ADS_STRUCT.
327 Take note of and update negative connection cache.
328 **********************************************************************/
330 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,const char *domain,
331 struct ip_service *ip_list, int count)
336 for (i = 0; i < count; i++) {
337 char server[INET6_ADDRSTRLEN];
339 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
341 if (!NT_STATUS_IS_OK(
342 check_negative_conn_cache(domain, server)))
345 /* Returns ok only if it matches the correct server type */
346 ok = ads_try_connect(ads, false, &ip_list[i].ss);
352 /* keep track of failures */
353 add_failed_connection_entry(domain, server,
354 NT_STATUS_UNSUCCESSFUL);
357 return NT_STATUS_NO_LOGON_SERVERS;
360 /***************************************************************************
361 resolve a name and perform an "ldap ping" using NetBIOS and related methods
362 ****************************************************************************/
364 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
365 const char *domain, const char *realm)
368 struct ip_service *ip_list;
369 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
371 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
374 status = get_sorted_dc_list(domain, NULL, &ip_list, &count,
376 if (!NT_STATUS_IS_OK(status)) {
380 /* remove servers which are known to be dead based on
381 the corresponding DNS method */
383 for (i = 0; i < count; ++i) {
384 char server[INET6_ADDRSTRLEN];
386 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
389 check_negative_conn_cache(realm, server))) {
390 /* Ensure we add the workgroup name for this
391 IP address as negative too. */
392 add_failed_connection_entry(
394 NT_STATUS_UNSUCCESSFUL);
399 status = cldap_ping_list(ads, domain, ip_list, count);
407 /**********************************************************************
408 resolve a name and perform an "ldap ping" using DNS
409 **********************************************************************/
411 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
415 struct ip_service *ip_list;
416 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
418 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
421 status = get_sorted_dc_list(realm, sitename, &ip_list, &count,
423 if (!NT_STATUS_IS_OK(status)) {
427 status = cldap_ping_list(ads, realm, ip_list, count);
434 /**********************************************************************
435 Try to find an AD dc using our internal name resolution routines
436 Try the realm first and then then workgroup name if netbios is not
438 **********************************************************************/
440 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
442 const char *c_domain = "";
444 bool use_own_domain = False;
445 char *sitename = NULL;
446 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
449 /* if the realm and workgroup are both empty, assume they are ours */
452 c_realm = ads->server.realm;
458 /* special case where no realm and no workgroup means our own */
459 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
460 use_own_domain = True;
461 c_realm = lp_realm();
465 if (!lp_disable_netbios()) {
466 if (use_own_domain) {
467 c_domain = lp_workgroup();
469 c_domain = ads->server.workgroup;
470 if (!*c_realm && (!c_domain || !*c_domain)) {
471 c_domain = lp_workgroup();
480 if (!*c_realm && !*c_domain) {
481 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
483 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
487 * In case of LDAP we use get_dc_name() as that
488 * creates the custom krb5.conf file
490 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
492 struct sockaddr_storage ip_out;
494 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
495 " and falling back to domain '%s'\n",
498 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
501 * we call ads_try_connect() to fill in the
502 * ads->config details
504 ok = ads_try_connect(ads, false, &ip_out);
510 return NT_STATUS_NO_LOGON_SERVERS;
514 sitename = sitename_fetch(talloc_tos(), c_realm);
515 status = resolve_and_ping_dns(ads, sitename, c_realm);
517 if (NT_STATUS_IS_OK(status)) {
518 TALLOC_FREE(sitename);
522 /* In case we failed to contact one of our closest DC on our
524 * need to try to find another DC, retry with a site-less SRV
529 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
530 "our site (%s), Trying to find another DC "
531 "for realm '%s' (domain '%s')\n",
532 sitename, c_realm, c_domain));
533 namecache_delete(c_realm, 0x1C);
535 resolve_and_ping_dns(ads, NULL, c_realm);
537 if (NT_STATUS_IS_OK(status)) {
538 TALLOC_FREE(sitename);
543 TALLOC_FREE(sitename);
546 /* try netbios as fallback - if permitted,
547 or if configuration specifically requests it */
550 DEBUG(3, ("ads_find_dc: falling back to netbios "
551 "name resolution for domain '%s' (realm '%s')\n",
555 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
556 if (NT_STATUS_IS_OK(status)) {
561 DEBUG(1, ("ads_find_dc: "
562 "name resolution for realm '%s' (domain '%s') failed: %s\n",
563 c_realm, c_domain, nt_errstr(status)));
567 * Connect to the LDAP server
568 * @param ads Pointer to an existing ADS_STRUCT
569 * @return status of connection
571 ADS_STATUS ads_connect(ADS_STRUCT *ads)
573 int version = LDAP_VERSION3;
576 char addr[INET6_ADDRSTRLEN];
578 ZERO_STRUCT(ads->ldap);
579 ZERO_STRUCT(ads->ldap_wrap_data);
580 ads->ldap.last_attempt = time_mono(NULL);
581 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
583 /* try with a user specified server */
585 if (DEBUGLEVEL >= 11) {
586 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
587 DEBUG(11,("ads_connect: entering\n"));
588 DEBUGADD(11,("%s\n", s));
592 if (ads->server.ldap_server) {
594 struct sockaddr_storage ss;
596 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
598 DEBUG(5,("ads_connect: unable to resolve name %s\n",
599 ads->server.ldap_server));
600 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
603 ok = ads_try_connect(ads, ads->server.gc, &ss);
608 /* The choice of which GC use is handled one level up in
609 ads_connect_gc(). If we continue on from here with
610 ads_find_dc() we will get GC searches on port 389 which
611 doesn't work. --jerry */
613 if (ads->server.gc == true) {
614 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
617 if (ads->server.no_fallback) {
618 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
623 ntstatus = ads_find_dc(ads);
624 if (NT_STATUS_IS_OK(ntstatus)) {
628 status = ADS_ERROR_NT(ntstatus);
633 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
634 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
636 if (!ads->auth.user_name) {
637 /* Must use the userPrincipalName value here or sAMAccountName
638 and not servicePrincipalName; found by Guenther Deschner */
640 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
641 DEBUG(0,("ads_connect: asprintf fail.\n"));
642 ads->auth.user_name = NULL;
646 if (!ads->auth.realm) {
647 ads->auth.realm = SMB_STRDUP(ads->config.realm);
650 if (!ads->auth.kdc_server) {
651 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
652 ads->auth.kdc_server = SMB_STRDUP(addr);
655 /* If the caller() requested no LDAP bind, then we are done */
657 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
658 status = ADS_SUCCESS;
662 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
663 if (!ads->ldap_wrap_data.mem_ctx) {
664 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
668 /* Otherwise setup the TCP LDAP session */
670 ads->ldap.ld = ldap_open_with_timeout(addr,
672 ads->ldap.port, lp_ldap_timeout());
673 if (ads->ldap.ld == NULL) {
674 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
677 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
679 /* cache the successful connection for workgroup and realm */
680 if (ads_closest_dc(ads)) {
681 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
682 saf_store( ads->server.realm, ads->config.ldap_server_name);
685 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
687 if ( lp_ldap_ssl_ads() ) {
688 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
689 if (!ADS_ERR_OK(status)) {
694 /* fill in the current time and offsets */
696 status = ads_current_time( ads );
697 if ( !ADS_ERR_OK(status) ) {
701 /* Now do the bind */
703 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
704 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
708 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
709 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
713 status = ads_sasl_bind(ads);
716 if (DEBUGLEVEL >= 11) {
717 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
718 DEBUG(11,("ads_connect: leaving with: %s\n",
719 ads_errstr(status)));
720 DEBUGADD(11,("%s\n", s));
728 * Connect to the LDAP server using given credentials
729 * @param ads Pointer to an existing ADS_STRUCT
730 * @return status of connection
732 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
734 ads->auth.flags |= ADS_AUTH_USER_CREDS;
736 return ads_connect(ads);
740 * Disconnect the LDAP server
741 * @param ads Pointer to an existing ADS_STRUCT
743 void ads_disconnect(ADS_STRUCT *ads)
746 ldap_unbind(ads->ldap.ld);
749 if (ads->ldap_wrap_data.wrap_ops &&
750 ads->ldap_wrap_data.wrap_ops->disconnect) {
751 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
753 if (ads->ldap_wrap_data.mem_ctx) {
754 talloc_free(ads->ldap_wrap_data.mem_ctx);
756 ZERO_STRUCT(ads->ldap);
757 ZERO_STRUCT(ads->ldap_wrap_data);
761 Duplicate a struct berval into talloc'ed memory
763 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
765 struct berval *value;
767 if (!in_val) return NULL;
769 value = talloc_zero(ctx, struct berval);
772 if (in_val->bv_len == 0) return value;
774 value->bv_len = in_val->bv_len;
775 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
781 Make a values list out of an array of (struct berval *)
783 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
784 const struct berval **in_vals)
786 struct berval **values;
789 if (!in_vals) return NULL;
790 for (i=0; in_vals[i]; i++)
792 values = talloc_zero_array(ctx, struct berval *, i+1);
793 if (!values) return NULL;
795 for (i=0; in_vals[i]; i++) {
796 values[i] = dup_berval(ctx, in_vals[i]);
802 UTF8-encode a values list out of an array of (char *)
804 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
810 if (!in_vals) return NULL;
811 for (i=0; in_vals[i]; i++)
813 values = talloc_zero_array(ctx, char *, i+1);
814 if (!values) return NULL;
816 for (i=0; in_vals[i]; i++) {
817 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
826 Pull a (char *) array out of a UTF8-encoded values list
828 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
832 size_t converted_size;
834 if (!in_vals) return NULL;
835 for (i=0; in_vals[i]; i++)
837 values = talloc_zero_array(ctx, char *, i+1);
838 if (!values) return NULL;
840 for (i=0; in_vals[i]; i++) {
841 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
843 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
844 "%s", strerror(errno)));
851 * Do a search with paged results. cookie must be null on the first
852 * call, and then returned on each subsequent call. It will be null
853 * again when the entire search is complete
854 * @param ads connection to ads server
855 * @param bind_path Base dn for the search
856 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
857 * @param expr Search expression - specified in local charset
858 * @param attrs Attributes to retrieve - specified in utf8 or ascii
859 * @param res ** which will contain results - free res* with ads_msgfree()
860 * @param count Number of entries retrieved on this page
861 * @param cookie The paged results cookie to be returned on subsequent calls
862 * @return status of search
864 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
865 const char *bind_path,
866 int scope, const char *expr,
867 const char **attrs, void *args,
869 int *count, struct berval **cookie)
872 char *utf8_expr, *utf8_path, **search_attrs = NULL;
873 size_t converted_size;
874 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
875 BerElement *cookie_be = NULL;
876 struct berval *cookie_bv= NULL;
877 BerElement *ext_be = NULL;
878 struct berval *ext_bv= NULL;
881 ads_control *external_control = (ads_control *) args;
885 if (!(ctx = talloc_init("ads_do_paged_search_args")))
886 return ADS_ERROR(LDAP_NO_MEMORY);
888 /* 0 means the conversion worked but the result was empty
889 so we only fail if it's -1. In any case, it always
890 at least nulls out the dest */
891 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
892 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
898 if (!attrs || !(*attrs))
901 /* This would be the utf8-encoded version...*/
902 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
903 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
909 /* Paged results only available on ldap v3 or later */
910 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
911 if (version < LDAP_VERSION3) {
912 rc = LDAP_NOT_SUPPORTED;
916 cookie_be = ber_alloc_t(LBER_USE_DER);
918 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
919 ber_bvfree(*cookie); /* don't need it from last time */
922 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
924 ber_flatten(cookie_be, &cookie_bv);
925 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
926 PagedResults.ldctl_iscritical = (char) 1;
927 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
928 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
930 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
931 NoReferrals.ldctl_iscritical = (char) 0;
932 NoReferrals.ldctl_value.bv_len = 0;
933 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
935 if (external_control &&
936 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
937 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
939 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
940 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
942 /* win2k does not accept a ldctl_value beeing passed in */
944 if (external_control->val != 0) {
946 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
951 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
955 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
960 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
961 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
964 ExternalCtrl.ldctl_value.bv_len = 0;
965 ExternalCtrl.ldctl_value.bv_val = NULL;
968 controls[0] = &NoReferrals;
969 controls[1] = &PagedResults;
970 controls[2] = &ExternalCtrl;
974 controls[0] = &NoReferrals;
975 controls[1] = &PagedResults;
979 /* we need to disable referrals as the openldap libs don't
980 handle them and paged results at the same time. Using them
981 together results in the result record containing the server
982 page control being removed from the result list (tridge/jmcd)
984 leaving this in despite the control that says don't generate
985 referrals, in case the server doesn't support it (jmcd)
987 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
989 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
990 search_attrs, 0, controls,
992 (LDAPMessage **)res);
994 ber_free(cookie_be, 1);
995 ber_bvfree(cookie_bv);
998 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
999 ldap_err2string(rc)));
1000 if (rc == LDAP_OTHER) {
1004 ret = ldap_parse_result(ads->ldap.ld,
1012 if (ret == LDAP_SUCCESS) {
1013 DEBUG(3, ("ldap_search_with_timeout(%s) "
1014 "error: %s\n", expr, ldap_errmsg));
1015 ldap_memfree(ldap_errmsg);
1021 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1022 NULL, &rcontrols, 0);
1028 for (i=0; rcontrols[i]; i++) {
1029 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1030 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1031 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1033 /* the berval is the cookie, but must be freed when
1035 if (cookie_bv->bv_len) /* still more to do */
1036 *cookie=ber_bvdup(cookie_bv);
1039 ber_bvfree(cookie_bv);
1040 ber_free(cookie_be, 1);
1044 ldap_controls_free(rcontrols);
1047 talloc_destroy(ctx);
1050 ber_free(ext_be, 1);
1057 if (rc != LDAP_SUCCESS && *res != NULL) {
1058 ads_msgfree(ads, *res);
1062 /* if/when we decide to utf8-encode attrs, take out this next line */
1063 TALLOC_FREE(search_attrs);
1065 return ADS_ERROR(rc);
1068 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1069 int scope, const char *expr,
1070 const char **attrs, LDAPMessage **res,
1071 int *count, struct berval **cookie)
1073 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1078 * Get all results for a search. This uses ads_do_paged_search() to return
1079 * all entries in a large search.
1080 * @param ads connection to ads server
1081 * @param bind_path Base dn for the search
1082 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1083 * @param expr Search expression
1084 * @param attrs Attributes to retrieve
1085 * @param res ** which will contain results - free res* with ads_msgfree()
1086 * @return status of search
1088 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1089 int scope, const char *expr,
1090 const char **attrs, void *args,
1093 struct berval *cookie = NULL;
1098 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1101 if (!ADS_ERR_OK(status))
1104 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1106 LDAPMessage *res2 = NULL;
1107 LDAPMessage *msg, *next;
1109 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1110 attrs, args, &res2, &count, &cookie);
1111 if (!ADS_ERR_OK(status)) {
1115 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1116 that this works on all ldap libs, but I have only tested with openldap */
1117 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1118 next = ads_next_message(ads, msg);
1119 ldap_add_result_entry((LDAPMessage **)res, msg);
1121 /* note that we do not free res2, as the memory is now
1122 part of the main returned list */
1125 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1126 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1132 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1133 int scope, const char *expr,
1134 const char **attrs, LDAPMessage **res)
1136 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1139 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1140 int scope, const char *expr,
1141 const char **attrs, uint32_t sd_flags,
1146 args.control = ADS_SD_FLAGS_OID;
1147 args.val = sd_flags;
1148 args.critical = True;
1150 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1155 * Run a function on all results for a search. Uses ads_do_paged_search() and
1156 * runs the function as each page is returned, using ads_process_results()
1157 * @param ads connection to ads server
1158 * @param bind_path Base dn for the search
1159 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1160 * @param expr Search expression - specified in local charset
1161 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1162 * @param fn Function which takes attr name, values list, and data_area
1163 * @param data_area Pointer which is passed to function on each call
1164 * @return status of search
1166 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1167 int scope, const char *expr, const char **attrs,
1168 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1171 struct berval *cookie = NULL;
1176 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1179 if (!ADS_ERR_OK(status)) return status;
1181 ads_process_results(ads, res, fn, data_area);
1182 ads_msgfree(ads, res);
1185 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1186 &res, &count, &cookie);
1188 if (!ADS_ERR_OK(status)) break;
1190 ads_process_results(ads, res, fn, data_area);
1191 ads_msgfree(ads, res);
1198 * Do a search with a timeout.
1199 * @param ads connection to ads server
1200 * @param bind_path Base dn for the search
1201 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1202 * @param expr Search expression
1203 * @param attrs Attributes to retrieve
1204 * @param res ** which will contain results - free res* with ads_msgfree()
1205 * @return status of search
1207 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1209 const char **attrs, LDAPMessage **res)
1212 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1213 size_t converted_size;
1217 if (!(ctx = talloc_init("ads_do_search"))) {
1218 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1219 return ADS_ERROR(LDAP_NO_MEMORY);
1222 /* 0 means the conversion worked but the result was empty
1223 so we only fail if it's negative. In any case, it always
1224 at least nulls out the dest */
1225 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1226 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1228 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1229 rc = LDAP_NO_MEMORY;
1233 if (!attrs || !(*attrs))
1234 search_attrs = NULL;
1236 /* This would be the utf8-encoded version...*/
1237 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1238 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1240 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1241 rc = LDAP_NO_MEMORY;
1246 /* see the note in ads_do_paged_search - we *must* disable referrals */
1247 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1249 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1250 search_attrs, 0, NULL, NULL,
1252 (LDAPMessage **)res);
1254 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1255 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1260 talloc_destroy(ctx);
1261 /* if/when we decide to utf8-encode attrs, take out this next line */
1262 TALLOC_FREE(search_attrs);
1263 return ADS_ERROR(rc);
1266 * Do a general ADS search
1267 * @param ads connection to ads server
1268 * @param res ** which will contain results - free res* with ads_msgfree()
1269 * @param expr Search expression
1270 * @param attrs Attributes to retrieve
1271 * @return status of search
1273 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1274 const char *expr, const char **attrs)
1276 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1281 * Do a search on a specific DistinguishedName
1282 * @param ads connection to ads server
1283 * @param res ** which will contain results - free res* with ads_msgfree()
1284 * @param dn DistinguishName to search
1285 * @param attrs Attributes to retrieve
1286 * @return status of search
1288 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1289 const char *dn, const char **attrs)
1291 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1296 * Free up memory from a ads_search
1297 * @param ads connection to ads server
1298 * @param msg Search results to free
1300 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1307 * Get a dn from search results
1308 * @param ads connection to ads server
1309 * @param msg Search result
1312 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1314 char *utf8_dn, *unix_dn;
1315 size_t converted_size;
1317 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1320 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1324 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1325 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1329 ldap_memfree(utf8_dn);
1334 * Get the parent from a dn
1335 * @param dn the dn to return the parent from
1336 * @return parent dn string
1338 char *ads_parent_dn(const char *dn)
1346 p = strchr(dn, ',');
1356 * Find a machine account given a hostname
1357 * @param ads connection to ads server
1358 * @param res ** which will contain results - free res* with ads_msgfree()
1359 * @param host Hostname to search for
1360 * @return status of search
1362 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1363 const char *machine)
1367 const char *attrs[] = {"*", "msDS-SupportedEncryptionTypes", "nTSecurityDescriptor", NULL};
1371 /* the easiest way to find a machine account anywhere in the tree
1372 is to look for hostname$ */
1373 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1374 DEBUG(1, ("asprintf failed!\n"));
1375 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1378 status = ads_search(ads, res, expr, attrs);
1384 * Initialize a list of mods to be used in a modify request
1385 * @param ctx An initialized TALLOC_CTX
1386 * @return allocated ADS_MODLIST
1388 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1390 #define ADS_MODLIST_ALLOC_SIZE 10
1393 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1394 /* -1 is safety to make sure we don't go over the end.
1395 need to reset it to NULL before doing ldap modify */
1396 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1398 return (ADS_MODLIST)mods;
1403 add an attribute to the list, with values list already constructed
1405 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1406 int mod_op, const char *name,
1407 const void *_invals)
1410 LDAPMod **modlist = (LDAPMod **) *mods;
1411 struct berval **ber_values = NULL;
1412 char **char_values = NULL;
1415 mod_op = LDAP_MOD_DELETE;
1417 if (mod_op & LDAP_MOD_BVALUES) {
1418 const struct berval **b;
1419 b = discard_const_p(const struct berval *, _invals);
1420 ber_values = ads_dup_values(ctx, b);
1423 c = discard_const_p(const char *, _invals);
1424 char_values = ads_push_strvals(ctx, c);
1428 /* find the first empty slot */
1429 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1431 if (modlist[curmod] == (LDAPMod *) -1) {
1432 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1433 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1434 return ADS_ERROR(LDAP_NO_MEMORY);
1435 memset(&modlist[curmod], 0,
1436 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1437 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1438 *mods = (ADS_MODLIST)modlist;
1441 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1442 return ADS_ERROR(LDAP_NO_MEMORY);
1443 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1444 if (mod_op & LDAP_MOD_BVALUES) {
1445 modlist[curmod]->mod_bvalues = ber_values;
1446 } else if (mod_op & LDAP_MOD_DELETE) {
1447 modlist[curmod]->mod_values = NULL;
1449 modlist[curmod]->mod_values = char_values;
1452 modlist[curmod]->mod_op = mod_op;
1453 return ADS_ERROR(LDAP_SUCCESS);
1457 * Add a single string value to a mod list
1458 * @param ctx An initialized TALLOC_CTX
1459 * @param mods An initialized ADS_MODLIST
1460 * @param name The attribute name to add
1461 * @param val The value to add - NULL means DELETE
1462 * @return ADS STATUS indicating success of add
1464 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1465 const char *name, const char *val)
1467 const char *values[2];
1473 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1474 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1478 * Add an array of string values to a mod list
1479 * @param ctx An initialized TALLOC_CTX
1480 * @param mods An initialized ADS_MODLIST
1481 * @param name The attribute name to add
1482 * @param vals The array of string values to add - NULL means DELETE
1483 * @return ADS STATUS indicating success of add
1485 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1486 const char *name, const char **vals)
1489 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1490 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1491 name, (const void **) vals);
1496 * Add a single ber-encoded value to a mod list
1497 * @param ctx An initialized TALLOC_CTX
1498 * @param mods An initialized ADS_MODLIST
1499 * @param name The attribute name to add
1500 * @param val The value to add - NULL means DELETE
1501 * @return ADS STATUS indicating success of add
1503 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1504 const char *name, const struct berval *val)
1506 const struct berval *values[2];
1511 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1512 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1513 name, (const void **) values);
1517 static void ads_print_error(int ret, LDAP *ld)
1520 char *ld_error = NULL;
1521 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1522 DEBUG(10,("AD LDAP failure %d (%s):\n%s\n", ret,
1523 ldap_err2string(ret), ld_error));
1524 SAFE_FREE(ld_error);
1529 * Perform an ldap modify
1530 * @param ads connection to ads server
1531 * @param mod_dn DistinguishedName to modify
1532 * @param mods list of modifications to perform
1533 * @return status of modify
1535 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1538 char *utf8_dn = NULL;
1539 size_t converted_size;
1541 this control is needed to modify that contains a currently
1542 non-existent attribute (but allowable for the object) to run
1544 LDAPControl PermitModify = {
1545 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1548 LDAPControl *controls[2];
1550 controls[0] = &PermitModify;
1553 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1554 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1557 /* find the end of the list, marked by NULL or -1 */
1558 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1559 /* make sure the end of the list is NULL */
1561 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1562 (LDAPMod **) mods, controls, NULL);
1563 ads_print_error(ret, ads->ldap.ld);
1564 TALLOC_FREE(utf8_dn);
1565 return ADS_ERROR(ret);
1569 * Perform an ldap add
1570 * @param ads connection to ads server
1571 * @param new_dn DistinguishedName to add
1572 * @param mods list of attributes and values for DN
1573 * @return status of add
1575 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1578 char *utf8_dn = NULL;
1579 size_t converted_size;
1581 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1582 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1583 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1586 /* find the end of the list, marked by NULL or -1 */
1587 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1588 /* make sure the end of the list is NULL */
1591 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1592 ads_print_error(ret, ads->ldap.ld);
1593 TALLOC_FREE(utf8_dn);
1594 return ADS_ERROR(ret);
1598 * Delete a DistinguishedName
1599 * @param ads connection to ads server
1600 * @param new_dn DistinguishedName to delete
1601 * @return status of delete
1603 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1606 char *utf8_dn = NULL;
1607 size_t converted_size;
1608 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1609 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1610 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1613 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1614 ads_print_error(ret, ads->ldap.ld);
1615 TALLOC_FREE(utf8_dn);
1616 return ADS_ERROR(ret);
1620 * Build an org unit string
1621 * if org unit is Computers or blank then assume a container, otherwise
1622 * assume a / separated list of organisational units.
1623 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1624 * @param ads connection to ads server
1625 * @param org_unit Organizational unit
1626 * @return org unit string - caller must free
1628 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1632 if (!org_unit || !*org_unit) {
1634 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1636 /* samba4 might not yet respond to a wellknownobject-query */
1637 return ret ? ret : SMB_STRDUP("cn=Computers");
1640 if (strequal(org_unit, "Computers")) {
1641 return SMB_STRDUP("cn=Computers");
1644 /* jmcd: removed "\\" from the separation chars, because it is
1645 needed as an escape for chars like '#' which are valid in an
1647 return ads_build_path(org_unit, "/", "ou=", 1);
1651 * Get a org unit string for a well-known GUID
1652 * @param ads connection to ads server
1653 * @param wknguid Well known GUID
1654 * @return org unit string - caller must free
1656 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1659 LDAPMessage *res = NULL;
1660 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1661 **bind_dn_exp = NULL;
1662 const char *attrs[] = {"distinguishedName", NULL};
1663 int new_ln, wkn_ln, bind_ln, i;
1665 if (wknguid == NULL) {
1669 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1670 DEBUG(1, ("asprintf failed!\n"));
1674 status = ads_search_dn(ads, &res, base, attrs);
1675 if (!ADS_ERR_OK(status)) {
1676 DEBUG(1,("Failed while searching for: %s\n", base));
1680 if (ads_count_replies(ads, res) != 1) {
1684 /* substitute the bind-path from the well-known-guid-search result */
1685 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1690 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1695 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1700 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1702 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1705 new_ln = wkn_ln - bind_ln;
1707 ret = SMB_STRDUP(wkn_dn_exp[0]);
1712 for (i=1; i < new_ln; i++) {
1715 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1721 ret = SMB_STRDUP(s);
1730 ads_msgfree(ads, res);
1731 TALLOC_FREE(wkn_dn);
1733 ldap_value_free(wkn_dn_exp);
1736 ldap_value_free(bind_dn_exp);
1743 * Adds (appends) an item to an attribute array, rather then
1744 * replacing the whole list
1745 * @param ctx An initialized TALLOC_CTX
1746 * @param mods An initialized ADS_MODLIST
1747 * @param name name of the ldap attribute to append to
1748 * @param vals an array of values to add
1749 * @return status of addition
1752 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1753 const char *name, const char **vals)
1755 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1756 (const void *) vals);
1760 * Determines the an account's current KVNO via an LDAP lookup
1761 * @param ads An initialized ADS_STRUCT
1762 * @param account_name the NT samaccountname.
1763 * @return the kvno for the account, or -1 in case of a failure.
1766 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1768 LDAPMessage *res = NULL;
1769 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1771 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1772 char *dn_string = NULL;
1773 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1775 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1776 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1779 ret = ads_search(ads, &res, filter, attrs);
1781 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1782 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1783 ads_msgfree(ads, res);
1787 dn_string = ads_get_dn(ads, talloc_tos(), res);
1789 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1790 ads_msgfree(ads, res);
1793 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1794 TALLOC_FREE(dn_string);
1796 /* ---------------------------------------------------------
1797 * 0 is returned as a default KVNO from this point on...
1798 * This is done because Windows 2000 does not support key
1799 * version numbers. Chances are that a failure in the next
1800 * step is simply due to Windows 2000 being used for a
1801 * domain controller. */
1804 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1805 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1806 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1807 ads_msgfree(ads, res);
1812 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1813 ads_msgfree(ads, res);
1818 * Determines the computer account's current KVNO via an LDAP lookup
1819 * @param ads An initialized ADS_STRUCT
1820 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1821 * @return the kvno for the computer account, or -1 in case of a failure.
1824 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1826 char *computer_account = NULL;
1829 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1833 kvno = ads_get_kvno(ads, computer_account);
1834 free(computer_account);
1840 * This clears out all registered spn's for a given hostname
1841 * @param ads An initilaized ADS_STRUCT
1842 * @param machine_name the NetBIOS name of the computer.
1843 * @return 0 upon success, non-zero otherwise.
1846 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1849 LDAPMessage *res = NULL;
1851 const char *servicePrincipalName[1] = {NULL};
1852 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1853 char *dn_string = NULL;
1855 ret = ads_find_machine_acct(ads, &res, machine_name);
1856 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1857 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1858 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1859 ads_msgfree(ads, res);
1860 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1863 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1864 ctx = talloc_init("ads_clear_service_principal_names");
1866 ads_msgfree(ads, res);
1867 return ADS_ERROR(LDAP_NO_MEMORY);
1870 if (!(mods = ads_init_mods(ctx))) {
1871 talloc_destroy(ctx);
1872 ads_msgfree(ads, res);
1873 return ADS_ERROR(LDAP_NO_MEMORY);
1875 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1876 if (!ADS_ERR_OK(ret)) {
1877 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1878 ads_msgfree(ads, res);
1879 talloc_destroy(ctx);
1882 dn_string = ads_get_dn(ads, talloc_tos(), res);
1884 talloc_destroy(ctx);
1885 ads_msgfree(ads, res);
1886 return ADS_ERROR(LDAP_NO_MEMORY);
1888 ret = ads_gen_mod(ads, dn_string, mods);
1889 TALLOC_FREE(dn_string);
1890 if (!ADS_ERR_OK(ret)) {
1891 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1893 ads_msgfree(ads, res);
1894 talloc_destroy(ctx);
1898 ads_msgfree(ads, res);
1899 talloc_destroy(ctx);
1904 * @brief Search for an element in a string array.
1906 * @param[in] el_array The string array to search.
1908 * @param[in] num_el The number of elements in the string array.
1910 * @param[in] el The string to search.
1912 * @return True if found, false if not.
1914 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1918 if (el_array == NULL || num_el == 0 || el == NULL) {
1922 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1925 cmp = strcasecmp_m(el_array[i], el);
1935 * @brief This gets the service principal names of an existing computer account.
1937 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1939 * @param[in] ads The ADS context to use.
1941 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1942 * identify the computer account.
1944 * @param[in] spn_array A pointer to store the array for SPNs.
1946 * @param[in] num_spns The number of principals stored in the array.
1948 * @return 0 on success, or a ADS error if a failure occurred.
1950 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1952 const char *machine_name,
1957 LDAPMessage *res = NULL;
1960 status = ads_find_machine_acct(ads,
1963 if (!ADS_ERR_OK(status)) {
1964 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1969 count = ads_count_replies(ads, res);
1971 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1975 *spn_array = ads_pull_strings(ads,
1978 "servicePrincipalName",
1980 if (*spn_array == NULL) {
1981 DEBUG(1, ("Host account for %s does not have service principal "
1984 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1989 ads_msgfree(ads, res);
1995 * This adds a service principal name to an existing computer account
1996 * (found by hostname) in AD.
1997 * @param ads An initialized ADS_STRUCT
1998 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1999 * @param spns An array or strings for the service principals to add,
2000 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2001 * @return 0 upon sucess, or non-zero if a failure occurs
2004 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2005 const char *machine_name,
2010 LDAPMessage *res = NULL;
2012 char *dn_string = NULL;
2013 const char **servicePrincipalName = spns;
2015 ret = ads_find_machine_acct(ads, &res, machine_name);
2016 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2017 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2019 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2020 ads_msgfree(ads, res);
2021 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2024 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2025 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2026 ads_msgfree(ads, res);
2027 return ADS_ERROR(LDAP_NO_MEMORY);
2030 DEBUG(5,("ads_add_service_principal_name: INFO: "
2031 "Adding %s to host %s\n",
2032 spns[0] ? "N/A" : spns[0], machine_name));
2035 DEBUG(5,("ads_add_service_principal_name: INFO: "
2036 "Adding %s to host %s\n",
2037 spns[1] ? "N/A" : spns[1], machine_name));
2039 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2040 ret = ADS_ERROR(LDAP_NO_MEMORY);
2044 ret = ads_add_strlist(ctx,
2046 "servicePrincipalName",
2047 servicePrincipalName);
2048 if (!ADS_ERR_OK(ret)) {
2049 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2053 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2054 ret = ADS_ERROR(LDAP_NO_MEMORY);
2058 ret = ads_gen_mod(ads, dn_string, mods);
2059 if (!ADS_ERR_OK(ret)) {
2060 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2066 ads_msgfree(ads, res);
2071 * adds a machine account to the ADS server
2072 * @param ads An intialized ADS_STRUCT
2073 * @param machine_name - the NetBIOS machine name of this account.
2074 * @param account_type A number indicating the type of account to create
2075 * @param org_unit The LDAP path in which to place this account
2076 * @return 0 upon success, or non-zero otherwise
2079 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2080 const char *machine_name,
2081 const char *org_unit,
2082 uint32_t etype_list)
2085 char *samAccountName, *controlstr;
2088 char *machine_escaped = NULL;
2090 const char *objectClass[] = {"top", "person", "organizationalPerson",
2091 "user", "computer", NULL};
2092 LDAPMessage *res = NULL;
2093 uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2094 UF_DONT_EXPIRE_PASSWD |\
2095 UF_ACCOUNTDISABLE );
2096 uint32_t func_level = 0;
2098 ret = ads_domain_func_level(ads, &func_level);
2099 if (!ADS_ERR_OK(ret)) {
2103 if (!(ctx = talloc_init("ads_add_machine_acct")))
2104 return ADS_ERROR(LDAP_NO_MEMORY);
2106 ret = ADS_ERROR(LDAP_NO_MEMORY);
2108 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2109 if (!machine_escaped) {
2113 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2114 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2116 if ( !new_dn || !samAccountName ) {
2120 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2124 if (!(mods = ads_init_mods(ctx))) {
2128 ads_mod_str(ctx, &mods, "cn", machine_name);
2129 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2130 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2131 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2133 if (func_level >= DS_DOMAIN_FUNCTION_2008) {
2134 const char *etype_list_str;
2136 etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list);
2137 if (etype_list_str == NULL) {
2140 ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes",
2144 ret = ads_gen_add(ads, new_dn, mods);
2147 SAFE_FREE(machine_escaped);
2148 ads_msgfree(ads, res);
2149 talloc_destroy(ctx);
2155 * move a machine account to another OU on the ADS server
2156 * @param ads - An intialized ADS_STRUCT
2157 * @param machine_name - the NetBIOS machine name of this account.
2158 * @param org_unit - The LDAP path in which to place this account
2159 * @param moved - whether we moved the machine account (optional)
2160 * @return 0 upon success, or non-zero otherwise
2163 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2164 const char *org_unit, bool *moved)
2168 LDAPMessage *res = NULL;
2169 char *filter = NULL;
2170 char *computer_dn = NULL;
2172 char *computer_rdn = NULL;
2173 bool need_move = False;
2175 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2176 rc = ADS_ERROR(LDAP_NO_MEMORY);
2180 /* Find pre-existing machine */
2181 rc = ads_search(ads, &res, filter, NULL);
2182 if (!ADS_ERR_OK(rc)) {
2186 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2188 rc = ADS_ERROR(LDAP_NO_MEMORY);
2192 parent_dn = ads_parent_dn(computer_dn);
2193 if (strequal(parent_dn, org_unit)) {
2199 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2200 rc = ADS_ERROR(LDAP_NO_MEMORY);
2204 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2205 org_unit, 1, NULL, NULL);
2206 rc = ADS_ERROR(ldap_status);
2209 ads_msgfree(ads, res);
2211 TALLOC_FREE(computer_dn);
2212 SAFE_FREE(computer_rdn);
2214 if (!ADS_ERR_OK(rc)) {
2226 dump a binary result from ldap
2228 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2231 for (i=0; values[i]; i++) {
2232 printf("%s: ", field);
2233 for (j=0; j<values[i]->bv_len; j++) {
2234 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2240 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2243 for (i=0; values[i]; i++) {
2245 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2248 status = GUID_from_ndr_blob(&in, &guid);
2249 if (NT_STATUS_IS_OK(status)) {
2250 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2252 printf("%s: INVALID GUID\n", field);
2258 dump a sid result from ldap
2260 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2263 for (i=0; values[i]; i++) {
2266 if (!sid_parse((const uint8_t *)values[i]->bv_val,
2267 values[i]->bv_len, &sid)) {
2270 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2275 dump ntSecurityDescriptor
2277 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2279 TALLOC_CTX *frame = talloc_stackframe();
2280 struct security_descriptor *psd;
2283 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2284 values[0]->bv_len, &psd);
2285 if (!NT_STATUS_IS_OK(status)) {
2286 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2287 nt_errstr(status)));
2293 ads_disp_sd(ads, talloc_tos(), psd);
2300 dump a string result from ldap
2302 static void dump_string(const char *field, char **values)
2305 for (i=0; values[i]; i++) {
2306 printf("%s: %s\n", field, values[i]);
2311 dump a field from LDAP on stdout
2315 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2320 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2322 {"objectGUID", False, dump_guid},
2323 {"netbootGUID", False, dump_guid},
2324 {"nTSecurityDescriptor", False, dump_sd},
2325 {"dnsRecord", False, dump_binary},
2326 {"objectSid", False, dump_sid},
2327 {"tokenGroups", False, dump_sid},
2328 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2329 {"tokengroupsGlobalandUniversal", False, dump_sid},
2330 {"mS-DS-CreatorSID", False, dump_sid},
2331 {"msExchMailboxGuid", False, dump_guid},
2336 if (!field) { /* must be end of an entry */
2341 for (i=0; handlers[i].name; i++) {
2342 if (strcasecmp_m(handlers[i].name, field) == 0) {
2343 if (!values) /* first time, indicate string or not */
2344 return handlers[i].string;
2345 handlers[i].handler(ads, field, (struct berval **) values);
2349 if (!handlers[i].name) {
2350 if (!values) /* first time, indicate string conversion */
2352 dump_string(field, (char **)values);
2358 * Dump a result from LDAP on stdout
2359 * used for debugging
2360 * @param ads connection to ads server
2361 * @param res Results to dump
2364 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2366 ads_process_results(ads, res, ads_dump_field, NULL);
2370 * Walk through results, calling a function for each entry found.
2371 * The function receives a field name, a berval * array of values,
2372 * and a data area passed through from the start. The function is
2373 * called once with null for field and values at the end of each
2375 * @param ads connection to ads server
2376 * @param res Results to process
2377 * @param fn Function for processing each result
2378 * @param data_area user-defined area to pass to function
2380 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2381 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2386 size_t converted_size;
2388 if (!(ctx = talloc_init("ads_process_results")))
2391 for (msg = ads_first_entry(ads, res); msg;
2392 msg = ads_next_entry(ads, msg)) {
2396 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2397 (LDAPMessage *)msg,&b);
2399 utf8_field=ldap_next_attribute(ads->ldap.ld,
2400 (LDAPMessage *)msg,b)) {
2401 struct berval **ber_vals;
2407 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2410 DEBUG(0,("ads_process_results: "
2411 "pull_utf8_talloc failed: %s",
2415 string = fn(ads, field, NULL, data_area);
2420 utf8_vals = ldap_get_values(ads->ldap.ld,
2421 (LDAPMessage *)msg, field);
2422 p = discard_const_p(const char *, utf8_vals);
2423 str_vals = ads_pull_strvals(ctx, p);
2424 fn(ads, field, (void **) str_vals, data_area);
2425 ldap_value_free(utf8_vals);
2427 ber_vals = ldap_get_values_len(ads->ldap.ld,
2428 (LDAPMessage *)msg, field);
2429 fn(ads, field, (void **) ber_vals, data_area);
2431 ldap_value_free_len(ber_vals);
2433 ldap_memfree(utf8_field);
2436 talloc_free_children(ctx);
2437 fn(ads, NULL, NULL, data_area); /* completed an entry */
2440 talloc_destroy(ctx);
2444 * count how many replies are in a LDAPMessage
2445 * @param ads connection to ads server
2446 * @param res Results to count
2447 * @return number of replies
2449 int ads_count_replies(ADS_STRUCT *ads, void *res)
2451 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2455 * pull the first entry from a ADS result
2456 * @param ads connection to ads server
2457 * @param res Results of search
2458 * @return first entry from result
2460 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2462 return ldap_first_entry(ads->ldap.ld, res);
2466 * pull the next entry from a ADS result
2467 * @param ads connection to ads server
2468 * @param res Results of search
2469 * @return next entry from result
2471 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2473 return ldap_next_entry(ads->ldap.ld, res);
2477 * pull the first message from a ADS result
2478 * @param ads connection to ads server
2479 * @param res Results of search
2480 * @return first message from result
2482 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2484 return ldap_first_message(ads->ldap.ld, res);
2488 * pull the next message from a ADS result
2489 * @param ads connection to ads server
2490 * @param res Results of search
2491 * @return next message from result
2493 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2495 return ldap_next_message(ads->ldap.ld, res);
2499 * pull a single string from a ADS result
2500 * @param ads connection to ads server
2501 * @param mem_ctx TALLOC_CTX to use for allocating result string
2502 * @param msg Results of search
2503 * @param field Attribute to retrieve
2504 * @return Result string in talloc context
2506 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2512 size_t converted_size;
2514 values = ldap_get_values(ads->ldap.ld, msg, field);
2518 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2523 ldap_value_free(values);
2528 * pull an array of strings from a ADS result
2529 * @param ads connection to ads server
2530 * @param mem_ctx TALLOC_CTX to use for allocating result string
2531 * @param msg Results of search
2532 * @param field Attribute to retrieve
2533 * @return Result strings in talloc context
2535 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2536 LDAPMessage *msg, const char *field,
2542 size_t converted_size;
2544 values = ldap_get_values(ads->ldap.ld, msg, field);
2548 *num_values = ldap_count_values(values);
2550 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2552 ldap_value_free(values);
2556 for (i=0;i<*num_values;i++) {
2557 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2560 ldap_value_free(values);
2566 ldap_value_free(values);
2571 * pull an array of strings from a ADS result
2572 * (handle large multivalue attributes with range retrieval)
2573 * @param ads connection to ads server
2574 * @param mem_ctx TALLOC_CTX to use for allocating result string
2575 * @param msg Results of search
2576 * @param field Attribute to retrieve
2577 * @param current_strings strings returned by a previous call to this function
2578 * @param next_attribute The next query should ask for this attribute
2579 * @param num_values How many values did we get this time?
2580 * @param more_values Are there more values to get?
2581 * @return Result strings in talloc context
2583 char **ads_pull_strings_range(ADS_STRUCT *ads,
2584 TALLOC_CTX *mem_ctx,
2585 LDAPMessage *msg, const char *field,
2586 char **current_strings,
2587 const char **next_attribute,
2588 size_t *num_strings,
2592 char *expected_range_attrib, *range_attr;
2593 BerElement *ptr = NULL;
2596 size_t num_new_strings;
2597 unsigned long int range_start;
2598 unsigned long int range_end;
2600 /* we might have been given the whole lot anyway */
2601 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2602 *more_strings = False;
2606 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2608 /* look for Range result */
2609 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2611 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2612 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2613 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2621 /* nothing here - this field is just empty */
2622 *more_strings = False;
2626 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2627 &range_start, &range_end) == 2) {
2628 *more_strings = True;
2630 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2631 &range_start) == 1) {
2632 *more_strings = False;
2634 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2636 ldap_memfree(range_attr);
2637 *more_strings = False;
2642 if ((*num_strings) != range_start) {
2643 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2644 " - aborting range retreival\n",
2645 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2646 ldap_memfree(range_attr);
2647 *more_strings = False;
2651 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2653 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2654 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2655 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2656 range_attr, (unsigned long int)range_end - range_start + 1,
2657 (unsigned long int)num_new_strings));
2658 ldap_memfree(range_attr);
2659 *more_strings = False;
2663 strings = talloc_realloc(mem_ctx, current_strings, char *,
2664 *num_strings + num_new_strings);
2666 if (strings == NULL) {
2667 ldap_memfree(range_attr);
2668 *more_strings = False;
2672 if (new_strings && num_new_strings) {
2673 memcpy(&strings[*num_strings], new_strings,
2674 sizeof(*new_strings) * num_new_strings);
2677 (*num_strings) += num_new_strings;
2679 if (*more_strings) {
2680 *next_attribute = talloc_asprintf(mem_ctx,
2685 if (!*next_attribute) {
2686 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2687 ldap_memfree(range_attr);
2688 *more_strings = False;
2693 ldap_memfree(range_attr);
2699 * pull a single uint32_t from a ADS result
2700 * @param ads connection to ads server
2701 * @param msg Results of search
2702 * @param field Attribute to retrieve
2703 * @param v Pointer to int to store result
2704 * @return boolean inidicating success
2706 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2711 values = ldap_get_values(ads->ldap.ld, msg, field);
2715 ldap_value_free(values);
2719 *v = atoi(values[0]);
2720 ldap_value_free(values);
2725 * pull a single objectGUID from an ADS result
2726 * @param ads connection to ADS server
2727 * @param msg results of search
2728 * @param guid 37-byte area to receive text guid
2729 * @return boolean indicating success
2731 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2736 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2741 status = GUID_from_ndr_blob(&blob, guid);
2742 talloc_free(blob.data);
2743 return NT_STATUS_IS_OK(status);
2748 * pull a single struct dom_sid from a ADS result
2749 * @param ads connection to ads server
2750 * @param msg Results of search
2751 * @param field Attribute to retrieve
2752 * @param sid Pointer to sid to store result
2753 * @return boolean inidicating success
2755 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2756 struct dom_sid *sid)
2758 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2762 * pull an array of struct dom_sids from a ADS result
2763 * @param ads connection to ads server
2764 * @param mem_ctx TALLOC_CTX for allocating sid array
2765 * @param msg Results of search
2766 * @param field Attribute to retrieve
2767 * @param sids pointer to sid array to allocate
2768 * @return the count of SIDs pulled
2770 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2771 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2773 struct berval **values;
2777 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2782 for (i=0; values[i]; i++)
2786 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2788 ldap_value_free_len(values);
2796 for (i=0; values[i]; i++) {
2797 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2798 values[i]->bv_len, &(*sids)[count]);
2800 DEBUG(10, ("pulling SID: %s\n",
2801 sid_string_dbg(&(*sids)[count])));
2806 ldap_value_free_len(values);
2811 * pull a struct security_descriptor from a ADS result
2812 * @param ads connection to ads server
2813 * @param mem_ctx TALLOC_CTX for allocating sid array
2814 * @param msg Results of search
2815 * @param field Attribute to retrieve
2816 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2817 * @return boolean inidicating success
2819 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2820 LDAPMessage *msg, const char *field,
2821 struct security_descriptor **sd)
2823 struct berval **values;
2826 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2828 if (!values) return false;
2832 status = unmarshall_sec_desc(mem_ctx,
2833 (uint8_t *)values[0]->bv_val,
2834 values[0]->bv_len, sd);
2835 if (!NT_STATUS_IS_OK(status)) {
2836 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2837 nt_errstr(status)));
2842 ldap_value_free_len(values);
2847 * in order to support usernames longer than 21 characters we need to
2848 * use both the sAMAccountName and the userPrincipalName attributes
2849 * It seems that not all users have the userPrincipalName attribute set
2851 * @param ads connection to ads server
2852 * @param mem_ctx TALLOC_CTX for allocating sid array
2853 * @param msg Results of search
2854 * @return the username
2856 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2862 /* lookup_name() only works on the sAMAccountName to
2863 returning the username portion of userPrincipalName
2864 breaks winbindd_getpwnam() */
2866 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2867 if (ret && (p = strchr_m(ret, '@'))) {
2872 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2877 * find the update serial number - this is the core of the ldap cache
2878 * @param ads connection to ads server
2879 * @param ads connection to ADS server
2880 * @param usn Pointer to retrieved update serial number
2881 * @return status of search
2883 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2885 const char *attrs[] = {"highestCommittedUSN", NULL};
2889 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2890 if (!ADS_ERR_OK(status))
2893 if (ads_count_replies(ads, res) != 1) {
2894 ads_msgfree(ads, res);
2895 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2898 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2899 ads_msgfree(ads, res);
2900 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2903 ads_msgfree(ads, res);
2907 /* parse a ADS timestring - typical string is
2908 '20020917091222.0Z0' which means 09:12.22 17th September
2910 static time_t ads_parse_time(const char *str)
2916 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2917 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2918 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2927 /********************************************************************
2928 ********************************************************************/
2930 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2932 const char *attrs[] = {"currentTime", NULL};
2937 ADS_STRUCT *ads_s = ads;
2939 if (!(ctx = talloc_init("ads_current_time"))) {
2940 return ADS_ERROR(LDAP_NO_MEMORY);
2943 /* establish a new ldap tcp session if necessary */
2945 if ( !ads->ldap.ld ) {
2946 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2947 ads->server.ldap_server )) == NULL )
2949 status = ADS_ERROR(LDAP_NO_MEMORY);
2952 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2953 status = ads_connect( ads_s );
2954 if ( !ADS_ERR_OK(status))
2958 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2959 if (!ADS_ERR_OK(status)) {
2963 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2965 ads_msgfree(ads_s, res);
2966 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2970 /* but save the time and offset in the original ADS_STRUCT */
2972 ads->config.current_time = ads_parse_time(timestr);
2974 if (ads->config.current_time != 0) {
2975 ads->auth.time_offset = ads->config.current_time - time(NULL);
2976 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2979 ads_msgfree(ads, res);
2981 status = ADS_SUCCESS;
2984 /* free any temporary ads connections */
2985 if ( ads_s != ads ) {
2986 ads_destroy( &ads_s );
2988 talloc_destroy(ctx);
2993 /********************************************************************
2994 ********************************************************************/
2996 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
2998 const char *attrs[] = {"domainFunctionality", NULL};
3001 ADS_STRUCT *ads_s = ads;
3003 *val = DS_DOMAIN_FUNCTION_2000;
3005 /* establish a new ldap tcp session if necessary */
3007 if ( !ads->ldap.ld ) {
3008 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3009 ads->server.ldap_server )) == NULL )
3011 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3014 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3015 status = ads_connect( ads_s );
3016 if ( !ADS_ERR_OK(status))
3020 /* If the attribute does not exist assume it is a Windows 2000
3021 functional domain */
3023 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3024 if (!ADS_ERR_OK(status)) {
3025 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3026 status = ADS_SUCCESS;
3031 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3032 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3034 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3037 ads_msgfree(ads, res);
3040 /* free any temporary ads connections */
3041 if ( ads_s != ads ) {
3042 ads_destroy( &ads_s );
3049 * find the domain sid for our domain
3050 * @param ads connection to ads server
3051 * @param sid Pointer to domain sid
3052 * @return status of search
3054 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3056 const char *attrs[] = {"objectSid", NULL};
3060 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3062 if (!ADS_ERR_OK(rc)) return rc;
3063 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3064 ads_msgfree(ads, res);
3065 return ADS_ERROR_SYSTEM(ENOENT);
3067 ads_msgfree(ads, res);
3073 * find our site name
3074 * @param ads connection to ads server
3075 * @param mem_ctx Pointer to talloc context
3076 * @param site_name Pointer to the sitename
3077 * @return status of search
3079 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3083 const char *dn, *service_name;
3084 const char *attrs[] = { "dsServiceName", NULL };
3086 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3087 if (!ADS_ERR_OK(status)) {
3091 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3092 if (service_name == NULL) {
3093 ads_msgfree(ads, res);
3094 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3097 ads_msgfree(ads, res);
3099 /* go up three levels */
3100 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3102 return ADS_ERROR(LDAP_NO_MEMORY);
3105 *site_name = talloc_strdup(mem_ctx, dn);
3106 if (*site_name == NULL) {
3107 return ADS_ERROR(LDAP_NO_MEMORY);
3112 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3117 * find the site dn where a machine resides
3118 * @param ads connection to ads server
3119 * @param mem_ctx Pointer to talloc context
3120 * @param computer_name name of the machine
3121 * @param site_name Pointer to the sitename
3122 * @return status of search
3124 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3128 const char *parent, *filter;
3129 char *config_context = NULL;
3132 /* shortcut a query */
3133 if (strequal(computer_name, ads->config.ldap_server_name)) {
3134 return ads_site_dn(ads, mem_ctx, site_dn);
3137 status = ads_config_path(ads, mem_ctx, &config_context);
3138 if (!ADS_ERR_OK(status)) {
3142 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3143 if (filter == NULL) {
3144 return ADS_ERROR(LDAP_NO_MEMORY);
3147 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3148 filter, NULL, &res);
3149 if (!ADS_ERR_OK(status)) {
3153 if (ads_count_replies(ads, res) != 1) {
3154 ads_msgfree(ads, res);
3155 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3158 dn = ads_get_dn(ads, mem_ctx, res);
3160 ads_msgfree(ads, res);
3161 return ADS_ERROR(LDAP_NO_MEMORY);
3164 /* go up three levels */
3165 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3166 if (parent == NULL) {
3167 ads_msgfree(ads, res);
3169 return ADS_ERROR(LDAP_NO_MEMORY);
3172 *site_dn = talloc_strdup(mem_ctx, parent);
3173 if (*site_dn == NULL) {
3174 ads_msgfree(ads, res);
3176 return ADS_ERROR(LDAP_NO_MEMORY);
3180 ads_msgfree(ads, res);
3186 * get the upn suffixes for a domain
3187 * @param ads connection to ads server
3188 * @param mem_ctx Pointer to talloc context
3189 * @param suffixes Pointer to an array of suffixes
3190 * @param num_suffixes Pointer to the number of suffixes
3191 * @return status of search
3193 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3198 char *config_context = NULL;
3199 const char *attrs[] = { "uPNSuffixes", NULL };
3201 status = ads_config_path(ads, mem_ctx, &config_context);
3202 if (!ADS_ERR_OK(status)) {
3206 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3208 return ADS_ERROR(LDAP_NO_MEMORY);
3211 status = ads_search_dn(ads, &res, base, attrs);
3212 if (!ADS_ERR_OK(status)) {
3216 if (ads_count_replies(ads, res) != 1) {
3217 ads_msgfree(ads, res);
3218 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3221 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3222 if ((*suffixes) == NULL) {
3223 ads_msgfree(ads, res);
3224 return ADS_ERROR(LDAP_NO_MEMORY);
3227 ads_msgfree(ads, res);
3233 * get the joinable ous for a domain
3234 * @param ads connection to ads server
3235 * @param mem_ctx Pointer to talloc context
3236 * @param ous Pointer to an array of ous
3237 * @param num_ous Pointer to the number of ous
3238 * @return status of search
3240 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3241 TALLOC_CTX *mem_ctx,
3246 LDAPMessage *res = NULL;
3247 LDAPMessage *msg = NULL;
3248 const char *attrs[] = { "dn", NULL };
3251 status = ads_search(ads, &res,
3252 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3254 if (!ADS_ERR_OK(status)) {
3258 count = ads_count_replies(ads, res);
3260 ads_msgfree(ads, res);
3261 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3264 for (msg = ads_first_entry(ads, res); msg;
3265 msg = ads_next_entry(ads, msg)) {
3266 const char **p = discard_const_p(const char *, *ous);
3269 dn = ads_get_dn(ads, talloc_tos(), msg);
3271 ads_msgfree(ads, res);
3272 return ADS_ERROR(LDAP_NO_MEMORY);
3275 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3277 ads_msgfree(ads, res);
3278 return ADS_ERROR(LDAP_NO_MEMORY);
3282 *ous = discard_const_p(char *, p);
3285 ads_msgfree(ads, res);
3292 * pull a struct dom_sid from an extended dn string
3293 * @param mem_ctx TALLOC_CTX
3294 * @param extended_dn string
3295 * @param flags string type of extended_dn
3296 * @param sid pointer to a struct dom_sid
3297 * @return NT_STATUS_OK on success,
3298 * NT_INVALID_PARAMETER on error,
3299 * NT_STATUS_NOT_FOUND if no SID present
3301 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3302 const char *extended_dn,
3303 enum ads_extended_dn_flags flags,
3304 struct dom_sid *sid)
3309 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3312 /* otherwise extended_dn gets stripped off */
3313 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3314 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3317 * ADS_EXTENDED_DN_HEX_STRING:
3318 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3320 * ADS_EXTENDED_DN_STRING (only with w2k3):
3321 * <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
3323 * Object with no SID, such as an Exchange Public Folder
3324 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3327 p = strchr(dn, ';');
3329 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3332 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3333 DEBUG(5,("No SID present in extended dn\n"));
3334 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3337 p += strlen(";<SID=");
3341 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3346 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3350 case ADS_EXTENDED_DN_STRING:
3351 if (!string_to_sid(sid, p)) {
3352 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3355 case ADS_EXTENDED_DN_HEX_STRING: {
3359 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3361 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3364 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3365 DEBUG(10,("failed to parse sid\n"));
3366 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3371 DEBUG(10,("unknown extended dn format\n"));
3372 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3375 return ADS_ERROR_NT(NT_STATUS_OK);
3378 /********************************************************************
3379 ********************************************************************/
3381 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3383 LDAPMessage *res = NULL;
3388 status = ads_find_machine_acct(ads, &res, machine_name);
3389 if (!ADS_ERR_OK(status)) {
3390 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3391 lp_netbios_name()));
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, "dNSHostName")) == NULL ) {
3401 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3405 ads_msgfree(ads, res);
3410 /********************************************************************
3411 ********************************************************************/
3413 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3415 LDAPMessage *res = NULL;
3420 status = ads_find_machine_acct(ads, &res, machine_name);
3421 if (!ADS_ERR_OK(status)) {
3422 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3423 lp_netbios_name()));
3427 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3428 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3432 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3433 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3437 ads_msgfree(ads, res);
3442 /********************************************************************
3443 ********************************************************************/
3445 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3447 LDAPMessage *res = NULL;
3453 status = ads_find_machine_acct(ads, &res, machine_name);
3454 if (!ADS_ERR_OK(status)) {
3455 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3456 lp_netbios_name()));
3460 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3461 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3465 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3466 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3470 ads_msgfree(ads, res);
3472 ok = (strlen(name) > 0);
3479 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3482 * Join a machine to a realm
3483 * Creates the machine account and sets the machine password
3484 * @param ads connection to ads server
3485 * @param machine name of host to add
3486 * @param org_unit Organizational unit to place machine in
3487 * @return status of join
3489 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3490 uint32_t account_type, const char *org_unit)
3493 LDAPMessage *res = NULL;
3496 /* machine name must be lowercase */
3497 machine = SMB_STRDUP(machine_name);
3498 strlower_m(machine);
3501 status = ads_find_machine_acct(ads, (void **)&res, machine);
3502 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3503 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3504 status = ads_leave_realm(ads, machine);
3505 if (!ADS_ERR_OK(status)) {
3506 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3507 machine, ads->config.realm));
3512 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3513 if (!ADS_ERR_OK(status)) {
3514 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3519 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3520 if (!ADS_ERR_OK(status)) {
3521 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3527 ads_msgfree(ads, res);
3534 * Delete a machine from the realm
3535 * @param ads connection to ads server
3536 * @param hostname Machine to remove
3537 * @return status of delete
3539 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3544 char *hostnameDN, *host;
3546 LDAPControl ldap_control;
3547 LDAPControl * pldap_control[2] = {NULL, NULL};
3549 pldap_control[0] = &ldap_control;
3550 memset(&ldap_control, 0, sizeof(LDAPControl));
3551 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3553 /* hostname must be lowercase */
3554 host = SMB_STRDUP(hostname);
3555 if (!strlower_m(host)) {
3557 return ADS_ERROR_SYSTEM(EINVAL);
3560 status = ads_find_machine_acct(ads, &res, host);
3561 if (!ADS_ERR_OK(status)) {
3562 DEBUG(0, ("Host account for %s does not exist.\n", host));
3567 msg = ads_first_entry(ads, res);
3570 return ADS_ERROR_SYSTEM(ENOENT);
3573 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3574 if (hostnameDN == NULL) {
3576 return ADS_ERROR_SYSTEM(ENOENT);
3579 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3581 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3583 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3586 if (rc != LDAP_SUCCESS) {
3587 const char *attrs[] = { "cn", NULL };
3588 LDAPMessage *msg_sub;
3590 /* we only search with scope ONE, we do not expect any further
3591 * objects to be created deeper */
3593 status = ads_do_search_retry(ads, hostnameDN,
3594 LDAP_SCOPE_ONELEVEL,
3595 "(objectclass=*)", attrs, &res);
3597 if (!ADS_ERR_OK(status)) {
3599 TALLOC_FREE(hostnameDN);
3603 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3604 msg_sub = ads_next_entry(ads, msg_sub)) {
3608 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3610 TALLOC_FREE(hostnameDN);
3611 return ADS_ERROR(LDAP_NO_MEMORY);
3614 status = ads_del_dn(ads, dn);
3615 if (!ADS_ERR_OK(status)) {
3616 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3619 TALLOC_FREE(hostnameDN);
3626 /* there should be no subordinate objects anymore */
3627 status = ads_do_search_retry(ads, hostnameDN,
3628 LDAP_SCOPE_ONELEVEL,
3629 "(objectclass=*)", attrs, &res);
3631 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3633 TALLOC_FREE(hostnameDN);
3637 /* delete hostnameDN now */
3638 status = ads_del_dn(ads, hostnameDN);
3639 if (!ADS_ERR_OK(status)) {
3641 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3642 TALLOC_FREE(hostnameDN);
3647 TALLOC_FREE(hostnameDN);
3649 status = ads_find_machine_acct(ads, &res, host);
3650 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3651 DEBUG(3, ("Failed to remove host account.\n"));
3661 * pull all token-sids from an LDAP dn
3662 * @param ads connection to ads server
3663 * @param mem_ctx TALLOC_CTX for allocating sid array
3664 * @param dn of LDAP object
3665 * @param user_sid pointer to struct dom_sid (objectSid)
3666 * @param primary_group_sid pointer to struct dom_sid (self composed)
3667 * @param sids pointer to sid array to allocate
3668 * @param num_sids counter of SIDs pulled
3669 * @return status of token query
3671 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3672 TALLOC_CTX *mem_ctx,
3674 struct dom_sid *user_sid,
3675 struct dom_sid *primary_group_sid,
3676 struct dom_sid **sids,
3680 LDAPMessage *res = NULL;
3682 size_t tmp_num_sids;
3683 struct dom_sid *tmp_sids;
3684 struct dom_sid tmp_user_sid;
3685 struct dom_sid tmp_primary_group_sid;
3687 const char *attrs[] = {
3694 status = ads_search_retry_dn(ads, &res, dn, attrs);
3695 if (!ADS_ERR_OK(status)) {
3699 count = ads_count_replies(ads, res);
3701 ads_msgfree(ads, res);
3702 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3705 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3706 ads_msgfree(ads, res);
3707 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3710 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3711 ads_msgfree(ads, res);
3712 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3716 /* hack to compose the primary group sid without knowing the
3719 struct dom_sid domsid;
3721 sid_copy(&domsid, &tmp_user_sid);
3723 if (!sid_split_rid(&domsid, NULL)) {
3724 ads_msgfree(ads, res);
3725 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3728 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3729 ads_msgfree(ads, res);
3730 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3734 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3736 if (tmp_num_sids == 0 || !tmp_sids) {
3737 ads_msgfree(ads, res);
3738 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3742 *num_sids = tmp_num_sids;
3750 *user_sid = tmp_user_sid;
3753 if (primary_group_sid) {
3754 *primary_group_sid = tmp_primary_group_sid;
3757 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3759 ads_msgfree(ads, res);
3760 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3764 * Find a sAMAccoutName in LDAP
3765 * @param ads connection to ads server
3766 * @param mem_ctx TALLOC_CTX for allocating sid array
3767 * @param samaccountname to search
3768 * @param uac_ret uint32_t pointer userAccountControl attribute value
3769 * @param dn_ret pointer to dn
3770 * @return status of token query
3772 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3773 TALLOC_CTX *mem_ctx,
3774 const char *samaccountname,
3776 const char **dn_ret)
3779 const char *attrs[] = { "userAccountControl", NULL };
3781 LDAPMessage *res = NULL;
3785 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3787 if (filter == NULL) {
3788 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3792 status = ads_do_search_all(ads, ads->config.bind_path,
3794 filter, attrs, &res);
3796 if (!ADS_ERR_OK(status)) {
3800 if (ads_count_replies(ads, res) != 1) {
3801 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3805 dn = ads_get_dn(ads, talloc_tos(), res);
3807 status = ADS_ERROR(LDAP_NO_MEMORY);
3811 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3812 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3821 *dn_ret = talloc_strdup(mem_ctx, dn);
3823 status = ADS_ERROR(LDAP_NO_MEMORY);
3829 ads_msgfree(ads, res);
3835 * find our configuration path
3836 * @param ads connection to ads server
3837 * @param mem_ctx Pointer to talloc context
3838 * @param config_path Pointer to the config path
3839 * @return status of search
3841 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3842 TALLOC_CTX *mem_ctx,
3846 LDAPMessage *res = NULL;
3847 const char *config_context = NULL;
3848 const char *attrs[] = { "configurationNamingContext", NULL };
3850 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3851 "(objectclass=*)", attrs, &res);
3852 if (!ADS_ERR_OK(status)) {
3856 config_context = ads_pull_string(ads, mem_ctx, res,
3857 "configurationNamingContext");
3858 ads_msgfree(ads, res);
3859 if (!config_context) {
3860 return ADS_ERROR(LDAP_NO_MEMORY);
3864 *config_path = talloc_strdup(mem_ctx, config_context);
3865 if (!*config_path) {
3866 return ADS_ERROR(LDAP_NO_MEMORY);
3870 return ADS_ERROR(LDAP_SUCCESS);
3874 * find the displayName of an extended right
3875 * @param ads connection to ads server
3876 * @param config_path The config path
3877 * @param mem_ctx Pointer to talloc context
3878 * @param GUID struct of the rightsGUID
3879 * @return status of search
3881 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3882 const char *config_path,
3883 TALLOC_CTX *mem_ctx,
3884 const struct GUID *rights_guid)
3887 LDAPMessage *res = NULL;
3889 const char *attrs[] = { "displayName", NULL };
3890 const char *result = NULL;
3893 if (!ads || !mem_ctx || !rights_guid) {
3897 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3898 GUID_string(mem_ctx, rights_guid));
3903 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3908 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3910 if (!ADS_ERR_OK(rc)) {
3914 if (ads_count_replies(ads, res) != 1) {
3918 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3921 ads_msgfree(ads, res);
3926 * verify or build and verify an account ou
3927 * @param mem_ctx Pointer to talloc context
3928 * @param ads connection to ads server
3930 * @return status of search
3933 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3935 const char **account_ou)
3941 if (account_ou == NULL) {
3942 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3945 if (*account_ou != NULL) {
3946 exploded_dn = ldap_explode_dn(*account_ou, 0);
3948 ldap_value_free(exploded_dn);
3953 ou_string = ads_ou_string(ads, *account_ou);
3955 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3958 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3959 ads->config.bind_path);
3960 SAFE_FREE(ou_string);
3963 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3966 exploded_dn = ldap_explode_dn(name, 0);
3968 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3970 ldap_value_free(exploded_dn);