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;
370 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
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;
417 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
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[] = {"*", "msDS-SupportedEncryptionTypes", "nTSecurityDescriptor", NULL};
1373 /* the easiest way to find a machine account anywhere in the tree
1374 is to look for hostname$ */
1375 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1376 DEBUG(1, ("asprintf failed!\n"));
1377 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1380 status = ads_search(ads, res, expr, attrs);
1386 * Initialize a list of mods to be used in a modify request
1387 * @param ctx An initialized TALLOC_CTX
1388 * @return allocated ADS_MODLIST
1390 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1392 #define ADS_MODLIST_ALLOC_SIZE 10
1395 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1396 /* -1 is safety to make sure we don't go over the end.
1397 need to reset it to NULL before doing ldap modify */
1398 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1400 return (ADS_MODLIST)mods;
1405 add an attribute to the list, with values list already constructed
1407 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1408 int mod_op, const char *name,
1409 const void *_invals)
1412 LDAPMod **modlist = (LDAPMod **) *mods;
1413 struct berval **ber_values = NULL;
1414 char **char_values = NULL;
1417 mod_op = LDAP_MOD_DELETE;
1419 if (mod_op & LDAP_MOD_BVALUES) {
1420 const struct berval **b;
1421 b = discard_const_p(const struct berval *, _invals);
1422 ber_values = ads_dup_values(ctx, b);
1425 c = discard_const_p(const char *, _invals);
1426 char_values = ads_push_strvals(ctx, c);
1430 /* find the first empty slot */
1431 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1433 if (modlist[curmod] == (LDAPMod *) -1) {
1434 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1435 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1436 return ADS_ERROR(LDAP_NO_MEMORY);
1437 memset(&modlist[curmod], 0,
1438 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1439 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1440 *mods = (ADS_MODLIST)modlist;
1443 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1444 return ADS_ERROR(LDAP_NO_MEMORY);
1445 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1446 if (mod_op & LDAP_MOD_BVALUES) {
1447 modlist[curmod]->mod_bvalues = ber_values;
1448 } else if (mod_op & LDAP_MOD_DELETE) {
1449 modlist[curmod]->mod_values = NULL;
1451 modlist[curmod]->mod_values = char_values;
1454 modlist[curmod]->mod_op = mod_op;
1455 return ADS_ERROR(LDAP_SUCCESS);
1459 * Add a single string value to a mod list
1460 * @param ctx An initialized TALLOC_CTX
1461 * @param mods An initialized ADS_MODLIST
1462 * @param name The attribute name to add
1463 * @param val The value to add - NULL means DELETE
1464 * @return ADS STATUS indicating success of add
1466 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1467 const char *name, const char *val)
1469 const char *values[2];
1475 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1476 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1480 * Add an array of string values to a mod list
1481 * @param ctx An initialized TALLOC_CTX
1482 * @param mods An initialized ADS_MODLIST
1483 * @param name The attribute name to add
1484 * @param vals The array of string values to add - NULL means DELETE
1485 * @return ADS STATUS indicating success of add
1487 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1488 const char *name, const char **vals)
1491 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1492 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1493 name, (const void **) vals);
1498 * Add a single ber-encoded value to a mod list
1499 * @param ctx An initialized TALLOC_CTX
1500 * @param mods An initialized ADS_MODLIST
1501 * @param name The attribute name to add
1502 * @param val The value to add - NULL means DELETE
1503 * @return ADS STATUS indicating success of add
1505 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1506 const char *name, const struct berval *val)
1508 const struct berval *values[2];
1513 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1514 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1515 name, (const void **) values);
1519 static void ads_print_error(int ret, LDAP *ld)
1522 char *ld_error = NULL;
1523 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1524 DEBUG(10,("AD LDAP failure %d (%s):\n%s\n", ret,
1525 ldap_err2string(ret), ld_error));
1526 SAFE_FREE(ld_error);
1531 * Perform an ldap modify
1532 * @param ads connection to ads server
1533 * @param mod_dn DistinguishedName to modify
1534 * @param mods list of modifications to perform
1535 * @return status of modify
1537 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1540 char *utf8_dn = NULL;
1541 size_t converted_size;
1543 this control is needed to modify that contains a currently
1544 non-existent attribute (but allowable for the object) to run
1546 LDAPControl PermitModify = {
1547 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1550 LDAPControl *controls[2];
1552 controls[0] = &PermitModify;
1555 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1556 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1559 /* find the end of the list, marked by NULL or -1 */
1560 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1561 /* make sure the end of the list is NULL */
1563 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1564 (LDAPMod **) mods, controls, NULL);
1565 ads_print_error(ret, ads->ldap.ld);
1566 TALLOC_FREE(utf8_dn);
1567 return ADS_ERROR(ret);
1571 * Perform an ldap add
1572 * @param ads connection to ads server
1573 * @param new_dn DistinguishedName to add
1574 * @param mods list of attributes and values for DN
1575 * @return status of add
1577 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1580 char *utf8_dn = NULL;
1581 size_t converted_size;
1583 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1584 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1585 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1588 /* find the end of the list, marked by NULL or -1 */
1589 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1590 /* make sure the end of the list is NULL */
1593 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1594 ads_print_error(ret, ads->ldap.ld);
1595 TALLOC_FREE(utf8_dn);
1596 return ADS_ERROR(ret);
1600 * Delete a DistinguishedName
1601 * @param ads connection to ads server
1602 * @param new_dn DistinguishedName to delete
1603 * @return status of delete
1605 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1608 char *utf8_dn = NULL;
1609 size_t converted_size;
1610 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1611 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1612 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1615 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1616 ads_print_error(ret, ads->ldap.ld);
1617 TALLOC_FREE(utf8_dn);
1618 return ADS_ERROR(ret);
1622 * Build an org unit string
1623 * if org unit is Computers or blank then assume a container, otherwise
1624 * assume a / separated list of organisational units.
1625 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1626 * @param ads connection to ads server
1627 * @param org_unit Organizational unit
1628 * @return org unit string - caller must free
1630 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1634 if (!org_unit || !*org_unit) {
1636 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1638 /* samba4 might not yet respond to a wellknownobject-query */
1639 return ret ? ret : SMB_STRDUP("cn=Computers");
1642 if (strequal(org_unit, "Computers")) {
1643 return SMB_STRDUP("cn=Computers");
1646 /* jmcd: removed "\\" from the separation chars, because it is
1647 needed as an escape for chars like '#' which are valid in an
1649 return ads_build_path(org_unit, "/", "ou=", 1);
1653 * Get a org unit string for a well-known GUID
1654 * @param ads connection to ads server
1655 * @param wknguid Well known GUID
1656 * @return org unit string - caller must free
1658 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1661 LDAPMessage *res = NULL;
1662 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1663 **bind_dn_exp = NULL;
1664 const char *attrs[] = {"distinguishedName", NULL};
1665 int new_ln, wkn_ln, bind_ln, i;
1667 if (wknguid == NULL) {
1671 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1672 DEBUG(1, ("asprintf failed!\n"));
1676 status = ads_search_dn(ads, &res, base, attrs);
1677 if (!ADS_ERR_OK(status)) {
1678 DEBUG(1,("Failed while searching for: %s\n", base));
1682 if (ads_count_replies(ads, res) != 1) {
1686 /* substitute the bind-path from the well-known-guid-search result */
1687 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1692 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1697 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1702 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1704 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1707 new_ln = wkn_ln - bind_ln;
1709 ret = SMB_STRDUP(wkn_dn_exp[0]);
1714 for (i=1; i < new_ln; i++) {
1717 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1723 ret = SMB_STRDUP(s);
1732 ads_msgfree(ads, res);
1733 TALLOC_FREE(wkn_dn);
1735 ldap_value_free(wkn_dn_exp);
1738 ldap_value_free(bind_dn_exp);
1745 * Adds (appends) an item to an attribute array, rather then
1746 * replacing the whole list
1747 * @param ctx An initialized TALLOC_CTX
1748 * @param mods An initialized ADS_MODLIST
1749 * @param name name of the ldap attribute to append to
1750 * @param vals an array of values to add
1751 * @return status of addition
1754 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1755 const char *name, const char **vals)
1757 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1758 (const void *) vals);
1762 * Determines the an account's current KVNO via an LDAP lookup
1763 * @param ads An initialized ADS_STRUCT
1764 * @param account_name the NT samaccountname.
1765 * @return the kvno for the account, or -1 in case of a failure.
1768 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1770 LDAPMessage *res = NULL;
1771 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1773 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1774 char *dn_string = NULL;
1775 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1777 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1778 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1781 ret = ads_search(ads, &res, filter, attrs);
1783 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1784 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1785 ads_msgfree(ads, res);
1789 dn_string = ads_get_dn(ads, talloc_tos(), res);
1791 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1792 ads_msgfree(ads, res);
1795 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1796 TALLOC_FREE(dn_string);
1798 /* ---------------------------------------------------------
1799 * 0 is returned as a default KVNO from this point on...
1800 * This is done because Windows 2000 does not support key
1801 * version numbers. Chances are that a failure in the next
1802 * step is simply due to Windows 2000 being used for a
1803 * domain controller. */
1806 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1807 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1808 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1809 ads_msgfree(ads, res);
1814 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1815 ads_msgfree(ads, res);
1820 * Determines the computer account's current KVNO via an LDAP lookup
1821 * @param ads An initialized ADS_STRUCT
1822 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1823 * @return the kvno for the computer account, or -1 in case of a failure.
1826 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1828 char *computer_account = NULL;
1831 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1835 kvno = ads_get_kvno(ads, computer_account);
1836 free(computer_account);
1842 * This clears out all registered spn's for a given hostname
1843 * @param ads An initilaized ADS_STRUCT
1844 * @param machine_name the NetBIOS name of the computer.
1845 * @return 0 upon success, non-zero otherwise.
1848 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1851 LDAPMessage *res = NULL;
1853 const char *servicePrincipalName[1] = {NULL};
1854 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1855 char *dn_string = NULL;
1857 ret = ads_find_machine_acct(ads, &res, machine_name);
1858 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1859 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1860 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1861 ads_msgfree(ads, res);
1862 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1865 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1866 ctx = talloc_init("ads_clear_service_principal_names");
1868 ads_msgfree(ads, res);
1869 return ADS_ERROR(LDAP_NO_MEMORY);
1872 if (!(mods = ads_init_mods(ctx))) {
1873 talloc_destroy(ctx);
1874 ads_msgfree(ads, res);
1875 return ADS_ERROR(LDAP_NO_MEMORY);
1877 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1878 if (!ADS_ERR_OK(ret)) {
1879 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1880 ads_msgfree(ads, res);
1881 talloc_destroy(ctx);
1884 dn_string = ads_get_dn(ads, talloc_tos(), res);
1886 talloc_destroy(ctx);
1887 ads_msgfree(ads, res);
1888 return ADS_ERROR(LDAP_NO_MEMORY);
1890 ret = ads_gen_mod(ads, dn_string, mods);
1891 TALLOC_FREE(dn_string);
1892 if (!ADS_ERR_OK(ret)) {
1893 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1895 ads_msgfree(ads, res);
1896 talloc_destroy(ctx);
1900 ads_msgfree(ads, res);
1901 talloc_destroy(ctx);
1906 * @brief Search for an element in a string array.
1908 * @param[in] el_array The string array to search.
1910 * @param[in] num_el The number of elements in the string array.
1912 * @param[in] el The string to search.
1914 * @return True if found, false if not.
1916 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1920 if (el_array == NULL || num_el == 0 || el == NULL) {
1924 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1927 cmp = strcasecmp_m(el_array[i], el);
1937 * @brief This gets the service principal names of an existing computer account.
1939 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1941 * @param[in] ads The ADS context to use.
1943 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1944 * identify the computer account.
1946 * @param[in] spn_array A pointer to store the array for SPNs.
1948 * @param[in] num_spns The number of principals stored in the array.
1950 * @return 0 on success, or a ADS error if a failure occurred.
1952 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1954 const char *machine_name,
1959 LDAPMessage *res = NULL;
1962 status = ads_find_machine_acct(ads,
1965 if (!ADS_ERR_OK(status)) {
1966 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1971 count = ads_count_replies(ads, res);
1973 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1977 *spn_array = ads_pull_strings(ads,
1980 "servicePrincipalName",
1982 if (*spn_array == NULL) {
1983 DEBUG(1, ("Host account for %s does not have service principal "
1986 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1991 ads_msgfree(ads, res);
1997 * This adds a service principal name to an existing computer account
1998 * (found by hostname) in AD.
1999 * @param ads An initialized ADS_STRUCT
2000 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2001 * @param spns An array or strings for the service principals to add,
2002 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2003 * @return 0 upon sucess, or non-zero if a failure occurs
2006 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2007 const char *machine_name,
2012 LDAPMessage *res = NULL;
2014 char *dn_string = NULL;
2015 const char **servicePrincipalName = spns;
2017 ret = ads_find_machine_acct(ads, &res, machine_name);
2018 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2019 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2021 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2022 ads_msgfree(ads, res);
2023 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2026 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2027 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2028 ads_msgfree(ads, res);
2029 return ADS_ERROR(LDAP_NO_MEMORY);
2032 DEBUG(5,("ads_add_service_principal_name: INFO: "
2033 "Adding %s to host %s\n",
2034 spns[0] ? "N/A" : spns[0], machine_name));
2037 DEBUG(5,("ads_add_service_principal_name: INFO: "
2038 "Adding %s to host %s\n",
2039 spns[1] ? "N/A" : spns[1], machine_name));
2041 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2042 ret = ADS_ERROR(LDAP_NO_MEMORY);
2046 ret = ads_add_strlist(ctx,
2048 "servicePrincipalName",
2049 servicePrincipalName);
2050 if (!ADS_ERR_OK(ret)) {
2051 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2055 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2056 ret = ADS_ERROR(LDAP_NO_MEMORY);
2060 ret = ads_gen_mod(ads, dn_string, mods);
2061 if (!ADS_ERR_OK(ret)) {
2062 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2068 ads_msgfree(ads, res);
2073 * adds a machine account to the ADS server
2074 * @param ads An intialized ADS_STRUCT
2075 * @param machine_name - the NetBIOS machine name of this account.
2076 * @param account_type A number indicating the type of account to create
2077 * @param org_unit The LDAP path in which to place this account
2078 * @return 0 upon success, or non-zero otherwise
2081 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2082 const char *machine_name,
2083 const char *org_unit,
2084 uint32_t etype_list)
2087 char *samAccountName, *controlstr;
2090 char *machine_escaped = NULL;
2092 const char *objectClass[] = {"top", "person", "organizationalPerson",
2093 "user", "computer", NULL};
2094 LDAPMessage *res = NULL;
2095 uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2096 UF_DONT_EXPIRE_PASSWD |\
2097 UF_ACCOUNTDISABLE );
2098 uint32_t func_level = 0;
2100 ret = ads_domain_func_level(ads, &func_level);
2101 if (!ADS_ERR_OK(ret)) {
2105 if (!(ctx = talloc_init("ads_add_machine_acct")))
2106 return ADS_ERROR(LDAP_NO_MEMORY);
2108 ret = ADS_ERROR(LDAP_NO_MEMORY);
2110 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2111 if (!machine_escaped) {
2115 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2116 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2118 if ( !new_dn || !samAccountName ) {
2122 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2126 if (!(mods = ads_init_mods(ctx))) {
2130 ads_mod_str(ctx, &mods, "cn", machine_name);
2131 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2132 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2133 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2135 if (func_level >= DS_DOMAIN_FUNCTION_2008) {
2136 const char *etype_list_str;
2138 etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list);
2139 if (etype_list_str == NULL) {
2142 ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes",
2146 ret = ads_gen_add(ads, new_dn, mods);
2149 SAFE_FREE(machine_escaped);
2150 ads_msgfree(ads, res);
2151 talloc_destroy(ctx);
2157 * move a machine account to another OU on the ADS server
2158 * @param ads - An intialized ADS_STRUCT
2159 * @param machine_name - the NetBIOS machine name of this account.
2160 * @param org_unit - The LDAP path in which to place this account
2161 * @param moved - whether we moved the machine account (optional)
2162 * @return 0 upon success, or non-zero otherwise
2165 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2166 const char *org_unit, bool *moved)
2170 LDAPMessage *res = NULL;
2171 char *filter = NULL;
2172 char *computer_dn = NULL;
2174 char *computer_rdn = NULL;
2175 bool need_move = False;
2177 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2178 rc = ADS_ERROR(LDAP_NO_MEMORY);
2182 /* Find pre-existing machine */
2183 rc = ads_search(ads, &res, filter, NULL);
2184 if (!ADS_ERR_OK(rc)) {
2188 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2190 rc = ADS_ERROR(LDAP_NO_MEMORY);
2194 parent_dn = ads_parent_dn(computer_dn);
2195 if (strequal(parent_dn, org_unit)) {
2201 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2202 rc = ADS_ERROR(LDAP_NO_MEMORY);
2206 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2207 org_unit, 1, NULL, NULL);
2208 rc = ADS_ERROR(ldap_status);
2211 ads_msgfree(ads, res);
2213 TALLOC_FREE(computer_dn);
2214 SAFE_FREE(computer_rdn);
2216 if (!ADS_ERR_OK(rc)) {
2228 dump a binary result from ldap
2230 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2233 for (i=0; values[i]; i++) {
2235 printf("%s: ", field);
2236 for (j=0; j<values[i]->bv_len; j++) {
2237 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2243 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2246 for (i=0; values[i]; i++) {
2248 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2251 status = GUID_from_ndr_blob(&in, &guid);
2252 if (NT_STATUS_IS_OK(status)) {
2253 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2255 printf("%s: INVALID GUID\n", field);
2261 dump a sid result from ldap
2263 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2266 for (i=0; values[i]; i++) {
2267 struct sid_parse_ret ret;
2269 struct dom_sid_buf tmp;
2270 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2271 values[i]->bv_len, &sid);
2272 if (ret.len == -1) {
2275 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2280 dump ntSecurityDescriptor
2282 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2284 TALLOC_CTX *frame = talloc_stackframe();
2285 struct security_descriptor *psd;
2288 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2289 values[0]->bv_len, &psd);
2290 if (!NT_STATUS_IS_OK(status)) {
2291 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2292 nt_errstr(status)));
2298 ads_disp_sd(ads, talloc_tos(), psd);
2305 dump a string result from ldap
2307 static void dump_string(const char *field, char **values)
2310 for (i=0; values[i]; i++) {
2311 printf("%s: %s\n", field, values[i]);
2316 dump a field from LDAP on stdout
2320 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2325 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2327 {"objectGUID", False, dump_guid},
2328 {"netbootGUID", False, dump_guid},
2329 {"nTSecurityDescriptor", False, dump_sd},
2330 {"dnsRecord", False, dump_binary},
2331 {"objectSid", False, dump_sid},
2332 {"tokenGroups", False, dump_sid},
2333 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2334 {"tokengroupsGlobalandUniversal", False, dump_sid},
2335 {"mS-DS-CreatorSID", False, dump_sid},
2336 {"msExchMailboxGuid", False, dump_guid},
2341 if (!field) { /* must be end of an entry */
2346 for (i=0; handlers[i].name; i++) {
2347 if (strcasecmp_m(handlers[i].name, field) == 0) {
2348 if (!values) /* first time, indicate string or not */
2349 return handlers[i].string;
2350 handlers[i].handler(ads, field, (struct berval **) values);
2354 if (!handlers[i].name) {
2355 if (!values) /* first time, indicate string conversion */
2357 dump_string(field, (char **)values);
2363 * Dump a result from LDAP on stdout
2364 * used for debugging
2365 * @param ads connection to ads server
2366 * @param res Results to dump
2369 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2371 ads_process_results(ads, res, ads_dump_field, NULL);
2375 * Walk through results, calling a function for each entry found.
2376 * The function receives a field name, a berval * array of values,
2377 * and a data area passed through from the start. The function is
2378 * called once with null for field and values at the end of each
2380 * @param ads connection to ads server
2381 * @param res Results to process
2382 * @param fn Function for processing each result
2383 * @param data_area user-defined area to pass to function
2385 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2386 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2391 size_t converted_size;
2393 if (!(ctx = talloc_init("ads_process_results")))
2396 for (msg = ads_first_entry(ads, res); msg;
2397 msg = ads_next_entry(ads, msg)) {
2401 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2402 (LDAPMessage *)msg,&b);
2404 utf8_field=ldap_next_attribute(ads->ldap.ld,
2405 (LDAPMessage *)msg,b)) {
2406 struct berval **ber_vals;
2412 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2415 DEBUG(0,("ads_process_results: "
2416 "pull_utf8_talloc failed: %s",
2420 string = fn(ads, field, NULL, data_area);
2425 utf8_vals = ldap_get_values(ads->ldap.ld,
2426 (LDAPMessage *)msg, field);
2427 p = discard_const_p(const char *, utf8_vals);
2428 str_vals = ads_pull_strvals(ctx, p);
2429 fn(ads, field, (void **) str_vals, data_area);
2430 ldap_value_free(utf8_vals);
2432 ber_vals = ldap_get_values_len(ads->ldap.ld,
2433 (LDAPMessage *)msg, field);
2434 fn(ads, field, (void **) ber_vals, data_area);
2436 ldap_value_free_len(ber_vals);
2438 ldap_memfree(utf8_field);
2441 talloc_free_children(ctx);
2442 fn(ads, NULL, NULL, data_area); /* completed an entry */
2445 talloc_destroy(ctx);
2449 * count how many replies are in a LDAPMessage
2450 * @param ads connection to ads server
2451 * @param res Results to count
2452 * @return number of replies
2454 int ads_count_replies(ADS_STRUCT *ads, void *res)
2456 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2460 * pull the first entry from a ADS result
2461 * @param ads connection to ads server
2462 * @param res Results of search
2463 * @return first entry from result
2465 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2467 return ldap_first_entry(ads->ldap.ld, res);
2471 * pull the next entry from a ADS result
2472 * @param ads connection to ads server
2473 * @param res Results of search
2474 * @return next entry from result
2476 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2478 return ldap_next_entry(ads->ldap.ld, res);
2482 * pull the first message from a ADS result
2483 * @param ads connection to ads server
2484 * @param res Results of search
2485 * @return first message from result
2487 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2489 return ldap_first_message(ads->ldap.ld, res);
2493 * pull the next message from a ADS result
2494 * @param ads connection to ads server
2495 * @param res Results of search
2496 * @return next message from result
2498 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2500 return ldap_next_message(ads->ldap.ld, res);
2504 * pull a single string from a ADS result
2505 * @param ads connection to ads server
2506 * @param mem_ctx TALLOC_CTX to use for allocating result string
2507 * @param msg Results of search
2508 * @param field Attribute to retrieve
2509 * @return Result string in talloc context
2511 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2517 size_t converted_size;
2519 values = ldap_get_values(ads->ldap.ld, msg, field);
2523 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2528 ldap_value_free(values);
2533 * pull an array of strings from a ADS result
2534 * @param ads connection to ads server
2535 * @param mem_ctx TALLOC_CTX to use for allocating result string
2536 * @param msg Results of search
2537 * @param field Attribute to retrieve
2538 * @return Result strings in talloc context
2540 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2541 LDAPMessage *msg, const char *field,
2546 size_t i, converted_size;
2548 values = ldap_get_values(ads->ldap.ld, msg, field);
2552 *num_values = ldap_count_values(values);
2554 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2556 ldap_value_free(values);
2560 for (i=0;i<*num_values;i++) {
2561 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2564 ldap_value_free(values);
2570 ldap_value_free(values);
2575 * pull an array of strings from a ADS result
2576 * (handle large multivalue attributes with range retrieval)
2577 * @param ads connection to ads server
2578 * @param mem_ctx TALLOC_CTX to use for allocating result string
2579 * @param msg Results of search
2580 * @param field Attribute to retrieve
2581 * @param current_strings strings returned by a previous call to this function
2582 * @param next_attribute The next query should ask for this attribute
2583 * @param num_values How many values did we get this time?
2584 * @param more_values Are there more values to get?
2585 * @return Result strings in talloc context
2587 char **ads_pull_strings_range(ADS_STRUCT *ads,
2588 TALLOC_CTX *mem_ctx,
2589 LDAPMessage *msg, const char *field,
2590 char **current_strings,
2591 const char **next_attribute,
2592 size_t *num_strings,
2596 char *expected_range_attrib, *range_attr;
2597 BerElement *ptr = NULL;
2600 size_t num_new_strings;
2601 unsigned long int range_start;
2602 unsigned long int range_end;
2604 /* we might have been given the whole lot anyway */
2605 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2606 *more_strings = False;
2610 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2612 /* look for Range result */
2613 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2615 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2616 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2617 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2625 /* nothing here - this field is just empty */
2626 *more_strings = False;
2630 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2631 &range_start, &range_end) == 2) {
2632 *more_strings = True;
2634 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2635 &range_start) == 1) {
2636 *more_strings = False;
2638 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2640 ldap_memfree(range_attr);
2641 *more_strings = False;
2646 if ((*num_strings) != range_start) {
2647 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2648 " - aborting range retreival\n",
2649 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2650 ldap_memfree(range_attr);
2651 *more_strings = False;
2655 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2657 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2658 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2659 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2660 range_attr, (unsigned long int)range_end - range_start + 1,
2661 (unsigned long int)num_new_strings));
2662 ldap_memfree(range_attr);
2663 *more_strings = False;
2667 strings = talloc_realloc(mem_ctx, current_strings, char *,
2668 *num_strings + num_new_strings);
2670 if (strings == NULL) {
2671 ldap_memfree(range_attr);
2672 *more_strings = False;
2676 if (new_strings && num_new_strings) {
2677 memcpy(&strings[*num_strings], new_strings,
2678 sizeof(*new_strings) * num_new_strings);
2681 (*num_strings) += num_new_strings;
2683 if (*more_strings) {
2684 *next_attribute = talloc_asprintf(mem_ctx,
2689 if (!*next_attribute) {
2690 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2691 ldap_memfree(range_attr);
2692 *more_strings = False;
2697 ldap_memfree(range_attr);
2703 * pull a single uint32_t from a ADS result
2704 * @param ads connection to ads server
2705 * @param msg Results of search
2706 * @param field Attribute to retrieve
2707 * @param v Pointer to int to store result
2708 * @return boolean inidicating success
2710 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2715 values = ldap_get_values(ads->ldap.ld, msg, field);
2719 ldap_value_free(values);
2723 *v = atoi(values[0]);
2724 ldap_value_free(values);
2729 * pull a single objectGUID from an ADS result
2730 * @param ads connection to ADS server
2731 * @param msg results of search
2732 * @param guid 37-byte area to receive text guid
2733 * @return boolean indicating success
2735 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2740 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2745 status = GUID_from_ndr_blob(&blob, guid);
2746 talloc_free(blob.data);
2747 return NT_STATUS_IS_OK(status);
2752 * pull a single struct dom_sid from a ADS result
2753 * @param ads connection to ads server
2754 * @param msg Results of search
2755 * @param field Attribute to retrieve
2756 * @param sid Pointer to sid to store result
2757 * @return boolean inidicating success
2759 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2760 struct dom_sid *sid)
2762 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2766 * pull an array of struct dom_sids from a ADS result
2767 * @param ads connection to ads server
2768 * @param mem_ctx TALLOC_CTX for allocating sid array
2769 * @param msg Results of search
2770 * @param field Attribute to retrieve
2771 * @param sids pointer to sid array to allocate
2772 * @return the count of SIDs pulled
2774 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2775 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2777 struct berval **values;
2780 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2785 for (i=0; values[i]; i++)
2789 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2791 ldap_value_free_len(values);
2799 for (i=0; values[i]; i++) {
2800 struct sid_parse_ret ret;
2801 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2802 values[i]->bv_len, &(*sids)[count]);
2803 if (ret.len != -1) {
2804 struct dom_sid_buf buf;
2805 DBG_DEBUG("pulling SID: %s\n",
2806 dom_sid_str_buf(&(*sids)[count], &buf));
2811 ldap_value_free_len(values);
2816 * pull a struct security_descriptor from a ADS result
2817 * @param ads connection to ads server
2818 * @param mem_ctx TALLOC_CTX for allocating sid array
2819 * @param msg Results of search
2820 * @param field Attribute to retrieve
2821 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2822 * @return boolean inidicating success
2824 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2825 LDAPMessage *msg, const char *field,
2826 struct security_descriptor **sd)
2828 struct berval **values;
2831 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2833 if (!values) return false;
2837 status = unmarshall_sec_desc(mem_ctx,
2838 (uint8_t *)values[0]->bv_val,
2839 values[0]->bv_len, sd);
2840 if (!NT_STATUS_IS_OK(status)) {
2841 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2842 nt_errstr(status)));
2847 ldap_value_free_len(values);
2852 * in order to support usernames longer than 21 characters we need to
2853 * use both the sAMAccountName and the userPrincipalName attributes
2854 * It seems that not all users have the userPrincipalName attribute set
2856 * @param ads connection to ads server
2857 * @param mem_ctx TALLOC_CTX for allocating sid array
2858 * @param msg Results of search
2859 * @return the username
2861 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2867 /* lookup_name() only works on the sAMAccountName to
2868 returning the username portion of userPrincipalName
2869 breaks winbindd_getpwnam() */
2871 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2872 if (ret && (p = strchr_m(ret, '@'))) {
2877 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2882 * find the update serial number - this is the core of the ldap cache
2883 * @param ads connection to ads server
2884 * @param ads connection to ADS server
2885 * @param usn Pointer to retrieved update serial number
2886 * @return status of search
2888 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2890 const char *attrs[] = {"highestCommittedUSN", NULL};
2894 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2895 if (!ADS_ERR_OK(status))
2898 if (ads_count_replies(ads, res) != 1) {
2899 ads_msgfree(ads, res);
2900 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2903 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2904 ads_msgfree(ads, res);
2905 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2908 ads_msgfree(ads, res);
2912 /* parse a ADS timestring - typical string is
2913 '20020917091222.0Z0' which means 09:12.22 17th September
2915 static time_t ads_parse_time(const char *str)
2921 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2922 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2923 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2932 /********************************************************************
2933 ********************************************************************/
2935 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2937 const char *attrs[] = {"currentTime", NULL};
2942 ADS_STRUCT *ads_s = ads;
2944 if (!(ctx = talloc_init("ads_current_time"))) {
2945 return ADS_ERROR(LDAP_NO_MEMORY);
2948 /* establish a new ldap tcp session if necessary */
2950 if ( !ads->ldap.ld ) {
2951 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2952 ads->server.ldap_server )) == NULL )
2954 status = ADS_ERROR(LDAP_NO_MEMORY);
2957 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2958 status = ads_connect( ads_s );
2959 if ( !ADS_ERR_OK(status))
2963 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2964 if (!ADS_ERR_OK(status)) {
2968 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2970 ads_msgfree(ads_s, res);
2971 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2975 /* but save the time and offset in the original ADS_STRUCT */
2977 ads->config.current_time = ads_parse_time(timestr);
2979 if (ads->config.current_time != 0) {
2980 ads->auth.time_offset = ads->config.current_time - time(NULL);
2981 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2984 ads_msgfree(ads, res);
2986 status = ADS_SUCCESS;
2989 /* free any temporary ads connections */
2990 if ( ads_s != ads ) {
2991 ads_destroy( &ads_s );
2993 talloc_destroy(ctx);
2998 /********************************************************************
2999 ********************************************************************/
3001 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3003 const char *attrs[] = {"domainFunctionality", NULL};
3006 ADS_STRUCT *ads_s = ads;
3008 *val = DS_DOMAIN_FUNCTION_2000;
3010 /* establish a new ldap tcp session if necessary */
3012 if ( !ads->ldap.ld ) {
3013 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3014 ads->server.ldap_server )) == NULL )
3016 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3019 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3020 status = ads_connect( ads_s );
3021 if ( !ADS_ERR_OK(status))
3025 /* If the attribute does not exist assume it is a Windows 2000
3026 functional domain */
3028 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3029 if (!ADS_ERR_OK(status)) {
3030 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3031 status = ADS_SUCCESS;
3036 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3037 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3039 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3042 ads_msgfree(ads, res);
3045 /* free any temporary ads connections */
3046 if ( ads_s != ads ) {
3047 ads_destroy( &ads_s );
3054 * find the domain sid for our domain
3055 * @param ads connection to ads server
3056 * @param sid Pointer to domain sid
3057 * @return status of search
3059 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3061 const char *attrs[] = {"objectSid", NULL};
3065 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3067 if (!ADS_ERR_OK(rc)) return rc;
3068 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3069 ads_msgfree(ads, res);
3070 return ADS_ERROR_SYSTEM(ENOENT);
3072 ads_msgfree(ads, res);
3078 * find our site name
3079 * @param ads connection to ads server
3080 * @param mem_ctx Pointer to talloc context
3081 * @param site_name Pointer to the sitename
3082 * @return status of search
3084 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3088 const char *dn, *service_name;
3089 const char *attrs[] = { "dsServiceName", NULL };
3091 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3092 if (!ADS_ERR_OK(status)) {
3096 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3097 if (service_name == NULL) {
3098 ads_msgfree(ads, res);
3099 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3102 ads_msgfree(ads, res);
3104 /* go up three levels */
3105 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3107 return ADS_ERROR(LDAP_NO_MEMORY);
3110 *site_name = talloc_strdup(mem_ctx, dn);
3111 if (*site_name == NULL) {
3112 return ADS_ERROR(LDAP_NO_MEMORY);
3117 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3122 * find the site dn where a machine resides
3123 * @param ads connection to ads server
3124 * @param mem_ctx Pointer to talloc context
3125 * @param computer_name name of the machine
3126 * @param site_name Pointer to the sitename
3127 * @return status of search
3129 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3133 const char *parent, *filter;
3134 char *config_context = NULL;
3137 /* shortcut a query */
3138 if (strequal(computer_name, ads->config.ldap_server_name)) {
3139 return ads_site_dn(ads, mem_ctx, site_dn);
3142 status = ads_config_path(ads, mem_ctx, &config_context);
3143 if (!ADS_ERR_OK(status)) {
3147 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3148 if (filter == NULL) {
3149 return ADS_ERROR(LDAP_NO_MEMORY);
3152 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3153 filter, NULL, &res);
3154 if (!ADS_ERR_OK(status)) {
3158 if (ads_count_replies(ads, res) != 1) {
3159 ads_msgfree(ads, res);
3160 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3163 dn = ads_get_dn(ads, mem_ctx, res);
3165 ads_msgfree(ads, res);
3166 return ADS_ERROR(LDAP_NO_MEMORY);
3169 /* go up three levels */
3170 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3171 if (parent == NULL) {
3172 ads_msgfree(ads, res);
3174 return ADS_ERROR(LDAP_NO_MEMORY);
3177 *site_dn = talloc_strdup(mem_ctx, parent);
3178 if (*site_dn == NULL) {
3179 ads_msgfree(ads, res);
3181 return ADS_ERROR(LDAP_NO_MEMORY);
3185 ads_msgfree(ads, res);
3191 * get the upn suffixes for a domain
3192 * @param ads connection to ads server
3193 * @param mem_ctx Pointer to talloc context
3194 * @param suffixes Pointer to an array of suffixes
3195 * @param num_suffixes Pointer to the number of suffixes
3196 * @return status of search
3198 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3203 char *config_context = NULL;
3204 const char *attrs[] = { "uPNSuffixes", NULL };
3206 status = ads_config_path(ads, mem_ctx, &config_context);
3207 if (!ADS_ERR_OK(status)) {
3211 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3213 return ADS_ERROR(LDAP_NO_MEMORY);
3216 status = ads_search_dn(ads, &res, base, attrs);
3217 if (!ADS_ERR_OK(status)) {
3221 if (ads_count_replies(ads, res) != 1) {
3222 ads_msgfree(ads, res);
3223 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3226 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3227 if ((*suffixes) == NULL) {
3228 ads_msgfree(ads, res);
3229 return ADS_ERROR(LDAP_NO_MEMORY);
3232 ads_msgfree(ads, res);
3238 * get the joinable ous for a domain
3239 * @param ads connection to ads server
3240 * @param mem_ctx Pointer to talloc context
3241 * @param ous Pointer to an array of ous
3242 * @param num_ous Pointer to the number of ous
3243 * @return status of search
3245 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3246 TALLOC_CTX *mem_ctx,
3251 LDAPMessage *res = NULL;
3252 LDAPMessage *msg = NULL;
3253 const char *attrs[] = { "dn", NULL };
3256 status = ads_search(ads, &res,
3257 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3259 if (!ADS_ERR_OK(status)) {
3263 count = ads_count_replies(ads, res);
3265 ads_msgfree(ads, res);
3266 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3269 for (msg = ads_first_entry(ads, res); msg;
3270 msg = ads_next_entry(ads, msg)) {
3271 const char **p = discard_const_p(const char *, *ous);
3274 dn = ads_get_dn(ads, talloc_tos(), msg);
3276 ads_msgfree(ads, res);
3277 return ADS_ERROR(LDAP_NO_MEMORY);
3280 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3282 ads_msgfree(ads, res);
3283 return ADS_ERROR(LDAP_NO_MEMORY);
3287 *ous = discard_const_p(char *, p);
3290 ads_msgfree(ads, res);
3297 * pull a struct dom_sid from an extended dn string
3298 * @param mem_ctx TALLOC_CTX
3299 * @param extended_dn string
3300 * @param flags string type of extended_dn
3301 * @param sid pointer to a struct dom_sid
3302 * @return NT_STATUS_OK on success,
3303 * NT_INVALID_PARAMETER on error,
3304 * NT_STATUS_NOT_FOUND if no SID present
3306 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3307 const char *extended_dn,
3308 enum ads_extended_dn_flags flags,
3309 struct dom_sid *sid)
3314 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3317 /* otherwise extended_dn gets stripped off */
3318 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3319 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3322 * ADS_EXTENDED_DN_HEX_STRING:
3323 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3325 * ADS_EXTENDED_DN_STRING (only with w2k3):
3326 * <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
3328 * Object with no SID, such as an Exchange Public Folder
3329 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3332 p = strchr(dn, ';');
3334 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3337 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3338 DEBUG(5,("No SID present in extended dn\n"));
3339 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3342 p += strlen(";<SID=");
3346 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3351 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3355 case ADS_EXTENDED_DN_STRING:
3356 if (!string_to_sid(sid, p)) {
3357 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3360 case ADS_EXTENDED_DN_HEX_STRING: {
3361 struct sid_parse_ret ret;
3365 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3367 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3370 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3371 if (ret.len == -1) {
3372 DEBUG(10,("failed to parse sid\n"));
3373 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3378 DEBUG(10,("unknown extended dn format\n"));
3379 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3382 return ADS_ERROR_NT(NT_STATUS_OK);
3385 /********************************************************************
3386 ********************************************************************/
3388 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3390 LDAPMessage *res = NULL;
3395 status = ads_find_machine_acct(ads, &res, machine_name);
3396 if (!ADS_ERR_OK(status)) {
3397 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3398 lp_netbios_name()));
3402 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3403 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3407 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3408 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3412 ads_msgfree(ads, res);
3417 /********************************************************************
3418 ********************************************************************/
3420 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3422 LDAPMessage *res = NULL;
3427 status = ads_find_machine_acct(ads, &res, machine_name);
3428 if (!ADS_ERR_OK(status)) {
3429 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3430 lp_netbios_name()));
3434 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3435 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3439 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3440 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3444 ads_msgfree(ads, res);
3449 /********************************************************************
3450 ********************************************************************/
3452 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3454 LDAPMessage *res = NULL;
3460 status = ads_find_machine_acct(ads, &res, machine_name);
3461 if (!ADS_ERR_OK(status)) {
3462 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3463 lp_netbios_name()));
3467 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3468 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3472 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3473 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3477 ads_msgfree(ads, res);
3479 ok = (strlen(name) > 0);
3487 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3490 * Join a machine to a realm
3491 * Creates the machine account and sets the machine password
3492 * @param ads connection to ads server
3493 * @param machine name of host to add
3494 * @param org_unit Organizational unit to place machine in
3495 * @return status of join
3497 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3498 uint32_t account_type, const char *org_unit)
3501 LDAPMessage *res = NULL;
3504 /* machine name must be lowercase */
3505 machine = SMB_STRDUP(machine_name);
3506 strlower_m(machine);
3509 status = ads_find_machine_acct(ads, (void **)&res, machine);
3510 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3511 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3512 status = ads_leave_realm(ads, machine);
3513 if (!ADS_ERR_OK(status)) {
3514 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3515 machine, ads->config.realm));
3520 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3521 if (!ADS_ERR_OK(status)) {
3522 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3527 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3528 if (!ADS_ERR_OK(status)) {
3529 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3535 ads_msgfree(ads, res);
3542 * Delete a machine from the realm
3543 * @param ads connection to ads server
3544 * @param hostname Machine to remove
3545 * @return status of delete
3547 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3552 char *hostnameDN, *host;
3554 LDAPControl ldap_control;
3555 LDAPControl * pldap_control[2] = {NULL, NULL};
3557 pldap_control[0] = &ldap_control;
3558 memset(&ldap_control, 0, sizeof(LDAPControl));
3559 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3561 /* hostname must be lowercase */
3562 host = SMB_STRDUP(hostname);
3563 if (!strlower_m(host)) {
3565 return ADS_ERROR_SYSTEM(EINVAL);
3568 status = ads_find_machine_acct(ads, &res, host);
3569 if (!ADS_ERR_OK(status)) {
3570 DEBUG(0, ("Host account for %s does not exist.\n", host));
3575 msg = ads_first_entry(ads, res);
3578 return ADS_ERROR_SYSTEM(ENOENT);
3581 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3582 if (hostnameDN == NULL) {
3584 return ADS_ERROR_SYSTEM(ENOENT);
3587 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3589 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3591 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3594 if (rc != LDAP_SUCCESS) {
3595 const char *attrs[] = { "cn", NULL };
3596 LDAPMessage *msg_sub;
3598 /* we only search with scope ONE, we do not expect any further
3599 * objects to be created deeper */
3601 status = ads_do_search_retry(ads, hostnameDN,
3602 LDAP_SCOPE_ONELEVEL,
3603 "(objectclass=*)", attrs, &res);
3605 if (!ADS_ERR_OK(status)) {
3607 TALLOC_FREE(hostnameDN);
3611 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3612 msg_sub = ads_next_entry(ads, msg_sub)) {
3616 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3618 TALLOC_FREE(hostnameDN);
3619 return ADS_ERROR(LDAP_NO_MEMORY);
3622 status = ads_del_dn(ads, dn);
3623 if (!ADS_ERR_OK(status)) {
3624 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3627 TALLOC_FREE(hostnameDN);
3634 /* there should be no subordinate objects anymore */
3635 status = ads_do_search_retry(ads, hostnameDN,
3636 LDAP_SCOPE_ONELEVEL,
3637 "(objectclass=*)", attrs, &res);
3639 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3641 TALLOC_FREE(hostnameDN);
3645 /* delete hostnameDN now */
3646 status = ads_del_dn(ads, hostnameDN);
3647 if (!ADS_ERR_OK(status)) {
3649 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3650 TALLOC_FREE(hostnameDN);
3655 TALLOC_FREE(hostnameDN);
3657 status = ads_find_machine_acct(ads, &res, host);
3658 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3659 DEBUG(3, ("Failed to remove host account.\n"));
3669 * pull all token-sids from an LDAP dn
3670 * @param ads connection to ads server
3671 * @param mem_ctx TALLOC_CTX for allocating sid array
3672 * @param dn of LDAP object
3673 * @param user_sid pointer to struct dom_sid (objectSid)
3674 * @param primary_group_sid pointer to struct dom_sid (self composed)
3675 * @param sids pointer to sid array to allocate
3676 * @param num_sids counter of SIDs pulled
3677 * @return status of token query
3679 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3680 TALLOC_CTX *mem_ctx,
3682 struct dom_sid *user_sid,
3683 struct dom_sid *primary_group_sid,
3684 struct dom_sid **sids,
3688 LDAPMessage *res = NULL;
3690 size_t tmp_num_sids;
3691 struct dom_sid *tmp_sids;
3692 struct dom_sid tmp_user_sid;
3693 struct dom_sid tmp_primary_group_sid;
3695 const char *attrs[] = {
3702 status = ads_search_retry_dn(ads, &res, dn, attrs);
3703 if (!ADS_ERR_OK(status)) {
3707 count = ads_count_replies(ads, res);
3709 ads_msgfree(ads, res);
3710 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3713 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3714 ads_msgfree(ads, res);
3715 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3718 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3719 ads_msgfree(ads, res);
3720 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3724 /* hack to compose the primary group sid without knowing the
3727 struct dom_sid domsid;
3729 sid_copy(&domsid, &tmp_user_sid);
3731 if (!sid_split_rid(&domsid, NULL)) {
3732 ads_msgfree(ads, res);
3733 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3736 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3737 ads_msgfree(ads, res);
3738 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3742 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3744 if (tmp_num_sids == 0 || !tmp_sids) {
3745 ads_msgfree(ads, res);
3746 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3750 *num_sids = tmp_num_sids;
3758 *user_sid = tmp_user_sid;
3761 if (primary_group_sid) {
3762 *primary_group_sid = tmp_primary_group_sid;
3765 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3767 ads_msgfree(ads, res);
3768 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3772 * Find a sAMAccoutName in LDAP
3773 * @param ads connection to ads server
3774 * @param mem_ctx TALLOC_CTX for allocating sid array
3775 * @param samaccountname to search
3776 * @param uac_ret uint32_t pointer userAccountControl attribute value
3777 * @param dn_ret pointer to dn
3778 * @return status of token query
3780 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3781 TALLOC_CTX *mem_ctx,
3782 const char *samaccountname,
3784 const char **dn_ret)
3787 const char *attrs[] = { "userAccountControl", NULL };
3789 LDAPMessage *res = NULL;
3793 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3795 if (filter == NULL) {
3796 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3800 status = ads_do_search_all(ads, ads->config.bind_path,
3802 filter, attrs, &res);
3804 if (!ADS_ERR_OK(status)) {
3808 if (ads_count_replies(ads, res) != 1) {
3809 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3813 dn = ads_get_dn(ads, talloc_tos(), res);
3815 status = ADS_ERROR(LDAP_NO_MEMORY);
3819 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3820 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3829 *dn_ret = talloc_strdup(mem_ctx, dn);
3831 status = ADS_ERROR(LDAP_NO_MEMORY);
3837 ads_msgfree(ads, res);
3843 * find our configuration path
3844 * @param ads connection to ads server
3845 * @param mem_ctx Pointer to talloc context
3846 * @param config_path Pointer to the config path
3847 * @return status of search
3849 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3850 TALLOC_CTX *mem_ctx,
3854 LDAPMessage *res = NULL;
3855 const char *config_context = NULL;
3856 const char *attrs[] = { "configurationNamingContext", NULL };
3858 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3859 "(objectclass=*)", attrs, &res);
3860 if (!ADS_ERR_OK(status)) {
3864 config_context = ads_pull_string(ads, mem_ctx, res,
3865 "configurationNamingContext");
3866 ads_msgfree(ads, res);
3867 if (!config_context) {
3868 return ADS_ERROR(LDAP_NO_MEMORY);
3872 *config_path = talloc_strdup(mem_ctx, config_context);
3873 if (!*config_path) {
3874 return ADS_ERROR(LDAP_NO_MEMORY);
3878 return ADS_ERROR(LDAP_SUCCESS);
3882 * find the displayName of an extended right
3883 * @param ads connection to ads server
3884 * @param config_path The config path
3885 * @param mem_ctx Pointer to talloc context
3886 * @param GUID struct of the rightsGUID
3887 * @return status of search
3889 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3890 const char *config_path,
3891 TALLOC_CTX *mem_ctx,
3892 const struct GUID *rights_guid)
3895 LDAPMessage *res = NULL;
3897 const char *attrs[] = { "displayName", NULL };
3898 const char *result = NULL;
3901 if (!ads || !mem_ctx || !rights_guid) {
3905 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3906 GUID_string(mem_ctx, rights_guid));
3911 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3916 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3918 if (!ADS_ERR_OK(rc)) {
3922 if (ads_count_replies(ads, res) != 1) {
3926 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3929 ads_msgfree(ads, res);
3934 * verify or build and verify an account ou
3935 * @param mem_ctx Pointer to talloc context
3936 * @param ads connection to ads server
3938 * @return status of search
3941 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3943 const char **account_ou)
3949 if (account_ou == NULL) {
3950 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3953 if (*account_ou != NULL) {
3954 exploded_dn = ldap_explode_dn(*account_ou, 0);
3956 ldap_value_free(exploded_dn);
3961 ou_string = ads_ou_string(ads, *account_ou);
3963 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3966 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3967 ads->config.bind_path);
3968 SAFE_FREE(ou_string);
3971 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3974 exploded_dn = ldap_explode_dn(name, 0);
3976 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3978 ldap_value_free(exploded_dn);