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_INITIALIZE
96 ldap_err = ldap_initialize(&ldp, uri);
98 ldp = ldap_open(server, port);
100 ldap_err = LDAP_SUCCESS;
102 ldap_err = LDAP_OTHER;
105 if (ldap_err != LDAP_SUCCESS) {
106 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
107 uri, ldap_err2string(ldap_err)));
109 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
113 /* Teardown timeout. */
115 CatchSignal(SIGALRM, SIG_IGN);
121 static int ldap_search_with_timeout(LDAP *ld,
122 LDAP_CONST char *base,
124 LDAP_CONST char *filter,
127 LDAPControl **sctrls,
128 LDAPControl **cctrls,
132 int to = lp_ldap_timeout();
133 struct timeval timeout;
134 struct timeval *timeout_ptr = NULL;
137 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
143 timeout_ptr = &timeout;
145 /* Setup alarm timeout. */
146 CatchSignal(SIGALRM, gotalarm_sig);
147 /* Make the alarm time one second beyond
148 the timout we're setting for the
149 remote search timeout, to allow that
150 to fire in preference. */
152 /* End setup timeout. */
156 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
157 attrsonly, sctrls, cctrls, timeout_ptr,
161 /* Teardown alarm timeout. */
162 CatchSignal(SIGALRM, SIG_IGN);
167 return LDAP_TIMELIMIT_EXCEEDED;
170 * A bug in OpenLDAP means ldap_search_ext_s can return
171 * LDAP_SUCCESS but with a NULL res pointer. Cope with
172 * this. See bug #6279 for details. JRA.
176 return LDAP_TIMELIMIT_EXCEEDED;
182 /**********************************************
183 Do client and server sitename match ?
184 **********************************************/
186 bool ads_sitename_match(ADS_STRUCT *ads)
188 if (ads->config.server_site_name == NULL &&
189 ads->config.client_site_name == NULL ) {
190 DEBUG(10,("ads_sitename_match: both null\n"));
193 if (ads->config.server_site_name &&
194 ads->config.client_site_name &&
195 strequal(ads->config.server_site_name,
196 ads->config.client_site_name)) {
197 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
200 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
201 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
202 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
206 /**********************************************
207 Is this the closest DC ?
208 **********************************************/
210 bool ads_closest_dc(ADS_STRUCT *ads)
212 if (ads->config.flags & NBT_SERVER_CLOSEST) {
213 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
217 /* not sure if this can ever happen */
218 if (ads_sitename_match(ads)) {
219 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
223 if (ads->config.client_site_name == NULL) {
224 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
228 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
229 ads->config.ldap_server_name));
236 try a connection to a given ldap server, returning True and setting the servers IP
237 in the ads struct if successful
239 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
240 struct sockaddr_storage *ss)
242 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
243 TALLOC_CTX *frame = talloc_stackframe();
245 char addr[INET6_ADDRSTRLEN];
252 print_sockaddr(addr, sizeof(addr), ss);
254 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
255 addr, ads->server.realm));
257 ZERO_STRUCT( cldap_reply );
259 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
260 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
265 /* Check the CLDAP reply flags */
267 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
268 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
274 /* Fill in the ads->config values */
276 SAFE_FREE(ads->config.realm);
277 SAFE_FREE(ads->config.bind_path);
278 SAFE_FREE(ads->config.ldap_server_name);
279 SAFE_FREE(ads->config.server_site_name);
280 SAFE_FREE(ads->config.client_site_name);
281 SAFE_FREE(ads->server.workgroup);
283 if (!check_cldap_reply_required_flags(cldap_reply.server_type,
284 ads->config.flags)) {
289 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
290 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
291 if (!strupper_m(ads->config.realm)) {
296 ads->config.bind_path = ads_build_dn(ads->config.realm);
297 if (*cldap_reply.server_site) {
298 ads->config.server_site_name =
299 SMB_STRDUP(cldap_reply.server_site);
301 if (*cldap_reply.client_site) {
302 ads->config.client_site_name =
303 SMB_STRDUP(cldap_reply.client_site);
305 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
307 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
310 /* Store our site name. */
311 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
312 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
314 /* Leave this until last so that the flags are not clobbered */
315 ads->config.flags = cldap_reply.server_type;
325 /**********************************************************************
326 send a cldap ping to list of servers, one at a time, until one of
327 them answers it's an ldap server. Record success in the ADS_STRUCT.
328 Take note of and update negative connection cache.
329 **********************************************************************/
331 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,const char *domain,
332 struct ip_service *ip_list, int count)
337 for (i = 0; i < count; i++) {
338 char server[INET6_ADDRSTRLEN];
340 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
342 if (!NT_STATUS_IS_OK(
343 check_negative_conn_cache(domain, server)))
346 /* Returns ok only if it matches the correct server type */
347 ok = ads_try_connect(ads, false, &ip_list[i].ss);
353 /* keep track of failures */
354 add_failed_connection_entry(domain, server,
355 NT_STATUS_UNSUCCESSFUL);
358 return NT_STATUS_NO_LOGON_SERVERS;
361 /***************************************************************************
362 resolve a name and perform an "ldap ping" using NetBIOS and related methods
363 ****************************************************************************/
365 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
366 const char *domain, const char *realm)
369 struct ip_service *ip_list;
372 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
375 status = get_sorted_dc_list(domain, NULL, &ip_list, &count,
377 if (!NT_STATUS_IS_OK(status)) {
381 /* remove servers which are known to be dead based on
382 the corresponding DNS method */
384 for (i = 0; i < count; ++i) {
385 char server[INET6_ADDRSTRLEN];
387 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
390 check_negative_conn_cache(realm, server))) {
391 /* Ensure we add the workgroup name for this
392 IP address as negative too. */
393 add_failed_connection_entry(
395 NT_STATUS_UNSUCCESSFUL);
400 status = cldap_ping_list(ads, domain, ip_list, count);
408 /**********************************************************************
409 resolve a name and perform an "ldap ping" using DNS
410 **********************************************************************/
412 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
416 struct ip_service *ip_list = NULL;
419 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
422 status = get_sorted_dc_list(realm, sitename, &ip_list, &count,
424 if (!NT_STATUS_IS_OK(status)) {
429 status = cldap_ping_list(ads, realm, ip_list, count);
436 /**********************************************************************
437 Try to find an AD dc using our internal name resolution routines
438 Try the realm first and then then workgroup name if netbios is not
440 **********************************************************************/
442 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
444 const char *c_domain = "";
446 bool use_own_domain = False;
447 char *sitename = NULL;
448 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
451 /* if the realm and workgroup are both empty, assume they are ours */
454 c_realm = ads->server.realm;
460 /* special case where no realm and no workgroup means our own */
461 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
462 use_own_domain = True;
463 c_realm = lp_realm();
467 if (!lp_disable_netbios()) {
468 if (use_own_domain) {
469 c_domain = lp_workgroup();
471 c_domain = ads->server.workgroup;
472 if (!*c_realm && (!c_domain || !*c_domain)) {
473 c_domain = lp_workgroup();
482 if (!*c_realm && !*c_domain) {
483 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
485 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
489 * In case of LDAP we use get_dc_name() as that
490 * creates the custom krb5.conf file
492 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
494 struct sockaddr_storage ip_out;
496 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
497 " and falling back to domain '%s'\n",
500 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
503 * we call ads_try_connect() to fill in the
504 * ads->config details
506 ok = ads_try_connect(ads, false, &ip_out);
512 return NT_STATUS_NO_LOGON_SERVERS;
516 sitename = sitename_fetch(talloc_tos(), c_realm);
517 status = resolve_and_ping_dns(ads, sitename, c_realm);
519 if (NT_STATUS_IS_OK(status)) {
520 TALLOC_FREE(sitename);
524 /* In case we failed to contact one of our closest DC on our
526 * need to try to find another DC, retry with a site-less SRV
531 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
532 "our site (%s), Trying to find another DC "
533 "for realm '%s' (domain '%s')\n",
534 sitename, c_realm, c_domain));
535 namecache_delete(c_realm, 0x1C);
537 resolve_and_ping_dns(ads, NULL, c_realm);
539 if (NT_STATUS_IS_OK(status)) {
540 TALLOC_FREE(sitename);
545 TALLOC_FREE(sitename);
548 /* try netbios as fallback - if permitted,
549 or if configuration specifically requests it */
552 DEBUG(3, ("ads_find_dc: falling back to netbios "
553 "name resolution for domain '%s' (realm '%s')\n",
557 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
558 if (NT_STATUS_IS_OK(status)) {
563 DEBUG(1, ("ads_find_dc: "
564 "name resolution for realm '%s' (domain '%s') failed: %s\n",
565 c_realm, c_domain, nt_errstr(status)));
569 * Connect to the LDAP server
570 * @param ads Pointer to an existing ADS_STRUCT
571 * @return status of connection
573 ADS_STATUS ads_connect(ADS_STRUCT *ads)
575 int version = LDAP_VERSION3;
578 char addr[INET6_ADDRSTRLEN];
580 ZERO_STRUCT(ads->ldap);
581 ZERO_STRUCT(ads->ldap_wrap_data);
582 ads->ldap.last_attempt = time_mono(NULL);
583 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
585 /* try with a user specified server */
587 if (DEBUGLEVEL >= 11) {
588 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
589 DEBUG(11,("ads_connect: entering\n"));
590 DEBUGADD(11,("%s\n", s));
594 if (ads->server.ldap_server) {
596 struct sockaddr_storage ss;
598 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
600 DEBUG(5,("ads_connect: unable to resolve name %s\n",
601 ads->server.ldap_server));
602 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
605 ok = ads_try_connect(ads, ads->server.gc, &ss);
610 /* The choice of which GC use is handled one level up in
611 ads_connect_gc(). If we continue on from here with
612 ads_find_dc() we will get GC searches on port 389 which
613 doesn't work. --jerry */
615 if (ads->server.gc == true) {
616 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
619 if (ads->server.no_fallback) {
620 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
625 ntstatus = ads_find_dc(ads);
626 if (NT_STATUS_IS_OK(ntstatus)) {
630 status = ADS_ERROR_NT(ntstatus);
635 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
636 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
638 if (!ads->auth.user_name) {
639 /* Must use the userPrincipalName value here or sAMAccountName
640 and not servicePrincipalName; found by Guenther Deschner */
642 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
643 DEBUG(0,("ads_connect: asprintf fail.\n"));
644 ads->auth.user_name = NULL;
648 if (!ads->auth.realm) {
649 ads->auth.realm = SMB_STRDUP(ads->config.realm);
652 if (!ads->auth.kdc_server) {
653 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
654 ads->auth.kdc_server = SMB_STRDUP(addr);
657 /* If the caller() requested no LDAP bind, then we are done */
659 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
660 status = ADS_SUCCESS;
664 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
665 if (!ads->ldap_wrap_data.mem_ctx) {
666 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
670 /* Otherwise setup the TCP LDAP session */
672 ads->ldap.ld = ldap_open_with_timeout(addr,
674 ads->ldap.port, lp_ldap_timeout());
675 if (ads->ldap.ld == NULL) {
676 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
679 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
681 /* cache the successful connection for workgroup and realm */
682 if (ads_closest_dc(ads)) {
683 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
684 saf_store( ads->server.realm, ads->config.ldap_server_name);
687 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
689 if ( lp_ldap_ssl_ads() ) {
690 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
691 if (!ADS_ERR_OK(status)) {
696 /* fill in the current time and offsets */
698 status = ads_current_time( ads );
699 if ( !ADS_ERR_OK(status) ) {
703 /* Now do the bind */
705 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
706 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
710 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
711 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
715 status = ads_sasl_bind(ads);
718 if (DEBUGLEVEL >= 11) {
719 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
720 DEBUG(11,("ads_connect: leaving with: %s\n",
721 ads_errstr(status)));
722 DEBUGADD(11,("%s\n", s));
730 * Connect to the LDAP server using given credentials
731 * @param ads Pointer to an existing ADS_STRUCT
732 * @return status of connection
734 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
736 ads->auth.flags |= ADS_AUTH_USER_CREDS;
738 return ads_connect(ads);
742 * Disconnect the LDAP server
743 * @param ads Pointer to an existing ADS_STRUCT
745 void ads_disconnect(ADS_STRUCT *ads)
748 ldap_unbind(ads->ldap.ld);
751 if (ads->ldap_wrap_data.wrap_ops &&
752 ads->ldap_wrap_data.wrap_ops->disconnect) {
753 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
755 if (ads->ldap_wrap_data.mem_ctx) {
756 talloc_free(ads->ldap_wrap_data.mem_ctx);
758 ZERO_STRUCT(ads->ldap);
759 ZERO_STRUCT(ads->ldap_wrap_data);
763 Duplicate a struct berval into talloc'ed memory
765 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
767 struct berval *value;
769 if (!in_val) return NULL;
771 value = talloc_zero(ctx, struct berval);
774 if (in_val->bv_len == 0) return value;
776 value->bv_len = in_val->bv_len;
777 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
783 Make a values list out of an array of (struct berval *)
785 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
786 const struct berval **in_vals)
788 struct berval **values;
791 if (!in_vals) return NULL;
792 for (i=0; in_vals[i]; i++)
794 values = talloc_zero_array(ctx, struct berval *, i+1);
795 if (!values) return NULL;
797 for (i=0; in_vals[i]; i++) {
798 values[i] = dup_berval(ctx, in_vals[i]);
804 UTF8-encode a values list out of an array of (char *)
806 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
812 if (!in_vals) return NULL;
813 for (i=0; in_vals[i]; i++)
815 values = talloc_zero_array(ctx, char *, i+1);
816 if (!values) return NULL;
818 for (i=0; in_vals[i]; i++) {
819 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
828 Pull a (char *) array out of a UTF8-encoded values list
830 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
834 size_t converted_size;
836 if (!in_vals) return NULL;
837 for (i=0; in_vals[i]; i++)
839 values = talloc_zero_array(ctx, char *, i+1);
840 if (!values) return NULL;
842 for (i=0; in_vals[i]; i++) {
843 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
845 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
846 "%s", strerror(errno)));
853 * Do a search with paged results. cookie must be null on the first
854 * call, and then returned on each subsequent call. It will be null
855 * again when the entire search is complete
856 * @param ads connection to ads server
857 * @param bind_path Base dn for the search
858 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
859 * @param expr Search expression - specified in local charset
860 * @param attrs Attributes to retrieve - specified in utf8 or ascii
861 * @param res ** which will contain results - free res* with ads_msgfree()
862 * @param count Number of entries retrieved on this page
863 * @param cookie The paged results cookie to be returned on subsequent calls
864 * @return status of search
866 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
867 const char *bind_path,
868 int scope, const char *expr,
869 const char **attrs, void *args,
871 int *count, struct berval **cookie)
874 char *utf8_expr, *utf8_path, **search_attrs = NULL;
875 size_t converted_size;
876 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
877 BerElement *cookie_be = NULL;
878 struct berval *cookie_bv= NULL;
879 BerElement *ext_be = NULL;
880 struct berval *ext_bv= NULL;
883 ads_control *external_control = (ads_control *) args;
887 if (!(ctx = talloc_init("ads_do_paged_search_args")))
888 return ADS_ERROR(LDAP_NO_MEMORY);
890 /* 0 means the conversion worked but the result was empty
891 so we only fail if it's -1. In any case, it always
892 at least nulls out the dest */
893 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
894 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
900 if (!attrs || !(*attrs))
903 /* This would be the utf8-encoded version...*/
904 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
905 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
911 /* Paged results only available on ldap v3 or later */
912 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
913 if (version < LDAP_VERSION3) {
914 rc = LDAP_NOT_SUPPORTED;
918 cookie_be = ber_alloc_t(LBER_USE_DER);
920 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
921 ber_bvfree(*cookie); /* don't need it from last time */
924 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
926 ber_flatten(cookie_be, &cookie_bv);
927 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
928 PagedResults.ldctl_iscritical = (char) 1;
929 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
930 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
932 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
933 NoReferrals.ldctl_iscritical = (char) 0;
934 NoReferrals.ldctl_value.bv_len = 0;
935 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
937 if (external_control &&
938 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
939 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
941 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
942 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
944 /* win2k does not accept a ldctl_value beeing passed in */
946 if (external_control->val != 0) {
948 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
953 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
957 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
962 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
963 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
966 ExternalCtrl.ldctl_value.bv_len = 0;
967 ExternalCtrl.ldctl_value.bv_val = NULL;
970 controls[0] = &NoReferrals;
971 controls[1] = &PagedResults;
972 controls[2] = &ExternalCtrl;
976 controls[0] = &NoReferrals;
977 controls[1] = &PagedResults;
981 /* we need to disable referrals as the openldap libs don't
982 handle them and paged results at the same time. Using them
983 together results in the result record containing the server
984 page control being removed from the result list (tridge/jmcd)
986 leaving this in despite the control that says don't generate
987 referrals, in case the server doesn't support it (jmcd)
989 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
991 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
992 search_attrs, 0, controls,
994 (LDAPMessage **)res);
996 ber_free(cookie_be, 1);
997 ber_bvfree(cookie_bv);
1000 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1001 ldap_err2string(rc)));
1002 if (rc == LDAP_OTHER) {
1006 ret = ldap_parse_result(ads->ldap.ld,
1014 if (ret == LDAP_SUCCESS) {
1015 DEBUG(3, ("ldap_search_with_timeout(%s) "
1016 "error: %s\n", expr, ldap_errmsg));
1017 ldap_memfree(ldap_errmsg);
1023 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1024 NULL, &rcontrols, 0);
1030 for (i=0; rcontrols[i]; i++) {
1031 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1032 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1033 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1035 /* the berval is the cookie, but must be freed when
1037 if (cookie_bv->bv_len) /* still more to do */
1038 *cookie=ber_bvdup(cookie_bv);
1041 ber_bvfree(cookie_bv);
1042 ber_free(cookie_be, 1);
1046 ldap_controls_free(rcontrols);
1049 talloc_destroy(ctx);
1052 ber_free(ext_be, 1);
1059 if (rc != LDAP_SUCCESS && *res != NULL) {
1060 ads_msgfree(ads, *res);
1064 /* if/when we decide to utf8-encode attrs, take out this next line */
1065 TALLOC_FREE(search_attrs);
1067 return ADS_ERROR(rc);
1070 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1071 int scope, const char *expr,
1072 const char **attrs, LDAPMessage **res,
1073 int *count, struct berval **cookie)
1075 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1080 * Get all results for a search. This uses ads_do_paged_search() to return
1081 * all entries in a large search.
1082 * @param ads connection to ads server
1083 * @param bind_path Base dn for the search
1084 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1085 * @param expr Search expression
1086 * @param attrs Attributes to retrieve
1087 * @param res ** which will contain results - free res* with ads_msgfree()
1088 * @return status of search
1090 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1091 int scope, const char *expr,
1092 const char **attrs, void *args,
1095 struct berval *cookie = NULL;
1100 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1103 if (!ADS_ERR_OK(status))
1106 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1108 LDAPMessage *res2 = NULL;
1109 LDAPMessage *msg, *next;
1111 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1112 attrs, args, &res2, &count, &cookie);
1113 if (!ADS_ERR_OK(status)) {
1117 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1118 that this works on all ldap libs, but I have only tested with openldap */
1119 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1120 next = ads_next_message(ads, msg);
1121 ldap_add_result_entry((LDAPMessage **)res, msg);
1123 /* note that we do not free res2, as the memory is now
1124 part of the main returned list */
1127 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1128 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1134 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1135 int scope, const char *expr,
1136 const char **attrs, LDAPMessage **res)
1138 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1141 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1142 int scope, const char *expr,
1143 const char **attrs, uint32_t sd_flags,
1148 args.control = ADS_SD_FLAGS_OID;
1149 args.val = sd_flags;
1150 args.critical = True;
1152 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1157 * Run a function on all results for a search. Uses ads_do_paged_search() and
1158 * runs the function as each page is returned, using ads_process_results()
1159 * @param ads connection to ads server
1160 * @param bind_path Base dn for the search
1161 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1162 * @param expr Search expression - specified in local charset
1163 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1164 * @param fn Function which takes attr name, values list, and data_area
1165 * @param data_area Pointer which is passed to function on each call
1166 * @return status of search
1168 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1169 int scope, const char *expr, const char **attrs,
1170 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1173 struct berval *cookie = NULL;
1178 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1181 if (!ADS_ERR_OK(status)) return status;
1183 ads_process_results(ads, res, fn, data_area);
1184 ads_msgfree(ads, res);
1187 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1188 &res, &count, &cookie);
1190 if (!ADS_ERR_OK(status)) break;
1192 ads_process_results(ads, res, fn, data_area);
1193 ads_msgfree(ads, res);
1200 * Do a search with a timeout.
1201 * @param ads connection to ads server
1202 * @param bind_path Base dn for the search
1203 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1204 * @param expr Search expression
1205 * @param attrs Attributes to retrieve
1206 * @param res ** which will contain results - free res* with ads_msgfree()
1207 * @return status of search
1209 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1211 const char **attrs, LDAPMessage **res)
1214 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1215 size_t converted_size;
1219 if (!(ctx = talloc_init("ads_do_search"))) {
1220 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1221 return ADS_ERROR(LDAP_NO_MEMORY);
1224 /* 0 means the conversion worked but the result was empty
1225 so we only fail if it's negative. In any case, it always
1226 at least nulls out the dest */
1227 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1228 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1230 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1231 rc = LDAP_NO_MEMORY;
1235 if (!attrs || !(*attrs))
1236 search_attrs = NULL;
1238 /* This would be the utf8-encoded version...*/
1239 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1240 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1242 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1243 rc = LDAP_NO_MEMORY;
1248 /* see the note in ads_do_paged_search - we *must* disable referrals */
1249 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1251 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1252 search_attrs, 0, NULL, NULL,
1254 (LDAPMessage **)res);
1256 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1257 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1262 talloc_destroy(ctx);
1263 /* if/when we decide to utf8-encode attrs, take out this next line */
1264 TALLOC_FREE(search_attrs);
1265 return ADS_ERROR(rc);
1268 * Do a general ADS search
1269 * @param ads connection to ads server
1270 * @param res ** which will contain results - free res* with ads_msgfree()
1271 * @param expr Search expression
1272 * @param attrs Attributes to retrieve
1273 * @return status of search
1275 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1276 const char *expr, const char **attrs)
1278 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1283 * Do a search on a specific DistinguishedName
1284 * @param ads connection to ads server
1285 * @param res ** which will contain results - free res* with ads_msgfree()
1286 * @param dn DistinguishName to search
1287 * @param attrs Attributes to retrieve
1288 * @return status of search
1290 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1291 const char *dn, const char **attrs)
1293 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1298 * Free up memory from a ads_search
1299 * @param ads connection to ads server
1300 * @param msg Search results to free
1302 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1309 * Get a dn from search results
1310 * @param ads connection to ads server
1311 * @param msg Search result
1314 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1316 char *utf8_dn, *unix_dn;
1317 size_t converted_size;
1319 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1322 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1326 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1327 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1331 ldap_memfree(utf8_dn);
1336 * Get the parent from a dn
1337 * @param dn the dn to return the parent from
1338 * @return parent dn string
1340 char *ads_parent_dn(const char *dn)
1348 p = strchr(dn, ',');
1358 * Find a machine account given a hostname
1359 * @param ads connection to ads server
1360 * @param res ** which will contain results - free res* with ads_msgfree()
1361 * @param host Hostname to search for
1362 * @return status of search
1364 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1365 const char *machine)
1369 const char *attrs[] = {
1370 /* This is how Windows checks for machine accounts */
1373 "userAccountControl",
1375 "ServicePrincipalName",
1378 /* Additional attributes Samba checks */
1379 "msDS-SupportedEncryptionTypes",
1380 "nTSecurityDescriptor",
1384 TALLOC_CTX *frame = talloc_stackframe();
1388 /* the easiest way to find a machine account anywhere in the tree
1389 is to look for hostname$ */
1390 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1392 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1396 status = ads_search(ads, res, expr, attrs);
1397 if (ADS_ERR_OK(status)) {
1398 if (ads_count_replies(ads, *res) != 1) {
1399 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1409 * Initialize a list of mods to be used in a modify request
1410 * @param ctx An initialized TALLOC_CTX
1411 * @return allocated ADS_MODLIST
1413 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1415 #define ADS_MODLIST_ALLOC_SIZE 10
1418 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1419 /* -1 is safety to make sure we don't go over the end.
1420 need to reset it to NULL before doing ldap modify */
1421 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1423 return (ADS_MODLIST)mods;
1428 add an attribute to the list, with values list already constructed
1430 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1431 int mod_op, const char *name,
1432 const void *_invals)
1435 LDAPMod **modlist = (LDAPMod **) *mods;
1436 struct berval **ber_values = NULL;
1437 char **char_values = NULL;
1440 mod_op = LDAP_MOD_DELETE;
1442 if (mod_op & LDAP_MOD_BVALUES) {
1443 const struct berval **b;
1444 b = discard_const_p(const struct berval *, _invals);
1445 ber_values = ads_dup_values(ctx, b);
1448 c = discard_const_p(const char *, _invals);
1449 char_values = ads_push_strvals(ctx, c);
1453 /* find the first empty slot */
1454 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1456 if (modlist[curmod] == (LDAPMod *) -1) {
1457 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1458 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1459 return ADS_ERROR(LDAP_NO_MEMORY);
1460 memset(&modlist[curmod], 0,
1461 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1462 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1463 *mods = (ADS_MODLIST)modlist;
1466 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1467 return ADS_ERROR(LDAP_NO_MEMORY);
1468 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1469 if (mod_op & LDAP_MOD_BVALUES) {
1470 modlist[curmod]->mod_bvalues = ber_values;
1471 } else if (mod_op & LDAP_MOD_DELETE) {
1472 modlist[curmod]->mod_values = NULL;
1474 modlist[curmod]->mod_values = char_values;
1477 modlist[curmod]->mod_op = mod_op;
1478 return ADS_ERROR(LDAP_SUCCESS);
1482 * Add a single string value to a mod list
1483 * @param ctx An initialized TALLOC_CTX
1484 * @param mods An initialized ADS_MODLIST
1485 * @param name The attribute name to add
1486 * @param val The value to add - NULL means DELETE
1487 * @return ADS STATUS indicating success of add
1489 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1490 const char *name, const char *val)
1492 const char *values[2];
1498 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1499 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1503 * Add an array of string values to a mod list
1504 * @param ctx An initialized TALLOC_CTX
1505 * @param mods An initialized ADS_MODLIST
1506 * @param name The attribute name to add
1507 * @param vals The array of string values to add - NULL means DELETE
1508 * @return ADS STATUS indicating success of add
1510 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1511 const char *name, const char **vals)
1514 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1515 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1516 name, (const void **) vals);
1520 * Add a single ber-encoded value to a mod list
1521 * @param ctx An initialized TALLOC_CTX
1522 * @param mods An initialized ADS_MODLIST
1523 * @param name The attribute name to add
1524 * @param val The value to add - NULL means DELETE
1525 * @return ADS STATUS indicating success of add
1527 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1528 const char *name, const struct berval *val)
1530 const struct berval *values[2];
1535 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1536 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1537 name, (const void **) values);
1540 static void ads_print_error(int ret, LDAP *ld)
1543 char *ld_error = NULL;
1544 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1545 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1547 ldap_err2string(ret),
1549 SAFE_FREE(ld_error);
1554 * Perform an ldap modify
1555 * @param ads connection to ads server
1556 * @param mod_dn DistinguishedName to modify
1557 * @param mods list of modifications to perform
1558 * @return status of modify
1560 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1563 char *utf8_dn = NULL;
1564 size_t converted_size;
1566 this control is needed to modify that contains a currently
1567 non-existent attribute (but allowable for the object) to run
1569 LDAPControl PermitModify = {
1570 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1573 LDAPControl *controls[2];
1575 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1577 controls[0] = &PermitModify;
1580 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1581 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1584 /* find the end of the list, marked by NULL or -1 */
1585 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1586 /* make sure the end of the list is NULL */
1588 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1589 (LDAPMod **) mods, controls, NULL);
1590 ads_print_error(ret, ads->ldap.ld);
1591 TALLOC_FREE(utf8_dn);
1592 return ADS_ERROR(ret);
1596 * Perform an ldap add
1597 * @param ads connection to ads server
1598 * @param new_dn DistinguishedName to add
1599 * @param mods list of attributes and values for DN
1600 * @return status of add
1602 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1605 char *utf8_dn = NULL;
1606 size_t converted_size;
1608 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1610 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1611 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1612 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1615 /* find the end of the list, marked by NULL or -1 */
1616 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1617 /* make sure the end of the list is NULL */
1620 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1621 ads_print_error(ret, ads->ldap.ld);
1622 TALLOC_FREE(utf8_dn);
1623 return ADS_ERROR(ret);
1627 * Delete a DistinguishedName
1628 * @param ads connection to ads server
1629 * @param new_dn DistinguishedName to delete
1630 * @return status of delete
1632 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1635 char *utf8_dn = NULL;
1636 size_t converted_size;
1637 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1638 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1639 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1642 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1644 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1645 ads_print_error(ret, ads->ldap.ld);
1646 TALLOC_FREE(utf8_dn);
1647 return ADS_ERROR(ret);
1651 * Build an org unit string
1652 * if org unit is Computers or blank then assume a container, otherwise
1653 * assume a / separated list of organisational units.
1654 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1655 * @param ads connection to ads server
1656 * @param org_unit Organizational unit
1657 * @return org unit string - caller must free
1659 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1663 if (!org_unit || !*org_unit) {
1665 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1667 /* samba4 might not yet respond to a wellknownobject-query */
1668 return ret ? ret : SMB_STRDUP("cn=Computers");
1671 if (strequal(org_unit, "Computers")) {
1672 return SMB_STRDUP("cn=Computers");
1675 /* jmcd: removed "\\" from the separation chars, because it is
1676 needed as an escape for chars like '#' which are valid in an
1678 return ads_build_path(org_unit, "/", "ou=", 1);
1682 * Get a org unit string for a well-known GUID
1683 * @param ads connection to ads server
1684 * @param wknguid Well known GUID
1685 * @return org unit string - caller must free
1687 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1690 LDAPMessage *res = NULL;
1691 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1692 **bind_dn_exp = NULL;
1693 const char *attrs[] = {"distinguishedName", NULL};
1694 int new_ln, wkn_ln, bind_ln, i;
1696 if (wknguid == NULL) {
1700 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1701 DEBUG(1, ("asprintf failed!\n"));
1705 status = ads_search_dn(ads, &res, base, attrs);
1706 if (!ADS_ERR_OK(status)) {
1707 DEBUG(1,("Failed while searching for: %s\n", base));
1711 if (ads_count_replies(ads, res) != 1) {
1715 /* substitute the bind-path from the well-known-guid-search result */
1716 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1721 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1726 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1731 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1733 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1736 new_ln = wkn_ln - bind_ln;
1738 ret = SMB_STRDUP(wkn_dn_exp[0]);
1743 for (i=1; i < new_ln; i++) {
1746 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1752 ret = SMB_STRDUP(s);
1761 ads_msgfree(ads, res);
1762 TALLOC_FREE(wkn_dn);
1764 ldap_value_free(wkn_dn_exp);
1767 ldap_value_free(bind_dn_exp);
1774 * Adds (appends) an item to an attribute array, rather then
1775 * replacing the whole list
1776 * @param ctx An initialized TALLOC_CTX
1777 * @param mods An initialized ADS_MODLIST
1778 * @param name name of the ldap attribute to append to
1779 * @param vals an array of values to add
1780 * @return status of addition
1783 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1784 const char *name, const char **vals)
1786 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1787 (const void *) vals);
1791 * Determines the an account's current KVNO via an LDAP lookup
1792 * @param ads An initialized ADS_STRUCT
1793 * @param account_name the NT samaccountname.
1794 * @return the kvno for the account, or -1 in case of a failure.
1797 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1799 LDAPMessage *res = NULL;
1800 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1802 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1803 char *dn_string = NULL;
1806 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1807 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1810 ret = ads_search(ads, &res, filter, attrs);
1812 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1813 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1814 ads_msgfree(ads, res);
1818 dn_string = ads_get_dn(ads, talloc_tos(), res);
1820 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1821 ads_msgfree(ads, res);
1824 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1825 TALLOC_FREE(dn_string);
1827 /* ---------------------------------------------------------
1828 * 0 is returned as a default KVNO from this point on...
1829 * This is done because Windows 2000 does not support key
1830 * version numbers. Chances are that a failure in the next
1831 * step is simply due to Windows 2000 being used for a
1832 * domain controller. */
1835 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1836 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1837 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1838 ads_msgfree(ads, res);
1843 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1844 ads_msgfree(ads, res);
1849 * Determines the computer account's current KVNO via an LDAP lookup
1850 * @param ads An initialized ADS_STRUCT
1851 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1852 * @return the kvno for the computer account, or -1 in case of a failure.
1855 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1857 char *computer_account = NULL;
1860 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1864 kvno = ads_get_kvno(ads, computer_account);
1865 free(computer_account);
1871 * This clears out all registered spn's for a given hostname
1872 * @param ads An initilaized ADS_STRUCT
1873 * @param machine_name the NetBIOS name of the computer.
1874 * @return 0 upon success, non-zero otherwise.
1877 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1880 LDAPMessage *res = NULL;
1882 const char *servicePrincipalName[1] = {NULL};
1884 char *dn_string = NULL;
1886 ret = ads_find_machine_acct(ads, &res, machine_name);
1887 if (!ADS_ERR_OK(ret)) {
1888 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1889 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1890 ads_msgfree(ads, res);
1894 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1895 ctx = talloc_init("ads_clear_service_principal_names");
1897 ads_msgfree(ads, res);
1898 return ADS_ERROR(LDAP_NO_MEMORY);
1901 if (!(mods = ads_init_mods(ctx))) {
1902 talloc_destroy(ctx);
1903 ads_msgfree(ads, res);
1904 return ADS_ERROR(LDAP_NO_MEMORY);
1906 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1907 if (!ADS_ERR_OK(ret)) {
1908 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1909 ads_msgfree(ads, res);
1910 talloc_destroy(ctx);
1913 dn_string = ads_get_dn(ads, talloc_tos(), res);
1915 talloc_destroy(ctx);
1916 ads_msgfree(ads, res);
1917 return ADS_ERROR(LDAP_NO_MEMORY);
1919 ret = ads_gen_mod(ads, dn_string, mods);
1920 TALLOC_FREE(dn_string);
1921 if (!ADS_ERR_OK(ret)) {
1922 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1924 ads_msgfree(ads, res);
1925 talloc_destroy(ctx);
1929 ads_msgfree(ads, res);
1930 talloc_destroy(ctx);
1935 * @brief Search for an element in a string array.
1937 * @param[in] el_array The string array to search.
1939 * @param[in] num_el The number of elements in the string array.
1941 * @param[in] el The string to search.
1943 * @return True if found, false if not.
1945 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1949 if (el_array == NULL || num_el == 0 || el == NULL) {
1953 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1956 cmp = strcasecmp_m(el_array[i], el);
1966 * @brief This gets the service principal names of an existing computer account.
1968 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1970 * @param[in] ads The ADS context to use.
1972 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1973 * identify the computer account.
1975 * @param[in] spn_array A pointer to store the array for SPNs.
1977 * @param[in] num_spns The number of principals stored in the array.
1979 * @return 0 on success, or a ADS error if a failure occurred.
1981 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1983 const char *machine_name,
1988 LDAPMessage *res = NULL;
1991 status = ads_find_machine_acct(ads,
1994 if (!ADS_ERR_OK(status)) {
1995 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2000 count = ads_count_replies(ads, res);
2002 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2006 *spn_array = ads_pull_strings(ads,
2009 "servicePrincipalName",
2011 if (*spn_array == NULL) {
2012 DEBUG(1, ("Host account for %s does not have service principal "
2015 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2020 ads_msgfree(ads, res);
2026 * This adds a service principal name to an existing computer account
2027 * (found by hostname) in AD.
2028 * @param ads An initialized ADS_STRUCT
2029 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2030 * @param spns An array or strings for the service principals to add,
2031 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2032 * @return 0 upon sucess, or non-zero if a failure occurs
2035 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2036 const char *machine_name,
2041 LDAPMessage *res = NULL;
2043 char *dn_string = NULL;
2044 const char **servicePrincipalName = spns;
2046 ret = ads_find_machine_acct(ads, &res, machine_name);
2047 if (!ADS_ERR_OK(ret)) {
2048 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2050 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2051 ads_msgfree(ads, res);
2055 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2056 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2057 ads_msgfree(ads, res);
2058 return ADS_ERROR(LDAP_NO_MEMORY);
2061 DEBUG(5,("ads_add_service_principal_name: INFO: "
2062 "Adding %s to host %s\n",
2063 spns[0] ? "N/A" : spns[0], machine_name));
2066 DEBUG(5,("ads_add_service_principal_name: INFO: "
2067 "Adding %s to host %s\n",
2068 spns[1] ? "N/A" : spns[1], machine_name));
2070 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2071 ret = ADS_ERROR(LDAP_NO_MEMORY);
2075 ret = ads_add_strlist(ctx,
2077 "servicePrincipalName",
2078 servicePrincipalName);
2079 if (!ADS_ERR_OK(ret)) {
2080 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2084 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2085 ret = ADS_ERROR(LDAP_NO_MEMORY);
2089 ret = ads_gen_mod(ads, dn_string, mods);
2090 if (!ADS_ERR_OK(ret)) {
2091 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2097 ads_msgfree(ads, res);
2101 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2104 uint32_t acct_ctrl = 0;
2107 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2115 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2117 const struct berval *machine_pw_val)
2121 TALLOC_CTX *frame = talloc_stackframe();
2122 uint32_t acct_control;
2123 char *control_str = NULL;
2124 const char *attrs[] = {
2128 LDAPMessage *res = NULL;
2131 dn = ads_get_dn(ads, frame, msg);
2133 ret = ADS_ERROR(LDAP_NO_MEMORY);
2137 acct_control = ads_get_acct_ctrl(ads, msg);
2138 if (acct_control == 0) {
2139 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2144 * Changing the password, disables the account. So we need to change the
2145 * userAccountControl flags to enable it again.
2147 mods = ads_init_mods(frame);
2149 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2153 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2155 ret = ads_gen_mod(ads, dn, mods);
2156 if (!ADS_ERR_OK(ret)) {
2162 * To activate the account, we need to disable and enable it.
2164 acct_control |= UF_ACCOUNTDISABLE;
2166 control_str = talloc_asprintf(frame, "%u", acct_control);
2167 if (control_str == NULL) {
2168 ret = ADS_ERROR(LDAP_NO_MEMORY);
2172 mods = ads_init_mods(frame);
2174 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2178 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2180 ret = ads_gen_mod(ads, dn, mods);
2181 if (!ADS_ERR_OK(ret)) {
2185 TALLOC_FREE(control_str);
2188 * Enable the account again.
2190 acct_control &= ~UF_ACCOUNTDISABLE;
2192 control_str = talloc_asprintf(frame, "%u", acct_control);
2193 if (control_str == NULL) {
2194 ret = ADS_ERROR(LDAP_NO_MEMORY);
2198 mods = ads_init_mods(frame);
2200 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2204 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2206 ret = ads_gen_mod(ads, dn, mods);
2207 if (!ADS_ERR_OK(ret)) {
2211 TALLOC_FREE(control_str);
2213 ret = ads_search_dn(ads, &res, dn, attrs);
2214 ads_msgfree(ads, res);
2223 * adds a machine account to the ADS server
2224 * @param ads An intialized ADS_STRUCT
2225 * @param machine_name - the NetBIOS machine name of this account.
2226 * @param account_type A number indicating the type of account to create
2227 * @param org_unit The LDAP path in which to place this account
2228 * @return 0 upon success, or non-zero otherwise
2231 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2232 const char *machine_name,
2233 const char *machine_password,
2234 const char *org_unit,
2235 uint32_t etype_list,
2236 const char *dns_domain_name)
2239 char *samAccountName = NULL;
2240 char *controlstr = NULL;
2241 TALLOC_CTX *ctx = NULL;
2243 char *machine_escaped = NULL;
2244 char *dns_hostname = NULL;
2245 char *new_dn = NULL;
2246 char *utf8_pw = NULL;
2247 size_t utf8_pw_len = 0;
2248 char *utf16_pw = NULL;
2249 size_t utf16_pw_len = 0;
2250 struct berval machine_pw_val;
2252 const char **spn_array = NULL;
2253 size_t num_spns = 0;
2254 const char *spn_prefix[] = {
2256 "RestrictedKrbHost",
2259 LDAPMessage *res = NULL;
2260 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2262 ctx = talloc_init("ads_add_machine_acct");
2264 return ADS_ERROR(LDAP_NO_MEMORY);
2267 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2268 if (machine_escaped == NULL) {
2269 ret = ADS_ERROR(LDAP_NO_MEMORY);
2273 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2274 if (utf8_pw == NULL) {
2275 ret = ADS_ERROR(LDAP_NO_MEMORY);
2278 utf8_pw_len = strlen(utf8_pw);
2280 ok = convert_string_talloc(ctx,
2281 CH_UTF8, CH_UTF16MUNGED,
2282 utf8_pw, utf8_pw_len,
2283 (void *)&utf16_pw, &utf16_pw_len);
2285 ret = ADS_ERROR(LDAP_NO_MEMORY);
2289 machine_pw_val = (struct berval) {
2291 .bv_len = utf16_pw_len,
2294 /* Check if the machine account already exists. */
2295 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2296 if (ADS_ERR_OK(ret)) {
2297 /* Change the machine account password */
2298 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2299 ads_msgfree(ads, res);
2303 ads_msgfree(ads, res);
2305 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2306 if (new_dn == NULL) {
2307 ret = ADS_ERROR(LDAP_NO_MEMORY);
2311 /* Create machine account */
2313 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2314 if (samAccountName == NULL) {
2315 ret = ADS_ERROR(LDAP_NO_MEMORY);
2319 dns_hostname = talloc_asprintf(ctx,
2323 if (dns_hostname == NULL) {
2324 ret = ADS_ERROR(LDAP_NO_MEMORY);
2328 /* Add dns_hostname SPNs */
2329 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2330 char *spn = talloc_asprintf(ctx,
2335 ret = ADS_ERROR(LDAP_NO_MEMORY);
2339 ok = add_string_to_array(spn_array,
2344 ret = ADS_ERROR(LDAP_NO_MEMORY);
2349 /* Add machine_name SPNs */
2350 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2351 char *spn = talloc_asprintf(ctx,
2356 ret = ADS_ERROR(LDAP_NO_MEMORY);
2360 ok = add_string_to_array(spn_array,
2365 ret = ADS_ERROR(LDAP_NO_MEMORY);
2370 /* Make sure to NULL terminate the array */
2371 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2372 if (spn_array == NULL) {
2373 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2375 spn_array[num_spns] = NULL;
2377 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2378 if (controlstr == NULL) {
2379 ret = ADS_ERROR(LDAP_NO_MEMORY);
2383 mods = ads_init_mods(ctx);
2385 ret = ADS_ERROR(LDAP_NO_MEMORY);
2389 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2390 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2391 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2392 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2393 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2394 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2396 ret = ads_gen_add(ads, new_dn, mods);
2399 SAFE_FREE(machine_escaped);
2400 talloc_destroy(ctx);
2406 * move a machine account to another OU on the ADS server
2407 * @param ads - An intialized ADS_STRUCT
2408 * @param machine_name - the NetBIOS machine name of this account.
2409 * @param org_unit - The LDAP path in which to place this account
2410 * @param moved - whether we moved the machine account (optional)
2411 * @return 0 upon success, or non-zero otherwise
2414 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2415 const char *org_unit, bool *moved)
2419 LDAPMessage *res = NULL;
2420 char *filter = NULL;
2421 char *computer_dn = NULL;
2423 char *computer_rdn = NULL;
2424 bool need_move = False;
2426 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2427 rc = ADS_ERROR(LDAP_NO_MEMORY);
2431 /* Find pre-existing machine */
2432 rc = ads_search(ads, &res, filter, NULL);
2433 if (!ADS_ERR_OK(rc)) {
2437 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2439 rc = ADS_ERROR(LDAP_NO_MEMORY);
2443 parent_dn = ads_parent_dn(computer_dn);
2444 if (strequal(parent_dn, org_unit)) {
2450 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2451 rc = ADS_ERROR(LDAP_NO_MEMORY);
2455 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2456 org_unit, 1, NULL, NULL);
2457 rc = ADS_ERROR(ldap_status);
2460 ads_msgfree(ads, res);
2462 TALLOC_FREE(computer_dn);
2463 SAFE_FREE(computer_rdn);
2465 if (!ADS_ERR_OK(rc)) {
2477 dump a binary result from ldap
2479 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2482 for (i=0; values[i]; i++) {
2484 printf("%s: ", field);
2485 for (j=0; j<values[i]->bv_len; j++) {
2486 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2492 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2495 for (i=0; values[i]; i++) {
2497 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2500 status = GUID_from_ndr_blob(&in, &guid);
2501 if (NT_STATUS_IS_OK(status)) {
2502 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2504 printf("%s: INVALID GUID\n", field);
2510 dump a sid result from ldap
2512 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2515 for (i=0; values[i]; i++) {
2518 struct dom_sid_buf tmp;
2519 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2520 values[i]->bv_len, &sid);
2524 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2529 dump ntSecurityDescriptor
2531 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2533 TALLOC_CTX *frame = talloc_stackframe();
2534 struct security_descriptor *psd;
2537 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2538 values[0]->bv_len, &psd);
2539 if (!NT_STATUS_IS_OK(status)) {
2540 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2541 nt_errstr(status)));
2547 ads_disp_sd(ads, talloc_tos(), psd);
2554 dump a string result from ldap
2556 static void dump_string(const char *field, char **values)
2559 for (i=0; values[i]; i++) {
2560 printf("%s: %s\n", field, values[i]);
2565 dump a field from LDAP on stdout
2569 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2574 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2576 {"objectGUID", False, dump_guid},
2577 {"netbootGUID", False, dump_guid},
2578 {"nTSecurityDescriptor", False, dump_sd},
2579 {"dnsRecord", False, dump_binary},
2580 {"objectSid", False, dump_sid},
2581 {"tokenGroups", False, dump_sid},
2582 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2583 {"tokengroupsGlobalandUniversal", False, dump_sid},
2584 {"mS-DS-CreatorSID", False, dump_sid},
2585 {"msExchMailboxGuid", False, dump_guid},
2590 if (!field) { /* must be end of an entry */
2595 for (i=0; handlers[i].name; i++) {
2596 if (strcasecmp_m(handlers[i].name, field) == 0) {
2597 if (!values) /* first time, indicate string or not */
2598 return handlers[i].string;
2599 handlers[i].handler(ads, field, (struct berval **) values);
2603 if (!handlers[i].name) {
2604 if (!values) /* first time, indicate string conversion */
2606 dump_string(field, (char **)values);
2612 * Dump a result from LDAP on stdout
2613 * used for debugging
2614 * @param ads connection to ads server
2615 * @param res Results to dump
2618 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2620 ads_process_results(ads, res, ads_dump_field, NULL);
2624 * Walk through results, calling a function for each entry found.
2625 * The function receives a field name, a berval * array of values,
2626 * and a data area passed through from the start. The function is
2627 * called once with null for field and values at the end of each
2629 * @param ads connection to ads server
2630 * @param res Results to process
2631 * @param fn Function for processing each result
2632 * @param data_area user-defined area to pass to function
2634 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2635 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2640 size_t converted_size;
2642 if (!(ctx = talloc_init("ads_process_results")))
2645 for (msg = ads_first_entry(ads, res); msg;
2646 msg = ads_next_entry(ads, msg)) {
2650 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2651 (LDAPMessage *)msg,&b);
2653 utf8_field=ldap_next_attribute(ads->ldap.ld,
2654 (LDAPMessage *)msg,b)) {
2655 struct berval **ber_vals;
2661 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2664 DEBUG(0,("ads_process_results: "
2665 "pull_utf8_talloc failed: %s",
2669 string = fn(ads, field, NULL, data_area);
2674 utf8_vals = ldap_get_values(ads->ldap.ld,
2675 (LDAPMessage *)msg, field);
2676 p = discard_const_p(const char *, utf8_vals);
2677 str_vals = ads_pull_strvals(ctx, p);
2678 fn(ads, field, (void **) str_vals, data_area);
2679 ldap_value_free(utf8_vals);
2681 ber_vals = ldap_get_values_len(ads->ldap.ld,
2682 (LDAPMessage *)msg, field);
2683 fn(ads, field, (void **) ber_vals, data_area);
2685 ldap_value_free_len(ber_vals);
2687 ldap_memfree(utf8_field);
2690 talloc_free_children(ctx);
2691 fn(ads, NULL, NULL, data_area); /* completed an entry */
2694 talloc_destroy(ctx);
2698 * count how many replies are in a LDAPMessage
2699 * @param ads connection to ads server
2700 * @param res Results to count
2701 * @return number of replies
2703 int ads_count_replies(ADS_STRUCT *ads, void *res)
2705 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2709 * pull the first entry from a ADS result
2710 * @param ads connection to ads server
2711 * @param res Results of search
2712 * @return first entry from result
2714 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2716 return ldap_first_entry(ads->ldap.ld, res);
2720 * pull the next entry from a ADS result
2721 * @param ads connection to ads server
2722 * @param res Results of search
2723 * @return next entry from result
2725 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2727 return ldap_next_entry(ads->ldap.ld, res);
2731 * pull the first message from a ADS result
2732 * @param ads connection to ads server
2733 * @param res Results of search
2734 * @return first message from result
2736 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2738 return ldap_first_message(ads->ldap.ld, res);
2742 * pull the next message from a ADS result
2743 * @param ads connection to ads server
2744 * @param res Results of search
2745 * @return next message from result
2747 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2749 return ldap_next_message(ads->ldap.ld, res);
2753 * pull a single string from a ADS result
2754 * @param ads connection to ads server
2755 * @param mem_ctx TALLOC_CTX to use for allocating result string
2756 * @param msg Results of search
2757 * @param field Attribute to retrieve
2758 * @return Result string in talloc context
2760 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2766 size_t converted_size;
2768 values = ldap_get_values(ads->ldap.ld, msg, field);
2772 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2777 ldap_value_free(values);
2782 * pull an array of strings from a ADS result
2783 * @param ads connection to ads server
2784 * @param mem_ctx TALLOC_CTX to use for allocating result string
2785 * @param msg Results of search
2786 * @param field Attribute to retrieve
2787 * @return Result strings in talloc context
2789 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2790 LDAPMessage *msg, const char *field,
2795 size_t i, converted_size;
2797 values = ldap_get_values(ads->ldap.ld, msg, field);
2801 *num_values = ldap_count_values(values);
2803 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2805 ldap_value_free(values);
2809 for (i=0;i<*num_values;i++) {
2810 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2813 ldap_value_free(values);
2819 ldap_value_free(values);
2824 * pull an array of strings from a ADS result
2825 * (handle large multivalue attributes with range retrieval)
2826 * @param ads connection to ads server
2827 * @param mem_ctx TALLOC_CTX to use for allocating result string
2828 * @param msg Results of search
2829 * @param field Attribute to retrieve
2830 * @param current_strings strings returned by a previous call to this function
2831 * @param next_attribute The next query should ask for this attribute
2832 * @param num_values How many values did we get this time?
2833 * @param more_values Are there more values to get?
2834 * @return Result strings in talloc context
2836 char **ads_pull_strings_range(ADS_STRUCT *ads,
2837 TALLOC_CTX *mem_ctx,
2838 LDAPMessage *msg, const char *field,
2839 char **current_strings,
2840 const char **next_attribute,
2841 size_t *num_strings,
2845 char *expected_range_attrib, *range_attr;
2846 BerElement *ptr = NULL;
2849 size_t num_new_strings;
2850 unsigned long int range_start;
2851 unsigned long int range_end;
2853 /* we might have been given the whole lot anyway */
2854 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2855 *more_strings = False;
2859 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2861 /* look for Range result */
2862 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2864 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2865 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2866 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2874 /* nothing here - this field is just empty */
2875 *more_strings = False;
2879 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2880 &range_start, &range_end) == 2) {
2881 *more_strings = True;
2883 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2884 &range_start) == 1) {
2885 *more_strings = False;
2887 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2889 ldap_memfree(range_attr);
2890 *more_strings = False;
2895 if ((*num_strings) != range_start) {
2896 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2897 " - aborting range retreival\n",
2898 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2899 ldap_memfree(range_attr);
2900 *more_strings = False;
2904 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2906 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2907 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2908 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2909 range_attr, (unsigned long int)range_end - range_start + 1,
2910 (unsigned long int)num_new_strings));
2911 ldap_memfree(range_attr);
2912 *more_strings = False;
2916 strings = talloc_realloc(mem_ctx, current_strings, char *,
2917 *num_strings + num_new_strings);
2919 if (strings == NULL) {
2920 ldap_memfree(range_attr);
2921 *more_strings = False;
2925 if (new_strings && num_new_strings) {
2926 memcpy(&strings[*num_strings], new_strings,
2927 sizeof(*new_strings) * num_new_strings);
2930 (*num_strings) += num_new_strings;
2932 if (*more_strings) {
2933 *next_attribute = talloc_asprintf(mem_ctx,
2938 if (!*next_attribute) {
2939 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2940 ldap_memfree(range_attr);
2941 *more_strings = False;
2946 ldap_memfree(range_attr);
2952 * pull a single uint32_t from a ADS result
2953 * @param ads connection to ads server
2954 * @param msg Results of search
2955 * @param field Attribute to retrieve
2956 * @param v Pointer to int to store result
2957 * @return boolean inidicating success
2959 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2964 values = ldap_get_values(ads->ldap.ld, msg, field);
2968 ldap_value_free(values);
2972 *v = atoi(values[0]);
2973 ldap_value_free(values);
2978 * pull a single objectGUID from an ADS result
2979 * @param ads connection to ADS server
2980 * @param msg results of search
2981 * @param guid 37-byte area to receive text guid
2982 * @return boolean indicating success
2984 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2989 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2994 status = GUID_from_ndr_blob(&blob, guid);
2995 talloc_free(blob.data);
2996 return NT_STATUS_IS_OK(status);
3001 * pull a single struct dom_sid from a ADS result
3002 * @param ads connection to ads server
3003 * @param msg Results of search
3004 * @param field Attribute to retrieve
3005 * @param sid Pointer to sid to store result
3006 * @return boolean inidicating success
3008 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3009 struct dom_sid *sid)
3011 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3015 * pull an array of struct dom_sids from a ADS result
3016 * @param ads connection to ads server
3017 * @param mem_ctx TALLOC_CTX for allocating sid array
3018 * @param msg Results of search
3019 * @param field Attribute to retrieve
3020 * @param sids pointer to sid array to allocate
3021 * @return the count of SIDs pulled
3023 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3024 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3026 struct berval **values;
3029 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3034 for (i=0; values[i]; i++)
3038 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3040 ldap_value_free_len(values);
3048 for (i=0; values[i]; i++) {
3050 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3051 values[i]->bv_len, &(*sids)[count]);
3053 struct dom_sid_buf buf;
3054 DBG_DEBUG("pulling SID: %s\n",
3055 dom_sid_str_buf(&(*sids)[count], &buf));
3060 ldap_value_free_len(values);
3065 * pull a struct security_descriptor from a ADS result
3066 * @param ads connection to ads server
3067 * @param mem_ctx TALLOC_CTX for allocating sid array
3068 * @param msg Results of search
3069 * @param field Attribute to retrieve
3070 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3071 * @return boolean inidicating success
3073 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3074 LDAPMessage *msg, const char *field,
3075 struct security_descriptor **sd)
3077 struct berval **values;
3080 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3082 if (!values) return false;
3086 status = unmarshall_sec_desc(mem_ctx,
3087 (uint8_t *)values[0]->bv_val,
3088 values[0]->bv_len, sd);
3089 if (!NT_STATUS_IS_OK(status)) {
3090 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3091 nt_errstr(status)));
3096 ldap_value_free_len(values);
3101 * in order to support usernames longer than 21 characters we need to
3102 * use both the sAMAccountName and the userPrincipalName attributes
3103 * It seems that not all users have the userPrincipalName attribute set
3105 * @param ads connection to ads server
3106 * @param mem_ctx TALLOC_CTX for allocating sid array
3107 * @param msg Results of search
3108 * @return the username
3110 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3116 /* lookup_name() only works on the sAMAccountName to
3117 returning the username portion of userPrincipalName
3118 breaks winbindd_getpwnam() */
3120 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3121 if (ret && (p = strchr_m(ret, '@'))) {
3126 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3131 * find the update serial number - this is the core of the ldap cache
3132 * @param ads connection to ads server
3133 * @param ads connection to ADS server
3134 * @param usn Pointer to retrieved update serial number
3135 * @return status of search
3137 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3139 const char *attrs[] = {"highestCommittedUSN", NULL};
3143 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3144 if (!ADS_ERR_OK(status))
3147 if (ads_count_replies(ads, res) != 1) {
3148 ads_msgfree(ads, res);
3149 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3152 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3153 ads_msgfree(ads, res);
3154 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3157 ads_msgfree(ads, res);
3161 /* parse a ADS timestring - typical string is
3162 '20020917091222.0Z0' which means 09:12.22 17th September
3164 static time_t ads_parse_time(const char *str)
3170 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3171 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3172 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3181 /********************************************************************
3182 ********************************************************************/
3184 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3186 const char *attrs[] = {"currentTime", NULL};
3191 ADS_STRUCT *ads_s = ads;
3193 if (!(ctx = talloc_init("ads_current_time"))) {
3194 return ADS_ERROR(LDAP_NO_MEMORY);
3197 /* establish a new ldap tcp session if necessary */
3199 if ( !ads->ldap.ld ) {
3200 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3201 ads->server.ldap_server, ADS_SASL_PLAIN )) == NULL )
3203 status = ADS_ERROR(LDAP_NO_MEMORY);
3206 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3207 status = ads_connect( ads_s );
3208 if ( !ADS_ERR_OK(status))
3212 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3213 if (!ADS_ERR_OK(status)) {
3217 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
3219 ads_msgfree(ads_s, res);
3220 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3224 /* but save the time and offset in the original ADS_STRUCT */
3226 ads->config.current_time = ads_parse_time(timestr);
3228 if (ads->config.current_time != 0) {
3229 ads->auth.time_offset = ads->config.current_time - time(NULL);
3230 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3233 ads_msgfree(ads, res);
3235 status = ADS_SUCCESS;
3238 /* free any temporary ads connections */
3239 if ( ads_s != ads ) {
3240 ads_destroy( &ads_s );
3242 talloc_destroy(ctx);
3247 /********************************************************************
3248 ********************************************************************/
3250 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3252 const char *attrs[] = {"domainFunctionality", NULL};
3255 ADS_STRUCT *ads_s = ads;
3257 *val = DS_DOMAIN_FUNCTION_2000;
3259 /* establish a new ldap tcp session if necessary */
3261 if ( !ads->ldap.ld ) {
3262 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3263 ads->server.ldap_server, ADS_SASL_PLAIN )) == NULL )
3265 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3268 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3269 status = ads_connect( ads_s );
3270 if ( !ADS_ERR_OK(status))
3274 /* If the attribute does not exist assume it is a Windows 2000
3275 functional domain */
3277 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3278 if (!ADS_ERR_OK(status)) {
3279 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3280 status = ADS_SUCCESS;
3285 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3286 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3288 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3291 ads_msgfree(ads, res);
3294 /* free any temporary ads connections */
3295 if ( ads_s != ads ) {
3296 ads_destroy( &ads_s );
3303 * find the domain sid for our domain
3304 * @param ads connection to ads server
3305 * @param sid Pointer to domain sid
3306 * @return status of search
3308 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3310 const char *attrs[] = {"objectSid", NULL};
3314 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3316 if (!ADS_ERR_OK(rc)) return rc;
3317 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3318 ads_msgfree(ads, res);
3319 return ADS_ERROR_SYSTEM(ENOENT);
3321 ads_msgfree(ads, res);
3327 * find our site name
3328 * @param ads connection to ads server
3329 * @param mem_ctx Pointer to talloc context
3330 * @param site_name Pointer to the sitename
3331 * @return status of search
3333 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3337 const char *dn, *service_name;
3338 const char *attrs[] = { "dsServiceName", NULL };
3340 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3341 if (!ADS_ERR_OK(status)) {
3345 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3346 if (service_name == NULL) {
3347 ads_msgfree(ads, res);
3348 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3351 ads_msgfree(ads, res);
3353 /* go up three levels */
3354 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3356 return ADS_ERROR(LDAP_NO_MEMORY);
3359 *site_name = talloc_strdup(mem_ctx, dn);
3360 if (*site_name == NULL) {
3361 return ADS_ERROR(LDAP_NO_MEMORY);
3366 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3371 * find the site dn where a machine resides
3372 * @param ads connection to ads server
3373 * @param mem_ctx Pointer to talloc context
3374 * @param computer_name name of the machine
3375 * @param site_name Pointer to the sitename
3376 * @return status of search
3378 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3382 const char *parent, *filter;
3383 char *config_context = NULL;
3386 /* shortcut a query */
3387 if (strequal(computer_name, ads->config.ldap_server_name)) {
3388 return ads_site_dn(ads, mem_ctx, site_dn);
3391 status = ads_config_path(ads, mem_ctx, &config_context);
3392 if (!ADS_ERR_OK(status)) {
3396 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3397 if (filter == NULL) {
3398 return ADS_ERROR(LDAP_NO_MEMORY);
3401 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3402 filter, NULL, &res);
3403 if (!ADS_ERR_OK(status)) {
3407 if (ads_count_replies(ads, res) != 1) {
3408 ads_msgfree(ads, res);
3409 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3412 dn = ads_get_dn(ads, mem_ctx, res);
3414 ads_msgfree(ads, res);
3415 return ADS_ERROR(LDAP_NO_MEMORY);
3418 /* go up three levels */
3419 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3420 if (parent == NULL) {
3421 ads_msgfree(ads, res);
3423 return ADS_ERROR(LDAP_NO_MEMORY);
3426 *site_dn = talloc_strdup(mem_ctx, parent);
3427 if (*site_dn == NULL) {
3428 ads_msgfree(ads, res);
3430 return ADS_ERROR(LDAP_NO_MEMORY);
3434 ads_msgfree(ads, res);
3440 * get the upn suffixes for a domain
3441 * @param ads connection to ads server
3442 * @param mem_ctx Pointer to talloc context
3443 * @param suffixes Pointer to an array of suffixes
3444 * @param num_suffixes Pointer to the number of suffixes
3445 * @return status of search
3447 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3452 char *config_context = NULL;
3453 const char *attrs[] = { "uPNSuffixes", NULL };
3455 status = ads_config_path(ads, mem_ctx, &config_context);
3456 if (!ADS_ERR_OK(status)) {
3460 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3462 return ADS_ERROR(LDAP_NO_MEMORY);
3465 status = ads_search_dn(ads, &res, base, attrs);
3466 if (!ADS_ERR_OK(status)) {
3470 if (ads_count_replies(ads, res) != 1) {
3471 ads_msgfree(ads, res);
3472 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3475 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3476 if ((*suffixes) == NULL) {
3477 ads_msgfree(ads, res);
3478 return ADS_ERROR(LDAP_NO_MEMORY);
3481 ads_msgfree(ads, res);
3487 * get the joinable ous for a domain
3488 * @param ads connection to ads server
3489 * @param mem_ctx Pointer to talloc context
3490 * @param ous Pointer to an array of ous
3491 * @param num_ous Pointer to the number of ous
3492 * @return status of search
3494 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3495 TALLOC_CTX *mem_ctx,
3500 LDAPMessage *res = NULL;
3501 LDAPMessage *msg = NULL;
3502 const char *attrs[] = { "dn", NULL };
3505 status = ads_search(ads, &res,
3506 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3508 if (!ADS_ERR_OK(status)) {
3512 count = ads_count_replies(ads, res);
3514 ads_msgfree(ads, res);
3515 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3518 for (msg = ads_first_entry(ads, res); msg;
3519 msg = ads_next_entry(ads, msg)) {
3520 const char **p = discard_const_p(const char *, *ous);
3523 dn = ads_get_dn(ads, talloc_tos(), msg);
3525 ads_msgfree(ads, res);
3526 return ADS_ERROR(LDAP_NO_MEMORY);
3529 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3531 ads_msgfree(ads, res);
3532 return ADS_ERROR(LDAP_NO_MEMORY);
3536 *ous = discard_const_p(char *, p);
3539 ads_msgfree(ads, res);
3546 * pull a struct dom_sid from an extended dn string
3547 * @param mem_ctx TALLOC_CTX
3548 * @param extended_dn string
3549 * @param flags string type of extended_dn
3550 * @param sid pointer to a struct dom_sid
3551 * @return NT_STATUS_OK on success,
3552 * NT_INVALID_PARAMETER on error,
3553 * NT_STATUS_NOT_FOUND if no SID present
3555 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3556 const char *extended_dn,
3557 enum ads_extended_dn_flags flags,
3558 struct dom_sid *sid)
3563 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3566 /* otherwise extended_dn gets stripped off */
3567 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3568 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3571 * ADS_EXTENDED_DN_HEX_STRING:
3572 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3574 * ADS_EXTENDED_DN_STRING (only with w2k3):
3575 * <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
3577 * Object with no SID, such as an Exchange Public Folder
3578 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3581 p = strchr(dn, ';');
3583 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3586 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3587 DEBUG(5,("No SID present in extended dn\n"));
3588 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3591 p += strlen(";<SID=");
3595 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3600 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3604 case ADS_EXTENDED_DN_STRING:
3605 if (!string_to_sid(sid, p)) {
3606 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3609 case ADS_EXTENDED_DN_HEX_STRING: {
3614 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3616 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3619 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3621 DEBUG(10,("failed to parse sid\n"));
3622 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3627 DEBUG(10,("unknown extended dn format\n"));
3628 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3631 return ADS_ERROR_NT(NT_STATUS_OK);
3634 /********************************************************************
3635 ********************************************************************/
3637 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3639 LDAPMessage *res = NULL;
3644 status = ads_find_machine_acct(ads, &res, machine_name);
3645 if (!ADS_ERR_OK(status)) {
3646 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3647 lp_netbios_name()));
3651 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3652 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3656 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3657 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3661 ads_msgfree(ads, res);
3666 /********************************************************************
3667 ********************************************************************/
3669 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3671 LDAPMessage *res = NULL;
3676 status = ads_find_machine_acct(ads, &res, machine_name);
3677 if (!ADS_ERR_OK(status)) {
3678 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3679 lp_netbios_name()));
3683 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3684 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3688 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3689 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3693 ads_msgfree(ads, res);
3698 /********************************************************************
3699 ********************************************************************/
3701 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3703 LDAPMessage *res = NULL;
3709 status = ads_find_machine_acct(ads, &res, machine_name);
3710 if (!ADS_ERR_OK(status)) {
3711 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3712 lp_netbios_name()));
3716 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3717 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3721 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3722 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3726 ads_msgfree(ads, res);
3728 ok = (strlen(name) > 0);
3736 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3739 * Join a machine to a realm
3740 * Creates the machine account and sets the machine password
3741 * @param ads connection to ads server
3742 * @param machine name of host to add
3743 * @param org_unit Organizational unit to place machine in
3744 * @return status of join
3746 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3747 uint32_t account_type, const char *org_unit)
3750 LDAPMessage *res = NULL;
3753 /* machine name must be lowercase */
3754 machine = SMB_STRDUP(machine_name);
3755 strlower_m(machine);
3758 status = ads_find_machine_acct(ads, (void **)&res, machine);
3759 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3760 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3761 status = ads_leave_realm(ads, machine);
3762 if (!ADS_ERR_OK(status)) {
3763 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3764 machine, ads->config.realm));
3769 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3770 if (!ADS_ERR_OK(status)) {
3771 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3776 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3777 if (!ADS_ERR_OK(status)) {
3778 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3784 ads_msgfree(ads, res);
3791 * Delete a machine from the realm
3792 * @param ads connection to ads server
3793 * @param hostname Machine to remove
3794 * @return status of delete
3796 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3801 char *hostnameDN, *host;
3803 LDAPControl ldap_control;
3804 LDAPControl * pldap_control[2] = {NULL, NULL};
3806 pldap_control[0] = &ldap_control;
3807 memset(&ldap_control, 0, sizeof(LDAPControl));
3808 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3810 /* hostname must be lowercase */
3811 host = SMB_STRDUP(hostname);
3812 if (!strlower_m(host)) {
3814 return ADS_ERROR_SYSTEM(EINVAL);
3817 status = ads_find_machine_acct(ads, &res, host);
3818 if (!ADS_ERR_OK(status)) {
3819 DEBUG(0, ("Host account for %s does not exist.\n", host));
3824 msg = ads_first_entry(ads, res);
3827 return ADS_ERROR_SYSTEM(ENOENT);
3830 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3831 if (hostnameDN == NULL) {
3833 return ADS_ERROR_SYSTEM(ENOENT);
3836 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3838 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3840 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3843 if (rc != LDAP_SUCCESS) {
3844 const char *attrs[] = { "cn", NULL };
3845 LDAPMessage *msg_sub;
3847 /* we only search with scope ONE, we do not expect any further
3848 * objects to be created deeper */
3850 status = ads_do_search_retry(ads, hostnameDN,
3851 LDAP_SCOPE_ONELEVEL,
3852 "(objectclass=*)", attrs, &res);
3854 if (!ADS_ERR_OK(status)) {
3856 TALLOC_FREE(hostnameDN);
3860 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3861 msg_sub = ads_next_entry(ads, msg_sub)) {
3865 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3867 TALLOC_FREE(hostnameDN);
3868 return ADS_ERROR(LDAP_NO_MEMORY);
3871 status = ads_del_dn(ads, dn);
3872 if (!ADS_ERR_OK(status)) {
3873 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3876 TALLOC_FREE(hostnameDN);
3883 /* there should be no subordinate objects anymore */
3884 status = ads_do_search_retry(ads, hostnameDN,
3885 LDAP_SCOPE_ONELEVEL,
3886 "(objectclass=*)", attrs, &res);
3888 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3890 TALLOC_FREE(hostnameDN);
3894 /* delete hostnameDN now */
3895 status = ads_del_dn(ads, hostnameDN);
3896 if (!ADS_ERR_OK(status)) {
3898 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3899 TALLOC_FREE(hostnameDN);
3904 TALLOC_FREE(hostnameDN);
3906 status = ads_find_machine_acct(ads, &res, host);
3907 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
3908 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
3909 DEBUG(3, ("Failed to remove host account.\n"));
3919 * pull all token-sids from an LDAP dn
3920 * @param ads connection to ads server
3921 * @param mem_ctx TALLOC_CTX for allocating sid array
3922 * @param dn of LDAP object
3923 * @param user_sid pointer to struct dom_sid (objectSid)
3924 * @param primary_group_sid pointer to struct dom_sid (self composed)
3925 * @param sids pointer to sid array to allocate
3926 * @param num_sids counter of SIDs pulled
3927 * @return status of token query
3929 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3930 TALLOC_CTX *mem_ctx,
3932 struct dom_sid *user_sid,
3933 struct dom_sid *primary_group_sid,
3934 struct dom_sid **sids,
3938 LDAPMessage *res = NULL;
3940 size_t tmp_num_sids;
3941 struct dom_sid *tmp_sids;
3942 struct dom_sid tmp_user_sid;
3943 struct dom_sid tmp_primary_group_sid;
3945 const char *attrs[] = {
3952 status = ads_search_retry_dn(ads, &res, dn, attrs);
3953 if (!ADS_ERR_OK(status)) {
3957 count = ads_count_replies(ads, res);
3959 ads_msgfree(ads, res);
3960 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3963 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3964 ads_msgfree(ads, res);
3965 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3968 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3969 ads_msgfree(ads, res);
3970 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3974 /* hack to compose the primary group sid without knowing the
3977 struct dom_sid domsid;
3979 sid_copy(&domsid, &tmp_user_sid);
3981 if (!sid_split_rid(&domsid, NULL)) {
3982 ads_msgfree(ads, res);
3983 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3986 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3987 ads_msgfree(ads, res);
3988 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3992 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3994 if (tmp_num_sids == 0 || !tmp_sids) {
3995 ads_msgfree(ads, res);
3996 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4000 *num_sids = tmp_num_sids;
4008 *user_sid = tmp_user_sid;
4011 if (primary_group_sid) {
4012 *primary_group_sid = tmp_primary_group_sid;
4015 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4017 ads_msgfree(ads, res);
4018 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4022 * Find a sAMAccoutName in LDAP
4023 * @param ads connection to ads server
4024 * @param mem_ctx TALLOC_CTX for allocating sid array
4025 * @param samaccountname to search
4026 * @param uac_ret uint32_t pointer userAccountControl attribute value
4027 * @param dn_ret pointer to dn
4028 * @return status of token query
4030 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4031 TALLOC_CTX *mem_ctx,
4032 const char *samaccountname,
4034 const char **dn_ret)
4037 const char *attrs[] = { "userAccountControl", NULL };
4039 LDAPMessage *res = NULL;
4043 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4045 if (filter == NULL) {
4046 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4050 status = ads_do_search_all(ads, ads->config.bind_path,
4052 filter, attrs, &res);
4054 if (!ADS_ERR_OK(status)) {
4058 if (ads_count_replies(ads, res) != 1) {
4059 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4063 dn = ads_get_dn(ads, talloc_tos(), res);
4065 status = ADS_ERROR(LDAP_NO_MEMORY);
4069 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4070 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4079 *dn_ret = talloc_strdup(mem_ctx, dn);
4081 status = ADS_ERROR(LDAP_NO_MEMORY);
4087 ads_msgfree(ads, res);
4093 * find our configuration path
4094 * @param ads connection to ads server
4095 * @param mem_ctx Pointer to talloc context
4096 * @param config_path Pointer to the config path
4097 * @return status of search
4099 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4100 TALLOC_CTX *mem_ctx,
4104 LDAPMessage *res = NULL;
4105 const char *config_context = NULL;
4106 const char *attrs[] = { "configurationNamingContext", NULL };
4108 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4109 "(objectclass=*)", attrs, &res);
4110 if (!ADS_ERR_OK(status)) {
4114 config_context = ads_pull_string(ads, mem_ctx, res,
4115 "configurationNamingContext");
4116 ads_msgfree(ads, res);
4117 if (!config_context) {
4118 return ADS_ERROR(LDAP_NO_MEMORY);
4122 *config_path = talloc_strdup(mem_ctx, config_context);
4123 if (!*config_path) {
4124 return ADS_ERROR(LDAP_NO_MEMORY);
4128 return ADS_ERROR(LDAP_SUCCESS);
4132 * find the displayName of an extended right
4133 * @param ads connection to ads server
4134 * @param config_path The config path
4135 * @param mem_ctx Pointer to talloc context
4136 * @param GUID struct of the rightsGUID
4137 * @return status of search
4139 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4140 const char *config_path,
4141 TALLOC_CTX *mem_ctx,
4142 const struct GUID *rights_guid)
4145 LDAPMessage *res = NULL;
4147 const char *attrs[] = { "displayName", NULL };
4148 const char *result = NULL;
4151 if (!ads || !mem_ctx || !rights_guid) {
4155 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4156 GUID_string(mem_ctx, rights_guid));
4161 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4166 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4168 if (!ADS_ERR_OK(rc)) {
4172 if (ads_count_replies(ads, res) != 1) {
4176 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4179 ads_msgfree(ads, res);
4184 * verify or build and verify an account ou
4185 * @param mem_ctx Pointer to talloc context
4186 * @param ads connection to ads server
4188 * @return status of search
4191 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4193 const char **account_ou)
4199 if (account_ou == NULL) {
4200 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4203 if (*account_ou != NULL) {
4204 exploded_dn = ldap_explode_dn(*account_ou, 0);
4206 ldap_value_free(exploded_dn);
4211 ou_string = ads_ou_string(ads, *account_ou);
4213 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4216 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4217 ads->config.bind_path);
4218 SAFE_FREE(ou_string);
4221 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4224 exploded_dn = ldap_explode_dn(name, 0);
4226 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4228 ldap_value_free(exploded_dn);