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)
388 struct ip_service *ip_list = NULL;
391 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
394 status = get_sorted_dc_list(domain, NULL, &ip_list, &count,
396 if (!NT_STATUS_IS_OK(status)) {
400 /* remove servers which are known to be dead based on
401 the corresponding DNS method */
403 for (i = 0; i < count; ++i) {
404 char server[INET6_ADDRSTRLEN];
406 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
409 check_negative_conn_cache(realm, server))) {
410 /* Ensure we add the workgroup name for this
411 IP address as negative too. */
412 add_failed_connection_entry(
414 NT_STATUS_UNSUCCESSFUL);
419 status = cldap_ping_list(ads, domain, ip_list, count);
427 /**********************************************************************
428 resolve a name and perform an "ldap ping" using DNS
429 **********************************************************************/
431 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
435 struct ip_service *ip_list = NULL;
438 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
441 status = get_sorted_dc_list(realm, sitename, &ip_list, &count,
443 if (!NT_STATUS_IS_OK(status)) {
448 status = cldap_ping_list(ads, realm, ip_list, count);
455 /**********************************************************************
456 Try to find an AD dc using our internal name resolution routines
457 Try the realm first and then then workgroup name if netbios is not
459 **********************************************************************/
461 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
463 const char *c_domain = "";
465 bool use_own_domain = False;
466 char *sitename = NULL;
467 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
470 /* if the realm and workgroup are both empty, assume they are ours */
473 c_realm = ads->server.realm;
479 /* special case where no realm and no workgroup means our own */
480 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
481 use_own_domain = True;
482 c_realm = lp_realm();
486 if (!lp_disable_netbios()) {
487 if (use_own_domain) {
488 c_domain = lp_workgroup();
490 c_domain = ads->server.workgroup;
491 if (!*c_realm && (!c_domain || !*c_domain)) {
492 c_domain = lp_workgroup();
501 if (!*c_realm && !*c_domain) {
502 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
504 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
508 * In case of LDAP we use get_dc_name() as that
509 * creates the custom krb5.conf file
511 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
513 struct sockaddr_storage ip_out;
515 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
516 " and falling back to domain '%s'\n",
519 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
522 * we call ads_try_connect() to fill in the
523 * ads->config details
525 ok = ads_try_connect(ads, false, &ip_out);
531 return NT_STATUS_NO_LOGON_SERVERS;
535 sitename = sitename_fetch(talloc_tos(), c_realm);
536 status = resolve_and_ping_dns(ads, sitename, c_realm);
538 if (NT_STATUS_IS_OK(status)) {
539 TALLOC_FREE(sitename);
543 /* In case we failed to contact one of our closest DC on our
545 * need to try to find another DC, retry with a site-less SRV
550 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
551 "our site (%s), Trying to find another DC "
552 "for realm '%s' (domain '%s')\n",
553 sitename, c_realm, c_domain));
554 namecache_delete(c_realm, 0x1C);
556 resolve_and_ping_dns(ads, NULL, c_realm);
558 if (NT_STATUS_IS_OK(status)) {
559 TALLOC_FREE(sitename);
564 TALLOC_FREE(sitename);
567 /* try netbios as fallback - if permitted,
568 or if configuration specifically requests it */
571 DEBUG(3, ("ads_find_dc: falling back to netbios "
572 "name resolution for domain '%s' (realm '%s')\n",
576 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
577 if (NT_STATUS_IS_OK(status)) {
582 DEBUG(1, ("ads_find_dc: "
583 "name resolution for realm '%s' (domain '%s') failed: %s\n",
584 c_realm, c_domain, nt_errstr(status)));
588 * Connect to the LDAP server
589 * @param ads Pointer to an existing ADS_STRUCT
590 * @return status of connection
592 ADS_STATUS ads_connect(ADS_STRUCT *ads)
594 int version = LDAP_VERSION3;
597 char addr[INET6_ADDRSTRLEN];
598 struct samba_sockaddr existing_sa = {0};
601 * ads_connect can be passed in a reused ADS_STRUCT
602 * with an existing non-zero ads->ldap.ss IP address
603 * that was stored by going through ads_find_dc()
604 * if ads->server.ldap_server was NULL.
606 * If ads->server.ldap_server is still NULL but
607 * the target address isn't the zero address, then
608 * store that address off off before zeroing out
609 * ads->ldap so we don't keep doing multiple calls
610 * to ads_find_dc() in the reuse case.
612 * If a caller wants a clean ADS_STRUCT they
613 * will re-initialize by calling ads_init(), or
614 * call ads_destroy() both of which ensures
615 * ads->ldap.ss is a properly zero'ed out valid IP
618 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
619 /* Save off the address we previously found by ads_find_dc(). */
620 bool ok = sockaddr_storage_to_samba_sockaddr(&existing_sa,
623 return ADS_ERROR_NT(NT_STATUS_INVALID_ADDRESS);
628 ZERO_STRUCT(ads->ldap_wrap_data);
629 ads->ldap.last_attempt = time_mono(NULL);
630 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
632 /* try with a user specified server */
634 if (DEBUGLEVEL >= 11) {
635 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
636 DEBUG(11,("ads_connect: entering\n"));
637 DEBUGADD(11,("%s\n", s));
641 if (ads->server.ldap_server) {
643 struct sockaddr_storage ss;
645 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
647 DEBUG(5,("ads_connect: unable to resolve name %s\n",
648 ads->server.ldap_server));
649 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
652 ok = ads_try_connect(ads, ads->server.gc, &ss);
657 /* The choice of which GC use is handled one level up in
658 ads_connect_gc(). If we continue on from here with
659 ads_find_dc() we will get GC searches on port 389 which
660 doesn't work. --jerry */
662 if (ads->server.gc == true) {
663 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
666 if (ads->server.no_fallback) {
667 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
672 if (!is_zero_addr(&existing_sa.u.ss)) {
673 /* We saved off who we should talk to. */
674 bool ok = ads_try_connect(ads,
681 * Keep trying to find a server and fall through
682 * into ads_find_dc() again.
686 ntstatus = ads_find_dc(ads);
687 if (NT_STATUS_IS_OK(ntstatus)) {
691 status = ADS_ERROR_NT(ntstatus);
696 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
697 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
699 if (!ads->auth.user_name) {
700 /* Must use the userPrincipalName value here or sAMAccountName
701 and not servicePrincipalName; found by Guenther Deschner */
703 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
704 DEBUG(0,("ads_connect: asprintf fail.\n"));
705 ads->auth.user_name = NULL;
709 if (!ads->auth.realm) {
710 ads->auth.realm = SMB_STRDUP(ads->config.realm);
713 if (!ads->auth.kdc_server) {
714 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
715 ads->auth.kdc_server = SMB_STRDUP(addr);
718 /* If the caller() requested no LDAP bind, then we are done */
720 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
721 status = ADS_SUCCESS;
725 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
726 if (!ads->ldap_wrap_data.mem_ctx) {
727 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
731 /* Otherwise setup the TCP LDAP session */
733 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
735 ads->ldap.port, lp_ldap_timeout());
736 if (ads->ldap.ld == NULL) {
737 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
740 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
742 /* cache the successful connection for workgroup and realm */
743 if (ads_closest_dc(ads)) {
744 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
745 saf_store( ads->server.realm, ads->config.ldap_server_name);
748 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
750 /* fill in the current time and offsets */
752 status = ads_current_time( ads );
753 if ( !ADS_ERR_OK(status) ) {
757 /* Now do the bind */
759 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
760 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
764 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
765 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
769 status = ads_sasl_bind(ads);
772 if (DEBUGLEVEL >= 11) {
773 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
774 DEBUG(11,("ads_connect: leaving with: %s\n",
775 ads_errstr(status)));
776 DEBUGADD(11,("%s\n", s));
784 * Connect to the LDAP server using given credentials
785 * @param ads Pointer to an existing ADS_STRUCT
786 * @return status of connection
788 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
790 ads->auth.flags |= ADS_AUTH_USER_CREDS;
792 return ads_connect(ads);
796 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
797 * @param ads Pointer to an existing ADS_STRUCT
799 * Sets the ads->ldap.ss to a valid
800 * zero ip address that can be detected by
801 * our is_zero_addr() function. Otherwise
802 * it is left as AF_UNSPEC (0).
804 void ads_zero_ldap(ADS_STRUCT *ads)
806 ZERO_STRUCT(ads->ldap);
808 * Initialize the sockaddr_storage so we can use
809 * sockaddr test functions against it.
811 zero_sockaddr(&ads->ldap.ss);
815 * Disconnect the LDAP server
816 * @param ads Pointer to an existing ADS_STRUCT
818 void ads_disconnect(ADS_STRUCT *ads)
821 ldap_unbind(ads->ldap.ld);
824 if (ads->ldap_wrap_data.wrap_ops &&
825 ads->ldap_wrap_data.wrap_ops->disconnect) {
826 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
828 if (ads->ldap_wrap_data.mem_ctx) {
829 talloc_free(ads->ldap_wrap_data.mem_ctx);
832 ZERO_STRUCT(ads->ldap_wrap_data);
836 Duplicate a struct berval into talloc'ed memory
838 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
840 struct berval *value;
842 if (!in_val) return NULL;
844 value = talloc_zero(ctx, struct berval);
847 if (in_val->bv_len == 0) return value;
849 value->bv_len = in_val->bv_len;
850 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
856 Make a values list out of an array of (struct berval *)
858 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
859 const struct berval **in_vals)
861 struct berval **values;
864 if (!in_vals) return NULL;
865 for (i=0; in_vals[i]; i++)
867 values = talloc_zero_array(ctx, struct berval *, i+1);
868 if (!values) return NULL;
870 for (i=0; in_vals[i]; i++) {
871 values[i] = dup_berval(ctx, in_vals[i]);
877 UTF8-encode a values list out of an array of (char *)
879 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
885 if (!in_vals) return NULL;
886 for (i=0; in_vals[i]; i++)
888 values = talloc_zero_array(ctx, char *, i+1);
889 if (!values) return NULL;
891 for (i=0; in_vals[i]; i++) {
892 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
901 Pull a (char *) array out of a UTF8-encoded values list
903 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
907 size_t converted_size;
909 if (!in_vals) return NULL;
910 for (i=0; in_vals[i]; i++)
912 values = talloc_zero_array(ctx, char *, i+1);
913 if (!values) return NULL;
915 for (i=0; in_vals[i]; i++) {
916 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
918 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
919 "%s", strerror(errno)));
926 * Do a search with paged results. cookie must be null on the first
927 * call, and then returned on each subsequent call. It will be null
928 * again when the entire search is complete
929 * @param ads connection to ads server
930 * @param bind_path Base dn for the search
931 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
932 * @param expr Search expression - specified in local charset
933 * @param attrs Attributes to retrieve - specified in utf8 or ascii
934 * @param res ** which will contain results - free res* with ads_msgfree()
935 * @param count Number of entries retrieved on this page
936 * @param cookie The paged results cookie to be returned on subsequent calls
937 * @return status of search
939 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
940 const char *bind_path,
941 int scope, const char *expr,
942 const char **attrs, void *args,
944 int *count, struct berval **cookie)
947 char *utf8_expr, *utf8_path, **search_attrs = NULL;
948 size_t converted_size;
949 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
950 BerElement *cookie_be = NULL;
951 struct berval *cookie_bv= NULL;
952 BerElement *ext_be = NULL;
953 struct berval *ext_bv= NULL;
956 ads_control *external_control = (ads_control *) args;
960 if (!(ctx = talloc_init("ads_do_paged_search_args")))
961 return ADS_ERROR(LDAP_NO_MEMORY);
963 /* 0 means the conversion worked but the result was empty
964 so we only fail if it's -1. In any case, it always
965 at least nulls out the dest */
966 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
967 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
973 if (!attrs || !(*attrs))
976 /* This would be the utf8-encoded version...*/
977 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
978 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
984 /* Paged results only available on ldap v3 or later */
985 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
986 if (version < LDAP_VERSION3) {
987 rc = LDAP_NOT_SUPPORTED;
991 cookie_be = ber_alloc_t(LBER_USE_DER);
993 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
994 ber_bvfree(*cookie); /* don't need it from last time */
997 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
999 ber_flatten(cookie_be, &cookie_bv);
1000 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1001 PagedResults.ldctl_iscritical = (char) 1;
1002 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1003 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1005 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1006 NoReferrals.ldctl_iscritical = (char) 0;
1007 NoReferrals.ldctl_value.bv_len = 0;
1008 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1010 if (external_control &&
1011 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1012 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1014 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1015 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1017 /* win2k does not accept a ldctl_value beeing passed in */
1019 if (external_control->val != 0) {
1021 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1022 rc = LDAP_NO_MEMORY;
1026 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1027 rc = LDAP_NO_MEMORY;
1030 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1031 rc = LDAP_NO_MEMORY;
1035 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1036 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1039 ExternalCtrl.ldctl_value.bv_len = 0;
1040 ExternalCtrl.ldctl_value.bv_val = NULL;
1043 controls[0] = &NoReferrals;
1044 controls[1] = &PagedResults;
1045 controls[2] = &ExternalCtrl;
1049 controls[0] = &NoReferrals;
1050 controls[1] = &PagedResults;
1054 /* we need to disable referrals as the openldap libs don't
1055 handle them and paged results at the same time. Using them
1056 together results in the result record containing the server
1057 page control being removed from the result list (tridge/jmcd)
1059 leaving this in despite the control that says don't generate
1060 referrals, in case the server doesn't support it (jmcd)
1062 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1064 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1065 search_attrs, 0, controls,
1066 NULL, LDAP_NO_LIMIT,
1067 (LDAPMessage **)res);
1069 ber_free(cookie_be, 1);
1070 ber_bvfree(cookie_bv);
1073 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1074 ldap_err2string(rc)));
1075 if (rc == LDAP_OTHER) {
1079 ret = ldap_parse_result(ads->ldap.ld,
1087 if (ret == LDAP_SUCCESS) {
1088 DEBUG(3, ("ldap_search_with_timeout(%s) "
1089 "error: %s\n", expr, ldap_errmsg));
1090 ldap_memfree(ldap_errmsg);
1096 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1097 NULL, &rcontrols, 0);
1103 for (i=0; rcontrols[i]; i++) {
1104 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1105 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1106 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1108 /* the berval is the cookie, but must be freed when
1110 if (cookie_bv->bv_len) /* still more to do */
1111 *cookie=ber_bvdup(cookie_bv);
1114 ber_bvfree(cookie_bv);
1115 ber_free(cookie_be, 1);
1119 ldap_controls_free(rcontrols);
1122 talloc_destroy(ctx);
1125 ber_free(ext_be, 1);
1132 if (rc != LDAP_SUCCESS && *res != NULL) {
1133 ads_msgfree(ads, *res);
1137 /* if/when we decide to utf8-encode attrs, take out this next line */
1138 TALLOC_FREE(search_attrs);
1140 return ADS_ERROR(rc);
1143 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1144 int scope, const char *expr,
1145 const char **attrs, LDAPMessage **res,
1146 int *count, struct berval **cookie)
1148 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1153 * Get all results for a search. This uses ads_do_paged_search() to return
1154 * all entries in a large search.
1155 * @param ads connection to ads server
1156 * @param bind_path Base dn for the search
1157 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1158 * @param expr Search expression
1159 * @param attrs Attributes to retrieve
1160 * @param res ** which will contain results - free res* with ads_msgfree()
1161 * @return status of search
1163 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1164 int scope, const char *expr,
1165 const char **attrs, void *args,
1168 struct berval *cookie = NULL;
1173 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1176 if (!ADS_ERR_OK(status))
1179 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1181 LDAPMessage *res2 = NULL;
1182 LDAPMessage *msg, *next;
1184 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1185 attrs, args, &res2, &count, &cookie);
1186 if (!ADS_ERR_OK(status)) {
1190 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1191 that this works on all ldap libs, but I have only tested with openldap */
1192 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1193 next = ads_next_message(ads, msg);
1194 ldap_add_result_entry((LDAPMessage **)res, msg);
1196 /* note that we do not free res2, as the memory is now
1197 part of the main returned list */
1200 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1201 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1207 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1208 int scope, const char *expr,
1209 const char **attrs, LDAPMessage **res)
1211 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1214 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1215 int scope, const char *expr,
1216 const char **attrs, uint32_t sd_flags,
1221 args.control = ADS_SD_FLAGS_OID;
1222 args.val = sd_flags;
1223 args.critical = True;
1225 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1230 * Run a function on all results for a search. Uses ads_do_paged_search() and
1231 * runs the function as each page is returned, using ads_process_results()
1232 * @param ads connection to ads server
1233 * @param bind_path Base dn for the search
1234 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1235 * @param expr Search expression - specified in local charset
1236 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1237 * @param fn Function which takes attr name, values list, and data_area
1238 * @param data_area Pointer which is passed to function on each call
1239 * @return status of search
1241 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1242 int scope, const char *expr, const char **attrs,
1243 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1246 struct berval *cookie = NULL;
1251 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1254 if (!ADS_ERR_OK(status)) return status;
1256 ads_process_results(ads, res, fn, data_area);
1257 ads_msgfree(ads, res);
1260 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1261 &res, &count, &cookie);
1263 if (!ADS_ERR_OK(status)) break;
1265 ads_process_results(ads, res, fn, data_area);
1266 ads_msgfree(ads, res);
1273 * Do a search with a timeout.
1274 * @param ads connection to ads server
1275 * @param bind_path Base dn for the search
1276 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1277 * @param expr Search expression
1278 * @param attrs Attributes to retrieve
1279 * @param res ** which will contain results - free res* with ads_msgfree()
1280 * @return status of search
1282 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1284 const char **attrs, LDAPMessage **res)
1287 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1288 size_t converted_size;
1292 if (!(ctx = talloc_init("ads_do_search"))) {
1293 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1294 return ADS_ERROR(LDAP_NO_MEMORY);
1297 /* 0 means the conversion worked but the result was empty
1298 so we only fail if it's negative. In any case, it always
1299 at least nulls out the dest */
1300 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1301 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1303 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1304 rc = LDAP_NO_MEMORY;
1308 if (!attrs || !(*attrs))
1309 search_attrs = NULL;
1311 /* This would be the utf8-encoded version...*/
1312 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1313 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1315 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1316 rc = LDAP_NO_MEMORY;
1321 /* see the note in ads_do_paged_search - we *must* disable referrals */
1322 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1324 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1325 search_attrs, 0, NULL, NULL,
1327 (LDAPMessage **)res);
1329 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1330 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1335 talloc_destroy(ctx);
1336 /* if/when we decide to utf8-encode attrs, take out this next line */
1337 TALLOC_FREE(search_attrs);
1338 return ADS_ERROR(rc);
1341 * Do a general ADS search
1342 * @param ads connection to ads server
1343 * @param res ** which will contain results - free res* with ads_msgfree()
1344 * @param expr Search expression
1345 * @param attrs Attributes to retrieve
1346 * @return status of search
1348 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1349 const char *expr, const char **attrs)
1351 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1356 * Do a search on a specific DistinguishedName
1357 * @param ads connection to ads server
1358 * @param res ** which will contain results - free res* with ads_msgfree()
1359 * @param dn DistinguishName to search
1360 * @param attrs Attributes to retrieve
1361 * @return status of search
1363 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1364 const char *dn, const char **attrs)
1366 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1371 * Free up memory from a ads_search
1372 * @param ads connection to ads server
1373 * @param msg Search results to free
1375 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1382 * Get a dn from search results
1383 * @param ads connection to ads server
1384 * @param msg Search result
1387 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1389 char *utf8_dn, *unix_dn;
1390 size_t converted_size;
1392 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1395 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1399 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1400 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1404 ldap_memfree(utf8_dn);
1409 * Get the parent from a dn
1410 * @param dn the dn to return the parent from
1411 * @return parent dn string
1413 char *ads_parent_dn(const char *dn)
1421 p = strchr(dn, ',');
1431 * Find a machine account given a hostname
1432 * @param ads connection to ads server
1433 * @param res ** which will contain results - free res* with ads_msgfree()
1434 * @param host Hostname to search for
1435 * @return status of search
1437 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1438 const char *machine)
1442 const char *attrs[] = {
1443 /* This is how Windows checks for machine accounts */
1446 "userAccountControl",
1448 "ServicePrincipalName",
1449 "userPrincipalName",
1452 /* Additional attributes Samba checks */
1453 "msDS-AdditionalDnsHostName",
1454 "msDS-SupportedEncryptionTypes",
1455 "nTSecurityDescriptor",
1459 TALLOC_CTX *frame = talloc_stackframe();
1463 /* the easiest way to find a machine account anywhere in the tree
1464 is to look for hostname$ */
1465 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1467 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1471 status = ads_search(ads, res, expr, attrs);
1472 if (ADS_ERR_OK(status)) {
1473 if (ads_count_replies(ads, *res) != 1) {
1474 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1484 * Initialize a list of mods to be used in a modify request
1485 * @param ctx An initialized TALLOC_CTX
1486 * @return allocated ADS_MODLIST
1488 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1490 #define ADS_MODLIST_ALLOC_SIZE 10
1493 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1494 /* -1 is safety to make sure we don't go over the end.
1495 need to reset it to NULL before doing ldap modify */
1496 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1498 return (ADS_MODLIST)mods;
1503 add an attribute to the list, with values list already constructed
1505 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1506 int mod_op, const char *name,
1507 const void *_invals)
1510 LDAPMod **modlist = (LDAPMod **) *mods;
1511 struct berval **ber_values = NULL;
1512 char **char_values = NULL;
1515 mod_op = LDAP_MOD_DELETE;
1517 if (mod_op & LDAP_MOD_BVALUES) {
1518 const struct berval **b;
1519 b = discard_const_p(const struct berval *, _invals);
1520 ber_values = ads_dup_values(ctx, b);
1523 c = discard_const_p(const char *, _invals);
1524 char_values = ads_push_strvals(ctx, c);
1528 /* find the first empty slot */
1529 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1531 if (modlist[curmod] == (LDAPMod *) -1) {
1532 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1533 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1534 return ADS_ERROR(LDAP_NO_MEMORY);
1535 memset(&modlist[curmod], 0,
1536 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1537 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1538 *mods = (ADS_MODLIST)modlist;
1541 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1542 return ADS_ERROR(LDAP_NO_MEMORY);
1543 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1544 if (mod_op & LDAP_MOD_BVALUES) {
1545 modlist[curmod]->mod_bvalues = ber_values;
1546 } else if (mod_op & LDAP_MOD_DELETE) {
1547 modlist[curmod]->mod_values = NULL;
1549 modlist[curmod]->mod_values = char_values;
1552 modlist[curmod]->mod_op = mod_op;
1553 return ADS_ERROR(LDAP_SUCCESS);
1557 * Add a single string value to a mod list
1558 * @param ctx An initialized TALLOC_CTX
1559 * @param mods An initialized ADS_MODLIST
1560 * @param name The attribute name to add
1561 * @param val The value to add - NULL means DELETE
1562 * @return ADS STATUS indicating success of add
1564 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1565 const char *name, const char *val)
1567 const char *values[2];
1573 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1574 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1578 * Add an array of string values to a mod list
1579 * @param ctx An initialized TALLOC_CTX
1580 * @param mods An initialized ADS_MODLIST
1581 * @param name The attribute name to add
1582 * @param vals The array of string values to add - NULL means DELETE
1583 * @return ADS STATUS indicating success of add
1585 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1586 const char *name, const char **vals)
1589 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1590 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1591 name, (const void **) vals);
1595 * Add a single ber-encoded value to a mod list
1596 * @param ctx An initialized TALLOC_CTX
1597 * @param mods An initialized ADS_MODLIST
1598 * @param name The attribute name to add
1599 * @param val The value to add - NULL means DELETE
1600 * @return ADS STATUS indicating success of add
1602 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1603 const char *name, const struct berval *val)
1605 const struct berval *values[2];
1610 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1611 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1612 name, (const void **) values);
1615 static void ads_print_error(int ret, LDAP *ld)
1618 char *ld_error = NULL;
1619 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1620 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1622 ldap_err2string(ret),
1624 SAFE_FREE(ld_error);
1629 * Perform an ldap modify
1630 * @param ads connection to ads server
1631 * @param mod_dn DistinguishedName to modify
1632 * @param mods list of modifications to perform
1633 * @return status of modify
1635 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1638 char *utf8_dn = NULL;
1639 size_t converted_size;
1641 this control is needed to modify that contains a currently
1642 non-existent attribute (but allowable for the object) to run
1644 LDAPControl PermitModify = {
1645 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1648 LDAPControl *controls[2];
1650 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1652 controls[0] = &PermitModify;
1655 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1656 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1659 /* find the end of the list, marked by NULL or -1 */
1660 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1661 /* make sure the end of the list is NULL */
1663 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1664 (LDAPMod **) mods, controls, NULL);
1665 ads_print_error(ret, ads->ldap.ld);
1666 TALLOC_FREE(utf8_dn);
1667 return ADS_ERROR(ret);
1671 * Perform an ldap add
1672 * @param ads connection to ads server
1673 * @param new_dn DistinguishedName to add
1674 * @param mods list of attributes and values for DN
1675 * @return status of add
1677 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1680 char *utf8_dn = NULL;
1681 size_t converted_size;
1683 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1685 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1686 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1687 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1690 /* find the end of the list, marked by NULL or -1 */
1691 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1692 /* make sure the end of the list is NULL */
1695 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1696 ads_print_error(ret, ads->ldap.ld);
1697 TALLOC_FREE(utf8_dn);
1698 return ADS_ERROR(ret);
1702 * Delete a DistinguishedName
1703 * @param ads connection to ads server
1704 * @param new_dn DistinguishedName to delete
1705 * @return status of delete
1707 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1710 char *utf8_dn = NULL;
1711 size_t converted_size;
1712 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1713 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1714 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1717 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1719 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1720 ads_print_error(ret, ads->ldap.ld);
1721 TALLOC_FREE(utf8_dn);
1722 return ADS_ERROR(ret);
1726 * Build an org unit string
1727 * if org unit is Computers or blank then assume a container, otherwise
1728 * assume a / separated list of organisational units.
1729 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1730 * @param ads connection to ads server
1731 * @param org_unit Organizational unit
1732 * @return org unit string - caller must free
1734 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1738 if (!org_unit || !*org_unit) {
1740 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1742 /* samba4 might not yet respond to a wellknownobject-query */
1743 return ret ? ret : SMB_STRDUP("cn=Computers");
1746 if (strequal(org_unit, "Computers")) {
1747 return SMB_STRDUP("cn=Computers");
1750 /* jmcd: removed "\\" from the separation chars, because it is
1751 needed as an escape for chars like '#' which are valid in an
1753 return ads_build_path(org_unit, "/", "ou=", 1);
1757 * Get a org unit string for a well-known GUID
1758 * @param ads connection to ads server
1759 * @param wknguid Well known GUID
1760 * @return org unit string - caller must free
1762 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1765 LDAPMessage *res = NULL;
1766 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1767 **bind_dn_exp = NULL;
1768 const char *attrs[] = {"distinguishedName", NULL};
1769 int new_ln, wkn_ln, bind_ln, i;
1771 if (wknguid == NULL) {
1775 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1776 DEBUG(1, ("asprintf failed!\n"));
1780 status = ads_search_dn(ads, &res, base, attrs);
1781 if (!ADS_ERR_OK(status)) {
1782 DEBUG(1,("Failed while searching for: %s\n", base));
1786 if (ads_count_replies(ads, res) != 1) {
1790 /* substitute the bind-path from the well-known-guid-search result */
1791 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1796 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1801 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1806 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1808 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1811 new_ln = wkn_ln - bind_ln;
1813 ret = SMB_STRDUP(wkn_dn_exp[0]);
1818 for (i=1; i < new_ln; i++) {
1821 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1827 ret = SMB_STRDUP(s);
1836 ads_msgfree(ads, res);
1837 TALLOC_FREE(wkn_dn);
1839 ldap_value_free(wkn_dn_exp);
1842 ldap_value_free(bind_dn_exp);
1849 * Adds (appends) an item to an attribute array, rather then
1850 * replacing the whole list
1851 * @param ctx An initialized TALLOC_CTX
1852 * @param mods An initialized ADS_MODLIST
1853 * @param name name of the ldap attribute to append to
1854 * @param vals an array of values to add
1855 * @return status of addition
1858 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1859 const char *name, const char **vals)
1861 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1862 (const void *) vals);
1866 * Determines the an account's current KVNO via an LDAP lookup
1867 * @param ads An initialized ADS_STRUCT
1868 * @param account_name the NT samaccountname.
1869 * @return the kvno for the account, or -1 in case of a failure.
1872 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1874 LDAPMessage *res = NULL;
1875 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1877 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1878 char *dn_string = NULL;
1881 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1882 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1885 ret = ads_search(ads, &res, filter, attrs);
1887 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1888 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1889 ads_msgfree(ads, res);
1893 dn_string = ads_get_dn(ads, talloc_tos(), res);
1895 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1896 ads_msgfree(ads, res);
1899 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1900 TALLOC_FREE(dn_string);
1902 /* ---------------------------------------------------------
1903 * 0 is returned as a default KVNO from this point on...
1904 * This is done because Windows 2000 does not support key
1905 * version numbers. Chances are that a failure in the next
1906 * step is simply due to Windows 2000 being used for a
1907 * domain controller. */
1910 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1911 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1912 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1913 ads_msgfree(ads, res);
1918 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1919 ads_msgfree(ads, res);
1924 * Determines the computer account's current KVNO via an LDAP lookup
1925 * @param ads An initialized ADS_STRUCT
1926 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1927 * @return the kvno for the computer account, or -1 in case of a failure.
1930 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1932 char *computer_account = NULL;
1935 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1939 kvno = ads_get_kvno(ads, computer_account);
1940 free(computer_account);
1946 * This clears out all registered spn's for a given hostname
1947 * @param ads An initilaized ADS_STRUCT
1948 * @param machine_name the NetBIOS name of the computer.
1949 * @return 0 upon success, non-zero otherwise.
1952 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1955 LDAPMessage *res = NULL;
1957 const char *servicePrincipalName[1] = {NULL};
1959 char *dn_string = NULL;
1961 ret = ads_find_machine_acct(ads, &res, machine_name);
1962 if (!ADS_ERR_OK(ret)) {
1963 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1964 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1965 ads_msgfree(ads, res);
1969 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1970 ctx = talloc_init("ads_clear_service_principal_names");
1972 ads_msgfree(ads, res);
1973 return ADS_ERROR(LDAP_NO_MEMORY);
1976 if (!(mods = ads_init_mods(ctx))) {
1977 talloc_destroy(ctx);
1978 ads_msgfree(ads, res);
1979 return ADS_ERROR(LDAP_NO_MEMORY);
1981 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1982 if (!ADS_ERR_OK(ret)) {
1983 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1984 ads_msgfree(ads, res);
1985 talloc_destroy(ctx);
1988 dn_string = ads_get_dn(ads, talloc_tos(), res);
1990 talloc_destroy(ctx);
1991 ads_msgfree(ads, res);
1992 return ADS_ERROR(LDAP_NO_MEMORY);
1994 ret = ads_gen_mod(ads, dn_string, mods);
1995 TALLOC_FREE(dn_string);
1996 if (!ADS_ERR_OK(ret)) {
1997 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1999 ads_msgfree(ads, res);
2000 talloc_destroy(ctx);
2004 ads_msgfree(ads, res);
2005 talloc_destroy(ctx);
2010 * @brief Search for an element in a string array.
2012 * @param[in] el_array The string array to search.
2014 * @param[in] num_el The number of elements in the string array.
2016 * @param[in] el The string to search.
2018 * @return True if found, false if not.
2020 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2024 if (el_array == NULL || num_el == 0 || el == NULL) {
2028 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2031 cmp = strcasecmp_m(el_array[i], el);
2041 * @brief This gets the service principal names of an existing computer account.
2043 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2045 * @param[in] ads The ADS context to use.
2047 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2048 * identify the computer account.
2050 * @param[in] spn_array A pointer to store the array for SPNs.
2052 * @param[in] num_spns The number of principals stored in the array.
2054 * @return 0 on success, or a ADS error if a failure occurred.
2056 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2058 const char *machine_name,
2063 LDAPMessage *res = NULL;
2066 status = ads_find_machine_acct(ads,
2069 if (!ADS_ERR_OK(status)) {
2070 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2075 count = ads_count_replies(ads, res);
2077 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2081 *spn_array = ads_pull_strings(ads,
2084 "servicePrincipalName",
2086 if (*spn_array == NULL) {
2087 DEBUG(1, ("Host account for %s does not have service principal "
2090 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2095 ads_msgfree(ads, res);
2101 * This adds a service principal name to an existing computer account
2102 * (found by hostname) in AD.
2103 * @param ads An initialized ADS_STRUCT
2104 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2105 * @param spns An array or strings for the service principals to add,
2106 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2107 * @return 0 upon sucess, or non-zero if a failure occurs
2110 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2111 const char *machine_name,
2116 LDAPMessage *res = NULL;
2118 char *dn_string = NULL;
2119 const char **servicePrincipalName = spns;
2121 ret = ads_find_machine_acct(ads, &res, machine_name);
2122 if (!ADS_ERR_OK(ret)) {
2123 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2125 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2126 ads_msgfree(ads, res);
2130 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2131 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2132 ads_msgfree(ads, res);
2133 return ADS_ERROR(LDAP_NO_MEMORY);
2136 DEBUG(5,("ads_add_service_principal_name: INFO: "
2137 "Adding %s to host %s\n",
2138 spns[0] ? "N/A" : spns[0], machine_name));
2141 DEBUG(5,("ads_add_service_principal_name: INFO: "
2142 "Adding %s to host %s\n",
2143 spns[1] ? "N/A" : spns[1], machine_name));
2145 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2146 ret = ADS_ERROR(LDAP_NO_MEMORY);
2150 ret = ads_add_strlist(ctx,
2152 "servicePrincipalName",
2153 servicePrincipalName);
2154 if (!ADS_ERR_OK(ret)) {
2155 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2159 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2160 ret = ADS_ERROR(LDAP_NO_MEMORY);
2164 ret = ads_gen_mod(ads, dn_string, mods);
2165 if (!ADS_ERR_OK(ret)) {
2166 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2172 ads_msgfree(ads, res);
2176 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2179 uint32_t acct_ctrl = 0;
2182 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2190 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2192 const struct berval *machine_pw_val)
2196 TALLOC_CTX *frame = talloc_stackframe();
2197 uint32_t acct_control;
2198 char *control_str = NULL;
2199 const char *attrs[] = {
2203 LDAPMessage *res = NULL;
2206 dn = ads_get_dn(ads, frame, msg);
2208 ret = ADS_ERROR(LDAP_NO_MEMORY);
2212 acct_control = ads_get_acct_ctrl(ads, msg);
2213 if (acct_control == 0) {
2214 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2219 * Changing the password, disables the account. So we need to change the
2220 * userAccountControl flags to enable it again.
2222 mods = ads_init_mods(frame);
2224 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2228 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2230 ret = ads_gen_mod(ads, dn, mods);
2231 if (!ADS_ERR_OK(ret)) {
2237 * To activate the account, we need to disable and enable it.
2239 acct_control |= UF_ACCOUNTDISABLE;
2241 control_str = talloc_asprintf(frame, "%u", acct_control);
2242 if (control_str == NULL) {
2243 ret = ADS_ERROR(LDAP_NO_MEMORY);
2247 mods = ads_init_mods(frame);
2249 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2253 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2255 ret = ads_gen_mod(ads, dn, mods);
2256 if (!ADS_ERR_OK(ret)) {
2260 TALLOC_FREE(control_str);
2263 * Enable the account again.
2265 acct_control &= ~UF_ACCOUNTDISABLE;
2267 control_str = talloc_asprintf(frame, "%u", acct_control);
2268 if (control_str == NULL) {
2269 ret = ADS_ERROR(LDAP_NO_MEMORY);
2273 mods = ads_init_mods(frame);
2275 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2279 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2281 ret = ads_gen_mod(ads, dn, mods);
2282 if (!ADS_ERR_OK(ret)) {
2286 TALLOC_FREE(control_str);
2288 ret = ads_search_dn(ads, &res, dn, attrs);
2289 ads_msgfree(ads, res);
2298 * adds a machine account to the ADS server
2299 * @param ads An intialized ADS_STRUCT
2300 * @param machine_name - the NetBIOS machine name of this account.
2301 * @param account_type A number indicating the type of account to create
2302 * @param org_unit The LDAP path in which to place this account
2303 * @return 0 upon success, or non-zero otherwise
2306 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2307 const char *machine_name,
2308 const char *machine_password,
2309 const char *org_unit,
2310 uint32_t etype_list,
2311 const char *dns_domain_name)
2314 char *samAccountName = NULL;
2315 char *controlstr = NULL;
2316 TALLOC_CTX *ctx = NULL;
2318 char *machine_escaped = NULL;
2319 char *dns_hostname = NULL;
2320 char *new_dn = NULL;
2321 char *utf8_pw = NULL;
2322 size_t utf8_pw_len = 0;
2323 char *utf16_pw = NULL;
2324 size_t utf16_pw_len = 0;
2325 struct berval machine_pw_val;
2327 const char **spn_array = NULL;
2328 size_t num_spns = 0;
2329 const char *spn_prefix[] = {
2331 "RestrictedKrbHost",
2334 LDAPMessage *res = NULL;
2335 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2337 ctx = talloc_init("ads_add_machine_acct");
2339 return ADS_ERROR(LDAP_NO_MEMORY);
2342 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2343 if (machine_escaped == NULL) {
2344 ret = ADS_ERROR(LDAP_NO_MEMORY);
2348 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2349 if (utf8_pw == NULL) {
2350 ret = ADS_ERROR(LDAP_NO_MEMORY);
2353 utf8_pw_len = strlen(utf8_pw);
2355 ok = convert_string_talloc(ctx,
2356 CH_UTF8, CH_UTF16MUNGED,
2357 utf8_pw, utf8_pw_len,
2358 (void *)&utf16_pw, &utf16_pw_len);
2360 ret = ADS_ERROR(LDAP_NO_MEMORY);
2364 machine_pw_val = (struct berval) {
2366 .bv_len = utf16_pw_len,
2369 /* Check if the machine account already exists. */
2370 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2371 if (ADS_ERR_OK(ret)) {
2372 /* Change the machine account password */
2373 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2374 ads_msgfree(ads, res);
2378 ads_msgfree(ads, res);
2380 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2381 if (new_dn == NULL) {
2382 ret = ADS_ERROR(LDAP_NO_MEMORY);
2386 /* Create machine account */
2388 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2389 if (samAccountName == NULL) {
2390 ret = ADS_ERROR(LDAP_NO_MEMORY);
2394 dns_hostname = talloc_asprintf(ctx,
2398 if (dns_hostname == NULL) {
2399 ret = ADS_ERROR(LDAP_NO_MEMORY);
2403 /* Add dns_hostname SPNs */
2404 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2405 char *spn = talloc_asprintf(ctx,
2410 ret = ADS_ERROR(LDAP_NO_MEMORY);
2414 ok = add_string_to_array(spn_array,
2419 ret = ADS_ERROR(LDAP_NO_MEMORY);
2424 /* Add machine_name SPNs */
2425 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2426 char *spn = talloc_asprintf(ctx,
2431 ret = ADS_ERROR(LDAP_NO_MEMORY);
2435 ok = add_string_to_array(spn_array,
2440 ret = ADS_ERROR(LDAP_NO_MEMORY);
2445 /* Make sure to NULL terminate the array */
2446 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2447 if (spn_array == NULL) {
2448 ret = ADS_ERROR(LDAP_NO_MEMORY);
2451 spn_array[num_spns] = NULL;
2453 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2454 if (controlstr == NULL) {
2455 ret = ADS_ERROR(LDAP_NO_MEMORY);
2459 mods = ads_init_mods(ctx);
2461 ret = ADS_ERROR(LDAP_NO_MEMORY);
2465 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2466 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2467 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2468 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2469 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2470 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2472 ret = ads_gen_add(ads, new_dn, mods);
2475 SAFE_FREE(machine_escaped);
2476 talloc_destroy(ctx);
2482 * move a machine account to another OU on the ADS server
2483 * @param ads - An intialized ADS_STRUCT
2484 * @param machine_name - the NetBIOS machine name of this account.
2485 * @param org_unit - The LDAP path in which to place this account
2486 * @param moved - whether we moved the machine account (optional)
2487 * @return 0 upon success, or non-zero otherwise
2490 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2491 const char *org_unit, bool *moved)
2495 LDAPMessage *res = NULL;
2496 char *filter = NULL;
2497 char *computer_dn = NULL;
2499 char *computer_rdn = NULL;
2500 bool need_move = False;
2502 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2503 rc = ADS_ERROR(LDAP_NO_MEMORY);
2507 /* Find pre-existing machine */
2508 rc = ads_search(ads, &res, filter, NULL);
2509 if (!ADS_ERR_OK(rc)) {
2513 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2515 rc = ADS_ERROR(LDAP_NO_MEMORY);
2519 parent_dn = ads_parent_dn(computer_dn);
2520 if (strequal(parent_dn, org_unit)) {
2526 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2527 rc = ADS_ERROR(LDAP_NO_MEMORY);
2531 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2532 org_unit, 1, NULL, NULL);
2533 rc = ADS_ERROR(ldap_status);
2536 ads_msgfree(ads, res);
2538 TALLOC_FREE(computer_dn);
2539 SAFE_FREE(computer_rdn);
2541 if (!ADS_ERR_OK(rc)) {
2553 dump a binary result from ldap
2555 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2558 for (i=0; values[i]; i++) {
2560 printf("%s: ", field);
2561 for (j=0; j<values[i]->bv_len; j++) {
2562 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2568 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2571 for (i=0; values[i]; i++) {
2573 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2576 status = GUID_from_ndr_blob(&in, &guid);
2577 if (NT_STATUS_IS_OK(status)) {
2578 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2580 printf("%s: INVALID GUID\n", field);
2586 dump a sid result from ldap
2588 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2591 for (i=0; values[i]; i++) {
2594 struct dom_sid_buf tmp;
2595 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2596 values[i]->bv_len, &sid);
2600 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2605 dump ntSecurityDescriptor
2607 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2609 TALLOC_CTX *frame = talloc_stackframe();
2610 struct security_descriptor *psd;
2613 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2614 values[0]->bv_len, &psd);
2615 if (!NT_STATUS_IS_OK(status)) {
2616 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2617 nt_errstr(status)));
2623 ads_disp_sd(ads, talloc_tos(), psd);
2630 dump a string result from ldap
2632 static void dump_string(const char *field, char **values)
2635 for (i=0; values[i]; i++) {
2636 printf("%s: %s\n", field, values[i]);
2641 dump a field from LDAP on stdout
2645 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2650 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2652 {"objectGUID", False, dump_guid},
2653 {"netbootGUID", False, dump_guid},
2654 {"nTSecurityDescriptor", False, dump_sd},
2655 {"dnsRecord", False, dump_binary},
2656 {"objectSid", False, dump_sid},
2657 {"tokenGroups", False, dump_sid},
2658 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2659 {"tokengroupsGlobalandUniversal", False, dump_sid},
2660 {"mS-DS-CreatorSID", False, dump_sid},
2661 {"msExchMailboxGuid", False, dump_guid},
2666 if (!field) { /* must be end of an entry */
2671 for (i=0; handlers[i].name; i++) {
2672 if (strcasecmp_m(handlers[i].name, field) == 0) {
2673 if (!values) /* first time, indicate string or not */
2674 return handlers[i].string;
2675 handlers[i].handler(ads, field, (struct berval **) values);
2679 if (!handlers[i].name) {
2680 if (!values) /* first time, indicate string conversion */
2682 dump_string(field, (char **)values);
2688 * Dump a result from LDAP on stdout
2689 * used for debugging
2690 * @param ads connection to ads server
2691 * @param res Results to dump
2694 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2696 ads_process_results(ads, res, ads_dump_field, NULL);
2700 * Walk through results, calling a function for each entry found.
2701 * The function receives a field name, a berval * array of values,
2702 * and a data area passed through from the start. The function is
2703 * called once with null for field and values at the end of each
2705 * @param ads connection to ads server
2706 * @param res Results to process
2707 * @param fn Function for processing each result
2708 * @param data_area user-defined area to pass to function
2710 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2711 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2716 size_t converted_size;
2718 if (!(ctx = talloc_init("ads_process_results")))
2721 for (msg = ads_first_entry(ads, res); msg;
2722 msg = ads_next_entry(ads, msg)) {
2726 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2727 (LDAPMessage *)msg,&b);
2729 utf8_field=ldap_next_attribute(ads->ldap.ld,
2730 (LDAPMessage *)msg,b)) {
2731 struct berval **ber_vals;
2737 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2740 DEBUG(0,("ads_process_results: "
2741 "pull_utf8_talloc failed: %s",
2745 string = fn(ads, field, NULL, data_area);
2750 utf8_vals = ldap_get_values(ads->ldap.ld,
2751 (LDAPMessage *)msg, field);
2752 p = discard_const_p(const char *, utf8_vals);
2753 str_vals = ads_pull_strvals(ctx, p);
2754 fn(ads, field, (void **) str_vals, data_area);
2755 ldap_value_free(utf8_vals);
2757 ber_vals = ldap_get_values_len(ads->ldap.ld,
2758 (LDAPMessage *)msg, field);
2759 fn(ads, field, (void **) ber_vals, data_area);
2761 ldap_value_free_len(ber_vals);
2763 ldap_memfree(utf8_field);
2766 talloc_free_children(ctx);
2767 fn(ads, NULL, NULL, data_area); /* completed an entry */
2770 talloc_destroy(ctx);
2774 * count how many replies are in a LDAPMessage
2775 * @param ads connection to ads server
2776 * @param res Results to count
2777 * @return number of replies
2779 int ads_count_replies(ADS_STRUCT *ads, void *res)
2781 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2785 * pull the first entry from a ADS result
2786 * @param ads connection to ads server
2787 * @param res Results of search
2788 * @return first entry from result
2790 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2792 return ldap_first_entry(ads->ldap.ld, res);
2796 * pull the next entry from a ADS result
2797 * @param ads connection to ads server
2798 * @param res Results of search
2799 * @return next entry from result
2801 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2803 return ldap_next_entry(ads->ldap.ld, res);
2807 * pull the first message from a ADS result
2808 * @param ads connection to ads server
2809 * @param res Results of search
2810 * @return first message from result
2812 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2814 return ldap_first_message(ads->ldap.ld, res);
2818 * pull the next message from a ADS result
2819 * @param ads connection to ads server
2820 * @param res Results of search
2821 * @return next message from result
2823 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2825 return ldap_next_message(ads->ldap.ld, res);
2829 * pull a single string from a ADS result
2830 * @param ads connection to ads server
2831 * @param mem_ctx TALLOC_CTX to use for allocating result string
2832 * @param msg Results of search
2833 * @param field Attribute to retrieve
2834 * @return Result string in talloc context
2836 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2842 size_t converted_size;
2844 values = ldap_get_values(ads->ldap.ld, msg, field);
2848 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2853 ldap_value_free(values);
2858 * pull an array of strings from a ADS result
2859 * @param ads connection to ads server
2860 * @param mem_ctx TALLOC_CTX to use for allocating result string
2861 * @param msg Results of search
2862 * @param field Attribute to retrieve
2863 * @return Result strings in talloc context
2865 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2866 LDAPMessage *msg, const char *field,
2871 size_t i, converted_size;
2873 values = ldap_get_values(ads->ldap.ld, msg, field);
2877 *num_values = ldap_count_values(values);
2879 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2881 ldap_value_free(values);
2885 for (i=0;i<*num_values;i++) {
2886 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2889 ldap_value_free(values);
2895 ldap_value_free(values);
2900 * pull an array of strings from a ADS result
2901 * (handle large multivalue attributes with range retrieval)
2902 * @param ads connection to ads server
2903 * @param mem_ctx TALLOC_CTX to use for allocating result string
2904 * @param msg Results of search
2905 * @param field Attribute to retrieve
2906 * @param current_strings strings returned by a previous call to this function
2907 * @param next_attribute The next query should ask for this attribute
2908 * @param num_values How many values did we get this time?
2909 * @param more_values Are there more values to get?
2910 * @return Result strings in talloc context
2912 char **ads_pull_strings_range(ADS_STRUCT *ads,
2913 TALLOC_CTX *mem_ctx,
2914 LDAPMessage *msg, const char *field,
2915 char **current_strings,
2916 const char **next_attribute,
2917 size_t *num_strings,
2921 char *expected_range_attrib, *range_attr;
2922 BerElement *ptr = NULL;
2925 size_t num_new_strings;
2926 unsigned long int range_start;
2927 unsigned long int range_end;
2929 /* we might have been given the whole lot anyway */
2930 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2931 *more_strings = False;
2935 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2937 /* look for Range result */
2938 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2940 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2941 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2942 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2950 /* nothing here - this field is just empty */
2951 *more_strings = False;
2955 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2956 &range_start, &range_end) == 2) {
2957 *more_strings = True;
2959 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2960 &range_start) == 1) {
2961 *more_strings = False;
2963 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2965 ldap_memfree(range_attr);
2966 *more_strings = False;
2971 if ((*num_strings) != range_start) {
2972 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2973 " - aborting range retreival\n",
2974 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2975 ldap_memfree(range_attr);
2976 *more_strings = False;
2980 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2982 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2983 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2984 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2985 range_attr, (unsigned long int)range_end - range_start + 1,
2986 (unsigned long int)num_new_strings));
2987 ldap_memfree(range_attr);
2988 *more_strings = False;
2992 strings = talloc_realloc(mem_ctx, current_strings, char *,
2993 *num_strings + num_new_strings);
2995 if (strings == NULL) {
2996 ldap_memfree(range_attr);
2997 *more_strings = False;
3001 if (new_strings && num_new_strings) {
3002 memcpy(&strings[*num_strings], new_strings,
3003 sizeof(*new_strings) * num_new_strings);
3006 (*num_strings) += num_new_strings;
3008 if (*more_strings) {
3009 *next_attribute = talloc_asprintf(mem_ctx,
3014 if (!*next_attribute) {
3015 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3016 ldap_memfree(range_attr);
3017 *more_strings = False;
3022 ldap_memfree(range_attr);
3028 * pull a single uint32_t from a ADS result
3029 * @param ads connection to ads server
3030 * @param msg Results of search
3031 * @param field Attribute to retrieve
3032 * @param v Pointer to int to store result
3033 * @return boolean inidicating success
3035 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3040 values = ldap_get_values(ads->ldap.ld, msg, field);
3044 ldap_value_free(values);
3048 *v = atoi(values[0]);
3049 ldap_value_free(values);
3054 * pull a single objectGUID from an ADS result
3055 * @param ads connection to ADS server
3056 * @param msg results of search
3057 * @param guid 37-byte area to receive text guid
3058 * @return boolean indicating success
3060 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3065 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3070 status = GUID_from_ndr_blob(&blob, guid);
3071 talloc_free(blob.data);
3072 return NT_STATUS_IS_OK(status);
3077 * pull a single struct dom_sid from a ADS result
3078 * @param ads connection to ads server
3079 * @param msg Results of search
3080 * @param field Attribute to retrieve
3081 * @param sid Pointer to sid to store result
3082 * @return boolean inidicating success
3084 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3085 struct dom_sid *sid)
3087 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3091 * pull an array of struct dom_sids from a ADS result
3092 * @param ads connection to ads server
3093 * @param mem_ctx TALLOC_CTX for allocating sid array
3094 * @param msg Results of search
3095 * @param field Attribute to retrieve
3096 * @param sids pointer to sid array to allocate
3097 * @return the count of SIDs pulled
3099 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3100 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3102 struct berval **values;
3105 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3110 for (i=0; values[i]; i++)
3114 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3116 ldap_value_free_len(values);
3124 for (i=0; values[i]; i++) {
3126 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3127 values[i]->bv_len, &(*sids)[count]);
3129 struct dom_sid_buf buf;
3130 DBG_DEBUG("pulling SID: %s\n",
3131 dom_sid_str_buf(&(*sids)[count], &buf));
3136 ldap_value_free_len(values);
3141 * pull a struct security_descriptor from a ADS result
3142 * @param ads connection to ads server
3143 * @param mem_ctx TALLOC_CTX for allocating sid array
3144 * @param msg Results of search
3145 * @param field Attribute to retrieve
3146 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3147 * @return boolean inidicating success
3149 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3150 LDAPMessage *msg, const char *field,
3151 struct security_descriptor **sd)
3153 struct berval **values;
3156 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3158 if (!values) return false;
3162 status = unmarshall_sec_desc(mem_ctx,
3163 (uint8_t *)values[0]->bv_val,
3164 values[0]->bv_len, sd);
3165 if (!NT_STATUS_IS_OK(status)) {
3166 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3167 nt_errstr(status)));
3172 ldap_value_free_len(values);
3177 * in order to support usernames longer than 21 characters we need to
3178 * use both the sAMAccountName and the userPrincipalName attributes
3179 * It seems that not all users have the userPrincipalName attribute set
3181 * @param ads connection to ads server
3182 * @param mem_ctx TALLOC_CTX for allocating sid array
3183 * @param msg Results of search
3184 * @return the username
3186 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3192 /* lookup_name() only works on the sAMAccountName to
3193 returning the username portion of userPrincipalName
3194 breaks winbindd_getpwnam() */
3196 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3197 if (ret && (p = strchr_m(ret, '@'))) {
3202 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3207 * find the update serial number - this is the core of the ldap cache
3208 * @param ads connection to ads server
3209 * @param ads connection to ADS server
3210 * @param usn Pointer to retrieved update serial number
3211 * @return status of search
3213 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3215 const char *attrs[] = {"highestCommittedUSN", NULL};
3219 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3220 if (!ADS_ERR_OK(status))
3223 if (ads_count_replies(ads, res) != 1) {
3224 ads_msgfree(ads, res);
3225 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3228 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3229 ads_msgfree(ads, res);
3230 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3233 ads_msgfree(ads, res);
3237 /* parse a ADS timestring - typical string is
3238 '20020917091222.0Z0' which means 09:12.22 17th September
3240 static time_t ads_parse_time(const char *str)
3246 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3247 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3248 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3257 /********************************************************************
3258 ********************************************************************/
3260 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3262 const char *attrs[] = {"currentTime", NULL};
3267 ADS_STRUCT *ads_s = ads;
3269 if (!(ctx = talloc_init("ads_current_time"))) {
3270 return ADS_ERROR(LDAP_NO_MEMORY);
3273 /* establish a new ldap tcp session if necessary */
3275 if ( !ads->ldap.ld ) {
3277 * ADS_STRUCT may be being reused after a
3278 * DC lookup, so ads->ldap.ss may already have a
3279 * good address. If not, re-initialize the passed-in
3280 * ADS_STRUCT with the given server.XXXX parameters.
3282 * Note that this doesn't depend on
3283 * ads->server.ldap_server != NULL,
3284 * as the case where ads->server.ldap_server==NULL and
3285 * ads->ldap.ss != zero_address is precisely the DC
3286 * lookup case where ads->ldap.ss was found by going
3287 * through ads_find_dc() again we want to avoid repeating.
3289 if (is_zero_addr(&ads->ldap.ss)) {
3290 ads_s = ads_init(ads->server.realm,
3291 ads->server.workgroup,
3292 ads->server.ldap_server,
3294 if (ads_s == NULL) {
3295 status = ADS_ERROR(LDAP_NO_MEMORY);
3299 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3300 status = ads_connect( ads_s );
3301 if ( !ADS_ERR_OK(status))
3305 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3306 if (!ADS_ERR_OK(status)) {
3310 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
3312 ads_msgfree(ads_s, res);
3313 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3317 /* but save the time and offset in the original ADS_STRUCT */
3319 ads->config.current_time = ads_parse_time(timestr);
3321 if (ads->config.current_time != 0) {
3322 ads->auth.time_offset = ads->config.current_time - time(NULL);
3323 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3326 ads_msgfree(ads, res);
3328 status = ADS_SUCCESS;
3331 /* free any temporary ads connections */
3332 if ( ads_s != ads ) {
3333 ads_destroy( &ads_s );
3335 talloc_destroy(ctx);
3340 /********************************************************************
3341 ********************************************************************/
3343 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3345 const char *attrs[] = {"domainFunctionality", NULL};
3348 ADS_STRUCT *ads_s = ads;
3350 *val = DS_DOMAIN_FUNCTION_2000;
3352 /* establish a new ldap tcp session if necessary */
3354 if ( !ads->ldap.ld ) {
3356 * ADS_STRUCT may be being reused after a
3357 * DC lookup, so ads->ldap.ss may already have a
3358 * good address. If not, re-initialize the passed-in
3359 * ADS_STRUCT with the given server.XXXX parameters.
3361 * Note that this doesn't depend on
3362 * ads->server.ldap_server != NULL,
3363 * as the case where ads->server.ldap_server==NULL and
3364 * ads->ldap.ss != zero_address is precisely the DC
3365 * lookup case where ads->ldap.ss was found by going
3366 * through ads_find_dc() again we want to avoid repeating.
3368 if (is_zero_addr(&ads->ldap.ss)) {
3369 ads_s = ads_init(ads->server.realm,
3370 ads->server.workgroup,
3371 ads->server.ldap_server,
3373 if (ads_s == NULL ) {
3374 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3378 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3379 status = ads_connect( ads_s );
3380 if ( !ADS_ERR_OK(status))
3384 /* If the attribute does not exist assume it is a Windows 2000
3385 functional domain */
3387 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3388 if (!ADS_ERR_OK(status)) {
3389 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3390 status = ADS_SUCCESS;
3395 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3396 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3398 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3401 ads_msgfree(ads, res);
3404 /* free any temporary ads connections */
3405 if ( ads_s != ads ) {
3406 ads_destroy( &ads_s );
3413 * find the domain sid for our domain
3414 * @param ads connection to ads server
3415 * @param sid Pointer to domain sid
3416 * @return status of search
3418 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3420 const char *attrs[] = {"objectSid", NULL};
3424 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3426 if (!ADS_ERR_OK(rc)) return rc;
3427 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3428 ads_msgfree(ads, res);
3429 return ADS_ERROR_SYSTEM(ENOENT);
3431 ads_msgfree(ads, res);
3437 * find our site name
3438 * @param ads connection to ads server
3439 * @param mem_ctx Pointer to talloc context
3440 * @param site_name Pointer to the sitename
3441 * @return status of search
3443 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3447 const char *dn, *service_name;
3448 const char *attrs[] = { "dsServiceName", NULL };
3450 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3451 if (!ADS_ERR_OK(status)) {
3455 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3456 if (service_name == NULL) {
3457 ads_msgfree(ads, res);
3458 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3461 ads_msgfree(ads, res);
3463 /* go up three levels */
3464 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3466 return ADS_ERROR(LDAP_NO_MEMORY);
3469 *site_name = talloc_strdup(mem_ctx, dn);
3470 if (*site_name == NULL) {
3471 return ADS_ERROR(LDAP_NO_MEMORY);
3476 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3481 * find the site dn where a machine resides
3482 * @param ads connection to ads server
3483 * @param mem_ctx Pointer to talloc context
3484 * @param computer_name name of the machine
3485 * @param site_name Pointer to the sitename
3486 * @return status of search
3488 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3492 const char *parent, *filter;
3493 char *config_context = NULL;
3496 /* shortcut a query */
3497 if (strequal(computer_name, ads->config.ldap_server_name)) {
3498 return ads_site_dn(ads, mem_ctx, site_dn);
3501 status = ads_config_path(ads, mem_ctx, &config_context);
3502 if (!ADS_ERR_OK(status)) {
3506 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3507 if (filter == NULL) {
3508 return ADS_ERROR(LDAP_NO_MEMORY);
3511 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3512 filter, NULL, &res);
3513 if (!ADS_ERR_OK(status)) {
3517 if (ads_count_replies(ads, res) != 1) {
3518 ads_msgfree(ads, res);
3519 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3522 dn = ads_get_dn(ads, mem_ctx, res);
3524 ads_msgfree(ads, res);
3525 return ADS_ERROR(LDAP_NO_MEMORY);
3528 /* go up three levels */
3529 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3530 if (parent == NULL) {
3531 ads_msgfree(ads, res);
3533 return ADS_ERROR(LDAP_NO_MEMORY);
3536 *site_dn = talloc_strdup(mem_ctx, parent);
3537 if (*site_dn == NULL) {
3538 ads_msgfree(ads, res);
3540 return ADS_ERROR(LDAP_NO_MEMORY);
3544 ads_msgfree(ads, res);
3550 * get the upn suffixes for a domain
3551 * @param ads connection to ads server
3552 * @param mem_ctx Pointer to talloc context
3553 * @param suffixes Pointer to an array of suffixes
3554 * @param num_suffixes Pointer to the number of suffixes
3555 * @return status of search
3557 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3562 char *config_context = NULL;
3563 const char *attrs[] = { "uPNSuffixes", NULL };
3565 status = ads_config_path(ads, mem_ctx, &config_context);
3566 if (!ADS_ERR_OK(status)) {
3570 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3572 return ADS_ERROR(LDAP_NO_MEMORY);
3575 status = ads_search_dn(ads, &res, base, attrs);
3576 if (!ADS_ERR_OK(status)) {
3580 if (ads_count_replies(ads, res) != 1) {
3581 ads_msgfree(ads, res);
3582 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3585 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3586 if ((*suffixes) == NULL) {
3587 ads_msgfree(ads, res);
3588 return ADS_ERROR(LDAP_NO_MEMORY);
3591 ads_msgfree(ads, res);
3597 * get the joinable ous for a domain
3598 * @param ads connection to ads server
3599 * @param mem_ctx Pointer to talloc context
3600 * @param ous Pointer to an array of ous
3601 * @param num_ous Pointer to the number of ous
3602 * @return status of search
3604 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3605 TALLOC_CTX *mem_ctx,
3610 LDAPMessage *res = NULL;
3611 LDAPMessage *msg = NULL;
3612 const char *attrs[] = { "dn", NULL };
3615 status = ads_search(ads, &res,
3616 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3618 if (!ADS_ERR_OK(status)) {
3622 count = ads_count_replies(ads, res);
3624 ads_msgfree(ads, res);
3625 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3628 for (msg = ads_first_entry(ads, res); msg;
3629 msg = ads_next_entry(ads, msg)) {
3630 const char **p = discard_const_p(const char *, *ous);
3633 dn = ads_get_dn(ads, talloc_tos(), msg);
3635 ads_msgfree(ads, res);
3636 return ADS_ERROR(LDAP_NO_MEMORY);
3639 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3641 ads_msgfree(ads, res);
3642 return ADS_ERROR(LDAP_NO_MEMORY);
3646 *ous = discard_const_p(char *, p);
3649 ads_msgfree(ads, res);
3656 * pull a struct dom_sid from an extended dn string
3657 * @param mem_ctx TALLOC_CTX
3658 * @param extended_dn string
3659 * @param flags string type of extended_dn
3660 * @param sid pointer to a struct dom_sid
3661 * @return NT_STATUS_OK on success,
3662 * NT_INVALID_PARAMETER on error,
3663 * NT_STATUS_NOT_FOUND if no SID present
3665 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3666 const char *extended_dn,
3667 enum ads_extended_dn_flags flags,
3668 struct dom_sid *sid)
3673 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3676 /* otherwise extended_dn gets stripped off */
3677 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3678 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3681 * ADS_EXTENDED_DN_HEX_STRING:
3682 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3684 * ADS_EXTENDED_DN_STRING (only with w2k3):
3685 * <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
3687 * Object with no SID, such as an Exchange Public Folder
3688 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3691 p = strchr(dn, ';');
3693 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3696 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3697 DEBUG(5,("No SID present in extended dn\n"));
3698 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3701 p += strlen(";<SID=");
3705 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3710 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3714 case ADS_EXTENDED_DN_STRING:
3715 if (!string_to_sid(sid, p)) {
3716 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3719 case ADS_EXTENDED_DN_HEX_STRING: {
3724 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3726 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3729 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3731 DEBUG(10,("failed to parse sid\n"));
3732 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3737 DEBUG(10,("unknown extended dn format\n"));
3738 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3741 return ADS_ERROR_NT(NT_STATUS_OK);
3744 /********************************************************************
3745 ********************************************************************/
3747 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3749 LDAPMessage *res = NULL;
3754 status = ads_find_machine_acct(ads, &res, machine_name);
3755 if (!ADS_ERR_OK(status)) {
3756 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3757 lp_netbios_name()));
3761 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3762 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3766 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3767 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3771 ads_msgfree(ads, res);
3776 /********************************************************************
3777 ********************************************************************/
3779 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3780 LDAPMessage *msg, size_t *num_values)
3782 const char *field = "msDS-AdditionalDnsHostName";
3783 struct berval **values = NULL;
3785 size_t i, converted_size;
3788 * Windows DC implicitly adds a short name for each FQDN added to
3789 * msDS-AdditionalDnsHostName, but it comes with a strage binary
3790 * suffix "\0$" which we should ignore (see bug #14406).
3793 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3794 if (values == NULL) {
3798 *num_values = ldap_count_values_len(values);
3800 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3802 ldap_value_free_len(values);
3806 for (i = 0; i < *num_values; i++) {
3808 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
3810 strnlen(values[i]->bv_val,
3812 &ret[i], &converted_size)) {
3813 ldap_value_free_len(values);
3819 ldap_value_free_len(values);
3823 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
3825 const char *machine_name,
3826 char ***hostnames_array,
3827 size_t *num_hostnames)
3830 LDAPMessage *res = NULL;
3833 status = ads_find_machine_acct(ads,
3836 if (!ADS_ERR_OK(status)) {
3837 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
3842 count = ads_count_replies(ads, res);
3844 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3848 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
3849 if (*hostnames_array == NULL) {
3850 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
3852 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3857 ads_msgfree(ads, res);
3862 /********************************************************************
3863 ********************************************************************/
3865 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3867 LDAPMessage *res = NULL;
3872 status = ads_find_machine_acct(ads, &res, machine_name);
3873 if (!ADS_ERR_OK(status)) {
3874 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3875 lp_netbios_name()));
3879 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3880 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3884 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3885 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3889 ads_msgfree(ads, res);
3894 /********************************************************************
3895 ********************************************************************/
3897 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3899 LDAPMessage *res = NULL;
3905 status = ads_find_machine_acct(ads, &res, machine_name);
3906 if (!ADS_ERR_OK(status)) {
3907 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3908 lp_netbios_name()));
3912 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3913 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3917 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3918 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3922 ads_msgfree(ads, res);
3924 ok = (strlen(name) > 0);
3932 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3935 * Join a machine to a realm
3936 * Creates the machine account and sets the machine password
3937 * @param ads connection to ads server
3938 * @param machine name of host to add
3939 * @param org_unit Organizational unit to place machine in
3940 * @return status of join
3942 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3943 uint32_t account_type, const char *org_unit)
3946 LDAPMessage *res = NULL;
3949 /* machine name must be lowercase */
3950 machine = SMB_STRDUP(machine_name);
3951 strlower_m(machine);
3954 status = ads_find_machine_acct(ads, (void **)&res, machine);
3955 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3956 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3957 status = ads_leave_realm(ads, machine);
3958 if (!ADS_ERR_OK(status)) {
3959 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3960 machine, ads->config.realm));
3965 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3966 if (!ADS_ERR_OK(status)) {
3967 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3972 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3973 if (!ADS_ERR_OK(status)) {
3974 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3980 ads_msgfree(ads, res);
3987 * Delete a machine from the realm
3988 * @param ads connection to ads server
3989 * @param hostname Machine to remove
3990 * @return status of delete
3992 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3997 char *hostnameDN, *host;
3999 LDAPControl ldap_control;
4000 LDAPControl * pldap_control[2] = {NULL, NULL};
4002 pldap_control[0] = &ldap_control;
4003 memset(&ldap_control, 0, sizeof(LDAPControl));
4004 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4006 /* hostname must be lowercase */
4007 host = SMB_STRDUP(hostname);
4008 if (!strlower_m(host)) {
4010 return ADS_ERROR_SYSTEM(EINVAL);
4013 status = ads_find_machine_acct(ads, &res, host);
4014 if (!ADS_ERR_OK(status)) {
4015 DEBUG(0, ("Host account for %s does not exist.\n", host));
4020 msg = ads_first_entry(ads, res);
4023 return ADS_ERROR_SYSTEM(ENOENT);
4026 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4027 if (hostnameDN == NULL) {
4029 return ADS_ERROR_SYSTEM(ENOENT);
4032 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4034 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4036 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4039 if (rc != LDAP_SUCCESS) {
4040 const char *attrs[] = { "cn", NULL };
4041 LDAPMessage *msg_sub;
4043 /* we only search with scope ONE, we do not expect any further
4044 * objects to be created deeper */
4046 status = ads_do_search_retry(ads, hostnameDN,
4047 LDAP_SCOPE_ONELEVEL,
4048 "(objectclass=*)", attrs, &res);
4050 if (!ADS_ERR_OK(status)) {
4052 TALLOC_FREE(hostnameDN);
4056 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4057 msg_sub = ads_next_entry(ads, msg_sub)) {
4061 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4063 TALLOC_FREE(hostnameDN);
4064 return ADS_ERROR(LDAP_NO_MEMORY);
4067 status = ads_del_dn(ads, dn);
4068 if (!ADS_ERR_OK(status)) {
4069 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4072 TALLOC_FREE(hostnameDN);
4079 /* there should be no subordinate objects anymore */
4080 status = ads_do_search_retry(ads, hostnameDN,
4081 LDAP_SCOPE_ONELEVEL,
4082 "(objectclass=*)", attrs, &res);
4084 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4086 TALLOC_FREE(hostnameDN);
4090 /* delete hostnameDN now */
4091 status = ads_del_dn(ads, hostnameDN);
4092 if (!ADS_ERR_OK(status)) {
4094 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4095 TALLOC_FREE(hostnameDN);
4100 TALLOC_FREE(hostnameDN);
4102 status = ads_find_machine_acct(ads, &res, host);
4103 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4104 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4105 DEBUG(3, ("Failed to remove host account.\n"));
4115 * pull all token-sids from an LDAP dn
4116 * @param ads connection to ads server
4117 * @param mem_ctx TALLOC_CTX for allocating sid array
4118 * @param dn of LDAP object
4119 * @param user_sid pointer to struct dom_sid (objectSid)
4120 * @param primary_group_sid pointer to struct dom_sid (self composed)
4121 * @param sids pointer to sid array to allocate
4122 * @param num_sids counter of SIDs pulled
4123 * @return status of token query
4125 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4126 TALLOC_CTX *mem_ctx,
4128 struct dom_sid *user_sid,
4129 struct dom_sid *primary_group_sid,
4130 struct dom_sid **sids,
4134 LDAPMessage *res = NULL;
4136 size_t tmp_num_sids;
4137 struct dom_sid *tmp_sids;
4138 struct dom_sid tmp_user_sid;
4139 struct dom_sid tmp_primary_group_sid;
4141 const char *attrs[] = {
4148 status = ads_search_retry_dn(ads, &res, dn, attrs);
4149 if (!ADS_ERR_OK(status)) {
4153 count = ads_count_replies(ads, res);
4155 ads_msgfree(ads, res);
4156 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4159 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4160 ads_msgfree(ads, res);
4161 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4164 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4165 ads_msgfree(ads, res);
4166 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4170 /* hack to compose the primary group sid without knowing the
4173 struct dom_sid domsid;
4175 sid_copy(&domsid, &tmp_user_sid);
4177 if (!sid_split_rid(&domsid, NULL)) {
4178 ads_msgfree(ads, res);
4179 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4182 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4183 ads_msgfree(ads, res);
4184 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4188 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4190 if (tmp_num_sids == 0 || !tmp_sids) {
4191 ads_msgfree(ads, res);
4192 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4196 *num_sids = tmp_num_sids;
4204 *user_sid = tmp_user_sid;
4207 if (primary_group_sid) {
4208 *primary_group_sid = tmp_primary_group_sid;
4211 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4213 ads_msgfree(ads, res);
4214 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4218 * Find a sAMAccoutName in LDAP
4219 * @param ads connection to ads server
4220 * @param mem_ctx TALLOC_CTX for allocating sid array
4221 * @param samaccountname to search
4222 * @param uac_ret uint32_t pointer userAccountControl attribute value
4223 * @param dn_ret pointer to dn
4224 * @return status of token query
4226 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4227 TALLOC_CTX *mem_ctx,
4228 const char *samaccountname,
4230 const char **dn_ret)
4233 const char *attrs[] = { "userAccountControl", NULL };
4235 LDAPMessage *res = NULL;
4239 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4241 if (filter == NULL) {
4242 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4246 status = ads_do_search_all(ads, ads->config.bind_path,
4248 filter, attrs, &res);
4250 if (!ADS_ERR_OK(status)) {
4254 if (ads_count_replies(ads, res) != 1) {
4255 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4259 dn = ads_get_dn(ads, talloc_tos(), res);
4261 status = ADS_ERROR(LDAP_NO_MEMORY);
4265 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4266 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4275 *dn_ret = talloc_strdup(mem_ctx, dn);
4277 status = ADS_ERROR(LDAP_NO_MEMORY);
4283 ads_msgfree(ads, res);
4289 * find our configuration path
4290 * @param ads connection to ads server
4291 * @param mem_ctx Pointer to talloc context
4292 * @param config_path Pointer to the config path
4293 * @return status of search
4295 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4296 TALLOC_CTX *mem_ctx,
4300 LDAPMessage *res = NULL;
4301 const char *config_context = NULL;
4302 const char *attrs[] = { "configurationNamingContext", NULL };
4304 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4305 "(objectclass=*)", attrs, &res);
4306 if (!ADS_ERR_OK(status)) {
4310 config_context = ads_pull_string(ads, mem_ctx, res,
4311 "configurationNamingContext");
4312 ads_msgfree(ads, res);
4313 if (!config_context) {
4314 return ADS_ERROR(LDAP_NO_MEMORY);
4318 *config_path = talloc_strdup(mem_ctx, config_context);
4319 if (!*config_path) {
4320 return ADS_ERROR(LDAP_NO_MEMORY);
4324 return ADS_ERROR(LDAP_SUCCESS);
4328 * find the displayName of an extended right
4329 * @param ads connection to ads server
4330 * @param config_path The config path
4331 * @param mem_ctx Pointer to talloc context
4332 * @param GUID struct of the rightsGUID
4333 * @return status of search
4335 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4336 const char *config_path,
4337 TALLOC_CTX *mem_ctx,
4338 const struct GUID *rights_guid)
4341 LDAPMessage *res = NULL;
4343 const char *attrs[] = { "displayName", NULL };
4344 const char *result = NULL;
4347 if (!ads || !mem_ctx || !rights_guid) {
4351 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4352 GUID_string(mem_ctx, rights_guid));
4357 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4362 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4364 if (!ADS_ERR_OK(rc)) {
4368 if (ads_count_replies(ads, res) != 1) {
4372 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4375 ads_msgfree(ads, res);
4380 * verify or build and verify an account ou
4381 * @param mem_ctx Pointer to talloc context
4382 * @param ads connection to ads server
4384 * @return status of search
4387 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4389 const char **account_ou)
4395 if (account_ou == NULL) {
4396 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4399 if (*account_ou != NULL) {
4400 exploded_dn = ldap_explode_dn(*account_ou, 0);
4402 ldap_value_free(exploded_dn);
4407 ou_string = ads_ou_string(ads, *account_ou);
4409 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4412 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4413 ads->config.bind_path);
4414 SAFE_FREE(ou_string);
4417 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4420 exploded_dn = ldap_explode_dn(name, 0);
4422 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4424 ldap_value_free(exploded_dn);