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 "lib/param/loadparm.h"
38 * @brief basic ldap client-side routines for ads server communications
40 * The routines contained here should do the necessary ldap calls for
43 * Important note: attribute names passed into ads_ routines must
44 * already be in UTF-8 format. We do not convert them because in almost
45 * all cases, they are just ascii (which is represented with the same
46 * codepoints in UTF-8). This may have to change at some point
50 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
52 static SIG_ATOMIC_T gotalarm;
54 /***************************************************************
55 Signal function to tell us we timed out.
56 ****************************************************************/
58 static void gotalarm_sig(int signum)
63 LDAP *ldap_open_with_timeout(const char *server,
64 struct sockaddr_storage *ss,
65 int port, unsigned int to)
71 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
72 "%u seconds\n", server, port, to));
77 CatchSignal(SIGALRM, gotalarm_sig);
79 /* End setup timeout. */
82 if ( strchr_m(server, ':') ) {
84 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
87 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
93 #ifdef HAVE_LDAP_INITIALIZE
94 ldap_err = ldap_initialize(&ldp, uri);
96 ldp = ldap_open(server, port);
98 ldap_err = LDAP_SUCCESS;
100 ldap_err = LDAP_OTHER;
103 if (ldap_err != LDAP_SUCCESS) {
104 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
105 uri, ldap_err2string(ldap_err)));
107 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
111 /* Teardown timeout. */
113 CatchSignal(SIGALRM, SIG_IGN);
119 static int ldap_search_with_timeout(LDAP *ld,
120 LDAP_CONST char *base,
122 LDAP_CONST char *filter,
125 LDAPControl **sctrls,
126 LDAPControl **cctrls,
130 int to = lp_ldap_timeout();
131 struct timeval timeout;
132 struct timeval *timeout_ptr = NULL;
135 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
141 timeout_ptr = &timeout;
143 /* Setup alarm timeout. */
144 CatchSignal(SIGALRM, gotalarm_sig);
145 /* Make the alarm time one second beyond
146 the timout we're setting for the
147 remote search timeout, to allow that
148 to fire in preference. */
150 /* End setup timeout. */
154 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
155 attrsonly, sctrls, cctrls, timeout_ptr,
159 /* Teardown alarm timeout. */
160 CatchSignal(SIGALRM, SIG_IGN);
165 return LDAP_TIMELIMIT_EXCEEDED;
168 * A bug in OpenLDAP means ldap_search_ext_s can return
169 * LDAP_SUCCESS but with a NULL res pointer. Cope with
170 * this. See bug #6279 for details. JRA.
174 return LDAP_TIMELIMIT_EXCEEDED;
180 /**********************************************
181 Do client and server sitename match ?
182 **********************************************/
184 bool ads_sitename_match(ADS_STRUCT *ads)
186 if (ads->config.server_site_name == NULL &&
187 ads->config.client_site_name == NULL ) {
188 DEBUG(10,("ads_sitename_match: both null\n"));
191 if (ads->config.server_site_name &&
192 ads->config.client_site_name &&
193 strequal(ads->config.server_site_name,
194 ads->config.client_site_name)) {
195 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
198 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
199 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
200 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
204 /**********************************************
205 Is this the closest DC ?
206 **********************************************/
208 bool ads_closest_dc(ADS_STRUCT *ads)
210 if (ads->config.flags & NBT_SERVER_CLOSEST) {
211 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
215 /* not sure if this can ever happen */
216 if (ads_sitename_match(ads)) {
217 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
221 if (ads->config.client_site_name == NULL) {
222 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
226 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
227 ads->config.ldap_server_name));
234 try a connection to a given ldap server, returning True and setting the servers IP
235 in the ads struct if successful
237 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
238 struct sockaddr_storage *ss)
240 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
241 TALLOC_CTX *frame = talloc_stackframe();
243 char addr[INET6_ADDRSTRLEN];
250 print_sockaddr(addr, sizeof(addr), ss);
252 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
253 addr, ads->server.realm));
255 ZERO_STRUCT( cldap_reply );
257 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
258 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
263 /* Check the CLDAP reply flags */
265 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
266 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
272 /* Fill in the ads->config values */
274 SAFE_FREE(ads->config.realm);
275 SAFE_FREE(ads->config.bind_path);
276 SAFE_FREE(ads->config.ldap_server_name);
277 SAFE_FREE(ads->config.server_site_name);
278 SAFE_FREE(ads->config.client_site_name);
279 SAFE_FREE(ads->server.workgroup);
281 ads->config.flags = cldap_reply.server_type;
282 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
283 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
284 if (!strupper_m(ads->config.realm)) {
289 ads->config.bind_path = ads_build_dn(ads->config.realm);
290 if (*cldap_reply.server_site) {
291 ads->config.server_site_name =
292 SMB_STRDUP(cldap_reply.server_site);
294 if (*cldap_reply.client_site) {
295 ads->config.client_site_name =
296 SMB_STRDUP(cldap_reply.client_site);
298 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
300 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
303 /* Store our site name. */
304 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
305 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
315 /**********************************************************************
316 resolve a name and perform an "ldap ping"
317 **********************************************************************/
319 static NTSTATUS resolve_and_ping(ADS_STRUCT *ads, const char *sitename,
320 const char *resolve_target, bool use_dns,
324 struct ip_service *ip_list;
325 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
328 DEBUG(6, ("resolve_and_ping: (cldap) looking for %s '%s'\n",
329 (use_dns ? "realm" : "domain"), resolve_target));
331 status = get_sorted_dc_list(resolve_target, sitename, &ip_list, &count,
333 if (!NT_STATUS_IS_OK(status)) {
337 /* if we fail this loop, then giveup since all the IP addresses returned
339 for (i = 0; i < count; i++) {
340 char server[INET6_ADDRSTRLEN];
342 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
344 if (!NT_STATUS_IS_OK(
345 check_negative_conn_cache(resolve_target, server)))
349 /* resolve_target in this case is a workgroup name. We
351 to ignore any IP addresses in the negative connection
352 cache that match ip addresses returned in the ad
355 if (realm && *realm &&
357 check_negative_conn_cache(realm, server))) {
358 /* Ensure we add the workgroup name for this
359 IP address as negative too. */
360 add_failed_connection_entry(
361 resolve_target, server,
362 NT_STATUS_UNSUCCESSFUL);
367 ok = ads_try_connect(ads, false, &ip_list[i].ss);
373 /* keep track of failures */
374 add_failed_connection_entry(resolve_target, server,
375 NT_STATUS_UNSUCCESSFUL);
380 return NT_STATUS_NO_LOGON_SERVERS;
383 /**********************************************************************
384 Try to find an AD dc using our internal name resolution routines
385 Try the realm first and then then workgroup name if netbios is not
387 **********************************************************************/
389 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
391 const char *c_domain = "";
393 bool use_own_domain = False;
394 char *sitename = NULL;
395 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
398 /* if the realm and workgroup are both empty, assume they are ours */
401 c_realm = ads->server.realm;
407 /* special case where no realm and no workgroup means our own */
408 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
409 use_own_domain = True;
410 c_realm = lp_realm();
414 if (!lp_disable_netbios()) {
415 if (use_own_domain) {
416 c_domain = lp_workgroup();
418 c_domain = ads->server.workgroup;
419 if (!*c_realm && (!c_domain || !*c_domain)) {
420 c_domain = lp_workgroup();
429 if (!*c_realm && !*c_domain) {
430 DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know "
432 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
436 * In case of LDAP we use get_dc_name() as that
437 * creates the custom krb5.conf file
439 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
441 struct sockaddr_storage ip_out;
443 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
444 " and falling back to domain '%s'\n",
447 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
450 * we call ads_try_connect() to fill in the
451 * ads->config details
453 ok = ads_try_connect(ads, false, &ip_out);
459 return NT_STATUS_NO_LOGON_SERVERS;
463 sitename = sitename_fetch(talloc_tos(), c_realm);
464 status = resolve_and_ping(ads, sitename, c_realm, true, c_realm);
466 if (NT_STATUS_IS_OK(status)) {
467 TALLOC_FREE(sitename);
471 /* In case we failed to contact one of our closest DC on our
473 * need to try to find another DC, retry with a site-less SRV
478 DEBUG(1, ("ads_find_dc: failed to find a valid DC on "
480 "trying to find another DC\n",
482 namecache_delete(c_realm, 0x1C);
484 resolve_and_ping(ads, NULL, c_realm, true, c_realm);
486 if (NT_STATUS_IS_OK(status)) {
487 TALLOC_FREE(sitename);
492 TALLOC_FREE(sitename);
495 /* try netbios as fallback - if permitted,
496 or if configuration specifically requests it */
499 DEBUG(1, ("ads_find_dc: falling back to netbios "
500 "name resolution for domain %s\n",
504 status = resolve_and_ping(ads, NULL, c_domain, false, c_realm);
510 /*********************************************************************
511 *********************************************************************/
513 static NTSTATUS ads_lookup_site(void)
515 ADS_STRUCT *ads = NULL;
516 ADS_STATUS ads_status;
517 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
519 ads = ads_init(lp_realm(), NULL, NULL);
521 return NT_STATUS_NO_MEMORY;
524 /* The NO_BIND here will find a DC and set the client site
525 but not establish the TCP connection */
527 ads->auth.flags = ADS_AUTH_NO_BIND;
528 ads_status = ads_connect(ads);
529 if (!ADS_ERR_OK(ads_status)) {
530 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
531 ads_errstr(ads_status)));
533 nt_status = ads_ntstatus(ads_status);
542 /*********************************************************************
543 *********************************************************************/
545 static const char* host_dns_domain(const char *fqdn)
547 const char *p = fqdn;
549 /* go to next char following '.' */
551 if ((p = strchr_m(fqdn, '.')) != NULL) {
560 * Connect to the Global Catalog server
561 * @param ads Pointer to an existing ADS_STRUCT
562 * @return status of connection
564 * Simple wrapper around ads_connect() that fills in the
565 * GC ldap server information
568 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
570 TALLOC_CTX *frame = talloc_stackframe();
571 struct dns_rr_srv *gcs_list;
573 const char *realm = ads->server.realm;
574 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
575 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
578 char *sitename = NULL;
583 if ((sitename = sitename_fetch(frame, realm)) == NULL) {
585 sitename = sitename_fetch(frame, realm);
589 /* We try once with a sitename and once without
590 (unless we don't have a sitename and then we're
593 if (sitename == NULL)
596 nt_status = ads_dns_query_gcs(frame,
602 if (!NT_STATUS_IS_OK(nt_status)) {
603 ads_status = ADS_ERROR_NT(nt_status);
607 /* Loop until we get a successful connection or have gone
608 through them all. When connecting a GC server, make sure that
609 the realm is the server's DNS name and not the forest root */
611 for (i=0; i<num_gcs; i++) {
612 ads->server.gc = true;
613 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
614 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
615 ads_status = ads_connect(ads);
616 if (ADS_ERR_OK(ads_status)) {
617 /* Reset the bind_dn to "". A Global Catalog server
618 may host multiple domain trees in a forest.
619 Windows 2003 GC server will accept "" as the search
620 path to imply search all domain trees in the forest */
622 SAFE_FREE(ads->config.bind_path);
623 ads->config.bind_path = SMB_STRDUP("");
628 SAFE_FREE(ads->server.ldap_server);
629 SAFE_FREE(ads->server.realm);
632 TALLOC_FREE(gcs_list);
637 talloc_destroy(frame);
644 * Connect to the LDAP server
645 * @param ads Pointer to an existing ADS_STRUCT
646 * @return status of connection
648 ADS_STATUS ads_connect(ADS_STRUCT *ads)
650 int version = LDAP_VERSION3;
653 char addr[INET6_ADDRSTRLEN];
655 ZERO_STRUCT(ads->ldap);
656 ads->ldap.last_attempt = time_mono(NULL);
657 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
659 /* try with a user specified server */
661 if (DEBUGLEVEL >= 11) {
662 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
663 DEBUG(11,("ads_connect: entering\n"));
664 DEBUGADD(11,("%s\n", s));
668 if (ads->server.ldap_server) {
670 struct sockaddr_storage ss;
672 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
674 DEBUG(5,("ads_connect: unable to resolve name %s\n",
675 ads->server.ldap_server));
676 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
679 ok = ads_try_connect(ads, ads->server.gc, &ss);
684 /* The choice of which GC use is handled one level up in
685 ads_connect_gc(). If we continue on from here with
686 ads_find_dc() we will get GC searches on port 389 which
687 doesn't work. --jerry */
689 if (ads->server.gc == true) {
690 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
694 ntstatus = ads_find_dc(ads);
695 if (NT_STATUS_IS_OK(ntstatus)) {
699 status = ADS_ERROR_NT(ntstatus);
704 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
705 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
707 if (!ads->auth.user_name) {
708 /* Must use the userPrincipalName value here or sAMAccountName
709 and not servicePrincipalName; found by Guenther Deschner */
711 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
712 DEBUG(0,("ads_connect: asprintf fail.\n"));
713 ads->auth.user_name = NULL;
717 if (!ads->auth.realm) {
718 ads->auth.realm = SMB_STRDUP(ads->config.realm);
721 if (!ads->auth.kdc_server) {
722 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
723 ads->auth.kdc_server = SMB_STRDUP(addr);
726 /* If the caller() requested no LDAP bind, then we are done */
728 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
729 status = ADS_SUCCESS;
733 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
734 if (!ads->ldap.mem_ctx) {
735 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
739 /* Otherwise setup the TCP LDAP session */
741 ads->ldap.ld = ldap_open_with_timeout(addr,
743 ads->ldap.port, lp_ldap_timeout());
744 if (ads->ldap.ld == NULL) {
745 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
748 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
750 /* cache the successful connection for workgroup and realm */
751 if (ads_closest_dc(ads)) {
752 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
753 saf_store( ads->server.realm, ads->config.ldap_server_name);
756 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
758 if ( lp_ldap_ssl_ads() ) {
759 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
760 if (!ADS_ERR_OK(status)) {
765 /* fill in the current time and offsets */
767 status = ads_current_time( ads );
768 if ( !ADS_ERR_OK(status) ) {
772 /* Now do the bind */
774 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
775 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
779 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
780 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
784 status = ads_sasl_bind(ads);
787 if (DEBUGLEVEL >= 11) {
788 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
789 DEBUG(11,("ads_connect: leaving with: %s\n",
790 ads_errstr(status)));
791 DEBUGADD(11,("%s\n", s));
799 * Connect to the LDAP server using given credentials
800 * @param ads Pointer to an existing ADS_STRUCT
801 * @return status of connection
803 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
805 ads->auth.flags |= ADS_AUTH_USER_CREDS;
807 return ads_connect(ads);
811 * Disconnect the LDAP server
812 * @param ads Pointer to an existing ADS_STRUCT
814 void ads_disconnect(ADS_STRUCT *ads)
817 ldap_unbind(ads->ldap.ld);
820 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
821 ads->ldap.wrap_ops->disconnect(ads);
823 if (ads->ldap.mem_ctx) {
824 talloc_free(ads->ldap.mem_ctx);
826 ZERO_STRUCT(ads->ldap);
830 Duplicate a struct berval into talloc'ed memory
832 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
834 struct berval *value;
836 if (!in_val) return NULL;
838 value = talloc_zero(ctx, struct berval);
841 if (in_val->bv_len == 0) return value;
843 value->bv_len = in_val->bv_len;
844 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
850 Make a values list out of an array of (struct berval *)
852 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
853 const struct berval **in_vals)
855 struct berval **values;
858 if (!in_vals) return NULL;
859 for (i=0; in_vals[i]; i++)
861 values = talloc_zero_array(ctx, struct berval *, i+1);
862 if (!values) return NULL;
864 for (i=0; in_vals[i]; i++) {
865 values[i] = dup_berval(ctx, in_vals[i]);
871 UTF8-encode a values list out of an array of (char *)
873 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
879 if (!in_vals) return NULL;
880 for (i=0; in_vals[i]; i++)
882 values = talloc_zero_array(ctx, char *, i+1);
883 if (!values) return NULL;
885 for (i=0; in_vals[i]; i++) {
886 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
895 Pull a (char *) array out of a UTF8-encoded values list
897 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
901 size_t converted_size;
903 if (!in_vals) return NULL;
904 for (i=0; in_vals[i]; i++)
906 values = talloc_zero_array(ctx, char *, i+1);
907 if (!values) return NULL;
909 for (i=0; in_vals[i]; i++) {
910 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
912 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
913 "%s", strerror(errno)));
920 * Do a search with paged results. cookie must be null on the first
921 * call, and then returned on each subsequent call. It will be null
922 * again when the entire search is complete
923 * @param ads connection to ads server
924 * @param bind_path Base dn for the search
925 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
926 * @param expr Search expression - specified in local charset
927 * @param attrs Attributes to retrieve - specified in utf8 or ascii
928 * @param res ** which will contain results - free res* with ads_msgfree()
929 * @param count Number of entries retrieved on this page
930 * @param cookie The paged results cookie to be returned on subsequent calls
931 * @return status of search
933 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
934 const char *bind_path,
935 int scope, const char *expr,
936 const char **attrs, void *args,
938 int *count, struct berval **cookie)
941 char *utf8_expr, *utf8_path, **search_attrs = NULL;
942 size_t converted_size;
943 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
944 BerElement *cookie_be = NULL;
945 struct berval *cookie_bv= NULL;
946 BerElement *ext_be = NULL;
947 struct berval *ext_bv= NULL;
950 ads_control *external_control = (ads_control *) args;
954 if (!(ctx = talloc_init("ads_do_paged_search_args")))
955 return ADS_ERROR(LDAP_NO_MEMORY);
957 /* 0 means the conversion worked but the result was empty
958 so we only fail if it's -1. In any case, it always
959 at least nulls out the dest */
960 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
961 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
967 if (!attrs || !(*attrs))
970 /* This would be the utf8-encoded version...*/
971 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
972 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
978 /* Paged results only available on ldap v3 or later */
979 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
980 if (version < LDAP_VERSION3) {
981 rc = LDAP_NOT_SUPPORTED;
985 cookie_be = ber_alloc_t(LBER_USE_DER);
987 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
988 ber_bvfree(*cookie); /* don't need it from last time */
991 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
993 ber_flatten(cookie_be, &cookie_bv);
994 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
995 PagedResults.ldctl_iscritical = (char) 1;
996 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
997 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
999 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1000 NoReferrals.ldctl_iscritical = (char) 0;
1001 NoReferrals.ldctl_value.bv_len = 0;
1002 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1004 if (external_control &&
1005 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1006 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1008 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1009 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1011 /* win2k does not accept a ldctl_value beeing passed in */
1013 if (external_control->val != 0) {
1015 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1016 rc = LDAP_NO_MEMORY;
1020 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1021 rc = LDAP_NO_MEMORY;
1024 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1025 rc = LDAP_NO_MEMORY;
1029 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1030 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1033 ExternalCtrl.ldctl_value.bv_len = 0;
1034 ExternalCtrl.ldctl_value.bv_val = NULL;
1037 controls[0] = &NoReferrals;
1038 controls[1] = &PagedResults;
1039 controls[2] = &ExternalCtrl;
1043 controls[0] = &NoReferrals;
1044 controls[1] = &PagedResults;
1048 /* we need to disable referrals as the openldap libs don't
1049 handle them and paged results at the same time. Using them
1050 together results in the result record containing the server
1051 page control being removed from the result list (tridge/jmcd)
1053 leaving this in despite the control that says don't generate
1054 referrals, in case the server doesn't support it (jmcd)
1056 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1058 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1059 search_attrs, 0, controls,
1060 NULL, LDAP_NO_LIMIT,
1061 (LDAPMessage **)res);
1063 ber_free(cookie_be, 1);
1064 ber_bvfree(cookie_bv);
1067 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1068 ldap_err2string(rc)));
1069 if (rc == LDAP_OTHER) {
1073 ret = ldap_parse_result(ads->ldap.ld,
1081 if (ret == LDAP_SUCCESS) {
1082 DEBUG(3, ("ldap_search_with_timeout(%s) "
1083 "error: %s\n", expr, ldap_errmsg));
1084 ldap_memfree(ldap_errmsg);
1090 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1091 NULL, &rcontrols, 0);
1097 for (i=0; rcontrols[i]; i++) {
1098 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1099 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1100 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1102 /* the berval is the cookie, but must be freed when
1104 if (cookie_bv->bv_len) /* still more to do */
1105 *cookie=ber_bvdup(cookie_bv);
1108 ber_bvfree(cookie_bv);
1109 ber_free(cookie_be, 1);
1113 ldap_controls_free(rcontrols);
1116 talloc_destroy(ctx);
1119 ber_free(ext_be, 1);
1126 /* if/when we decide to utf8-encode attrs, take out this next line */
1127 TALLOC_FREE(search_attrs);
1129 return ADS_ERROR(rc);
1132 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1133 int scope, const char *expr,
1134 const char **attrs, LDAPMessage **res,
1135 int *count, struct berval **cookie)
1137 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1142 * Get all results for a search. This uses ads_do_paged_search() to return
1143 * all entries in a large search.
1144 * @param ads connection to ads server
1145 * @param bind_path Base dn for the search
1146 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1147 * @param expr Search expression
1148 * @param attrs Attributes to retrieve
1149 * @param res ** which will contain results - free res* with ads_msgfree()
1150 * @return status of search
1152 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1153 int scope, const char *expr,
1154 const char **attrs, void *args,
1157 struct berval *cookie = NULL;
1162 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1165 if (!ADS_ERR_OK(status))
1168 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1170 LDAPMessage *res2 = NULL;
1171 LDAPMessage *msg, *next;
1173 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1174 attrs, args, &res2, &count, &cookie);
1175 if (!ADS_ERR_OK(status)) {
1176 /* Ensure we free all collected results */
1177 ads_msgfree(ads, *res);
1182 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1183 that this works on all ldap libs, but I have only tested with openldap */
1184 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1185 next = ads_next_message(ads, msg);
1186 ldap_add_result_entry((LDAPMessage **)res, msg);
1188 /* note that we do not free res2, as the memory is now
1189 part of the main returned list */
1192 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1193 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1199 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1200 int scope, const char *expr,
1201 const char **attrs, LDAPMessage **res)
1203 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1206 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1207 int scope, const char *expr,
1208 const char **attrs, uint32_t sd_flags,
1213 args.control = ADS_SD_FLAGS_OID;
1214 args.val = sd_flags;
1215 args.critical = True;
1217 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1222 * Run a function on all results for a search. Uses ads_do_paged_search() and
1223 * runs the function as each page is returned, using ads_process_results()
1224 * @param ads connection to ads server
1225 * @param bind_path Base dn for the search
1226 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1227 * @param expr Search expression - specified in local charset
1228 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1229 * @param fn Function which takes attr name, values list, and data_area
1230 * @param data_area Pointer which is passed to function on each call
1231 * @return status of search
1233 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1234 int scope, const char *expr, const char **attrs,
1235 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1238 struct berval *cookie = NULL;
1243 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1246 if (!ADS_ERR_OK(status)) return status;
1248 ads_process_results(ads, res, fn, data_area);
1249 ads_msgfree(ads, res);
1252 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1253 &res, &count, &cookie);
1255 if (!ADS_ERR_OK(status)) break;
1257 ads_process_results(ads, res, fn, data_area);
1258 ads_msgfree(ads, res);
1265 * Do a search with a timeout.
1266 * @param ads connection to ads server
1267 * @param bind_path Base dn for the search
1268 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1269 * @param expr Search expression
1270 * @param attrs Attributes to retrieve
1271 * @param res ** which will contain results - free res* with ads_msgfree()
1272 * @return status of search
1274 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1276 const char **attrs, LDAPMessage **res)
1279 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1280 size_t converted_size;
1284 if (!(ctx = talloc_init("ads_do_search"))) {
1285 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1286 return ADS_ERROR(LDAP_NO_MEMORY);
1289 /* 0 means the conversion worked but the result was empty
1290 so we only fail if it's negative. In any case, it always
1291 at least nulls out the dest */
1292 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1293 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1295 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1296 rc = LDAP_NO_MEMORY;
1300 if (!attrs || !(*attrs))
1301 search_attrs = NULL;
1303 /* This would be the utf8-encoded version...*/
1304 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1305 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1307 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1308 rc = LDAP_NO_MEMORY;
1313 /* see the note in ads_do_paged_search - we *must* disable referrals */
1314 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1316 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1317 search_attrs, 0, NULL, NULL,
1319 (LDAPMessage **)res);
1321 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1322 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1327 talloc_destroy(ctx);
1328 /* if/when we decide to utf8-encode attrs, take out this next line */
1329 TALLOC_FREE(search_attrs);
1330 return ADS_ERROR(rc);
1333 * Do a general ADS search
1334 * @param ads connection to ads server
1335 * @param res ** which will contain results - free res* with ads_msgfree()
1336 * @param expr Search expression
1337 * @param attrs Attributes to retrieve
1338 * @return status of search
1340 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1341 const char *expr, const char **attrs)
1343 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1348 * Do a search on a specific DistinguishedName
1349 * @param ads connection to ads server
1350 * @param res ** which will contain results - free res* with ads_msgfree()
1351 * @param dn DistinguishName to search
1352 * @param attrs Attributes to retrieve
1353 * @return status of search
1355 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1356 const char *dn, const char **attrs)
1358 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1363 * Free up memory from a ads_search
1364 * @param ads connection to ads server
1365 * @param msg Search results to free
1367 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1374 * Get a dn from search results
1375 * @param ads connection to ads server
1376 * @param msg Search result
1379 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1381 char *utf8_dn, *unix_dn;
1382 size_t converted_size;
1384 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1387 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1391 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1392 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1396 ldap_memfree(utf8_dn);
1401 * Get the parent from a dn
1402 * @param dn the dn to return the parent from
1403 * @return parent dn string
1405 char *ads_parent_dn(const char *dn)
1413 p = strchr(dn, ',');
1423 * Find a machine account given a hostname
1424 * @param ads connection to ads server
1425 * @param res ** which will contain results - free res* with ads_msgfree()
1426 * @param host Hostname to search for
1427 * @return status of search
1429 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1430 const char *machine)
1434 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1438 /* the easiest way to find a machine account anywhere in the tree
1439 is to look for hostname$ */
1440 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1441 DEBUG(1, ("asprintf failed!\n"));
1442 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1445 status = ads_search(ads, res, expr, attrs);
1451 * Initialize a list of mods to be used in a modify request
1452 * @param ctx An initialized TALLOC_CTX
1453 * @return allocated ADS_MODLIST
1455 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1457 #define ADS_MODLIST_ALLOC_SIZE 10
1460 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1461 /* -1 is safety to make sure we don't go over the end.
1462 need to reset it to NULL before doing ldap modify */
1463 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1465 return (ADS_MODLIST)mods;
1470 add an attribute to the list, with values list already constructed
1472 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1473 int mod_op, const char *name,
1474 const void *_invals)
1477 LDAPMod **modlist = (LDAPMod **) *mods;
1478 struct berval **ber_values = NULL;
1479 char **char_values = NULL;
1482 mod_op = LDAP_MOD_DELETE;
1484 if (mod_op & LDAP_MOD_BVALUES) {
1485 const struct berval **b;
1486 b = discard_const_p(const struct berval *, _invals);
1487 ber_values = ads_dup_values(ctx, b);
1490 c = discard_const_p(const char *, _invals);
1491 char_values = ads_push_strvals(ctx, c);
1495 /* find the first empty slot */
1496 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1498 if (modlist[curmod] == (LDAPMod *) -1) {
1499 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1500 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1501 return ADS_ERROR(LDAP_NO_MEMORY);
1502 memset(&modlist[curmod], 0,
1503 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1504 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1505 *mods = (ADS_MODLIST)modlist;
1508 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1509 return ADS_ERROR(LDAP_NO_MEMORY);
1510 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1511 if (mod_op & LDAP_MOD_BVALUES) {
1512 modlist[curmod]->mod_bvalues = ber_values;
1513 } else if (mod_op & LDAP_MOD_DELETE) {
1514 modlist[curmod]->mod_values = NULL;
1516 modlist[curmod]->mod_values = char_values;
1519 modlist[curmod]->mod_op = mod_op;
1520 return ADS_ERROR(LDAP_SUCCESS);
1524 * Add a single string value to a mod list
1525 * @param ctx An initialized TALLOC_CTX
1526 * @param mods An initialized ADS_MODLIST
1527 * @param name The attribute name to add
1528 * @param val The value to add - NULL means DELETE
1529 * @return ADS STATUS indicating success of add
1531 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1532 const char *name, const char *val)
1534 const char *values[2];
1540 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1541 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1545 * Add an array of string values to a mod list
1546 * @param ctx An initialized TALLOC_CTX
1547 * @param mods An initialized ADS_MODLIST
1548 * @param name The attribute name to add
1549 * @param vals The array of string values to add - NULL means DELETE
1550 * @return ADS STATUS indicating success of add
1552 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1553 const char *name, const char **vals)
1556 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1557 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1558 name, (const void **) vals);
1563 * Add a single ber-encoded value to a mod list
1564 * @param ctx An initialized TALLOC_CTX
1565 * @param mods An initialized ADS_MODLIST
1566 * @param name The attribute name to add
1567 * @param val The value to add - NULL means DELETE
1568 * @return ADS STATUS indicating success of add
1570 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1571 const char *name, const struct berval *val)
1573 const struct berval *values[2];
1578 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1579 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1580 name, (const void **) values);
1585 * Perform an ldap modify
1586 * @param ads connection to ads server
1587 * @param mod_dn DistinguishedName to modify
1588 * @param mods list of modifications to perform
1589 * @return status of modify
1591 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1594 char *utf8_dn = NULL;
1595 size_t converted_size;
1597 this control is needed to modify that contains a currently
1598 non-existent attribute (but allowable for the object) to run
1600 LDAPControl PermitModify = {
1601 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1604 LDAPControl *controls[2];
1606 controls[0] = &PermitModify;
1609 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1610 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1613 /* find the end of the list, marked by NULL or -1 */
1614 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1615 /* make sure the end of the list is NULL */
1617 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1618 (LDAPMod **) mods, controls, NULL);
1619 TALLOC_FREE(utf8_dn);
1620 return ADS_ERROR(ret);
1624 * Perform an ldap add
1625 * @param ads connection to ads server
1626 * @param new_dn DistinguishedName to add
1627 * @param mods list of attributes and values for DN
1628 * @return status of add
1630 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1633 char *utf8_dn = NULL;
1634 size_t converted_size;
1636 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1637 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1638 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1641 /* find the end of the list, marked by NULL or -1 */
1642 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1643 /* make sure the end of the list is NULL */
1646 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1647 TALLOC_FREE(utf8_dn);
1648 return ADS_ERROR(ret);
1652 * Delete a DistinguishedName
1653 * @param ads connection to ads server
1654 * @param new_dn DistinguishedName to delete
1655 * @return status of delete
1657 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1660 char *utf8_dn = NULL;
1661 size_t converted_size;
1662 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1663 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1664 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1667 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1668 TALLOC_FREE(utf8_dn);
1669 return ADS_ERROR(ret);
1673 * Build an org unit string
1674 * if org unit is Computers or blank then assume a container, otherwise
1675 * assume a / separated list of organisational units.
1676 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1677 * @param ads connection to ads server
1678 * @param org_unit Organizational unit
1679 * @return org unit string - caller must free
1681 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1685 if (!org_unit || !*org_unit) {
1687 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1689 /* samba4 might not yet respond to a wellknownobject-query */
1690 return ret ? ret : SMB_STRDUP("cn=Computers");
1693 if (strequal(org_unit, "Computers")) {
1694 return SMB_STRDUP("cn=Computers");
1697 /* jmcd: removed "\\" from the separation chars, because it is
1698 needed as an escape for chars like '#' which are valid in an
1700 return ads_build_path(org_unit, "/", "ou=", 1);
1704 * Get a org unit string for a well-known GUID
1705 * @param ads connection to ads server
1706 * @param wknguid Well known GUID
1707 * @return org unit string - caller must free
1709 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1712 LDAPMessage *res = NULL;
1713 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1714 **bind_dn_exp = NULL;
1715 const char *attrs[] = {"distinguishedName", NULL};
1716 int new_ln, wkn_ln, bind_ln, i;
1718 if (wknguid == NULL) {
1722 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1723 DEBUG(1, ("asprintf failed!\n"));
1727 status = ads_search_dn(ads, &res, base, attrs);
1728 if (!ADS_ERR_OK(status)) {
1729 DEBUG(1,("Failed while searching for: %s\n", base));
1733 if (ads_count_replies(ads, res) != 1) {
1737 /* substitute the bind-path from the well-known-guid-search result */
1738 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1743 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1748 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1753 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1755 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1758 new_ln = wkn_ln - bind_ln;
1760 ret = SMB_STRDUP(wkn_dn_exp[0]);
1765 for (i=1; i < new_ln; i++) {
1768 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1774 ret = SMB_STRDUP(s);
1783 ads_msgfree(ads, res);
1784 TALLOC_FREE(wkn_dn);
1786 ldap_value_free(wkn_dn_exp);
1789 ldap_value_free(bind_dn_exp);
1796 * Adds (appends) an item to an attribute array, rather then
1797 * replacing the whole list
1798 * @param ctx An initialized TALLOC_CTX
1799 * @param mods An initialized ADS_MODLIST
1800 * @param name name of the ldap attribute to append to
1801 * @param vals an array of values to add
1802 * @return status of addition
1805 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1806 const char *name, const char **vals)
1808 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1809 (const void *) vals);
1813 * Determines the an account's current KVNO via an LDAP lookup
1814 * @param ads An initialized ADS_STRUCT
1815 * @param account_name the NT samaccountname.
1816 * @return the kvno for the account, or -1 in case of a failure.
1819 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1821 LDAPMessage *res = NULL;
1822 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1824 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1825 char *dn_string = NULL;
1826 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1828 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1829 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1832 ret = ads_search(ads, &res, filter, attrs);
1834 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1835 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1836 ads_msgfree(ads, res);
1840 dn_string = ads_get_dn(ads, talloc_tos(), res);
1842 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1843 ads_msgfree(ads, res);
1846 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1847 TALLOC_FREE(dn_string);
1849 /* ---------------------------------------------------------
1850 * 0 is returned as a default KVNO from this point on...
1851 * This is done because Windows 2000 does not support key
1852 * version numbers. Chances are that a failure in the next
1853 * step is simply due to Windows 2000 being used for a
1854 * domain controller. */
1857 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1858 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1859 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1860 ads_msgfree(ads, res);
1865 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1866 ads_msgfree(ads, res);
1871 * Determines the computer account's current KVNO via an LDAP lookup
1872 * @param ads An initialized ADS_STRUCT
1873 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1874 * @return the kvno for the computer account, or -1 in case of a failure.
1877 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1879 char *computer_account = NULL;
1882 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1886 kvno = ads_get_kvno(ads, computer_account);
1887 free(computer_account);
1893 * This clears out all registered spn's for a given hostname
1894 * @param ads An initilaized ADS_STRUCT
1895 * @param machine_name the NetBIOS name of the computer.
1896 * @return 0 upon success, non-zero otherwise.
1899 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1902 LDAPMessage *res = NULL;
1904 const char *servicePrincipalName[1] = {NULL};
1905 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1906 char *dn_string = NULL;
1908 ret = ads_find_machine_acct(ads, &res, machine_name);
1909 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1910 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1911 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1912 ads_msgfree(ads, res);
1913 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1916 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1917 ctx = talloc_init("ads_clear_service_principal_names");
1919 ads_msgfree(ads, res);
1920 return ADS_ERROR(LDAP_NO_MEMORY);
1923 if (!(mods = ads_init_mods(ctx))) {
1924 talloc_destroy(ctx);
1925 ads_msgfree(ads, res);
1926 return ADS_ERROR(LDAP_NO_MEMORY);
1928 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1929 if (!ADS_ERR_OK(ret)) {
1930 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1931 ads_msgfree(ads, res);
1932 talloc_destroy(ctx);
1935 dn_string = ads_get_dn(ads, talloc_tos(), res);
1937 talloc_destroy(ctx);
1938 ads_msgfree(ads, res);
1939 return ADS_ERROR(LDAP_NO_MEMORY);
1941 ret = ads_gen_mod(ads, dn_string, mods);
1942 TALLOC_FREE(dn_string);
1943 if (!ADS_ERR_OK(ret)) {
1944 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1946 ads_msgfree(ads, res);
1947 talloc_destroy(ctx);
1951 ads_msgfree(ads, res);
1952 talloc_destroy(ctx);
1957 * @brief Search for an element in a string array.
1959 * @param[in] el_array The string array to search.
1961 * @param[in] num_el The number of elements in the string array.
1963 * @param[in] el The string to search.
1965 * @return True if found, false if not.
1967 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1971 if (el_array == NULL || num_el == 0 || el == NULL) {
1975 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1978 cmp = strcasecmp_m(el_array[i], el);
1988 * @brief This gets the service principal names of an existing computer account.
1990 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1992 * @param[in] ads The ADS context to use.
1994 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1995 * identify the computer account.
1997 * @param[in] spn_array A pointer to store the array for SPNs.
1999 * @param[in] num_spns The number of principals stored in the array.
2001 * @return 0 on success, or a ADS error if a failure occured.
2003 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2005 const char *machine_name,
2010 LDAPMessage *res = NULL;
2013 status = ads_find_machine_acct(ads,
2016 if (!ADS_ERR_OK(status)) {
2017 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2022 count = ads_count_replies(ads, res);
2024 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2028 *spn_array = ads_pull_strings(ads,
2031 "servicePrincipalName",
2035 ads_msgfree(ads, res);
2041 * This adds a service principal name to an existing computer account
2042 * (found by hostname) in AD.
2043 * @param ads An initialized ADS_STRUCT
2044 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2045 * @param my_fqdn The fully qualified DNS name of the machine
2046 * @param spn A string of the service principal to add, i.e. 'host'
2047 * @return 0 upon sucess, or non-zero if a failure occurs
2050 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
2051 const char *my_fqdn, const char *spn)
2055 LDAPMessage *res = NULL;
2058 char *dn_string = NULL;
2059 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
2061 ret = ads_find_machine_acct(ads, &res, machine_name);
2062 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2063 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2065 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2066 spn, machine_name, ads->config.realm));
2067 ads_msgfree(ads, res);
2068 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2071 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2072 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2073 ads_msgfree(ads, res);
2074 return ADS_ERROR(LDAP_NO_MEMORY);
2077 /* add short name spn */
2079 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2080 talloc_destroy(ctx);
2081 ads_msgfree(ads, res);
2082 return ADS_ERROR(LDAP_NO_MEMORY);
2084 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2085 ret = ADS_ERROR(LDAP_NO_MEMORY);
2088 servicePrincipalName[0] = psp1;
2090 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2091 psp1, machine_name));
2094 /* add fully qualified spn */
2096 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2097 ret = ADS_ERROR(LDAP_NO_MEMORY);
2100 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2101 ret = ADS_ERROR(LDAP_NO_MEMORY);
2104 servicePrincipalName[1] = psp2;
2106 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2107 psp2, machine_name));
2109 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2110 ret = ADS_ERROR(LDAP_NO_MEMORY);
2114 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2115 if (!ADS_ERR_OK(ret)) {
2116 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2120 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2121 ret = ADS_ERROR(LDAP_NO_MEMORY);
2125 ret = ads_gen_mod(ads, dn_string, mods);
2126 if (!ADS_ERR_OK(ret)) {
2127 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2133 ads_msgfree(ads, res);
2138 * adds a machine account to the ADS server
2139 * @param ads An intialized ADS_STRUCT
2140 * @param machine_name - the NetBIOS machine name of this account.
2141 * @param account_type A number indicating the type of account to create
2142 * @param org_unit The LDAP path in which to place this account
2143 * @return 0 upon success, or non-zero otherwise
2146 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2147 const char *org_unit)
2150 char *samAccountName, *controlstr;
2153 char *machine_escaped = NULL;
2155 const char *objectClass[] = {"top", "person", "organizationalPerson",
2156 "user", "computer", NULL};
2157 LDAPMessage *res = NULL;
2158 uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2159 UF_DONT_EXPIRE_PASSWD |\
2160 UF_ACCOUNTDISABLE );
2162 if (!(ctx = talloc_init("ads_add_machine_acct")))
2163 return ADS_ERROR(LDAP_NO_MEMORY);
2165 ret = ADS_ERROR(LDAP_NO_MEMORY);
2167 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2168 if (!machine_escaped) {
2172 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2173 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2175 if ( !new_dn || !samAccountName ) {
2179 #ifndef ENCTYPE_ARCFOUR_HMAC
2180 acct_control |= UF_USE_DES_KEY_ONLY;
2183 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2187 if (!(mods = ads_init_mods(ctx))) {
2191 ads_mod_str(ctx, &mods, "cn", machine_name);
2192 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2193 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2194 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2196 ret = ads_gen_add(ads, new_dn, mods);
2199 SAFE_FREE(machine_escaped);
2200 ads_msgfree(ads, res);
2201 talloc_destroy(ctx);
2207 * move a machine account to another OU on the ADS server
2208 * @param ads - An intialized ADS_STRUCT
2209 * @param machine_name - the NetBIOS machine name of this account.
2210 * @param org_unit - The LDAP path in which to place this account
2211 * @param moved - whether we moved the machine account (optional)
2212 * @return 0 upon success, or non-zero otherwise
2215 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2216 const char *org_unit, bool *moved)
2220 LDAPMessage *res = NULL;
2221 char *filter = NULL;
2222 char *computer_dn = NULL;
2224 char *computer_rdn = NULL;
2225 bool need_move = False;
2227 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2228 rc = ADS_ERROR(LDAP_NO_MEMORY);
2232 /* Find pre-existing machine */
2233 rc = ads_search(ads, &res, filter, NULL);
2234 if (!ADS_ERR_OK(rc)) {
2238 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2240 rc = ADS_ERROR(LDAP_NO_MEMORY);
2244 parent_dn = ads_parent_dn(computer_dn);
2245 if (strequal(parent_dn, org_unit)) {
2251 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2252 rc = ADS_ERROR(LDAP_NO_MEMORY);
2256 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2257 org_unit, 1, NULL, NULL);
2258 rc = ADS_ERROR(ldap_status);
2261 ads_msgfree(ads, res);
2263 TALLOC_FREE(computer_dn);
2264 SAFE_FREE(computer_rdn);
2266 if (!ADS_ERR_OK(rc)) {
2278 dump a binary result from ldap
2280 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2283 for (i=0; values[i]; i++) {
2284 printf("%s: ", field);
2285 for (j=0; j<values[i]->bv_len; j++) {
2286 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2292 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2295 for (i=0; values[i]; i++) {
2297 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2300 status = GUID_from_ndr_blob(&in, &guid);
2301 if (NT_STATUS_IS_OK(status)) {
2302 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2304 printf("%s: INVALID GUID\n", field);
2310 dump a sid result from ldap
2312 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2315 for (i=0; values[i]; i++) {
2318 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2321 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2326 dump ntSecurityDescriptor
2328 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2330 TALLOC_CTX *frame = talloc_stackframe();
2331 struct security_descriptor *psd;
2334 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2335 values[0]->bv_len, &psd);
2336 if (!NT_STATUS_IS_OK(status)) {
2337 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2338 nt_errstr(status)));
2344 ads_disp_sd(ads, talloc_tos(), psd);
2351 dump a string result from ldap
2353 static void dump_string(const char *field, char **values)
2356 for (i=0; values[i]; i++) {
2357 printf("%s: %s\n", field, values[i]);
2362 dump a field from LDAP on stdout
2366 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2371 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2373 {"objectGUID", False, dump_guid},
2374 {"netbootGUID", False, dump_guid},
2375 {"nTSecurityDescriptor", False, dump_sd},
2376 {"dnsRecord", False, dump_binary},
2377 {"objectSid", False, dump_sid},
2378 {"tokenGroups", False, dump_sid},
2379 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2380 {"tokengroupsGlobalandUniversal", False, dump_sid},
2381 {"mS-DS-CreatorSID", False, dump_sid},
2382 {"msExchMailboxGuid", False, dump_guid},
2387 if (!field) { /* must be end of an entry */
2392 for (i=0; handlers[i].name; i++) {
2393 if (strcasecmp_m(handlers[i].name, field) == 0) {
2394 if (!values) /* first time, indicate string or not */
2395 return handlers[i].string;
2396 handlers[i].handler(ads, field, (struct berval **) values);
2400 if (!handlers[i].name) {
2401 if (!values) /* first time, indicate string conversion */
2403 dump_string(field, (char **)values);
2409 * Dump a result from LDAP on stdout
2410 * used for debugging
2411 * @param ads connection to ads server
2412 * @param res Results to dump
2415 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2417 ads_process_results(ads, res, ads_dump_field, NULL);
2421 * Walk through results, calling a function for each entry found.
2422 * The function receives a field name, a berval * array of values,
2423 * and a data area passed through from the start. The function is
2424 * called once with null for field and values at the end of each
2426 * @param ads connection to ads server
2427 * @param res Results to process
2428 * @param fn Function for processing each result
2429 * @param data_area user-defined area to pass to function
2431 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2432 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2437 size_t converted_size;
2439 if (!(ctx = talloc_init("ads_process_results")))
2442 for (msg = ads_first_entry(ads, res); msg;
2443 msg = ads_next_entry(ads, msg)) {
2447 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2448 (LDAPMessage *)msg,&b);
2450 utf8_field=ldap_next_attribute(ads->ldap.ld,
2451 (LDAPMessage *)msg,b)) {
2452 struct berval **ber_vals;
2458 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2461 DEBUG(0,("ads_process_results: "
2462 "pull_utf8_talloc failed: %s",
2466 string = fn(ads, field, NULL, data_area);
2471 utf8_vals = ldap_get_values(ads->ldap.ld,
2472 (LDAPMessage *)msg, field);
2473 p = discard_const_p(const char *, utf8_vals);
2474 str_vals = ads_pull_strvals(ctx, p);
2475 fn(ads, field, (void **) str_vals, data_area);
2476 ldap_value_free(utf8_vals);
2478 ber_vals = ldap_get_values_len(ads->ldap.ld,
2479 (LDAPMessage *)msg, field);
2480 fn(ads, field, (void **) ber_vals, data_area);
2482 ldap_value_free_len(ber_vals);
2484 ldap_memfree(utf8_field);
2487 talloc_free_children(ctx);
2488 fn(ads, NULL, NULL, data_area); /* completed an entry */
2491 talloc_destroy(ctx);
2495 * count how many replies are in a LDAPMessage
2496 * @param ads connection to ads server
2497 * @param res Results to count
2498 * @return number of replies
2500 int ads_count_replies(ADS_STRUCT *ads, void *res)
2502 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2506 * pull the first entry from a ADS result
2507 * @param ads connection to ads server
2508 * @param res Results of search
2509 * @return first entry from result
2511 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2513 return ldap_first_entry(ads->ldap.ld, res);
2517 * pull the next entry from a ADS result
2518 * @param ads connection to ads server
2519 * @param res Results of search
2520 * @return next entry from result
2522 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2524 return ldap_next_entry(ads->ldap.ld, res);
2528 * pull the first message from a ADS result
2529 * @param ads connection to ads server
2530 * @param res Results of search
2531 * @return first message from result
2533 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2535 return ldap_first_message(ads->ldap.ld, res);
2539 * pull the next message from a ADS result
2540 * @param ads connection to ads server
2541 * @param res Results of search
2542 * @return next message from result
2544 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2546 return ldap_next_message(ads->ldap.ld, res);
2550 * pull a single string from a ADS result
2551 * @param ads connection to ads server
2552 * @param mem_ctx TALLOC_CTX to use for allocating result string
2553 * @param msg Results of search
2554 * @param field Attribute to retrieve
2555 * @return Result string in talloc context
2557 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2563 size_t converted_size;
2565 values = ldap_get_values(ads->ldap.ld, msg, field);
2569 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2574 ldap_value_free(values);
2579 * pull an array of strings from a ADS result
2580 * @param ads connection to ads server
2581 * @param mem_ctx TALLOC_CTX to use for allocating result string
2582 * @param msg Results of search
2583 * @param field Attribute to retrieve
2584 * @return Result strings in talloc context
2586 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2587 LDAPMessage *msg, const char *field,
2593 size_t converted_size;
2595 values = ldap_get_values(ads->ldap.ld, msg, field);
2599 *num_values = ldap_count_values(values);
2601 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2603 ldap_value_free(values);
2607 for (i=0;i<*num_values;i++) {
2608 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2611 ldap_value_free(values);
2617 ldap_value_free(values);
2622 * pull an array of strings from a ADS result
2623 * (handle large multivalue attributes with range retrieval)
2624 * @param ads connection to ads server
2625 * @param mem_ctx TALLOC_CTX to use for allocating result string
2626 * @param msg Results of search
2627 * @param field Attribute to retrieve
2628 * @param current_strings strings returned by a previous call to this function
2629 * @param next_attribute The next query should ask for this attribute
2630 * @param num_values How many values did we get this time?
2631 * @param more_values Are there more values to get?
2632 * @return Result strings in talloc context
2634 char **ads_pull_strings_range(ADS_STRUCT *ads,
2635 TALLOC_CTX *mem_ctx,
2636 LDAPMessage *msg, const char *field,
2637 char **current_strings,
2638 const char **next_attribute,
2639 size_t *num_strings,
2643 char *expected_range_attrib, *range_attr;
2644 BerElement *ptr = NULL;
2647 size_t num_new_strings;
2648 unsigned long int range_start;
2649 unsigned long int range_end;
2651 /* we might have been given the whole lot anyway */
2652 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2653 *more_strings = False;
2657 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2659 /* look for Range result */
2660 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2662 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2663 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2664 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2672 /* nothing here - this field is just empty */
2673 *more_strings = False;
2677 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2678 &range_start, &range_end) == 2) {
2679 *more_strings = True;
2681 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2682 &range_start) == 1) {
2683 *more_strings = False;
2685 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2687 ldap_memfree(range_attr);
2688 *more_strings = False;
2693 if ((*num_strings) != range_start) {
2694 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2695 " - aborting range retreival\n",
2696 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2697 ldap_memfree(range_attr);
2698 *more_strings = False;
2702 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2704 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2705 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2706 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2707 range_attr, (unsigned long int)range_end - range_start + 1,
2708 (unsigned long int)num_new_strings));
2709 ldap_memfree(range_attr);
2710 *more_strings = False;
2714 strings = talloc_realloc(mem_ctx, current_strings, char *,
2715 *num_strings + num_new_strings);
2717 if (strings == NULL) {
2718 ldap_memfree(range_attr);
2719 *more_strings = False;
2723 if (new_strings && num_new_strings) {
2724 memcpy(&strings[*num_strings], new_strings,
2725 sizeof(*new_strings) * num_new_strings);
2728 (*num_strings) += num_new_strings;
2730 if (*more_strings) {
2731 *next_attribute = talloc_asprintf(mem_ctx,
2736 if (!*next_attribute) {
2737 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2738 ldap_memfree(range_attr);
2739 *more_strings = False;
2744 ldap_memfree(range_attr);
2750 * pull a single uint32_t from a ADS result
2751 * @param ads connection to ads server
2752 * @param msg Results of search
2753 * @param field Attribute to retrieve
2754 * @param v Pointer to int to store result
2755 * @return boolean inidicating success
2757 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2762 values = ldap_get_values(ads->ldap.ld, msg, field);
2766 ldap_value_free(values);
2770 *v = atoi(values[0]);
2771 ldap_value_free(values);
2776 * pull a single objectGUID from an ADS result
2777 * @param ads connection to ADS server
2778 * @param msg results of search
2779 * @param guid 37-byte area to receive text guid
2780 * @return boolean indicating success
2782 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2787 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2792 status = GUID_from_ndr_blob(&blob, guid);
2793 talloc_free(blob.data);
2794 return NT_STATUS_IS_OK(status);
2799 * pull a single struct dom_sid from a ADS result
2800 * @param ads connection to ads server
2801 * @param msg Results of search
2802 * @param field Attribute to retrieve
2803 * @param sid Pointer to sid to store result
2804 * @return boolean inidicating success
2806 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2807 struct dom_sid *sid)
2809 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2813 * pull an array of struct dom_sids from a ADS result
2814 * @param ads connection to ads server
2815 * @param mem_ctx TALLOC_CTX for allocating sid array
2816 * @param msg Results of search
2817 * @param field Attribute to retrieve
2818 * @param sids pointer to sid array to allocate
2819 * @return the count of SIDs pulled
2821 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2822 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2824 struct berval **values;
2828 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2833 for (i=0; values[i]; i++)
2837 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2839 ldap_value_free_len(values);
2847 for (i=0; values[i]; i++) {
2848 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2850 DEBUG(10, ("pulling SID: %s\n",
2851 sid_string_dbg(&(*sids)[count])));
2856 ldap_value_free_len(values);
2861 * pull a struct security_descriptor from a ADS result
2862 * @param ads connection to ads server
2863 * @param mem_ctx TALLOC_CTX for allocating sid array
2864 * @param msg Results of search
2865 * @param field Attribute to retrieve
2866 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2867 * @return boolean inidicating success
2869 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2870 LDAPMessage *msg, const char *field,
2871 struct security_descriptor **sd)
2873 struct berval **values;
2876 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2878 if (!values) return false;
2882 status = unmarshall_sec_desc(mem_ctx,
2883 (uint8_t *)values[0]->bv_val,
2884 values[0]->bv_len, sd);
2885 if (!NT_STATUS_IS_OK(status)) {
2886 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2887 nt_errstr(status)));
2892 ldap_value_free_len(values);
2897 * in order to support usernames longer than 21 characters we need to
2898 * use both the sAMAccountName and the userPrincipalName attributes
2899 * It seems that not all users have the userPrincipalName attribute set
2901 * @param ads connection to ads server
2902 * @param mem_ctx TALLOC_CTX for allocating sid array
2903 * @param msg Results of search
2904 * @return the username
2906 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2912 /* lookup_name() only works on the sAMAccountName to
2913 returning the username portion of userPrincipalName
2914 breaks winbindd_getpwnam() */
2916 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2917 if (ret && (p = strchr_m(ret, '@'))) {
2922 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2927 * find the update serial number - this is the core of the ldap cache
2928 * @param ads connection to ads server
2929 * @param ads connection to ADS server
2930 * @param usn Pointer to retrieved update serial number
2931 * @return status of search
2933 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2935 const char *attrs[] = {"highestCommittedUSN", NULL};
2939 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2940 if (!ADS_ERR_OK(status))
2943 if (ads_count_replies(ads, res) != 1) {
2944 ads_msgfree(ads, res);
2945 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2948 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2949 ads_msgfree(ads, res);
2950 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2953 ads_msgfree(ads, res);
2957 /* parse a ADS timestring - typical string is
2958 '20020917091222.0Z0' which means 09:12.22 17th September
2960 static time_t ads_parse_time(const char *str)
2966 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2967 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2968 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2977 /********************************************************************
2978 ********************************************************************/
2980 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2982 const char *attrs[] = {"currentTime", NULL};
2987 ADS_STRUCT *ads_s = ads;
2989 if (!(ctx = talloc_init("ads_current_time"))) {
2990 return ADS_ERROR(LDAP_NO_MEMORY);
2993 /* establish a new ldap tcp session if necessary */
2995 if ( !ads->ldap.ld ) {
2996 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2997 ads->server.ldap_server )) == NULL )
3001 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3002 status = ads_connect( ads_s );
3003 if ( !ADS_ERR_OK(status))
3007 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3008 if (!ADS_ERR_OK(status)) {
3012 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
3014 ads_msgfree(ads_s, res);
3015 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3019 /* but save the time and offset in the original ADS_STRUCT */
3021 ads->config.current_time = ads_parse_time(timestr);
3023 if (ads->config.current_time != 0) {
3024 ads->auth.time_offset = ads->config.current_time - time(NULL);
3025 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3028 ads_msgfree(ads, res);
3030 status = ADS_SUCCESS;
3033 /* free any temporary ads connections */
3034 if ( ads_s != ads ) {
3035 ads_destroy( &ads_s );
3037 talloc_destroy(ctx);
3042 /********************************************************************
3043 ********************************************************************/
3045 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3047 const char *attrs[] = {"domainFunctionality", NULL};
3050 ADS_STRUCT *ads_s = ads;
3052 *val = DS_DOMAIN_FUNCTION_2000;
3054 /* establish a new ldap tcp session if necessary */
3056 if ( !ads->ldap.ld ) {
3057 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3058 ads->server.ldap_server )) == NULL )
3060 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3063 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3064 status = ads_connect( ads_s );
3065 if ( !ADS_ERR_OK(status))
3069 /* If the attribute does not exist assume it is a Windows 2000
3070 functional domain */
3072 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3073 if (!ADS_ERR_OK(status)) {
3074 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3075 status = ADS_SUCCESS;
3080 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3081 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3083 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3086 ads_msgfree(ads, res);
3089 /* free any temporary ads connections */
3090 if ( ads_s != ads ) {
3091 ads_destroy( &ads_s );
3098 * find the domain sid for our domain
3099 * @param ads connection to ads server
3100 * @param sid Pointer to domain sid
3101 * @return status of search
3103 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3105 const char *attrs[] = {"objectSid", NULL};
3109 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3111 if (!ADS_ERR_OK(rc)) return rc;
3112 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3113 ads_msgfree(ads, res);
3114 return ADS_ERROR_SYSTEM(ENOENT);
3116 ads_msgfree(ads, res);
3122 * find our site name
3123 * @param ads connection to ads server
3124 * @param mem_ctx Pointer to talloc context
3125 * @param site_name Pointer to the sitename
3126 * @return status of search
3128 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3132 const char *dn, *service_name;
3133 const char *attrs[] = { "dsServiceName", NULL };
3135 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3136 if (!ADS_ERR_OK(status)) {
3140 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3141 if (service_name == NULL) {
3142 ads_msgfree(ads, res);
3143 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3146 ads_msgfree(ads, res);
3148 /* go up three levels */
3149 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3151 return ADS_ERROR(LDAP_NO_MEMORY);
3154 *site_name = talloc_strdup(mem_ctx, dn);
3155 if (*site_name == NULL) {
3156 return ADS_ERROR(LDAP_NO_MEMORY);
3161 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3166 * find the site dn where a machine resides
3167 * @param ads connection to ads server
3168 * @param mem_ctx Pointer to talloc context
3169 * @param computer_name name of the machine
3170 * @param site_name Pointer to the sitename
3171 * @return status of search
3173 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3177 const char *parent, *filter;
3178 char *config_context = NULL;
3181 /* shortcut a query */
3182 if (strequal(computer_name, ads->config.ldap_server_name)) {
3183 return ads_site_dn(ads, mem_ctx, site_dn);
3186 status = ads_config_path(ads, mem_ctx, &config_context);
3187 if (!ADS_ERR_OK(status)) {
3191 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3192 if (filter == NULL) {
3193 return ADS_ERROR(LDAP_NO_MEMORY);
3196 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3197 filter, NULL, &res);
3198 if (!ADS_ERR_OK(status)) {
3202 if (ads_count_replies(ads, res) != 1) {
3203 ads_msgfree(ads, res);
3204 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3207 dn = ads_get_dn(ads, mem_ctx, res);
3209 ads_msgfree(ads, res);
3210 return ADS_ERROR(LDAP_NO_MEMORY);
3213 /* go up three levels */
3214 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3215 if (parent == NULL) {
3216 ads_msgfree(ads, res);
3218 return ADS_ERROR(LDAP_NO_MEMORY);
3221 *site_dn = talloc_strdup(mem_ctx, parent);
3222 if (*site_dn == NULL) {
3223 ads_msgfree(ads, res);
3225 return ADS_ERROR(LDAP_NO_MEMORY);
3229 ads_msgfree(ads, res);
3235 * get the upn suffixes for a domain
3236 * @param ads connection to ads server
3237 * @param mem_ctx Pointer to talloc context
3238 * @param suffixes Pointer to an array of suffixes
3239 * @param num_suffixes Pointer to the number of suffixes
3240 * @return status of search
3242 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3247 char *config_context = NULL;
3248 const char *attrs[] = { "uPNSuffixes", NULL };
3250 status = ads_config_path(ads, mem_ctx, &config_context);
3251 if (!ADS_ERR_OK(status)) {
3255 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3257 return ADS_ERROR(LDAP_NO_MEMORY);
3260 status = ads_search_dn(ads, &res, base, attrs);
3261 if (!ADS_ERR_OK(status)) {
3265 if (ads_count_replies(ads, res) != 1) {
3266 ads_msgfree(ads, res);
3267 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3270 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3271 if ((*suffixes) == NULL) {
3272 ads_msgfree(ads, res);
3273 return ADS_ERROR(LDAP_NO_MEMORY);
3276 ads_msgfree(ads, res);
3282 * get the joinable ous for a domain
3283 * @param ads connection to ads server
3284 * @param mem_ctx Pointer to talloc context
3285 * @param ous Pointer to an array of ous
3286 * @param num_ous Pointer to the number of ous
3287 * @return status of search
3289 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3290 TALLOC_CTX *mem_ctx,
3295 LDAPMessage *res = NULL;
3296 LDAPMessage *msg = NULL;
3297 const char *attrs[] = { "dn", NULL };
3300 status = ads_search(ads, &res,
3301 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3303 if (!ADS_ERR_OK(status)) {
3307 count = ads_count_replies(ads, res);
3309 ads_msgfree(ads, res);
3310 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3313 for (msg = ads_first_entry(ads, res); msg;
3314 msg = ads_next_entry(ads, msg)) {
3315 const char **p = discard_const_p(const char *, *ous);
3318 dn = ads_get_dn(ads, talloc_tos(), msg);
3320 ads_msgfree(ads, res);
3321 return ADS_ERROR(LDAP_NO_MEMORY);
3324 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3326 ads_msgfree(ads, res);
3327 return ADS_ERROR(LDAP_NO_MEMORY);
3331 *ous = discard_const_p(char *, p);
3334 ads_msgfree(ads, res);
3341 * pull a struct dom_sid from an extended dn string
3342 * @param mem_ctx TALLOC_CTX
3343 * @param extended_dn string
3344 * @param flags string type of extended_dn
3345 * @param sid pointer to a struct dom_sid
3346 * @return NT_STATUS_OK on success,
3347 * NT_INVALID_PARAMETER on error,
3348 * NT_STATUS_NOT_FOUND if no SID present
3350 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3351 const char *extended_dn,
3352 enum ads_extended_dn_flags flags,
3353 struct dom_sid *sid)
3358 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3361 /* otherwise extended_dn gets stripped off */
3362 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3363 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3366 * ADS_EXTENDED_DN_HEX_STRING:
3367 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3369 * ADS_EXTENDED_DN_STRING (only with w2k3):
3370 * <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
3372 * Object with no SID, such as an Exchange Public Folder
3373 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3376 p = strchr(dn, ';');
3378 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3381 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3382 DEBUG(5,("No SID present in extended dn\n"));
3383 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3386 p += strlen(";<SID=");
3390 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3395 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3399 case ADS_EXTENDED_DN_STRING:
3400 if (!string_to_sid(sid, p)) {
3401 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3404 case ADS_EXTENDED_DN_HEX_STRING: {
3408 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3410 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3413 if (!sid_parse(buf, buf_len, sid)) {
3414 DEBUG(10,("failed to parse sid\n"));
3415 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3420 DEBUG(10,("unknown extended dn format\n"));
3421 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3424 return ADS_ERROR_NT(NT_STATUS_OK);
3427 /********************************************************************
3428 ********************************************************************/
3430 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3432 LDAPMessage *res = NULL;
3437 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3438 if (!ADS_ERR_OK(status)) {
3439 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3440 lp_netbios_name()));
3444 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3445 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3449 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3450 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3454 ads_msgfree(ads, res);
3459 /********************************************************************
3460 ********************************************************************/
3462 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3464 LDAPMessage *res = NULL;
3469 status = ads_find_machine_acct(ads, &res, machine_name);
3470 if (!ADS_ERR_OK(status)) {
3471 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3472 lp_netbios_name()));
3476 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3477 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3481 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3482 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3486 ads_msgfree(ads, res);
3491 /********************************************************************
3492 ********************************************************************/
3494 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3496 LDAPMessage *res = NULL;
3501 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3502 if (!ADS_ERR_OK(status)) {
3503 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3504 lp_netbios_name()));
3508 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3509 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3513 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3514 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3518 ads_msgfree(ads, res);
3525 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3528 * Join a machine to a realm
3529 * Creates the machine account and sets the machine password
3530 * @param ads connection to ads server
3531 * @param machine name of host to add
3532 * @param org_unit Organizational unit to place machine in
3533 * @return status of join
3535 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3536 uint32_t account_type, const char *org_unit)
3539 LDAPMessage *res = NULL;
3542 /* machine name must be lowercase */
3543 machine = SMB_STRDUP(machine_name);
3544 strlower_m(machine);
3547 status = ads_find_machine_acct(ads, (void **)&res, machine);
3548 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3549 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3550 status = ads_leave_realm(ads, machine);
3551 if (!ADS_ERR_OK(status)) {
3552 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3553 machine, ads->config.realm));
3558 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3559 if (!ADS_ERR_OK(status)) {
3560 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3565 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3566 if (!ADS_ERR_OK(status)) {
3567 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3573 ads_msgfree(ads, res);
3580 * Delete a machine from the realm
3581 * @param ads connection to ads server
3582 * @param hostname Machine to remove
3583 * @return status of delete
3585 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3590 char *hostnameDN, *host;
3592 LDAPControl ldap_control;
3593 LDAPControl * pldap_control[2] = {NULL, NULL};
3595 pldap_control[0] = &ldap_control;
3596 memset(&ldap_control, 0, sizeof(LDAPControl));
3597 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3599 /* hostname must be lowercase */
3600 host = SMB_STRDUP(hostname);
3601 if (!strlower_m(host)) {
3603 return ADS_ERROR_SYSTEM(EINVAL);
3606 status = ads_find_machine_acct(ads, &res, host);
3607 if (!ADS_ERR_OK(status)) {
3608 DEBUG(0, ("Host account for %s does not exist.\n", host));
3613 msg = ads_first_entry(ads, res);
3616 return ADS_ERROR_SYSTEM(ENOENT);
3619 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3620 if (hostnameDN == NULL) {
3622 return ADS_ERROR_SYSTEM(ENOENT);
3625 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3627 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3629 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3632 if (rc != LDAP_SUCCESS) {
3633 const char *attrs[] = { "cn", NULL };
3634 LDAPMessage *msg_sub;
3636 /* we only search with scope ONE, we do not expect any further
3637 * objects to be created deeper */
3639 status = ads_do_search_retry(ads, hostnameDN,
3640 LDAP_SCOPE_ONELEVEL,
3641 "(objectclass=*)", attrs, &res);
3643 if (!ADS_ERR_OK(status)) {
3645 TALLOC_FREE(hostnameDN);
3649 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3650 msg_sub = ads_next_entry(ads, msg_sub)) {
3654 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3656 TALLOC_FREE(hostnameDN);
3657 return ADS_ERROR(LDAP_NO_MEMORY);
3660 status = ads_del_dn(ads, dn);
3661 if (!ADS_ERR_OK(status)) {
3662 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3665 TALLOC_FREE(hostnameDN);
3672 /* there should be no subordinate objects anymore */
3673 status = ads_do_search_retry(ads, hostnameDN,
3674 LDAP_SCOPE_ONELEVEL,
3675 "(objectclass=*)", attrs, &res);
3677 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3679 TALLOC_FREE(hostnameDN);
3683 /* delete hostnameDN now */
3684 status = ads_del_dn(ads, hostnameDN);
3685 if (!ADS_ERR_OK(status)) {
3687 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3688 TALLOC_FREE(hostnameDN);
3693 TALLOC_FREE(hostnameDN);
3695 status = ads_find_machine_acct(ads, &res, host);
3696 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3697 DEBUG(3, ("Failed to remove host account.\n"));
3707 * pull all token-sids from an LDAP dn
3708 * @param ads connection to ads server
3709 * @param mem_ctx TALLOC_CTX for allocating sid array
3710 * @param dn of LDAP object
3711 * @param user_sid pointer to struct dom_sid (objectSid)
3712 * @param primary_group_sid pointer to struct dom_sid (self composed)
3713 * @param sids pointer to sid array to allocate
3714 * @param num_sids counter of SIDs pulled
3715 * @return status of token query
3717 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3718 TALLOC_CTX *mem_ctx,
3720 struct dom_sid *user_sid,
3721 struct dom_sid *primary_group_sid,
3722 struct dom_sid **sids,
3726 LDAPMessage *res = NULL;
3728 size_t tmp_num_sids;
3729 struct dom_sid *tmp_sids;
3730 struct dom_sid tmp_user_sid;
3731 struct dom_sid tmp_primary_group_sid;
3733 const char *attrs[] = {
3740 status = ads_search_retry_dn(ads, &res, dn, attrs);
3741 if (!ADS_ERR_OK(status)) {
3745 count = ads_count_replies(ads, res);
3747 ads_msgfree(ads, res);
3748 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3751 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3752 ads_msgfree(ads, res);
3753 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3756 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3757 ads_msgfree(ads, res);
3758 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3762 /* hack to compose the primary group sid without knowing the
3765 struct dom_sid domsid;
3767 sid_copy(&domsid, &tmp_user_sid);
3769 if (!sid_split_rid(&domsid, NULL)) {
3770 ads_msgfree(ads, res);
3771 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3774 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3775 ads_msgfree(ads, res);
3776 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3780 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3782 if (tmp_num_sids == 0 || !tmp_sids) {
3783 ads_msgfree(ads, res);
3784 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3788 *num_sids = tmp_num_sids;
3796 *user_sid = tmp_user_sid;
3799 if (primary_group_sid) {
3800 *primary_group_sid = tmp_primary_group_sid;
3803 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3805 ads_msgfree(ads, res);
3806 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3810 * Find a sAMAccoutName in LDAP
3811 * @param ads connection to ads server
3812 * @param mem_ctx TALLOC_CTX for allocating sid array
3813 * @param samaccountname to search
3814 * @param uac_ret uint32_t pointer userAccountControl attribute value
3815 * @param dn_ret pointer to dn
3816 * @return status of token query
3818 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3819 TALLOC_CTX *mem_ctx,
3820 const char *samaccountname,
3822 const char **dn_ret)
3825 const char *attrs[] = { "userAccountControl", NULL };
3827 LDAPMessage *res = NULL;
3831 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3833 if (filter == NULL) {
3834 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3838 status = ads_do_search_all(ads, ads->config.bind_path,
3840 filter, attrs, &res);
3842 if (!ADS_ERR_OK(status)) {
3846 if (ads_count_replies(ads, res) != 1) {
3847 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3851 dn = ads_get_dn(ads, talloc_tos(), res);
3853 status = ADS_ERROR(LDAP_NO_MEMORY);
3857 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3858 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3867 *dn_ret = talloc_strdup(mem_ctx, dn);
3869 status = ADS_ERROR(LDAP_NO_MEMORY);
3875 ads_msgfree(ads, res);
3881 * find our configuration path
3882 * @param ads connection to ads server
3883 * @param mem_ctx Pointer to talloc context
3884 * @param config_path Pointer to the config path
3885 * @return status of search
3887 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3888 TALLOC_CTX *mem_ctx,
3892 LDAPMessage *res = NULL;
3893 const char *config_context = NULL;
3894 const char *attrs[] = { "configurationNamingContext", NULL };
3896 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3897 "(objectclass=*)", attrs, &res);
3898 if (!ADS_ERR_OK(status)) {
3902 config_context = ads_pull_string(ads, mem_ctx, res,
3903 "configurationNamingContext");
3904 ads_msgfree(ads, res);
3905 if (!config_context) {
3906 return ADS_ERROR(LDAP_NO_MEMORY);
3910 *config_path = talloc_strdup(mem_ctx, config_context);
3911 if (!*config_path) {
3912 return ADS_ERROR(LDAP_NO_MEMORY);
3916 return ADS_ERROR(LDAP_SUCCESS);
3920 * find the displayName of an extended right
3921 * @param ads connection to ads server
3922 * @param config_path The config path
3923 * @param mem_ctx Pointer to talloc context
3924 * @param GUID struct of the rightsGUID
3925 * @return status of search
3927 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3928 const char *config_path,
3929 TALLOC_CTX *mem_ctx,
3930 const struct GUID *rights_guid)
3933 LDAPMessage *res = NULL;
3935 const char *attrs[] = { "displayName", NULL };
3936 const char *result = NULL;
3939 if (!ads || !mem_ctx || !rights_guid) {
3943 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3944 GUID_string(mem_ctx, rights_guid));
3949 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3954 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3956 if (!ADS_ERR_OK(rc)) {
3960 if (ads_count_replies(ads, res) != 1) {
3964 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3967 ads_msgfree(ads, res);
3972 * verify or build and verify an account ou
3973 * @param mem_ctx Pointer to talloc context
3974 * @param ads connection to ads server
3976 * @return status of search
3979 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3981 const char **account_ou)
3987 exploded_dn = ldap_explode_dn(*account_ou, 0);
3989 ldap_value_free(exploded_dn);
3993 ou_string = ads_ou_string(ads, *account_ou);
3995 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3998 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3999 ads->config.bind_path);
4000 SAFE_FREE(ou_string);
4003 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4006 exploded_dn = ldap_explode_dn(name, 0);
4008 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4010 ldap_value_free(exploded_dn);