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++) {
2234 printf("%s: ", field);
2235 for (j=0; j<values[i]->bv_len; j++) {
2236 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2242 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2245 for (i=0; values[i]; i++) {
2247 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2250 status = GUID_from_ndr_blob(&in, &guid);
2251 if (NT_STATUS_IS_OK(status)) {
2252 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2254 printf("%s: INVALID GUID\n", field);
2260 dump a sid result from ldap
2262 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2265 for (i=0; values[i]; i++) {
2268 if (!sid_parse((const uint8_t *)values[i]->bv_val,
2269 values[i]->bv_len, &sid)) {
2272 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2277 dump ntSecurityDescriptor
2279 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2281 TALLOC_CTX *frame = talloc_stackframe();
2282 struct security_descriptor *psd;
2285 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2286 values[0]->bv_len, &psd);
2287 if (!NT_STATUS_IS_OK(status)) {
2288 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2289 nt_errstr(status)));
2295 ads_disp_sd(ads, talloc_tos(), psd);
2302 dump a string result from ldap
2304 static void dump_string(const char *field, char **values)
2307 for (i=0; values[i]; i++) {
2308 printf("%s: %s\n", field, values[i]);
2313 dump a field from LDAP on stdout
2317 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2322 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2324 {"objectGUID", False, dump_guid},
2325 {"netbootGUID", False, dump_guid},
2326 {"nTSecurityDescriptor", False, dump_sd},
2327 {"dnsRecord", False, dump_binary},
2328 {"objectSid", False, dump_sid},
2329 {"tokenGroups", False, dump_sid},
2330 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2331 {"tokengroupsGlobalandUniversal", False, dump_sid},
2332 {"mS-DS-CreatorSID", False, dump_sid},
2333 {"msExchMailboxGuid", False, dump_guid},
2338 if (!field) { /* must be end of an entry */
2343 for (i=0; handlers[i].name; i++) {
2344 if (strcasecmp_m(handlers[i].name, field) == 0) {
2345 if (!values) /* first time, indicate string or not */
2346 return handlers[i].string;
2347 handlers[i].handler(ads, field, (struct berval **) values);
2351 if (!handlers[i].name) {
2352 if (!values) /* first time, indicate string conversion */
2354 dump_string(field, (char **)values);
2360 * Dump a result from LDAP on stdout
2361 * used for debugging
2362 * @param ads connection to ads server
2363 * @param res Results to dump
2366 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2368 ads_process_results(ads, res, ads_dump_field, NULL);
2372 * Walk through results, calling a function for each entry found.
2373 * The function receives a field name, a berval * array of values,
2374 * and a data area passed through from the start. The function is
2375 * called once with null for field and values at the end of each
2377 * @param ads connection to ads server
2378 * @param res Results to process
2379 * @param fn Function for processing each result
2380 * @param data_area user-defined area to pass to function
2382 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2383 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2388 size_t converted_size;
2390 if (!(ctx = talloc_init("ads_process_results")))
2393 for (msg = ads_first_entry(ads, res); msg;
2394 msg = ads_next_entry(ads, msg)) {
2398 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2399 (LDAPMessage *)msg,&b);
2401 utf8_field=ldap_next_attribute(ads->ldap.ld,
2402 (LDAPMessage *)msg,b)) {
2403 struct berval **ber_vals;
2409 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2412 DEBUG(0,("ads_process_results: "
2413 "pull_utf8_talloc failed: %s",
2417 string = fn(ads, field, NULL, data_area);
2422 utf8_vals = ldap_get_values(ads->ldap.ld,
2423 (LDAPMessage *)msg, field);
2424 p = discard_const_p(const char *, utf8_vals);
2425 str_vals = ads_pull_strvals(ctx, p);
2426 fn(ads, field, (void **) str_vals, data_area);
2427 ldap_value_free(utf8_vals);
2429 ber_vals = ldap_get_values_len(ads->ldap.ld,
2430 (LDAPMessage *)msg, field);
2431 fn(ads, field, (void **) ber_vals, data_area);
2433 ldap_value_free_len(ber_vals);
2435 ldap_memfree(utf8_field);
2438 talloc_free_children(ctx);
2439 fn(ads, NULL, NULL, data_area); /* completed an entry */
2442 talloc_destroy(ctx);
2446 * count how many replies are in a LDAPMessage
2447 * @param ads connection to ads server
2448 * @param res Results to count
2449 * @return number of replies
2451 int ads_count_replies(ADS_STRUCT *ads, void *res)
2453 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2457 * pull the first entry from a ADS result
2458 * @param ads connection to ads server
2459 * @param res Results of search
2460 * @return first entry from result
2462 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2464 return ldap_first_entry(ads->ldap.ld, res);
2468 * pull the next entry from a ADS result
2469 * @param ads connection to ads server
2470 * @param res Results of search
2471 * @return next entry from result
2473 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2475 return ldap_next_entry(ads->ldap.ld, res);
2479 * pull the first message from a ADS result
2480 * @param ads connection to ads server
2481 * @param res Results of search
2482 * @return first message from result
2484 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2486 return ldap_first_message(ads->ldap.ld, res);
2490 * pull the next message from a ADS result
2491 * @param ads connection to ads server
2492 * @param res Results of search
2493 * @return next message from result
2495 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2497 return ldap_next_message(ads->ldap.ld, res);
2501 * pull a single string from a ADS result
2502 * @param ads connection to ads server
2503 * @param mem_ctx TALLOC_CTX to use for allocating result string
2504 * @param msg Results of search
2505 * @param field Attribute to retrieve
2506 * @return Result string in talloc context
2508 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2514 size_t converted_size;
2516 values = ldap_get_values(ads->ldap.ld, msg, field);
2520 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2525 ldap_value_free(values);
2530 * pull an array of strings from a ADS result
2531 * @param ads connection to ads server
2532 * @param mem_ctx TALLOC_CTX to use for allocating result string
2533 * @param msg Results of search
2534 * @param field Attribute to retrieve
2535 * @return Result strings in talloc context
2537 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2538 LDAPMessage *msg, const char *field,
2544 size_t converted_size;
2546 values = ldap_get_values(ads->ldap.ld, msg, field);
2550 *num_values = ldap_count_values(values);
2552 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2554 ldap_value_free(values);
2558 for (i=0;i<*num_values;i++) {
2559 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2562 ldap_value_free(values);
2568 ldap_value_free(values);
2573 * pull an array of strings from a ADS result
2574 * (handle large multivalue attributes with range retrieval)
2575 * @param ads connection to ads server
2576 * @param mem_ctx TALLOC_CTX to use for allocating result string
2577 * @param msg Results of search
2578 * @param field Attribute to retrieve
2579 * @param current_strings strings returned by a previous call to this function
2580 * @param next_attribute The next query should ask for this attribute
2581 * @param num_values How many values did we get this time?
2582 * @param more_values Are there more values to get?
2583 * @return Result strings in talloc context
2585 char **ads_pull_strings_range(ADS_STRUCT *ads,
2586 TALLOC_CTX *mem_ctx,
2587 LDAPMessage *msg, const char *field,
2588 char **current_strings,
2589 const char **next_attribute,
2590 size_t *num_strings,
2594 char *expected_range_attrib, *range_attr;
2595 BerElement *ptr = NULL;
2598 size_t num_new_strings;
2599 unsigned long int range_start;
2600 unsigned long int range_end;
2602 /* we might have been given the whole lot anyway */
2603 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2604 *more_strings = False;
2608 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2610 /* look for Range result */
2611 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2613 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2614 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2615 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2623 /* nothing here - this field is just empty */
2624 *more_strings = False;
2628 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2629 &range_start, &range_end) == 2) {
2630 *more_strings = True;
2632 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2633 &range_start) == 1) {
2634 *more_strings = False;
2636 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2638 ldap_memfree(range_attr);
2639 *more_strings = False;
2644 if ((*num_strings) != range_start) {
2645 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2646 " - aborting range retreival\n",
2647 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2648 ldap_memfree(range_attr);
2649 *more_strings = False;
2653 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2655 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2656 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2657 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2658 range_attr, (unsigned long int)range_end - range_start + 1,
2659 (unsigned long int)num_new_strings));
2660 ldap_memfree(range_attr);
2661 *more_strings = False;
2665 strings = talloc_realloc(mem_ctx, current_strings, char *,
2666 *num_strings + num_new_strings);
2668 if (strings == NULL) {
2669 ldap_memfree(range_attr);
2670 *more_strings = False;
2674 if (new_strings && num_new_strings) {
2675 memcpy(&strings[*num_strings], new_strings,
2676 sizeof(*new_strings) * num_new_strings);
2679 (*num_strings) += num_new_strings;
2681 if (*more_strings) {
2682 *next_attribute = talloc_asprintf(mem_ctx,
2687 if (!*next_attribute) {
2688 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2689 ldap_memfree(range_attr);
2690 *more_strings = False;
2695 ldap_memfree(range_attr);
2701 * pull a single uint32_t from a ADS result
2702 * @param ads connection to ads server
2703 * @param msg Results of search
2704 * @param field Attribute to retrieve
2705 * @param v Pointer to int to store result
2706 * @return boolean inidicating success
2708 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2713 values = ldap_get_values(ads->ldap.ld, msg, field);
2717 ldap_value_free(values);
2721 *v = atoi(values[0]);
2722 ldap_value_free(values);
2727 * pull a single objectGUID from an ADS result
2728 * @param ads connection to ADS server
2729 * @param msg results of search
2730 * @param guid 37-byte area to receive text guid
2731 * @return boolean indicating success
2733 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2738 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2743 status = GUID_from_ndr_blob(&blob, guid);
2744 talloc_free(blob.data);
2745 return NT_STATUS_IS_OK(status);
2750 * pull a single struct dom_sid from a ADS result
2751 * @param ads connection to ads server
2752 * @param msg Results of search
2753 * @param field Attribute to retrieve
2754 * @param sid Pointer to sid to store result
2755 * @return boolean inidicating success
2757 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2758 struct dom_sid *sid)
2760 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2764 * pull an array of struct dom_sids from a ADS result
2765 * @param ads connection to ads server
2766 * @param mem_ctx TALLOC_CTX for allocating sid array
2767 * @param msg Results of search
2768 * @param field Attribute to retrieve
2769 * @param sids pointer to sid array to allocate
2770 * @return the count of SIDs pulled
2772 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2773 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2775 struct berval **values;
2779 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2784 for (i=0; values[i]; i++)
2788 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2790 ldap_value_free_len(values);
2798 for (i=0; values[i]; i++) {
2799 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2800 values[i]->bv_len, &(*sids)[count]);
2802 DEBUG(10, ("pulling SID: %s\n",
2803 sid_string_dbg(&(*sids)[count])));
2808 ldap_value_free_len(values);
2813 * pull a struct security_descriptor from a ADS result
2814 * @param ads connection to ads server
2815 * @param mem_ctx TALLOC_CTX for allocating sid array
2816 * @param msg Results of search
2817 * @param field Attribute to retrieve
2818 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2819 * @return boolean inidicating success
2821 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2822 LDAPMessage *msg, const char *field,
2823 struct security_descriptor **sd)
2825 struct berval **values;
2828 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2830 if (!values) return false;
2834 status = unmarshall_sec_desc(mem_ctx,
2835 (uint8_t *)values[0]->bv_val,
2836 values[0]->bv_len, sd);
2837 if (!NT_STATUS_IS_OK(status)) {
2838 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2839 nt_errstr(status)));
2844 ldap_value_free_len(values);
2849 * in order to support usernames longer than 21 characters we need to
2850 * use both the sAMAccountName and the userPrincipalName attributes
2851 * It seems that not all users have the userPrincipalName attribute set
2853 * @param ads connection to ads server
2854 * @param mem_ctx TALLOC_CTX for allocating sid array
2855 * @param msg Results of search
2856 * @return the username
2858 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2864 /* lookup_name() only works on the sAMAccountName to
2865 returning the username portion of userPrincipalName
2866 breaks winbindd_getpwnam() */
2868 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2869 if (ret && (p = strchr_m(ret, '@'))) {
2874 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2879 * find the update serial number - this is the core of the ldap cache
2880 * @param ads connection to ads server
2881 * @param ads connection to ADS server
2882 * @param usn Pointer to retrieved update serial number
2883 * @return status of search
2885 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2887 const char *attrs[] = {"highestCommittedUSN", NULL};
2891 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2892 if (!ADS_ERR_OK(status))
2895 if (ads_count_replies(ads, res) != 1) {
2896 ads_msgfree(ads, res);
2897 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2900 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2901 ads_msgfree(ads, res);
2902 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2905 ads_msgfree(ads, res);
2909 /* parse a ADS timestring - typical string is
2910 '20020917091222.0Z0' which means 09:12.22 17th September
2912 static time_t ads_parse_time(const char *str)
2918 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2919 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2920 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2929 /********************************************************************
2930 ********************************************************************/
2932 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2934 const char *attrs[] = {"currentTime", NULL};
2939 ADS_STRUCT *ads_s = ads;
2941 if (!(ctx = talloc_init("ads_current_time"))) {
2942 return ADS_ERROR(LDAP_NO_MEMORY);
2945 /* establish a new ldap tcp session if necessary */
2947 if ( !ads->ldap.ld ) {
2948 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2949 ads->server.ldap_server )) == NULL )
2951 status = ADS_ERROR(LDAP_NO_MEMORY);
2954 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2955 status = ads_connect( ads_s );
2956 if ( !ADS_ERR_OK(status))
2960 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2961 if (!ADS_ERR_OK(status)) {
2965 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2967 ads_msgfree(ads_s, res);
2968 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2972 /* but save the time and offset in the original ADS_STRUCT */
2974 ads->config.current_time = ads_parse_time(timestr);
2976 if (ads->config.current_time != 0) {
2977 ads->auth.time_offset = ads->config.current_time - time(NULL);
2978 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2981 ads_msgfree(ads, res);
2983 status = ADS_SUCCESS;
2986 /* free any temporary ads connections */
2987 if ( ads_s != ads ) {
2988 ads_destroy( &ads_s );
2990 talloc_destroy(ctx);
2995 /********************************************************************
2996 ********************************************************************/
2998 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3000 const char *attrs[] = {"domainFunctionality", NULL};
3003 ADS_STRUCT *ads_s = ads;
3005 *val = DS_DOMAIN_FUNCTION_2000;
3007 /* establish a new ldap tcp session if necessary */
3009 if ( !ads->ldap.ld ) {
3010 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3011 ads->server.ldap_server )) == NULL )
3013 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3016 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3017 status = ads_connect( ads_s );
3018 if ( !ADS_ERR_OK(status))
3022 /* If the attribute does not exist assume it is a Windows 2000
3023 functional domain */
3025 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3026 if (!ADS_ERR_OK(status)) {
3027 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3028 status = ADS_SUCCESS;
3033 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3034 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3036 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3039 ads_msgfree(ads, res);
3042 /* free any temporary ads connections */
3043 if ( ads_s != ads ) {
3044 ads_destroy( &ads_s );
3051 * find the domain sid for our domain
3052 * @param ads connection to ads server
3053 * @param sid Pointer to domain sid
3054 * @return status of search
3056 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3058 const char *attrs[] = {"objectSid", NULL};
3062 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3064 if (!ADS_ERR_OK(rc)) return rc;
3065 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3066 ads_msgfree(ads, res);
3067 return ADS_ERROR_SYSTEM(ENOENT);
3069 ads_msgfree(ads, res);
3075 * find our site name
3076 * @param ads connection to ads server
3077 * @param mem_ctx Pointer to talloc context
3078 * @param site_name Pointer to the sitename
3079 * @return status of search
3081 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3085 const char *dn, *service_name;
3086 const char *attrs[] = { "dsServiceName", NULL };
3088 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3089 if (!ADS_ERR_OK(status)) {
3093 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3094 if (service_name == NULL) {
3095 ads_msgfree(ads, res);
3096 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3099 ads_msgfree(ads, res);
3101 /* go up three levels */
3102 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3104 return ADS_ERROR(LDAP_NO_MEMORY);
3107 *site_name = talloc_strdup(mem_ctx, dn);
3108 if (*site_name == NULL) {
3109 return ADS_ERROR(LDAP_NO_MEMORY);
3114 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3119 * find the site dn where a machine resides
3120 * @param ads connection to ads server
3121 * @param mem_ctx Pointer to talloc context
3122 * @param computer_name name of the machine
3123 * @param site_name Pointer to the sitename
3124 * @return status of search
3126 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3130 const char *parent, *filter;
3131 char *config_context = NULL;
3134 /* shortcut a query */
3135 if (strequal(computer_name, ads->config.ldap_server_name)) {
3136 return ads_site_dn(ads, mem_ctx, site_dn);
3139 status = ads_config_path(ads, mem_ctx, &config_context);
3140 if (!ADS_ERR_OK(status)) {
3144 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3145 if (filter == NULL) {
3146 return ADS_ERROR(LDAP_NO_MEMORY);
3149 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3150 filter, NULL, &res);
3151 if (!ADS_ERR_OK(status)) {
3155 if (ads_count_replies(ads, res) != 1) {
3156 ads_msgfree(ads, res);
3157 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3160 dn = ads_get_dn(ads, mem_ctx, res);
3162 ads_msgfree(ads, res);
3163 return ADS_ERROR(LDAP_NO_MEMORY);
3166 /* go up three levels */
3167 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3168 if (parent == NULL) {
3169 ads_msgfree(ads, res);
3171 return ADS_ERROR(LDAP_NO_MEMORY);
3174 *site_dn = talloc_strdup(mem_ctx, parent);
3175 if (*site_dn == NULL) {
3176 ads_msgfree(ads, res);
3178 return ADS_ERROR(LDAP_NO_MEMORY);
3182 ads_msgfree(ads, res);
3188 * get the upn suffixes for a domain
3189 * @param ads connection to ads server
3190 * @param mem_ctx Pointer to talloc context
3191 * @param suffixes Pointer to an array of suffixes
3192 * @param num_suffixes Pointer to the number of suffixes
3193 * @return status of search
3195 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3200 char *config_context = NULL;
3201 const char *attrs[] = { "uPNSuffixes", NULL };
3203 status = ads_config_path(ads, mem_ctx, &config_context);
3204 if (!ADS_ERR_OK(status)) {
3208 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3210 return ADS_ERROR(LDAP_NO_MEMORY);
3213 status = ads_search_dn(ads, &res, base, attrs);
3214 if (!ADS_ERR_OK(status)) {
3218 if (ads_count_replies(ads, res) != 1) {
3219 ads_msgfree(ads, res);
3220 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3223 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3224 if ((*suffixes) == NULL) {
3225 ads_msgfree(ads, res);
3226 return ADS_ERROR(LDAP_NO_MEMORY);
3229 ads_msgfree(ads, res);
3235 * get the joinable ous for a domain
3236 * @param ads connection to ads server
3237 * @param mem_ctx Pointer to talloc context
3238 * @param ous Pointer to an array of ous
3239 * @param num_ous Pointer to the number of ous
3240 * @return status of search
3242 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3243 TALLOC_CTX *mem_ctx,
3248 LDAPMessage *res = NULL;
3249 LDAPMessage *msg = NULL;
3250 const char *attrs[] = { "dn", NULL };
3253 status = ads_search(ads, &res,
3254 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3256 if (!ADS_ERR_OK(status)) {
3260 count = ads_count_replies(ads, res);
3262 ads_msgfree(ads, res);
3263 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3266 for (msg = ads_first_entry(ads, res); msg;
3267 msg = ads_next_entry(ads, msg)) {
3268 const char **p = discard_const_p(const char *, *ous);
3271 dn = ads_get_dn(ads, talloc_tos(), msg);
3273 ads_msgfree(ads, res);
3274 return ADS_ERROR(LDAP_NO_MEMORY);
3277 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3279 ads_msgfree(ads, res);
3280 return ADS_ERROR(LDAP_NO_MEMORY);
3284 *ous = discard_const_p(char *, p);
3287 ads_msgfree(ads, res);
3294 * pull a struct dom_sid from an extended dn string
3295 * @param mem_ctx TALLOC_CTX
3296 * @param extended_dn string
3297 * @param flags string type of extended_dn
3298 * @param sid pointer to a struct dom_sid
3299 * @return NT_STATUS_OK on success,
3300 * NT_INVALID_PARAMETER on error,
3301 * NT_STATUS_NOT_FOUND if no SID present
3303 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3304 const char *extended_dn,
3305 enum ads_extended_dn_flags flags,
3306 struct dom_sid *sid)
3311 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3314 /* otherwise extended_dn gets stripped off */
3315 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3316 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3319 * ADS_EXTENDED_DN_HEX_STRING:
3320 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3322 * ADS_EXTENDED_DN_STRING (only with w2k3):
3323 * <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
3325 * Object with no SID, such as an Exchange Public Folder
3326 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3329 p = strchr(dn, ';');
3331 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3334 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3335 DEBUG(5,("No SID present in extended dn\n"));
3336 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3339 p += strlen(";<SID=");
3343 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3348 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3352 case ADS_EXTENDED_DN_STRING:
3353 if (!string_to_sid(sid, p)) {
3354 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3357 case ADS_EXTENDED_DN_HEX_STRING: {
3361 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3363 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3366 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3367 DEBUG(10,("failed to parse sid\n"));
3368 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3373 DEBUG(10,("unknown extended dn format\n"));
3374 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3377 return ADS_ERROR_NT(NT_STATUS_OK);
3380 /********************************************************************
3381 ********************************************************************/
3383 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3385 LDAPMessage *res = NULL;
3390 status = ads_find_machine_acct(ads, &res, machine_name);
3391 if (!ADS_ERR_OK(status)) {
3392 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3393 lp_netbios_name()));
3397 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3398 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3402 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3403 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3407 ads_msgfree(ads, res);
3412 /********************************************************************
3413 ********************************************************************/
3415 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3417 LDAPMessage *res = NULL;
3422 status = ads_find_machine_acct(ads, &res, machine_name);
3423 if (!ADS_ERR_OK(status)) {
3424 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3425 lp_netbios_name()));
3429 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3430 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3434 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3435 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3439 ads_msgfree(ads, res);
3444 /********************************************************************
3445 ********************************************************************/
3447 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3449 LDAPMessage *res = NULL;
3455 status = ads_find_machine_acct(ads, &res, machine_name);
3456 if (!ADS_ERR_OK(status)) {
3457 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3458 lp_netbios_name()));
3462 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3463 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3467 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3468 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3472 ads_msgfree(ads, res);
3474 ok = (strlen(name) > 0);
3482 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3485 * Join a machine to a realm
3486 * Creates the machine account and sets the machine password
3487 * @param ads connection to ads server
3488 * @param machine name of host to add
3489 * @param org_unit Organizational unit to place machine in
3490 * @return status of join
3492 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3493 uint32_t account_type, const char *org_unit)
3496 LDAPMessage *res = NULL;
3499 /* machine name must be lowercase */
3500 machine = SMB_STRDUP(machine_name);
3501 strlower_m(machine);
3504 status = ads_find_machine_acct(ads, (void **)&res, machine);
3505 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3506 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3507 status = ads_leave_realm(ads, machine);
3508 if (!ADS_ERR_OK(status)) {
3509 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3510 machine, ads->config.realm));
3515 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3516 if (!ADS_ERR_OK(status)) {
3517 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3522 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3523 if (!ADS_ERR_OK(status)) {
3524 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3530 ads_msgfree(ads, res);
3537 * Delete a machine from the realm
3538 * @param ads connection to ads server
3539 * @param hostname Machine to remove
3540 * @return status of delete
3542 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3547 char *hostnameDN, *host;
3549 LDAPControl ldap_control;
3550 LDAPControl * pldap_control[2] = {NULL, NULL};
3552 pldap_control[0] = &ldap_control;
3553 memset(&ldap_control, 0, sizeof(LDAPControl));
3554 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3556 /* hostname must be lowercase */
3557 host = SMB_STRDUP(hostname);
3558 if (!strlower_m(host)) {
3560 return ADS_ERROR_SYSTEM(EINVAL);
3563 status = ads_find_machine_acct(ads, &res, host);
3564 if (!ADS_ERR_OK(status)) {
3565 DEBUG(0, ("Host account for %s does not exist.\n", host));
3570 msg = ads_first_entry(ads, res);
3573 return ADS_ERROR_SYSTEM(ENOENT);
3576 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3577 if (hostnameDN == NULL) {
3579 return ADS_ERROR_SYSTEM(ENOENT);
3582 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3584 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3586 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3589 if (rc != LDAP_SUCCESS) {
3590 const char *attrs[] = { "cn", NULL };
3591 LDAPMessage *msg_sub;
3593 /* we only search with scope ONE, we do not expect any further
3594 * objects to be created deeper */
3596 status = ads_do_search_retry(ads, hostnameDN,
3597 LDAP_SCOPE_ONELEVEL,
3598 "(objectclass=*)", attrs, &res);
3600 if (!ADS_ERR_OK(status)) {
3602 TALLOC_FREE(hostnameDN);
3606 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3607 msg_sub = ads_next_entry(ads, msg_sub)) {
3611 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3613 TALLOC_FREE(hostnameDN);
3614 return ADS_ERROR(LDAP_NO_MEMORY);
3617 status = ads_del_dn(ads, dn);
3618 if (!ADS_ERR_OK(status)) {
3619 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3622 TALLOC_FREE(hostnameDN);
3629 /* there should be no subordinate objects anymore */
3630 status = ads_do_search_retry(ads, hostnameDN,
3631 LDAP_SCOPE_ONELEVEL,
3632 "(objectclass=*)", attrs, &res);
3634 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3636 TALLOC_FREE(hostnameDN);
3640 /* delete hostnameDN now */
3641 status = ads_del_dn(ads, hostnameDN);
3642 if (!ADS_ERR_OK(status)) {
3644 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3645 TALLOC_FREE(hostnameDN);
3650 TALLOC_FREE(hostnameDN);
3652 status = ads_find_machine_acct(ads, &res, host);
3653 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3654 DEBUG(3, ("Failed to remove host account.\n"));
3664 * pull all token-sids from an LDAP dn
3665 * @param ads connection to ads server
3666 * @param mem_ctx TALLOC_CTX for allocating sid array
3667 * @param dn of LDAP object
3668 * @param user_sid pointer to struct dom_sid (objectSid)
3669 * @param primary_group_sid pointer to struct dom_sid (self composed)
3670 * @param sids pointer to sid array to allocate
3671 * @param num_sids counter of SIDs pulled
3672 * @return status of token query
3674 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3675 TALLOC_CTX *mem_ctx,
3677 struct dom_sid *user_sid,
3678 struct dom_sid *primary_group_sid,
3679 struct dom_sid **sids,
3683 LDAPMessage *res = NULL;
3685 size_t tmp_num_sids;
3686 struct dom_sid *tmp_sids;
3687 struct dom_sid tmp_user_sid;
3688 struct dom_sid tmp_primary_group_sid;
3690 const char *attrs[] = {
3697 status = ads_search_retry_dn(ads, &res, dn, attrs);
3698 if (!ADS_ERR_OK(status)) {
3702 count = ads_count_replies(ads, res);
3704 ads_msgfree(ads, res);
3705 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3708 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3709 ads_msgfree(ads, res);
3710 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3713 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3714 ads_msgfree(ads, res);
3715 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3719 /* hack to compose the primary group sid without knowing the
3722 struct dom_sid domsid;
3724 sid_copy(&domsid, &tmp_user_sid);
3726 if (!sid_split_rid(&domsid, NULL)) {
3727 ads_msgfree(ads, res);
3728 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3731 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3732 ads_msgfree(ads, res);
3733 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3737 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3739 if (tmp_num_sids == 0 || !tmp_sids) {
3740 ads_msgfree(ads, res);
3741 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3745 *num_sids = tmp_num_sids;
3753 *user_sid = tmp_user_sid;
3756 if (primary_group_sid) {
3757 *primary_group_sid = tmp_primary_group_sid;
3760 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3762 ads_msgfree(ads, res);
3763 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3767 * Find a sAMAccoutName in LDAP
3768 * @param ads connection to ads server
3769 * @param mem_ctx TALLOC_CTX for allocating sid array
3770 * @param samaccountname to search
3771 * @param uac_ret uint32_t pointer userAccountControl attribute value
3772 * @param dn_ret pointer to dn
3773 * @return status of token query
3775 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3776 TALLOC_CTX *mem_ctx,
3777 const char *samaccountname,
3779 const char **dn_ret)
3782 const char *attrs[] = { "userAccountControl", NULL };
3784 LDAPMessage *res = NULL;
3788 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3790 if (filter == NULL) {
3791 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3795 status = ads_do_search_all(ads, ads->config.bind_path,
3797 filter, attrs, &res);
3799 if (!ADS_ERR_OK(status)) {
3803 if (ads_count_replies(ads, res) != 1) {
3804 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3808 dn = ads_get_dn(ads, talloc_tos(), res);
3810 status = ADS_ERROR(LDAP_NO_MEMORY);
3814 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3815 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3824 *dn_ret = talloc_strdup(mem_ctx, dn);
3826 status = ADS_ERROR(LDAP_NO_MEMORY);
3832 ads_msgfree(ads, res);
3838 * find our configuration path
3839 * @param ads connection to ads server
3840 * @param mem_ctx Pointer to talloc context
3841 * @param config_path Pointer to the config path
3842 * @return status of search
3844 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3845 TALLOC_CTX *mem_ctx,
3849 LDAPMessage *res = NULL;
3850 const char *config_context = NULL;
3851 const char *attrs[] = { "configurationNamingContext", NULL };
3853 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3854 "(objectclass=*)", attrs, &res);
3855 if (!ADS_ERR_OK(status)) {
3859 config_context = ads_pull_string(ads, mem_ctx, res,
3860 "configurationNamingContext");
3861 ads_msgfree(ads, res);
3862 if (!config_context) {
3863 return ADS_ERROR(LDAP_NO_MEMORY);
3867 *config_path = talloc_strdup(mem_ctx, config_context);
3868 if (!*config_path) {
3869 return ADS_ERROR(LDAP_NO_MEMORY);
3873 return ADS_ERROR(LDAP_SUCCESS);
3877 * find the displayName of an extended right
3878 * @param ads connection to ads server
3879 * @param config_path The config path
3880 * @param mem_ctx Pointer to talloc context
3881 * @param GUID struct of the rightsGUID
3882 * @return status of search
3884 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3885 const char *config_path,
3886 TALLOC_CTX *mem_ctx,
3887 const struct GUID *rights_guid)
3890 LDAPMessage *res = NULL;
3892 const char *attrs[] = { "displayName", NULL };
3893 const char *result = NULL;
3896 if (!ads || !mem_ctx || !rights_guid) {
3900 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3901 GUID_string(mem_ctx, rights_guid));
3906 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3911 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3913 if (!ADS_ERR_OK(rc)) {
3917 if (ads_count_replies(ads, res) != 1) {
3921 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3924 ads_msgfree(ads, res);
3929 * verify or build and verify an account ou
3930 * @param mem_ctx Pointer to talloc context
3931 * @param ads connection to ads server
3933 * @return status of search
3936 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3938 const char **account_ou)
3944 if (account_ou == NULL) {
3945 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3948 if (*account_ou != NULL) {
3949 exploded_dn = ldap_explode_dn(*account_ou, 0);
3951 ldap_value_free(exploded_dn);
3956 ou_string = ads_ou_string(ads, *account_ou);
3958 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3961 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3962 ads->config.bind_path);
3963 SAFE_FREE(ou_string);
3966 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3969 exploded_dn = ldap_explode_dn(name, 0);
3971 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3973 ldap_value_free(exploded_dn);