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 = NULL;
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)) {
428 status = cldap_ping_list(ads, realm, ip_list, count);
435 /**********************************************************************
436 Try to find an AD dc using our internal name resolution routines
437 Try the realm first and then then workgroup name if netbios is not
439 **********************************************************************/
441 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
443 const char *c_domain = "";
445 bool use_own_domain = False;
446 char *sitename = NULL;
447 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
450 /* if the realm and workgroup are both empty, assume they are ours */
453 c_realm = ads->server.realm;
459 /* special case where no realm and no workgroup means our own */
460 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
461 use_own_domain = True;
462 c_realm = lp_realm();
466 if (!lp_disable_netbios()) {
467 if (use_own_domain) {
468 c_domain = lp_workgroup();
470 c_domain = ads->server.workgroup;
471 if (!*c_realm && (!c_domain || !*c_domain)) {
472 c_domain = lp_workgroup();
481 if (!*c_realm && !*c_domain) {
482 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
484 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
488 * In case of LDAP we use get_dc_name() as that
489 * creates the custom krb5.conf file
491 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
493 struct sockaddr_storage ip_out;
495 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
496 " and falling back to domain '%s'\n",
499 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
502 * we call ads_try_connect() to fill in the
503 * ads->config details
505 ok = ads_try_connect(ads, false, &ip_out);
511 return NT_STATUS_NO_LOGON_SERVERS;
515 sitename = sitename_fetch(talloc_tos(), c_realm);
516 status = resolve_and_ping_dns(ads, sitename, c_realm);
518 if (NT_STATUS_IS_OK(status)) {
519 TALLOC_FREE(sitename);
523 /* In case we failed to contact one of our closest DC on our
525 * need to try to find another DC, retry with a site-less SRV
530 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
531 "our site (%s), Trying to find another DC "
532 "for realm '%s' (domain '%s')\n",
533 sitename, c_realm, c_domain));
534 namecache_delete(c_realm, 0x1C);
536 resolve_and_ping_dns(ads, NULL, c_realm);
538 if (NT_STATUS_IS_OK(status)) {
539 TALLOC_FREE(sitename);
544 TALLOC_FREE(sitename);
547 /* try netbios as fallback - if permitted,
548 or if configuration specifically requests it */
551 DEBUG(3, ("ads_find_dc: falling back to netbios "
552 "name resolution for domain '%s' (realm '%s')\n",
556 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
557 if (NT_STATUS_IS_OK(status)) {
562 DEBUG(1, ("ads_find_dc: "
563 "name resolution for realm '%s' (domain '%s') failed: %s\n",
564 c_realm, c_domain, nt_errstr(status)));
568 * Connect to the LDAP server
569 * @param ads Pointer to an existing ADS_STRUCT
570 * @return status of connection
572 ADS_STATUS ads_connect(ADS_STRUCT *ads)
574 int version = LDAP_VERSION3;
577 char addr[INET6_ADDRSTRLEN];
579 ZERO_STRUCT(ads->ldap);
580 ZERO_STRUCT(ads->ldap_wrap_data);
581 ads->ldap.last_attempt = time_mono(NULL);
582 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
584 /* try with a user specified server */
586 if (DEBUGLEVEL >= 11) {
587 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
588 DEBUG(11,("ads_connect: entering\n"));
589 DEBUGADD(11,("%s\n", s));
593 if (ads->server.ldap_server) {
595 struct sockaddr_storage ss;
597 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
599 DEBUG(5,("ads_connect: unable to resolve name %s\n",
600 ads->server.ldap_server));
601 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
604 ok = ads_try_connect(ads, ads->server.gc, &ss);
609 /* The choice of which GC use is handled one level up in
610 ads_connect_gc(). If we continue on from here with
611 ads_find_dc() we will get GC searches on port 389 which
612 doesn't work. --jerry */
614 if (ads->server.gc == true) {
615 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
618 if (ads->server.no_fallback) {
619 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
624 ntstatus = ads_find_dc(ads);
625 if (NT_STATUS_IS_OK(ntstatus)) {
629 status = ADS_ERROR_NT(ntstatus);
634 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
635 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
637 if (!ads->auth.user_name) {
638 /* Must use the userPrincipalName value here or sAMAccountName
639 and not servicePrincipalName; found by Guenther Deschner */
641 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
642 DEBUG(0,("ads_connect: asprintf fail.\n"));
643 ads->auth.user_name = NULL;
647 if (!ads->auth.realm) {
648 ads->auth.realm = SMB_STRDUP(ads->config.realm);
651 if (!ads->auth.kdc_server) {
652 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
653 ads->auth.kdc_server = SMB_STRDUP(addr);
656 /* If the caller() requested no LDAP bind, then we are done */
658 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
659 status = ADS_SUCCESS;
663 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
664 if (!ads->ldap_wrap_data.mem_ctx) {
665 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
669 /* Otherwise setup the TCP LDAP session */
671 ads->ldap.ld = ldap_open_with_timeout(addr,
673 ads->ldap.port, lp_ldap_timeout());
674 if (ads->ldap.ld == NULL) {
675 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
678 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
680 /* cache the successful connection for workgroup and realm */
681 if (ads_closest_dc(ads)) {
682 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
683 saf_store( ads->server.realm, ads->config.ldap_server_name);
686 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
688 if ( lp_ldap_ssl_ads() ) {
689 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
690 if (!ADS_ERR_OK(status)) {
695 /* fill in the current time and offsets */
697 status = ads_current_time( ads );
698 if ( !ADS_ERR_OK(status) ) {
702 /* Now do the bind */
704 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
705 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
709 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
710 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
714 status = ads_sasl_bind(ads);
717 if (DEBUGLEVEL >= 11) {
718 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
719 DEBUG(11,("ads_connect: leaving with: %s\n",
720 ads_errstr(status)));
721 DEBUGADD(11,("%s\n", s));
729 * Connect to the LDAP server using given credentials
730 * @param ads Pointer to an existing ADS_STRUCT
731 * @return status of connection
733 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
735 ads->auth.flags |= ADS_AUTH_USER_CREDS;
737 return ads_connect(ads);
741 * Disconnect the LDAP server
742 * @param ads Pointer to an existing ADS_STRUCT
744 void ads_disconnect(ADS_STRUCT *ads)
747 ldap_unbind(ads->ldap.ld);
750 if (ads->ldap_wrap_data.wrap_ops &&
751 ads->ldap_wrap_data.wrap_ops->disconnect) {
752 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
754 if (ads->ldap_wrap_data.mem_ctx) {
755 talloc_free(ads->ldap_wrap_data.mem_ctx);
757 ZERO_STRUCT(ads->ldap);
758 ZERO_STRUCT(ads->ldap_wrap_data);
762 Duplicate a struct berval into talloc'ed memory
764 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
766 struct berval *value;
768 if (!in_val) return NULL;
770 value = talloc_zero(ctx, struct berval);
773 if (in_val->bv_len == 0) return value;
775 value->bv_len = in_val->bv_len;
776 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
782 Make a values list out of an array of (struct berval *)
784 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
785 const struct berval **in_vals)
787 struct berval **values;
790 if (!in_vals) return NULL;
791 for (i=0; in_vals[i]; i++)
793 values = talloc_zero_array(ctx, struct berval *, i+1);
794 if (!values) return NULL;
796 for (i=0; in_vals[i]; i++) {
797 values[i] = dup_berval(ctx, in_vals[i]);
803 UTF8-encode a values list out of an array of (char *)
805 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
811 if (!in_vals) return NULL;
812 for (i=0; in_vals[i]; i++)
814 values = talloc_zero_array(ctx, char *, i+1);
815 if (!values) return NULL;
817 for (i=0; in_vals[i]; i++) {
818 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
827 Pull a (char *) array out of a UTF8-encoded values list
829 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
833 size_t converted_size;
835 if (!in_vals) return NULL;
836 for (i=0; in_vals[i]; i++)
838 values = talloc_zero_array(ctx, char *, i+1);
839 if (!values) return NULL;
841 for (i=0; in_vals[i]; i++) {
842 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
844 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
845 "%s", strerror(errno)));
852 * Do a search with paged results. cookie must be null on the first
853 * call, and then returned on each subsequent call. It will be null
854 * again when the entire search is complete
855 * @param ads connection to ads server
856 * @param bind_path Base dn for the search
857 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
858 * @param expr Search expression - specified in local charset
859 * @param attrs Attributes to retrieve - specified in utf8 or ascii
860 * @param res ** which will contain results - free res* with ads_msgfree()
861 * @param count Number of entries retrieved on this page
862 * @param cookie The paged results cookie to be returned on subsequent calls
863 * @return status of search
865 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
866 const char *bind_path,
867 int scope, const char *expr,
868 const char **attrs, void *args,
870 int *count, struct berval **cookie)
873 char *utf8_expr, *utf8_path, **search_attrs = NULL;
874 size_t converted_size;
875 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
876 BerElement *cookie_be = NULL;
877 struct berval *cookie_bv= NULL;
878 BerElement *ext_be = NULL;
879 struct berval *ext_bv= NULL;
882 ads_control *external_control = (ads_control *) args;
886 if (!(ctx = talloc_init("ads_do_paged_search_args")))
887 return ADS_ERROR(LDAP_NO_MEMORY);
889 /* 0 means the conversion worked but the result was empty
890 so we only fail if it's -1. In any case, it always
891 at least nulls out the dest */
892 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
893 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
899 if (!attrs || !(*attrs))
902 /* This would be the utf8-encoded version...*/
903 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
904 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
910 /* Paged results only available on ldap v3 or later */
911 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
912 if (version < LDAP_VERSION3) {
913 rc = LDAP_NOT_SUPPORTED;
917 cookie_be = ber_alloc_t(LBER_USE_DER);
919 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
920 ber_bvfree(*cookie); /* don't need it from last time */
923 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
925 ber_flatten(cookie_be, &cookie_bv);
926 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
927 PagedResults.ldctl_iscritical = (char) 1;
928 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
929 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
931 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
932 NoReferrals.ldctl_iscritical = (char) 0;
933 NoReferrals.ldctl_value.bv_len = 0;
934 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
936 if (external_control &&
937 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
938 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
940 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
941 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
943 /* win2k does not accept a ldctl_value beeing passed in */
945 if (external_control->val != 0) {
947 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
952 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
956 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
961 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
962 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
965 ExternalCtrl.ldctl_value.bv_len = 0;
966 ExternalCtrl.ldctl_value.bv_val = NULL;
969 controls[0] = &NoReferrals;
970 controls[1] = &PagedResults;
971 controls[2] = &ExternalCtrl;
975 controls[0] = &NoReferrals;
976 controls[1] = &PagedResults;
980 /* we need to disable referrals as the openldap libs don't
981 handle them and paged results at the same time. Using them
982 together results in the result record containing the server
983 page control being removed from the result list (tridge/jmcd)
985 leaving this in despite the control that says don't generate
986 referrals, in case the server doesn't support it (jmcd)
988 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
990 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
991 search_attrs, 0, controls,
993 (LDAPMessage **)res);
995 ber_free(cookie_be, 1);
996 ber_bvfree(cookie_bv);
999 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1000 ldap_err2string(rc)));
1001 if (rc == LDAP_OTHER) {
1005 ret = ldap_parse_result(ads->ldap.ld,
1013 if (ret == LDAP_SUCCESS) {
1014 DEBUG(3, ("ldap_search_with_timeout(%s) "
1015 "error: %s\n", expr, ldap_errmsg));
1016 ldap_memfree(ldap_errmsg);
1022 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1023 NULL, &rcontrols, 0);
1029 for (i=0; rcontrols[i]; i++) {
1030 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1031 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1032 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1034 /* the berval is the cookie, but must be freed when
1036 if (cookie_bv->bv_len) /* still more to do */
1037 *cookie=ber_bvdup(cookie_bv);
1040 ber_bvfree(cookie_bv);
1041 ber_free(cookie_be, 1);
1045 ldap_controls_free(rcontrols);
1048 talloc_destroy(ctx);
1051 ber_free(ext_be, 1);
1058 if (rc != LDAP_SUCCESS && *res != NULL) {
1059 ads_msgfree(ads, *res);
1063 /* if/when we decide to utf8-encode attrs, take out this next line */
1064 TALLOC_FREE(search_attrs);
1066 return ADS_ERROR(rc);
1069 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1070 int scope, const char *expr,
1071 const char **attrs, LDAPMessage **res,
1072 int *count, struct berval **cookie)
1074 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1079 * Get all results for a search. This uses ads_do_paged_search() to return
1080 * all entries in a large search.
1081 * @param ads connection to ads server
1082 * @param bind_path Base dn for the search
1083 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1084 * @param expr Search expression
1085 * @param attrs Attributes to retrieve
1086 * @param res ** which will contain results - free res* with ads_msgfree()
1087 * @return status of search
1089 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1090 int scope, const char *expr,
1091 const char **attrs, void *args,
1094 struct berval *cookie = NULL;
1099 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1102 if (!ADS_ERR_OK(status))
1105 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1107 LDAPMessage *res2 = NULL;
1108 LDAPMessage *msg, *next;
1110 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1111 attrs, args, &res2, &count, &cookie);
1112 if (!ADS_ERR_OK(status)) {
1116 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1117 that this works on all ldap libs, but I have only tested with openldap */
1118 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1119 next = ads_next_message(ads, msg);
1120 ldap_add_result_entry((LDAPMessage **)res, msg);
1122 /* note that we do not free res2, as the memory is now
1123 part of the main returned list */
1126 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1127 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1133 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1134 int scope, const char *expr,
1135 const char **attrs, LDAPMessage **res)
1137 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1140 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1141 int scope, const char *expr,
1142 const char **attrs, uint32_t sd_flags,
1147 args.control = ADS_SD_FLAGS_OID;
1148 args.val = sd_flags;
1149 args.critical = True;
1151 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1156 * Run a function on all results for a search. Uses ads_do_paged_search() and
1157 * runs the function as each page is returned, using ads_process_results()
1158 * @param ads connection to ads server
1159 * @param bind_path Base dn for the search
1160 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1161 * @param expr Search expression - specified in local charset
1162 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1163 * @param fn Function which takes attr name, values list, and data_area
1164 * @param data_area Pointer which is passed to function on each call
1165 * @return status of search
1167 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1168 int scope, const char *expr, const char **attrs,
1169 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1172 struct berval *cookie = NULL;
1177 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1180 if (!ADS_ERR_OK(status)) return status;
1182 ads_process_results(ads, res, fn, data_area);
1183 ads_msgfree(ads, res);
1186 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1187 &res, &count, &cookie);
1189 if (!ADS_ERR_OK(status)) break;
1191 ads_process_results(ads, res, fn, data_area);
1192 ads_msgfree(ads, res);
1199 * Do a search with a timeout.
1200 * @param ads connection to ads server
1201 * @param bind_path Base dn for the search
1202 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1203 * @param expr Search expression
1204 * @param attrs Attributes to retrieve
1205 * @param res ** which will contain results - free res* with ads_msgfree()
1206 * @return status of search
1208 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1210 const char **attrs, LDAPMessage **res)
1213 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1214 size_t converted_size;
1218 if (!(ctx = talloc_init("ads_do_search"))) {
1219 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1220 return ADS_ERROR(LDAP_NO_MEMORY);
1223 /* 0 means the conversion worked but the result was empty
1224 so we only fail if it's negative. In any case, it always
1225 at least nulls out the dest */
1226 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1227 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1229 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1230 rc = LDAP_NO_MEMORY;
1234 if (!attrs || !(*attrs))
1235 search_attrs = NULL;
1237 /* This would be the utf8-encoded version...*/
1238 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1239 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1241 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1242 rc = LDAP_NO_MEMORY;
1247 /* see the note in ads_do_paged_search - we *must* disable referrals */
1248 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1250 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1251 search_attrs, 0, NULL, NULL,
1253 (LDAPMessage **)res);
1255 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1256 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1261 talloc_destroy(ctx);
1262 /* if/when we decide to utf8-encode attrs, take out this next line */
1263 TALLOC_FREE(search_attrs);
1264 return ADS_ERROR(rc);
1267 * Do a general ADS search
1268 * @param ads connection to ads server
1269 * @param res ** which will contain results - free res* with ads_msgfree()
1270 * @param expr Search expression
1271 * @param attrs Attributes to retrieve
1272 * @return status of search
1274 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1275 const char *expr, const char **attrs)
1277 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1282 * Do a search on a specific DistinguishedName
1283 * @param ads connection to ads server
1284 * @param res ** which will contain results - free res* with ads_msgfree()
1285 * @param dn DistinguishName to search
1286 * @param attrs Attributes to retrieve
1287 * @return status of search
1289 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1290 const char *dn, const char **attrs)
1292 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1297 * Free up memory from a ads_search
1298 * @param ads connection to ads server
1299 * @param msg Search results to free
1301 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1308 * Get a dn from search results
1309 * @param ads connection to ads server
1310 * @param msg Search result
1313 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1315 char *utf8_dn, *unix_dn;
1316 size_t converted_size;
1318 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1321 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1325 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1326 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1330 ldap_memfree(utf8_dn);
1335 * Get the parent from a dn
1336 * @param dn the dn to return the parent from
1337 * @return parent dn string
1339 char *ads_parent_dn(const char *dn)
1347 p = strchr(dn, ',');
1357 * Find a machine account given a hostname
1358 * @param ads connection to ads server
1359 * @param res ** which will contain results - free res* with ads_msgfree()
1360 * @param host Hostname to search for
1361 * @return status of search
1363 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1364 const char *machine)
1368 const char *attrs[] = {"*", "msDS-SupportedEncryptionTypes", "nTSecurityDescriptor", NULL};
1372 /* the easiest way to find a machine account anywhere in the tree
1373 is to look for hostname$ */
1374 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1375 DEBUG(1, ("asprintf failed!\n"));
1376 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1379 status = ads_search(ads, res, expr, attrs);
1385 * Initialize a list of mods to be used in a modify request
1386 * @param ctx An initialized TALLOC_CTX
1387 * @return allocated ADS_MODLIST
1389 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1391 #define ADS_MODLIST_ALLOC_SIZE 10
1394 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1395 /* -1 is safety to make sure we don't go over the end.
1396 need to reset it to NULL before doing ldap modify */
1397 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1399 return (ADS_MODLIST)mods;
1404 add an attribute to the list, with values list already constructed
1406 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1407 int mod_op, const char *name,
1408 const void *_invals)
1411 LDAPMod **modlist = (LDAPMod **) *mods;
1412 struct berval **ber_values = NULL;
1413 char **char_values = NULL;
1416 mod_op = LDAP_MOD_DELETE;
1418 if (mod_op & LDAP_MOD_BVALUES) {
1419 const struct berval **b;
1420 b = discard_const_p(const struct berval *, _invals);
1421 ber_values = ads_dup_values(ctx, b);
1424 c = discard_const_p(const char *, _invals);
1425 char_values = ads_push_strvals(ctx, c);
1429 /* find the first empty slot */
1430 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1432 if (modlist[curmod] == (LDAPMod *) -1) {
1433 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1434 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1435 return ADS_ERROR(LDAP_NO_MEMORY);
1436 memset(&modlist[curmod], 0,
1437 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1438 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1439 *mods = (ADS_MODLIST)modlist;
1442 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1443 return ADS_ERROR(LDAP_NO_MEMORY);
1444 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1445 if (mod_op & LDAP_MOD_BVALUES) {
1446 modlist[curmod]->mod_bvalues = ber_values;
1447 } else if (mod_op & LDAP_MOD_DELETE) {
1448 modlist[curmod]->mod_values = NULL;
1450 modlist[curmod]->mod_values = char_values;
1453 modlist[curmod]->mod_op = mod_op;
1454 return ADS_ERROR(LDAP_SUCCESS);
1458 * Add a single string value to a mod list
1459 * @param ctx An initialized TALLOC_CTX
1460 * @param mods An initialized ADS_MODLIST
1461 * @param name The attribute name to add
1462 * @param val The value to add - NULL means DELETE
1463 * @return ADS STATUS indicating success of add
1465 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1466 const char *name, const char *val)
1468 const char *values[2];
1474 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1475 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1479 * Add an array of string values to a mod list
1480 * @param ctx An initialized TALLOC_CTX
1481 * @param mods An initialized ADS_MODLIST
1482 * @param name The attribute name to add
1483 * @param vals The array of string values to add - NULL means DELETE
1484 * @return ADS STATUS indicating success of add
1486 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1487 const char *name, const char **vals)
1490 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1491 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1492 name, (const void **) vals);
1497 * Add a single ber-encoded value to a mod list
1498 * @param ctx An initialized TALLOC_CTX
1499 * @param mods An initialized ADS_MODLIST
1500 * @param name The attribute name to add
1501 * @param val The value to add - NULL means DELETE
1502 * @return ADS STATUS indicating success of add
1504 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1505 const char *name, const struct berval *val)
1507 const struct berval *values[2];
1512 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1513 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1514 name, (const void **) values);
1518 static void ads_print_error(int ret, LDAP *ld)
1521 char *ld_error = NULL;
1522 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1523 DEBUG(10,("AD LDAP failure %d (%s):\n%s\n", ret,
1524 ldap_err2string(ret), ld_error));
1525 SAFE_FREE(ld_error);
1530 * Perform an ldap modify
1531 * @param ads connection to ads server
1532 * @param mod_dn DistinguishedName to modify
1533 * @param mods list of modifications to perform
1534 * @return status of modify
1536 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1539 char *utf8_dn = NULL;
1540 size_t converted_size;
1542 this control is needed to modify that contains a currently
1543 non-existent attribute (but allowable for the object) to run
1545 LDAPControl PermitModify = {
1546 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1549 LDAPControl *controls[2];
1551 controls[0] = &PermitModify;
1554 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1555 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1558 /* find the end of the list, marked by NULL or -1 */
1559 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1560 /* make sure the end of the list is NULL */
1562 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1563 (LDAPMod **) mods, controls, NULL);
1564 ads_print_error(ret, ads->ldap.ld);
1565 TALLOC_FREE(utf8_dn);
1566 return ADS_ERROR(ret);
1570 * Perform an ldap add
1571 * @param ads connection to ads server
1572 * @param new_dn DistinguishedName to add
1573 * @param mods list of attributes and values for DN
1574 * @return status of add
1576 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1579 char *utf8_dn = NULL;
1580 size_t converted_size;
1582 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1583 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1584 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1587 /* find the end of the list, marked by NULL or -1 */
1588 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1589 /* make sure the end of the list is NULL */
1592 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1593 ads_print_error(ret, ads->ldap.ld);
1594 TALLOC_FREE(utf8_dn);
1595 return ADS_ERROR(ret);
1599 * Delete a DistinguishedName
1600 * @param ads connection to ads server
1601 * @param new_dn DistinguishedName to delete
1602 * @return status of delete
1604 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1607 char *utf8_dn = NULL;
1608 size_t converted_size;
1609 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1610 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1611 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1614 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1615 ads_print_error(ret, ads->ldap.ld);
1616 TALLOC_FREE(utf8_dn);
1617 return ADS_ERROR(ret);
1621 * Build an org unit string
1622 * if org unit is Computers or blank then assume a container, otherwise
1623 * assume a / separated list of organisational units.
1624 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1625 * @param ads connection to ads server
1626 * @param org_unit Organizational unit
1627 * @return org unit string - caller must free
1629 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1633 if (!org_unit || !*org_unit) {
1635 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1637 /* samba4 might not yet respond to a wellknownobject-query */
1638 return ret ? ret : SMB_STRDUP("cn=Computers");
1641 if (strequal(org_unit, "Computers")) {
1642 return SMB_STRDUP("cn=Computers");
1645 /* jmcd: removed "\\" from the separation chars, because it is
1646 needed as an escape for chars like '#' which are valid in an
1648 return ads_build_path(org_unit, "/", "ou=", 1);
1652 * Get a org unit string for a well-known GUID
1653 * @param ads connection to ads server
1654 * @param wknguid Well known GUID
1655 * @return org unit string - caller must free
1657 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1660 LDAPMessage *res = NULL;
1661 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1662 **bind_dn_exp = NULL;
1663 const char *attrs[] = {"distinguishedName", NULL};
1664 int new_ln, wkn_ln, bind_ln, i;
1666 if (wknguid == NULL) {
1670 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1671 DEBUG(1, ("asprintf failed!\n"));
1675 status = ads_search_dn(ads, &res, base, attrs);
1676 if (!ADS_ERR_OK(status)) {
1677 DEBUG(1,("Failed while searching for: %s\n", base));
1681 if (ads_count_replies(ads, res) != 1) {
1685 /* substitute the bind-path from the well-known-guid-search result */
1686 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1691 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1696 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1701 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1703 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1706 new_ln = wkn_ln - bind_ln;
1708 ret = SMB_STRDUP(wkn_dn_exp[0]);
1713 for (i=1; i < new_ln; i++) {
1716 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1722 ret = SMB_STRDUP(s);
1731 ads_msgfree(ads, res);
1732 TALLOC_FREE(wkn_dn);
1734 ldap_value_free(wkn_dn_exp);
1737 ldap_value_free(bind_dn_exp);
1744 * Adds (appends) an item to an attribute array, rather then
1745 * replacing the whole list
1746 * @param ctx An initialized TALLOC_CTX
1747 * @param mods An initialized ADS_MODLIST
1748 * @param name name of the ldap attribute to append to
1749 * @param vals an array of values to add
1750 * @return status of addition
1753 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1754 const char *name, const char **vals)
1756 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1757 (const void *) vals);
1761 * Determines the an account's current KVNO via an LDAP lookup
1762 * @param ads An initialized ADS_STRUCT
1763 * @param account_name the NT samaccountname.
1764 * @return the kvno for the account, or -1 in case of a failure.
1767 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1769 LDAPMessage *res = NULL;
1770 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1772 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1773 char *dn_string = NULL;
1774 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1776 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1777 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1780 ret = ads_search(ads, &res, filter, attrs);
1782 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1783 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1784 ads_msgfree(ads, res);
1788 dn_string = ads_get_dn(ads, talloc_tos(), res);
1790 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1791 ads_msgfree(ads, res);
1794 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1795 TALLOC_FREE(dn_string);
1797 /* ---------------------------------------------------------
1798 * 0 is returned as a default KVNO from this point on...
1799 * This is done because Windows 2000 does not support key
1800 * version numbers. Chances are that a failure in the next
1801 * step is simply due to Windows 2000 being used for a
1802 * domain controller. */
1805 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1806 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1807 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1808 ads_msgfree(ads, res);
1813 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1814 ads_msgfree(ads, res);
1819 * Determines the computer account's current KVNO via an LDAP lookup
1820 * @param ads An initialized ADS_STRUCT
1821 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1822 * @return the kvno for the computer account, or -1 in case of a failure.
1825 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1827 char *computer_account = NULL;
1830 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1834 kvno = ads_get_kvno(ads, computer_account);
1835 free(computer_account);
1841 * This clears out all registered spn's for a given hostname
1842 * @param ads An initilaized ADS_STRUCT
1843 * @param machine_name the NetBIOS name of the computer.
1844 * @return 0 upon success, non-zero otherwise.
1847 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1850 LDAPMessage *res = NULL;
1852 const char *servicePrincipalName[1] = {NULL};
1853 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1854 char *dn_string = NULL;
1856 ret = ads_find_machine_acct(ads, &res, machine_name);
1857 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1858 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1859 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1860 ads_msgfree(ads, res);
1861 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1864 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1865 ctx = talloc_init("ads_clear_service_principal_names");
1867 ads_msgfree(ads, res);
1868 return ADS_ERROR(LDAP_NO_MEMORY);
1871 if (!(mods = ads_init_mods(ctx))) {
1872 talloc_destroy(ctx);
1873 ads_msgfree(ads, res);
1874 return ADS_ERROR(LDAP_NO_MEMORY);
1876 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1877 if (!ADS_ERR_OK(ret)) {
1878 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1879 ads_msgfree(ads, res);
1880 talloc_destroy(ctx);
1883 dn_string = ads_get_dn(ads, talloc_tos(), res);
1885 talloc_destroy(ctx);
1886 ads_msgfree(ads, res);
1887 return ADS_ERROR(LDAP_NO_MEMORY);
1889 ret = ads_gen_mod(ads, dn_string, mods);
1890 TALLOC_FREE(dn_string);
1891 if (!ADS_ERR_OK(ret)) {
1892 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1894 ads_msgfree(ads, res);
1895 talloc_destroy(ctx);
1899 ads_msgfree(ads, res);
1900 talloc_destroy(ctx);
1905 * @brief Search for an element in a string array.
1907 * @param[in] el_array The string array to search.
1909 * @param[in] num_el The number of elements in the string array.
1911 * @param[in] el The string to search.
1913 * @return True if found, false if not.
1915 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1919 if (el_array == NULL || num_el == 0 || el == NULL) {
1923 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1926 cmp = strcasecmp_m(el_array[i], el);
1936 * @brief This gets the service principal names of an existing computer account.
1938 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1940 * @param[in] ads The ADS context to use.
1942 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1943 * identify the computer account.
1945 * @param[in] spn_array A pointer to store the array for SPNs.
1947 * @param[in] num_spns The number of principals stored in the array.
1949 * @return 0 on success, or a ADS error if a failure occurred.
1951 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1953 const char *machine_name,
1958 LDAPMessage *res = NULL;
1961 status = ads_find_machine_acct(ads,
1964 if (!ADS_ERR_OK(status)) {
1965 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1970 count = ads_count_replies(ads, res);
1972 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1976 *spn_array = ads_pull_strings(ads,
1979 "servicePrincipalName",
1981 if (*spn_array == NULL) {
1982 DEBUG(1, ("Host account for %s does not have service principal "
1985 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1990 ads_msgfree(ads, res);
1996 * This adds a service principal name to an existing computer account
1997 * (found by hostname) in AD.
1998 * @param ads An initialized ADS_STRUCT
1999 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2000 * @param spns An array or strings for the service principals to add,
2001 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2002 * @return 0 upon sucess, or non-zero if a failure occurs
2005 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2006 const char *machine_name,
2011 LDAPMessage *res = NULL;
2013 char *dn_string = NULL;
2014 const char **servicePrincipalName = spns;
2016 ret = ads_find_machine_acct(ads, &res, machine_name);
2017 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2018 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2020 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2021 ads_msgfree(ads, res);
2022 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2025 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2026 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2027 ads_msgfree(ads, res);
2028 return ADS_ERROR(LDAP_NO_MEMORY);
2031 DEBUG(5,("ads_add_service_principal_name: INFO: "
2032 "Adding %s to host %s\n",
2033 spns[0] ? "N/A" : spns[0], machine_name));
2036 DEBUG(5,("ads_add_service_principal_name: INFO: "
2037 "Adding %s to host %s\n",
2038 spns[1] ? "N/A" : spns[1], machine_name));
2040 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2041 ret = ADS_ERROR(LDAP_NO_MEMORY);
2045 ret = ads_add_strlist(ctx,
2047 "servicePrincipalName",
2048 servicePrincipalName);
2049 if (!ADS_ERR_OK(ret)) {
2050 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2054 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2055 ret = ADS_ERROR(LDAP_NO_MEMORY);
2059 ret = ads_gen_mod(ads, dn_string, mods);
2060 if (!ADS_ERR_OK(ret)) {
2061 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2067 ads_msgfree(ads, res);
2072 * adds a machine account to the ADS server
2073 * @param ads An intialized ADS_STRUCT
2074 * @param machine_name - the NetBIOS machine name of this account.
2075 * @param account_type A number indicating the type of account to create
2076 * @param org_unit The LDAP path in which to place this account
2077 * @return 0 upon success, or non-zero otherwise
2080 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2081 const char *machine_name,
2082 const char *org_unit,
2083 uint32_t etype_list)
2086 char *samAccountName, *controlstr;
2089 char *machine_escaped = NULL;
2091 const char *objectClass[] = {"top", "person", "organizationalPerson",
2092 "user", "computer", NULL};
2093 LDAPMessage *res = NULL;
2094 uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2095 UF_DONT_EXPIRE_PASSWD |\
2096 UF_ACCOUNTDISABLE );
2097 uint32_t func_level = 0;
2099 ret = ads_domain_func_level(ads, &func_level);
2100 if (!ADS_ERR_OK(ret)) {
2104 if (!(ctx = talloc_init("ads_add_machine_acct")))
2105 return ADS_ERROR(LDAP_NO_MEMORY);
2107 ret = ADS_ERROR(LDAP_NO_MEMORY);
2109 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2110 if (!machine_escaped) {
2114 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2115 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2117 if ( !new_dn || !samAccountName ) {
2121 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2125 if (!(mods = ads_init_mods(ctx))) {
2129 ads_mod_str(ctx, &mods, "cn", machine_name);
2130 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2131 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2132 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2134 if (func_level >= DS_DOMAIN_FUNCTION_2008) {
2135 const char *etype_list_str;
2137 etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list);
2138 if (etype_list_str == NULL) {
2141 ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes",
2145 ret = ads_gen_add(ads, new_dn, mods);
2148 SAFE_FREE(machine_escaped);
2149 ads_msgfree(ads, res);
2150 talloc_destroy(ctx);
2156 * move a machine account to another OU on the ADS server
2157 * @param ads - An intialized ADS_STRUCT
2158 * @param machine_name - the NetBIOS machine name of this account.
2159 * @param org_unit - The LDAP path in which to place this account
2160 * @param moved - whether we moved the machine account (optional)
2161 * @return 0 upon success, or non-zero otherwise
2164 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2165 const char *org_unit, bool *moved)
2169 LDAPMessage *res = NULL;
2170 char *filter = NULL;
2171 char *computer_dn = NULL;
2173 char *computer_rdn = NULL;
2174 bool need_move = False;
2176 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2177 rc = ADS_ERROR(LDAP_NO_MEMORY);
2181 /* Find pre-existing machine */
2182 rc = ads_search(ads, &res, filter, NULL);
2183 if (!ADS_ERR_OK(rc)) {
2187 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2189 rc = ADS_ERROR(LDAP_NO_MEMORY);
2193 parent_dn = ads_parent_dn(computer_dn);
2194 if (strequal(parent_dn, org_unit)) {
2200 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2201 rc = ADS_ERROR(LDAP_NO_MEMORY);
2205 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2206 org_unit, 1, NULL, NULL);
2207 rc = ADS_ERROR(ldap_status);
2210 ads_msgfree(ads, res);
2212 TALLOC_FREE(computer_dn);
2213 SAFE_FREE(computer_rdn);
2215 if (!ADS_ERR_OK(rc)) {
2227 dump a binary result from ldap
2229 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2232 for (i=0; values[i]; i++) {
2233 printf("%s: ", field);
2234 for (j=0; j<values[i]->bv_len; j++) {
2235 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2241 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2244 for (i=0; values[i]; i++) {
2246 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2249 status = GUID_from_ndr_blob(&in, &guid);
2250 if (NT_STATUS_IS_OK(status)) {
2251 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2253 printf("%s: INVALID GUID\n", field);
2259 dump a sid result from ldap
2261 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2264 for (i=0; values[i]; i++) {
2267 if (!sid_parse((const uint8_t *)values[i]->bv_val,
2268 values[i]->bv_len, &sid)) {
2271 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2276 dump ntSecurityDescriptor
2278 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2280 TALLOC_CTX *frame = talloc_stackframe();
2281 struct security_descriptor *psd;
2284 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2285 values[0]->bv_len, &psd);
2286 if (!NT_STATUS_IS_OK(status)) {
2287 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2288 nt_errstr(status)));
2294 ads_disp_sd(ads, talloc_tos(), psd);
2301 dump a string result from ldap
2303 static void dump_string(const char *field, char **values)
2306 for (i=0; values[i]; i++) {
2307 printf("%s: %s\n", field, values[i]);
2312 dump a field from LDAP on stdout
2316 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2321 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2323 {"objectGUID", False, dump_guid},
2324 {"netbootGUID", False, dump_guid},
2325 {"nTSecurityDescriptor", False, dump_sd},
2326 {"dnsRecord", False, dump_binary},
2327 {"objectSid", False, dump_sid},
2328 {"tokenGroups", False, dump_sid},
2329 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2330 {"tokengroupsGlobalandUniversal", False, dump_sid},
2331 {"mS-DS-CreatorSID", False, dump_sid},
2332 {"msExchMailboxGuid", False, dump_guid},
2337 if (!field) { /* must be end of an entry */
2342 for (i=0; handlers[i].name; i++) {
2343 if (strcasecmp_m(handlers[i].name, field) == 0) {
2344 if (!values) /* first time, indicate string or not */
2345 return handlers[i].string;
2346 handlers[i].handler(ads, field, (struct berval **) values);
2350 if (!handlers[i].name) {
2351 if (!values) /* first time, indicate string conversion */
2353 dump_string(field, (char **)values);
2359 * Dump a result from LDAP on stdout
2360 * used for debugging
2361 * @param ads connection to ads server
2362 * @param res Results to dump
2365 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2367 ads_process_results(ads, res, ads_dump_field, NULL);
2371 * Walk through results, calling a function for each entry found.
2372 * The function receives a field name, a berval * array of values,
2373 * and a data area passed through from the start. The function is
2374 * called once with null for field and values at the end of each
2376 * @param ads connection to ads server
2377 * @param res Results to process
2378 * @param fn Function for processing each result
2379 * @param data_area user-defined area to pass to function
2381 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2382 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2387 size_t converted_size;
2389 if (!(ctx = talloc_init("ads_process_results")))
2392 for (msg = ads_first_entry(ads, res); msg;
2393 msg = ads_next_entry(ads, msg)) {
2397 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2398 (LDAPMessage *)msg,&b);
2400 utf8_field=ldap_next_attribute(ads->ldap.ld,
2401 (LDAPMessage *)msg,b)) {
2402 struct berval **ber_vals;
2408 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2411 DEBUG(0,("ads_process_results: "
2412 "pull_utf8_talloc failed: %s",
2416 string = fn(ads, field, NULL, data_area);
2421 utf8_vals = ldap_get_values(ads->ldap.ld,
2422 (LDAPMessage *)msg, field);
2423 p = discard_const_p(const char *, utf8_vals);
2424 str_vals = ads_pull_strvals(ctx, p);
2425 fn(ads, field, (void **) str_vals, data_area);
2426 ldap_value_free(utf8_vals);
2428 ber_vals = ldap_get_values_len(ads->ldap.ld,
2429 (LDAPMessage *)msg, field);
2430 fn(ads, field, (void **) ber_vals, data_area);
2432 ldap_value_free_len(ber_vals);
2434 ldap_memfree(utf8_field);
2437 talloc_free_children(ctx);
2438 fn(ads, NULL, NULL, data_area); /* completed an entry */
2441 talloc_destroy(ctx);
2445 * count how many replies are in a LDAPMessage
2446 * @param ads connection to ads server
2447 * @param res Results to count
2448 * @return number of replies
2450 int ads_count_replies(ADS_STRUCT *ads, void *res)
2452 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2456 * pull the first entry from a ADS result
2457 * @param ads connection to ads server
2458 * @param res Results of search
2459 * @return first entry from result
2461 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2463 return ldap_first_entry(ads->ldap.ld, res);
2467 * pull the next entry from a ADS result
2468 * @param ads connection to ads server
2469 * @param res Results of search
2470 * @return next entry from result
2472 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2474 return ldap_next_entry(ads->ldap.ld, res);
2478 * pull the first message from a ADS result
2479 * @param ads connection to ads server
2480 * @param res Results of search
2481 * @return first message from result
2483 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2485 return ldap_first_message(ads->ldap.ld, res);
2489 * pull the next message from a ADS result
2490 * @param ads connection to ads server
2491 * @param res Results of search
2492 * @return next message from result
2494 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2496 return ldap_next_message(ads->ldap.ld, res);
2500 * pull a single string from a ADS result
2501 * @param ads connection to ads server
2502 * @param mem_ctx TALLOC_CTX to use for allocating result string
2503 * @param msg Results of search
2504 * @param field Attribute to retrieve
2505 * @return Result string in talloc context
2507 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2513 size_t converted_size;
2515 values = ldap_get_values(ads->ldap.ld, msg, field);
2519 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2524 ldap_value_free(values);
2529 * pull an array of strings from a ADS result
2530 * @param ads connection to ads server
2531 * @param mem_ctx TALLOC_CTX to use for allocating result string
2532 * @param msg Results of search
2533 * @param field Attribute to retrieve
2534 * @return Result strings in talloc context
2536 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2537 LDAPMessage *msg, const char *field,
2543 size_t converted_size;
2545 values = ldap_get_values(ads->ldap.ld, msg, field);
2549 *num_values = ldap_count_values(values);
2551 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2553 ldap_value_free(values);
2557 for (i=0;i<*num_values;i++) {
2558 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2561 ldap_value_free(values);
2567 ldap_value_free(values);
2572 * pull an array of strings from a ADS result
2573 * (handle large multivalue attributes with range retrieval)
2574 * @param ads connection to ads server
2575 * @param mem_ctx TALLOC_CTX to use for allocating result string
2576 * @param msg Results of search
2577 * @param field Attribute to retrieve
2578 * @param current_strings strings returned by a previous call to this function
2579 * @param next_attribute The next query should ask for this attribute
2580 * @param num_values How many values did we get this time?
2581 * @param more_values Are there more values to get?
2582 * @return Result strings in talloc context
2584 char **ads_pull_strings_range(ADS_STRUCT *ads,
2585 TALLOC_CTX *mem_ctx,
2586 LDAPMessage *msg, const char *field,
2587 char **current_strings,
2588 const char **next_attribute,
2589 size_t *num_strings,
2593 char *expected_range_attrib, *range_attr;
2594 BerElement *ptr = NULL;
2597 size_t num_new_strings;
2598 unsigned long int range_start;
2599 unsigned long int range_end;
2601 /* we might have been given the whole lot anyway */
2602 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2603 *more_strings = False;
2607 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2609 /* look for Range result */
2610 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2612 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2613 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2614 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2622 /* nothing here - this field is just empty */
2623 *more_strings = False;
2627 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2628 &range_start, &range_end) == 2) {
2629 *more_strings = True;
2631 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2632 &range_start) == 1) {
2633 *more_strings = False;
2635 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2637 ldap_memfree(range_attr);
2638 *more_strings = False;
2643 if ((*num_strings) != range_start) {
2644 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2645 " - aborting range retreival\n",
2646 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2647 ldap_memfree(range_attr);
2648 *more_strings = False;
2652 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2654 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2655 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2656 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2657 range_attr, (unsigned long int)range_end - range_start + 1,
2658 (unsigned long int)num_new_strings));
2659 ldap_memfree(range_attr);
2660 *more_strings = False;
2664 strings = talloc_realloc(mem_ctx, current_strings, char *,
2665 *num_strings + num_new_strings);
2667 if (strings == NULL) {
2668 ldap_memfree(range_attr);
2669 *more_strings = False;
2673 if (new_strings && num_new_strings) {
2674 memcpy(&strings[*num_strings], new_strings,
2675 sizeof(*new_strings) * num_new_strings);
2678 (*num_strings) += num_new_strings;
2680 if (*more_strings) {
2681 *next_attribute = talloc_asprintf(mem_ctx,
2686 if (!*next_attribute) {
2687 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2688 ldap_memfree(range_attr);
2689 *more_strings = False;
2694 ldap_memfree(range_attr);
2700 * pull a single uint32_t from a ADS result
2701 * @param ads connection to ads server
2702 * @param msg Results of search
2703 * @param field Attribute to retrieve
2704 * @param v Pointer to int to store result
2705 * @return boolean inidicating success
2707 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2712 values = ldap_get_values(ads->ldap.ld, msg, field);
2716 ldap_value_free(values);
2720 *v = atoi(values[0]);
2721 ldap_value_free(values);
2726 * pull a single objectGUID from an ADS result
2727 * @param ads connection to ADS server
2728 * @param msg results of search
2729 * @param guid 37-byte area to receive text guid
2730 * @return boolean indicating success
2732 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2737 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2742 status = GUID_from_ndr_blob(&blob, guid);
2743 talloc_free(blob.data);
2744 return NT_STATUS_IS_OK(status);
2749 * pull a single struct dom_sid from a ADS result
2750 * @param ads connection to ads server
2751 * @param msg Results of search
2752 * @param field Attribute to retrieve
2753 * @param sid Pointer to sid to store result
2754 * @return boolean inidicating success
2756 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2757 struct dom_sid *sid)
2759 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2763 * pull an array of struct dom_sids from a ADS result
2764 * @param ads connection to ads server
2765 * @param mem_ctx TALLOC_CTX for allocating sid array
2766 * @param msg Results of search
2767 * @param field Attribute to retrieve
2768 * @param sids pointer to sid array to allocate
2769 * @return the count of SIDs pulled
2771 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2772 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2774 struct berval **values;
2778 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2783 for (i=0; values[i]; i++)
2787 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2789 ldap_value_free_len(values);
2797 for (i=0; values[i]; i++) {
2798 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2799 values[i]->bv_len, &(*sids)[count]);
2801 DEBUG(10, ("pulling SID: %s\n",
2802 sid_string_dbg(&(*sids)[count])));
2807 ldap_value_free_len(values);
2812 * pull a struct security_descriptor from a ADS result
2813 * @param ads connection to ads server
2814 * @param mem_ctx TALLOC_CTX for allocating sid array
2815 * @param msg Results of search
2816 * @param field Attribute to retrieve
2817 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2818 * @return boolean inidicating success
2820 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2821 LDAPMessage *msg, const char *field,
2822 struct security_descriptor **sd)
2824 struct berval **values;
2827 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2829 if (!values) return false;
2833 status = unmarshall_sec_desc(mem_ctx,
2834 (uint8_t *)values[0]->bv_val,
2835 values[0]->bv_len, sd);
2836 if (!NT_STATUS_IS_OK(status)) {
2837 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2838 nt_errstr(status)));
2843 ldap_value_free_len(values);
2848 * in order to support usernames longer than 21 characters we need to
2849 * use both the sAMAccountName and the userPrincipalName attributes
2850 * It seems that not all users have the userPrincipalName attribute set
2852 * @param ads connection to ads server
2853 * @param mem_ctx TALLOC_CTX for allocating sid array
2854 * @param msg Results of search
2855 * @return the username
2857 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2863 /* lookup_name() only works on the sAMAccountName to
2864 returning the username portion of userPrincipalName
2865 breaks winbindd_getpwnam() */
2867 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2868 if (ret && (p = strchr_m(ret, '@'))) {
2873 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2878 * find the update serial number - this is the core of the ldap cache
2879 * @param ads connection to ads server
2880 * @param ads connection to ADS server
2881 * @param usn Pointer to retrieved update serial number
2882 * @return status of search
2884 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2886 const char *attrs[] = {"highestCommittedUSN", NULL};
2890 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2891 if (!ADS_ERR_OK(status))
2894 if (ads_count_replies(ads, res) != 1) {
2895 ads_msgfree(ads, res);
2896 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2899 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2900 ads_msgfree(ads, res);
2901 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2904 ads_msgfree(ads, res);
2908 /* parse a ADS timestring - typical string is
2909 '20020917091222.0Z0' which means 09:12.22 17th September
2911 static time_t ads_parse_time(const char *str)
2917 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2918 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2919 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2928 /********************************************************************
2929 ********************************************************************/
2931 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2933 const char *attrs[] = {"currentTime", NULL};
2938 ADS_STRUCT *ads_s = ads;
2940 if (!(ctx = talloc_init("ads_current_time"))) {
2941 return ADS_ERROR(LDAP_NO_MEMORY);
2944 /* establish a new ldap tcp session if necessary */
2946 if ( !ads->ldap.ld ) {
2947 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2948 ads->server.ldap_server )) == NULL )
2950 status = ADS_ERROR(LDAP_NO_MEMORY);
2953 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2954 status = ads_connect( ads_s );
2955 if ( !ADS_ERR_OK(status))
2959 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2960 if (!ADS_ERR_OK(status)) {
2964 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2966 ads_msgfree(ads_s, res);
2967 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2971 /* but save the time and offset in the original ADS_STRUCT */
2973 ads->config.current_time = ads_parse_time(timestr);
2975 if (ads->config.current_time != 0) {
2976 ads->auth.time_offset = ads->config.current_time - time(NULL);
2977 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2980 ads_msgfree(ads, res);
2982 status = ADS_SUCCESS;
2985 /* free any temporary ads connections */
2986 if ( ads_s != ads ) {
2987 ads_destroy( &ads_s );
2989 talloc_destroy(ctx);
2994 /********************************************************************
2995 ********************************************************************/
2997 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
2999 const char *attrs[] = {"domainFunctionality", NULL};
3002 ADS_STRUCT *ads_s = ads;
3004 *val = DS_DOMAIN_FUNCTION_2000;
3006 /* establish a new ldap tcp session if necessary */
3008 if ( !ads->ldap.ld ) {
3009 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3010 ads->server.ldap_server )) == NULL )
3012 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3015 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3016 status = ads_connect( ads_s );
3017 if ( !ADS_ERR_OK(status))
3021 /* If the attribute does not exist assume it is a Windows 2000
3022 functional domain */
3024 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3025 if (!ADS_ERR_OK(status)) {
3026 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3027 status = ADS_SUCCESS;
3032 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3033 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3035 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3038 ads_msgfree(ads, res);
3041 /* free any temporary ads connections */
3042 if ( ads_s != ads ) {
3043 ads_destroy( &ads_s );
3050 * find the domain sid for our domain
3051 * @param ads connection to ads server
3052 * @param sid Pointer to domain sid
3053 * @return status of search
3055 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3057 const char *attrs[] = {"objectSid", NULL};
3061 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3063 if (!ADS_ERR_OK(rc)) return rc;
3064 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3065 ads_msgfree(ads, res);
3066 return ADS_ERROR_SYSTEM(ENOENT);
3068 ads_msgfree(ads, res);
3074 * find our site name
3075 * @param ads connection to ads server
3076 * @param mem_ctx Pointer to talloc context
3077 * @param site_name Pointer to the sitename
3078 * @return status of search
3080 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3084 const char *dn, *service_name;
3085 const char *attrs[] = { "dsServiceName", NULL };
3087 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3088 if (!ADS_ERR_OK(status)) {
3092 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3093 if (service_name == NULL) {
3094 ads_msgfree(ads, res);
3095 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3098 ads_msgfree(ads, res);
3100 /* go up three levels */
3101 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3103 return ADS_ERROR(LDAP_NO_MEMORY);
3106 *site_name = talloc_strdup(mem_ctx, dn);
3107 if (*site_name == NULL) {
3108 return ADS_ERROR(LDAP_NO_MEMORY);
3113 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3118 * find the site dn where a machine resides
3119 * @param ads connection to ads server
3120 * @param mem_ctx Pointer to talloc context
3121 * @param computer_name name of the machine
3122 * @param site_name Pointer to the sitename
3123 * @return status of search
3125 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3129 const char *parent, *filter;
3130 char *config_context = NULL;
3133 /* shortcut a query */
3134 if (strequal(computer_name, ads->config.ldap_server_name)) {
3135 return ads_site_dn(ads, mem_ctx, site_dn);
3138 status = ads_config_path(ads, mem_ctx, &config_context);
3139 if (!ADS_ERR_OK(status)) {
3143 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3144 if (filter == NULL) {
3145 return ADS_ERROR(LDAP_NO_MEMORY);
3148 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3149 filter, NULL, &res);
3150 if (!ADS_ERR_OK(status)) {
3154 if (ads_count_replies(ads, res) != 1) {
3155 ads_msgfree(ads, res);
3156 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3159 dn = ads_get_dn(ads, mem_ctx, res);
3161 ads_msgfree(ads, res);
3162 return ADS_ERROR(LDAP_NO_MEMORY);
3165 /* go up three levels */
3166 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3167 if (parent == NULL) {
3168 ads_msgfree(ads, res);
3170 return ADS_ERROR(LDAP_NO_MEMORY);
3173 *site_dn = talloc_strdup(mem_ctx, parent);
3174 if (*site_dn == NULL) {
3175 ads_msgfree(ads, res);
3177 return ADS_ERROR(LDAP_NO_MEMORY);
3181 ads_msgfree(ads, res);
3187 * get the upn suffixes for a domain
3188 * @param ads connection to ads server
3189 * @param mem_ctx Pointer to talloc context
3190 * @param suffixes Pointer to an array of suffixes
3191 * @param num_suffixes Pointer to the number of suffixes
3192 * @return status of search
3194 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3199 char *config_context = NULL;
3200 const char *attrs[] = { "uPNSuffixes", NULL };
3202 status = ads_config_path(ads, mem_ctx, &config_context);
3203 if (!ADS_ERR_OK(status)) {
3207 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3209 return ADS_ERROR(LDAP_NO_MEMORY);
3212 status = ads_search_dn(ads, &res, base, attrs);
3213 if (!ADS_ERR_OK(status)) {
3217 if (ads_count_replies(ads, res) != 1) {
3218 ads_msgfree(ads, res);
3219 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3222 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3223 if ((*suffixes) == NULL) {
3224 ads_msgfree(ads, res);
3225 return ADS_ERROR(LDAP_NO_MEMORY);
3228 ads_msgfree(ads, res);
3234 * get the joinable ous for a domain
3235 * @param ads connection to ads server
3236 * @param mem_ctx Pointer to talloc context
3237 * @param ous Pointer to an array of ous
3238 * @param num_ous Pointer to the number of ous
3239 * @return status of search
3241 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3242 TALLOC_CTX *mem_ctx,
3247 LDAPMessage *res = NULL;
3248 LDAPMessage *msg = NULL;
3249 const char *attrs[] = { "dn", NULL };
3252 status = ads_search(ads, &res,
3253 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3255 if (!ADS_ERR_OK(status)) {
3259 count = ads_count_replies(ads, res);
3261 ads_msgfree(ads, res);
3262 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3265 for (msg = ads_first_entry(ads, res); msg;
3266 msg = ads_next_entry(ads, msg)) {
3267 const char **p = discard_const_p(const char *, *ous);
3270 dn = ads_get_dn(ads, talloc_tos(), msg);
3272 ads_msgfree(ads, res);
3273 return ADS_ERROR(LDAP_NO_MEMORY);
3276 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3278 ads_msgfree(ads, res);
3279 return ADS_ERROR(LDAP_NO_MEMORY);
3283 *ous = discard_const_p(char *, p);
3286 ads_msgfree(ads, res);
3293 * pull a struct dom_sid from an extended dn string
3294 * @param mem_ctx TALLOC_CTX
3295 * @param extended_dn string
3296 * @param flags string type of extended_dn
3297 * @param sid pointer to a struct dom_sid
3298 * @return NT_STATUS_OK on success,
3299 * NT_INVALID_PARAMETER on error,
3300 * NT_STATUS_NOT_FOUND if no SID present
3302 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3303 const char *extended_dn,
3304 enum ads_extended_dn_flags flags,
3305 struct dom_sid *sid)
3310 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3313 /* otherwise extended_dn gets stripped off */
3314 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3315 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3318 * ADS_EXTENDED_DN_HEX_STRING:
3319 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3321 * ADS_EXTENDED_DN_STRING (only with w2k3):
3322 * <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
3324 * Object with no SID, such as an Exchange Public Folder
3325 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3328 p = strchr(dn, ';');
3330 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3333 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3334 DEBUG(5,("No SID present in extended dn\n"));
3335 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3338 p += strlen(";<SID=");
3342 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3347 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3351 case ADS_EXTENDED_DN_STRING:
3352 if (!string_to_sid(sid, p)) {
3353 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3356 case ADS_EXTENDED_DN_HEX_STRING: {
3360 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3362 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3365 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3366 DEBUG(10,("failed to parse sid\n"));
3367 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3372 DEBUG(10,("unknown extended dn format\n"));
3373 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3376 return ADS_ERROR_NT(NT_STATUS_OK);
3379 /********************************************************************
3380 ********************************************************************/
3382 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3384 LDAPMessage *res = NULL;
3389 status = ads_find_machine_acct(ads, &res, machine_name);
3390 if (!ADS_ERR_OK(status)) {
3391 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3392 lp_netbios_name()));
3396 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3397 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3401 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3402 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3406 ads_msgfree(ads, res);
3411 /********************************************************************
3412 ********************************************************************/
3414 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3416 LDAPMessage *res = NULL;
3421 status = ads_find_machine_acct(ads, &res, machine_name);
3422 if (!ADS_ERR_OK(status)) {
3423 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3424 lp_netbios_name()));
3428 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3429 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3433 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3434 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3438 ads_msgfree(ads, res);
3443 /********************************************************************
3444 ********************************************************************/
3446 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3448 LDAPMessage *res = NULL;
3454 status = ads_find_machine_acct(ads, &res, machine_name);
3455 if (!ADS_ERR_OK(status)) {
3456 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3457 lp_netbios_name()));
3461 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3462 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3466 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3467 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3471 ads_msgfree(ads, res);
3473 ok = (strlen(name) > 0);
3481 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3484 * Join a machine to a realm
3485 * Creates the machine account and sets the machine password
3486 * @param ads connection to ads server
3487 * @param machine name of host to add
3488 * @param org_unit Organizational unit to place machine in
3489 * @return status of join
3491 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3492 uint32_t account_type, const char *org_unit)
3495 LDAPMessage *res = NULL;
3498 /* machine name must be lowercase */
3499 machine = SMB_STRDUP(machine_name);
3500 strlower_m(machine);
3503 status = ads_find_machine_acct(ads, (void **)&res, machine);
3504 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3505 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3506 status = ads_leave_realm(ads, machine);
3507 if (!ADS_ERR_OK(status)) {
3508 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3509 machine, ads->config.realm));
3514 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3515 if (!ADS_ERR_OK(status)) {
3516 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3521 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3522 if (!ADS_ERR_OK(status)) {
3523 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3529 ads_msgfree(ads, res);
3536 * Delete a machine from the realm
3537 * @param ads connection to ads server
3538 * @param hostname Machine to remove
3539 * @return status of delete
3541 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3546 char *hostnameDN, *host;
3548 LDAPControl ldap_control;
3549 LDAPControl * pldap_control[2] = {NULL, NULL};
3551 pldap_control[0] = &ldap_control;
3552 memset(&ldap_control, 0, sizeof(LDAPControl));
3553 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3555 /* hostname must be lowercase */
3556 host = SMB_STRDUP(hostname);
3557 if (!strlower_m(host)) {
3559 return ADS_ERROR_SYSTEM(EINVAL);
3562 status = ads_find_machine_acct(ads, &res, host);
3563 if (!ADS_ERR_OK(status)) {
3564 DEBUG(0, ("Host account for %s does not exist.\n", host));
3569 msg = ads_first_entry(ads, res);
3572 return ADS_ERROR_SYSTEM(ENOENT);
3575 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3576 if (hostnameDN == NULL) {
3578 return ADS_ERROR_SYSTEM(ENOENT);
3581 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3583 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3585 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3588 if (rc != LDAP_SUCCESS) {
3589 const char *attrs[] = { "cn", NULL };
3590 LDAPMessage *msg_sub;
3592 /* we only search with scope ONE, we do not expect any further
3593 * objects to be created deeper */
3595 status = ads_do_search_retry(ads, hostnameDN,
3596 LDAP_SCOPE_ONELEVEL,
3597 "(objectclass=*)", attrs, &res);
3599 if (!ADS_ERR_OK(status)) {
3601 TALLOC_FREE(hostnameDN);
3605 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3606 msg_sub = ads_next_entry(ads, msg_sub)) {
3610 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3612 TALLOC_FREE(hostnameDN);
3613 return ADS_ERROR(LDAP_NO_MEMORY);
3616 status = ads_del_dn(ads, dn);
3617 if (!ADS_ERR_OK(status)) {
3618 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3621 TALLOC_FREE(hostnameDN);
3628 /* there should be no subordinate objects anymore */
3629 status = ads_do_search_retry(ads, hostnameDN,
3630 LDAP_SCOPE_ONELEVEL,
3631 "(objectclass=*)", attrs, &res);
3633 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3635 TALLOC_FREE(hostnameDN);
3639 /* delete hostnameDN now */
3640 status = ads_del_dn(ads, hostnameDN);
3641 if (!ADS_ERR_OK(status)) {
3643 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3644 TALLOC_FREE(hostnameDN);
3649 TALLOC_FREE(hostnameDN);
3651 status = ads_find_machine_acct(ads, &res, host);
3652 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3653 DEBUG(3, ("Failed to remove host account.\n"));
3663 * pull all token-sids from an LDAP dn
3664 * @param ads connection to ads server
3665 * @param mem_ctx TALLOC_CTX for allocating sid array
3666 * @param dn of LDAP object
3667 * @param user_sid pointer to struct dom_sid (objectSid)
3668 * @param primary_group_sid pointer to struct dom_sid (self composed)
3669 * @param sids pointer to sid array to allocate
3670 * @param num_sids counter of SIDs pulled
3671 * @return status of token query
3673 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3674 TALLOC_CTX *mem_ctx,
3676 struct dom_sid *user_sid,
3677 struct dom_sid *primary_group_sid,
3678 struct dom_sid **sids,
3682 LDAPMessage *res = NULL;
3684 size_t tmp_num_sids;
3685 struct dom_sid *tmp_sids;
3686 struct dom_sid tmp_user_sid;
3687 struct dom_sid tmp_primary_group_sid;
3689 const char *attrs[] = {
3696 status = ads_search_retry_dn(ads, &res, dn, attrs);
3697 if (!ADS_ERR_OK(status)) {
3701 count = ads_count_replies(ads, res);
3703 ads_msgfree(ads, res);
3704 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3707 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3708 ads_msgfree(ads, res);
3709 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3712 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3713 ads_msgfree(ads, res);
3714 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3718 /* hack to compose the primary group sid without knowing the
3721 struct dom_sid domsid;
3723 sid_copy(&domsid, &tmp_user_sid);
3725 if (!sid_split_rid(&domsid, NULL)) {
3726 ads_msgfree(ads, res);
3727 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3730 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3731 ads_msgfree(ads, res);
3732 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3736 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3738 if (tmp_num_sids == 0 || !tmp_sids) {
3739 ads_msgfree(ads, res);
3740 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3744 *num_sids = tmp_num_sids;
3752 *user_sid = tmp_user_sid;
3755 if (primary_group_sid) {
3756 *primary_group_sid = tmp_primary_group_sid;
3759 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3761 ads_msgfree(ads, res);
3762 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3766 * Find a sAMAccoutName in LDAP
3767 * @param ads connection to ads server
3768 * @param mem_ctx TALLOC_CTX for allocating sid array
3769 * @param samaccountname to search
3770 * @param uac_ret uint32_t pointer userAccountControl attribute value
3771 * @param dn_ret pointer to dn
3772 * @return status of token query
3774 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3775 TALLOC_CTX *mem_ctx,
3776 const char *samaccountname,
3778 const char **dn_ret)
3781 const char *attrs[] = { "userAccountControl", NULL };
3783 LDAPMessage *res = NULL;
3787 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3789 if (filter == NULL) {
3790 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3794 status = ads_do_search_all(ads, ads->config.bind_path,
3796 filter, attrs, &res);
3798 if (!ADS_ERR_OK(status)) {
3802 if (ads_count_replies(ads, res) != 1) {
3803 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3807 dn = ads_get_dn(ads, talloc_tos(), res);
3809 status = ADS_ERROR(LDAP_NO_MEMORY);
3813 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3814 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3823 *dn_ret = talloc_strdup(mem_ctx, dn);
3825 status = ADS_ERROR(LDAP_NO_MEMORY);
3831 ads_msgfree(ads, res);
3837 * find our configuration path
3838 * @param ads connection to ads server
3839 * @param mem_ctx Pointer to talloc context
3840 * @param config_path Pointer to the config path
3841 * @return status of search
3843 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3844 TALLOC_CTX *mem_ctx,
3848 LDAPMessage *res = NULL;
3849 const char *config_context = NULL;
3850 const char *attrs[] = { "configurationNamingContext", NULL };
3852 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3853 "(objectclass=*)", attrs, &res);
3854 if (!ADS_ERR_OK(status)) {
3858 config_context = ads_pull_string(ads, mem_ctx, res,
3859 "configurationNamingContext");
3860 ads_msgfree(ads, res);
3861 if (!config_context) {
3862 return ADS_ERROR(LDAP_NO_MEMORY);
3866 *config_path = talloc_strdup(mem_ctx, config_context);
3867 if (!*config_path) {
3868 return ADS_ERROR(LDAP_NO_MEMORY);
3872 return ADS_ERROR(LDAP_SUCCESS);
3876 * find the displayName of an extended right
3877 * @param ads connection to ads server
3878 * @param config_path The config path
3879 * @param mem_ctx Pointer to talloc context
3880 * @param GUID struct of the rightsGUID
3881 * @return status of search
3883 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3884 const char *config_path,
3885 TALLOC_CTX *mem_ctx,
3886 const struct GUID *rights_guid)
3889 LDAPMessage *res = NULL;
3891 const char *attrs[] = { "displayName", NULL };
3892 const char *result = NULL;
3895 if (!ads || !mem_ctx || !rights_guid) {
3899 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3900 GUID_string(mem_ctx, rights_guid));
3905 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3910 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3912 if (!ADS_ERR_OK(rc)) {
3916 if (ads_count_replies(ads, res) != 1) {
3920 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3923 ads_msgfree(ads, res);
3928 * verify or build and verify an account ou
3929 * @param mem_ctx Pointer to talloc context
3930 * @param ads connection to ads server
3932 * @return status of search
3935 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3937 const char **account_ou)
3943 if (account_ou == NULL) {
3944 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3947 if (*account_ou != NULL) {
3948 exploded_dn = ldap_explode_dn(*account_ou, 0);
3950 ldap_value_free(exploded_dn);
3955 ou_string = ads_ou_string(ads, *account_ou);
3957 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3960 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3961 ads->config.bind_path);
3962 SAFE_FREE(ou_string);
3965 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3968 exploded_dn = ldap_explode_dn(name, 0);
3970 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3972 ldap_value_free(exploded_dn);