2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/addns/dnsquery.h"
29 #include "../libds/common/flags.h"
31 #include "../libcli/security/security.h"
32 #include "../librpc/gen_ndr/netlogon.h"
33 #include "lib/param/loadparm.h"
34 #include "libsmb/namequery.h"
40 * @brief basic ldap client-side routines for ads server communications
42 * The routines contained here should do the necessary ldap calls for
45 * Important note: attribute names passed into ads_ routines must
46 * already be in UTF-8 format. We do not convert them because in almost
47 * all cases, they are just ascii (which is represented with the same
48 * codepoints in UTF-8). This may have to change at some point
52 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
54 static SIG_ATOMIC_T gotalarm;
56 /***************************************************************
57 Signal function to tell us we timed out.
58 ****************************************************************/
60 static void gotalarm_sig(int signum)
65 LDAP *ldap_open_with_timeout(const char *server,
66 struct sockaddr_storage *ss,
67 int port, unsigned int to)
73 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
74 "%u seconds\n", server, port, to));
79 CatchSignal(SIGALRM, gotalarm_sig);
81 /* End setup timeout. */
84 if ( strchr_m(server, ':') ) {
86 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
89 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
95 #ifdef HAVE_LDAP_INIT_FD
98 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
99 unsigned timeout_ms = 1000 * to;
101 status = open_socket_out(ss, port, timeout_ms, &fd);
102 if (!NT_STATUS_IS_OK(status)) {
103 DEBUG(3, ("open_socket_out: failed to open socket\n"));
107 /* define LDAP_PROTO_TCP from openldap.h if required */
108 #ifndef LDAP_PROTO_TCP
109 #define LDAP_PROTO_TCP 1
111 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
113 #elif defined(HAVE_LDAP_INITIALIZE)
114 ldap_err = ldap_initialize(&ldp, uri);
116 ldp = ldap_open(server, port);
118 ldap_err = LDAP_SUCCESS;
120 ldap_err = LDAP_OTHER;
123 if (ldap_err != LDAP_SUCCESS) {
124 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
125 uri, ldap_err2string(ldap_err)));
127 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
131 /* Teardown timeout. */
133 CatchSignal(SIGALRM, SIG_IGN);
139 static int ldap_search_with_timeout(LDAP *ld,
140 LDAP_CONST char *base,
142 LDAP_CONST char *filter,
145 LDAPControl **sctrls,
146 LDAPControl **cctrls,
150 int to = lp_ldap_timeout();
151 struct timeval timeout;
152 struct timeval *timeout_ptr = NULL;
155 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
161 timeout_ptr = &timeout;
163 /* Setup alarm timeout. */
164 CatchSignal(SIGALRM, gotalarm_sig);
165 /* Make the alarm time one second beyond
166 the timout we're setting for the
167 remote search timeout, to allow that
168 to fire in preference. */
170 /* End setup timeout. */
174 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
175 attrsonly, sctrls, cctrls, timeout_ptr,
179 /* Teardown alarm timeout. */
180 CatchSignal(SIGALRM, SIG_IGN);
185 return LDAP_TIMELIMIT_EXCEEDED;
188 * A bug in OpenLDAP means ldap_search_ext_s can return
189 * LDAP_SUCCESS but with a NULL res pointer. Cope with
190 * this. See bug #6279 for details. JRA.
194 return LDAP_TIMELIMIT_EXCEEDED;
200 /**********************************************
201 Do client and server sitename match ?
202 **********************************************/
204 bool ads_sitename_match(ADS_STRUCT *ads)
206 if (ads->config.server_site_name == NULL &&
207 ads->config.client_site_name == NULL ) {
208 DEBUG(10,("ads_sitename_match: both null\n"));
211 if (ads->config.server_site_name &&
212 ads->config.client_site_name &&
213 strequal(ads->config.server_site_name,
214 ads->config.client_site_name)) {
215 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
218 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
219 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
220 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
224 /**********************************************
225 Is this the closest DC ?
226 **********************************************/
228 bool ads_closest_dc(ADS_STRUCT *ads)
230 if (ads->config.flags & NBT_SERVER_CLOSEST) {
231 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
235 /* not sure if this can ever happen */
236 if (ads_sitename_match(ads)) {
237 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
241 if (ads->config.client_site_name == NULL) {
242 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
246 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
247 ads->config.ldap_server_name));
254 try a connection to a given ldap server, returning True and setting the servers IP
255 in the ads struct if successful
257 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
258 struct sockaddr_storage *ss)
260 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
261 TALLOC_CTX *frame = talloc_stackframe();
263 char addr[INET6_ADDRSTRLEN];
270 print_sockaddr(addr, sizeof(addr), ss);
272 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
273 addr, ads->server.realm));
275 ZERO_STRUCT( cldap_reply );
277 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
278 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
283 /* Check the CLDAP reply flags */
285 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
286 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
292 /* Fill in the ads->config values */
294 SAFE_FREE(ads->config.realm);
295 SAFE_FREE(ads->config.bind_path);
296 SAFE_FREE(ads->config.ldap_server_name);
297 SAFE_FREE(ads->config.server_site_name);
298 SAFE_FREE(ads->config.client_site_name);
299 SAFE_FREE(ads->server.workgroup);
301 if (!check_cldap_reply_required_flags(cldap_reply.server_type,
302 ads->config.flags)) {
307 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
308 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
309 if (!strupper_m(ads->config.realm)) {
314 ads->config.bind_path = ads_build_dn(ads->config.realm);
315 if (*cldap_reply.server_site) {
316 ads->config.server_site_name =
317 SMB_STRDUP(cldap_reply.server_site);
319 if (*cldap_reply.client_site) {
320 ads->config.client_site_name =
321 SMB_STRDUP(cldap_reply.client_site);
323 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
325 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
328 /* Store our site name. */
329 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
330 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
332 /* Leave this until last so that the flags are not clobbered */
333 ads->config.flags = cldap_reply.server_type;
343 /**********************************************************************
344 send a cldap ping to list of servers, one at a time, until one of
345 them answers it's an ldap server. Record success in the ADS_STRUCT.
346 Take note of and update negative connection cache.
347 **********************************************************************/
349 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,const char *domain,
350 struct ip_service *ip_list, int count)
355 for (i = 0; i < count; i++) {
356 char server[INET6_ADDRSTRLEN];
358 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
360 if (!NT_STATUS_IS_OK(
361 check_negative_conn_cache(domain, server)))
364 /* Returns ok only if it matches the correct server type */
365 ok = ads_try_connect(ads, false, &ip_list[i].ss);
371 /* keep track of failures */
372 add_failed_connection_entry(domain, server,
373 NT_STATUS_UNSUCCESSFUL);
376 return NT_STATUS_NO_LOGON_SERVERS;
379 /***************************************************************************
380 resolve a name and perform an "ldap ping" using NetBIOS and related methods
381 ****************************************************************************/
383 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
384 const char *domain, const char *realm)
387 struct ip_service *ip_list;
390 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
393 status = get_sorted_dc_list(domain, NULL, &ip_list, &count,
395 if (!NT_STATUS_IS_OK(status)) {
399 /* remove servers which are known to be dead based on
400 the corresponding DNS method */
402 for (i = 0; i < count; ++i) {
403 char server[INET6_ADDRSTRLEN];
405 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
408 check_negative_conn_cache(realm, server))) {
409 /* Ensure we add the workgroup name for this
410 IP address as negative too. */
411 add_failed_connection_entry(
413 NT_STATUS_UNSUCCESSFUL);
418 status = cldap_ping_list(ads, domain, ip_list, count);
426 /**********************************************************************
427 resolve a name and perform an "ldap ping" using DNS
428 **********************************************************************/
430 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
434 struct ip_service *ip_list = NULL;
437 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
440 status = get_sorted_dc_list(realm, sitename, &ip_list, &count,
442 if (!NT_STATUS_IS_OK(status)) {
447 status = cldap_ping_list(ads, realm, ip_list, count);
454 /**********************************************************************
455 Try to find an AD dc using our internal name resolution routines
456 Try the realm first and then then workgroup name if netbios is not
458 **********************************************************************/
460 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
462 const char *c_domain = "";
464 bool use_own_domain = False;
465 char *sitename = NULL;
466 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
469 /* if the realm and workgroup are both empty, assume they are ours */
472 c_realm = ads->server.realm;
478 /* special case where no realm and no workgroup means our own */
479 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
480 use_own_domain = True;
481 c_realm = lp_realm();
485 if (!lp_disable_netbios()) {
486 if (use_own_domain) {
487 c_domain = lp_workgroup();
489 c_domain = ads->server.workgroup;
490 if (!*c_realm && (!c_domain || !*c_domain)) {
491 c_domain = lp_workgroup();
500 if (!*c_realm && !*c_domain) {
501 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
503 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
507 * In case of LDAP we use get_dc_name() as that
508 * creates the custom krb5.conf file
510 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
512 struct sockaddr_storage ip_out;
514 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
515 " and falling back to domain '%s'\n",
518 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
521 * we call ads_try_connect() to fill in the
522 * ads->config details
524 ok = ads_try_connect(ads, false, &ip_out);
530 return NT_STATUS_NO_LOGON_SERVERS;
534 sitename = sitename_fetch(talloc_tos(), c_realm);
535 status = resolve_and_ping_dns(ads, sitename, c_realm);
537 if (NT_STATUS_IS_OK(status)) {
538 TALLOC_FREE(sitename);
542 /* In case we failed to contact one of our closest DC on our
544 * need to try to find another DC, retry with a site-less SRV
549 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
550 "our site (%s), Trying to find another DC "
551 "for realm '%s' (domain '%s')\n",
552 sitename, c_realm, c_domain));
553 namecache_delete(c_realm, 0x1C);
555 resolve_and_ping_dns(ads, NULL, c_realm);
557 if (NT_STATUS_IS_OK(status)) {
558 TALLOC_FREE(sitename);
563 TALLOC_FREE(sitename);
566 /* try netbios as fallback - if permitted,
567 or if configuration specifically requests it */
570 DEBUG(3, ("ads_find_dc: falling back to netbios "
571 "name resolution for domain '%s' (realm '%s')\n",
575 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
576 if (NT_STATUS_IS_OK(status)) {
581 DEBUG(1, ("ads_find_dc: "
582 "name resolution for realm '%s' (domain '%s') failed: %s\n",
583 c_realm, c_domain, nt_errstr(status)));
587 * Connect to the LDAP server
588 * @param ads Pointer to an existing ADS_STRUCT
589 * @return status of connection
591 ADS_STATUS ads_connect(ADS_STRUCT *ads)
593 int version = LDAP_VERSION3;
596 char addr[INET6_ADDRSTRLEN];
597 struct samba_sockaddr existing_sa = {0};
600 * ads_connect can be passed in a reused ADS_STRUCT
601 * with an existing non-zero ads->ldap.ss IP address
602 * that was stored by going through ads_find_dc()
603 * if ads->server.ldap_server was NULL.
605 * If ads->server.ldap_server is still NULL but
606 * the target address isn't the zero address, then
607 * store that address off off before zeroing out
608 * ads->ldap so we don't keep doing multiple calls
609 * to ads_find_dc() in the reuse case.
611 * If a caller wants a clean ADS_STRUCT they
612 * will re-initialize by calling ads_init(), or
613 * call ads_destroy() both of which ensures
614 * ads->ldap.ss is a properly zero'ed out valid IP
617 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
618 /* Save off the address we previously found by ads_find_dc(). */
619 bool ok = sockaddr_storage_to_samba_sockaddr(&existing_sa,
622 return ADS_ERROR_NT(NT_STATUS_INVALID_ADDRESS);
627 ZERO_STRUCT(ads->ldap_wrap_data);
628 ads->ldap.last_attempt = time_mono(NULL);
629 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
631 /* try with a user specified server */
633 if (DEBUGLEVEL >= 11) {
634 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
635 DEBUG(11,("ads_connect: entering\n"));
636 DEBUGADD(11,("%s\n", s));
640 if (ads->server.ldap_server) {
642 struct sockaddr_storage ss;
644 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
646 DEBUG(5,("ads_connect: unable to resolve name %s\n",
647 ads->server.ldap_server));
648 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
651 ok = ads_try_connect(ads, ads->server.gc, &ss);
656 /* The choice of which GC use is handled one level up in
657 ads_connect_gc(). If we continue on from here with
658 ads_find_dc() we will get GC searches on port 389 which
659 doesn't work. --jerry */
661 if (ads->server.gc == true) {
662 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
665 if (ads->server.no_fallback) {
666 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
671 if (!is_zero_addr(&existing_sa.u.ss)) {
672 /* We saved off who we should talk to. */
673 bool ok = ads_try_connect(ads,
680 * Keep trying to find a server and fall through
681 * into ads_find_dc() again.
685 ntstatus = ads_find_dc(ads);
686 if (NT_STATUS_IS_OK(ntstatus)) {
690 status = ADS_ERROR_NT(ntstatus);
695 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
696 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
698 if (!ads->auth.user_name) {
699 /* Must use the userPrincipalName value here or sAMAccountName
700 and not servicePrincipalName; found by Guenther Deschner */
702 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
703 DEBUG(0,("ads_connect: asprintf fail.\n"));
704 ads->auth.user_name = NULL;
708 if (!ads->auth.realm) {
709 ads->auth.realm = SMB_STRDUP(ads->config.realm);
712 if (!ads->auth.kdc_server) {
713 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
714 ads->auth.kdc_server = SMB_STRDUP(addr);
717 /* If the caller() requested no LDAP bind, then we are done */
719 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
720 status = ADS_SUCCESS;
724 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
725 if (!ads->ldap_wrap_data.mem_ctx) {
726 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
730 /* Otherwise setup the TCP LDAP session */
732 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
734 ads->ldap.port, lp_ldap_timeout());
735 if (ads->ldap.ld == NULL) {
736 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
739 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
741 /* cache the successful connection for workgroup and realm */
742 if (ads_closest_dc(ads)) {
743 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
744 saf_store( ads->server.realm, ads->config.ldap_server_name);
747 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
749 /* fill in the current time and offsets */
751 status = ads_current_time( ads );
752 if ( !ADS_ERR_OK(status) ) {
756 /* Now do the bind */
758 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
759 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
763 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
764 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
768 status = ads_sasl_bind(ads);
771 if (DEBUGLEVEL >= 11) {
772 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
773 DEBUG(11,("ads_connect: leaving with: %s\n",
774 ads_errstr(status)));
775 DEBUGADD(11,("%s\n", s));
783 * Connect to the LDAP server using given credentials
784 * @param ads Pointer to an existing ADS_STRUCT
785 * @return status of connection
787 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
789 ads->auth.flags |= ADS_AUTH_USER_CREDS;
791 return ads_connect(ads);
795 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
796 * @param ads Pointer to an existing ADS_STRUCT
798 * Sets the ads->ldap.ss to a valid
799 * zero ip address that can be detected by
800 * our is_zero_addr() function. Otherwise
801 * it is left as AF_UNSPEC (0).
803 void ads_zero_ldap(ADS_STRUCT *ads)
805 ZERO_STRUCT(ads->ldap);
807 * Initialize the sockaddr_storage so we can use
808 * sockaddr test functions against it.
810 zero_sockaddr(&ads->ldap.ss);
814 * Disconnect the LDAP server
815 * @param ads Pointer to an existing ADS_STRUCT
817 void ads_disconnect(ADS_STRUCT *ads)
820 ldap_unbind(ads->ldap.ld);
823 if (ads->ldap_wrap_data.wrap_ops &&
824 ads->ldap_wrap_data.wrap_ops->disconnect) {
825 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
827 if (ads->ldap_wrap_data.mem_ctx) {
828 talloc_free(ads->ldap_wrap_data.mem_ctx);
831 ZERO_STRUCT(ads->ldap_wrap_data);
835 Duplicate a struct berval into talloc'ed memory
837 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
839 struct berval *value;
841 if (!in_val) return NULL;
843 value = talloc_zero(ctx, struct berval);
846 if (in_val->bv_len == 0) return value;
848 value->bv_len = in_val->bv_len;
849 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
855 Make a values list out of an array of (struct berval *)
857 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
858 const struct berval **in_vals)
860 struct berval **values;
863 if (!in_vals) return NULL;
864 for (i=0; in_vals[i]; i++)
866 values = talloc_zero_array(ctx, struct berval *, i+1);
867 if (!values) return NULL;
869 for (i=0; in_vals[i]; i++) {
870 values[i] = dup_berval(ctx, in_vals[i]);
876 UTF8-encode a values list out of an array of (char *)
878 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
884 if (!in_vals) return NULL;
885 for (i=0; in_vals[i]; i++)
887 values = talloc_zero_array(ctx, char *, i+1);
888 if (!values) return NULL;
890 for (i=0; in_vals[i]; i++) {
891 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
900 Pull a (char *) array out of a UTF8-encoded values list
902 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
906 size_t converted_size;
908 if (!in_vals) return NULL;
909 for (i=0; in_vals[i]; i++)
911 values = talloc_zero_array(ctx, char *, i+1);
912 if (!values) return NULL;
914 for (i=0; in_vals[i]; i++) {
915 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
917 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
918 "%s", strerror(errno)));
925 * Do a search with paged results. cookie must be null on the first
926 * call, and then returned on each subsequent call. It will be null
927 * again when the entire search is complete
928 * @param ads connection to ads server
929 * @param bind_path Base dn for the search
930 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
931 * @param expr Search expression - specified in local charset
932 * @param attrs Attributes to retrieve - specified in utf8 or ascii
933 * @param res ** which will contain results - free res* with ads_msgfree()
934 * @param count Number of entries retrieved on this page
935 * @param cookie The paged results cookie to be returned on subsequent calls
936 * @return status of search
938 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
939 const char *bind_path,
940 int scope, const char *expr,
941 const char **attrs, void *args,
943 int *count, struct berval **cookie)
946 char *utf8_expr, *utf8_path, **search_attrs = NULL;
947 size_t converted_size;
948 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
949 BerElement *cookie_be = NULL;
950 struct berval *cookie_bv= NULL;
951 BerElement *ext_be = NULL;
952 struct berval *ext_bv= NULL;
955 ads_control *external_control = (ads_control *) args;
959 if (!(ctx = talloc_init("ads_do_paged_search_args")))
960 return ADS_ERROR(LDAP_NO_MEMORY);
962 /* 0 means the conversion worked but the result was empty
963 so we only fail if it's -1. In any case, it always
964 at least nulls out the dest */
965 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
966 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
972 if (!attrs || !(*attrs))
975 /* This would be the utf8-encoded version...*/
976 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
977 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
983 /* Paged results only available on ldap v3 or later */
984 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
985 if (version < LDAP_VERSION3) {
986 rc = LDAP_NOT_SUPPORTED;
990 cookie_be = ber_alloc_t(LBER_USE_DER);
992 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
993 ber_bvfree(*cookie); /* don't need it from last time */
996 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
998 ber_flatten(cookie_be, &cookie_bv);
999 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1000 PagedResults.ldctl_iscritical = (char) 1;
1001 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1002 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1004 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1005 NoReferrals.ldctl_iscritical = (char) 0;
1006 NoReferrals.ldctl_value.bv_len = 0;
1007 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1009 if (external_control &&
1010 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1011 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1013 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1014 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1016 /* win2k does not accept a ldctl_value beeing passed in */
1018 if (external_control->val != 0) {
1020 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1021 rc = LDAP_NO_MEMORY;
1025 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1026 rc = LDAP_NO_MEMORY;
1029 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1030 rc = LDAP_NO_MEMORY;
1034 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1035 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1038 ExternalCtrl.ldctl_value.bv_len = 0;
1039 ExternalCtrl.ldctl_value.bv_val = NULL;
1042 controls[0] = &NoReferrals;
1043 controls[1] = &PagedResults;
1044 controls[2] = &ExternalCtrl;
1048 controls[0] = &NoReferrals;
1049 controls[1] = &PagedResults;
1053 /* we need to disable referrals as the openldap libs don't
1054 handle them and paged results at the same time. Using them
1055 together results in the result record containing the server
1056 page control being removed from the result list (tridge/jmcd)
1058 leaving this in despite the control that says don't generate
1059 referrals, in case the server doesn't support it (jmcd)
1061 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1063 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1064 search_attrs, 0, controls,
1065 NULL, LDAP_NO_LIMIT,
1066 (LDAPMessage **)res);
1068 ber_free(cookie_be, 1);
1069 ber_bvfree(cookie_bv);
1072 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1073 ldap_err2string(rc)));
1074 if (rc == LDAP_OTHER) {
1078 ret = ldap_parse_result(ads->ldap.ld,
1086 if (ret == LDAP_SUCCESS) {
1087 DEBUG(3, ("ldap_search_with_timeout(%s) "
1088 "error: %s\n", expr, ldap_errmsg));
1089 ldap_memfree(ldap_errmsg);
1095 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1096 NULL, &rcontrols, 0);
1102 for (i=0; rcontrols[i]; i++) {
1103 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1104 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1105 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1107 /* the berval is the cookie, but must be freed when
1109 if (cookie_bv->bv_len) /* still more to do */
1110 *cookie=ber_bvdup(cookie_bv);
1113 ber_bvfree(cookie_bv);
1114 ber_free(cookie_be, 1);
1118 ldap_controls_free(rcontrols);
1121 talloc_destroy(ctx);
1124 ber_free(ext_be, 1);
1131 if (rc != LDAP_SUCCESS && *res != NULL) {
1132 ads_msgfree(ads, *res);
1136 /* if/when we decide to utf8-encode attrs, take out this next line */
1137 TALLOC_FREE(search_attrs);
1139 return ADS_ERROR(rc);
1142 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1143 int scope, const char *expr,
1144 const char **attrs, LDAPMessage **res,
1145 int *count, struct berval **cookie)
1147 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1152 * Get all results for a search. This uses ads_do_paged_search() to return
1153 * all entries in a large search.
1154 * @param ads connection to ads server
1155 * @param bind_path Base dn for the search
1156 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1157 * @param expr Search expression
1158 * @param attrs Attributes to retrieve
1159 * @param res ** which will contain results - free res* with ads_msgfree()
1160 * @return status of search
1162 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1163 int scope, const char *expr,
1164 const char **attrs, void *args,
1167 struct berval *cookie = NULL;
1172 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1175 if (!ADS_ERR_OK(status))
1178 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1180 LDAPMessage *res2 = NULL;
1181 LDAPMessage *msg, *next;
1183 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1184 attrs, args, &res2, &count, &cookie);
1185 if (!ADS_ERR_OK(status)) {
1189 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1190 that this works on all ldap libs, but I have only tested with openldap */
1191 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1192 next = ads_next_message(ads, msg);
1193 ldap_add_result_entry((LDAPMessage **)res, msg);
1195 /* note that we do not free res2, as the memory is now
1196 part of the main returned list */
1199 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1200 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1206 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1207 int scope, const char *expr,
1208 const char **attrs, LDAPMessage **res)
1210 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1213 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1214 int scope, const char *expr,
1215 const char **attrs, uint32_t sd_flags,
1220 args.control = ADS_SD_FLAGS_OID;
1221 args.val = sd_flags;
1222 args.critical = True;
1224 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1229 * Run a function on all results for a search. Uses ads_do_paged_search() and
1230 * runs the function as each page is returned, using ads_process_results()
1231 * @param ads connection to ads server
1232 * @param bind_path Base dn for the search
1233 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1234 * @param expr Search expression - specified in local charset
1235 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1236 * @param fn Function which takes attr name, values list, and data_area
1237 * @param data_area Pointer which is passed to function on each call
1238 * @return status of search
1240 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1241 int scope, const char *expr, const char **attrs,
1242 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1245 struct berval *cookie = NULL;
1250 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1253 if (!ADS_ERR_OK(status)) return status;
1255 ads_process_results(ads, res, fn, data_area);
1256 ads_msgfree(ads, res);
1259 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1260 &res, &count, &cookie);
1262 if (!ADS_ERR_OK(status)) break;
1264 ads_process_results(ads, res, fn, data_area);
1265 ads_msgfree(ads, res);
1272 * Do a search with a timeout.
1273 * @param ads connection to ads server
1274 * @param bind_path Base dn for the search
1275 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1276 * @param expr Search expression
1277 * @param attrs Attributes to retrieve
1278 * @param res ** which will contain results - free res* with ads_msgfree()
1279 * @return status of search
1281 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1283 const char **attrs, LDAPMessage **res)
1286 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1287 size_t converted_size;
1291 if (!(ctx = talloc_init("ads_do_search"))) {
1292 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1293 return ADS_ERROR(LDAP_NO_MEMORY);
1296 /* 0 means the conversion worked but the result was empty
1297 so we only fail if it's negative. In any case, it always
1298 at least nulls out the dest */
1299 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1300 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1302 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1303 rc = LDAP_NO_MEMORY;
1307 if (!attrs || !(*attrs))
1308 search_attrs = NULL;
1310 /* This would be the utf8-encoded version...*/
1311 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1312 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1314 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1315 rc = LDAP_NO_MEMORY;
1320 /* see the note in ads_do_paged_search - we *must* disable referrals */
1321 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1323 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1324 search_attrs, 0, NULL, NULL,
1326 (LDAPMessage **)res);
1328 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1329 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1334 talloc_destroy(ctx);
1335 /* if/when we decide to utf8-encode attrs, take out this next line */
1336 TALLOC_FREE(search_attrs);
1337 return ADS_ERROR(rc);
1340 * Do a general ADS search
1341 * @param ads connection to ads server
1342 * @param res ** which will contain results - free res* with ads_msgfree()
1343 * @param expr Search expression
1344 * @param attrs Attributes to retrieve
1345 * @return status of search
1347 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1348 const char *expr, const char **attrs)
1350 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1355 * Do a search on a specific DistinguishedName
1356 * @param ads connection to ads server
1357 * @param res ** which will contain results - free res* with ads_msgfree()
1358 * @param dn DistinguishName to search
1359 * @param attrs Attributes to retrieve
1360 * @return status of search
1362 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1363 const char *dn, const char **attrs)
1365 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1370 * Free up memory from a ads_search
1371 * @param ads connection to ads server
1372 * @param msg Search results to free
1374 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1381 * Get a dn from search results
1382 * @param ads connection to ads server
1383 * @param msg Search result
1386 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1388 char *utf8_dn, *unix_dn;
1389 size_t converted_size;
1391 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1394 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1398 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1399 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1403 ldap_memfree(utf8_dn);
1408 * Get the parent from a dn
1409 * @param dn the dn to return the parent from
1410 * @return parent dn string
1412 char *ads_parent_dn(const char *dn)
1420 p = strchr(dn, ',');
1430 * Find a machine account given a hostname
1431 * @param ads connection to ads server
1432 * @param res ** which will contain results - free res* with ads_msgfree()
1433 * @param host Hostname to search for
1434 * @return status of search
1436 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1437 const char *machine)
1441 const char *attrs[] = {
1442 /* This is how Windows checks for machine accounts */
1445 "userAccountControl",
1447 "ServicePrincipalName",
1448 "userPrincipalName",
1451 /* Additional attributes Samba checks */
1452 "msDS-AdditionalDnsHostName",
1453 "msDS-SupportedEncryptionTypes",
1454 "nTSecurityDescriptor",
1458 TALLOC_CTX *frame = talloc_stackframe();
1462 /* the easiest way to find a machine account anywhere in the tree
1463 is to look for hostname$ */
1464 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1466 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1470 status = ads_search(ads, res, expr, attrs);
1471 if (ADS_ERR_OK(status)) {
1472 if (ads_count_replies(ads, *res) != 1) {
1473 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1483 * Initialize a list of mods to be used in a modify request
1484 * @param ctx An initialized TALLOC_CTX
1485 * @return allocated ADS_MODLIST
1487 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1489 #define ADS_MODLIST_ALLOC_SIZE 10
1492 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1493 /* -1 is safety to make sure we don't go over the end.
1494 need to reset it to NULL before doing ldap modify */
1495 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1497 return (ADS_MODLIST)mods;
1502 add an attribute to the list, with values list already constructed
1504 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1505 int mod_op, const char *name,
1506 const void *_invals)
1509 LDAPMod **modlist = (LDAPMod **) *mods;
1510 struct berval **ber_values = NULL;
1511 char **char_values = NULL;
1514 mod_op = LDAP_MOD_DELETE;
1516 if (mod_op & LDAP_MOD_BVALUES) {
1517 const struct berval **b;
1518 b = discard_const_p(const struct berval *, _invals);
1519 ber_values = ads_dup_values(ctx, b);
1522 c = discard_const_p(const char *, _invals);
1523 char_values = ads_push_strvals(ctx, c);
1527 /* find the first empty slot */
1528 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1530 if (modlist[curmod] == (LDAPMod *) -1) {
1531 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1532 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1533 return ADS_ERROR(LDAP_NO_MEMORY);
1534 memset(&modlist[curmod], 0,
1535 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1536 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1537 *mods = (ADS_MODLIST)modlist;
1540 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1541 return ADS_ERROR(LDAP_NO_MEMORY);
1542 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1543 if (mod_op & LDAP_MOD_BVALUES) {
1544 modlist[curmod]->mod_bvalues = ber_values;
1545 } else if (mod_op & LDAP_MOD_DELETE) {
1546 modlist[curmod]->mod_values = NULL;
1548 modlist[curmod]->mod_values = char_values;
1551 modlist[curmod]->mod_op = mod_op;
1552 return ADS_ERROR(LDAP_SUCCESS);
1556 * Add a single string value to a mod list
1557 * @param ctx An initialized TALLOC_CTX
1558 * @param mods An initialized ADS_MODLIST
1559 * @param name The attribute name to add
1560 * @param val The value to add - NULL means DELETE
1561 * @return ADS STATUS indicating success of add
1563 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1564 const char *name, const char *val)
1566 const char *values[2];
1572 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1573 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1577 * Add an array of string values to a mod list
1578 * @param ctx An initialized TALLOC_CTX
1579 * @param mods An initialized ADS_MODLIST
1580 * @param name The attribute name to add
1581 * @param vals The array of string values to add - NULL means DELETE
1582 * @return ADS STATUS indicating success of add
1584 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1585 const char *name, const char **vals)
1588 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1589 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1590 name, (const void **) vals);
1594 * Add a single ber-encoded value to a mod list
1595 * @param ctx An initialized TALLOC_CTX
1596 * @param mods An initialized ADS_MODLIST
1597 * @param name The attribute name to add
1598 * @param val The value to add - NULL means DELETE
1599 * @return ADS STATUS indicating success of add
1601 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1602 const char *name, const struct berval *val)
1604 const struct berval *values[2];
1609 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1610 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1611 name, (const void **) values);
1614 static void ads_print_error(int ret, LDAP *ld)
1617 char *ld_error = NULL;
1618 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1619 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1621 ldap_err2string(ret),
1623 SAFE_FREE(ld_error);
1628 * Perform an ldap modify
1629 * @param ads connection to ads server
1630 * @param mod_dn DistinguishedName to modify
1631 * @param mods list of modifications to perform
1632 * @return status of modify
1634 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1637 char *utf8_dn = NULL;
1638 size_t converted_size;
1640 this control is needed to modify that contains a currently
1641 non-existent attribute (but allowable for the object) to run
1643 LDAPControl PermitModify = {
1644 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1647 LDAPControl *controls[2];
1649 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1651 controls[0] = &PermitModify;
1654 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1655 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1658 /* find the end of the list, marked by NULL or -1 */
1659 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1660 /* make sure the end of the list is NULL */
1662 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1663 (LDAPMod **) mods, controls, NULL);
1664 ads_print_error(ret, ads->ldap.ld);
1665 TALLOC_FREE(utf8_dn);
1666 return ADS_ERROR(ret);
1670 * Perform an ldap add
1671 * @param ads connection to ads server
1672 * @param new_dn DistinguishedName to add
1673 * @param mods list of attributes and values for DN
1674 * @return status of add
1676 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1679 char *utf8_dn = NULL;
1680 size_t converted_size;
1682 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1684 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1685 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1686 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1689 /* find the end of the list, marked by NULL or -1 */
1690 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1691 /* make sure the end of the list is NULL */
1694 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1695 ads_print_error(ret, ads->ldap.ld);
1696 TALLOC_FREE(utf8_dn);
1697 return ADS_ERROR(ret);
1701 * Delete a DistinguishedName
1702 * @param ads connection to ads server
1703 * @param new_dn DistinguishedName to delete
1704 * @return status of delete
1706 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1709 char *utf8_dn = NULL;
1710 size_t converted_size;
1711 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1712 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1713 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1716 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1718 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1719 ads_print_error(ret, ads->ldap.ld);
1720 TALLOC_FREE(utf8_dn);
1721 return ADS_ERROR(ret);
1725 * Build an org unit string
1726 * if org unit is Computers or blank then assume a container, otherwise
1727 * assume a / separated list of organisational units.
1728 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1729 * @param ads connection to ads server
1730 * @param org_unit Organizational unit
1731 * @return org unit string - caller must free
1733 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1737 if (!org_unit || !*org_unit) {
1739 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1741 /* samba4 might not yet respond to a wellknownobject-query */
1742 return ret ? ret : SMB_STRDUP("cn=Computers");
1745 if (strequal(org_unit, "Computers")) {
1746 return SMB_STRDUP("cn=Computers");
1749 /* jmcd: removed "\\" from the separation chars, because it is
1750 needed as an escape for chars like '#' which are valid in an
1752 return ads_build_path(org_unit, "/", "ou=", 1);
1756 * Get a org unit string for a well-known GUID
1757 * @param ads connection to ads server
1758 * @param wknguid Well known GUID
1759 * @return org unit string - caller must free
1761 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1764 LDAPMessage *res = NULL;
1765 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1766 **bind_dn_exp = NULL;
1767 const char *attrs[] = {"distinguishedName", NULL};
1768 int new_ln, wkn_ln, bind_ln, i;
1770 if (wknguid == NULL) {
1774 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1775 DEBUG(1, ("asprintf failed!\n"));
1779 status = ads_search_dn(ads, &res, base, attrs);
1780 if (!ADS_ERR_OK(status)) {
1781 DEBUG(1,("Failed while searching for: %s\n", base));
1785 if (ads_count_replies(ads, res) != 1) {
1789 /* substitute the bind-path from the well-known-guid-search result */
1790 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1795 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1800 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1805 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1807 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1810 new_ln = wkn_ln - bind_ln;
1812 ret = SMB_STRDUP(wkn_dn_exp[0]);
1817 for (i=1; i < new_ln; i++) {
1820 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1826 ret = SMB_STRDUP(s);
1835 ads_msgfree(ads, res);
1836 TALLOC_FREE(wkn_dn);
1838 ldap_value_free(wkn_dn_exp);
1841 ldap_value_free(bind_dn_exp);
1848 * Adds (appends) an item to an attribute array, rather then
1849 * replacing the whole list
1850 * @param ctx An initialized TALLOC_CTX
1851 * @param mods An initialized ADS_MODLIST
1852 * @param name name of the ldap attribute to append to
1853 * @param vals an array of values to add
1854 * @return status of addition
1857 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1858 const char *name, const char **vals)
1860 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1861 (const void *) vals);
1865 * Determines the an account's current KVNO via an LDAP lookup
1866 * @param ads An initialized ADS_STRUCT
1867 * @param account_name the NT samaccountname.
1868 * @return the kvno for the account, or -1 in case of a failure.
1871 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1873 LDAPMessage *res = NULL;
1874 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1876 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1877 char *dn_string = NULL;
1880 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1881 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1884 ret = ads_search(ads, &res, filter, attrs);
1886 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1887 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1888 ads_msgfree(ads, res);
1892 dn_string = ads_get_dn(ads, talloc_tos(), res);
1894 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1895 ads_msgfree(ads, res);
1898 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1899 TALLOC_FREE(dn_string);
1901 /* ---------------------------------------------------------
1902 * 0 is returned as a default KVNO from this point on...
1903 * This is done because Windows 2000 does not support key
1904 * version numbers. Chances are that a failure in the next
1905 * step is simply due to Windows 2000 being used for a
1906 * domain controller. */
1909 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1910 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1911 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1912 ads_msgfree(ads, res);
1917 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1918 ads_msgfree(ads, res);
1923 * Determines the computer account's current KVNO via an LDAP lookup
1924 * @param ads An initialized ADS_STRUCT
1925 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1926 * @return the kvno for the computer account, or -1 in case of a failure.
1929 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1931 char *computer_account = NULL;
1934 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1938 kvno = ads_get_kvno(ads, computer_account);
1939 free(computer_account);
1945 * This clears out all registered spn's for a given hostname
1946 * @param ads An initilaized ADS_STRUCT
1947 * @param machine_name the NetBIOS name of the computer.
1948 * @return 0 upon success, non-zero otherwise.
1951 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1954 LDAPMessage *res = NULL;
1956 const char *servicePrincipalName[1] = {NULL};
1958 char *dn_string = NULL;
1960 ret = ads_find_machine_acct(ads, &res, machine_name);
1961 if (!ADS_ERR_OK(ret)) {
1962 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1963 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1964 ads_msgfree(ads, res);
1968 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1969 ctx = talloc_init("ads_clear_service_principal_names");
1971 ads_msgfree(ads, res);
1972 return ADS_ERROR(LDAP_NO_MEMORY);
1975 if (!(mods = ads_init_mods(ctx))) {
1976 talloc_destroy(ctx);
1977 ads_msgfree(ads, res);
1978 return ADS_ERROR(LDAP_NO_MEMORY);
1980 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1981 if (!ADS_ERR_OK(ret)) {
1982 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1983 ads_msgfree(ads, res);
1984 talloc_destroy(ctx);
1987 dn_string = ads_get_dn(ads, talloc_tos(), res);
1989 talloc_destroy(ctx);
1990 ads_msgfree(ads, res);
1991 return ADS_ERROR(LDAP_NO_MEMORY);
1993 ret = ads_gen_mod(ads, dn_string, mods);
1994 TALLOC_FREE(dn_string);
1995 if (!ADS_ERR_OK(ret)) {
1996 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1998 ads_msgfree(ads, res);
1999 talloc_destroy(ctx);
2003 ads_msgfree(ads, res);
2004 talloc_destroy(ctx);
2009 * @brief Search for an element in a string array.
2011 * @param[in] el_array The string array to search.
2013 * @param[in] num_el The number of elements in the string array.
2015 * @param[in] el The string to search.
2017 * @return True if found, false if not.
2019 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2023 if (el_array == NULL || num_el == 0 || el == NULL) {
2027 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2030 cmp = strcasecmp_m(el_array[i], el);
2040 * @brief This gets the service principal names of an existing computer account.
2042 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2044 * @param[in] ads The ADS context to use.
2046 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2047 * identify the computer account.
2049 * @param[in] spn_array A pointer to store the array for SPNs.
2051 * @param[in] num_spns The number of principals stored in the array.
2053 * @return 0 on success, or a ADS error if a failure occurred.
2055 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2057 const char *machine_name,
2062 LDAPMessage *res = NULL;
2065 status = ads_find_machine_acct(ads,
2068 if (!ADS_ERR_OK(status)) {
2069 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2074 count = ads_count_replies(ads, res);
2076 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2080 *spn_array = ads_pull_strings(ads,
2083 "servicePrincipalName",
2085 if (*spn_array == NULL) {
2086 DEBUG(1, ("Host account for %s does not have service principal "
2089 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2094 ads_msgfree(ads, res);
2100 * This adds a service principal name to an existing computer account
2101 * (found by hostname) in AD.
2102 * @param ads An initialized ADS_STRUCT
2103 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2104 * @param spns An array or strings for the service principals to add,
2105 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2106 * @return 0 upon sucess, or non-zero if a failure occurs
2109 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2110 const char *machine_name,
2115 LDAPMessage *res = NULL;
2117 char *dn_string = NULL;
2118 const char **servicePrincipalName = spns;
2120 ret = ads_find_machine_acct(ads, &res, machine_name);
2121 if (!ADS_ERR_OK(ret)) {
2122 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2124 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2125 ads_msgfree(ads, res);
2129 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2130 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2131 ads_msgfree(ads, res);
2132 return ADS_ERROR(LDAP_NO_MEMORY);
2135 DEBUG(5,("ads_add_service_principal_name: INFO: "
2136 "Adding %s to host %s\n",
2137 spns[0] ? "N/A" : spns[0], machine_name));
2140 DEBUG(5,("ads_add_service_principal_name: INFO: "
2141 "Adding %s to host %s\n",
2142 spns[1] ? "N/A" : spns[1], machine_name));
2144 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2145 ret = ADS_ERROR(LDAP_NO_MEMORY);
2149 ret = ads_add_strlist(ctx,
2151 "servicePrincipalName",
2152 servicePrincipalName);
2153 if (!ADS_ERR_OK(ret)) {
2154 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2158 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2159 ret = ADS_ERROR(LDAP_NO_MEMORY);
2163 ret = ads_gen_mod(ads, dn_string, mods);
2164 if (!ADS_ERR_OK(ret)) {
2165 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2171 ads_msgfree(ads, res);
2175 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2178 uint32_t acct_ctrl = 0;
2181 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2189 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2191 const struct berval *machine_pw_val)
2195 TALLOC_CTX *frame = talloc_stackframe();
2196 uint32_t acct_control;
2197 char *control_str = NULL;
2198 const char *attrs[] = {
2202 LDAPMessage *res = NULL;
2205 dn = ads_get_dn(ads, frame, msg);
2207 ret = ADS_ERROR(LDAP_NO_MEMORY);
2211 acct_control = ads_get_acct_ctrl(ads, msg);
2212 if (acct_control == 0) {
2213 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2218 * Changing the password, disables the account. So we need to change the
2219 * userAccountControl flags to enable it again.
2221 mods = ads_init_mods(frame);
2223 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2227 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2229 ret = ads_gen_mod(ads, dn, mods);
2230 if (!ADS_ERR_OK(ret)) {
2236 * To activate the account, we need to disable and enable it.
2238 acct_control |= UF_ACCOUNTDISABLE;
2240 control_str = talloc_asprintf(frame, "%u", acct_control);
2241 if (control_str == NULL) {
2242 ret = ADS_ERROR(LDAP_NO_MEMORY);
2246 mods = ads_init_mods(frame);
2248 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2252 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2254 ret = ads_gen_mod(ads, dn, mods);
2255 if (!ADS_ERR_OK(ret)) {
2259 TALLOC_FREE(control_str);
2262 * Enable the account again.
2264 acct_control &= ~UF_ACCOUNTDISABLE;
2266 control_str = talloc_asprintf(frame, "%u", acct_control);
2267 if (control_str == NULL) {
2268 ret = ADS_ERROR(LDAP_NO_MEMORY);
2272 mods = ads_init_mods(frame);
2274 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2278 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2280 ret = ads_gen_mod(ads, dn, mods);
2281 if (!ADS_ERR_OK(ret)) {
2285 TALLOC_FREE(control_str);
2287 ret = ads_search_dn(ads, &res, dn, attrs);
2288 ads_msgfree(ads, res);
2297 * adds a machine account to the ADS server
2298 * @param ads An intialized ADS_STRUCT
2299 * @param machine_name - the NetBIOS machine name of this account.
2300 * @param account_type A number indicating the type of account to create
2301 * @param org_unit The LDAP path in which to place this account
2302 * @return 0 upon success, or non-zero otherwise
2305 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2306 const char *machine_name,
2307 const char *machine_password,
2308 const char *org_unit,
2309 uint32_t etype_list,
2310 const char *dns_domain_name)
2313 char *samAccountName = NULL;
2314 char *controlstr = NULL;
2315 TALLOC_CTX *ctx = NULL;
2317 char *machine_escaped = NULL;
2318 char *dns_hostname = NULL;
2319 char *new_dn = NULL;
2320 char *utf8_pw = NULL;
2321 size_t utf8_pw_len = 0;
2322 char *utf16_pw = NULL;
2323 size_t utf16_pw_len = 0;
2324 struct berval machine_pw_val;
2326 const char **spn_array = NULL;
2327 size_t num_spns = 0;
2328 const char *spn_prefix[] = {
2330 "RestrictedKrbHost",
2333 LDAPMessage *res = NULL;
2334 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2336 ctx = talloc_init("ads_add_machine_acct");
2338 return ADS_ERROR(LDAP_NO_MEMORY);
2341 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2342 if (machine_escaped == NULL) {
2343 ret = ADS_ERROR(LDAP_NO_MEMORY);
2347 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2348 if (utf8_pw == NULL) {
2349 ret = ADS_ERROR(LDAP_NO_MEMORY);
2352 utf8_pw_len = strlen(utf8_pw);
2354 ok = convert_string_talloc(ctx,
2355 CH_UTF8, CH_UTF16MUNGED,
2356 utf8_pw, utf8_pw_len,
2357 (void *)&utf16_pw, &utf16_pw_len);
2359 ret = ADS_ERROR(LDAP_NO_MEMORY);
2363 machine_pw_val = (struct berval) {
2365 .bv_len = utf16_pw_len,
2368 /* Check if the machine account already exists. */
2369 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2370 if (ADS_ERR_OK(ret)) {
2371 /* Change the machine account password */
2372 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2373 ads_msgfree(ads, res);
2377 ads_msgfree(ads, res);
2379 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2380 if (new_dn == NULL) {
2381 ret = ADS_ERROR(LDAP_NO_MEMORY);
2385 /* Create machine account */
2387 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2388 if (samAccountName == NULL) {
2389 ret = ADS_ERROR(LDAP_NO_MEMORY);
2393 dns_hostname = talloc_asprintf(ctx,
2397 if (dns_hostname == NULL) {
2398 ret = ADS_ERROR(LDAP_NO_MEMORY);
2402 /* Add dns_hostname SPNs */
2403 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2404 char *spn = talloc_asprintf(ctx,
2409 ret = ADS_ERROR(LDAP_NO_MEMORY);
2413 ok = add_string_to_array(spn_array,
2418 ret = ADS_ERROR(LDAP_NO_MEMORY);
2423 /* Add machine_name SPNs */
2424 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2425 char *spn = talloc_asprintf(ctx,
2430 ret = ADS_ERROR(LDAP_NO_MEMORY);
2434 ok = add_string_to_array(spn_array,
2439 ret = ADS_ERROR(LDAP_NO_MEMORY);
2444 /* Make sure to NULL terminate the array */
2445 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2446 if (spn_array == NULL) {
2447 ret = ADS_ERROR(LDAP_NO_MEMORY);
2450 spn_array[num_spns] = NULL;
2452 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2453 if (controlstr == NULL) {
2454 ret = ADS_ERROR(LDAP_NO_MEMORY);
2458 mods = ads_init_mods(ctx);
2460 ret = ADS_ERROR(LDAP_NO_MEMORY);
2464 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2465 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2466 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2467 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2468 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2469 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2471 ret = ads_gen_add(ads, new_dn, mods);
2474 SAFE_FREE(machine_escaped);
2475 talloc_destroy(ctx);
2481 * move a machine account to another OU on the ADS server
2482 * @param ads - An intialized ADS_STRUCT
2483 * @param machine_name - the NetBIOS machine name of this account.
2484 * @param org_unit - The LDAP path in which to place this account
2485 * @param moved - whether we moved the machine account (optional)
2486 * @return 0 upon success, or non-zero otherwise
2489 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2490 const char *org_unit, bool *moved)
2494 LDAPMessage *res = NULL;
2495 char *filter = NULL;
2496 char *computer_dn = NULL;
2498 char *computer_rdn = NULL;
2499 bool need_move = False;
2501 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2502 rc = ADS_ERROR(LDAP_NO_MEMORY);
2506 /* Find pre-existing machine */
2507 rc = ads_search(ads, &res, filter, NULL);
2508 if (!ADS_ERR_OK(rc)) {
2512 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2514 rc = ADS_ERROR(LDAP_NO_MEMORY);
2518 parent_dn = ads_parent_dn(computer_dn);
2519 if (strequal(parent_dn, org_unit)) {
2525 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2526 rc = ADS_ERROR(LDAP_NO_MEMORY);
2530 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2531 org_unit, 1, NULL, NULL);
2532 rc = ADS_ERROR(ldap_status);
2535 ads_msgfree(ads, res);
2537 TALLOC_FREE(computer_dn);
2538 SAFE_FREE(computer_rdn);
2540 if (!ADS_ERR_OK(rc)) {
2552 dump a binary result from ldap
2554 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2557 for (i=0; values[i]; i++) {
2559 printf("%s: ", field);
2560 for (j=0; j<values[i]->bv_len; j++) {
2561 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2567 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2570 for (i=0; values[i]; i++) {
2572 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2575 status = GUID_from_ndr_blob(&in, &guid);
2576 if (NT_STATUS_IS_OK(status)) {
2577 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2579 printf("%s: INVALID GUID\n", field);
2585 dump a sid result from ldap
2587 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2590 for (i=0; values[i]; i++) {
2593 struct dom_sid_buf tmp;
2594 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2595 values[i]->bv_len, &sid);
2599 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2604 dump ntSecurityDescriptor
2606 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2608 TALLOC_CTX *frame = talloc_stackframe();
2609 struct security_descriptor *psd;
2612 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2613 values[0]->bv_len, &psd);
2614 if (!NT_STATUS_IS_OK(status)) {
2615 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2616 nt_errstr(status)));
2622 ads_disp_sd(ads, talloc_tos(), psd);
2629 dump a string result from ldap
2631 static void dump_string(const char *field, char **values)
2634 for (i=0; values[i]; i++) {
2635 printf("%s: %s\n", field, values[i]);
2640 dump a field from LDAP on stdout
2644 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2649 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2651 {"objectGUID", False, dump_guid},
2652 {"netbootGUID", False, dump_guid},
2653 {"nTSecurityDescriptor", False, dump_sd},
2654 {"dnsRecord", False, dump_binary},
2655 {"objectSid", False, dump_sid},
2656 {"tokenGroups", False, dump_sid},
2657 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2658 {"tokengroupsGlobalandUniversal", False, dump_sid},
2659 {"mS-DS-CreatorSID", False, dump_sid},
2660 {"msExchMailboxGuid", False, dump_guid},
2665 if (!field) { /* must be end of an entry */
2670 for (i=0; handlers[i].name; i++) {
2671 if (strcasecmp_m(handlers[i].name, field) == 0) {
2672 if (!values) /* first time, indicate string or not */
2673 return handlers[i].string;
2674 handlers[i].handler(ads, field, (struct berval **) values);
2678 if (!handlers[i].name) {
2679 if (!values) /* first time, indicate string conversion */
2681 dump_string(field, (char **)values);
2687 * Dump a result from LDAP on stdout
2688 * used for debugging
2689 * @param ads connection to ads server
2690 * @param res Results to dump
2693 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2695 ads_process_results(ads, res, ads_dump_field, NULL);
2699 * Walk through results, calling a function for each entry found.
2700 * The function receives a field name, a berval * array of values,
2701 * and a data area passed through from the start. The function is
2702 * called once with null for field and values at the end of each
2704 * @param ads connection to ads server
2705 * @param res Results to process
2706 * @param fn Function for processing each result
2707 * @param data_area user-defined area to pass to function
2709 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2710 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2715 size_t converted_size;
2717 if (!(ctx = talloc_init("ads_process_results")))
2720 for (msg = ads_first_entry(ads, res); msg;
2721 msg = ads_next_entry(ads, msg)) {
2725 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2726 (LDAPMessage *)msg,&b);
2728 utf8_field=ldap_next_attribute(ads->ldap.ld,
2729 (LDAPMessage *)msg,b)) {
2730 struct berval **ber_vals;
2736 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2739 DEBUG(0,("ads_process_results: "
2740 "pull_utf8_talloc failed: %s",
2744 string = fn(ads, field, NULL, data_area);
2749 utf8_vals = ldap_get_values(ads->ldap.ld,
2750 (LDAPMessage *)msg, field);
2751 p = discard_const_p(const char *, utf8_vals);
2752 str_vals = ads_pull_strvals(ctx, p);
2753 fn(ads, field, (void **) str_vals, data_area);
2754 ldap_value_free(utf8_vals);
2756 ber_vals = ldap_get_values_len(ads->ldap.ld,
2757 (LDAPMessage *)msg, field);
2758 fn(ads, field, (void **) ber_vals, data_area);
2760 ldap_value_free_len(ber_vals);
2762 ldap_memfree(utf8_field);
2765 talloc_free_children(ctx);
2766 fn(ads, NULL, NULL, data_area); /* completed an entry */
2769 talloc_destroy(ctx);
2773 * count how many replies are in a LDAPMessage
2774 * @param ads connection to ads server
2775 * @param res Results to count
2776 * @return number of replies
2778 int ads_count_replies(ADS_STRUCT *ads, void *res)
2780 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2784 * pull the first entry from a ADS result
2785 * @param ads connection to ads server
2786 * @param res Results of search
2787 * @return first entry from result
2789 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2791 return ldap_first_entry(ads->ldap.ld, res);
2795 * pull the next entry from a ADS result
2796 * @param ads connection to ads server
2797 * @param res Results of search
2798 * @return next entry from result
2800 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2802 return ldap_next_entry(ads->ldap.ld, res);
2806 * pull the first message from a ADS result
2807 * @param ads connection to ads server
2808 * @param res Results of search
2809 * @return first message from result
2811 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2813 return ldap_first_message(ads->ldap.ld, res);
2817 * pull the next message from a ADS result
2818 * @param ads connection to ads server
2819 * @param res Results of search
2820 * @return next message from result
2822 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2824 return ldap_next_message(ads->ldap.ld, res);
2828 * pull a single string from a ADS result
2829 * @param ads connection to ads server
2830 * @param mem_ctx TALLOC_CTX to use for allocating result string
2831 * @param msg Results of search
2832 * @param field Attribute to retrieve
2833 * @return Result string in talloc context
2835 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2841 size_t converted_size;
2843 values = ldap_get_values(ads->ldap.ld, msg, field);
2847 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2852 ldap_value_free(values);
2857 * pull an array of strings from a ADS result
2858 * @param ads connection to ads server
2859 * @param mem_ctx TALLOC_CTX to use for allocating result string
2860 * @param msg Results of search
2861 * @param field Attribute to retrieve
2862 * @return Result strings in talloc context
2864 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2865 LDAPMessage *msg, const char *field,
2870 size_t i, converted_size;
2872 values = ldap_get_values(ads->ldap.ld, msg, field);
2876 *num_values = ldap_count_values(values);
2878 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2880 ldap_value_free(values);
2884 for (i=0;i<*num_values;i++) {
2885 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2888 ldap_value_free(values);
2894 ldap_value_free(values);
2899 * pull an array of strings from a ADS result
2900 * (handle large multivalue attributes with range retrieval)
2901 * @param ads connection to ads server
2902 * @param mem_ctx TALLOC_CTX to use for allocating result string
2903 * @param msg Results of search
2904 * @param field Attribute to retrieve
2905 * @param current_strings strings returned by a previous call to this function
2906 * @param next_attribute The next query should ask for this attribute
2907 * @param num_values How many values did we get this time?
2908 * @param more_values Are there more values to get?
2909 * @return Result strings in talloc context
2911 char **ads_pull_strings_range(ADS_STRUCT *ads,
2912 TALLOC_CTX *mem_ctx,
2913 LDAPMessage *msg, const char *field,
2914 char **current_strings,
2915 const char **next_attribute,
2916 size_t *num_strings,
2920 char *expected_range_attrib, *range_attr;
2921 BerElement *ptr = NULL;
2924 size_t num_new_strings;
2925 unsigned long int range_start;
2926 unsigned long int range_end;
2928 /* we might have been given the whole lot anyway */
2929 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2930 *more_strings = False;
2934 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2936 /* look for Range result */
2937 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2939 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2940 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2941 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2949 /* nothing here - this field is just empty */
2950 *more_strings = False;
2954 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2955 &range_start, &range_end) == 2) {
2956 *more_strings = True;
2958 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2959 &range_start) == 1) {
2960 *more_strings = False;
2962 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2964 ldap_memfree(range_attr);
2965 *more_strings = False;
2970 if ((*num_strings) != range_start) {
2971 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2972 " - aborting range retreival\n",
2973 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2974 ldap_memfree(range_attr);
2975 *more_strings = False;
2979 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2981 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2982 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2983 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2984 range_attr, (unsigned long int)range_end - range_start + 1,
2985 (unsigned long int)num_new_strings));
2986 ldap_memfree(range_attr);
2987 *more_strings = False;
2991 strings = talloc_realloc(mem_ctx, current_strings, char *,
2992 *num_strings + num_new_strings);
2994 if (strings == NULL) {
2995 ldap_memfree(range_attr);
2996 *more_strings = False;
3000 if (new_strings && num_new_strings) {
3001 memcpy(&strings[*num_strings], new_strings,
3002 sizeof(*new_strings) * num_new_strings);
3005 (*num_strings) += num_new_strings;
3007 if (*more_strings) {
3008 *next_attribute = talloc_asprintf(mem_ctx,
3013 if (!*next_attribute) {
3014 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3015 ldap_memfree(range_attr);
3016 *more_strings = False;
3021 ldap_memfree(range_attr);
3027 * pull a single uint32_t from a ADS result
3028 * @param ads connection to ads server
3029 * @param msg Results of search
3030 * @param field Attribute to retrieve
3031 * @param v Pointer to int to store result
3032 * @return boolean inidicating success
3034 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3039 values = ldap_get_values(ads->ldap.ld, msg, field);
3043 ldap_value_free(values);
3047 *v = atoi(values[0]);
3048 ldap_value_free(values);
3053 * pull a single objectGUID from an ADS result
3054 * @param ads connection to ADS server
3055 * @param msg results of search
3056 * @param guid 37-byte area to receive text guid
3057 * @return boolean indicating success
3059 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3064 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3069 status = GUID_from_ndr_blob(&blob, guid);
3070 talloc_free(blob.data);
3071 return NT_STATUS_IS_OK(status);
3076 * pull a single struct dom_sid from a ADS result
3077 * @param ads connection to ads server
3078 * @param msg Results of search
3079 * @param field Attribute to retrieve
3080 * @param sid Pointer to sid to store result
3081 * @return boolean inidicating success
3083 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3084 struct dom_sid *sid)
3086 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3090 * pull an array of struct dom_sids from a ADS result
3091 * @param ads connection to ads server
3092 * @param mem_ctx TALLOC_CTX for allocating sid array
3093 * @param msg Results of search
3094 * @param field Attribute to retrieve
3095 * @param sids pointer to sid array to allocate
3096 * @return the count of SIDs pulled
3098 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3099 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3101 struct berval **values;
3104 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3109 for (i=0; values[i]; i++)
3113 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3115 ldap_value_free_len(values);
3123 for (i=0; values[i]; i++) {
3125 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3126 values[i]->bv_len, &(*sids)[count]);
3128 struct dom_sid_buf buf;
3129 DBG_DEBUG("pulling SID: %s\n",
3130 dom_sid_str_buf(&(*sids)[count], &buf));
3135 ldap_value_free_len(values);
3140 * pull a struct security_descriptor from a ADS result
3141 * @param ads connection to ads server
3142 * @param mem_ctx TALLOC_CTX for allocating sid array
3143 * @param msg Results of search
3144 * @param field Attribute to retrieve
3145 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3146 * @return boolean inidicating success
3148 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3149 LDAPMessage *msg, const char *field,
3150 struct security_descriptor **sd)
3152 struct berval **values;
3155 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3157 if (!values) return false;
3161 status = unmarshall_sec_desc(mem_ctx,
3162 (uint8_t *)values[0]->bv_val,
3163 values[0]->bv_len, sd);
3164 if (!NT_STATUS_IS_OK(status)) {
3165 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3166 nt_errstr(status)));
3171 ldap_value_free_len(values);
3176 * in order to support usernames longer than 21 characters we need to
3177 * use both the sAMAccountName and the userPrincipalName attributes
3178 * It seems that not all users have the userPrincipalName attribute set
3180 * @param ads connection to ads server
3181 * @param mem_ctx TALLOC_CTX for allocating sid array
3182 * @param msg Results of search
3183 * @return the username
3185 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3191 /* lookup_name() only works on the sAMAccountName to
3192 returning the username portion of userPrincipalName
3193 breaks winbindd_getpwnam() */
3195 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3196 if (ret && (p = strchr_m(ret, '@'))) {
3201 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3206 * find the update serial number - this is the core of the ldap cache
3207 * @param ads connection to ads server
3208 * @param ads connection to ADS server
3209 * @param usn Pointer to retrieved update serial number
3210 * @return status of search
3212 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3214 const char *attrs[] = {"highestCommittedUSN", NULL};
3218 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3219 if (!ADS_ERR_OK(status))
3222 if (ads_count_replies(ads, res) != 1) {
3223 ads_msgfree(ads, res);
3224 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3227 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3228 ads_msgfree(ads, res);
3229 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3232 ads_msgfree(ads, res);
3236 /* parse a ADS timestring - typical string is
3237 '20020917091222.0Z0' which means 09:12.22 17th September
3239 static time_t ads_parse_time(const char *str)
3245 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3246 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3247 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3256 /********************************************************************
3257 ********************************************************************/
3259 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3261 const char *attrs[] = {"currentTime", NULL};
3266 ADS_STRUCT *ads_s = ads;
3268 if (!(ctx = talloc_init("ads_current_time"))) {
3269 return ADS_ERROR(LDAP_NO_MEMORY);
3272 /* establish a new ldap tcp session if necessary */
3274 if ( !ads->ldap.ld ) {
3276 * ADS_STRUCT may be being reused after a
3277 * DC lookup, so ads->ldap.ss may already have a
3278 * good address. If not, re-initialize the passed-in
3279 * ADS_STRUCT with the given server.XXXX parameters.
3281 * Note that this doesn't depend on
3282 * ads->server.ldap_server != NULL,
3283 * as the case where ads->server.ldap_server==NULL and
3284 * ads->ldap.ss != zero_address is precisely the DC
3285 * lookup case where ads->ldap.ss was found by going
3286 * through ads_find_dc() again we want to avoid repeating.
3288 if (is_zero_addr(&ads->ldap.ss)) {
3289 ads_s = ads_init(ads->server.realm,
3290 ads->server.workgroup,
3291 ads->server.ldap_server,
3293 if (ads_s == NULL) {
3294 status = ADS_ERROR(LDAP_NO_MEMORY);
3298 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3299 status = ads_connect( ads_s );
3300 if ( !ADS_ERR_OK(status))
3304 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3305 if (!ADS_ERR_OK(status)) {
3309 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
3311 ads_msgfree(ads_s, res);
3312 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3316 /* but save the time and offset in the original ADS_STRUCT */
3318 ads->config.current_time = ads_parse_time(timestr);
3320 if (ads->config.current_time != 0) {
3321 ads->auth.time_offset = ads->config.current_time - time(NULL);
3322 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3325 ads_msgfree(ads, res);
3327 status = ADS_SUCCESS;
3330 /* free any temporary ads connections */
3331 if ( ads_s != ads ) {
3332 ads_destroy( &ads_s );
3334 talloc_destroy(ctx);
3339 /********************************************************************
3340 ********************************************************************/
3342 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3344 const char *attrs[] = {"domainFunctionality", NULL};
3347 ADS_STRUCT *ads_s = ads;
3349 *val = DS_DOMAIN_FUNCTION_2000;
3351 /* establish a new ldap tcp session if necessary */
3353 if ( !ads->ldap.ld ) {
3355 * ADS_STRUCT may be being reused after a
3356 * DC lookup, so ads->ldap.ss may already have a
3357 * good address. If not, re-initialize the passed-in
3358 * ADS_STRUCT with the given server.XXXX parameters.
3360 * Note that this doesn't depend on
3361 * ads->server.ldap_server != NULL,
3362 * as the case where ads->server.ldap_server==NULL and
3363 * ads->ldap.ss != zero_address is precisely the DC
3364 * lookup case where ads->ldap.ss was found by going
3365 * through ads_find_dc() again we want to avoid repeating.
3367 if (is_zero_addr(&ads->ldap.ss)) {
3368 ads_s = ads_init(ads->server.realm,
3369 ads->server.workgroup,
3370 ads->server.ldap_server,
3372 if (ads_s == NULL ) {
3373 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3377 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3378 status = ads_connect( ads_s );
3379 if ( !ADS_ERR_OK(status))
3383 /* If the attribute does not exist assume it is a Windows 2000
3384 functional domain */
3386 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3387 if (!ADS_ERR_OK(status)) {
3388 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3389 status = ADS_SUCCESS;
3394 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3395 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3397 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3400 ads_msgfree(ads, res);
3403 /* free any temporary ads connections */
3404 if ( ads_s != ads ) {
3405 ads_destroy( &ads_s );
3412 * find the domain sid for our domain
3413 * @param ads connection to ads server
3414 * @param sid Pointer to domain sid
3415 * @return status of search
3417 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3419 const char *attrs[] = {"objectSid", NULL};
3423 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3425 if (!ADS_ERR_OK(rc)) return rc;
3426 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3427 ads_msgfree(ads, res);
3428 return ADS_ERROR_SYSTEM(ENOENT);
3430 ads_msgfree(ads, res);
3436 * find our site name
3437 * @param ads connection to ads server
3438 * @param mem_ctx Pointer to talloc context
3439 * @param site_name Pointer to the sitename
3440 * @return status of search
3442 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3446 const char *dn, *service_name;
3447 const char *attrs[] = { "dsServiceName", NULL };
3449 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3450 if (!ADS_ERR_OK(status)) {
3454 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3455 if (service_name == NULL) {
3456 ads_msgfree(ads, res);
3457 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3460 ads_msgfree(ads, res);
3462 /* go up three levels */
3463 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3465 return ADS_ERROR(LDAP_NO_MEMORY);
3468 *site_name = talloc_strdup(mem_ctx, dn);
3469 if (*site_name == NULL) {
3470 return ADS_ERROR(LDAP_NO_MEMORY);
3475 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3480 * find the site dn where a machine resides
3481 * @param ads connection to ads server
3482 * @param mem_ctx Pointer to talloc context
3483 * @param computer_name name of the machine
3484 * @param site_name Pointer to the sitename
3485 * @return status of search
3487 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3491 const char *parent, *filter;
3492 char *config_context = NULL;
3495 /* shortcut a query */
3496 if (strequal(computer_name, ads->config.ldap_server_name)) {
3497 return ads_site_dn(ads, mem_ctx, site_dn);
3500 status = ads_config_path(ads, mem_ctx, &config_context);
3501 if (!ADS_ERR_OK(status)) {
3505 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3506 if (filter == NULL) {
3507 return ADS_ERROR(LDAP_NO_MEMORY);
3510 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3511 filter, NULL, &res);
3512 if (!ADS_ERR_OK(status)) {
3516 if (ads_count_replies(ads, res) != 1) {
3517 ads_msgfree(ads, res);
3518 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3521 dn = ads_get_dn(ads, mem_ctx, res);
3523 ads_msgfree(ads, res);
3524 return ADS_ERROR(LDAP_NO_MEMORY);
3527 /* go up three levels */
3528 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3529 if (parent == NULL) {
3530 ads_msgfree(ads, res);
3532 return ADS_ERROR(LDAP_NO_MEMORY);
3535 *site_dn = talloc_strdup(mem_ctx, parent);
3536 if (*site_dn == NULL) {
3537 ads_msgfree(ads, res);
3539 return ADS_ERROR(LDAP_NO_MEMORY);
3543 ads_msgfree(ads, res);
3549 * get the upn suffixes for a domain
3550 * @param ads connection to ads server
3551 * @param mem_ctx Pointer to talloc context
3552 * @param suffixes Pointer to an array of suffixes
3553 * @param num_suffixes Pointer to the number of suffixes
3554 * @return status of search
3556 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3561 char *config_context = NULL;
3562 const char *attrs[] = { "uPNSuffixes", NULL };
3564 status = ads_config_path(ads, mem_ctx, &config_context);
3565 if (!ADS_ERR_OK(status)) {
3569 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3571 return ADS_ERROR(LDAP_NO_MEMORY);
3574 status = ads_search_dn(ads, &res, base, attrs);
3575 if (!ADS_ERR_OK(status)) {
3579 if (ads_count_replies(ads, res) != 1) {
3580 ads_msgfree(ads, res);
3581 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3584 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3585 if ((*suffixes) == NULL) {
3586 ads_msgfree(ads, res);
3587 return ADS_ERROR(LDAP_NO_MEMORY);
3590 ads_msgfree(ads, res);
3596 * get the joinable ous for a domain
3597 * @param ads connection to ads server
3598 * @param mem_ctx Pointer to talloc context
3599 * @param ous Pointer to an array of ous
3600 * @param num_ous Pointer to the number of ous
3601 * @return status of search
3603 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3604 TALLOC_CTX *mem_ctx,
3609 LDAPMessage *res = NULL;
3610 LDAPMessage *msg = NULL;
3611 const char *attrs[] = { "dn", NULL };
3614 status = ads_search(ads, &res,
3615 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3617 if (!ADS_ERR_OK(status)) {
3621 count = ads_count_replies(ads, res);
3623 ads_msgfree(ads, res);
3624 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3627 for (msg = ads_first_entry(ads, res); msg;
3628 msg = ads_next_entry(ads, msg)) {
3629 const char **p = discard_const_p(const char *, *ous);
3632 dn = ads_get_dn(ads, talloc_tos(), msg);
3634 ads_msgfree(ads, res);
3635 return ADS_ERROR(LDAP_NO_MEMORY);
3638 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3640 ads_msgfree(ads, res);
3641 return ADS_ERROR(LDAP_NO_MEMORY);
3645 *ous = discard_const_p(char *, p);
3648 ads_msgfree(ads, res);
3655 * pull a struct dom_sid from an extended dn string
3656 * @param mem_ctx TALLOC_CTX
3657 * @param extended_dn string
3658 * @param flags string type of extended_dn
3659 * @param sid pointer to a struct dom_sid
3660 * @return NT_STATUS_OK on success,
3661 * NT_INVALID_PARAMETER on error,
3662 * NT_STATUS_NOT_FOUND if no SID present
3664 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3665 const char *extended_dn,
3666 enum ads_extended_dn_flags flags,
3667 struct dom_sid *sid)
3672 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3675 /* otherwise extended_dn gets stripped off */
3676 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3677 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3680 * ADS_EXTENDED_DN_HEX_STRING:
3681 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3683 * ADS_EXTENDED_DN_STRING (only with w2k3):
3684 * <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
3686 * Object with no SID, such as an Exchange Public Folder
3687 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3690 p = strchr(dn, ';');
3692 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3695 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3696 DEBUG(5,("No SID present in extended dn\n"));
3697 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3700 p += strlen(";<SID=");
3704 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3709 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3713 case ADS_EXTENDED_DN_STRING:
3714 if (!string_to_sid(sid, p)) {
3715 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3718 case ADS_EXTENDED_DN_HEX_STRING: {
3723 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3725 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3728 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3730 DEBUG(10,("failed to parse sid\n"));
3731 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3736 DEBUG(10,("unknown extended dn format\n"));
3737 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3740 return ADS_ERROR_NT(NT_STATUS_OK);
3743 /********************************************************************
3744 ********************************************************************/
3746 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3748 LDAPMessage *res = NULL;
3753 status = ads_find_machine_acct(ads, &res, machine_name);
3754 if (!ADS_ERR_OK(status)) {
3755 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3756 lp_netbios_name()));
3760 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3761 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3765 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3766 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3770 ads_msgfree(ads, res);
3775 /********************************************************************
3776 ********************************************************************/
3778 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3779 LDAPMessage *msg, size_t *num_values)
3781 const char *field = "msDS-AdditionalDnsHostName";
3782 struct berval **values = NULL;
3784 size_t i, converted_size;
3787 * Windows DC implicitly adds a short name for each FQDN added to
3788 * msDS-AdditionalDnsHostName, but it comes with a strage binary
3789 * suffix "\0$" which we should ignore (see bug #14406).
3792 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3793 if (values == NULL) {
3797 *num_values = ldap_count_values_len(values);
3799 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3801 ldap_value_free_len(values);
3805 for (i = 0; i < *num_values; i++) {
3807 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
3809 strnlen(values[i]->bv_val,
3811 &ret[i], &converted_size)) {
3812 ldap_value_free_len(values);
3818 ldap_value_free_len(values);
3822 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
3824 const char *machine_name,
3825 char ***hostnames_array,
3826 size_t *num_hostnames)
3829 LDAPMessage *res = NULL;
3832 status = ads_find_machine_acct(ads,
3835 if (!ADS_ERR_OK(status)) {
3836 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
3841 count = ads_count_replies(ads, res);
3843 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3847 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
3848 if (*hostnames_array == NULL) {
3849 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
3851 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3856 ads_msgfree(ads, res);
3861 /********************************************************************
3862 ********************************************************************/
3864 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3866 LDAPMessage *res = NULL;
3871 status = ads_find_machine_acct(ads, &res, machine_name);
3872 if (!ADS_ERR_OK(status)) {
3873 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3874 lp_netbios_name()));
3878 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3879 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3883 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3884 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3888 ads_msgfree(ads, res);
3893 /********************************************************************
3894 ********************************************************************/
3896 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3898 LDAPMessage *res = NULL;
3904 status = ads_find_machine_acct(ads, &res, machine_name);
3905 if (!ADS_ERR_OK(status)) {
3906 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3907 lp_netbios_name()));
3911 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3912 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3916 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3917 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3921 ads_msgfree(ads, res);
3923 ok = (strlen(name) > 0);
3931 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3934 * Join a machine to a realm
3935 * Creates the machine account and sets the machine password
3936 * @param ads connection to ads server
3937 * @param machine name of host to add
3938 * @param org_unit Organizational unit to place machine in
3939 * @return status of join
3941 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3942 uint32_t account_type, const char *org_unit)
3945 LDAPMessage *res = NULL;
3948 /* machine name must be lowercase */
3949 machine = SMB_STRDUP(machine_name);
3950 strlower_m(machine);
3953 status = ads_find_machine_acct(ads, (void **)&res, machine);
3954 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3955 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3956 status = ads_leave_realm(ads, machine);
3957 if (!ADS_ERR_OK(status)) {
3958 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3959 machine, ads->config.realm));
3964 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3965 if (!ADS_ERR_OK(status)) {
3966 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3971 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3972 if (!ADS_ERR_OK(status)) {
3973 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3979 ads_msgfree(ads, res);
3986 * Delete a machine from the realm
3987 * @param ads connection to ads server
3988 * @param hostname Machine to remove
3989 * @return status of delete
3991 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3996 char *hostnameDN, *host;
3998 LDAPControl ldap_control;
3999 LDAPControl * pldap_control[2] = {NULL, NULL};
4001 pldap_control[0] = &ldap_control;
4002 memset(&ldap_control, 0, sizeof(LDAPControl));
4003 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4005 /* hostname must be lowercase */
4006 host = SMB_STRDUP(hostname);
4007 if (!strlower_m(host)) {
4009 return ADS_ERROR_SYSTEM(EINVAL);
4012 status = ads_find_machine_acct(ads, &res, host);
4013 if (!ADS_ERR_OK(status)) {
4014 DEBUG(0, ("Host account for %s does not exist.\n", host));
4019 msg = ads_first_entry(ads, res);
4022 return ADS_ERROR_SYSTEM(ENOENT);
4025 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4026 if (hostnameDN == NULL) {
4028 return ADS_ERROR_SYSTEM(ENOENT);
4031 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4033 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4035 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4038 if (rc != LDAP_SUCCESS) {
4039 const char *attrs[] = { "cn", NULL };
4040 LDAPMessage *msg_sub;
4042 /* we only search with scope ONE, we do not expect any further
4043 * objects to be created deeper */
4045 status = ads_do_search_retry(ads, hostnameDN,
4046 LDAP_SCOPE_ONELEVEL,
4047 "(objectclass=*)", attrs, &res);
4049 if (!ADS_ERR_OK(status)) {
4051 TALLOC_FREE(hostnameDN);
4055 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4056 msg_sub = ads_next_entry(ads, msg_sub)) {
4060 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4062 TALLOC_FREE(hostnameDN);
4063 return ADS_ERROR(LDAP_NO_MEMORY);
4066 status = ads_del_dn(ads, dn);
4067 if (!ADS_ERR_OK(status)) {
4068 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4071 TALLOC_FREE(hostnameDN);
4078 /* there should be no subordinate objects anymore */
4079 status = ads_do_search_retry(ads, hostnameDN,
4080 LDAP_SCOPE_ONELEVEL,
4081 "(objectclass=*)", attrs, &res);
4083 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4085 TALLOC_FREE(hostnameDN);
4089 /* delete hostnameDN now */
4090 status = ads_del_dn(ads, hostnameDN);
4091 if (!ADS_ERR_OK(status)) {
4093 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4094 TALLOC_FREE(hostnameDN);
4099 TALLOC_FREE(hostnameDN);
4101 status = ads_find_machine_acct(ads, &res, host);
4102 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4103 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4104 DEBUG(3, ("Failed to remove host account.\n"));
4114 * pull all token-sids from an LDAP dn
4115 * @param ads connection to ads server
4116 * @param mem_ctx TALLOC_CTX for allocating sid array
4117 * @param dn of LDAP object
4118 * @param user_sid pointer to struct dom_sid (objectSid)
4119 * @param primary_group_sid pointer to struct dom_sid (self composed)
4120 * @param sids pointer to sid array to allocate
4121 * @param num_sids counter of SIDs pulled
4122 * @return status of token query
4124 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4125 TALLOC_CTX *mem_ctx,
4127 struct dom_sid *user_sid,
4128 struct dom_sid *primary_group_sid,
4129 struct dom_sid **sids,
4133 LDAPMessage *res = NULL;
4135 size_t tmp_num_sids;
4136 struct dom_sid *tmp_sids;
4137 struct dom_sid tmp_user_sid;
4138 struct dom_sid tmp_primary_group_sid;
4140 const char *attrs[] = {
4147 status = ads_search_retry_dn(ads, &res, dn, attrs);
4148 if (!ADS_ERR_OK(status)) {
4152 count = ads_count_replies(ads, res);
4154 ads_msgfree(ads, res);
4155 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4158 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4159 ads_msgfree(ads, res);
4160 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4163 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4164 ads_msgfree(ads, res);
4165 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4169 /* hack to compose the primary group sid without knowing the
4172 struct dom_sid domsid;
4174 sid_copy(&domsid, &tmp_user_sid);
4176 if (!sid_split_rid(&domsid, NULL)) {
4177 ads_msgfree(ads, res);
4178 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4181 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4182 ads_msgfree(ads, res);
4183 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4187 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4189 if (tmp_num_sids == 0 || !tmp_sids) {
4190 ads_msgfree(ads, res);
4191 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4195 *num_sids = tmp_num_sids;
4203 *user_sid = tmp_user_sid;
4206 if (primary_group_sid) {
4207 *primary_group_sid = tmp_primary_group_sid;
4210 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4212 ads_msgfree(ads, res);
4213 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4217 * Find a sAMAccoutName in LDAP
4218 * @param ads connection to ads server
4219 * @param mem_ctx TALLOC_CTX for allocating sid array
4220 * @param samaccountname to search
4221 * @param uac_ret uint32_t pointer userAccountControl attribute value
4222 * @param dn_ret pointer to dn
4223 * @return status of token query
4225 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4226 TALLOC_CTX *mem_ctx,
4227 const char *samaccountname,
4229 const char **dn_ret)
4232 const char *attrs[] = { "userAccountControl", NULL };
4234 LDAPMessage *res = NULL;
4238 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4240 if (filter == NULL) {
4241 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4245 status = ads_do_search_all(ads, ads->config.bind_path,
4247 filter, attrs, &res);
4249 if (!ADS_ERR_OK(status)) {
4253 if (ads_count_replies(ads, res) != 1) {
4254 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4258 dn = ads_get_dn(ads, talloc_tos(), res);
4260 status = ADS_ERROR(LDAP_NO_MEMORY);
4264 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4265 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4274 *dn_ret = talloc_strdup(mem_ctx, dn);
4276 status = ADS_ERROR(LDAP_NO_MEMORY);
4282 ads_msgfree(ads, res);
4288 * find our configuration path
4289 * @param ads connection to ads server
4290 * @param mem_ctx Pointer to talloc context
4291 * @param config_path Pointer to the config path
4292 * @return status of search
4294 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4295 TALLOC_CTX *mem_ctx,
4299 LDAPMessage *res = NULL;
4300 const char *config_context = NULL;
4301 const char *attrs[] = { "configurationNamingContext", NULL };
4303 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4304 "(objectclass=*)", attrs, &res);
4305 if (!ADS_ERR_OK(status)) {
4309 config_context = ads_pull_string(ads, mem_ctx, res,
4310 "configurationNamingContext");
4311 ads_msgfree(ads, res);
4312 if (!config_context) {
4313 return ADS_ERROR(LDAP_NO_MEMORY);
4317 *config_path = talloc_strdup(mem_ctx, config_context);
4318 if (!*config_path) {
4319 return ADS_ERROR(LDAP_NO_MEMORY);
4323 return ADS_ERROR(LDAP_SUCCESS);
4327 * find the displayName of an extended right
4328 * @param ads connection to ads server
4329 * @param config_path The config path
4330 * @param mem_ctx Pointer to talloc context
4331 * @param GUID struct of the rightsGUID
4332 * @return status of search
4334 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4335 const char *config_path,
4336 TALLOC_CTX *mem_ctx,
4337 const struct GUID *rights_guid)
4340 LDAPMessage *res = NULL;
4342 const char *attrs[] = { "displayName", NULL };
4343 const char *result = NULL;
4346 if (!ads || !mem_ctx || !rights_guid) {
4350 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4351 GUID_string(mem_ctx, rights_guid));
4356 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4361 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4363 if (!ADS_ERR_OK(rc)) {
4367 if (ads_count_replies(ads, res) != 1) {
4371 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4374 ads_msgfree(ads, res);
4379 * verify or build and verify an account ou
4380 * @param mem_ctx Pointer to talloc context
4381 * @param ads connection to ads server
4383 * @return status of search
4386 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4388 const char **account_ou)
4394 if (account_ou == NULL) {
4395 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4398 if (*account_ou != NULL) {
4399 exploded_dn = ldap_explode_dn(*account_ou, 0);
4401 ldap_value_free(exploded_dn);
4406 ou_string = ads_ou_string(ads, *account_ou);
4408 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4411 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4412 ads->config.bind_path);
4413 SAFE_FREE(ou_string);
4416 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4419 exploded_dn = ldap_explode_dn(name, 0);
4421 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4423 ldap_value_free(exploded_dn);