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[] = {"*", "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);
1498 * Perform an ldap modify
1499 * @param ads connection to ads server
1500 * @param mod_dn DistinguishedName to modify
1501 * @param mods list of modifications to perform
1502 * @return status of modify
1504 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1507 char *utf8_dn = NULL;
1508 size_t converted_size;
1510 this control is needed to modify that contains a currently
1511 non-existent attribute (but allowable for the object) to run
1513 LDAPControl PermitModify = {
1514 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1517 LDAPControl *controls[2];
1519 controls[0] = &PermitModify;
1522 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1523 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1526 /* find the end of the list, marked by NULL or -1 */
1527 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1528 /* make sure the end of the list is NULL */
1530 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1531 (LDAPMod **) mods, controls, NULL);
1532 TALLOC_FREE(utf8_dn);
1533 return ADS_ERROR(ret);
1537 * Perform an ldap add
1538 * @param ads connection to ads server
1539 * @param new_dn DistinguishedName to add
1540 * @param mods list of attributes and values for DN
1541 * @return status of add
1543 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1546 char *utf8_dn = NULL;
1547 size_t converted_size;
1549 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1550 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1551 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1554 /* find the end of the list, marked by NULL or -1 */
1555 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1556 /* make sure the end of the list is NULL */
1559 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1560 TALLOC_FREE(utf8_dn);
1561 return ADS_ERROR(ret);
1565 * Delete a DistinguishedName
1566 * @param ads connection to ads server
1567 * @param new_dn DistinguishedName to delete
1568 * @return status of delete
1570 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1573 char *utf8_dn = NULL;
1574 size_t converted_size;
1575 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1576 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1577 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1580 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1581 TALLOC_FREE(utf8_dn);
1582 return ADS_ERROR(ret);
1586 * Build an org unit string
1587 * if org unit is Computers or blank then assume a container, otherwise
1588 * assume a / separated list of organisational units.
1589 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1590 * @param ads connection to ads server
1591 * @param org_unit Organizational unit
1592 * @return org unit string - caller must free
1594 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1598 if (!org_unit || !*org_unit) {
1600 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1602 /* samba4 might not yet respond to a wellknownobject-query */
1603 return ret ? ret : SMB_STRDUP("cn=Computers");
1606 if (strequal(org_unit, "Computers")) {
1607 return SMB_STRDUP("cn=Computers");
1610 /* jmcd: removed "\\" from the separation chars, because it is
1611 needed as an escape for chars like '#' which are valid in an
1613 return ads_build_path(org_unit, "/", "ou=", 1);
1617 * Get a org unit string for a well-known GUID
1618 * @param ads connection to ads server
1619 * @param wknguid Well known GUID
1620 * @return org unit string - caller must free
1622 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1625 LDAPMessage *res = NULL;
1626 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1627 **bind_dn_exp = NULL;
1628 const char *attrs[] = {"distinguishedName", NULL};
1629 int new_ln, wkn_ln, bind_ln, i;
1631 if (wknguid == NULL) {
1635 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1636 DEBUG(1, ("asprintf failed!\n"));
1640 status = ads_search_dn(ads, &res, base, attrs);
1641 if (!ADS_ERR_OK(status)) {
1642 DEBUG(1,("Failed while searching for: %s\n", base));
1646 if (ads_count_replies(ads, res) != 1) {
1650 /* substitute the bind-path from the well-known-guid-search result */
1651 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1656 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1661 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1666 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1668 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1671 new_ln = wkn_ln - bind_ln;
1673 ret = SMB_STRDUP(wkn_dn_exp[0]);
1678 for (i=1; i < new_ln; i++) {
1681 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1687 ret = SMB_STRDUP(s);
1696 ads_msgfree(ads, res);
1697 TALLOC_FREE(wkn_dn);
1699 ldap_value_free(wkn_dn_exp);
1702 ldap_value_free(bind_dn_exp);
1709 * Adds (appends) an item to an attribute array, rather then
1710 * replacing the whole list
1711 * @param ctx An initialized TALLOC_CTX
1712 * @param mods An initialized ADS_MODLIST
1713 * @param name name of the ldap attribute to append to
1714 * @param vals an array of values to add
1715 * @return status of addition
1718 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1719 const char *name, const char **vals)
1721 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1722 (const void *) vals);
1726 * Determines the an account's current KVNO via an LDAP lookup
1727 * @param ads An initialized ADS_STRUCT
1728 * @param account_name the NT samaccountname.
1729 * @return the kvno for the account, or -1 in case of a failure.
1732 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1734 LDAPMessage *res = NULL;
1735 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1737 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1738 char *dn_string = NULL;
1739 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1741 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1742 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1745 ret = ads_search(ads, &res, filter, attrs);
1747 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1748 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1749 ads_msgfree(ads, res);
1753 dn_string = ads_get_dn(ads, talloc_tos(), res);
1755 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1756 ads_msgfree(ads, res);
1759 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1760 TALLOC_FREE(dn_string);
1762 /* ---------------------------------------------------------
1763 * 0 is returned as a default KVNO from this point on...
1764 * This is done because Windows 2000 does not support key
1765 * version numbers. Chances are that a failure in the next
1766 * step is simply due to Windows 2000 being used for a
1767 * domain controller. */
1770 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1771 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1772 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1773 ads_msgfree(ads, res);
1778 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1779 ads_msgfree(ads, res);
1784 * Determines the computer account's current KVNO via an LDAP lookup
1785 * @param ads An initialized ADS_STRUCT
1786 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1787 * @return the kvno for the computer account, or -1 in case of a failure.
1790 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1792 char *computer_account = NULL;
1795 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1799 kvno = ads_get_kvno(ads, computer_account);
1800 free(computer_account);
1806 * This clears out all registered spn's for a given hostname
1807 * @param ads An initilaized ADS_STRUCT
1808 * @param machine_name the NetBIOS name of the computer.
1809 * @return 0 upon success, non-zero otherwise.
1812 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1815 LDAPMessage *res = NULL;
1817 const char *servicePrincipalName[1] = {NULL};
1818 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1819 char *dn_string = NULL;
1821 ret = ads_find_machine_acct(ads, &res, machine_name);
1822 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1823 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1824 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1825 ads_msgfree(ads, res);
1826 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1829 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1830 ctx = talloc_init("ads_clear_service_principal_names");
1832 ads_msgfree(ads, res);
1833 return ADS_ERROR(LDAP_NO_MEMORY);
1836 if (!(mods = ads_init_mods(ctx))) {
1837 talloc_destroy(ctx);
1838 ads_msgfree(ads, res);
1839 return ADS_ERROR(LDAP_NO_MEMORY);
1841 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1842 if (!ADS_ERR_OK(ret)) {
1843 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1844 ads_msgfree(ads, res);
1845 talloc_destroy(ctx);
1848 dn_string = ads_get_dn(ads, talloc_tos(), res);
1850 talloc_destroy(ctx);
1851 ads_msgfree(ads, res);
1852 return ADS_ERROR(LDAP_NO_MEMORY);
1854 ret = ads_gen_mod(ads, dn_string, mods);
1855 TALLOC_FREE(dn_string);
1856 if (!ADS_ERR_OK(ret)) {
1857 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1859 ads_msgfree(ads, res);
1860 talloc_destroy(ctx);
1864 ads_msgfree(ads, res);
1865 talloc_destroy(ctx);
1870 * @brief Search for an element in a string array.
1872 * @param[in] el_array The string array to search.
1874 * @param[in] num_el The number of elements in the string array.
1876 * @param[in] el The string to search.
1878 * @return True if found, false if not.
1880 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1884 if (el_array == NULL || num_el == 0 || el == NULL) {
1888 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1891 cmp = strcasecmp_m(el_array[i], el);
1901 * @brief This gets the service principal names of an existing computer account.
1903 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1905 * @param[in] ads The ADS context to use.
1907 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1908 * identify the computer account.
1910 * @param[in] spn_array A pointer to store the array for SPNs.
1912 * @param[in] num_spns The number of principals stored in the array.
1914 * @return 0 on success, or a ADS error if a failure occured.
1916 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1918 const char *machine_name,
1923 LDAPMessage *res = NULL;
1926 status = ads_find_machine_acct(ads,
1929 if (!ADS_ERR_OK(status)) {
1930 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1935 count = ads_count_replies(ads, res);
1937 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1941 *spn_array = ads_pull_strings(ads,
1944 "servicePrincipalName",
1946 if (*spn_array == NULL) {
1947 DEBUG(1, ("Host account for %s does not have service principal "
1950 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1955 ads_msgfree(ads, res);
1961 * This adds a service principal name to an existing computer account
1962 * (found by hostname) in AD.
1963 * @param ads An initialized ADS_STRUCT
1964 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1965 * @param my_fqdn The fully qualified DNS name of the machine
1966 * @param spn A string of the service principal to add, i.e. 'host'
1967 * @return 0 upon sucess, or non-zero if a failure occurs
1970 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1971 const char *my_fqdn, const char *spn)
1975 LDAPMessage *res = NULL;
1978 char *dn_string = NULL;
1979 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1981 ret = ads_find_machine_acct(ads, &res, machine_name);
1982 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1983 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1985 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1986 spn, machine_name, ads->config.realm));
1987 ads_msgfree(ads, res);
1988 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1991 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1992 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1993 ads_msgfree(ads, res);
1994 return ADS_ERROR(LDAP_NO_MEMORY);
1997 /* add short name spn */
1999 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2000 talloc_destroy(ctx);
2001 ads_msgfree(ads, res);
2002 return ADS_ERROR(LDAP_NO_MEMORY);
2004 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2005 ret = ADS_ERROR(LDAP_NO_MEMORY);
2008 servicePrincipalName[0] = psp1;
2010 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2011 psp1, machine_name));
2014 /* add fully qualified spn */
2016 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2017 ret = ADS_ERROR(LDAP_NO_MEMORY);
2020 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2021 ret = ADS_ERROR(LDAP_NO_MEMORY);
2024 servicePrincipalName[1] = psp2;
2026 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2027 psp2, machine_name));
2029 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2030 ret = ADS_ERROR(LDAP_NO_MEMORY);
2034 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2035 if (!ADS_ERR_OK(ret)) {
2036 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2040 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2041 ret = ADS_ERROR(LDAP_NO_MEMORY);
2045 ret = ads_gen_mod(ads, dn_string, mods);
2046 if (!ADS_ERR_OK(ret)) {
2047 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2053 ads_msgfree(ads, res);
2058 * adds a machine account to the ADS server
2059 * @param ads An intialized ADS_STRUCT
2060 * @param machine_name - the NetBIOS machine name of this account.
2061 * @param account_type A number indicating the type of account to create
2062 * @param org_unit The LDAP path in which to place this account
2063 * @return 0 upon success, or non-zero otherwise
2066 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2067 const char *org_unit)
2070 char *samAccountName, *controlstr;
2073 char *machine_escaped = NULL;
2075 const char *objectClass[] = {"top", "person", "organizationalPerson",
2076 "user", "computer", NULL};
2077 LDAPMessage *res = NULL;
2078 uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2079 UF_DONT_EXPIRE_PASSWD |\
2080 UF_ACCOUNTDISABLE );
2081 uint32_t func_level = 0;
2083 ret = ads_domain_func_level(ads, &func_level);
2084 if (!ADS_ERR_OK(ret)) {
2088 if (!(ctx = talloc_init("ads_add_machine_acct")))
2089 return ADS_ERROR(LDAP_NO_MEMORY);
2091 ret = ADS_ERROR(LDAP_NO_MEMORY);
2093 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2094 if (!machine_escaped) {
2098 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2099 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2101 if ( !new_dn || !samAccountName ) {
2105 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2109 if (!(mods = ads_init_mods(ctx))) {
2113 ads_mod_str(ctx, &mods, "cn", machine_name);
2114 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2115 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2116 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2118 if (func_level >= DS_DOMAIN_FUNCTION_2008) {
2119 uint32_t etype_list = ENC_CRC32 | ENC_RSA_MD5 | ENC_RC4_HMAC_MD5;
2120 const char *etype_list_str;
2122 #ifdef HAVE_ENCTYPE_AES128_CTS_HMAC_SHA1_96
2123 etype_list |= ENC_HMAC_SHA1_96_AES128;
2125 #ifdef HAVE_ENCTYPE_AES256_CTS_HMAC_SHA1_96
2126 etype_list |= ENC_HMAC_SHA1_96_AES256;
2129 etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list);
2130 if (etype_list_str == NULL) {
2133 ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes",
2137 ret = ads_gen_add(ads, new_dn, mods);
2140 SAFE_FREE(machine_escaped);
2141 ads_msgfree(ads, res);
2142 talloc_destroy(ctx);
2148 * move a machine account to another OU on the ADS server
2149 * @param ads - An intialized ADS_STRUCT
2150 * @param machine_name - the NetBIOS machine name of this account.
2151 * @param org_unit - The LDAP path in which to place this account
2152 * @param moved - whether we moved the machine account (optional)
2153 * @return 0 upon success, or non-zero otherwise
2156 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2157 const char *org_unit, bool *moved)
2161 LDAPMessage *res = NULL;
2162 char *filter = NULL;
2163 char *computer_dn = NULL;
2165 char *computer_rdn = NULL;
2166 bool need_move = False;
2168 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2169 rc = ADS_ERROR(LDAP_NO_MEMORY);
2173 /* Find pre-existing machine */
2174 rc = ads_search(ads, &res, filter, NULL);
2175 if (!ADS_ERR_OK(rc)) {
2179 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2181 rc = ADS_ERROR(LDAP_NO_MEMORY);
2185 parent_dn = ads_parent_dn(computer_dn);
2186 if (strequal(parent_dn, org_unit)) {
2192 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2193 rc = ADS_ERROR(LDAP_NO_MEMORY);
2197 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2198 org_unit, 1, NULL, NULL);
2199 rc = ADS_ERROR(ldap_status);
2202 ads_msgfree(ads, res);
2204 TALLOC_FREE(computer_dn);
2205 SAFE_FREE(computer_rdn);
2207 if (!ADS_ERR_OK(rc)) {
2219 dump a binary result from ldap
2221 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2224 for (i=0; values[i]; i++) {
2225 printf("%s: ", field);
2226 for (j=0; j<values[i]->bv_len; j++) {
2227 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2233 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2236 for (i=0; values[i]; i++) {
2238 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2241 status = GUID_from_ndr_blob(&in, &guid);
2242 if (NT_STATUS_IS_OK(status)) {
2243 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2245 printf("%s: INVALID GUID\n", field);
2251 dump a sid result from ldap
2253 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2256 for (i=0; values[i]; i++) {
2259 if (!sid_parse((const uint8_t *)values[i]->bv_val,
2260 values[i]->bv_len, &sid)) {
2263 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2268 dump ntSecurityDescriptor
2270 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2272 TALLOC_CTX *frame = talloc_stackframe();
2273 struct security_descriptor *psd;
2276 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2277 values[0]->bv_len, &psd);
2278 if (!NT_STATUS_IS_OK(status)) {
2279 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2280 nt_errstr(status)));
2286 ads_disp_sd(ads, talloc_tos(), psd);
2293 dump a string result from ldap
2295 static void dump_string(const char *field, char **values)
2298 for (i=0; values[i]; i++) {
2299 printf("%s: %s\n", field, values[i]);
2304 dump a field from LDAP on stdout
2308 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2313 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2315 {"objectGUID", False, dump_guid},
2316 {"netbootGUID", False, dump_guid},
2317 {"nTSecurityDescriptor", False, dump_sd},
2318 {"dnsRecord", False, dump_binary},
2319 {"objectSid", False, dump_sid},
2320 {"tokenGroups", False, dump_sid},
2321 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2322 {"tokengroupsGlobalandUniversal", False, dump_sid},
2323 {"mS-DS-CreatorSID", False, dump_sid},
2324 {"msExchMailboxGuid", False, dump_guid},
2329 if (!field) { /* must be end of an entry */
2334 for (i=0; handlers[i].name; i++) {
2335 if (strcasecmp_m(handlers[i].name, field) == 0) {
2336 if (!values) /* first time, indicate string or not */
2337 return handlers[i].string;
2338 handlers[i].handler(ads, field, (struct berval **) values);
2342 if (!handlers[i].name) {
2343 if (!values) /* first time, indicate string conversion */
2345 dump_string(field, (char **)values);
2351 * Dump a result from LDAP on stdout
2352 * used for debugging
2353 * @param ads connection to ads server
2354 * @param res Results to dump
2357 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2359 ads_process_results(ads, res, ads_dump_field, NULL);
2363 * Walk through results, calling a function for each entry found.
2364 * The function receives a field name, a berval * array of values,
2365 * and a data area passed through from the start. The function is
2366 * called once with null for field and values at the end of each
2368 * @param ads connection to ads server
2369 * @param res Results to process
2370 * @param fn Function for processing each result
2371 * @param data_area user-defined area to pass to function
2373 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2374 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2379 size_t converted_size;
2381 if (!(ctx = talloc_init("ads_process_results")))
2384 for (msg = ads_first_entry(ads, res); msg;
2385 msg = ads_next_entry(ads, msg)) {
2389 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2390 (LDAPMessage *)msg,&b);
2392 utf8_field=ldap_next_attribute(ads->ldap.ld,
2393 (LDAPMessage *)msg,b)) {
2394 struct berval **ber_vals;
2400 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2403 DEBUG(0,("ads_process_results: "
2404 "pull_utf8_talloc failed: %s",
2408 string = fn(ads, field, NULL, data_area);
2413 utf8_vals = ldap_get_values(ads->ldap.ld,
2414 (LDAPMessage *)msg, field);
2415 p = discard_const_p(const char *, utf8_vals);
2416 str_vals = ads_pull_strvals(ctx, p);
2417 fn(ads, field, (void **) str_vals, data_area);
2418 ldap_value_free(utf8_vals);
2420 ber_vals = ldap_get_values_len(ads->ldap.ld,
2421 (LDAPMessage *)msg, field);
2422 fn(ads, field, (void **) ber_vals, data_area);
2424 ldap_value_free_len(ber_vals);
2426 ldap_memfree(utf8_field);
2429 talloc_free_children(ctx);
2430 fn(ads, NULL, NULL, data_area); /* completed an entry */
2433 talloc_destroy(ctx);
2437 * count how many replies are in a LDAPMessage
2438 * @param ads connection to ads server
2439 * @param res Results to count
2440 * @return number of replies
2442 int ads_count_replies(ADS_STRUCT *ads, void *res)
2444 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2448 * pull the first entry from a ADS result
2449 * @param ads connection to ads server
2450 * @param res Results of search
2451 * @return first entry from result
2453 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2455 return ldap_first_entry(ads->ldap.ld, res);
2459 * pull the next entry from a ADS result
2460 * @param ads connection to ads server
2461 * @param res Results of search
2462 * @return next entry from result
2464 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2466 return ldap_next_entry(ads->ldap.ld, res);
2470 * pull the first message from a ADS result
2471 * @param ads connection to ads server
2472 * @param res Results of search
2473 * @return first message from result
2475 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2477 return ldap_first_message(ads->ldap.ld, res);
2481 * pull the next message from a ADS result
2482 * @param ads connection to ads server
2483 * @param res Results of search
2484 * @return next message from result
2486 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2488 return ldap_next_message(ads->ldap.ld, res);
2492 * pull a single string from a ADS result
2493 * @param ads connection to ads server
2494 * @param mem_ctx TALLOC_CTX to use for allocating result string
2495 * @param msg Results of search
2496 * @param field Attribute to retrieve
2497 * @return Result string in talloc context
2499 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2505 size_t converted_size;
2507 values = ldap_get_values(ads->ldap.ld, msg, field);
2511 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2516 ldap_value_free(values);
2521 * pull an array of strings from a ADS result
2522 * @param ads connection to ads server
2523 * @param mem_ctx TALLOC_CTX to use for allocating result string
2524 * @param msg Results of search
2525 * @param field Attribute to retrieve
2526 * @return Result strings in talloc context
2528 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2529 LDAPMessage *msg, const char *field,
2535 size_t converted_size;
2537 values = ldap_get_values(ads->ldap.ld, msg, field);
2541 *num_values = ldap_count_values(values);
2543 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2545 ldap_value_free(values);
2549 for (i=0;i<*num_values;i++) {
2550 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2553 ldap_value_free(values);
2559 ldap_value_free(values);
2564 * pull an array of strings from a ADS result
2565 * (handle large multivalue attributes with range retrieval)
2566 * @param ads connection to ads server
2567 * @param mem_ctx TALLOC_CTX to use for allocating result string
2568 * @param msg Results of search
2569 * @param field Attribute to retrieve
2570 * @param current_strings strings returned by a previous call to this function
2571 * @param next_attribute The next query should ask for this attribute
2572 * @param num_values How many values did we get this time?
2573 * @param more_values Are there more values to get?
2574 * @return Result strings in talloc context
2576 char **ads_pull_strings_range(ADS_STRUCT *ads,
2577 TALLOC_CTX *mem_ctx,
2578 LDAPMessage *msg, const char *field,
2579 char **current_strings,
2580 const char **next_attribute,
2581 size_t *num_strings,
2585 char *expected_range_attrib, *range_attr;
2586 BerElement *ptr = NULL;
2589 size_t num_new_strings;
2590 unsigned long int range_start;
2591 unsigned long int range_end;
2593 /* we might have been given the whole lot anyway */
2594 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2595 *more_strings = False;
2599 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2601 /* look for Range result */
2602 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2604 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2605 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2606 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2614 /* nothing here - this field is just empty */
2615 *more_strings = False;
2619 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2620 &range_start, &range_end) == 2) {
2621 *more_strings = True;
2623 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2624 &range_start) == 1) {
2625 *more_strings = False;
2627 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2629 ldap_memfree(range_attr);
2630 *more_strings = False;
2635 if ((*num_strings) != range_start) {
2636 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2637 " - aborting range retreival\n",
2638 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2639 ldap_memfree(range_attr);
2640 *more_strings = False;
2644 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2646 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2647 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2648 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2649 range_attr, (unsigned long int)range_end - range_start + 1,
2650 (unsigned long int)num_new_strings));
2651 ldap_memfree(range_attr);
2652 *more_strings = False;
2656 strings = talloc_realloc(mem_ctx, current_strings, char *,
2657 *num_strings + num_new_strings);
2659 if (strings == NULL) {
2660 ldap_memfree(range_attr);
2661 *more_strings = False;
2665 if (new_strings && num_new_strings) {
2666 memcpy(&strings[*num_strings], new_strings,
2667 sizeof(*new_strings) * num_new_strings);
2670 (*num_strings) += num_new_strings;
2672 if (*more_strings) {
2673 *next_attribute = talloc_asprintf(mem_ctx,
2678 if (!*next_attribute) {
2679 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2680 ldap_memfree(range_attr);
2681 *more_strings = False;
2686 ldap_memfree(range_attr);
2692 * pull a single uint32_t from a ADS result
2693 * @param ads connection to ads server
2694 * @param msg Results of search
2695 * @param field Attribute to retrieve
2696 * @param v Pointer to int to store result
2697 * @return boolean inidicating success
2699 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2704 values = ldap_get_values(ads->ldap.ld, msg, field);
2708 ldap_value_free(values);
2712 *v = atoi(values[0]);
2713 ldap_value_free(values);
2718 * pull a single objectGUID from an ADS result
2719 * @param ads connection to ADS server
2720 * @param msg results of search
2721 * @param guid 37-byte area to receive text guid
2722 * @return boolean indicating success
2724 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2729 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2734 status = GUID_from_ndr_blob(&blob, guid);
2735 talloc_free(blob.data);
2736 return NT_STATUS_IS_OK(status);
2741 * pull a single struct dom_sid from a ADS result
2742 * @param ads connection to ads server
2743 * @param msg Results of search
2744 * @param field Attribute to retrieve
2745 * @param sid Pointer to sid to store result
2746 * @return boolean inidicating success
2748 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2749 struct dom_sid *sid)
2751 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2755 * pull an array of struct dom_sids from a ADS result
2756 * @param ads connection to ads server
2757 * @param mem_ctx TALLOC_CTX for allocating sid array
2758 * @param msg Results of search
2759 * @param field Attribute to retrieve
2760 * @param sids pointer to sid array to allocate
2761 * @return the count of SIDs pulled
2763 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2764 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2766 struct berval **values;
2770 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2775 for (i=0; values[i]; i++)
2779 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2781 ldap_value_free_len(values);
2789 for (i=0; values[i]; i++) {
2790 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2791 values[i]->bv_len, &(*sids)[count]);
2793 DEBUG(10, ("pulling SID: %s\n",
2794 sid_string_dbg(&(*sids)[count])));
2799 ldap_value_free_len(values);
2804 * pull a struct security_descriptor from a ADS result
2805 * @param ads connection to ads server
2806 * @param mem_ctx TALLOC_CTX for allocating sid array
2807 * @param msg Results of search
2808 * @param field Attribute to retrieve
2809 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2810 * @return boolean inidicating success
2812 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2813 LDAPMessage *msg, const char *field,
2814 struct security_descriptor **sd)
2816 struct berval **values;
2819 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2821 if (!values) return false;
2825 status = unmarshall_sec_desc(mem_ctx,
2826 (uint8_t *)values[0]->bv_val,
2827 values[0]->bv_len, sd);
2828 if (!NT_STATUS_IS_OK(status)) {
2829 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2830 nt_errstr(status)));
2835 ldap_value_free_len(values);
2840 * in order to support usernames longer than 21 characters we need to
2841 * use both the sAMAccountName and the userPrincipalName attributes
2842 * It seems that not all users have the userPrincipalName attribute set
2844 * @param ads connection to ads server
2845 * @param mem_ctx TALLOC_CTX for allocating sid array
2846 * @param msg Results of search
2847 * @return the username
2849 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2855 /* lookup_name() only works on the sAMAccountName to
2856 returning the username portion of userPrincipalName
2857 breaks winbindd_getpwnam() */
2859 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2860 if (ret && (p = strchr_m(ret, '@'))) {
2865 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2870 * find the update serial number - this is the core of the ldap cache
2871 * @param ads connection to ads server
2872 * @param ads connection to ADS server
2873 * @param usn Pointer to retrieved update serial number
2874 * @return status of search
2876 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2878 const char *attrs[] = {"highestCommittedUSN", NULL};
2882 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2883 if (!ADS_ERR_OK(status))
2886 if (ads_count_replies(ads, res) != 1) {
2887 ads_msgfree(ads, res);
2888 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2891 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2892 ads_msgfree(ads, res);
2893 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2896 ads_msgfree(ads, res);
2900 /* parse a ADS timestring - typical string is
2901 '20020917091222.0Z0' which means 09:12.22 17th September
2903 static time_t ads_parse_time(const char *str)
2909 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2910 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2911 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2920 /********************************************************************
2921 ********************************************************************/
2923 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2925 const char *attrs[] = {"currentTime", NULL};
2930 ADS_STRUCT *ads_s = ads;
2932 if (!(ctx = talloc_init("ads_current_time"))) {
2933 return ADS_ERROR(LDAP_NO_MEMORY);
2936 /* establish a new ldap tcp session if necessary */
2938 if ( !ads->ldap.ld ) {
2939 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2940 ads->server.ldap_server )) == NULL )
2944 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2945 status = ads_connect( ads_s );
2946 if ( !ADS_ERR_OK(status))
2950 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2951 if (!ADS_ERR_OK(status)) {
2955 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2957 ads_msgfree(ads_s, res);
2958 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2962 /* but save the time and offset in the original ADS_STRUCT */
2964 ads->config.current_time = ads_parse_time(timestr);
2966 if (ads->config.current_time != 0) {
2967 ads->auth.time_offset = ads->config.current_time - time(NULL);
2968 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2971 ads_msgfree(ads, res);
2973 status = ADS_SUCCESS;
2976 /* free any temporary ads connections */
2977 if ( ads_s != ads ) {
2978 ads_destroy( &ads_s );
2980 talloc_destroy(ctx);
2985 /********************************************************************
2986 ********************************************************************/
2988 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
2990 const char *attrs[] = {"domainFunctionality", NULL};
2993 ADS_STRUCT *ads_s = ads;
2995 *val = DS_DOMAIN_FUNCTION_2000;
2997 /* establish a new ldap tcp session if necessary */
2999 if ( !ads->ldap.ld ) {
3000 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3001 ads->server.ldap_server )) == NULL )
3003 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3006 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3007 status = ads_connect( ads_s );
3008 if ( !ADS_ERR_OK(status))
3012 /* If the attribute does not exist assume it is a Windows 2000
3013 functional domain */
3015 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3016 if (!ADS_ERR_OK(status)) {
3017 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3018 status = ADS_SUCCESS;
3023 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3024 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3026 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3029 ads_msgfree(ads, res);
3032 /* free any temporary ads connections */
3033 if ( ads_s != ads ) {
3034 ads_destroy( &ads_s );
3041 * find the domain sid for our domain
3042 * @param ads connection to ads server
3043 * @param sid Pointer to domain sid
3044 * @return status of search
3046 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3048 const char *attrs[] = {"objectSid", NULL};
3052 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3054 if (!ADS_ERR_OK(rc)) return rc;
3055 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3056 ads_msgfree(ads, res);
3057 return ADS_ERROR_SYSTEM(ENOENT);
3059 ads_msgfree(ads, res);
3065 * find our site name
3066 * @param ads connection to ads server
3067 * @param mem_ctx Pointer to talloc context
3068 * @param site_name Pointer to the sitename
3069 * @return status of search
3071 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3075 const char *dn, *service_name;
3076 const char *attrs[] = { "dsServiceName", NULL };
3078 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3079 if (!ADS_ERR_OK(status)) {
3083 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3084 if (service_name == NULL) {
3085 ads_msgfree(ads, res);
3086 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3089 ads_msgfree(ads, res);
3091 /* go up three levels */
3092 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3094 return ADS_ERROR(LDAP_NO_MEMORY);
3097 *site_name = talloc_strdup(mem_ctx, dn);
3098 if (*site_name == NULL) {
3099 return ADS_ERROR(LDAP_NO_MEMORY);
3104 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3109 * find the site dn where a machine resides
3110 * @param ads connection to ads server
3111 * @param mem_ctx Pointer to talloc context
3112 * @param computer_name name of the machine
3113 * @param site_name Pointer to the sitename
3114 * @return status of search
3116 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3120 const char *parent, *filter;
3121 char *config_context = NULL;
3124 /* shortcut a query */
3125 if (strequal(computer_name, ads->config.ldap_server_name)) {
3126 return ads_site_dn(ads, mem_ctx, site_dn);
3129 status = ads_config_path(ads, mem_ctx, &config_context);
3130 if (!ADS_ERR_OK(status)) {
3134 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3135 if (filter == NULL) {
3136 return ADS_ERROR(LDAP_NO_MEMORY);
3139 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3140 filter, NULL, &res);
3141 if (!ADS_ERR_OK(status)) {
3145 if (ads_count_replies(ads, res) != 1) {
3146 ads_msgfree(ads, res);
3147 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3150 dn = ads_get_dn(ads, mem_ctx, res);
3152 ads_msgfree(ads, res);
3153 return ADS_ERROR(LDAP_NO_MEMORY);
3156 /* go up three levels */
3157 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3158 if (parent == NULL) {
3159 ads_msgfree(ads, res);
3161 return ADS_ERROR(LDAP_NO_MEMORY);
3164 *site_dn = talloc_strdup(mem_ctx, parent);
3165 if (*site_dn == NULL) {
3166 ads_msgfree(ads, res);
3168 return ADS_ERROR(LDAP_NO_MEMORY);
3172 ads_msgfree(ads, res);
3178 * get the upn suffixes for a domain
3179 * @param ads connection to ads server
3180 * @param mem_ctx Pointer to talloc context
3181 * @param suffixes Pointer to an array of suffixes
3182 * @param num_suffixes Pointer to the number of suffixes
3183 * @return status of search
3185 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3190 char *config_context = NULL;
3191 const char *attrs[] = { "uPNSuffixes", NULL };
3193 status = ads_config_path(ads, mem_ctx, &config_context);
3194 if (!ADS_ERR_OK(status)) {
3198 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3200 return ADS_ERROR(LDAP_NO_MEMORY);
3203 status = ads_search_dn(ads, &res, base, attrs);
3204 if (!ADS_ERR_OK(status)) {
3208 if (ads_count_replies(ads, res) != 1) {
3209 ads_msgfree(ads, res);
3210 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3213 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3214 if ((*suffixes) == NULL) {
3215 ads_msgfree(ads, res);
3216 return ADS_ERROR(LDAP_NO_MEMORY);
3219 ads_msgfree(ads, res);
3225 * get the joinable ous for a domain
3226 * @param ads connection to ads server
3227 * @param mem_ctx Pointer to talloc context
3228 * @param ous Pointer to an array of ous
3229 * @param num_ous Pointer to the number of ous
3230 * @return status of search
3232 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3233 TALLOC_CTX *mem_ctx,
3238 LDAPMessage *res = NULL;
3239 LDAPMessage *msg = NULL;
3240 const char *attrs[] = { "dn", NULL };
3243 status = ads_search(ads, &res,
3244 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3246 if (!ADS_ERR_OK(status)) {
3250 count = ads_count_replies(ads, res);
3252 ads_msgfree(ads, res);
3253 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3256 for (msg = ads_first_entry(ads, res); msg;
3257 msg = ads_next_entry(ads, msg)) {
3258 const char **p = discard_const_p(const char *, *ous);
3261 dn = ads_get_dn(ads, talloc_tos(), msg);
3263 ads_msgfree(ads, res);
3264 return ADS_ERROR(LDAP_NO_MEMORY);
3267 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3269 ads_msgfree(ads, res);
3270 return ADS_ERROR(LDAP_NO_MEMORY);
3274 *ous = discard_const_p(char *, p);
3277 ads_msgfree(ads, res);
3284 * pull a struct dom_sid from an extended dn string
3285 * @param mem_ctx TALLOC_CTX
3286 * @param extended_dn string
3287 * @param flags string type of extended_dn
3288 * @param sid pointer to a struct dom_sid
3289 * @return NT_STATUS_OK on success,
3290 * NT_INVALID_PARAMETER on error,
3291 * NT_STATUS_NOT_FOUND if no SID present
3293 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3294 const char *extended_dn,
3295 enum ads_extended_dn_flags flags,
3296 struct dom_sid *sid)
3301 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3304 /* otherwise extended_dn gets stripped off */
3305 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3306 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3309 * ADS_EXTENDED_DN_HEX_STRING:
3310 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3312 * ADS_EXTENDED_DN_STRING (only with w2k3):
3313 * <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
3315 * Object with no SID, such as an Exchange Public Folder
3316 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3319 p = strchr(dn, ';');
3321 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3324 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3325 DEBUG(5,("No SID present in extended dn\n"));
3326 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3329 p += strlen(";<SID=");
3333 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3338 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3342 case ADS_EXTENDED_DN_STRING:
3343 if (!string_to_sid(sid, p)) {
3344 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3347 case ADS_EXTENDED_DN_HEX_STRING: {
3351 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3353 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3356 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3357 DEBUG(10,("failed to parse sid\n"));
3358 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3363 DEBUG(10,("unknown extended dn format\n"));
3364 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3367 return ADS_ERROR_NT(NT_STATUS_OK);
3370 /********************************************************************
3371 ********************************************************************/
3373 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3375 LDAPMessage *res = NULL;
3380 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3381 if (!ADS_ERR_OK(status)) {
3382 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3383 lp_netbios_name()));
3387 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3388 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3392 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3393 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3397 ads_msgfree(ads, res);
3402 /********************************************************************
3403 ********************************************************************/
3405 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3407 LDAPMessage *res = NULL;
3412 status = ads_find_machine_acct(ads, &res, machine_name);
3413 if (!ADS_ERR_OK(status)) {
3414 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3415 lp_netbios_name()));
3419 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3420 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3424 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3425 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3429 ads_msgfree(ads, res);
3434 /********************************************************************
3435 ********************************************************************/
3437 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3439 LDAPMessage *res = NULL;
3444 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3445 if (!ADS_ERR_OK(status)) {
3446 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3447 lp_netbios_name()));
3451 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3452 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3456 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3457 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3461 ads_msgfree(ads, res);
3468 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3471 * Join a machine to a realm
3472 * Creates the machine account and sets the machine password
3473 * @param ads connection to ads server
3474 * @param machine name of host to add
3475 * @param org_unit Organizational unit to place machine in
3476 * @return status of join
3478 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3479 uint32_t account_type, const char *org_unit)
3482 LDAPMessage *res = NULL;
3485 /* machine name must be lowercase */
3486 machine = SMB_STRDUP(machine_name);
3487 strlower_m(machine);
3490 status = ads_find_machine_acct(ads, (void **)&res, machine);
3491 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3492 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3493 status = ads_leave_realm(ads, machine);
3494 if (!ADS_ERR_OK(status)) {
3495 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3496 machine, ads->config.realm));
3501 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3502 if (!ADS_ERR_OK(status)) {
3503 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3508 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3509 if (!ADS_ERR_OK(status)) {
3510 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3516 ads_msgfree(ads, res);
3523 * Delete a machine from the realm
3524 * @param ads connection to ads server
3525 * @param hostname Machine to remove
3526 * @return status of delete
3528 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3533 char *hostnameDN, *host;
3535 LDAPControl ldap_control;
3536 LDAPControl * pldap_control[2] = {NULL, NULL};
3538 pldap_control[0] = &ldap_control;
3539 memset(&ldap_control, 0, sizeof(LDAPControl));
3540 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3542 /* hostname must be lowercase */
3543 host = SMB_STRDUP(hostname);
3544 if (!strlower_m(host)) {
3546 return ADS_ERROR_SYSTEM(EINVAL);
3549 status = ads_find_machine_acct(ads, &res, host);
3550 if (!ADS_ERR_OK(status)) {
3551 DEBUG(0, ("Host account for %s does not exist.\n", host));
3556 msg = ads_first_entry(ads, res);
3559 return ADS_ERROR_SYSTEM(ENOENT);
3562 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3563 if (hostnameDN == NULL) {
3565 return ADS_ERROR_SYSTEM(ENOENT);
3568 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3570 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3572 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3575 if (rc != LDAP_SUCCESS) {
3576 const char *attrs[] = { "cn", NULL };
3577 LDAPMessage *msg_sub;
3579 /* we only search with scope ONE, we do not expect any further
3580 * objects to be created deeper */
3582 status = ads_do_search_retry(ads, hostnameDN,
3583 LDAP_SCOPE_ONELEVEL,
3584 "(objectclass=*)", attrs, &res);
3586 if (!ADS_ERR_OK(status)) {
3588 TALLOC_FREE(hostnameDN);
3592 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3593 msg_sub = ads_next_entry(ads, msg_sub)) {
3597 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3599 TALLOC_FREE(hostnameDN);
3600 return ADS_ERROR(LDAP_NO_MEMORY);
3603 status = ads_del_dn(ads, dn);
3604 if (!ADS_ERR_OK(status)) {
3605 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3608 TALLOC_FREE(hostnameDN);
3615 /* there should be no subordinate objects anymore */
3616 status = ads_do_search_retry(ads, hostnameDN,
3617 LDAP_SCOPE_ONELEVEL,
3618 "(objectclass=*)", attrs, &res);
3620 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3622 TALLOC_FREE(hostnameDN);
3626 /* delete hostnameDN now */
3627 status = ads_del_dn(ads, hostnameDN);
3628 if (!ADS_ERR_OK(status)) {
3630 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3631 TALLOC_FREE(hostnameDN);
3636 TALLOC_FREE(hostnameDN);
3638 status = ads_find_machine_acct(ads, &res, host);
3639 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3640 DEBUG(3, ("Failed to remove host account.\n"));
3650 * pull all token-sids from an LDAP dn
3651 * @param ads connection to ads server
3652 * @param mem_ctx TALLOC_CTX for allocating sid array
3653 * @param dn of LDAP object
3654 * @param user_sid pointer to struct dom_sid (objectSid)
3655 * @param primary_group_sid pointer to struct dom_sid (self composed)
3656 * @param sids pointer to sid array to allocate
3657 * @param num_sids counter of SIDs pulled
3658 * @return status of token query
3660 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3661 TALLOC_CTX *mem_ctx,
3663 struct dom_sid *user_sid,
3664 struct dom_sid *primary_group_sid,
3665 struct dom_sid **sids,
3669 LDAPMessage *res = NULL;
3671 size_t tmp_num_sids;
3672 struct dom_sid *tmp_sids;
3673 struct dom_sid tmp_user_sid;
3674 struct dom_sid tmp_primary_group_sid;
3676 const char *attrs[] = {
3683 status = ads_search_retry_dn(ads, &res, dn, attrs);
3684 if (!ADS_ERR_OK(status)) {
3688 count = ads_count_replies(ads, res);
3690 ads_msgfree(ads, res);
3691 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3694 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3695 ads_msgfree(ads, res);
3696 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3699 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3700 ads_msgfree(ads, res);
3701 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3705 /* hack to compose the primary group sid without knowing the
3708 struct dom_sid domsid;
3710 sid_copy(&domsid, &tmp_user_sid);
3712 if (!sid_split_rid(&domsid, NULL)) {
3713 ads_msgfree(ads, res);
3714 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3717 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3718 ads_msgfree(ads, res);
3719 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3723 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3725 if (tmp_num_sids == 0 || !tmp_sids) {
3726 ads_msgfree(ads, res);
3727 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3731 *num_sids = tmp_num_sids;
3739 *user_sid = tmp_user_sid;
3742 if (primary_group_sid) {
3743 *primary_group_sid = tmp_primary_group_sid;
3746 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3748 ads_msgfree(ads, res);
3749 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3753 * Find a sAMAccoutName in LDAP
3754 * @param ads connection to ads server
3755 * @param mem_ctx TALLOC_CTX for allocating sid array
3756 * @param samaccountname to search
3757 * @param uac_ret uint32_t pointer userAccountControl attribute value
3758 * @param dn_ret pointer to dn
3759 * @return status of token query
3761 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3762 TALLOC_CTX *mem_ctx,
3763 const char *samaccountname,
3765 const char **dn_ret)
3768 const char *attrs[] = { "userAccountControl", NULL };
3770 LDAPMessage *res = NULL;
3774 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3776 if (filter == NULL) {
3777 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3781 status = ads_do_search_all(ads, ads->config.bind_path,
3783 filter, attrs, &res);
3785 if (!ADS_ERR_OK(status)) {
3789 if (ads_count_replies(ads, res) != 1) {
3790 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3794 dn = ads_get_dn(ads, talloc_tos(), res);
3796 status = ADS_ERROR(LDAP_NO_MEMORY);
3800 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3801 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3810 *dn_ret = talloc_strdup(mem_ctx, dn);
3812 status = ADS_ERROR(LDAP_NO_MEMORY);
3818 ads_msgfree(ads, res);
3824 * find our configuration path
3825 * @param ads connection to ads server
3826 * @param mem_ctx Pointer to talloc context
3827 * @param config_path Pointer to the config path
3828 * @return status of search
3830 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3831 TALLOC_CTX *mem_ctx,
3835 LDAPMessage *res = NULL;
3836 const char *config_context = NULL;
3837 const char *attrs[] = { "configurationNamingContext", NULL };
3839 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3840 "(objectclass=*)", attrs, &res);
3841 if (!ADS_ERR_OK(status)) {
3845 config_context = ads_pull_string(ads, mem_ctx, res,
3846 "configurationNamingContext");
3847 ads_msgfree(ads, res);
3848 if (!config_context) {
3849 return ADS_ERROR(LDAP_NO_MEMORY);
3853 *config_path = talloc_strdup(mem_ctx, config_context);
3854 if (!*config_path) {
3855 return ADS_ERROR(LDAP_NO_MEMORY);
3859 return ADS_ERROR(LDAP_SUCCESS);
3863 * find the displayName of an extended right
3864 * @param ads connection to ads server
3865 * @param config_path The config path
3866 * @param mem_ctx Pointer to talloc context
3867 * @param GUID struct of the rightsGUID
3868 * @return status of search
3870 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3871 const char *config_path,
3872 TALLOC_CTX *mem_ctx,
3873 const struct GUID *rights_guid)
3876 LDAPMessage *res = NULL;
3878 const char *attrs[] = { "displayName", NULL };
3879 const char *result = NULL;
3882 if (!ads || !mem_ctx || !rights_guid) {
3886 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3887 GUID_string(mem_ctx, rights_guid));
3892 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3897 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3899 if (!ADS_ERR_OK(rc)) {
3903 if (ads_count_replies(ads, res) != 1) {
3907 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3910 ads_msgfree(ads, res);
3915 * verify or build and verify an account ou
3916 * @param mem_ctx Pointer to talloc context
3917 * @param ads connection to ads server
3919 * @return status of search
3922 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3924 const char **account_ou)
3930 exploded_dn = ldap_explode_dn(*account_ou, 0);
3932 ldap_value_free(exploded_dn);
3936 ou_string = ads_ou_string(ads, *account_ou);
3938 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3941 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3942 ads->config.bind_path);
3943 SAFE_FREE(ou_string);
3946 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3949 exploded_dn = ldap_explode_dn(name, 0);
3951 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3953 ldap_value_free(exploded_dn);