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"
34 #include "libsmb/namequery.h"
40 * @brief basic ldap client-side routines for ads server communications
42 * The routines contained here should do the necessary ldap calls for
45 * Important note: attribute names passed into ads_ routines must
46 * already be in UTF-8 format. We do not convert them because in almost
47 * all cases, they are just ascii (which is represented with the same
48 * codepoints in UTF-8). This may have to change at some point
52 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
54 static SIG_ATOMIC_T gotalarm;
56 /***************************************************************
57 Signal function to tell us we timed out.
58 ****************************************************************/
60 static void gotalarm_sig(int signum)
65 LDAP *ldap_open_with_timeout(const char *server,
66 struct sockaddr_storage *ss,
67 int port, unsigned int to)
73 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
74 "%u seconds\n", server, port, to));
79 CatchSignal(SIGALRM, gotalarm_sig);
81 /* End setup timeout. */
84 if ( strchr_m(server, ':') ) {
86 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
89 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
95 #ifdef HAVE_LDAP_INIT_FD
98 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
100 status = open_socket_out(ss, port, to, &fd);
101 if (!NT_STATUS_IS_OK(status)) {
105 /* define LDAP_PROTO_TCP from openldap.h if required */
106 #ifndef LDAP_PROTO_TCP
107 #define LDAP_PROTO_TCP 1
109 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
111 #elif defined(HAVE_LDAP_INITIALIZE)
112 ldap_err = ldap_initialize(&ldp, uri);
114 ldp = ldap_open(server, port);
116 ldap_err = LDAP_SUCCESS;
118 ldap_err = LDAP_OTHER;
121 if (ldap_err != LDAP_SUCCESS) {
122 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
123 uri, ldap_err2string(ldap_err)));
125 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
129 /* Teardown timeout. */
131 CatchSignal(SIGALRM, SIG_IGN);
137 static int ldap_search_with_timeout(LDAP *ld,
138 LDAP_CONST char *base,
140 LDAP_CONST char *filter,
143 LDAPControl **sctrls,
144 LDAPControl **cctrls,
148 int to = lp_ldap_timeout();
149 struct timeval timeout;
150 struct timeval *timeout_ptr = NULL;
153 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
159 timeout_ptr = &timeout;
161 /* Setup alarm timeout. */
162 CatchSignal(SIGALRM, gotalarm_sig);
163 /* Make the alarm time one second beyond
164 the timout we're setting for the
165 remote search timeout, to allow that
166 to fire in preference. */
168 /* End setup timeout. */
172 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
173 attrsonly, sctrls, cctrls, timeout_ptr,
177 /* Teardown alarm timeout. */
178 CatchSignal(SIGALRM, SIG_IGN);
183 return LDAP_TIMELIMIT_EXCEEDED;
186 * A bug in OpenLDAP means ldap_search_ext_s can return
187 * LDAP_SUCCESS but with a NULL res pointer. Cope with
188 * this. See bug #6279 for details. JRA.
192 return LDAP_TIMELIMIT_EXCEEDED;
198 /**********************************************
199 Do client and server sitename match ?
200 **********************************************/
202 bool ads_sitename_match(ADS_STRUCT *ads)
204 if (ads->config.server_site_name == NULL &&
205 ads->config.client_site_name == NULL ) {
206 DEBUG(10,("ads_sitename_match: both null\n"));
209 if (ads->config.server_site_name &&
210 ads->config.client_site_name &&
211 strequal(ads->config.server_site_name,
212 ads->config.client_site_name)) {
213 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
216 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
217 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
218 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
222 /**********************************************
223 Is this the closest DC ?
224 **********************************************/
226 bool ads_closest_dc(ADS_STRUCT *ads)
228 if (ads->config.flags & NBT_SERVER_CLOSEST) {
229 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
233 /* not sure if this can ever happen */
234 if (ads_sitename_match(ads)) {
235 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
239 if (ads->config.client_site_name == NULL) {
240 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
244 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
245 ads->config.ldap_server_name));
252 try a connection to a given ldap server, returning True and setting the servers IP
253 in the ads struct if successful
255 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
256 struct sockaddr_storage *ss)
258 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
259 TALLOC_CTX *frame = talloc_stackframe();
261 char addr[INET6_ADDRSTRLEN];
268 print_sockaddr(addr, sizeof(addr), ss);
270 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
271 addr, ads->server.realm));
273 ZERO_STRUCT( cldap_reply );
275 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
276 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
281 /* Check the CLDAP reply flags */
283 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
284 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
290 /* Fill in the ads->config values */
292 SAFE_FREE(ads->config.realm);
293 SAFE_FREE(ads->config.bind_path);
294 SAFE_FREE(ads->config.ldap_server_name);
295 SAFE_FREE(ads->config.server_site_name);
296 SAFE_FREE(ads->config.client_site_name);
297 SAFE_FREE(ads->server.workgroup);
299 if (!check_cldap_reply_required_flags(cldap_reply.server_type,
300 ads->config.flags)) {
305 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
306 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
307 if (!strupper_m(ads->config.realm)) {
312 ads->config.bind_path = ads_build_dn(ads->config.realm);
313 if (*cldap_reply.server_site) {
314 ads->config.server_site_name =
315 SMB_STRDUP(cldap_reply.server_site);
317 if (*cldap_reply.client_site) {
318 ads->config.client_site_name =
319 SMB_STRDUP(cldap_reply.client_site);
321 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
323 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
326 /* Store our site name. */
327 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
328 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
330 /* Leave this until last so that the flags are not clobbered */
331 ads->config.flags = cldap_reply.server_type;
341 /**********************************************************************
342 send a cldap ping to list of servers, one at a time, until one of
343 them answers it's an ldap server. Record success in the ADS_STRUCT.
344 Take note of and update negative connection cache.
345 **********************************************************************/
347 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,const char *domain,
348 struct ip_service *ip_list, int count)
353 for (i = 0; i < count; i++) {
354 char server[INET6_ADDRSTRLEN];
356 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
358 if (!NT_STATUS_IS_OK(
359 check_negative_conn_cache(domain, server)))
362 /* Returns ok only if it matches the correct server type */
363 ok = ads_try_connect(ads, false, &ip_list[i].ss);
369 /* keep track of failures */
370 add_failed_connection_entry(domain, server,
371 NT_STATUS_UNSUCCESSFUL);
374 return NT_STATUS_NO_LOGON_SERVERS;
377 /***************************************************************************
378 resolve a name and perform an "ldap ping" using NetBIOS and related methods
379 ****************************************************************************/
381 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
382 const char *domain, const char *realm)
385 struct ip_service *ip_list;
388 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
391 status = get_sorted_dc_list(domain, NULL, &ip_list, &count,
393 if (!NT_STATUS_IS_OK(status)) {
397 /* remove servers which are known to be dead based on
398 the corresponding DNS method */
400 for (i = 0; i < count; ++i) {
401 char server[INET6_ADDRSTRLEN];
403 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
406 check_negative_conn_cache(realm, server))) {
407 /* Ensure we add the workgroup name for this
408 IP address as negative too. */
409 add_failed_connection_entry(
411 NT_STATUS_UNSUCCESSFUL);
416 status = cldap_ping_list(ads, domain, ip_list, count);
424 /**********************************************************************
425 resolve a name and perform an "ldap ping" using DNS
426 **********************************************************************/
428 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
432 struct ip_service *ip_list = NULL;
435 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
438 status = get_sorted_dc_list(realm, sitename, &ip_list, &count,
440 if (!NT_STATUS_IS_OK(status)) {
445 status = cldap_ping_list(ads, realm, ip_list, count);
452 /**********************************************************************
453 Try to find an AD dc using our internal name resolution routines
454 Try the realm first and then then workgroup name if netbios is not
456 **********************************************************************/
458 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
460 const char *c_domain = "";
462 bool use_own_domain = False;
463 char *sitename = NULL;
464 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
467 /* if the realm and workgroup are both empty, assume they are ours */
470 c_realm = ads->server.realm;
476 /* special case where no realm and no workgroup means our own */
477 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
478 use_own_domain = True;
479 c_realm = lp_realm();
483 if (!lp_disable_netbios()) {
484 if (use_own_domain) {
485 c_domain = lp_workgroup();
487 c_domain = ads->server.workgroup;
488 if (!*c_realm && (!c_domain || !*c_domain)) {
489 c_domain = lp_workgroup();
498 if (!*c_realm && !*c_domain) {
499 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
501 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
505 * In case of LDAP we use get_dc_name() as that
506 * creates the custom krb5.conf file
508 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
510 struct sockaddr_storage ip_out;
512 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
513 " and falling back to domain '%s'\n",
516 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
519 * we call ads_try_connect() to fill in the
520 * ads->config details
522 ok = ads_try_connect(ads, false, &ip_out);
528 return NT_STATUS_NO_LOGON_SERVERS;
532 sitename = sitename_fetch(talloc_tos(), c_realm);
533 status = resolve_and_ping_dns(ads, sitename, c_realm);
535 if (NT_STATUS_IS_OK(status)) {
536 TALLOC_FREE(sitename);
540 /* In case we failed to contact one of our closest DC on our
542 * need to try to find another DC, retry with a site-less SRV
547 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
548 "our site (%s), Trying to find another DC "
549 "for realm '%s' (domain '%s')\n",
550 sitename, c_realm, c_domain));
551 namecache_delete(c_realm, 0x1C);
553 resolve_and_ping_dns(ads, NULL, c_realm);
555 if (NT_STATUS_IS_OK(status)) {
556 TALLOC_FREE(sitename);
561 TALLOC_FREE(sitename);
564 /* try netbios as fallback - if permitted,
565 or if configuration specifically requests it */
568 DEBUG(3, ("ads_find_dc: falling back to netbios "
569 "name resolution for domain '%s' (realm '%s')\n",
573 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
574 if (NT_STATUS_IS_OK(status)) {
579 DEBUG(1, ("ads_find_dc: "
580 "name resolution for realm '%s' (domain '%s') failed: %s\n",
581 c_realm, c_domain, nt_errstr(status)));
585 * Connect to the LDAP server
586 * @param ads Pointer to an existing ADS_STRUCT
587 * @return status of connection
589 ADS_STATUS ads_connect(ADS_STRUCT *ads)
591 int version = LDAP_VERSION3;
594 char addr[INET6_ADDRSTRLEN];
596 ZERO_STRUCT(ads->ldap);
597 ZERO_STRUCT(ads->ldap_wrap_data);
598 ads->ldap.last_attempt = time_mono(NULL);
599 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
601 /* try with a user specified server */
603 if (DEBUGLEVEL >= 11) {
604 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
605 DEBUG(11,("ads_connect: entering\n"));
606 DEBUGADD(11,("%s\n", s));
610 if (ads->server.ldap_server) {
612 struct sockaddr_storage ss;
614 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
616 DEBUG(5,("ads_connect: unable to resolve name %s\n",
617 ads->server.ldap_server));
618 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
621 ok = ads_try_connect(ads, ads->server.gc, &ss);
626 /* The choice of which GC use is handled one level up in
627 ads_connect_gc(). If we continue on from here with
628 ads_find_dc() we will get GC searches on port 389 which
629 doesn't work. --jerry */
631 if (ads->server.gc == true) {
632 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
635 if (ads->server.no_fallback) {
636 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
641 ntstatus = ads_find_dc(ads);
642 if (NT_STATUS_IS_OK(ntstatus)) {
646 status = ADS_ERROR_NT(ntstatus);
651 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
652 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
654 if (!ads->auth.user_name) {
655 /* Must use the userPrincipalName value here or sAMAccountName
656 and not servicePrincipalName; found by Guenther Deschner */
658 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
659 DEBUG(0,("ads_connect: asprintf fail.\n"));
660 ads->auth.user_name = NULL;
664 if (!ads->auth.realm) {
665 ads->auth.realm = SMB_STRDUP(ads->config.realm);
668 if (!ads->auth.kdc_server) {
669 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
670 ads->auth.kdc_server = SMB_STRDUP(addr);
673 /* If the caller() requested no LDAP bind, then we are done */
675 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
676 status = ADS_SUCCESS;
680 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
681 if (!ads->ldap_wrap_data.mem_ctx) {
682 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
686 /* Otherwise setup the TCP LDAP session */
688 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
690 ads->ldap.port, lp_ldap_timeout());
691 if (ads->ldap.ld == NULL) {
692 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
695 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
697 /* cache the successful connection for workgroup and realm */
698 if (ads_closest_dc(ads)) {
699 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
700 saf_store( ads->server.realm, ads->config.ldap_server_name);
703 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
705 if ( lp_ldap_ssl_ads() ) {
706 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
707 if (!ADS_ERR_OK(status)) {
712 /* fill in the current time and offsets */
714 status = ads_current_time( ads );
715 if ( !ADS_ERR_OK(status) ) {
719 /* Now do the bind */
721 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
722 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
726 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
727 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
731 status = ads_sasl_bind(ads);
734 if (DEBUGLEVEL >= 11) {
735 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
736 DEBUG(11,("ads_connect: leaving with: %s\n",
737 ads_errstr(status)));
738 DEBUGADD(11,("%s\n", s));
746 * Connect to the LDAP server using given credentials
747 * @param ads Pointer to an existing ADS_STRUCT
748 * @return status of connection
750 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
752 ads->auth.flags |= ADS_AUTH_USER_CREDS;
754 return ads_connect(ads);
758 * Disconnect the LDAP server
759 * @param ads Pointer to an existing ADS_STRUCT
761 void ads_disconnect(ADS_STRUCT *ads)
764 ldap_unbind(ads->ldap.ld);
767 if (ads->ldap_wrap_data.wrap_ops &&
768 ads->ldap_wrap_data.wrap_ops->disconnect) {
769 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
771 if (ads->ldap_wrap_data.mem_ctx) {
772 talloc_free(ads->ldap_wrap_data.mem_ctx);
774 ZERO_STRUCT(ads->ldap);
775 ZERO_STRUCT(ads->ldap_wrap_data);
779 Duplicate a struct berval into talloc'ed memory
781 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
783 struct berval *value;
785 if (!in_val) return NULL;
787 value = talloc_zero(ctx, struct berval);
790 if (in_val->bv_len == 0) return value;
792 value->bv_len = in_val->bv_len;
793 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
799 Make a values list out of an array of (struct berval *)
801 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
802 const struct berval **in_vals)
804 struct berval **values;
807 if (!in_vals) return NULL;
808 for (i=0; in_vals[i]; i++)
810 values = talloc_zero_array(ctx, struct berval *, i+1);
811 if (!values) return NULL;
813 for (i=0; in_vals[i]; i++) {
814 values[i] = dup_berval(ctx, in_vals[i]);
820 UTF8-encode a values list out of an array of (char *)
822 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
828 if (!in_vals) return NULL;
829 for (i=0; in_vals[i]; i++)
831 values = talloc_zero_array(ctx, char *, i+1);
832 if (!values) return NULL;
834 for (i=0; in_vals[i]; i++) {
835 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
844 Pull a (char *) array out of a UTF8-encoded values list
846 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
850 size_t converted_size;
852 if (!in_vals) return NULL;
853 for (i=0; in_vals[i]; i++)
855 values = talloc_zero_array(ctx, char *, i+1);
856 if (!values) return NULL;
858 for (i=0; in_vals[i]; i++) {
859 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
861 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
862 "%s", strerror(errno)));
869 * Do a search with paged results. cookie must be null on the first
870 * call, and then returned on each subsequent call. It will be null
871 * again when the entire search is complete
872 * @param ads connection to ads server
873 * @param bind_path Base dn for the search
874 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
875 * @param expr Search expression - specified in local charset
876 * @param attrs Attributes to retrieve - specified in utf8 or ascii
877 * @param res ** which will contain results - free res* with ads_msgfree()
878 * @param count Number of entries retrieved on this page
879 * @param cookie The paged results cookie to be returned on subsequent calls
880 * @return status of search
882 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
883 const char *bind_path,
884 int scope, const char *expr,
885 const char **attrs, void *args,
887 int *count, struct berval **cookie)
890 char *utf8_expr, *utf8_path, **search_attrs = NULL;
891 size_t converted_size;
892 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
893 BerElement *cookie_be = NULL;
894 struct berval *cookie_bv= NULL;
895 BerElement *ext_be = NULL;
896 struct berval *ext_bv= NULL;
899 ads_control *external_control = (ads_control *) args;
903 if (!(ctx = talloc_init("ads_do_paged_search_args")))
904 return ADS_ERROR(LDAP_NO_MEMORY);
906 /* 0 means the conversion worked but the result was empty
907 so we only fail if it's -1. In any case, it always
908 at least nulls out the dest */
909 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
910 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
916 if (!attrs || !(*attrs))
919 /* This would be the utf8-encoded version...*/
920 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
921 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
927 /* Paged results only available on ldap v3 or later */
928 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
929 if (version < LDAP_VERSION3) {
930 rc = LDAP_NOT_SUPPORTED;
934 cookie_be = ber_alloc_t(LBER_USE_DER);
936 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
937 ber_bvfree(*cookie); /* don't need it from last time */
940 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
942 ber_flatten(cookie_be, &cookie_bv);
943 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
944 PagedResults.ldctl_iscritical = (char) 1;
945 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
946 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
948 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
949 NoReferrals.ldctl_iscritical = (char) 0;
950 NoReferrals.ldctl_value.bv_len = 0;
951 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
953 if (external_control &&
954 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
955 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
957 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
958 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
960 /* win2k does not accept a ldctl_value beeing passed in */
962 if (external_control->val != 0) {
964 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
969 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
973 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
978 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
979 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
982 ExternalCtrl.ldctl_value.bv_len = 0;
983 ExternalCtrl.ldctl_value.bv_val = NULL;
986 controls[0] = &NoReferrals;
987 controls[1] = &PagedResults;
988 controls[2] = &ExternalCtrl;
992 controls[0] = &NoReferrals;
993 controls[1] = &PagedResults;
997 /* we need to disable referrals as the openldap libs don't
998 handle them and paged results at the same time. Using them
999 together results in the result record containing the server
1000 page control being removed from the result list (tridge/jmcd)
1002 leaving this in despite the control that says don't generate
1003 referrals, in case the server doesn't support it (jmcd)
1005 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1007 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1008 search_attrs, 0, controls,
1009 NULL, LDAP_NO_LIMIT,
1010 (LDAPMessage **)res);
1012 ber_free(cookie_be, 1);
1013 ber_bvfree(cookie_bv);
1016 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1017 ldap_err2string(rc)));
1018 if (rc == LDAP_OTHER) {
1022 ret = ldap_parse_result(ads->ldap.ld,
1030 if (ret == LDAP_SUCCESS) {
1031 DEBUG(3, ("ldap_search_with_timeout(%s) "
1032 "error: %s\n", expr, ldap_errmsg));
1033 ldap_memfree(ldap_errmsg);
1039 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1040 NULL, &rcontrols, 0);
1046 for (i=0; rcontrols[i]; i++) {
1047 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1048 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1049 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1051 /* the berval is the cookie, but must be freed when
1053 if (cookie_bv->bv_len) /* still more to do */
1054 *cookie=ber_bvdup(cookie_bv);
1057 ber_bvfree(cookie_bv);
1058 ber_free(cookie_be, 1);
1062 ldap_controls_free(rcontrols);
1065 talloc_destroy(ctx);
1068 ber_free(ext_be, 1);
1075 if (rc != LDAP_SUCCESS && *res != NULL) {
1076 ads_msgfree(ads, *res);
1080 /* if/when we decide to utf8-encode attrs, take out this next line */
1081 TALLOC_FREE(search_attrs);
1083 return ADS_ERROR(rc);
1086 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1087 int scope, const char *expr,
1088 const char **attrs, LDAPMessage **res,
1089 int *count, struct berval **cookie)
1091 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1096 * Get all results for a search. This uses ads_do_paged_search() to return
1097 * all entries in a large search.
1098 * @param ads connection to ads server
1099 * @param bind_path Base dn for the search
1100 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1101 * @param expr Search expression
1102 * @param attrs Attributes to retrieve
1103 * @param res ** which will contain results - free res* with ads_msgfree()
1104 * @return status of search
1106 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1107 int scope, const char *expr,
1108 const char **attrs, void *args,
1111 struct berval *cookie = NULL;
1116 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1119 if (!ADS_ERR_OK(status))
1122 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1124 LDAPMessage *res2 = NULL;
1125 LDAPMessage *msg, *next;
1127 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1128 attrs, args, &res2, &count, &cookie);
1129 if (!ADS_ERR_OK(status)) {
1133 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1134 that this works on all ldap libs, but I have only tested with openldap */
1135 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1136 next = ads_next_message(ads, msg);
1137 ldap_add_result_entry((LDAPMessage **)res, msg);
1139 /* note that we do not free res2, as the memory is now
1140 part of the main returned list */
1143 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1144 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1150 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1151 int scope, const char *expr,
1152 const char **attrs, LDAPMessage **res)
1154 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1157 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1158 int scope, const char *expr,
1159 const char **attrs, uint32_t sd_flags,
1164 args.control = ADS_SD_FLAGS_OID;
1165 args.val = sd_flags;
1166 args.critical = True;
1168 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1173 * Run a function on all results for a search. Uses ads_do_paged_search() and
1174 * runs the function as each page is returned, using ads_process_results()
1175 * @param ads connection to ads server
1176 * @param bind_path Base dn for the search
1177 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1178 * @param expr Search expression - specified in local charset
1179 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1180 * @param fn Function which takes attr name, values list, and data_area
1181 * @param data_area Pointer which is passed to function on each call
1182 * @return status of search
1184 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1185 int scope, const char *expr, const char **attrs,
1186 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1189 struct berval *cookie = NULL;
1194 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1197 if (!ADS_ERR_OK(status)) return status;
1199 ads_process_results(ads, res, fn, data_area);
1200 ads_msgfree(ads, res);
1203 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1204 &res, &count, &cookie);
1206 if (!ADS_ERR_OK(status)) break;
1208 ads_process_results(ads, res, fn, data_area);
1209 ads_msgfree(ads, res);
1216 * Do a search with a timeout.
1217 * @param ads connection to ads server
1218 * @param bind_path Base dn for the search
1219 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1220 * @param expr Search expression
1221 * @param attrs Attributes to retrieve
1222 * @param res ** which will contain results - free res* with ads_msgfree()
1223 * @return status of search
1225 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1227 const char **attrs, LDAPMessage **res)
1230 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1231 size_t converted_size;
1235 if (!(ctx = talloc_init("ads_do_search"))) {
1236 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1237 return ADS_ERROR(LDAP_NO_MEMORY);
1240 /* 0 means the conversion worked but the result was empty
1241 so we only fail if it's negative. In any case, it always
1242 at least nulls out the dest */
1243 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1244 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1246 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1247 rc = LDAP_NO_MEMORY;
1251 if (!attrs || !(*attrs))
1252 search_attrs = NULL;
1254 /* This would be the utf8-encoded version...*/
1255 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1256 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1258 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1259 rc = LDAP_NO_MEMORY;
1264 /* see the note in ads_do_paged_search - we *must* disable referrals */
1265 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1267 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1268 search_attrs, 0, NULL, NULL,
1270 (LDAPMessage **)res);
1272 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1273 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1278 talloc_destroy(ctx);
1279 /* if/when we decide to utf8-encode attrs, take out this next line */
1280 TALLOC_FREE(search_attrs);
1281 return ADS_ERROR(rc);
1284 * Do a general ADS search
1285 * @param ads connection to ads server
1286 * @param res ** which will contain results - free res* with ads_msgfree()
1287 * @param expr Search expression
1288 * @param attrs Attributes to retrieve
1289 * @return status of search
1291 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1292 const char *expr, const char **attrs)
1294 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1299 * Do a search on a specific DistinguishedName
1300 * @param ads connection to ads server
1301 * @param res ** which will contain results - free res* with ads_msgfree()
1302 * @param dn DistinguishName to search
1303 * @param attrs Attributes to retrieve
1304 * @return status of search
1306 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1307 const char *dn, const char **attrs)
1309 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1314 * Free up memory from a ads_search
1315 * @param ads connection to ads server
1316 * @param msg Search results to free
1318 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1325 * Get a dn from search results
1326 * @param ads connection to ads server
1327 * @param msg Search result
1330 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1332 char *utf8_dn, *unix_dn;
1333 size_t converted_size;
1335 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1338 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1342 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1343 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1347 ldap_memfree(utf8_dn);
1352 * Get the parent from a dn
1353 * @param dn the dn to return the parent from
1354 * @return parent dn string
1356 char *ads_parent_dn(const char *dn)
1364 p = strchr(dn, ',');
1374 * Find a machine account given a hostname
1375 * @param ads connection to ads server
1376 * @param res ** which will contain results - free res* with ads_msgfree()
1377 * @param host Hostname to search for
1378 * @return status of search
1380 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1381 const char *machine)
1385 const char *attrs[] = {
1386 /* This is how Windows checks for machine accounts */
1389 "userAccountControl",
1391 "ServicePrincipalName",
1392 "userPrincipalName",
1395 /* Additional attributes Samba checks */
1396 "msDS-AdditionalDnsHostName",
1397 "msDS-SupportedEncryptionTypes",
1398 "nTSecurityDescriptor",
1402 TALLOC_CTX *frame = talloc_stackframe();
1406 /* the easiest way to find a machine account anywhere in the tree
1407 is to look for hostname$ */
1408 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1410 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1414 status = ads_search(ads, res, expr, attrs);
1415 if (ADS_ERR_OK(status)) {
1416 if (ads_count_replies(ads, *res) != 1) {
1417 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1427 * Initialize a list of mods to be used in a modify request
1428 * @param ctx An initialized TALLOC_CTX
1429 * @return allocated ADS_MODLIST
1431 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1433 #define ADS_MODLIST_ALLOC_SIZE 10
1436 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1437 /* -1 is safety to make sure we don't go over the end.
1438 need to reset it to NULL before doing ldap modify */
1439 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1441 return (ADS_MODLIST)mods;
1446 add an attribute to the list, with values list already constructed
1448 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1449 int mod_op, const char *name,
1450 const void *_invals)
1453 LDAPMod **modlist = (LDAPMod **) *mods;
1454 struct berval **ber_values = NULL;
1455 char **char_values = NULL;
1458 mod_op = LDAP_MOD_DELETE;
1460 if (mod_op & LDAP_MOD_BVALUES) {
1461 const struct berval **b;
1462 b = discard_const_p(const struct berval *, _invals);
1463 ber_values = ads_dup_values(ctx, b);
1466 c = discard_const_p(const char *, _invals);
1467 char_values = ads_push_strvals(ctx, c);
1471 /* find the first empty slot */
1472 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1474 if (modlist[curmod] == (LDAPMod *) -1) {
1475 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1476 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1477 return ADS_ERROR(LDAP_NO_MEMORY);
1478 memset(&modlist[curmod], 0,
1479 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1480 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1481 *mods = (ADS_MODLIST)modlist;
1484 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1485 return ADS_ERROR(LDAP_NO_MEMORY);
1486 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1487 if (mod_op & LDAP_MOD_BVALUES) {
1488 modlist[curmod]->mod_bvalues = ber_values;
1489 } else if (mod_op & LDAP_MOD_DELETE) {
1490 modlist[curmod]->mod_values = NULL;
1492 modlist[curmod]->mod_values = char_values;
1495 modlist[curmod]->mod_op = mod_op;
1496 return ADS_ERROR(LDAP_SUCCESS);
1500 * Add a single string value to a mod list
1501 * @param ctx An initialized TALLOC_CTX
1502 * @param mods An initialized ADS_MODLIST
1503 * @param name The attribute name to add
1504 * @param val The value to add - NULL means DELETE
1505 * @return ADS STATUS indicating success of add
1507 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1508 const char *name, const char *val)
1510 const char *values[2];
1516 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1517 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1521 * Add an array of string values to a mod list
1522 * @param ctx An initialized TALLOC_CTX
1523 * @param mods An initialized ADS_MODLIST
1524 * @param name The attribute name to add
1525 * @param vals The array of string values to add - NULL means DELETE
1526 * @return ADS STATUS indicating success of add
1528 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1529 const char *name, const char **vals)
1532 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1533 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1534 name, (const void **) vals);
1538 * Add a single ber-encoded value to a mod list
1539 * @param ctx An initialized TALLOC_CTX
1540 * @param mods An initialized ADS_MODLIST
1541 * @param name The attribute name to add
1542 * @param val The value to add - NULL means DELETE
1543 * @return ADS STATUS indicating success of add
1545 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1546 const char *name, const struct berval *val)
1548 const struct berval *values[2];
1553 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1554 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1555 name, (const void **) values);
1558 static void ads_print_error(int ret, LDAP *ld)
1561 char *ld_error = NULL;
1562 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1563 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1565 ldap_err2string(ret),
1567 SAFE_FREE(ld_error);
1572 * Perform an ldap modify
1573 * @param ads connection to ads server
1574 * @param mod_dn DistinguishedName to modify
1575 * @param mods list of modifications to perform
1576 * @return status of modify
1578 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1581 char *utf8_dn = NULL;
1582 size_t converted_size;
1584 this control is needed to modify that contains a currently
1585 non-existent attribute (but allowable for the object) to run
1587 LDAPControl PermitModify = {
1588 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1591 LDAPControl *controls[2];
1593 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1595 controls[0] = &PermitModify;
1598 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1599 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1602 /* find the end of the list, marked by NULL or -1 */
1603 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1604 /* make sure the end of the list is NULL */
1606 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1607 (LDAPMod **) mods, controls, NULL);
1608 ads_print_error(ret, ads->ldap.ld);
1609 TALLOC_FREE(utf8_dn);
1610 return ADS_ERROR(ret);
1614 * Perform an ldap add
1615 * @param ads connection to ads server
1616 * @param new_dn DistinguishedName to add
1617 * @param mods list of attributes and values for DN
1618 * @return status of add
1620 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1623 char *utf8_dn = NULL;
1624 size_t converted_size;
1626 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1628 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1629 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1630 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1633 /* find the end of the list, marked by NULL or -1 */
1634 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1635 /* make sure the end of the list is NULL */
1638 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1639 ads_print_error(ret, ads->ldap.ld);
1640 TALLOC_FREE(utf8_dn);
1641 return ADS_ERROR(ret);
1645 * Delete a DistinguishedName
1646 * @param ads connection to ads server
1647 * @param new_dn DistinguishedName to delete
1648 * @return status of delete
1650 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1653 char *utf8_dn = NULL;
1654 size_t converted_size;
1655 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1656 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1657 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1660 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1662 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1663 ads_print_error(ret, ads->ldap.ld);
1664 TALLOC_FREE(utf8_dn);
1665 return ADS_ERROR(ret);
1669 * Build an org unit string
1670 * if org unit is Computers or blank then assume a container, otherwise
1671 * assume a / separated list of organisational units.
1672 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1673 * @param ads connection to ads server
1674 * @param org_unit Organizational unit
1675 * @return org unit string - caller must free
1677 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1681 if (!org_unit || !*org_unit) {
1683 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1685 /* samba4 might not yet respond to a wellknownobject-query */
1686 return ret ? ret : SMB_STRDUP("cn=Computers");
1689 if (strequal(org_unit, "Computers")) {
1690 return SMB_STRDUP("cn=Computers");
1693 /* jmcd: removed "\\" from the separation chars, because it is
1694 needed as an escape for chars like '#' which are valid in an
1696 return ads_build_path(org_unit, "/", "ou=", 1);
1700 * Get a org unit string for a well-known GUID
1701 * @param ads connection to ads server
1702 * @param wknguid Well known GUID
1703 * @return org unit string - caller must free
1705 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1708 LDAPMessage *res = NULL;
1709 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1710 **bind_dn_exp = NULL;
1711 const char *attrs[] = {"distinguishedName", NULL};
1712 int new_ln, wkn_ln, bind_ln, i;
1714 if (wknguid == NULL) {
1718 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1719 DEBUG(1, ("asprintf failed!\n"));
1723 status = ads_search_dn(ads, &res, base, attrs);
1724 if (!ADS_ERR_OK(status)) {
1725 DEBUG(1,("Failed while searching for: %s\n", base));
1729 if (ads_count_replies(ads, res) != 1) {
1733 /* substitute the bind-path from the well-known-guid-search result */
1734 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1739 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1744 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1749 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1751 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1754 new_ln = wkn_ln - bind_ln;
1756 ret = SMB_STRDUP(wkn_dn_exp[0]);
1761 for (i=1; i < new_ln; i++) {
1764 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1770 ret = SMB_STRDUP(s);
1779 ads_msgfree(ads, res);
1780 TALLOC_FREE(wkn_dn);
1782 ldap_value_free(wkn_dn_exp);
1785 ldap_value_free(bind_dn_exp);
1792 * Adds (appends) an item to an attribute array, rather then
1793 * replacing the whole list
1794 * @param ctx An initialized TALLOC_CTX
1795 * @param mods An initialized ADS_MODLIST
1796 * @param name name of the ldap attribute to append to
1797 * @param vals an array of values to add
1798 * @return status of addition
1801 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1802 const char *name, const char **vals)
1804 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1805 (const void *) vals);
1809 * Determines the an account's current KVNO via an LDAP lookup
1810 * @param ads An initialized ADS_STRUCT
1811 * @param account_name the NT samaccountname.
1812 * @return the kvno for the account, or -1 in case of a failure.
1815 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1817 LDAPMessage *res = NULL;
1818 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1820 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1821 char *dn_string = NULL;
1824 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1825 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1828 ret = ads_search(ads, &res, filter, attrs);
1830 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1831 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1832 ads_msgfree(ads, res);
1836 dn_string = ads_get_dn(ads, talloc_tos(), res);
1838 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1839 ads_msgfree(ads, res);
1842 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1843 TALLOC_FREE(dn_string);
1845 /* ---------------------------------------------------------
1846 * 0 is returned as a default KVNO from this point on...
1847 * This is done because Windows 2000 does not support key
1848 * version numbers. Chances are that a failure in the next
1849 * step is simply due to Windows 2000 being used for a
1850 * domain controller. */
1853 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1854 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1855 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1856 ads_msgfree(ads, res);
1861 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1862 ads_msgfree(ads, res);
1867 * Determines the computer account's current KVNO via an LDAP lookup
1868 * @param ads An initialized ADS_STRUCT
1869 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1870 * @return the kvno for the computer account, or -1 in case of a failure.
1873 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1875 char *computer_account = NULL;
1878 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1882 kvno = ads_get_kvno(ads, computer_account);
1883 free(computer_account);
1889 * This clears out all registered spn's for a given hostname
1890 * @param ads An initilaized ADS_STRUCT
1891 * @param machine_name the NetBIOS name of the computer.
1892 * @return 0 upon success, non-zero otherwise.
1895 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1898 LDAPMessage *res = NULL;
1900 const char *servicePrincipalName[1] = {NULL};
1902 char *dn_string = NULL;
1904 ret = ads_find_machine_acct(ads, &res, machine_name);
1905 if (!ADS_ERR_OK(ret)) {
1906 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1907 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1908 ads_msgfree(ads, res);
1912 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1913 ctx = talloc_init("ads_clear_service_principal_names");
1915 ads_msgfree(ads, res);
1916 return ADS_ERROR(LDAP_NO_MEMORY);
1919 if (!(mods = ads_init_mods(ctx))) {
1920 talloc_destroy(ctx);
1921 ads_msgfree(ads, res);
1922 return ADS_ERROR(LDAP_NO_MEMORY);
1924 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1925 if (!ADS_ERR_OK(ret)) {
1926 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1927 ads_msgfree(ads, res);
1928 talloc_destroy(ctx);
1931 dn_string = ads_get_dn(ads, talloc_tos(), res);
1933 talloc_destroy(ctx);
1934 ads_msgfree(ads, res);
1935 return ADS_ERROR(LDAP_NO_MEMORY);
1937 ret = ads_gen_mod(ads, dn_string, mods);
1938 TALLOC_FREE(dn_string);
1939 if (!ADS_ERR_OK(ret)) {
1940 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1942 ads_msgfree(ads, res);
1943 talloc_destroy(ctx);
1947 ads_msgfree(ads, res);
1948 talloc_destroy(ctx);
1953 * @brief Search for an element in a string array.
1955 * @param[in] el_array The string array to search.
1957 * @param[in] num_el The number of elements in the string array.
1959 * @param[in] el The string to search.
1961 * @return True if found, false if not.
1963 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1967 if (el_array == NULL || num_el == 0 || el == NULL) {
1971 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1974 cmp = strcasecmp_m(el_array[i], el);
1984 * @brief This gets the service principal names of an existing computer account.
1986 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1988 * @param[in] ads The ADS context to use.
1990 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1991 * identify the computer account.
1993 * @param[in] spn_array A pointer to store the array for SPNs.
1995 * @param[in] num_spns The number of principals stored in the array.
1997 * @return 0 on success, or a ADS error if a failure occurred.
1999 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2001 const char *machine_name,
2006 LDAPMessage *res = NULL;
2009 status = ads_find_machine_acct(ads,
2012 if (!ADS_ERR_OK(status)) {
2013 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2018 count = ads_count_replies(ads, res);
2020 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2024 *spn_array = ads_pull_strings(ads,
2027 "servicePrincipalName",
2029 if (*spn_array == NULL) {
2030 DEBUG(1, ("Host account for %s does not have service principal "
2033 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2038 ads_msgfree(ads, res);
2044 * This adds a service principal name to an existing computer account
2045 * (found by hostname) in AD.
2046 * @param ads An initialized ADS_STRUCT
2047 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2048 * @param spns An array or strings for the service principals to add,
2049 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2050 * @return 0 upon sucess, or non-zero if a failure occurs
2053 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2054 const char *machine_name,
2059 LDAPMessage *res = NULL;
2061 char *dn_string = NULL;
2062 const char **servicePrincipalName = spns;
2064 ret = ads_find_machine_acct(ads, &res, machine_name);
2065 if (!ADS_ERR_OK(ret)) {
2066 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2068 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2069 ads_msgfree(ads, res);
2073 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2074 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2075 ads_msgfree(ads, res);
2076 return ADS_ERROR(LDAP_NO_MEMORY);
2079 DEBUG(5,("ads_add_service_principal_name: INFO: "
2080 "Adding %s to host %s\n",
2081 spns[0] ? "N/A" : spns[0], machine_name));
2084 DEBUG(5,("ads_add_service_principal_name: INFO: "
2085 "Adding %s to host %s\n",
2086 spns[1] ? "N/A" : spns[1], machine_name));
2088 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2089 ret = ADS_ERROR(LDAP_NO_MEMORY);
2093 ret = ads_add_strlist(ctx,
2095 "servicePrincipalName",
2096 servicePrincipalName);
2097 if (!ADS_ERR_OK(ret)) {
2098 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2102 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2103 ret = ADS_ERROR(LDAP_NO_MEMORY);
2107 ret = ads_gen_mod(ads, dn_string, mods);
2108 if (!ADS_ERR_OK(ret)) {
2109 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2115 ads_msgfree(ads, res);
2119 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2122 uint32_t acct_ctrl = 0;
2125 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2133 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2135 const struct berval *machine_pw_val)
2139 TALLOC_CTX *frame = talloc_stackframe();
2140 uint32_t acct_control;
2141 char *control_str = NULL;
2142 const char *attrs[] = {
2146 LDAPMessage *res = NULL;
2149 dn = ads_get_dn(ads, frame, msg);
2151 ret = ADS_ERROR(LDAP_NO_MEMORY);
2155 acct_control = ads_get_acct_ctrl(ads, msg);
2156 if (acct_control == 0) {
2157 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2162 * Changing the password, disables the account. So we need to change the
2163 * userAccountControl flags to enable it again.
2165 mods = ads_init_mods(frame);
2167 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2171 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2173 ret = ads_gen_mod(ads, dn, mods);
2174 if (!ADS_ERR_OK(ret)) {
2180 * To activate the account, we need to disable and enable it.
2182 acct_control |= UF_ACCOUNTDISABLE;
2184 control_str = talloc_asprintf(frame, "%u", acct_control);
2185 if (control_str == NULL) {
2186 ret = ADS_ERROR(LDAP_NO_MEMORY);
2190 mods = ads_init_mods(frame);
2192 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2196 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2198 ret = ads_gen_mod(ads, dn, mods);
2199 if (!ADS_ERR_OK(ret)) {
2203 TALLOC_FREE(control_str);
2206 * Enable the account again.
2208 acct_control &= ~UF_ACCOUNTDISABLE;
2210 control_str = talloc_asprintf(frame, "%u", acct_control);
2211 if (control_str == NULL) {
2212 ret = ADS_ERROR(LDAP_NO_MEMORY);
2216 mods = ads_init_mods(frame);
2218 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2222 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2224 ret = ads_gen_mod(ads, dn, mods);
2225 if (!ADS_ERR_OK(ret)) {
2229 TALLOC_FREE(control_str);
2231 ret = ads_search_dn(ads, &res, dn, attrs);
2232 ads_msgfree(ads, res);
2241 * adds a machine account to the ADS server
2242 * @param ads An intialized ADS_STRUCT
2243 * @param machine_name - the NetBIOS machine name of this account.
2244 * @param account_type A number indicating the type of account to create
2245 * @param org_unit The LDAP path in which to place this account
2246 * @return 0 upon success, or non-zero otherwise
2249 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2250 const char *machine_name,
2251 const char *machine_password,
2252 const char *org_unit,
2253 uint32_t etype_list,
2254 const char *dns_domain_name)
2257 char *samAccountName = NULL;
2258 char *controlstr = NULL;
2259 TALLOC_CTX *ctx = NULL;
2261 char *machine_escaped = NULL;
2262 char *dns_hostname = NULL;
2263 char *new_dn = NULL;
2264 char *utf8_pw = NULL;
2265 size_t utf8_pw_len = 0;
2266 char *utf16_pw = NULL;
2267 size_t utf16_pw_len = 0;
2268 struct berval machine_pw_val;
2270 const char **spn_array = NULL;
2271 size_t num_spns = 0;
2272 const char *spn_prefix[] = {
2274 "RestrictedKrbHost",
2277 LDAPMessage *res = NULL;
2278 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2280 ctx = talloc_init("ads_add_machine_acct");
2282 return ADS_ERROR(LDAP_NO_MEMORY);
2285 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2286 if (machine_escaped == NULL) {
2287 ret = ADS_ERROR(LDAP_NO_MEMORY);
2291 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2292 if (utf8_pw == NULL) {
2293 ret = ADS_ERROR(LDAP_NO_MEMORY);
2296 utf8_pw_len = strlen(utf8_pw);
2298 ok = convert_string_talloc(ctx,
2299 CH_UTF8, CH_UTF16MUNGED,
2300 utf8_pw, utf8_pw_len,
2301 (void *)&utf16_pw, &utf16_pw_len);
2303 ret = ADS_ERROR(LDAP_NO_MEMORY);
2307 machine_pw_val = (struct berval) {
2309 .bv_len = utf16_pw_len,
2312 /* Check if the machine account already exists. */
2313 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2314 if (ADS_ERR_OK(ret)) {
2315 /* Change the machine account password */
2316 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2317 ads_msgfree(ads, res);
2321 ads_msgfree(ads, res);
2323 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2324 if (new_dn == NULL) {
2325 ret = ADS_ERROR(LDAP_NO_MEMORY);
2329 /* Create machine account */
2331 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2332 if (samAccountName == NULL) {
2333 ret = ADS_ERROR(LDAP_NO_MEMORY);
2337 dns_hostname = talloc_asprintf(ctx,
2341 if (dns_hostname == NULL) {
2342 ret = ADS_ERROR(LDAP_NO_MEMORY);
2346 /* Add dns_hostname SPNs */
2347 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2348 char *spn = talloc_asprintf(ctx,
2353 ret = ADS_ERROR(LDAP_NO_MEMORY);
2357 ok = add_string_to_array(spn_array,
2362 ret = ADS_ERROR(LDAP_NO_MEMORY);
2367 /* Add machine_name SPNs */
2368 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2369 char *spn = talloc_asprintf(ctx,
2374 ret = ADS_ERROR(LDAP_NO_MEMORY);
2378 ok = add_string_to_array(spn_array,
2383 ret = ADS_ERROR(LDAP_NO_MEMORY);
2388 /* Make sure to NULL terminate the array */
2389 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2390 if (spn_array == NULL) {
2391 ret = ADS_ERROR(LDAP_NO_MEMORY);
2394 spn_array[num_spns] = NULL;
2396 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2397 if (controlstr == NULL) {
2398 ret = ADS_ERROR(LDAP_NO_MEMORY);
2402 mods = ads_init_mods(ctx);
2404 ret = ADS_ERROR(LDAP_NO_MEMORY);
2408 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2409 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2410 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2411 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2412 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2413 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2415 ret = ads_gen_add(ads, new_dn, mods);
2418 SAFE_FREE(machine_escaped);
2419 talloc_destroy(ctx);
2425 * move a machine account to another OU on the ADS server
2426 * @param ads - An intialized ADS_STRUCT
2427 * @param machine_name - the NetBIOS machine name of this account.
2428 * @param org_unit - The LDAP path in which to place this account
2429 * @param moved - whether we moved the machine account (optional)
2430 * @return 0 upon success, or non-zero otherwise
2433 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2434 const char *org_unit, bool *moved)
2438 LDAPMessage *res = NULL;
2439 char *filter = NULL;
2440 char *computer_dn = NULL;
2442 char *computer_rdn = NULL;
2443 bool need_move = False;
2445 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2446 rc = ADS_ERROR(LDAP_NO_MEMORY);
2450 /* Find pre-existing machine */
2451 rc = ads_search(ads, &res, filter, NULL);
2452 if (!ADS_ERR_OK(rc)) {
2456 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2458 rc = ADS_ERROR(LDAP_NO_MEMORY);
2462 parent_dn = ads_parent_dn(computer_dn);
2463 if (strequal(parent_dn, org_unit)) {
2469 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2470 rc = ADS_ERROR(LDAP_NO_MEMORY);
2474 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2475 org_unit, 1, NULL, NULL);
2476 rc = ADS_ERROR(ldap_status);
2479 ads_msgfree(ads, res);
2481 TALLOC_FREE(computer_dn);
2482 SAFE_FREE(computer_rdn);
2484 if (!ADS_ERR_OK(rc)) {
2496 dump a binary result from ldap
2498 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2501 for (i=0; values[i]; i++) {
2503 printf("%s: ", field);
2504 for (j=0; j<values[i]->bv_len; j++) {
2505 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2511 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2514 for (i=0; values[i]; i++) {
2516 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2519 status = GUID_from_ndr_blob(&in, &guid);
2520 if (NT_STATUS_IS_OK(status)) {
2521 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2523 printf("%s: INVALID GUID\n", field);
2529 dump a sid result from ldap
2531 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2534 for (i=0; values[i]; i++) {
2537 struct dom_sid_buf tmp;
2538 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2539 values[i]->bv_len, &sid);
2543 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2548 dump ntSecurityDescriptor
2550 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2552 TALLOC_CTX *frame = talloc_stackframe();
2553 struct security_descriptor *psd;
2556 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2557 values[0]->bv_len, &psd);
2558 if (!NT_STATUS_IS_OK(status)) {
2559 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2560 nt_errstr(status)));
2566 ads_disp_sd(ads, talloc_tos(), psd);
2573 dump a string result from ldap
2575 static void dump_string(const char *field, char **values)
2578 for (i=0; values[i]; i++) {
2579 printf("%s: %s\n", field, values[i]);
2584 dump a field from LDAP on stdout
2588 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2593 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2595 {"objectGUID", False, dump_guid},
2596 {"netbootGUID", False, dump_guid},
2597 {"nTSecurityDescriptor", False, dump_sd},
2598 {"dnsRecord", False, dump_binary},
2599 {"objectSid", False, dump_sid},
2600 {"tokenGroups", False, dump_sid},
2601 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2602 {"tokengroupsGlobalandUniversal", False, dump_sid},
2603 {"mS-DS-CreatorSID", False, dump_sid},
2604 {"msExchMailboxGuid", False, dump_guid},
2609 if (!field) { /* must be end of an entry */
2614 for (i=0; handlers[i].name; i++) {
2615 if (strcasecmp_m(handlers[i].name, field) == 0) {
2616 if (!values) /* first time, indicate string or not */
2617 return handlers[i].string;
2618 handlers[i].handler(ads, field, (struct berval **) values);
2622 if (!handlers[i].name) {
2623 if (!values) /* first time, indicate string conversion */
2625 dump_string(field, (char **)values);
2631 * Dump a result from LDAP on stdout
2632 * used for debugging
2633 * @param ads connection to ads server
2634 * @param res Results to dump
2637 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2639 ads_process_results(ads, res, ads_dump_field, NULL);
2643 * Walk through results, calling a function for each entry found.
2644 * The function receives a field name, a berval * array of values,
2645 * and a data area passed through from the start. The function is
2646 * called once with null for field and values at the end of each
2648 * @param ads connection to ads server
2649 * @param res Results to process
2650 * @param fn Function for processing each result
2651 * @param data_area user-defined area to pass to function
2653 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2654 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2659 size_t converted_size;
2661 if (!(ctx = talloc_init("ads_process_results")))
2664 for (msg = ads_first_entry(ads, res); msg;
2665 msg = ads_next_entry(ads, msg)) {
2669 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2670 (LDAPMessage *)msg,&b);
2672 utf8_field=ldap_next_attribute(ads->ldap.ld,
2673 (LDAPMessage *)msg,b)) {
2674 struct berval **ber_vals;
2680 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2683 DEBUG(0,("ads_process_results: "
2684 "pull_utf8_talloc failed: %s",
2688 string = fn(ads, field, NULL, data_area);
2693 utf8_vals = ldap_get_values(ads->ldap.ld,
2694 (LDAPMessage *)msg, field);
2695 p = discard_const_p(const char *, utf8_vals);
2696 str_vals = ads_pull_strvals(ctx, p);
2697 fn(ads, field, (void **) str_vals, data_area);
2698 ldap_value_free(utf8_vals);
2700 ber_vals = ldap_get_values_len(ads->ldap.ld,
2701 (LDAPMessage *)msg, field);
2702 fn(ads, field, (void **) ber_vals, data_area);
2704 ldap_value_free_len(ber_vals);
2706 ldap_memfree(utf8_field);
2709 talloc_free_children(ctx);
2710 fn(ads, NULL, NULL, data_area); /* completed an entry */
2713 talloc_destroy(ctx);
2717 * count how many replies are in a LDAPMessage
2718 * @param ads connection to ads server
2719 * @param res Results to count
2720 * @return number of replies
2722 int ads_count_replies(ADS_STRUCT *ads, void *res)
2724 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2728 * pull the first entry from a ADS result
2729 * @param ads connection to ads server
2730 * @param res Results of search
2731 * @return first entry from result
2733 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2735 return ldap_first_entry(ads->ldap.ld, res);
2739 * pull the next entry from a ADS result
2740 * @param ads connection to ads server
2741 * @param res Results of search
2742 * @return next entry from result
2744 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2746 return ldap_next_entry(ads->ldap.ld, res);
2750 * pull the first message from a ADS result
2751 * @param ads connection to ads server
2752 * @param res Results of search
2753 * @return first message from result
2755 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2757 return ldap_first_message(ads->ldap.ld, res);
2761 * pull the next message from a ADS result
2762 * @param ads connection to ads server
2763 * @param res Results of search
2764 * @return next message from result
2766 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2768 return ldap_next_message(ads->ldap.ld, res);
2772 * pull a single string from a ADS result
2773 * @param ads connection to ads server
2774 * @param mem_ctx TALLOC_CTX to use for allocating result string
2775 * @param msg Results of search
2776 * @param field Attribute to retrieve
2777 * @return Result string in talloc context
2779 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2785 size_t converted_size;
2787 values = ldap_get_values(ads->ldap.ld, msg, field);
2791 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2796 ldap_value_free(values);
2801 * pull an array of strings from a ADS result
2802 * @param ads connection to ads server
2803 * @param mem_ctx TALLOC_CTX to use for allocating result string
2804 * @param msg Results of search
2805 * @param field Attribute to retrieve
2806 * @return Result strings in talloc context
2808 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2809 LDAPMessage *msg, const char *field,
2814 size_t i, converted_size;
2816 values = ldap_get_values(ads->ldap.ld, msg, field);
2820 *num_values = ldap_count_values(values);
2822 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2824 ldap_value_free(values);
2828 for (i=0;i<*num_values;i++) {
2829 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2832 ldap_value_free(values);
2838 ldap_value_free(values);
2843 * pull an array of strings from a ADS result
2844 * (handle large multivalue attributes with range retrieval)
2845 * @param ads connection to ads server
2846 * @param mem_ctx TALLOC_CTX to use for allocating result string
2847 * @param msg Results of search
2848 * @param field Attribute to retrieve
2849 * @param current_strings strings returned by a previous call to this function
2850 * @param next_attribute The next query should ask for this attribute
2851 * @param num_values How many values did we get this time?
2852 * @param more_values Are there more values to get?
2853 * @return Result strings in talloc context
2855 char **ads_pull_strings_range(ADS_STRUCT *ads,
2856 TALLOC_CTX *mem_ctx,
2857 LDAPMessage *msg, const char *field,
2858 char **current_strings,
2859 const char **next_attribute,
2860 size_t *num_strings,
2864 char *expected_range_attrib, *range_attr;
2865 BerElement *ptr = NULL;
2868 size_t num_new_strings;
2869 unsigned long int range_start;
2870 unsigned long int range_end;
2872 /* we might have been given the whole lot anyway */
2873 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2874 *more_strings = False;
2878 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2880 /* look for Range result */
2881 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2883 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2884 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2885 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2893 /* nothing here - this field is just empty */
2894 *more_strings = False;
2898 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2899 &range_start, &range_end) == 2) {
2900 *more_strings = True;
2902 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2903 &range_start) == 1) {
2904 *more_strings = False;
2906 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2908 ldap_memfree(range_attr);
2909 *more_strings = False;
2914 if ((*num_strings) != range_start) {
2915 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2916 " - aborting range retreival\n",
2917 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2918 ldap_memfree(range_attr);
2919 *more_strings = False;
2923 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2925 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2926 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2927 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2928 range_attr, (unsigned long int)range_end - range_start + 1,
2929 (unsigned long int)num_new_strings));
2930 ldap_memfree(range_attr);
2931 *more_strings = False;
2935 strings = talloc_realloc(mem_ctx, current_strings, char *,
2936 *num_strings + num_new_strings);
2938 if (strings == NULL) {
2939 ldap_memfree(range_attr);
2940 *more_strings = False;
2944 if (new_strings && num_new_strings) {
2945 memcpy(&strings[*num_strings], new_strings,
2946 sizeof(*new_strings) * num_new_strings);
2949 (*num_strings) += num_new_strings;
2951 if (*more_strings) {
2952 *next_attribute = talloc_asprintf(mem_ctx,
2957 if (!*next_attribute) {
2958 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2959 ldap_memfree(range_attr);
2960 *more_strings = False;
2965 ldap_memfree(range_attr);
2971 * pull a single uint32_t from a ADS result
2972 * @param ads connection to ads server
2973 * @param msg Results of search
2974 * @param field Attribute to retrieve
2975 * @param v Pointer to int to store result
2976 * @return boolean inidicating success
2978 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2983 values = ldap_get_values(ads->ldap.ld, msg, field);
2987 ldap_value_free(values);
2991 *v = atoi(values[0]);
2992 ldap_value_free(values);
2997 * pull a single objectGUID from an ADS result
2998 * @param ads connection to ADS server
2999 * @param msg results of search
3000 * @param guid 37-byte area to receive text guid
3001 * @return boolean indicating success
3003 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3008 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3013 status = GUID_from_ndr_blob(&blob, guid);
3014 talloc_free(blob.data);
3015 return NT_STATUS_IS_OK(status);
3020 * pull a single struct dom_sid from a ADS result
3021 * @param ads connection to ads server
3022 * @param msg Results of search
3023 * @param field Attribute to retrieve
3024 * @param sid Pointer to sid to store result
3025 * @return boolean inidicating success
3027 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3028 struct dom_sid *sid)
3030 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3034 * pull an array of struct dom_sids from a ADS result
3035 * @param ads connection to ads server
3036 * @param mem_ctx TALLOC_CTX for allocating sid array
3037 * @param msg Results of search
3038 * @param field Attribute to retrieve
3039 * @param sids pointer to sid array to allocate
3040 * @return the count of SIDs pulled
3042 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3043 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3045 struct berval **values;
3048 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3053 for (i=0; values[i]; i++)
3057 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3059 ldap_value_free_len(values);
3067 for (i=0; values[i]; i++) {
3069 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3070 values[i]->bv_len, &(*sids)[count]);
3072 struct dom_sid_buf buf;
3073 DBG_DEBUG("pulling SID: %s\n",
3074 dom_sid_str_buf(&(*sids)[count], &buf));
3079 ldap_value_free_len(values);
3084 * pull a struct security_descriptor from a ADS result
3085 * @param ads connection to ads server
3086 * @param mem_ctx TALLOC_CTX for allocating sid array
3087 * @param msg Results of search
3088 * @param field Attribute to retrieve
3089 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3090 * @return boolean inidicating success
3092 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3093 LDAPMessage *msg, const char *field,
3094 struct security_descriptor **sd)
3096 struct berval **values;
3099 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3101 if (!values) return false;
3105 status = unmarshall_sec_desc(mem_ctx,
3106 (uint8_t *)values[0]->bv_val,
3107 values[0]->bv_len, sd);
3108 if (!NT_STATUS_IS_OK(status)) {
3109 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3110 nt_errstr(status)));
3115 ldap_value_free_len(values);
3120 * in order to support usernames longer than 21 characters we need to
3121 * use both the sAMAccountName and the userPrincipalName attributes
3122 * It seems that not all users have the userPrincipalName attribute set
3124 * @param ads connection to ads server
3125 * @param mem_ctx TALLOC_CTX for allocating sid array
3126 * @param msg Results of search
3127 * @return the username
3129 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3135 /* lookup_name() only works on the sAMAccountName to
3136 returning the username portion of userPrincipalName
3137 breaks winbindd_getpwnam() */
3139 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3140 if (ret && (p = strchr_m(ret, '@'))) {
3145 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3150 * find the update serial number - this is the core of the ldap cache
3151 * @param ads connection to ads server
3152 * @param ads connection to ADS server
3153 * @param usn Pointer to retrieved update serial number
3154 * @return status of search
3156 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3158 const char *attrs[] = {"highestCommittedUSN", NULL};
3162 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3163 if (!ADS_ERR_OK(status))
3166 if (ads_count_replies(ads, res) != 1) {
3167 ads_msgfree(ads, res);
3168 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3171 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3172 ads_msgfree(ads, res);
3173 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3176 ads_msgfree(ads, res);
3180 /* parse a ADS timestring - typical string is
3181 '20020917091222.0Z0' which means 09:12.22 17th September
3183 static time_t ads_parse_time(const char *str)
3189 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3190 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3191 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3200 /********************************************************************
3201 ********************************************************************/
3203 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3205 const char *attrs[] = {"currentTime", NULL};
3210 ADS_STRUCT *ads_s = ads;
3212 if (!(ctx = talloc_init("ads_current_time"))) {
3213 return ADS_ERROR(LDAP_NO_MEMORY);
3216 /* establish a new ldap tcp session if necessary */
3218 if ( !ads->ldap.ld ) {
3219 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3220 ads->server.ldap_server, ADS_SASL_PLAIN )) == NULL )
3222 status = ADS_ERROR(LDAP_NO_MEMORY);
3225 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3226 status = ads_connect( ads_s );
3227 if ( !ADS_ERR_OK(status))
3231 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3232 if (!ADS_ERR_OK(status)) {
3236 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
3238 ads_msgfree(ads_s, res);
3239 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3243 /* but save the time and offset in the original ADS_STRUCT */
3245 ads->config.current_time = ads_parse_time(timestr);
3247 if (ads->config.current_time != 0) {
3248 ads->auth.time_offset = ads->config.current_time - time(NULL);
3249 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3252 ads_msgfree(ads, res);
3254 status = ADS_SUCCESS;
3257 /* free any temporary ads connections */
3258 if ( ads_s != ads ) {
3259 ads_destroy( &ads_s );
3261 talloc_destroy(ctx);
3266 /********************************************************************
3267 ********************************************************************/
3269 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3271 const char *attrs[] = {"domainFunctionality", NULL};
3274 ADS_STRUCT *ads_s = ads;
3276 *val = DS_DOMAIN_FUNCTION_2000;
3278 /* establish a new ldap tcp session if necessary */
3280 if ( !ads->ldap.ld ) {
3281 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3282 ads->server.ldap_server, ADS_SASL_PLAIN )) == NULL )
3284 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3287 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3288 status = ads_connect( ads_s );
3289 if ( !ADS_ERR_OK(status))
3293 /* If the attribute does not exist assume it is a Windows 2000
3294 functional domain */
3296 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3297 if (!ADS_ERR_OK(status)) {
3298 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3299 status = ADS_SUCCESS;
3304 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3305 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3307 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3310 ads_msgfree(ads, res);
3313 /* free any temporary ads connections */
3314 if ( ads_s != ads ) {
3315 ads_destroy( &ads_s );
3322 * find the domain sid for our domain
3323 * @param ads connection to ads server
3324 * @param sid Pointer to domain sid
3325 * @return status of search
3327 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3329 const char *attrs[] = {"objectSid", NULL};
3333 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3335 if (!ADS_ERR_OK(rc)) return rc;
3336 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3337 ads_msgfree(ads, res);
3338 return ADS_ERROR_SYSTEM(ENOENT);
3340 ads_msgfree(ads, res);
3346 * find our site name
3347 * @param ads connection to ads server
3348 * @param mem_ctx Pointer to talloc context
3349 * @param site_name Pointer to the sitename
3350 * @return status of search
3352 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3356 const char *dn, *service_name;
3357 const char *attrs[] = { "dsServiceName", NULL };
3359 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3360 if (!ADS_ERR_OK(status)) {
3364 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3365 if (service_name == NULL) {
3366 ads_msgfree(ads, res);
3367 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3370 ads_msgfree(ads, res);
3372 /* go up three levels */
3373 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3375 return ADS_ERROR(LDAP_NO_MEMORY);
3378 *site_name = talloc_strdup(mem_ctx, dn);
3379 if (*site_name == NULL) {
3380 return ADS_ERROR(LDAP_NO_MEMORY);
3385 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3390 * find the site dn where a machine resides
3391 * @param ads connection to ads server
3392 * @param mem_ctx Pointer to talloc context
3393 * @param computer_name name of the machine
3394 * @param site_name Pointer to the sitename
3395 * @return status of search
3397 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3401 const char *parent, *filter;
3402 char *config_context = NULL;
3405 /* shortcut a query */
3406 if (strequal(computer_name, ads->config.ldap_server_name)) {
3407 return ads_site_dn(ads, mem_ctx, site_dn);
3410 status = ads_config_path(ads, mem_ctx, &config_context);
3411 if (!ADS_ERR_OK(status)) {
3415 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3416 if (filter == NULL) {
3417 return ADS_ERROR(LDAP_NO_MEMORY);
3420 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3421 filter, NULL, &res);
3422 if (!ADS_ERR_OK(status)) {
3426 if (ads_count_replies(ads, res) != 1) {
3427 ads_msgfree(ads, res);
3428 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3431 dn = ads_get_dn(ads, mem_ctx, res);
3433 ads_msgfree(ads, res);
3434 return ADS_ERROR(LDAP_NO_MEMORY);
3437 /* go up three levels */
3438 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3439 if (parent == NULL) {
3440 ads_msgfree(ads, res);
3442 return ADS_ERROR(LDAP_NO_MEMORY);
3445 *site_dn = talloc_strdup(mem_ctx, parent);
3446 if (*site_dn == NULL) {
3447 ads_msgfree(ads, res);
3449 return ADS_ERROR(LDAP_NO_MEMORY);
3453 ads_msgfree(ads, res);
3459 * get the upn suffixes for a domain
3460 * @param ads connection to ads server
3461 * @param mem_ctx Pointer to talloc context
3462 * @param suffixes Pointer to an array of suffixes
3463 * @param num_suffixes Pointer to the number of suffixes
3464 * @return status of search
3466 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3471 char *config_context = NULL;
3472 const char *attrs[] = { "uPNSuffixes", NULL };
3474 status = ads_config_path(ads, mem_ctx, &config_context);
3475 if (!ADS_ERR_OK(status)) {
3479 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3481 return ADS_ERROR(LDAP_NO_MEMORY);
3484 status = ads_search_dn(ads, &res, base, attrs);
3485 if (!ADS_ERR_OK(status)) {
3489 if (ads_count_replies(ads, res) != 1) {
3490 ads_msgfree(ads, res);
3491 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3494 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3495 if ((*suffixes) == NULL) {
3496 ads_msgfree(ads, res);
3497 return ADS_ERROR(LDAP_NO_MEMORY);
3500 ads_msgfree(ads, res);
3506 * get the joinable ous for a domain
3507 * @param ads connection to ads server
3508 * @param mem_ctx Pointer to talloc context
3509 * @param ous Pointer to an array of ous
3510 * @param num_ous Pointer to the number of ous
3511 * @return status of search
3513 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3514 TALLOC_CTX *mem_ctx,
3519 LDAPMessage *res = NULL;
3520 LDAPMessage *msg = NULL;
3521 const char *attrs[] = { "dn", NULL };
3524 status = ads_search(ads, &res,
3525 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3527 if (!ADS_ERR_OK(status)) {
3531 count = ads_count_replies(ads, res);
3533 ads_msgfree(ads, res);
3534 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3537 for (msg = ads_first_entry(ads, res); msg;
3538 msg = ads_next_entry(ads, msg)) {
3539 const char **p = discard_const_p(const char *, *ous);
3542 dn = ads_get_dn(ads, talloc_tos(), msg);
3544 ads_msgfree(ads, res);
3545 return ADS_ERROR(LDAP_NO_MEMORY);
3548 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3550 ads_msgfree(ads, res);
3551 return ADS_ERROR(LDAP_NO_MEMORY);
3555 *ous = discard_const_p(char *, p);
3558 ads_msgfree(ads, res);
3565 * pull a struct dom_sid from an extended dn string
3566 * @param mem_ctx TALLOC_CTX
3567 * @param extended_dn string
3568 * @param flags string type of extended_dn
3569 * @param sid pointer to a struct dom_sid
3570 * @return NT_STATUS_OK on success,
3571 * NT_INVALID_PARAMETER on error,
3572 * NT_STATUS_NOT_FOUND if no SID present
3574 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3575 const char *extended_dn,
3576 enum ads_extended_dn_flags flags,
3577 struct dom_sid *sid)
3582 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3585 /* otherwise extended_dn gets stripped off */
3586 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3587 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3590 * ADS_EXTENDED_DN_HEX_STRING:
3591 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3593 * ADS_EXTENDED_DN_STRING (only with w2k3):
3594 * <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
3596 * Object with no SID, such as an Exchange Public Folder
3597 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3600 p = strchr(dn, ';');
3602 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3605 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3606 DEBUG(5,("No SID present in extended dn\n"));
3607 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3610 p += strlen(";<SID=");
3614 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3619 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3623 case ADS_EXTENDED_DN_STRING:
3624 if (!string_to_sid(sid, p)) {
3625 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3628 case ADS_EXTENDED_DN_HEX_STRING: {
3633 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3635 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3638 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3640 DEBUG(10,("failed to parse sid\n"));
3641 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3646 DEBUG(10,("unknown extended dn format\n"));
3647 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3650 return ADS_ERROR_NT(NT_STATUS_OK);
3653 /********************************************************************
3654 ********************************************************************/
3656 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3658 LDAPMessage *res = NULL;
3663 status = ads_find_machine_acct(ads, &res, machine_name);
3664 if (!ADS_ERR_OK(status)) {
3665 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3666 lp_netbios_name()));
3670 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3671 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3675 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3676 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3680 ads_msgfree(ads, res);
3685 /********************************************************************
3686 ********************************************************************/
3688 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
3690 const char *machine_name,
3691 char ***hostnames_array,
3692 size_t *num_hostnames)
3695 LDAPMessage *res = NULL;
3698 status = ads_find_machine_acct(ads,
3701 if (!ADS_ERR_OK(status)) {
3702 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
3707 count = ads_count_replies(ads, res);
3709 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3713 *hostnames_array = ads_pull_strings(ads, mem_ctx, res,
3714 "msDS-AdditionalDnsHostName",
3716 if (*hostnames_array == NULL) {
3717 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
3719 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3724 ads_msgfree(ads, res);
3729 /********************************************************************
3730 ********************************************************************/
3732 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3734 LDAPMessage *res = NULL;
3739 status = ads_find_machine_acct(ads, &res, machine_name);
3740 if (!ADS_ERR_OK(status)) {
3741 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3742 lp_netbios_name()));
3746 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3747 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3751 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3752 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3756 ads_msgfree(ads, res);
3761 /********************************************************************
3762 ********************************************************************/
3764 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3766 LDAPMessage *res = NULL;
3772 status = ads_find_machine_acct(ads, &res, machine_name);
3773 if (!ADS_ERR_OK(status)) {
3774 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3775 lp_netbios_name()));
3779 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3780 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3784 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3785 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3789 ads_msgfree(ads, res);
3791 ok = (strlen(name) > 0);
3799 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3802 * Join a machine to a realm
3803 * Creates the machine account and sets the machine password
3804 * @param ads connection to ads server
3805 * @param machine name of host to add
3806 * @param org_unit Organizational unit to place machine in
3807 * @return status of join
3809 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3810 uint32_t account_type, const char *org_unit)
3813 LDAPMessage *res = NULL;
3816 /* machine name must be lowercase */
3817 machine = SMB_STRDUP(machine_name);
3818 strlower_m(machine);
3821 status = ads_find_machine_acct(ads, (void **)&res, machine);
3822 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3823 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3824 status = ads_leave_realm(ads, machine);
3825 if (!ADS_ERR_OK(status)) {
3826 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3827 machine, ads->config.realm));
3832 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3833 if (!ADS_ERR_OK(status)) {
3834 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3839 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3840 if (!ADS_ERR_OK(status)) {
3841 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3847 ads_msgfree(ads, res);
3854 * Delete a machine from the realm
3855 * @param ads connection to ads server
3856 * @param hostname Machine to remove
3857 * @return status of delete
3859 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3864 char *hostnameDN, *host;
3866 LDAPControl ldap_control;
3867 LDAPControl * pldap_control[2] = {NULL, NULL};
3869 pldap_control[0] = &ldap_control;
3870 memset(&ldap_control, 0, sizeof(LDAPControl));
3871 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3873 /* hostname must be lowercase */
3874 host = SMB_STRDUP(hostname);
3875 if (!strlower_m(host)) {
3877 return ADS_ERROR_SYSTEM(EINVAL);
3880 status = ads_find_machine_acct(ads, &res, host);
3881 if (!ADS_ERR_OK(status)) {
3882 DEBUG(0, ("Host account for %s does not exist.\n", host));
3887 msg = ads_first_entry(ads, res);
3890 return ADS_ERROR_SYSTEM(ENOENT);
3893 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3894 if (hostnameDN == NULL) {
3896 return ADS_ERROR_SYSTEM(ENOENT);
3899 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3901 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3903 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3906 if (rc != LDAP_SUCCESS) {
3907 const char *attrs[] = { "cn", NULL };
3908 LDAPMessage *msg_sub;
3910 /* we only search with scope ONE, we do not expect any further
3911 * objects to be created deeper */
3913 status = ads_do_search_retry(ads, hostnameDN,
3914 LDAP_SCOPE_ONELEVEL,
3915 "(objectclass=*)", attrs, &res);
3917 if (!ADS_ERR_OK(status)) {
3919 TALLOC_FREE(hostnameDN);
3923 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3924 msg_sub = ads_next_entry(ads, msg_sub)) {
3928 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3930 TALLOC_FREE(hostnameDN);
3931 return ADS_ERROR(LDAP_NO_MEMORY);
3934 status = ads_del_dn(ads, dn);
3935 if (!ADS_ERR_OK(status)) {
3936 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3939 TALLOC_FREE(hostnameDN);
3946 /* there should be no subordinate objects anymore */
3947 status = ads_do_search_retry(ads, hostnameDN,
3948 LDAP_SCOPE_ONELEVEL,
3949 "(objectclass=*)", attrs, &res);
3951 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3953 TALLOC_FREE(hostnameDN);
3957 /* delete hostnameDN now */
3958 status = ads_del_dn(ads, hostnameDN);
3959 if (!ADS_ERR_OK(status)) {
3961 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3962 TALLOC_FREE(hostnameDN);
3967 TALLOC_FREE(hostnameDN);
3969 status = ads_find_machine_acct(ads, &res, host);
3970 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
3971 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
3972 DEBUG(3, ("Failed to remove host account.\n"));
3982 * pull all token-sids from an LDAP dn
3983 * @param ads connection to ads server
3984 * @param mem_ctx TALLOC_CTX for allocating sid array
3985 * @param dn of LDAP object
3986 * @param user_sid pointer to struct dom_sid (objectSid)
3987 * @param primary_group_sid pointer to struct dom_sid (self composed)
3988 * @param sids pointer to sid array to allocate
3989 * @param num_sids counter of SIDs pulled
3990 * @return status of token query
3992 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3993 TALLOC_CTX *mem_ctx,
3995 struct dom_sid *user_sid,
3996 struct dom_sid *primary_group_sid,
3997 struct dom_sid **sids,
4001 LDAPMessage *res = NULL;
4003 size_t tmp_num_sids;
4004 struct dom_sid *tmp_sids;
4005 struct dom_sid tmp_user_sid;
4006 struct dom_sid tmp_primary_group_sid;
4008 const char *attrs[] = {
4015 status = ads_search_retry_dn(ads, &res, dn, attrs);
4016 if (!ADS_ERR_OK(status)) {
4020 count = ads_count_replies(ads, res);
4022 ads_msgfree(ads, res);
4023 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4026 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4027 ads_msgfree(ads, res);
4028 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4031 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4032 ads_msgfree(ads, res);
4033 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4037 /* hack to compose the primary group sid without knowing the
4040 struct dom_sid domsid;
4042 sid_copy(&domsid, &tmp_user_sid);
4044 if (!sid_split_rid(&domsid, NULL)) {
4045 ads_msgfree(ads, res);
4046 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4049 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4050 ads_msgfree(ads, res);
4051 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4055 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4057 if (tmp_num_sids == 0 || !tmp_sids) {
4058 ads_msgfree(ads, res);
4059 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4063 *num_sids = tmp_num_sids;
4071 *user_sid = tmp_user_sid;
4074 if (primary_group_sid) {
4075 *primary_group_sid = tmp_primary_group_sid;
4078 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4080 ads_msgfree(ads, res);
4081 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4085 * Find a sAMAccoutName in LDAP
4086 * @param ads connection to ads server
4087 * @param mem_ctx TALLOC_CTX for allocating sid array
4088 * @param samaccountname to search
4089 * @param uac_ret uint32_t pointer userAccountControl attribute value
4090 * @param dn_ret pointer to dn
4091 * @return status of token query
4093 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4094 TALLOC_CTX *mem_ctx,
4095 const char *samaccountname,
4097 const char **dn_ret)
4100 const char *attrs[] = { "userAccountControl", NULL };
4102 LDAPMessage *res = NULL;
4106 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4108 if (filter == NULL) {
4109 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4113 status = ads_do_search_all(ads, ads->config.bind_path,
4115 filter, attrs, &res);
4117 if (!ADS_ERR_OK(status)) {
4121 if (ads_count_replies(ads, res) != 1) {
4122 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4126 dn = ads_get_dn(ads, talloc_tos(), res);
4128 status = ADS_ERROR(LDAP_NO_MEMORY);
4132 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4133 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4142 *dn_ret = talloc_strdup(mem_ctx, dn);
4144 status = ADS_ERROR(LDAP_NO_MEMORY);
4150 ads_msgfree(ads, res);
4156 * find our configuration path
4157 * @param ads connection to ads server
4158 * @param mem_ctx Pointer to talloc context
4159 * @param config_path Pointer to the config path
4160 * @return status of search
4162 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4163 TALLOC_CTX *mem_ctx,
4167 LDAPMessage *res = NULL;
4168 const char *config_context = NULL;
4169 const char *attrs[] = { "configurationNamingContext", NULL };
4171 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4172 "(objectclass=*)", attrs, &res);
4173 if (!ADS_ERR_OK(status)) {
4177 config_context = ads_pull_string(ads, mem_ctx, res,
4178 "configurationNamingContext");
4179 ads_msgfree(ads, res);
4180 if (!config_context) {
4181 return ADS_ERROR(LDAP_NO_MEMORY);
4185 *config_path = talloc_strdup(mem_ctx, config_context);
4186 if (!*config_path) {
4187 return ADS_ERROR(LDAP_NO_MEMORY);
4191 return ADS_ERROR(LDAP_SUCCESS);
4195 * find the displayName of an extended right
4196 * @param ads connection to ads server
4197 * @param config_path The config path
4198 * @param mem_ctx Pointer to talloc context
4199 * @param GUID struct of the rightsGUID
4200 * @return status of search
4202 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4203 const char *config_path,
4204 TALLOC_CTX *mem_ctx,
4205 const struct GUID *rights_guid)
4208 LDAPMessage *res = NULL;
4210 const char *attrs[] = { "displayName", NULL };
4211 const char *result = NULL;
4214 if (!ads || !mem_ctx || !rights_guid) {
4218 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4219 GUID_string(mem_ctx, rights_guid));
4224 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4229 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4231 if (!ADS_ERR_OK(rc)) {
4235 if (ads_count_replies(ads, res) != 1) {
4239 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4242 ads_msgfree(ads, res);
4247 * verify or build and verify an account ou
4248 * @param mem_ctx Pointer to talloc context
4249 * @param ads connection to ads server
4251 * @return status of search
4254 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4256 const char **account_ou)
4262 if (account_ou == NULL) {
4263 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4266 if (*account_ou != NULL) {
4267 exploded_dn = ldap_explode_dn(*account_ou, 0);
4269 ldap_value_free(exploded_dn);
4274 ou_string = ads_ou_string(ads, *account_ou);
4276 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4279 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4280 ads->config.bind_path);
4281 SAFE_FREE(ou_string);
4284 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4287 exploded_dn = ldap_explode_dn(name, 0);
4289 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4291 ldap_value_free(exploded_dn);