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"
39 * @brief basic ldap client-side routines for ads server communications
41 * The routines contained here should do the necessary ldap calls for
44 * Important note: attribute names passed into ads_ routines must
45 * already be in UTF-8 format. We do not convert them because in almost
46 * all cases, they are just ascii (which is represented with the same
47 * codepoints in UTF-8). This may have to change at some point
51 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
53 static SIG_ATOMIC_T gotalarm;
55 /***************************************************************
56 Signal function to tell us we timed out.
57 ****************************************************************/
59 static void gotalarm_sig(int signum)
64 LDAP *ldap_open_with_timeout(const char *server,
65 struct sockaddr_storage *ss,
66 int port, unsigned int to)
72 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
73 "%u seconds\n", server, port, to));
78 CatchSignal(SIGALRM, gotalarm_sig);
80 /* End setup timeout. */
83 if ( strchr_m(server, ':') ) {
85 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
88 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
94 #ifdef HAVE_LDAP_INITIALIZE
95 ldap_err = ldap_initialize(&ldp, uri);
97 ldp = ldap_open(server, port);
99 ldap_err = LDAP_SUCCESS;
101 ldap_err = LDAP_OTHER;
104 if (ldap_err != LDAP_SUCCESS) {
105 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
106 uri, ldap_err2string(ldap_err)));
108 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
112 /* Teardown timeout. */
114 CatchSignal(SIGALRM, SIG_IGN);
120 static int ldap_search_with_timeout(LDAP *ld,
121 LDAP_CONST char *base,
123 LDAP_CONST char *filter,
126 LDAPControl **sctrls,
127 LDAPControl **cctrls,
131 int to = lp_ldap_timeout();
132 struct timeval timeout;
133 struct timeval *timeout_ptr = NULL;
136 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
142 timeout_ptr = &timeout;
144 /* Setup alarm timeout. */
145 CatchSignal(SIGALRM, gotalarm_sig);
146 /* Make the alarm time one second beyond
147 the timout we're setting for the
148 remote search timeout, to allow that
149 to fire in preference. */
151 /* End setup timeout. */
155 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
156 attrsonly, sctrls, cctrls, timeout_ptr,
160 /* Teardown alarm timeout. */
161 CatchSignal(SIGALRM, SIG_IGN);
166 return LDAP_TIMELIMIT_EXCEEDED;
169 * A bug in OpenLDAP means ldap_search_ext_s can return
170 * LDAP_SUCCESS but with a NULL res pointer. Cope with
171 * this. See bug #6279 for details. JRA.
175 return LDAP_TIMELIMIT_EXCEEDED;
181 /**********************************************
182 Do client and server sitename match ?
183 **********************************************/
185 bool ads_sitename_match(ADS_STRUCT *ads)
187 if (ads->config.server_site_name == NULL &&
188 ads->config.client_site_name == NULL ) {
189 DEBUG(10,("ads_sitename_match: both null\n"));
192 if (ads->config.server_site_name &&
193 ads->config.client_site_name &&
194 strequal(ads->config.server_site_name,
195 ads->config.client_site_name)) {
196 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
199 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
200 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
201 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
205 /**********************************************
206 Is this the closest DC ?
207 **********************************************/
209 bool ads_closest_dc(ADS_STRUCT *ads)
211 if (ads->config.flags & NBT_SERVER_CLOSEST) {
212 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
216 /* not sure if this can ever happen */
217 if (ads_sitename_match(ads)) {
218 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
222 if (ads->config.client_site_name == NULL) {
223 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
227 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
228 ads->config.ldap_server_name));
235 try a connection to a given ldap server, returning True and setting the servers IP
236 in the ads struct if successful
238 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
239 struct sockaddr_storage *ss)
241 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
242 TALLOC_CTX *frame = talloc_stackframe();
244 char addr[INET6_ADDRSTRLEN];
251 print_sockaddr(addr, sizeof(addr), ss);
253 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
254 addr, ads->server.realm));
256 ZERO_STRUCT( cldap_reply );
258 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
259 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
264 /* Check the CLDAP reply flags */
266 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
267 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
273 /* Fill in the ads->config values */
275 SAFE_FREE(ads->config.realm);
276 SAFE_FREE(ads->config.bind_path);
277 SAFE_FREE(ads->config.ldap_server_name);
278 SAFE_FREE(ads->config.server_site_name);
279 SAFE_FREE(ads->config.client_site_name);
280 SAFE_FREE(ads->server.workgroup);
282 ads->config.flags = cldap_reply.server_type;
283 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
284 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
285 if (!strupper_m(ads->config.realm)) {
290 ads->config.bind_path = ads_build_dn(ads->config.realm);
291 if (*cldap_reply.server_site) {
292 ads->config.server_site_name =
293 SMB_STRDUP(cldap_reply.server_site);
295 if (*cldap_reply.client_site) {
296 ads->config.client_site_name =
297 SMB_STRDUP(cldap_reply.client_site);
299 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
301 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
304 /* Store our site name. */
305 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
306 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
316 /**********************************************************************
317 send a cldap ping to list of servers, one at a time, until one of
318 them answers it's an ldap server. Record success in the ADS_STRUCT.
319 Take note of and update negative connection cache.
320 **********************************************************************/
322 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,const char *domain,
323 struct ip_service *ip_list, int count)
328 for (i = 0; i < count; i++) {
329 char server[INET6_ADDRSTRLEN];
331 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
333 if (!NT_STATUS_IS_OK(
334 check_negative_conn_cache(domain, server)))
337 ok = ads_try_connect(ads, false, &ip_list[i].ss);
342 /* keep track of failures */
343 add_failed_connection_entry(domain, server,
344 NT_STATUS_UNSUCCESSFUL);
347 return NT_STATUS_NO_LOGON_SERVERS;
350 /***************************************************************************
351 resolve a name and perform an "ldap ping" using NetBIOS and related methods
352 ****************************************************************************/
354 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
355 const char *domain, const char *realm)
358 struct ip_service *ip_list;
359 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
361 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
364 status = get_sorted_dc_list(domain, NULL, &ip_list, &count,
366 if (!NT_STATUS_IS_OK(status)) {
370 /* remove servers which are known to be dead based on
371 the corresponding DNS method */
373 for (i = 0; i < count; ++i) {
374 char server[INET6_ADDRSTRLEN];
376 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
379 check_negative_conn_cache(realm, server))) {
380 /* Ensure we add the workgroup name for this
381 IP address as negative too. */
382 add_failed_connection_entry(
384 NT_STATUS_UNSUCCESSFUL);
389 status = cldap_ping_list(ads, domain, ip_list, count);
397 /**********************************************************************
398 resolve a name and perform an "ldap ping" using DNS
399 **********************************************************************/
401 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
405 struct ip_service *ip_list;
406 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
408 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
411 status = get_sorted_dc_list(realm, sitename, &ip_list, &count,
413 if (!NT_STATUS_IS_OK(status)) {
417 status = cldap_ping_list(ads, realm, ip_list, count);
424 /**********************************************************************
425 Try to find an AD dc using our internal name resolution routines
426 Try the realm first and then then workgroup name if netbios is not
428 **********************************************************************/
430 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
432 const char *c_domain = "";
434 bool use_own_domain = False;
435 char *sitename = NULL;
436 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
439 /* if the realm and workgroup are both empty, assume they are ours */
442 c_realm = ads->server.realm;
448 /* special case where no realm and no workgroup means our own */
449 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
450 use_own_domain = True;
451 c_realm = lp_realm();
455 if (!lp_disable_netbios()) {
456 if (use_own_domain) {
457 c_domain = lp_workgroup();
459 c_domain = ads->server.workgroup;
460 if (!*c_realm && (!c_domain || !*c_domain)) {
461 c_domain = lp_workgroup();
470 if (!*c_realm && !*c_domain) {
471 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
473 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
477 * In case of LDAP we use get_dc_name() as that
478 * creates the custom krb5.conf file
480 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
482 struct sockaddr_storage ip_out;
484 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
485 " and falling back to domain '%s'\n",
488 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
491 * we call ads_try_connect() to fill in the
492 * ads->config details
494 ok = ads_try_connect(ads, false, &ip_out);
500 return NT_STATUS_NO_LOGON_SERVERS;
504 sitename = sitename_fetch(talloc_tos(), c_realm);
505 status = resolve_and_ping_dns(ads, sitename, c_realm);
507 if (NT_STATUS_IS_OK(status)) {
508 TALLOC_FREE(sitename);
512 /* In case we failed to contact one of our closest DC on our
514 * need to try to find another DC, retry with a site-less SRV
519 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
520 "our site (%s), Trying to find another DC "
521 "for realm '%s' (domain '%s')\n",
522 sitename, c_realm, c_domain));
523 namecache_delete(c_realm, 0x1C);
525 resolve_and_ping_dns(ads, NULL, c_realm);
527 if (NT_STATUS_IS_OK(status)) {
528 TALLOC_FREE(sitename);
533 TALLOC_FREE(sitename);
536 /* try netbios as fallback - if permitted,
537 or if configuration specifically requests it */
540 DEBUG(3, ("ads_find_dc: falling back to netbios "
541 "name resolution for domain '%s' (realm '%s')\n",
545 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
546 if (NT_STATUS_IS_OK(status)) {
551 DEBUG(1, ("ads_find_dc: "
552 "name resolution for realm '%s' (domain '%s') failed: %s\n",
553 c_realm, c_domain, nt_errstr(status)));
557 * Connect to the LDAP server
558 * @param ads Pointer to an existing ADS_STRUCT
559 * @return status of connection
561 ADS_STATUS ads_connect(ADS_STRUCT *ads)
563 int version = LDAP_VERSION3;
566 char addr[INET6_ADDRSTRLEN];
568 ZERO_STRUCT(ads->ldap);
569 ads->ldap.last_attempt = time_mono(NULL);
570 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
572 /* try with a user specified server */
574 if (DEBUGLEVEL >= 11) {
575 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
576 DEBUG(11,("ads_connect: entering\n"));
577 DEBUGADD(11,("%s\n", s));
581 if (ads->server.ldap_server) {
583 struct sockaddr_storage ss;
585 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
587 DEBUG(5,("ads_connect: unable to resolve name %s\n",
588 ads->server.ldap_server));
589 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
592 ok = ads_try_connect(ads, ads->server.gc, &ss);
597 /* The choice of which GC use is handled one level up in
598 ads_connect_gc(). If we continue on from here with
599 ads_find_dc() we will get GC searches on port 389 which
600 doesn't work. --jerry */
602 if (ads->server.gc == true) {
603 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
607 ntstatus = ads_find_dc(ads);
608 if (NT_STATUS_IS_OK(ntstatus)) {
612 status = ADS_ERROR_NT(ntstatus);
617 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
618 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
620 if (!ads->auth.user_name) {
621 /* Must use the userPrincipalName value here or sAMAccountName
622 and not servicePrincipalName; found by Guenther Deschner */
624 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
625 DEBUG(0,("ads_connect: asprintf fail.\n"));
626 ads->auth.user_name = NULL;
630 if (!ads->auth.realm) {
631 ads->auth.realm = SMB_STRDUP(ads->config.realm);
634 if (!ads->auth.kdc_server) {
635 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
636 ads->auth.kdc_server = SMB_STRDUP(addr);
639 /* If the caller() requested no LDAP bind, then we are done */
641 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
642 status = ADS_SUCCESS;
646 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
647 if (!ads->ldap.mem_ctx) {
648 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
652 /* Otherwise setup the TCP LDAP session */
654 ads->ldap.ld = ldap_open_with_timeout(addr,
656 ads->ldap.port, lp_ldap_timeout());
657 if (ads->ldap.ld == NULL) {
658 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
661 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
663 /* cache the successful connection for workgroup and realm */
664 if (ads_closest_dc(ads)) {
665 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
666 saf_store( ads->server.realm, ads->config.ldap_server_name);
669 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
671 if ( lp_ldap_ssl_ads() ) {
672 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
673 if (!ADS_ERR_OK(status)) {
678 /* fill in the current time and offsets */
680 status = ads_current_time( ads );
681 if ( !ADS_ERR_OK(status) ) {
685 /* Now do the bind */
687 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
688 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
692 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
693 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
697 status = ads_sasl_bind(ads);
700 if (DEBUGLEVEL >= 11) {
701 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
702 DEBUG(11,("ads_connect: leaving with: %s\n",
703 ads_errstr(status)));
704 DEBUGADD(11,("%s\n", s));
712 * Connect to the LDAP server using given credentials
713 * @param ads Pointer to an existing ADS_STRUCT
714 * @return status of connection
716 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
718 ads->auth.flags |= ADS_AUTH_USER_CREDS;
720 return ads_connect(ads);
724 * Disconnect the LDAP server
725 * @param ads Pointer to an existing ADS_STRUCT
727 void ads_disconnect(ADS_STRUCT *ads)
730 ldap_unbind(ads->ldap.ld);
733 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
734 ads->ldap.wrap_ops->disconnect(ads);
736 if (ads->ldap.mem_ctx) {
737 talloc_free(ads->ldap.mem_ctx);
739 ZERO_STRUCT(ads->ldap);
743 Duplicate a struct berval into talloc'ed memory
745 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
747 struct berval *value;
749 if (!in_val) return NULL;
751 value = talloc_zero(ctx, struct berval);
754 if (in_val->bv_len == 0) return value;
756 value->bv_len = in_val->bv_len;
757 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
763 Make a values list out of an array of (struct berval *)
765 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
766 const struct berval **in_vals)
768 struct berval **values;
771 if (!in_vals) return NULL;
772 for (i=0; in_vals[i]; i++)
774 values = talloc_zero_array(ctx, struct berval *, i+1);
775 if (!values) return NULL;
777 for (i=0; in_vals[i]; i++) {
778 values[i] = dup_berval(ctx, in_vals[i]);
784 UTF8-encode a values list out of an array of (char *)
786 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
792 if (!in_vals) return NULL;
793 for (i=0; in_vals[i]; i++)
795 values = talloc_zero_array(ctx, char *, i+1);
796 if (!values) return NULL;
798 for (i=0; in_vals[i]; i++) {
799 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
808 Pull a (char *) array out of a UTF8-encoded values list
810 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
814 size_t converted_size;
816 if (!in_vals) return NULL;
817 for (i=0; in_vals[i]; i++)
819 values = talloc_zero_array(ctx, char *, i+1);
820 if (!values) return NULL;
822 for (i=0; in_vals[i]; i++) {
823 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
825 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
826 "%s", strerror(errno)));
833 * Do a search with paged results. cookie must be null on the first
834 * call, and then returned on each subsequent call. It will be null
835 * again when the entire search is complete
836 * @param ads connection to ads server
837 * @param bind_path Base dn for the search
838 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
839 * @param expr Search expression - specified in local charset
840 * @param attrs Attributes to retrieve - specified in utf8 or ascii
841 * @param res ** which will contain results - free res* with ads_msgfree()
842 * @param count Number of entries retrieved on this page
843 * @param cookie The paged results cookie to be returned on subsequent calls
844 * @return status of search
846 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
847 const char *bind_path,
848 int scope, const char *expr,
849 const char **attrs, void *args,
851 int *count, struct berval **cookie)
854 char *utf8_expr, *utf8_path, **search_attrs = NULL;
855 size_t converted_size;
856 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
857 BerElement *cookie_be = NULL;
858 struct berval *cookie_bv= NULL;
859 BerElement *ext_be = NULL;
860 struct berval *ext_bv= NULL;
863 ads_control *external_control = (ads_control *) args;
867 if (!(ctx = talloc_init("ads_do_paged_search_args")))
868 return ADS_ERROR(LDAP_NO_MEMORY);
870 /* 0 means the conversion worked but the result was empty
871 so we only fail if it's -1. In any case, it always
872 at least nulls out the dest */
873 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
874 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
880 if (!attrs || !(*attrs))
883 /* This would be the utf8-encoded version...*/
884 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
885 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
891 /* Paged results only available on ldap v3 or later */
892 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
893 if (version < LDAP_VERSION3) {
894 rc = LDAP_NOT_SUPPORTED;
898 cookie_be = ber_alloc_t(LBER_USE_DER);
900 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
901 ber_bvfree(*cookie); /* don't need it from last time */
904 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
906 ber_flatten(cookie_be, &cookie_bv);
907 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
908 PagedResults.ldctl_iscritical = (char) 1;
909 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
910 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
912 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
913 NoReferrals.ldctl_iscritical = (char) 0;
914 NoReferrals.ldctl_value.bv_len = 0;
915 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
917 if (external_control &&
918 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
919 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
921 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
922 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
924 /* win2k does not accept a ldctl_value beeing passed in */
926 if (external_control->val != 0) {
928 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
933 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
937 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
942 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
943 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
946 ExternalCtrl.ldctl_value.bv_len = 0;
947 ExternalCtrl.ldctl_value.bv_val = NULL;
950 controls[0] = &NoReferrals;
951 controls[1] = &PagedResults;
952 controls[2] = &ExternalCtrl;
956 controls[0] = &NoReferrals;
957 controls[1] = &PagedResults;
961 /* we need to disable referrals as the openldap libs don't
962 handle them and paged results at the same time. Using them
963 together results in the result record containing the server
964 page control being removed from the result list (tridge/jmcd)
966 leaving this in despite the control that says don't generate
967 referrals, in case the server doesn't support it (jmcd)
969 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
971 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
972 search_attrs, 0, controls,
974 (LDAPMessage **)res);
976 ber_free(cookie_be, 1);
977 ber_bvfree(cookie_bv);
980 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
981 ldap_err2string(rc)));
982 if (rc == LDAP_OTHER) {
986 ret = ldap_parse_result(ads->ldap.ld,
994 if (ret == LDAP_SUCCESS) {
995 DEBUG(3, ("ldap_search_with_timeout(%s) "
996 "error: %s\n", expr, ldap_errmsg));
997 ldap_memfree(ldap_errmsg);
1003 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1004 NULL, &rcontrols, 0);
1010 for (i=0; rcontrols[i]; i++) {
1011 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1012 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1013 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1015 /* the berval is the cookie, but must be freed when
1017 if (cookie_bv->bv_len) /* still more to do */
1018 *cookie=ber_bvdup(cookie_bv);
1021 ber_bvfree(cookie_bv);
1022 ber_free(cookie_be, 1);
1026 ldap_controls_free(rcontrols);
1029 talloc_destroy(ctx);
1032 ber_free(ext_be, 1);
1039 /* if/when we decide to utf8-encode attrs, take out this next line */
1040 TALLOC_FREE(search_attrs);
1042 return ADS_ERROR(rc);
1045 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1046 int scope, const char *expr,
1047 const char **attrs, LDAPMessage **res,
1048 int *count, struct berval **cookie)
1050 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1055 * Get all results for a search. This uses ads_do_paged_search() to return
1056 * all entries in a large search.
1057 * @param ads connection to ads server
1058 * @param bind_path Base dn for the search
1059 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1060 * @param expr Search expression
1061 * @param attrs Attributes to retrieve
1062 * @param res ** which will contain results - free res* with ads_msgfree()
1063 * @return status of search
1065 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1066 int scope, const char *expr,
1067 const char **attrs, void *args,
1070 struct berval *cookie = NULL;
1075 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1078 if (!ADS_ERR_OK(status))
1081 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1083 LDAPMessage *res2 = NULL;
1084 LDAPMessage *msg, *next;
1086 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1087 attrs, args, &res2, &count, &cookie);
1088 if (!ADS_ERR_OK(status)) {
1089 /* Ensure we free all collected results */
1090 ads_msgfree(ads, *res);
1095 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1096 that this works on all ldap libs, but I have only tested with openldap */
1097 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1098 next = ads_next_message(ads, msg);
1099 ldap_add_result_entry((LDAPMessage **)res, msg);
1101 /* note that we do not free res2, as the memory is now
1102 part of the main returned list */
1105 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1106 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1112 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1113 int scope, const char *expr,
1114 const char **attrs, LDAPMessage **res)
1116 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1119 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1120 int scope, const char *expr,
1121 const char **attrs, uint32_t sd_flags,
1126 args.control = ADS_SD_FLAGS_OID;
1127 args.val = sd_flags;
1128 args.critical = True;
1130 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1135 * Run a function on all results for a search. Uses ads_do_paged_search() and
1136 * runs the function as each page is returned, using ads_process_results()
1137 * @param ads connection to ads server
1138 * @param bind_path Base dn for the search
1139 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1140 * @param expr Search expression - specified in local charset
1141 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1142 * @param fn Function which takes attr name, values list, and data_area
1143 * @param data_area Pointer which is passed to function on each call
1144 * @return status of search
1146 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1147 int scope, const char *expr, const char **attrs,
1148 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1151 struct berval *cookie = NULL;
1156 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1159 if (!ADS_ERR_OK(status)) return status;
1161 ads_process_results(ads, res, fn, data_area);
1162 ads_msgfree(ads, res);
1165 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1166 &res, &count, &cookie);
1168 if (!ADS_ERR_OK(status)) break;
1170 ads_process_results(ads, res, fn, data_area);
1171 ads_msgfree(ads, res);
1178 * Do a search with a timeout.
1179 * @param ads connection to ads server
1180 * @param bind_path Base dn for the search
1181 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1182 * @param expr Search expression
1183 * @param attrs Attributes to retrieve
1184 * @param res ** which will contain results - free res* with ads_msgfree()
1185 * @return status of search
1187 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1189 const char **attrs, LDAPMessage **res)
1192 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1193 size_t converted_size;
1197 if (!(ctx = talloc_init("ads_do_search"))) {
1198 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1199 return ADS_ERROR(LDAP_NO_MEMORY);
1202 /* 0 means the conversion worked but the result was empty
1203 so we only fail if it's negative. In any case, it always
1204 at least nulls out the dest */
1205 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1206 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1208 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1209 rc = LDAP_NO_MEMORY;
1213 if (!attrs || !(*attrs))
1214 search_attrs = NULL;
1216 /* This would be the utf8-encoded version...*/
1217 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1218 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1220 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1221 rc = LDAP_NO_MEMORY;
1226 /* see the note in ads_do_paged_search - we *must* disable referrals */
1227 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1229 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1230 search_attrs, 0, NULL, NULL,
1232 (LDAPMessage **)res);
1234 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1235 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1240 talloc_destroy(ctx);
1241 /* if/when we decide to utf8-encode attrs, take out this next line */
1242 TALLOC_FREE(search_attrs);
1243 return ADS_ERROR(rc);
1246 * Do a general ADS search
1247 * @param ads connection to ads server
1248 * @param res ** which will contain results - free res* with ads_msgfree()
1249 * @param expr Search expression
1250 * @param attrs Attributes to retrieve
1251 * @return status of search
1253 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1254 const char *expr, const char **attrs)
1256 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1261 * Do a search on a specific DistinguishedName
1262 * @param ads connection to ads server
1263 * @param res ** which will contain results - free res* with ads_msgfree()
1264 * @param dn DistinguishName to search
1265 * @param attrs Attributes to retrieve
1266 * @return status of search
1268 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1269 const char *dn, const char **attrs)
1271 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1276 * Free up memory from a ads_search
1277 * @param ads connection to ads server
1278 * @param msg Search results to free
1280 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1287 * Get a dn from search results
1288 * @param ads connection to ads server
1289 * @param msg Search result
1292 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1294 char *utf8_dn, *unix_dn;
1295 size_t converted_size;
1297 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1300 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1304 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1305 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1309 ldap_memfree(utf8_dn);
1314 * Get the parent from a dn
1315 * @param dn the dn to return the parent from
1316 * @return parent dn string
1318 char *ads_parent_dn(const char *dn)
1326 p = strchr(dn, ',');
1336 * Find a machine account given a hostname
1337 * @param ads connection to ads server
1338 * @param res ** which will contain results - free res* with ads_msgfree()
1339 * @param host Hostname to search for
1340 * @return status of search
1342 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1343 const char *machine)
1347 const char *attrs[] = {"*", "msDS-SupportedEncryptionTypes", "nTSecurityDescriptor", NULL};
1351 /* the easiest way to find a machine account anywhere in the tree
1352 is to look for hostname$ */
1353 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1354 DEBUG(1, ("asprintf failed!\n"));
1355 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1358 status = ads_search(ads, res, expr, attrs);
1364 * Initialize a list of mods to be used in a modify request
1365 * @param ctx An initialized TALLOC_CTX
1366 * @return allocated ADS_MODLIST
1368 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1370 #define ADS_MODLIST_ALLOC_SIZE 10
1373 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1374 /* -1 is safety to make sure we don't go over the end.
1375 need to reset it to NULL before doing ldap modify */
1376 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1378 return (ADS_MODLIST)mods;
1383 add an attribute to the list, with values list already constructed
1385 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1386 int mod_op, const char *name,
1387 const void *_invals)
1390 LDAPMod **modlist = (LDAPMod **) *mods;
1391 struct berval **ber_values = NULL;
1392 char **char_values = NULL;
1395 mod_op = LDAP_MOD_DELETE;
1397 if (mod_op & LDAP_MOD_BVALUES) {
1398 const struct berval **b;
1399 b = discard_const_p(const struct berval *, _invals);
1400 ber_values = ads_dup_values(ctx, b);
1403 c = discard_const_p(const char *, _invals);
1404 char_values = ads_push_strvals(ctx, c);
1408 /* find the first empty slot */
1409 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1411 if (modlist[curmod] == (LDAPMod *) -1) {
1412 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1413 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1414 return ADS_ERROR(LDAP_NO_MEMORY);
1415 memset(&modlist[curmod], 0,
1416 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1417 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1418 *mods = (ADS_MODLIST)modlist;
1421 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1422 return ADS_ERROR(LDAP_NO_MEMORY);
1423 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1424 if (mod_op & LDAP_MOD_BVALUES) {
1425 modlist[curmod]->mod_bvalues = ber_values;
1426 } else if (mod_op & LDAP_MOD_DELETE) {
1427 modlist[curmod]->mod_values = NULL;
1429 modlist[curmod]->mod_values = char_values;
1432 modlist[curmod]->mod_op = mod_op;
1433 return ADS_ERROR(LDAP_SUCCESS);
1437 * Add a single string value to a mod list
1438 * @param ctx An initialized TALLOC_CTX
1439 * @param mods An initialized ADS_MODLIST
1440 * @param name The attribute name to add
1441 * @param val The value to add - NULL means DELETE
1442 * @return ADS STATUS indicating success of add
1444 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1445 const char *name, const char *val)
1447 const char *values[2];
1453 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1454 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1458 * Add an array of string values to a mod list
1459 * @param ctx An initialized TALLOC_CTX
1460 * @param mods An initialized ADS_MODLIST
1461 * @param name The attribute name to add
1462 * @param vals The array of string values to add - NULL means DELETE
1463 * @return ADS STATUS indicating success of add
1465 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1466 const char *name, const char **vals)
1469 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1470 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1471 name, (const void **) vals);
1476 * Add a single ber-encoded value to a mod list
1477 * @param ctx An initialized TALLOC_CTX
1478 * @param mods An initialized ADS_MODLIST
1479 * @param name The attribute name to add
1480 * @param val The value to add - NULL means DELETE
1481 * @return ADS STATUS indicating success of add
1483 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1484 const char *name, const struct berval *val)
1486 const struct berval *values[2];
1491 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1492 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1493 name, (const void **) values);
1497 static void ads_print_error(int ret, LDAP *ld)
1500 char *ld_error = NULL;
1501 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1502 DEBUG(10,("AD LDAP failure %d (%s):\n%s\n", ret,
1503 ldap_err2string(ret), ld_error));
1504 SAFE_FREE(ld_error);
1509 * Perform an ldap modify
1510 * @param ads connection to ads server
1511 * @param mod_dn DistinguishedName to modify
1512 * @param mods list of modifications to perform
1513 * @return status of modify
1515 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1518 char *utf8_dn = NULL;
1519 size_t converted_size;
1521 this control is needed to modify that contains a currently
1522 non-existent attribute (but allowable for the object) to run
1524 LDAPControl PermitModify = {
1525 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1528 LDAPControl *controls[2];
1530 controls[0] = &PermitModify;
1533 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1534 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1537 /* find the end of the list, marked by NULL or -1 */
1538 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1539 /* make sure the end of the list is NULL */
1541 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1542 (LDAPMod **) mods, controls, NULL);
1543 ads_print_error(ret, ads->ldap.ld);
1544 TALLOC_FREE(utf8_dn);
1545 return ADS_ERROR(ret);
1549 * Perform an ldap add
1550 * @param ads connection to ads server
1551 * @param new_dn DistinguishedName to add
1552 * @param mods list of attributes and values for DN
1553 * @return status of add
1555 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1558 char *utf8_dn = NULL;
1559 size_t converted_size;
1561 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1562 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1563 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1566 /* find the end of the list, marked by NULL or -1 */
1567 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1568 /* make sure the end of the list is NULL */
1571 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1572 ads_print_error(ret, ads->ldap.ld);
1573 TALLOC_FREE(utf8_dn);
1574 return ADS_ERROR(ret);
1578 * Delete a DistinguishedName
1579 * @param ads connection to ads server
1580 * @param new_dn DistinguishedName to delete
1581 * @return status of delete
1583 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1586 char *utf8_dn = NULL;
1587 size_t converted_size;
1588 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1589 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1590 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1593 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1594 ads_print_error(ret, ads->ldap.ld);
1595 TALLOC_FREE(utf8_dn);
1596 return ADS_ERROR(ret);
1600 * Build an org unit string
1601 * if org unit is Computers or blank then assume a container, otherwise
1602 * assume a / separated list of organisational units.
1603 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1604 * @param ads connection to ads server
1605 * @param org_unit Organizational unit
1606 * @return org unit string - caller must free
1608 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1612 if (!org_unit || !*org_unit) {
1614 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1616 /* samba4 might not yet respond to a wellknownobject-query */
1617 return ret ? ret : SMB_STRDUP("cn=Computers");
1620 if (strequal(org_unit, "Computers")) {
1621 return SMB_STRDUP("cn=Computers");
1624 /* jmcd: removed "\\" from the separation chars, because it is
1625 needed as an escape for chars like '#' which are valid in an
1627 return ads_build_path(org_unit, "/", "ou=", 1);
1631 * Get a org unit string for a well-known GUID
1632 * @param ads connection to ads server
1633 * @param wknguid Well known GUID
1634 * @return org unit string - caller must free
1636 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1639 LDAPMessage *res = NULL;
1640 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1641 **bind_dn_exp = NULL;
1642 const char *attrs[] = {"distinguishedName", NULL};
1643 int new_ln, wkn_ln, bind_ln, i;
1645 if (wknguid == NULL) {
1649 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1650 DEBUG(1, ("asprintf failed!\n"));
1654 status = ads_search_dn(ads, &res, base, attrs);
1655 if (!ADS_ERR_OK(status)) {
1656 DEBUG(1,("Failed while searching for: %s\n", base));
1660 if (ads_count_replies(ads, res) != 1) {
1664 /* substitute the bind-path from the well-known-guid-search result */
1665 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1670 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1675 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1680 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1682 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1685 new_ln = wkn_ln - bind_ln;
1687 ret = SMB_STRDUP(wkn_dn_exp[0]);
1692 for (i=1; i < new_ln; i++) {
1695 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1701 ret = SMB_STRDUP(s);
1710 ads_msgfree(ads, res);
1711 TALLOC_FREE(wkn_dn);
1713 ldap_value_free(wkn_dn_exp);
1716 ldap_value_free(bind_dn_exp);
1723 * Adds (appends) an item to an attribute array, rather then
1724 * replacing the whole list
1725 * @param ctx An initialized TALLOC_CTX
1726 * @param mods An initialized ADS_MODLIST
1727 * @param name name of the ldap attribute to append to
1728 * @param vals an array of values to add
1729 * @return status of addition
1732 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1733 const char *name, const char **vals)
1735 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1736 (const void *) vals);
1740 * Determines the an account's current KVNO via an LDAP lookup
1741 * @param ads An initialized ADS_STRUCT
1742 * @param account_name the NT samaccountname.
1743 * @return the kvno for the account, or -1 in case of a failure.
1746 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1748 LDAPMessage *res = NULL;
1749 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1751 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1752 char *dn_string = NULL;
1753 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1755 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1756 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1759 ret = ads_search(ads, &res, filter, attrs);
1761 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1762 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1763 ads_msgfree(ads, res);
1767 dn_string = ads_get_dn(ads, talloc_tos(), res);
1769 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1770 ads_msgfree(ads, res);
1773 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1774 TALLOC_FREE(dn_string);
1776 /* ---------------------------------------------------------
1777 * 0 is returned as a default KVNO from this point on...
1778 * This is done because Windows 2000 does not support key
1779 * version numbers. Chances are that a failure in the next
1780 * step is simply due to Windows 2000 being used for a
1781 * domain controller. */
1784 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1785 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1786 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1787 ads_msgfree(ads, res);
1792 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1793 ads_msgfree(ads, res);
1798 * Determines the computer account's current KVNO via an LDAP lookup
1799 * @param ads An initialized ADS_STRUCT
1800 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1801 * @return the kvno for the computer account, or -1 in case of a failure.
1804 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1806 char *computer_account = NULL;
1809 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1813 kvno = ads_get_kvno(ads, computer_account);
1814 free(computer_account);
1820 * This clears out all registered spn's for a given hostname
1821 * @param ads An initilaized ADS_STRUCT
1822 * @param machine_name the NetBIOS name of the computer.
1823 * @return 0 upon success, non-zero otherwise.
1826 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1829 LDAPMessage *res = NULL;
1831 const char *servicePrincipalName[1] = {NULL};
1832 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1833 char *dn_string = NULL;
1835 ret = ads_find_machine_acct(ads, &res, machine_name);
1836 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1837 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1838 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1839 ads_msgfree(ads, res);
1840 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1843 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1844 ctx = talloc_init("ads_clear_service_principal_names");
1846 ads_msgfree(ads, res);
1847 return ADS_ERROR(LDAP_NO_MEMORY);
1850 if (!(mods = ads_init_mods(ctx))) {
1851 talloc_destroy(ctx);
1852 ads_msgfree(ads, res);
1853 return ADS_ERROR(LDAP_NO_MEMORY);
1855 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1856 if (!ADS_ERR_OK(ret)) {
1857 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1858 ads_msgfree(ads, res);
1859 talloc_destroy(ctx);
1862 dn_string = ads_get_dn(ads, talloc_tos(), res);
1864 talloc_destroy(ctx);
1865 ads_msgfree(ads, res);
1866 return ADS_ERROR(LDAP_NO_MEMORY);
1868 ret = ads_gen_mod(ads, dn_string, mods);
1869 TALLOC_FREE(dn_string);
1870 if (!ADS_ERR_OK(ret)) {
1871 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1873 ads_msgfree(ads, res);
1874 talloc_destroy(ctx);
1878 ads_msgfree(ads, res);
1879 talloc_destroy(ctx);
1884 * @brief Search for an element in a string array.
1886 * @param[in] el_array The string array to search.
1888 * @param[in] num_el The number of elements in the string array.
1890 * @param[in] el The string to search.
1892 * @return True if found, false if not.
1894 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1898 if (el_array == NULL || num_el == 0 || el == NULL) {
1902 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1905 cmp = strcasecmp_m(el_array[i], el);
1915 * @brief This gets the service principal names of an existing computer account.
1917 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1919 * @param[in] ads The ADS context to use.
1921 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1922 * identify the computer account.
1924 * @param[in] spn_array A pointer to store the array for SPNs.
1926 * @param[in] num_spns The number of principals stored in the array.
1928 * @return 0 on success, or a ADS error if a failure occurred.
1930 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1932 const char *machine_name,
1937 LDAPMessage *res = NULL;
1940 status = ads_find_machine_acct(ads,
1943 if (!ADS_ERR_OK(status)) {
1944 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1949 count = ads_count_replies(ads, res);
1951 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1955 *spn_array = ads_pull_strings(ads,
1958 "servicePrincipalName",
1960 if (*spn_array == NULL) {
1961 DEBUG(1, ("Host account for %s does not have service principal "
1964 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1969 ads_msgfree(ads, res);
1975 * This adds a service principal name to an existing computer account
1976 * (found by hostname) in AD.
1977 * @param ads An initialized ADS_STRUCT
1978 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1979 * @param my_fqdn The fully qualified DNS name of the machine
1980 * @param spn A string of the service principal to add, i.e. 'host'
1981 * @return 0 upon sucess, or non-zero if a failure occurs
1984 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1985 const char *my_fqdn, const char *spn)
1989 LDAPMessage *res = NULL;
1992 char *dn_string = NULL;
1993 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1995 ret = ads_find_machine_acct(ads, &res, machine_name);
1996 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1997 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1999 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2000 spn, machine_name, ads->config.realm));
2001 ads_msgfree(ads, res);
2002 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2005 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2006 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2007 ads_msgfree(ads, res);
2008 return ADS_ERROR(LDAP_NO_MEMORY);
2011 /* add short name spn */
2013 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2014 talloc_destroy(ctx);
2015 ads_msgfree(ads, res);
2016 return ADS_ERROR(LDAP_NO_MEMORY);
2018 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2019 ret = ADS_ERROR(LDAP_NO_MEMORY);
2022 servicePrincipalName[0] = psp1;
2024 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2025 psp1, machine_name));
2028 /* add fully qualified spn */
2030 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2031 ret = ADS_ERROR(LDAP_NO_MEMORY);
2034 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2035 ret = ADS_ERROR(LDAP_NO_MEMORY);
2038 servicePrincipalName[1] = psp2;
2040 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2041 psp2, machine_name));
2043 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2044 ret = ADS_ERROR(LDAP_NO_MEMORY);
2048 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2049 if (!ADS_ERR_OK(ret)) {
2050 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2054 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2055 ret = ADS_ERROR(LDAP_NO_MEMORY);
2059 ret = ads_gen_mod(ads, dn_string, mods);
2060 if (!ADS_ERR_OK(ret)) {
2061 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2067 ads_msgfree(ads, res);
2072 * adds a machine account to the ADS server
2073 * @param ads An intialized ADS_STRUCT
2074 * @param machine_name - the NetBIOS machine name of this account.
2075 * @param account_type A number indicating the type of account to create
2076 * @param org_unit The LDAP path in which to place this account
2077 * @return 0 upon success, or non-zero otherwise
2080 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2081 const char *machine_name,
2082 const char *org_unit,
2083 uint32_t etype_list)
2086 char *samAccountName, *controlstr;
2089 char *machine_escaped = NULL;
2091 const char *objectClass[] = {"top", "person", "organizationalPerson",
2092 "user", "computer", NULL};
2093 LDAPMessage *res = NULL;
2094 uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2095 UF_DONT_EXPIRE_PASSWD |\
2096 UF_ACCOUNTDISABLE );
2097 uint32_t func_level = 0;
2099 ret = ads_domain_func_level(ads, &func_level);
2100 if (!ADS_ERR_OK(ret)) {
2104 if (!(ctx = talloc_init("ads_add_machine_acct")))
2105 return ADS_ERROR(LDAP_NO_MEMORY);
2107 ret = ADS_ERROR(LDAP_NO_MEMORY);
2109 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2110 if (!machine_escaped) {
2114 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2115 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2117 if ( !new_dn || !samAccountName ) {
2121 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2125 if (!(mods = ads_init_mods(ctx))) {
2129 ads_mod_str(ctx, &mods, "cn", machine_name);
2130 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2131 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2132 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2134 if (func_level >= DS_DOMAIN_FUNCTION_2008) {
2135 const char *etype_list_str;
2137 etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list);
2138 if (etype_list_str == NULL) {
2141 ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes",
2145 ret = ads_gen_add(ads, new_dn, mods);
2148 SAFE_FREE(machine_escaped);
2149 ads_msgfree(ads, res);
2150 talloc_destroy(ctx);
2156 * move a machine account to another OU on the ADS server
2157 * @param ads - An intialized ADS_STRUCT
2158 * @param machine_name - the NetBIOS machine name of this account.
2159 * @param org_unit - The LDAP path in which to place this account
2160 * @param moved - whether we moved the machine account (optional)
2161 * @return 0 upon success, or non-zero otherwise
2164 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2165 const char *org_unit, bool *moved)
2169 LDAPMessage *res = NULL;
2170 char *filter = NULL;
2171 char *computer_dn = NULL;
2173 char *computer_rdn = NULL;
2174 bool need_move = False;
2176 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2177 rc = ADS_ERROR(LDAP_NO_MEMORY);
2181 /* Find pre-existing machine */
2182 rc = ads_search(ads, &res, filter, NULL);
2183 if (!ADS_ERR_OK(rc)) {
2187 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2189 rc = ADS_ERROR(LDAP_NO_MEMORY);
2193 parent_dn = ads_parent_dn(computer_dn);
2194 if (strequal(parent_dn, org_unit)) {
2200 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2201 rc = ADS_ERROR(LDAP_NO_MEMORY);
2205 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2206 org_unit, 1, NULL, NULL);
2207 rc = ADS_ERROR(ldap_status);
2210 ads_msgfree(ads, res);
2212 TALLOC_FREE(computer_dn);
2213 SAFE_FREE(computer_rdn);
2215 if (!ADS_ERR_OK(rc)) {
2227 dump a binary result from ldap
2229 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2232 for (i=0; values[i]; i++) {
2233 printf("%s: ", field);
2234 for (j=0; j<values[i]->bv_len; j++) {
2235 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2241 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2244 for (i=0; values[i]; i++) {
2246 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2249 status = GUID_from_ndr_blob(&in, &guid);
2250 if (NT_STATUS_IS_OK(status)) {
2251 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2253 printf("%s: INVALID GUID\n", field);
2259 dump a sid result from ldap
2261 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2264 for (i=0; values[i]; i++) {
2267 if (!sid_parse((const uint8_t *)values[i]->bv_val,
2268 values[i]->bv_len, &sid)) {
2271 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2276 dump ntSecurityDescriptor
2278 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2280 TALLOC_CTX *frame = talloc_stackframe();
2281 struct security_descriptor *psd;
2284 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2285 values[0]->bv_len, &psd);
2286 if (!NT_STATUS_IS_OK(status)) {
2287 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2288 nt_errstr(status)));
2294 ads_disp_sd(ads, talloc_tos(), psd);
2301 dump a string result from ldap
2303 static void dump_string(const char *field, char **values)
2306 for (i=0; values[i]; i++) {
2307 printf("%s: %s\n", field, values[i]);
2312 dump a field from LDAP on stdout
2316 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2321 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2323 {"objectGUID", False, dump_guid},
2324 {"netbootGUID", False, dump_guid},
2325 {"nTSecurityDescriptor", False, dump_sd},
2326 {"dnsRecord", False, dump_binary},
2327 {"objectSid", False, dump_sid},
2328 {"tokenGroups", False, dump_sid},
2329 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2330 {"tokengroupsGlobalandUniversal", False, dump_sid},
2331 {"mS-DS-CreatorSID", False, dump_sid},
2332 {"msExchMailboxGuid", False, dump_guid},
2337 if (!field) { /* must be end of an entry */
2342 for (i=0; handlers[i].name; i++) {
2343 if (strcasecmp_m(handlers[i].name, field) == 0) {
2344 if (!values) /* first time, indicate string or not */
2345 return handlers[i].string;
2346 handlers[i].handler(ads, field, (struct berval **) values);
2350 if (!handlers[i].name) {
2351 if (!values) /* first time, indicate string conversion */
2353 dump_string(field, (char **)values);
2359 * Dump a result from LDAP on stdout
2360 * used for debugging
2361 * @param ads connection to ads server
2362 * @param res Results to dump
2365 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2367 ads_process_results(ads, res, ads_dump_field, NULL);
2371 * Walk through results, calling a function for each entry found.
2372 * The function receives a field name, a berval * array of values,
2373 * and a data area passed through from the start. The function is
2374 * called once with null for field and values at the end of each
2376 * @param ads connection to ads server
2377 * @param res Results to process
2378 * @param fn Function for processing each result
2379 * @param data_area user-defined area to pass to function
2381 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2382 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2387 size_t converted_size;
2389 if (!(ctx = talloc_init("ads_process_results")))
2392 for (msg = ads_first_entry(ads, res); msg;
2393 msg = ads_next_entry(ads, msg)) {
2397 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2398 (LDAPMessage *)msg,&b);
2400 utf8_field=ldap_next_attribute(ads->ldap.ld,
2401 (LDAPMessage *)msg,b)) {
2402 struct berval **ber_vals;
2408 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2411 DEBUG(0,("ads_process_results: "
2412 "pull_utf8_talloc failed: %s",
2416 string = fn(ads, field, NULL, data_area);
2421 utf8_vals = ldap_get_values(ads->ldap.ld,
2422 (LDAPMessage *)msg, field);
2423 p = discard_const_p(const char *, utf8_vals);
2424 str_vals = ads_pull_strvals(ctx, p);
2425 fn(ads, field, (void **) str_vals, data_area);
2426 ldap_value_free(utf8_vals);
2428 ber_vals = ldap_get_values_len(ads->ldap.ld,
2429 (LDAPMessage *)msg, field);
2430 fn(ads, field, (void **) ber_vals, data_area);
2432 ldap_value_free_len(ber_vals);
2434 ldap_memfree(utf8_field);
2437 talloc_free_children(ctx);
2438 fn(ads, NULL, NULL, data_area); /* completed an entry */
2441 talloc_destroy(ctx);
2445 * count how many replies are in a LDAPMessage
2446 * @param ads connection to ads server
2447 * @param res Results to count
2448 * @return number of replies
2450 int ads_count_replies(ADS_STRUCT *ads, void *res)
2452 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2456 * pull the first entry from a ADS result
2457 * @param ads connection to ads server
2458 * @param res Results of search
2459 * @return first entry from result
2461 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2463 return ldap_first_entry(ads->ldap.ld, res);
2467 * pull the next entry from a ADS result
2468 * @param ads connection to ads server
2469 * @param res Results of search
2470 * @return next entry from result
2472 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2474 return ldap_next_entry(ads->ldap.ld, res);
2478 * pull the first message from a ADS result
2479 * @param ads connection to ads server
2480 * @param res Results of search
2481 * @return first message from result
2483 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2485 return ldap_first_message(ads->ldap.ld, res);
2489 * pull the next message from a ADS result
2490 * @param ads connection to ads server
2491 * @param res Results of search
2492 * @return next message from result
2494 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2496 return ldap_next_message(ads->ldap.ld, res);
2500 * pull a single string from a ADS result
2501 * @param ads connection to ads server
2502 * @param mem_ctx TALLOC_CTX to use for allocating result string
2503 * @param msg Results of search
2504 * @param field Attribute to retrieve
2505 * @return Result string in talloc context
2507 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2513 size_t converted_size;
2515 values = ldap_get_values(ads->ldap.ld, msg, field);
2519 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2524 ldap_value_free(values);
2529 * pull an array of strings from a ADS result
2530 * @param ads connection to ads server
2531 * @param mem_ctx TALLOC_CTX to use for allocating result string
2532 * @param msg Results of search
2533 * @param field Attribute to retrieve
2534 * @return Result strings in talloc context
2536 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2537 LDAPMessage *msg, const char *field,
2543 size_t converted_size;
2545 values = ldap_get_values(ads->ldap.ld, msg, field);
2549 *num_values = ldap_count_values(values);
2551 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2553 ldap_value_free(values);
2557 for (i=0;i<*num_values;i++) {
2558 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2561 ldap_value_free(values);
2567 ldap_value_free(values);
2572 * pull an array of strings from a ADS result
2573 * (handle large multivalue attributes with range retrieval)
2574 * @param ads connection to ads server
2575 * @param mem_ctx TALLOC_CTX to use for allocating result string
2576 * @param msg Results of search
2577 * @param field Attribute to retrieve
2578 * @param current_strings strings returned by a previous call to this function
2579 * @param next_attribute The next query should ask for this attribute
2580 * @param num_values How many values did we get this time?
2581 * @param more_values Are there more values to get?
2582 * @return Result strings in talloc context
2584 char **ads_pull_strings_range(ADS_STRUCT *ads,
2585 TALLOC_CTX *mem_ctx,
2586 LDAPMessage *msg, const char *field,
2587 char **current_strings,
2588 const char **next_attribute,
2589 size_t *num_strings,
2593 char *expected_range_attrib, *range_attr;
2594 BerElement *ptr = NULL;
2597 size_t num_new_strings;
2598 unsigned long int range_start;
2599 unsigned long int range_end;
2601 /* we might have been given the whole lot anyway */
2602 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2603 *more_strings = False;
2607 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2609 /* look for Range result */
2610 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2612 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2613 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2614 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2622 /* nothing here - this field is just empty */
2623 *more_strings = False;
2627 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2628 &range_start, &range_end) == 2) {
2629 *more_strings = True;
2631 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2632 &range_start) == 1) {
2633 *more_strings = False;
2635 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2637 ldap_memfree(range_attr);
2638 *more_strings = False;
2643 if ((*num_strings) != range_start) {
2644 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2645 " - aborting range retreival\n",
2646 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2647 ldap_memfree(range_attr);
2648 *more_strings = False;
2652 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2654 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2655 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2656 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2657 range_attr, (unsigned long int)range_end - range_start + 1,
2658 (unsigned long int)num_new_strings));
2659 ldap_memfree(range_attr);
2660 *more_strings = False;
2664 strings = talloc_realloc(mem_ctx, current_strings, char *,
2665 *num_strings + num_new_strings);
2667 if (strings == NULL) {
2668 ldap_memfree(range_attr);
2669 *more_strings = False;
2673 if (new_strings && num_new_strings) {
2674 memcpy(&strings[*num_strings], new_strings,
2675 sizeof(*new_strings) * num_new_strings);
2678 (*num_strings) += num_new_strings;
2680 if (*more_strings) {
2681 *next_attribute = talloc_asprintf(mem_ctx,
2686 if (!*next_attribute) {
2687 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2688 ldap_memfree(range_attr);
2689 *more_strings = False;
2694 ldap_memfree(range_attr);
2700 * pull a single uint32_t from a ADS result
2701 * @param ads connection to ads server
2702 * @param msg Results of search
2703 * @param field Attribute to retrieve
2704 * @param v Pointer to int to store result
2705 * @return boolean inidicating success
2707 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2712 values = ldap_get_values(ads->ldap.ld, msg, field);
2716 ldap_value_free(values);
2720 *v = atoi(values[0]);
2721 ldap_value_free(values);
2726 * pull a single objectGUID from an ADS result
2727 * @param ads connection to ADS server
2728 * @param msg results of search
2729 * @param guid 37-byte area to receive text guid
2730 * @return boolean indicating success
2732 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2737 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2742 status = GUID_from_ndr_blob(&blob, guid);
2743 talloc_free(blob.data);
2744 return NT_STATUS_IS_OK(status);
2749 * pull a single struct dom_sid from a ADS result
2750 * @param ads connection to ads server
2751 * @param msg Results of search
2752 * @param field Attribute to retrieve
2753 * @param sid Pointer to sid to store result
2754 * @return boolean inidicating success
2756 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2757 struct dom_sid *sid)
2759 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2763 * pull an array of struct dom_sids from a ADS result
2764 * @param ads connection to ads server
2765 * @param mem_ctx TALLOC_CTX for allocating sid array
2766 * @param msg Results of search
2767 * @param field Attribute to retrieve
2768 * @param sids pointer to sid array to allocate
2769 * @return the count of SIDs pulled
2771 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2772 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2774 struct berval **values;
2778 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2783 for (i=0; values[i]; i++)
2787 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2789 ldap_value_free_len(values);
2797 for (i=0; values[i]; i++) {
2798 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2799 values[i]->bv_len, &(*sids)[count]);
2801 DEBUG(10, ("pulling SID: %s\n",
2802 sid_string_dbg(&(*sids)[count])));
2807 ldap_value_free_len(values);
2812 * pull a struct security_descriptor from a ADS result
2813 * @param ads connection to ads server
2814 * @param mem_ctx TALLOC_CTX for allocating sid array
2815 * @param msg Results of search
2816 * @param field Attribute to retrieve
2817 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2818 * @return boolean inidicating success
2820 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2821 LDAPMessage *msg, const char *field,
2822 struct security_descriptor **sd)
2824 struct berval **values;
2827 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2829 if (!values) return false;
2833 status = unmarshall_sec_desc(mem_ctx,
2834 (uint8_t *)values[0]->bv_val,
2835 values[0]->bv_len, sd);
2836 if (!NT_STATUS_IS_OK(status)) {
2837 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2838 nt_errstr(status)));
2843 ldap_value_free_len(values);
2848 * in order to support usernames longer than 21 characters we need to
2849 * use both the sAMAccountName and the userPrincipalName attributes
2850 * It seems that not all users have the userPrincipalName attribute set
2852 * @param ads connection to ads server
2853 * @param mem_ctx TALLOC_CTX for allocating sid array
2854 * @param msg Results of search
2855 * @return the username
2857 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2863 /* lookup_name() only works on the sAMAccountName to
2864 returning the username portion of userPrincipalName
2865 breaks winbindd_getpwnam() */
2867 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2868 if (ret && (p = strchr_m(ret, '@'))) {
2873 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2878 * find the update serial number - this is the core of the ldap cache
2879 * @param ads connection to ads server
2880 * @param ads connection to ADS server
2881 * @param usn Pointer to retrieved update serial number
2882 * @return status of search
2884 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2886 const char *attrs[] = {"highestCommittedUSN", NULL};
2890 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2891 if (!ADS_ERR_OK(status))
2894 if (ads_count_replies(ads, res) != 1) {
2895 ads_msgfree(ads, res);
2896 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2899 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2900 ads_msgfree(ads, res);
2901 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2904 ads_msgfree(ads, res);
2908 /* parse a ADS timestring - typical string is
2909 '20020917091222.0Z0' which means 09:12.22 17th September
2911 static time_t ads_parse_time(const char *str)
2917 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2918 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2919 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2928 /********************************************************************
2929 ********************************************************************/
2931 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2933 const char *attrs[] = {"currentTime", NULL};
2938 ADS_STRUCT *ads_s = ads;
2940 if (!(ctx = talloc_init("ads_current_time"))) {
2941 return ADS_ERROR(LDAP_NO_MEMORY);
2944 /* establish a new ldap tcp session if necessary */
2946 if ( !ads->ldap.ld ) {
2947 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2948 ads->server.ldap_server )) == NULL )
2950 status = ADS_ERROR(LDAP_NO_MEMORY);
2953 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2954 status = ads_connect( ads_s );
2955 if ( !ADS_ERR_OK(status))
2959 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2960 if (!ADS_ERR_OK(status)) {
2964 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2966 ads_msgfree(ads_s, res);
2967 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2971 /* but save the time and offset in the original ADS_STRUCT */
2973 ads->config.current_time = ads_parse_time(timestr);
2975 if (ads->config.current_time != 0) {
2976 ads->auth.time_offset = ads->config.current_time - time(NULL);
2977 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2980 ads_msgfree(ads, res);
2982 status = ADS_SUCCESS;
2985 /* free any temporary ads connections */
2986 if ( ads_s != ads ) {
2987 ads_destroy( &ads_s );
2989 talloc_destroy(ctx);
2994 /********************************************************************
2995 ********************************************************************/
2997 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
2999 const char *attrs[] = {"domainFunctionality", NULL};
3002 ADS_STRUCT *ads_s = ads;
3004 *val = DS_DOMAIN_FUNCTION_2000;
3006 /* establish a new ldap tcp session if necessary */
3008 if ( !ads->ldap.ld ) {
3009 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3010 ads->server.ldap_server )) == NULL )
3012 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3015 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3016 status = ads_connect( ads_s );
3017 if ( !ADS_ERR_OK(status))
3021 /* If the attribute does not exist assume it is a Windows 2000
3022 functional domain */
3024 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3025 if (!ADS_ERR_OK(status)) {
3026 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3027 status = ADS_SUCCESS;
3032 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3033 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3035 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3038 ads_msgfree(ads, res);
3041 /* free any temporary ads connections */
3042 if ( ads_s != ads ) {
3043 ads_destroy( &ads_s );
3050 * find the domain sid for our domain
3051 * @param ads connection to ads server
3052 * @param sid Pointer to domain sid
3053 * @return status of search
3055 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3057 const char *attrs[] = {"objectSid", NULL};
3061 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3063 if (!ADS_ERR_OK(rc)) return rc;
3064 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3065 ads_msgfree(ads, res);
3066 return ADS_ERROR_SYSTEM(ENOENT);
3068 ads_msgfree(ads, res);
3074 * find our site name
3075 * @param ads connection to ads server
3076 * @param mem_ctx Pointer to talloc context
3077 * @param site_name Pointer to the sitename
3078 * @return status of search
3080 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3084 const char *dn, *service_name;
3085 const char *attrs[] = { "dsServiceName", NULL };
3087 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3088 if (!ADS_ERR_OK(status)) {
3092 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3093 if (service_name == NULL) {
3094 ads_msgfree(ads, res);
3095 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3098 ads_msgfree(ads, res);
3100 /* go up three levels */
3101 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3103 return ADS_ERROR(LDAP_NO_MEMORY);
3106 *site_name = talloc_strdup(mem_ctx, dn);
3107 if (*site_name == NULL) {
3108 return ADS_ERROR(LDAP_NO_MEMORY);
3113 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3118 * find the site dn where a machine resides
3119 * @param ads connection to ads server
3120 * @param mem_ctx Pointer to talloc context
3121 * @param computer_name name of the machine
3122 * @param site_name Pointer to the sitename
3123 * @return status of search
3125 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3129 const char *parent, *filter;
3130 char *config_context = NULL;
3133 /* shortcut a query */
3134 if (strequal(computer_name, ads->config.ldap_server_name)) {
3135 return ads_site_dn(ads, mem_ctx, site_dn);
3138 status = ads_config_path(ads, mem_ctx, &config_context);
3139 if (!ADS_ERR_OK(status)) {
3143 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3144 if (filter == NULL) {
3145 return ADS_ERROR(LDAP_NO_MEMORY);
3148 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3149 filter, NULL, &res);
3150 if (!ADS_ERR_OK(status)) {
3154 if (ads_count_replies(ads, res) != 1) {
3155 ads_msgfree(ads, res);
3156 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3159 dn = ads_get_dn(ads, mem_ctx, res);
3161 ads_msgfree(ads, res);
3162 return ADS_ERROR(LDAP_NO_MEMORY);
3165 /* go up three levels */
3166 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3167 if (parent == NULL) {
3168 ads_msgfree(ads, res);
3170 return ADS_ERROR(LDAP_NO_MEMORY);
3173 *site_dn = talloc_strdup(mem_ctx, parent);
3174 if (*site_dn == NULL) {
3175 ads_msgfree(ads, res);
3177 return ADS_ERROR(LDAP_NO_MEMORY);
3181 ads_msgfree(ads, res);
3187 * get the upn suffixes for a domain
3188 * @param ads connection to ads server
3189 * @param mem_ctx Pointer to talloc context
3190 * @param suffixes Pointer to an array of suffixes
3191 * @param num_suffixes Pointer to the number of suffixes
3192 * @return status of search
3194 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3199 char *config_context = NULL;
3200 const char *attrs[] = { "uPNSuffixes", NULL };
3202 status = ads_config_path(ads, mem_ctx, &config_context);
3203 if (!ADS_ERR_OK(status)) {
3207 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3209 return ADS_ERROR(LDAP_NO_MEMORY);
3212 status = ads_search_dn(ads, &res, base, attrs);
3213 if (!ADS_ERR_OK(status)) {
3217 if (ads_count_replies(ads, res) != 1) {
3218 ads_msgfree(ads, res);
3219 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3222 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3223 if ((*suffixes) == NULL) {
3224 ads_msgfree(ads, res);
3225 return ADS_ERROR(LDAP_NO_MEMORY);
3228 ads_msgfree(ads, res);
3234 * get the joinable ous for a domain
3235 * @param ads connection to ads server
3236 * @param mem_ctx Pointer to talloc context
3237 * @param ous Pointer to an array of ous
3238 * @param num_ous Pointer to the number of ous
3239 * @return status of search
3241 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3242 TALLOC_CTX *mem_ctx,
3247 LDAPMessage *res = NULL;
3248 LDAPMessage *msg = NULL;
3249 const char *attrs[] = { "dn", NULL };
3252 status = ads_search(ads, &res,
3253 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3255 if (!ADS_ERR_OK(status)) {
3259 count = ads_count_replies(ads, res);
3261 ads_msgfree(ads, res);
3262 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3265 for (msg = ads_first_entry(ads, res); msg;
3266 msg = ads_next_entry(ads, msg)) {
3267 const char **p = discard_const_p(const char *, *ous);
3270 dn = ads_get_dn(ads, talloc_tos(), msg);
3272 ads_msgfree(ads, res);
3273 return ADS_ERROR(LDAP_NO_MEMORY);
3276 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3278 ads_msgfree(ads, res);
3279 return ADS_ERROR(LDAP_NO_MEMORY);
3283 *ous = discard_const_p(char *, p);
3286 ads_msgfree(ads, res);
3293 * pull a struct dom_sid from an extended dn string
3294 * @param mem_ctx TALLOC_CTX
3295 * @param extended_dn string
3296 * @param flags string type of extended_dn
3297 * @param sid pointer to a struct dom_sid
3298 * @return NT_STATUS_OK on success,
3299 * NT_INVALID_PARAMETER on error,
3300 * NT_STATUS_NOT_FOUND if no SID present
3302 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3303 const char *extended_dn,
3304 enum ads_extended_dn_flags flags,
3305 struct dom_sid *sid)
3310 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3313 /* otherwise extended_dn gets stripped off */
3314 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3315 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3318 * ADS_EXTENDED_DN_HEX_STRING:
3319 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3321 * ADS_EXTENDED_DN_STRING (only with w2k3):
3322 * <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
3324 * Object with no SID, such as an Exchange Public Folder
3325 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3328 p = strchr(dn, ';');
3330 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3333 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3334 DEBUG(5,("No SID present in extended dn\n"));
3335 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3338 p += strlen(";<SID=");
3342 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3347 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3351 case ADS_EXTENDED_DN_STRING:
3352 if (!string_to_sid(sid, p)) {
3353 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3356 case ADS_EXTENDED_DN_HEX_STRING: {
3360 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3362 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3365 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3366 DEBUG(10,("failed to parse sid\n"));
3367 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3372 DEBUG(10,("unknown extended dn format\n"));
3373 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3376 return ADS_ERROR_NT(NT_STATUS_OK);
3379 /********************************************************************
3380 ********************************************************************/
3382 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3384 LDAPMessage *res = NULL;
3389 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3390 if (!ADS_ERR_OK(status)) {
3391 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3392 lp_netbios_name()));
3396 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3397 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3401 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3402 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3406 ads_msgfree(ads, res);
3411 /********************************************************************
3412 ********************************************************************/
3414 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3416 LDAPMessage *res = NULL;
3421 status = ads_find_machine_acct(ads, &res, machine_name);
3422 if (!ADS_ERR_OK(status)) {
3423 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3424 lp_netbios_name()));
3428 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3429 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3433 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3434 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3438 ads_msgfree(ads, res);
3443 /********************************************************************
3444 ********************************************************************/
3446 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3448 LDAPMessage *res = NULL;
3453 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3454 if (!ADS_ERR_OK(status)) {
3455 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3456 lp_netbios_name()));
3460 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3461 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3465 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3466 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3470 ads_msgfree(ads, res);
3477 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3480 * Join a machine to a realm
3481 * Creates the machine account and sets the machine password
3482 * @param ads connection to ads server
3483 * @param machine name of host to add
3484 * @param org_unit Organizational unit to place machine in
3485 * @return status of join
3487 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3488 uint32_t account_type, const char *org_unit)
3491 LDAPMessage *res = NULL;
3494 /* machine name must be lowercase */
3495 machine = SMB_STRDUP(machine_name);
3496 strlower_m(machine);
3499 status = ads_find_machine_acct(ads, (void **)&res, machine);
3500 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3501 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3502 status = ads_leave_realm(ads, machine);
3503 if (!ADS_ERR_OK(status)) {
3504 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3505 machine, ads->config.realm));
3510 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3511 if (!ADS_ERR_OK(status)) {
3512 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3517 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3518 if (!ADS_ERR_OK(status)) {
3519 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3525 ads_msgfree(ads, res);
3532 * Delete a machine from the realm
3533 * @param ads connection to ads server
3534 * @param hostname Machine to remove
3535 * @return status of delete
3537 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3542 char *hostnameDN, *host;
3544 LDAPControl ldap_control;
3545 LDAPControl * pldap_control[2] = {NULL, NULL};
3547 pldap_control[0] = &ldap_control;
3548 memset(&ldap_control, 0, sizeof(LDAPControl));
3549 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3551 /* hostname must be lowercase */
3552 host = SMB_STRDUP(hostname);
3553 if (!strlower_m(host)) {
3555 return ADS_ERROR_SYSTEM(EINVAL);
3558 status = ads_find_machine_acct(ads, &res, host);
3559 if (!ADS_ERR_OK(status)) {
3560 DEBUG(0, ("Host account for %s does not exist.\n", host));
3565 msg = ads_first_entry(ads, res);
3568 return ADS_ERROR_SYSTEM(ENOENT);
3571 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3572 if (hostnameDN == NULL) {
3574 return ADS_ERROR_SYSTEM(ENOENT);
3577 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3579 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3581 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3584 if (rc != LDAP_SUCCESS) {
3585 const char *attrs[] = { "cn", NULL };
3586 LDAPMessage *msg_sub;
3588 /* we only search with scope ONE, we do not expect any further
3589 * objects to be created deeper */
3591 status = ads_do_search_retry(ads, hostnameDN,
3592 LDAP_SCOPE_ONELEVEL,
3593 "(objectclass=*)", attrs, &res);
3595 if (!ADS_ERR_OK(status)) {
3597 TALLOC_FREE(hostnameDN);
3601 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3602 msg_sub = ads_next_entry(ads, msg_sub)) {
3606 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3608 TALLOC_FREE(hostnameDN);
3609 return ADS_ERROR(LDAP_NO_MEMORY);
3612 status = ads_del_dn(ads, dn);
3613 if (!ADS_ERR_OK(status)) {
3614 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3617 TALLOC_FREE(hostnameDN);
3624 /* there should be no subordinate objects anymore */
3625 status = ads_do_search_retry(ads, hostnameDN,
3626 LDAP_SCOPE_ONELEVEL,
3627 "(objectclass=*)", attrs, &res);
3629 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3631 TALLOC_FREE(hostnameDN);
3635 /* delete hostnameDN now */
3636 status = ads_del_dn(ads, hostnameDN);
3637 if (!ADS_ERR_OK(status)) {
3639 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3640 TALLOC_FREE(hostnameDN);
3645 TALLOC_FREE(hostnameDN);
3647 status = ads_find_machine_acct(ads, &res, host);
3648 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3649 DEBUG(3, ("Failed to remove host account.\n"));
3659 * pull all token-sids from an LDAP dn
3660 * @param ads connection to ads server
3661 * @param mem_ctx TALLOC_CTX for allocating sid array
3662 * @param dn of LDAP object
3663 * @param user_sid pointer to struct dom_sid (objectSid)
3664 * @param primary_group_sid pointer to struct dom_sid (self composed)
3665 * @param sids pointer to sid array to allocate
3666 * @param num_sids counter of SIDs pulled
3667 * @return status of token query
3669 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3670 TALLOC_CTX *mem_ctx,
3672 struct dom_sid *user_sid,
3673 struct dom_sid *primary_group_sid,
3674 struct dom_sid **sids,
3678 LDAPMessage *res = NULL;
3680 size_t tmp_num_sids;
3681 struct dom_sid *tmp_sids;
3682 struct dom_sid tmp_user_sid;
3683 struct dom_sid tmp_primary_group_sid;
3685 const char *attrs[] = {
3692 status = ads_search_retry_dn(ads, &res, dn, attrs);
3693 if (!ADS_ERR_OK(status)) {
3697 count = ads_count_replies(ads, res);
3699 ads_msgfree(ads, res);
3700 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3703 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3704 ads_msgfree(ads, res);
3705 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3708 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3709 ads_msgfree(ads, res);
3710 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3714 /* hack to compose the primary group sid without knowing the
3717 struct dom_sid domsid;
3719 sid_copy(&domsid, &tmp_user_sid);
3721 if (!sid_split_rid(&domsid, NULL)) {
3722 ads_msgfree(ads, res);
3723 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3726 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3727 ads_msgfree(ads, res);
3728 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3732 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3734 if (tmp_num_sids == 0 || !tmp_sids) {
3735 ads_msgfree(ads, res);
3736 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3740 *num_sids = tmp_num_sids;
3748 *user_sid = tmp_user_sid;
3751 if (primary_group_sid) {
3752 *primary_group_sid = tmp_primary_group_sid;
3755 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3757 ads_msgfree(ads, res);
3758 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3762 * Find a sAMAccoutName in LDAP
3763 * @param ads connection to ads server
3764 * @param mem_ctx TALLOC_CTX for allocating sid array
3765 * @param samaccountname to search
3766 * @param uac_ret uint32_t pointer userAccountControl attribute value
3767 * @param dn_ret pointer to dn
3768 * @return status of token query
3770 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3771 TALLOC_CTX *mem_ctx,
3772 const char *samaccountname,
3774 const char **dn_ret)
3777 const char *attrs[] = { "userAccountControl", NULL };
3779 LDAPMessage *res = NULL;
3783 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3785 if (filter == NULL) {
3786 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3790 status = ads_do_search_all(ads, ads->config.bind_path,
3792 filter, attrs, &res);
3794 if (!ADS_ERR_OK(status)) {
3798 if (ads_count_replies(ads, res) != 1) {
3799 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3803 dn = ads_get_dn(ads, talloc_tos(), res);
3805 status = ADS_ERROR(LDAP_NO_MEMORY);
3809 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3810 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3819 *dn_ret = talloc_strdup(mem_ctx, dn);
3821 status = ADS_ERROR(LDAP_NO_MEMORY);
3827 ads_msgfree(ads, res);
3833 * find our configuration path
3834 * @param ads connection to ads server
3835 * @param mem_ctx Pointer to talloc context
3836 * @param config_path Pointer to the config path
3837 * @return status of search
3839 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3840 TALLOC_CTX *mem_ctx,
3844 LDAPMessage *res = NULL;
3845 const char *config_context = NULL;
3846 const char *attrs[] = { "configurationNamingContext", NULL };
3848 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3849 "(objectclass=*)", attrs, &res);
3850 if (!ADS_ERR_OK(status)) {
3854 config_context = ads_pull_string(ads, mem_ctx, res,
3855 "configurationNamingContext");
3856 ads_msgfree(ads, res);
3857 if (!config_context) {
3858 return ADS_ERROR(LDAP_NO_MEMORY);
3862 *config_path = talloc_strdup(mem_ctx, config_context);
3863 if (!*config_path) {
3864 return ADS_ERROR(LDAP_NO_MEMORY);
3868 return ADS_ERROR(LDAP_SUCCESS);
3872 * find the displayName of an extended right
3873 * @param ads connection to ads server
3874 * @param config_path The config path
3875 * @param mem_ctx Pointer to talloc context
3876 * @param GUID struct of the rightsGUID
3877 * @return status of search
3879 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3880 const char *config_path,
3881 TALLOC_CTX *mem_ctx,
3882 const struct GUID *rights_guid)
3885 LDAPMessage *res = NULL;
3887 const char *attrs[] = { "displayName", NULL };
3888 const char *result = NULL;
3891 if (!ads || !mem_ctx || !rights_guid) {
3895 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3896 GUID_string(mem_ctx, rights_guid));
3901 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3906 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3908 if (!ADS_ERR_OK(rc)) {
3912 if (ads_count_replies(ads, res) != 1) {
3916 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3919 ads_msgfree(ads, res);
3924 * verify or build and verify an account ou
3925 * @param mem_ctx Pointer to talloc context
3926 * @param ads connection to ads server
3928 * @return status of search
3931 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3933 const char **account_ou)
3939 if (account_ou == NULL) {
3940 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3943 if (*account_ou != NULL) {
3944 exploded_dn = ldap_explode_dn(*account_ou, 0);
3946 ldap_value_free(exploded_dn);
3951 ou_string = ads_ou_string(ads, *account_ou);
3953 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3956 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3957 ads->config.bind_path);
3958 SAFE_FREE(ou_string);
3961 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3964 exploded_dn = ldap_explode_dn(name, 0);
3966 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3968 ldap_value_free(exploded_dn);