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 struct dom_sid_buf buf;
2803 DBG_DEBUG("pulling SID: %s\n",
2804 dom_sid_str_buf(&(*sids)[count], &buf));
2809 ldap_value_free_len(values);
2814 * pull a struct security_descriptor from a ADS result
2815 * @param ads connection to ads server
2816 * @param mem_ctx TALLOC_CTX for allocating sid array
2817 * @param msg Results of search
2818 * @param field Attribute to retrieve
2819 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2820 * @return boolean inidicating success
2822 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2823 LDAPMessage *msg, const char *field,
2824 struct security_descriptor **sd)
2826 struct berval **values;
2829 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2831 if (!values) return false;
2835 status = unmarshall_sec_desc(mem_ctx,
2836 (uint8_t *)values[0]->bv_val,
2837 values[0]->bv_len, sd);
2838 if (!NT_STATUS_IS_OK(status)) {
2839 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2840 nt_errstr(status)));
2845 ldap_value_free_len(values);
2850 * in order to support usernames longer than 21 characters we need to
2851 * use both the sAMAccountName and the userPrincipalName attributes
2852 * It seems that not all users have the userPrincipalName attribute set
2854 * @param ads connection to ads server
2855 * @param mem_ctx TALLOC_CTX for allocating sid array
2856 * @param msg Results of search
2857 * @return the username
2859 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2865 /* lookup_name() only works on the sAMAccountName to
2866 returning the username portion of userPrincipalName
2867 breaks winbindd_getpwnam() */
2869 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2870 if (ret && (p = strchr_m(ret, '@'))) {
2875 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2880 * find the update serial number - this is the core of the ldap cache
2881 * @param ads connection to ads server
2882 * @param ads connection to ADS server
2883 * @param usn Pointer to retrieved update serial number
2884 * @return status of search
2886 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2888 const char *attrs[] = {"highestCommittedUSN", NULL};
2892 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2893 if (!ADS_ERR_OK(status))
2896 if (ads_count_replies(ads, res) != 1) {
2897 ads_msgfree(ads, res);
2898 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2901 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2902 ads_msgfree(ads, res);
2903 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2906 ads_msgfree(ads, res);
2910 /* parse a ADS timestring - typical string is
2911 '20020917091222.0Z0' which means 09:12.22 17th September
2913 static time_t ads_parse_time(const char *str)
2919 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2920 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2921 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2930 /********************************************************************
2931 ********************************************************************/
2933 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2935 const char *attrs[] = {"currentTime", NULL};
2940 ADS_STRUCT *ads_s = ads;
2942 if (!(ctx = talloc_init("ads_current_time"))) {
2943 return ADS_ERROR(LDAP_NO_MEMORY);
2946 /* establish a new ldap tcp session if necessary */
2948 if ( !ads->ldap.ld ) {
2949 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2950 ads->server.ldap_server )) == NULL )
2952 status = ADS_ERROR(LDAP_NO_MEMORY);
2955 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2956 status = ads_connect( ads_s );
2957 if ( !ADS_ERR_OK(status))
2961 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2962 if (!ADS_ERR_OK(status)) {
2966 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2968 ads_msgfree(ads_s, res);
2969 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2973 /* but save the time and offset in the original ADS_STRUCT */
2975 ads->config.current_time = ads_parse_time(timestr);
2977 if (ads->config.current_time != 0) {
2978 ads->auth.time_offset = ads->config.current_time - time(NULL);
2979 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2982 ads_msgfree(ads, res);
2984 status = ADS_SUCCESS;
2987 /* free any temporary ads connections */
2988 if ( ads_s != ads ) {
2989 ads_destroy( &ads_s );
2991 talloc_destroy(ctx);
2996 /********************************************************************
2997 ********************************************************************/
2999 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3001 const char *attrs[] = {"domainFunctionality", NULL};
3004 ADS_STRUCT *ads_s = ads;
3006 *val = DS_DOMAIN_FUNCTION_2000;
3008 /* establish a new ldap tcp session if necessary */
3010 if ( !ads->ldap.ld ) {
3011 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3012 ads->server.ldap_server )) == NULL )
3014 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3017 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3018 status = ads_connect( ads_s );
3019 if ( !ADS_ERR_OK(status))
3023 /* If the attribute does not exist assume it is a Windows 2000
3024 functional domain */
3026 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3027 if (!ADS_ERR_OK(status)) {
3028 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3029 status = ADS_SUCCESS;
3034 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3035 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3037 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3040 ads_msgfree(ads, res);
3043 /* free any temporary ads connections */
3044 if ( ads_s != ads ) {
3045 ads_destroy( &ads_s );
3052 * find the domain sid for our domain
3053 * @param ads connection to ads server
3054 * @param sid Pointer to domain sid
3055 * @return status of search
3057 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3059 const char *attrs[] = {"objectSid", NULL};
3063 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3065 if (!ADS_ERR_OK(rc)) return rc;
3066 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3067 ads_msgfree(ads, res);
3068 return ADS_ERROR_SYSTEM(ENOENT);
3070 ads_msgfree(ads, res);
3076 * find our site name
3077 * @param ads connection to ads server
3078 * @param mem_ctx Pointer to talloc context
3079 * @param site_name Pointer to the sitename
3080 * @return status of search
3082 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3086 const char *dn, *service_name;
3087 const char *attrs[] = { "dsServiceName", NULL };
3089 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3090 if (!ADS_ERR_OK(status)) {
3094 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3095 if (service_name == NULL) {
3096 ads_msgfree(ads, res);
3097 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3100 ads_msgfree(ads, res);
3102 /* go up three levels */
3103 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3105 return ADS_ERROR(LDAP_NO_MEMORY);
3108 *site_name = talloc_strdup(mem_ctx, dn);
3109 if (*site_name == NULL) {
3110 return ADS_ERROR(LDAP_NO_MEMORY);
3115 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3120 * find the site dn where a machine resides
3121 * @param ads connection to ads server
3122 * @param mem_ctx Pointer to talloc context
3123 * @param computer_name name of the machine
3124 * @param site_name Pointer to the sitename
3125 * @return status of search
3127 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3131 const char *parent, *filter;
3132 char *config_context = NULL;
3135 /* shortcut a query */
3136 if (strequal(computer_name, ads->config.ldap_server_name)) {
3137 return ads_site_dn(ads, mem_ctx, site_dn);
3140 status = ads_config_path(ads, mem_ctx, &config_context);
3141 if (!ADS_ERR_OK(status)) {
3145 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3146 if (filter == NULL) {
3147 return ADS_ERROR(LDAP_NO_MEMORY);
3150 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3151 filter, NULL, &res);
3152 if (!ADS_ERR_OK(status)) {
3156 if (ads_count_replies(ads, res) != 1) {
3157 ads_msgfree(ads, res);
3158 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3161 dn = ads_get_dn(ads, mem_ctx, res);
3163 ads_msgfree(ads, res);
3164 return ADS_ERROR(LDAP_NO_MEMORY);
3167 /* go up three levels */
3168 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3169 if (parent == NULL) {
3170 ads_msgfree(ads, res);
3172 return ADS_ERROR(LDAP_NO_MEMORY);
3175 *site_dn = talloc_strdup(mem_ctx, parent);
3176 if (*site_dn == NULL) {
3177 ads_msgfree(ads, res);
3179 return ADS_ERROR(LDAP_NO_MEMORY);
3183 ads_msgfree(ads, res);
3189 * get the upn suffixes for a domain
3190 * @param ads connection to ads server
3191 * @param mem_ctx Pointer to talloc context
3192 * @param suffixes Pointer to an array of suffixes
3193 * @param num_suffixes Pointer to the number of suffixes
3194 * @return status of search
3196 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3201 char *config_context = NULL;
3202 const char *attrs[] = { "uPNSuffixes", NULL };
3204 status = ads_config_path(ads, mem_ctx, &config_context);
3205 if (!ADS_ERR_OK(status)) {
3209 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3211 return ADS_ERROR(LDAP_NO_MEMORY);
3214 status = ads_search_dn(ads, &res, base, attrs);
3215 if (!ADS_ERR_OK(status)) {
3219 if (ads_count_replies(ads, res) != 1) {
3220 ads_msgfree(ads, res);
3221 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3224 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3225 if ((*suffixes) == NULL) {
3226 ads_msgfree(ads, res);
3227 return ADS_ERROR(LDAP_NO_MEMORY);
3230 ads_msgfree(ads, res);
3236 * get the joinable ous for a domain
3237 * @param ads connection to ads server
3238 * @param mem_ctx Pointer to talloc context
3239 * @param ous Pointer to an array of ous
3240 * @param num_ous Pointer to the number of ous
3241 * @return status of search
3243 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3244 TALLOC_CTX *mem_ctx,
3249 LDAPMessage *res = NULL;
3250 LDAPMessage *msg = NULL;
3251 const char *attrs[] = { "dn", NULL };
3254 status = ads_search(ads, &res,
3255 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3257 if (!ADS_ERR_OK(status)) {
3261 count = ads_count_replies(ads, res);
3263 ads_msgfree(ads, res);
3264 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3267 for (msg = ads_first_entry(ads, res); msg;
3268 msg = ads_next_entry(ads, msg)) {
3269 const char **p = discard_const_p(const char *, *ous);
3272 dn = ads_get_dn(ads, talloc_tos(), msg);
3274 ads_msgfree(ads, res);
3275 return ADS_ERROR(LDAP_NO_MEMORY);
3278 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3280 ads_msgfree(ads, res);
3281 return ADS_ERROR(LDAP_NO_MEMORY);
3285 *ous = discard_const_p(char *, p);
3288 ads_msgfree(ads, res);
3295 * pull a struct dom_sid from an extended dn string
3296 * @param mem_ctx TALLOC_CTX
3297 * @param extended_dn string
3298 * @param flags string type of extended_dn
3299 * @param sid pointer to a struct dom_sid
3300 * @return NT_STATUS_OK on success,
3301 * NT_INVALID_PARAMETER on error,
3302 * NT_STATUS_NOT_FOUND if no SID present
3304 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3305 const char *extended_dn,
3306 enum ads_extended_dn_flags flags,
3307 struct dom_sid *sid)
3312 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3315 /* otherwise extended_dn gets stripped off */
3316 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3317 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3320 * ADS_EXTENDED_DN_HEX_STRING:
3321 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3323 * ADS_EXTENDED_DN_STRING (only with w2k3):
3324 * <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
3326 * Object with no SID, such as an Exchange Public Folder
3327 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3330 p = strchr(dn, ';');
3332 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3335 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3336 DEBUG(5,("No SID present in extended dn\n"));
3337 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3340 p += strlen(";<SID=");
3344 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3349 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3353 case ADS_EXTENDED_DN_STRING:
3354 if (!string_to_sid(sid, p)) {
3355 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3358 case ADS_EXTENDED_DN_HEX_STRING: {
3362 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3364 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3367 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3368 DEBUG(10,("failed to parse sid\n"));
3369 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3374 DEBUG(10,("unknown extended dn format\n"));
3375 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3378 return ADS_ERROR_NT(NT_STATUS_OK);
3381 /********************************************************************
3382 ********************************************************************/
3384 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3386 LDAPMessage *res = NULL;
3391 status = ads_find_machine_acct(ads, &res, machine_name);
3392 if (!ADS_ERR_OK(status)) {
3393 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3394 lp_netbios_name()));
3398 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3399 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3403 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3404 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3408 ads_msgfree(ads, res);
3413 /********************************************************************
3414 ********************************************************************/
3416 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3418 LDAPMessage *res = NULL;
3423 status = ads_find_machine_acct(ads, &res, machine_name);
3424 if (!ADS_ERR_OK(status)) {
3425 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3426 lp_netbios_name()));
3430 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3431 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3435 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3436 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3440 ads_msgfree(ads, res);
3445 /********************************************************************
3446 ********************************************************************/
3448 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3450 LDAPMessage *res = NULL;
3456 status = ads_find_machine_acct(ads, &res, machine_name);
3457 if (!ADS_ERR_OK(status)) {
3458 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3459 lp_netbios_name()));
3463 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3464 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3468 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3469 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3473 ads_msgfree(ads, res);
3475 ok = (strlen(name) > 0);
3483 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3486 * Join a machine to a realm
3487 * Creates the machine account and sets the machine password
3488 * @param ads connection to ads server
3489 * @param machine name of host to add
3490 * @param org_unit Organizational unit to place machine in
3491 * @return status of join
3493 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3494 uint32_t account_type, const char *org_unit)
3497 LDAPMessage *res = NULL;
3500 /* machine name must be lowercase */
3501 machine = SMB_STRDUP(machine_name);
3502 strlower_m(machine);
3505 status = ads_find_machine_acct(ads, (void **)&res, machine);
3506 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3507 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3508 status = ads_leave_realm(ads, machine);
3509 if (!ADS_ERR_OK(status)) {
3510 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3511 machine, ads->config.realm));
3516 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3517 if (!ADS_ERR_OK(status)) {
3518 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3523 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3524 if (!ADS_ERR_OK(status)) {
3525 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3531 ads_msgfree(ads, res);
3538 * Delete a machine from the realm
3539 * @param ads connection to ads server
3540 * @param hostname Machine to remove
3541 * @return status of delete
3543 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3548 char *hostnameDN, *host;
3550 LDAPControl ldap_control;
3551 LDAPControl * pldap_control[2] = {NULL, NULL};
3553 pldap_control[0] = &ldap_control;
3554 memset(&ldap_control, 0, sizeof(LDAPControl));
3555 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3557 /* hostname must be lowercase */
3558 host = SMB_STRDUP(hostname);
3559 if (!strlower_m(host)) {
3561 return ADS_ERROR_SYSTEM(EINVAL);
3564 status = ads_find_machine_acct(ads, &res, host);
3565 if (!ADS_ERR_OK(status)) {
3566 DEBUG(0, ("Host account for %s does not exist.\n", host));
3571 msg = ads_first_entry(ads, res);
3574 return ADS_ERROR_SYSTEM(ENOENT);
3577 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3578 if (hostnameDN == NULL) {
3580 return ADS_ERROR_SYSTEM(ENOENT);
3583 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3585 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3587 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3590 if (rc != LDAP_SUCCESS) {
3591 const char *attrs[] = { "cn", NULL };
3592 LDAPMessage *msg_sub;
3594 /* we only search with scope ONE, we do not expect any further
3595 * objects to be created deeper */
3597 status = ads_do_search_retry(ads, hostnameDN,
3598 LDAP_SCOPE_ONELEVEL,
3599 "(objectclass=*)", attrs, &res);
3601 if (!ADS_ERR_OK(status)) {
3603 TALLOC_FREE(hostnameDN);
3607 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3608 msg_sub = ads_next_entry(ads, msg_sub)) {
3612 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3614 TALLOC_FREE(hostnameDN);
3615 return ADS_ERROR(LDAP_NO_MEMORY);
3618 status = ads_del_dn(ads, dn);
3619 if (!ADS_ERR_OK(status)) {
3620 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3623 TALLOC_FREE(hostnameDN);
3630 /* there should be no subordinate objects anymore */
3631 status = ads_do_search_retry(ads, hostnameDN,
3632 LDAP_SCOPE_ONELEVEL,
3633 "(objectclass=*)", attrs, &res);
3635 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3637 TALLOC_FREE(hostnameDN);
3641 /* delete hostnameDN now */
3642 status = ads_del_dn(ads, hostnameDN);
3643 if (!ADS_ERR_OK(status)) {
3645 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3646 TALLOC_FREE(hostnameDN);
3651 TALLOC_FREE(hostnameDN);
3653 status = ads_find_machine_acct(ads, &res, host);
3654 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3655 DEBUG(3, ("Failed to remove host account.\n"));
3665 * pull all token-sids from an LDAP dn
3666 * @param ads connection to ads server
3667 * @param mem_ctx TALLOC_CTX for allocating sid array
3668 * @param dn of LDAP object
3669 * @param user_sid pointer to struct dom_sid (objectSid)
3670 * @param primary_group_sid pointer to struct dom_sid (self composed)
3671 * @param sids pointer to sid array to allocate
3672 * @param num_sids counter of SIDs pulled
3673 * @return status of token query
3675 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3676 TALLOC_CTX *mem_ctx,
3678 struct dom_sid *user_sid,
3679 struct dom_sid *primary_group_sid,
3680 struct dom_sid **sids,
3684 LDAPMessage *res = NULL;
3686 size_t tmp_num_sids;
3687 struct dom_sid *tmp_sids;
3688 struct dom_sid tmp_user_sid;
3689 struct dom_sid tmp_primary_group_sid;
3691 const char *attrs[] = {
3698 status = ads_search_retry_dn(ads, &res, dn, attrs);
3699 if (!ADS_ERR_OK(status)) {
3703 count = ads_count_replies(ads, res);
3705 ads_msgfree(ads, res);
3706 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3709 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3710 ads_msgfree(ads, res);
3711 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3714 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3715 ads_msgfree(ads, res);
3716 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3720 /* hack to compose the primary group sid without knowing the
3723 struct dom_sid domsid;
3725 sid_copy(&domsid, &tmp_user_sid);
3727 if (!sid_split_rid(&domsid, NULL)) {
3728 ads_msgfree(ads, res);
3729 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3732 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3733 ads_msgfree(ads, res);
3734 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3738 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3740 if (tmp_num_sids == 0 || !tmp_sids) {
3741 ads_msgfree(ads, res);
3742 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3746 *num_sids = tmp_num_sids;
3754 *user_sid = tmp_user_sid;
3757 if (primary_group_sid) {
3758 *primary_group_sid = tmp_primary_group_sid;
3761 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3763 ads_msgfree(ads, res);
3764 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3768 * Find a sAMAccoutName in LDAP
3769 * @param ads connection to ads server
3770 * @param mem_ctx TALLOC_CTX for allocating sid array
3771 * @param samaccountname to search
3772 * @param uac_ret uint32_t pointer userAccountControl attribute value
3773 * @param dn_ret pointer to dn
3774 * @return status of token query
3776 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3777 TALLOC_CTX *mem_ctx,
3778 const char *samaccountname,
3780 const char **dn_ret)
3783 const char *attrs[] = { "userAccountControl", NULL };
3785 LDAPMessage *res = NULL;
3789 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3791 if (filter == NULL) {
3792 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3796 status = ads_do_search_all(ads, ads->config.bind_path,
3798 filter, attrs, &res);
3800 if (!ADS_ERR_OK(status)) {
3804 if (ads_count_replies(ads, res) != 1) {
3805 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3809 dn = ads_get_dn(ads, talloc_tos(), res);
3811 status = ADS_ERROR(LDAP_NO_MEMORY);
3815 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3816 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3825 *dn_ret = talloc_strdup(mem_ctx, dn);
3827 status = ADS_ERROR(LDAP_NO_MEMORY);
3833 ads_msgfree(ads, res);
3839 * find our configuration path
3840 * @param ads connection to ads server
3841 * @param mem_ctx Pointer to talloc context
3842 * @param config_path Pointer to the config path
3843 * @return status of search
3845 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3846 TALLOC_CTX *mem_ctx,
3850 LDAPMessage *res = NULL;
3851 const char *config_context = NULL;
3852 const char *attrs[] = { "configurationNamingContext", NULL };
3854 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3855 "(objectclass=*)", attrs, &res);
3856 if (!ADS_ERR_OK(status)) {
3860 config_context = ads_pull_string(ads, mem_ctx, res,
3861 "configurationNamingContext");
3862 ads_msgfree(ads, res);
3863 if (!config_context) {
3864 return ADS_ERROR(LDAP_NO_MEMORY);
3868 *config_path = talloc_strdup(mem_ctx, config_context);
3869 if (!*config_path) {
3870 return ADS_ERROR(LDAP_NO_MEMORY);
3874 return ADS_ERROR(LDAP_SUCCESS);
3878 * find the displayName of an extended right
3879 * @param ads connection to ads server
3880 * @param config_path The config path
3881 * @param mem_ctx Pointer to talloc context
3882 * @param GUID struct of the rightsGUID
3883 * @return status of search
3885 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3886 const char *config_path,
3887 TALLOC_CTX *mem_ctx,
3888 const struct GUID *rights_guid)
3891 LDAPMessage *res = NULL;
3893 const char *attrs[] = { "displayName", NULL };
3894 const char *result = NULL;
3897 if (!ads || !mem_ctx || !rights_guid) {
3901 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3902 GUID_string(mem_ctx, rights_guid));
3907 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3912 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3914 if (!ADS_ERR_OK(rc)) {
3918 if (ads_count_replies(ads, res) != 1) {
3922 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3925 ads_msgfree(ads, res);
3930 * verify or build and verify an account ou
3931 * @param mem_ctx Pointer to talloc context
3932 * @param ads connection to ads server
3934 * @return status of search
3937 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3939 const char **account_ou)
3945 if (account_ou == NULL) {
3946 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3949 if (*account_ou != NULL) {
3950 exploded_dn = ldap_explode_dn(*account_ou, 0);
3952 ldap_value_free(exploded_dn);
3957 ou_string = ads_ou_string(ads, *account_ou);
3959 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3962 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3963 ads->config.bind_path);
3964 SAFE_FREE(ou_string);
3967 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3970 exploded_dn = ldap_explode_dn(name, 0);
3972 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3974 ldap_value_free(exploded_dn);