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];
271 print_sockaddr(addr, sizeof(addr), ss);
273 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
274 addr, ads->server.realm));
276 ZERO_STRUCT( cldap_reply );
278 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
279 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
284 /* Check the CLDAP reply flags */
286 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
287 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
293 /* Fill in the ads->config values */
295 TALLOC_FREE(ads->config.realm);
296 TALLOC_FREE(ads->config.bind_path);
297 TALLOC_FREE(ads->config.ldap_server_name);
298 TALLOC_FREE(ads->config.server_site_name);
299 TALLOC_FREE(ads->config.client_site_name);
300 TALLOC_FREE(ads->server.workgroup);
302 if (!check_cldap_reply_required_flags(cldap_reply.server_type,
303 ads->config.flags)) {
308 ads->config.ldap_server_name = talloc_strdup(ads,
309 cldap_reply.pdc_dns_name);
310 if (ads->config.ldap_server_name == NULL) {
311 DBG_WARNING("Out of memory\n");
316 ads->config.realm = talloc_asprintf_strupper_m(ads,
318 cldap_reply.dns_domain);
319 if (ads->config.realm == NULL) {
320 DBG_WARNING("Out of memory\n");
325 status = ads_build_dn(ads->config.realm, ads, &ads->config.bind_path);
326 if (!ADS_ERR_OK(status)) {
327 DBG_DEBUG("Failed to build bind path: %s\n",
333 if (*cldap_reply.server_site) {
334 ads->config.server_site_name =
335 talloc_strdup(ads, cldap_reply.server_site);
336 if (ads->config.server_site_name == NULL) {
337 DBG_WARNING("Out of memory\n");
343 if (*cldap_reply.client_site) {
344 ads->config.client_site_name =
345 talloc_strdup(ads, cldap_reply.client_site);
346 if (ads->config.client_site_name == NULL) {
347 DBG_WARNING("Out of memory\n");
353 ads->server.workgroup = talloc_strdup(ads, cldap_reply.domain_name);
354 if (ads->server.workgroup == NULL) {
355 DBG_WARNING("Out of memory\n");
360 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
363 /* Store our site name. */
364 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
365 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
367 /* Leave this until last so that the flags are not clobbered */
368 ads->config.flags = cldap_reply.server_type;
378 /**********************************************************************
379 send a cldap ping to list of servers, one at a time, until one of
380 them answers it's an ldap server. Record success in the ADS_STRUCT.
381 Take note of and update negative connection cache.
382 **********************************************************************/
384 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
386 struct samba_sockaddr *sa_list,
392 for (i = 0; i < count; i++) {
393 char server[INET6_ADDRSTRLEN];
395 if (is_zero_addr(&sa_list[i].u.ss)) {
399 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
401 if (!NT_STATUS_IS_OK(
402 check_negative_conn_cache(domain, server)))
405 /* Returns ok only if it matches the correct server type */
406 ok = ads_try_connect(ads, false, &sa_list[i].u.ss);
412 /* keep track of failures */
413 add_failed_connection_entry(domain, server,
414 NT_STATUS_UNSUCCESSFUL);
417 return NT_STATUS_NO_LOGON_SERVERS;
420 /***************************************************************************
421 resolve a name and perform an "ldap ping" using NetBIOS and related methods
422 ****************************************************************************/
424 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
425 const char *domain, const char *realm)
429 struct samba_sockaddr *sa_list = NULL;
432 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
435 status = get_sorted_dc_list(talloc_tos(),
441 if (!NT_STATUS_IS_OK(status)) {
445 /* remove servers which are known to be dead based on
446 the corresponding DNS method */
448 for (i = 0; i < count; ++i) {
449 char server[INET6_ADDRSTRLEN];
451 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
454 check_negative_conn_cache(realm, server))) {
455 /* Ensure we add the workgroup name for this
456 IP address as negative too. */
457 add_failed_connection_entry(
459 NT_STATUS_UNSUCCESSFUL);
464 status = cldap_ping_list(ads, domain, sa_list, count);
466 TALLOC_FREE(sa_list);
472 /**********************************************************************
473 resolve a name and perform an "ldap ping" using DNS
474 **********************************************************************/
476 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
480 struct samba_sockaddr *sa_list = NULL;
483 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
486 status = get_sorted_dc_list(talloc_tos(),
492 if (!NT_STATUS_IS_OK(status)) {
493 TALLOC_FREE(sa_list);
497 status = cldap_ping_list(ads, realm, sa_list, count);
499 TALLOC_FREE(sa_list);
504 /**********************************************************************
505 Try to find an AD dc using our internal name resolution routines
506 Try the realm first and then then workgroup name if netbios is not
508 **********************************************************************/
510 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
512 const char *c_domain = "";
514 bool use_own_domain = False;
515 char *sitename = NULL;
516 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
519 /* if the realm and workgroup are both empty, assume they are ours */
522 c_realm = ads->server.realm;
528 /* special case where no realm and no workgroup means our own */
529 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
530 use_own_domain = True;
531 c_realm = lp_realm();
535 if (!lp_disable_netbios()) {
536 if (use_own_domain) {
537 c_domain = lp_workgroup();
539 c_domain = ads->server.workgroup;
540 if (!*c_realm && (!c_domain || !*c_domain)) {
541 c_domain = lp_workgroup();
550 if (!*c_realm && !*c_domain) {
551 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
553 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
557 * In case of LDAP we use get_dc_name() as that
558 * creates the custom krb5.conf file
560 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
562 struct sockaddr_storage ip_out;
564 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
565 " and falling back to domain '%s'\n",
568 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
570 if (is_zero_addr(&ip_out)) {
571 return NT_STATUS_NO_LOGON_SERVERS;
575 * we call ads_try_connect() to fill in the
576 * ads->config details
578 ok = ads_try_connect(ads, false, &ip_out);
584 return NT_STATUS_NO_LOGON_SERVERS;
588 sitename = sitename_fetch(talloc_tos(), c_realm);
589 status = resolve_and_ping_dns(ads, sitename, c_realm);
591 if (NT_STATUS_IS_OK(status)) {
592 TALLOC_FREE(sitename);
596 /* In case we failed to contact one of our closest DC on our
598 * need to try to find another DC, retry with a site-less SRV
603 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
604 "our site (%s), Trying to find another DC "
605 "for realm '%s' (domain '%s')\n",
606 sitename, c_realm, c_domain));
607 namecache_delete(c_realm, 0x1C);
609 resolve_and_ping_dns(ads, NULL, c_realm);
611 if (NT_STATUS_IS_OK(status)) {
612 TALLOC_FREE(sitename);
617 TALLOC_FREE(sitename);
620 /* try netbios as fallback - if permitted,
621 or if configuration specifically requests it */
624 DEBUG(3, ("ads_find_dc: falling back to netbios "
625 "name resolution for domain '%s' (realm '%s')\n",
629 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
630 if (NT_STATUS_IS_OK(status)) {
635 DEBUG(1, ("ads_find_dc: "
636 "name resolution for realm '%s' (domain '%s') failed: %s\n",
637 c_realm, c_domain, nt_errstr(status)));
641 * Connect to the LDAP server
642 * @param ads Pointer to an existing ADS_STRUCT
643 * @return status of connection
645 ADS_STATUS ads_connect(ADS_STRUCT *ads)
647 int version = LDAP_VERSION3;
650 char addr[INET6_ADDRSTRLEN];
651 struct sockaddr_storage existing_ss;
653 zero_sockaddr(&existing_ss);
656 * ads_connect can be passed in a reused ADS_STRUCT
657 * with an existing non-zero ads->ldap.ss IP address
658 * that was stored by going through ads_find_dc()
659 * if ads->server.ldap_server was NULL.
661 * If ads->server.ldap_server is still NULL but
662 * the target address isn't the zero address, then
663 * store that address off off before zeroing out
664 * ads->ldap so we don't keep doing multiple calls
665 * to ads_find_dc() in the reuse case.
667 * If a caller wants a clean ADS_STRUCT they
668 * will TALLOC_FREE it and allocate a new one
669 * by calling ads_init(), which ensures
670 * ads->ldap.ss is a properly zero'ed out valid IP
673 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
674 /* Save off the address we previously found by ads_find_dc(). */
675 existing_ss = ads->ldap.ss;
679 ZERO_STRUCT(ads->ldap_wrap_data);
680 ads->ldap.last_attempt = time_mono(NULL);
681 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
683 /* try with a user specified server */
685 if (DEBUGLEVEL >= 11) {
686 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
687 DEBUG(11,("ads_connect: entering\n"));
688 DEBUGADD(11,("%s\n", s));
692 if (ads->server.ldap_server) {
694 struct sockaddr_storage ss;
696 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
698 DEBUG(5,("ads_connect: unable to resolve name %s\n",
699 ads->server.ldap_server));
700 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
704 if (is_zero_addr(&ss)) {
705 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
709 ok = ads_try_connect(ads, ads->server.gc, &ss);
714 /* The choice of which GC use is handled one level up in
715 ads_connect_gc(). If we continue on from here with
716 ads_find_dc() we will get GC searches on port 389 which
717 doesn't work. --jerry */
719 if (ads->server.gc == true) {
720 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
723 if (ads->server.no_fallback) {
724 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
729 if (!is_zero_addr(&existing_ss)) {
730 /* We saved off who we should talk to. */
731 bool ok = ads_try_connect(ads,
738 * Keep trying to find a server and fall through
739 * into ads_find_dc() again.
743 ntstatus = ads_find_dc(ads);
744 if (NT_STATUS_IS_OK(ntstatus)) {
748 status = ADS_ERROR_NT(ntstatus);
753 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
754 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
756 if (!ads->auth.user_name) {
757 /* Must use the userPrincipalName value here or sAMAccountName
758 and not servicePrincipalName; found by Guenther Deschner */
759 ads->auth.user_name = talloc_asprintf(ads,
762 if (ads->auth.user_name == NULL) {
763 DBG_ERR("talloc_asprintf failed\n");
764 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
769 if (ads->auth.realm == NULL) {
770 ads->auth.realm = talloc_strdup(ads, ads->config.realm);
771 if (ads->auth.realm == NULL) {
772 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
777 if (!ads->auth.kdc_server) {
778 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
779 ads->auth.kdc_server = talloc_strdup(ads, addr);
780 if (ads->auth.kdc_server == NULL) {
781 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
786 /* If the caller() requested no LDAP bind, then we are done */
788 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
789 status = ADS_SUCCESS;
793 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
794 if (!ads->ldap_wrap_data.mem_ctx) {
795 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
799 /* Otherwise setup the TCP LDAP session */
801 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
803 ads->ldap.port, lp_ldap_timeout());
804 if (ads->ldap.ld == NULL) {
805 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
808 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
810 /* cache the successful connection for workgroup and realm */
811 if (ads_closest_dc(ads)) {
812 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
813 saf_store( ads->server.realm, ads->config.ldap_server_name);
816 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
818 /* fill in the current time and offsets */
820 status = ads_current_time( ads );
821 if ( !ADS_ERR_OK(status) ) {
825 /* Now do the bind */
827 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
828 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
832 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
833 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
837 status = ads_sasl_bind(ads);
840 if (DEBUGLEVEL >= 11) {
841 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
842 DEBUG(11,("ads_connect: leaving with: %s\n",
843 ads_errstr(status)));
844 DEBUGADD(11,("%s\n", s));
852 * Connect to the LDAP server using given credentials
853 * @param ads Pointer to an existing ADS_STRUCT
854 * @return status of connection
856 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
858 ads->auth.flags |= ADS_AUTH_USER_CREDS;
860 return ads_connect(ads);
864 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
865 * @param ads Pointer to an existing ADS_STRUCT
867 * Sets the ads->ldap.ss to a valid
868 * zero ip address that can be detected by
869 * our is_zero_addr() function. Otherwise
870 * it is left as AF_UNSPEC (0).
872 void ads_zero_ldap(ADS_STRUCT *ads)
874 ZERO_STRUCT(ads->ldap);
876 * Initialize the sockaddr_storage so we can use
877 * sockaddr test functions against it.
879 zero_sockaddr(&ads->ldap.ss);
883 * Disconnect the LDAP server
884 * @param ads Pointer to an existing ADS_STRUCT
886 void ads_disconnect(ADS_STRUCT *ads)
889 ldap_unbind(ads->ldap.ld);
892 if (ads->ldap_wrap_data.wrap_ops &&
893 ads->ldap_wrap_data.wrap_ops->disconnect) {
894 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
896 if (ads->ldap_wrap_data.mem_ctx) {
897 talloc_free(ads->ldap_wrap_data.mem_ctx);
900 ZERO_STRUCT(ads->ldap_wrap_data);
904 Duplicate a struct berval into talloc'ed memory
906 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
908 struct berval *value;
910 if (!in_val) return NULL;
912 value = talloc_zero(ctx, struct berval);
915 if (in_val->bv_len == 0) return value;
917 value->bv_len = in_val->bv_len;
918 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
924 Make a values list out of an array of (struct berval *)
926 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
927 const struct berval **in_vals)
929 struct berval **values;
932 if (!in_vals) return NULL;
933 for (i=0; in_vals[i]; i++)
935 values = talloc_zero_array(ctx, struct berval *, i+1);
936 if (!values) return NULL;
938 for (i=0; in_vals[i]; i++) {
939 values[i] = dup_berval(ctx, in_vals[i]);
945 UTF8-encode a values list out of an array of (char *)
947 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
953 if (!in_vals) return NULL;
954 for (i=0; in_vals[i]; i++)
956 values = talloc_zero_array(ctx, char *, i+1);
957 if (!values) return NULL;
959 for (i=0; in_vals[i]; i++) {
960 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
969 Pull a (char *) array out of a UTF8-encoded values list
971 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
975 size_t converted_size;
977 if (!in_vals) return NULL;
978 for (i=0; in_vals[i]; i++)
980 values = talloc_zero_array(ctx, char *, i+1);
981 if (!values) return NULL;
983 for (i=0; in_vals[i]; i++) {
984 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
986 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
987 "%s", strerror(errno)));
994 * Do a search with paged results. cookie must be null on the first
995 * call, and then returned on each subsequent call. It will be null
996 * again when the entire search is complete
997 * @param ads connection to ads server
998 * @param bind_path Base dn for the search
999 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1000 * @param expr Search expression - specified in local charset
1001 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1002 * @param res ** which will contain results - free res* with ads_msgfree()
1003 * @param count Number of entries retrieved on this page
1004 * @param cookie The paged results cookie to be returned on subsequent calls
1005 * @return status of search
1007 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1008 const char *bind_path,
1009 int scope, const char *expr,
1010 const char **attrs, void *args,
1012 int *count, struct berval **cookie)
1015 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1016 size_t converted_size;
1017 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1018 BerElement *cookie_be = NULL;
1019 struct berval *cookie_bv= NULL;
1020 BerElement *ext_be = NULL;
1021 struct berval *ext_bv= NULL;
1024 ads_control *external_control = (ads_control *) args;
1028 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1029 return ADS_ERROR(LDAP_NO_MEMORY);
1031 /* 0 means the conversion worked but the result was empty
1032 so we only fail if it's -1. In any case, it always
1033 at least nulls out the dest */
1034 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1035 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1037 rc = LDAP_NO_MEMORY;
1041 if (!attrs || !(*attrs))
1042 search_attrs = NULL;
1044 /* This would be the utf8-encoded version...*/
1045 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1046 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1047 rc = LDAP_NO_MEMORY;
1052 /* Paged results only available on ldap v3 or later */
1053 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1054 if (version < LDAP_VERSION3) {
1055 rc = LDAP_NOT_SUPPORTED;
1059 cookie_be = ber_alloc_t(LBER_USE_DER);
1061 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1062 ber_bvfree(*cookie); /* don't need it from last time */
1065 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1067 ber_flatten(cookie_be, &cookie_bv);
1068 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1069 PagedResults.ldctl_iscritical = (char) 1;
1070 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1071 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1073 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1074 NoReferrals.ldctl_iscritical = (char) 0;
1075 NoReferrals.ldctl_value.bv_len = 0;
1076 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1078 if (external_control &&
1079 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1080 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1082 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1083 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1085 /* win2k does not accept a ldctl_value beeing passed in */
1087 if (external_control->val != 0) {
1089 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1090 rc = LDAP_NO_MEMORY;
1094 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1095 rc = LDAP_NO_MEMORY;
1098 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1099 rc = LDAP_NO_MEMORY;
1103 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1104 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1107 ExternalCtrl.ldctl_value.bv_len = 0;
1108 ExternalCtrl.ldctl_value.bv_val = NULL;
1111 controls[0] = &NoReferrals;
1112 controls[1] = &PagedResults;
1113 controls[2] = &ExternalCtrl;
1117 controls[0] = &NoReferrals;
1118 controls[1] = &PagedResults;
1122 /* we need to disable referrals as the openldap libs don't
1123 handle them and paged results at the same time. Using them
1124 together results in the result record containing the server
1125 page control being removed from the result list (tridge/jmcd)
1127 leaving this in despite the control that says don't generate
1128 referrals, in case the server doesn't support it (jmcd)
1130 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1132 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1133 search_attrs, 0, controls,
1134 NULL, LDAP_NO_LIMIT,
1135 (LDAPMessage **)res);
1137 ber_free(cookie_be, 1);
1138 ber_bvfree(cookie_bv);
1141 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1142 ldap_err2string(rc)));
1143 if (rc == LDAP_OTHER) {
1147 ret = ldap_parse_result(ads->ldap.ld,
1155 if (ret == LDAP_SUCCESS) {
1156 DEBUG(3, ("ldap_search_with_timeout(%s) "
1157 "error: %s\n", expr, ldap_errmsg));
1158 ldap_memfree(ldap_errmsg);
1164 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1165 NULL, &rcontrols, 0);
1171 for (i=0; rcontrols[i]; i++) {
1172 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1173 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1174 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1176 /* the berval is the cookie, but must be freed when
1178 if (cookie_bv->bv_len) /* still more to do */
1179 *cookie=ber_bvdup(cookie_bv);
1182 ber_bvfree(cookie_bv);
1183 ber_free(cookie_be, 1);
1187 ldap_controls_free(rcontrols);
1190 talloc_destroy(ctx);
1193 ber_free(ext_be, 1);
1200 if (rc != LDAP_SUCCESS && *res != NULL) {
1201 ads_msgfree(ads, *res);
1205 /* if/when we decide to utf8-encode attrs, take out this next line */
1206 TALLOC_FREE(search_attrs);
1208 return ADS_ERROR(rc);
1211 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1212 int scope, const char *expr,
1213 const char **attrs, LDAPMessage **res,
1214 int *count, struct berval **cookie)
1216 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1221 * Get all results for a search. This uses ads_do_paged_search() to return
1222 * all entries in a large search.
1223 * @param ads connection to ads server
1224 * @param bind_path Base dn for the search
1225 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1226 * @param expr Search expression
1227 * @param attrs Attributes to retrieve
1228 * @param res ** which will contain results - free res* with ads_msgfree()
1229 * @return status of search
1231 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1232 int scope, const char *expr,
1233 const char **attrs, void *args,
1236 struct berval *cookie = NULL;
1241 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1244 if (!ADS_ERR_OK(status))
1247 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1249 LDAPMessage *res2 = NULL;
1250 LDAPMessage *msg, *next;
1252 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1253 attrs, args, &res2, &count, &cookie);
1254 if (!ADS_ERR_OK(status)) {
1258 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1259 that this works on all ldap libs, but I have only tested with openldap */
1260 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1261 next = ads_next_message(ads, msg);
1262 ldap_add_result_entry((LDAPMessage **)res, msg);
1264 /* note that we do not free res2, as the memory is now
1265 part of the main returned list */
1268 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1269 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1275 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1276 int scope, const char *expr,
1277 const char **attrs, LDAPMessage **res)
1279 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1282 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1283 int scope, const char *expr,
1284 const char **attrs, uint32_t sd_flags,
1289 args.control = ADS_SD_FLAGS_OID;
1290 args.val = sd_flags;
1291 args.critical = True;
1293 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1298 * Run a function on all results for a search. Uses ads_do_paged_search() and
1299 * runs the function as each page is returned, using ads_process_results()
1300 * @param ads connection to ads server
1301 * @param bind_path Base dn for the search
1302 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1303 * @param expr Search expression - specified in local charset
1304 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1305 * @param fn Function which takes attr name, values list, and data_area
1306 * @param data_area Pointer which is passed to function on each call
1307 * @return status of search
1309 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1310 int scope, const char *expr, const char **attrs,
1311 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1314 struct berval *cookie = NULL;
1319 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1322 if (!ADS_ERR_OK(status)) return status;
1324 ads_process_results(ads, res, fn, data_area);
1325 ads_msgfree(ads, res);
1328 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1329 &res, &count, &cookie);
1331 if (!ADS_ERR_OK(status)) break;
1333 ads_process_results(ads, res, fn, data_area);
1334 ads_msgfree(ads, res);
1341 * Do a search with a timeout.
1342 * @param ads connection to ads server
1343 * @param bind_path Base dn for the search
1344 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1345 * @param expr Search expression
1346 * @param attrs Attributes to retrieve
1347 * @param res ** which will contain results - free res* with ads_msgfree()
1348 * @return status of search
1350 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1352 const char **attrs, LDAPMessage **res)
1355 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1356 size_t converted_size;
1360 if (!(ctx = talloc_init("ads_do_search"))) {
1361 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1362 return ADS_ERROR(LDAP_NO_MEMORY);
1365 /* 0 means the conversion worked but the result was empty
1366 so we only fail if it's negative. In any case, it always
1367 at least nulls out the dest */
1368 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1369 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1371 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1372 rc = LDAP_NO_MEMORY;
1376 if (!attrs || !(*attrs))
1377 search_attrs = NULL;
1379 /* This would be the utf8-encoded version...*/
1380 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1381 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1383 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1384 rc = LDAP_NO_MEMORY;
1389 /* see the note in ads_do_paged_search - we *must* disable referrals */
1390 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1392 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1393 search_attrs, 0, NULL, NULL,
1395 (LDAPMessage **)res);
1397 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1398 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1403 talloc_destroy(ctx);
1404 /* if/when we decide to utf8-encode attrs, take out this next line */
1405 TALLOC_FREE(search_attrs);
1406 return ADS_ERROR(rc);
1409 * Do a general ADS search
1410 * @param ads connection to ads server
1411 * @param res ** which will contain results - free res* with ads_msgfree()
1412 * @param expr Search expression
1413 * @param attrs Attributes to retrieve
1414 * @return status of search
1416 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1417 const char *expr, const char **attrs)
1419 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1424 * Do a search on a specific DistinguishedName
1425 * @param ads connection to ads server
1426 * @param res ** which will contain results - free res* with ads_msgfree()
1427 * @param dn DistinguishName to search
1428 * @param attrs Attributes to retrieve
1429 * @return status of search
1431 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1432 const char *dn, const char **attrs)
1434 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1439 * Free up memory from a ads_search
1440 * @param ads connection to ads server
1441 * @param msg Search results to free
1443 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1450 * Get a dn from search results
1451 * @param ads connection to ads server
1452 * @param msg Search result
1455 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1457 char *utf8_dn, *unix_dn;
1458 size_t converted_size;
1460 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1463 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1467 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1468 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1472 ldap_memfree(utf8_dn);
1477 * Get the parent from a dn
1478 * @param dn the dn to return the parent from
1479 * @return parent dn string
1481 char *ads_parent_dn(const char *dn)
1489 p = strchr(dn, ',');
1499 * Find a machine account given a hostname
1500 * @param ads connection to ads server
1501 * @param res ** which will contain results - free res* with ads_msgfree()
1502 * @param host Hostname to search for
1503 * @return status of search
1505 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1506 const char *machine)
1510 const char *attrs[] = {
1511 /* This is how Windows checks for machine accounts */
1514 "userAccountControl",
1516 "ServicePrincipalName",
1517 "userPrincipalName",
1520 /* Additional attributes Samba checks */
1521 "msDS-AdditionalDnsHostName",
1522 "msDS-SupportedEncryptionTypes",
1523 "nTSecurityDescriptor",
1528 TALLOC_CTX *frame = talloc_stackframe();
1532 /* the easiest way to find a machine account anywhere in the tree
1533 is to look for hostname$ */
1534 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1536 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1540 status = ads_search(ads, res, expr, attrs);
1541 if (ADS_ERR_OK(status)) {
1542 if (ads_count_replies(ads, *res) != 1) {
1543 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1553 * Initialize a list of mods to be used in a modify request
1554 * @param ctx An initialized TALLOC_CTX
1555 * @return allocated ADS_MODLIST
1557 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1559 #define ADS_MODLIST_ALLOC_SIZE 10
1562 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1563 /* -1 is safety to make sure we don't go over the end.
1564 need to reset it to NULL before doing ldap modify */
1565 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1567 return (ADS_MODLIST)mods;
1572 add an attribute to the list, with values list already constructed
1574 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1575 int mod_op, const char *name,
1576 const void *_invals)
1579 LDAPMod **modlist = (LDAPMod **) *mods;
1580 struct berval **ber_values = NULL;
1581 char **char_values = NULL;
1584 mod_op = LDAP_MOD_DELETE;
1586 if (mod_op & LDAP_MOD_BVALUES) {
1587 const struct berval **b;
1588 b = discard_const_p(const struct berval *, _invals);
1589 ber_values = ads_dup_values(ctx, b);
1592 c = discard_const_p(const char *, _invals);
1593 char_values = ads_push_strvals(ctx, c);
1597 /* find the first empty slot */
1598 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1600 if (modlist[curmod] == (LDAPMod *) -1) {
1601 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1602 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1603 return ADS_ERROR(LDAP_NO_MEMORY);
1604 memset(&modlist[curmod], 0,
1605 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1606 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1607 *mods = (ADS_MODLIST)modlist;
1610 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1611 return ADS_ERROR(LDAP_NO_MEMORY);
1612 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1613 if (mod_op & LDAP_MOD_BVALUES) {
1614 modlist[curmod]->mod_bvalues = ber_values;
1615 } else if (mod_op & LDAP_MOD_DELETE) {
1616 modlist[curmod]->mod_values = NULL;
1618 modlist[curmod]->mod_values = char_values;
1621 modlist[curmod]->mod_op = mod_op;
1622 return ADS_ERROR(LDAP_SUCCESS);
1626 * Add a single string value to a mod list
1627 * @param ctx An initialized TALLOC_CTX
1628 * @param mods An initialized ADS_MODLIST
1629 * @param name The attribute name to add
1630 * @param val The value to add - NULL means DELETE
1631 * @return ADS STATUS indicating success of add
1633 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1634 const char *name, const char *val)
1636 const char *values[2];
1642 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1643 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1647 * Add an array of string values to a mod list
1648 * @param ctx An initialized TALLOC_CTX
1649 * @param mods An initialized ADS_MODLIST
1650 * @param name The attribute name to add
1651 * @param vals The array of string values to add - NULL means DELETE
1652 * @return ADS STATUS indicating success of add
1654 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1655 const char *name, const char **vals)
1658 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1659 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1660 name, (const void **) vals);
1664 * Add a single ber-encoded value to a mod list
1665 * @param ctx An initialized TALLOC_CTX
1666 * @param mods An initialized ADS_MODLIST
1667 * @param name The attribute name to add
1668 * @param val The value to add - NULL means DELETE
1669 * @return ADS STATUS indicating success of add
1671 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1672 const char *name, const struct berval *val)
1674 const struct berval *values[2];
1679 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1680 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1681 name, (const void **) values);
1684 static void ads_print_error(int ret, LDAP *ld)
1687 char *ld_error = NULL;
1688 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1689 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1691 ldap_err2string(ret),
1693 SAFE_FREE(ld_error);
1698 * Perform an ldap modify
1699 * @param ads connection to ads server
1700 * @param mod_dn DistinguishedName to modify
1701 * @param mods list of modifications to perform
1702 * @return status of modify
1704 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1707 char *utf8_dn = NULL;
1708 size_t converted_size;
1710 this control is needed to modify that contains a currently
1711 non-existent attribute (but allowable for the object) to run
1713 LDAPControl PermitModify = {
1714 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1717 LDAPControl *controls[2];
1719 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1721 controls[0] = &PermitModify;
1724 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1725 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1728 /* find the end of the list, marked by NULL or -1 */
1729 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1730 /* make sure the end of the list is NULL */
1732 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1733 (LDAPMod **) mods, controls, NULL);
1734 ads_print_error(ret, ads->ldap.ld);
1735 TALLOC_FREE(utf8_dn);
1736 return ADS_ERROR(ret);
1740 * Perform an ldap add
1741 * @param ads connection to ads server
1742 * @param new_dn DistinguishedName to add
1743 * @param mods list of attributes and values for DN
1744 * @return status of add
1746 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1749 char *utf8_dn = NULL;
1750 size_t converted_size;
1752 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1754 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1755 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1756 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1759 /* find the end of the list, marked by NULL or -1 */
1760 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1761 /* make sure the end of the list is NULL */
1764 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1765 ads_print_error(ret, ads->ldap.ld);
1766 TALLOC_FREE(utf8_dn);
1767 return ADS_ERROR(ret);
1771 * Delete a DistinguishedName
1772 * @param ads connection to ads server
1773 * @param new_dn DistinguishedName to delete
1774 * @return status of delete
1776 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1779 char *utf8_dn = NULL;
1780 size_t converted_size;
1781 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1782 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1783 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1786 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1788 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1789 ads_print_error(ret, ads->ldap.ld);
1790 TALLOC_FREE(utf8_dn);
1791 return ADS_ERROR(ret);
1795 * Build an org unit string
1796 * if org unit is Computers or blank then assume a container, otherwise
1797 * assume a / separated list of organisational units.
1798 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1799 * @param ads connection to ads server
1800 * @param org_unit Organizational unit
1801 * @return org unit string - caller must free
1803 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1809 if (!org_unit || !*org_unit) {
1811 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1813 /* samba4 might not yet respond to a wellknownobject-query */
1814 return ret ? ret : SMB_STRDUP("cn=Computers");
1817 if (strequal(org_unit, "Computers")) {
1818 return SMB_STRDUP("cn=Computers");
1821 /* jmcd: removed "\\" from the separation chars, because it is
1822 needed as an escape for chars like '#' which are valid in an
1824 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
1825 if (!ADS_ERR_OK(status)) {
1833 * Get a org unit string for a well-known GUID
1834 * @param ads connection to ads server
1835 * @param wknguid Well known GUID
1836 * @return org unit string - caller must free
1838 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1841 LDAPMessage *res = NULL;
1842 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1843 **bind_dn_exp = NULL;
1844 const char *attrs[] = {"distinguishedName", NULL};
1845 int new_ln, wkn_ln, bind_ln, i;
1847 if (wknguid == NULL) {
1851 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1852 DEBUG(1, ("asprintf failed!\n"));
1856 status = ads_search_dn(ads, &res, base, attrs);
1857 if (!ADS_ERR_OK(status)) {
1858 DEBUG(1,("Failed while searching for: %s\n", base));
1862 if (ads_count_replies(ads, res) != 1) {
1866 /* substitute the bind-path from the well-known-guid-search result */
1867 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1872 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1877 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1882 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1884 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1887 new_ln = wkn_ln - bind_ln;
1889 ret = SMB_STRDUP(wkn_dn_exp[0]);
1894 for (i=1; i < new_ln; i++) {
1897 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1903 ret = SMB_STRDUP(s);
1912 ads_msgfree(ads, res);
1913 TALLOC_FREE(wkn_dn);
1915 ldap_value_free(wkn_dn_exp);
1918 ldap_value_free(bind_dn_exp);
1925 * Adds (appends) an item to an attribute array, rather then
1926 * replacing the whole list
1927 * @param ctx An initialized TALLOC_CTX
1928 * @param mods An initialized ADS_MODLIST
1929 * @param name name of the ldap attribute to append to
1930 * @param vals an array of values to add
1931 * @return status of addition
1934 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1935 const char *name, const char **vals)
1937 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1938 (const void *) vals);
1942 * Determines the an account's current KVNO via an LDAP lookup
1943 * @param ads An initialized ADS_STRUCT
1944 * @param account_name the NT samaccountname.
1945 * @return the kvno for the account, or -1 in case of a failure.
1948 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1950 LDAPMessage *res = NULL;
1951 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1953 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1954 char *dn_string = NULL;
1957 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1958 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1961 ret = ads_search(ads, &res, filter, attrs);
1963 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1964 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1965 ads_msgfree(ads, res);
1969 dn_string = ads_get_dn(ads, talloc_tos(), res);
1971 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1972 ads_msgfree(ads, res);
1975 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1976 TALLOC_FREE(dn_string);
1978 /* ---------------------------------------------------------
1979 * 0 is returned as a default KVNO from this point on...
1980 * This is done because Windows 2000 does not support key
1981 * version numbers. Chances are that a failure in the next
1982 * step is simply due to Windows 2000 being used for a
1983 * domain controller. */
1986 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1987 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1988 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1989 ads_msgfree(ads, res);
1994 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1995 ads_msgfree(ads, res);
2000 * Determines the computer account's current KVNO via an LDAP lookup
2001 * @param ads An initialized ADS_STRUCT
2002 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2003 * @return the kvno for the computer account, or -1 in case of a failure.
2006 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2008 char *computer_account = NULL;
2011 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2015 kvno = ads_get_kvno(ads, computer_account);
2016 free(computer_account);
2022 * This clears out all registered spn's for a given hostname
2023 * @param ads An initilaized ADS_STRUCT
2024 * @param machine_name the NetBIOS name of the computer.
2025 * @return 0 upon success, non-zero otherwise.
2028 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2031 LDAPMessage *res = NULL;
2033 const char *servicePrincipalName[1] = {NULL};
2035 char *dn_string = NULL;
2037 ret = ads_find_machine_acct(ads, &res, machine_name);
2038 if (!ADS_ERR_OK(ret)) {
2039 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2040 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2041 ads_msgfree(ads, res);
2045 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2046 ctx = talloc_init("ads_clear_service_principal_names");
2048 ads_msgfree(ads, res);
2049 return ADS_ERROR(LDAP_NO_MEMORY);
2052 if (!(mods = ads_init_mods(ctx))) {
2053 talloc_destroy(ctx);
2054 ads_msgfree(ads, res);
2055 return ADS_ERROR(LDAP_NO_MEMORY);
2057 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2058 if (!ADS_ERR_OK(ret)) {
2059 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2060 ads_msgfree(ads, res);
2061 talloc_destroy(ctx);
2064 dn_string = ads_get_dn(ads, talloc_tos(), res);
2066 talloc_destroy(ctx);
2067 ads_msgfree(ads, res);
2068 return ADS_ERROR(LDAP_NO_MEMORY);
2070 ret = ads_gen_mod(ads, dn_string, mods);
2071 TALLOC_FREE(dn_string);
2072 if (!ADS_ERR_OK(ret)) {
2073 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2075 ads_msgfree(ads, res);
2076 talloc_destroy(ctx);
2080 ads_msgfree(ads, res);
2081 talloc_destroy(ctx);
2086 * @brief Search for an element in a string array.
2088 * @param[in] el_array The string array to search.
2090 * @param[in] num_el The number of elements in the string array.
2092 * @param[in] el The string to search.
2094 * @return True if found, false if not.
2096 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2100 if (el_array == NULL || num_el == 0 || el == NULL) {
2104 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2107 cmp = strcasecmp_m(el_array[i], el);
2117 * @brief This gets the service principal names of an existing computer account.
2119 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2121 * @param[in] ads The ADS context to use.
2123 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2124 * identify the computer account.
2126 * @param[in] spn_array A pointer to store the array for SPNs.
2128 * @param[in] num_spns The number of principals stored in the array.
2130 * @return 0 on success, or a ADS error if a failure occurred.
2132 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2134 const char *machine_name,
2139 LDAPMessage *res = NULL;
2142 status = ads_find_machine_acct(ads,
2145 if (!ADS_ERR_OK(status)) {
2146 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2151 count = ads_count_replies(ads, res);
2153 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2157 *spn_array = ads_pull_strings(ads,
2160 "servicePrincipalName",
2162 if (*spn_array == NULL) {
2163 DEBUG(1, ("Host account for %s does not have service principal "
2166 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2171 ads_msgfree(ads, res);
2177 * This adds a service principal name to an existing computer account
2178 * (found by hostname) in AD.
2179 * @param ads An initialized ADS_STRUCT
2180 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2181 * @param spns An array or strings for the service principals to add,
2182 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2183 * @return 0 upon sucess, or non-zero if a failure occurs
2186 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2187 const char *machine_name,
2192 LDAPMessage *res = NULL;
2194 char *dn_string = NULL;
2195 const char **servicePrincipalName = spns;
2197 ret = ads_find_machine_acct(ads, &res, machine_name);
2198 if (!ADS_ERR_OK(ret)) {
2199 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2201 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2202 ads_msgfree(ads, res);
2206 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2207 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2208 ads_msgfree(ads, res);
2209 return ADS_ERROR(LDAP_NO_MEMORY);
2212 DEBUG(5,("ads_add_service_principal_name: INFO: "
2213 "Adding %s to host %s\n",
2214 spns[0] ? "N/A" : spns[0], machine_name));
2217 DEBUG(5,("ads_add_service_principal_name: INFO: "
2218 "Adding %s to host %s\n",
2219 spns[1] ? "N/A" : spns[1], machine_name));
2221 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2222 ret = ADS_ERROR(LDAP_NO_MEMORY);
2226 ret = ads_add_strlist(ctx,
2228 "servicePrincipalName",
2229 servicePrincipalName);
2230 if (!ADS_ERR_OK(ret)) {
2231 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2235 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2236 ret = ADS_ERROR(LDAP_NO_MEMORY);
2240 ret = ads_gen_mod(ads, dn_string, mods);
2241 if (!ADS_ERR_OK(ret)) {
2242 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2248 ads_msgfree(ads, res);
2252 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2255 uint32_t acct_ctrl = 0;
2258 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2266 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2268 const struct berval *machine_pw_val)
2272 TALLOC_CTX *frame = talloc_stackframe();
2273 uint32_t acct_control;
2274 char *control_str = NULL;
2275 const char *attrs[] = {
2279 LDAPMessage *res = NULL;
2282 dn = ads_get_dn(ads, frame, msg);
2284 ret = ADS_ERROR(LDAP_NO_MEMORY);
2288 acct_control = ads_get_acct_ctrl(ads, msg);
2289 if (acct_control == 0) {
2290 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2295 * Changing the password, disables the account. So we need to change the
2296 * userAccountControl flags to enable it again.
2298 mods = ads_init_mods(frame);
2300 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2304 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2306 ret = ads_gen_mod(ads, dn, mods);
2307 if (!ADS_ERR_OK(ret)) {
2313 * To activate the account, we need to disable and enable it.
2315 acct_control |= UF_ACCOUNTDISABLE;
2317 control_str = talloc_asprintf(frame, "%u", acct_control);
2318 if (control_str == NULL) {
2319 ret = ADS_ERROR(LDAP_NO_MEMORY);
2323 mods = ads_init_mods(frame);
2325 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2329 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2331 ret = ads_gen_mod(ads, dn, mods);
2332 if (!ADS_ERR_OK(ret)) {
2336 TALLOC_FREE(control_str);
2339 * Enable the account again.
2341 acct_control &= ~UF_ACCOUNTDISABLE;
2343 control_str = talloc_asprintf(frame, "%u", acct_control);
2344 if (control_str == NULL) {
2345 ret = ADS_ERROR(LDAP_NO_MEMORY);
2349 mods = ads_init_mods(frame);
2351 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2355 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2357 ret = ads_gen_mod(ads, dn, mods);
2358 if (!ADS_ERR_OK(ret)) {
2362 TALLOC_FREE(control_str);
2364 ret = ads_search_dn(ads, &res, dn, attrs);
2365 ads_msgfree(ads, res);
2374 * adds a machine account to the ADS server
2375 * @param ads An intialized ADS_STRUCT
2376 * @param machine_name - the NetBIOS machine name of this account.
2377 * @param account_type A number indicating the type of account to create
2378 * @param org_unit The LDAP path in which to place this account
2379 * @return 0 upon success, or non-zero otherwise
2382 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2383 const char *machine_name,
2384 const char *machine_password,
2385 const char *org_unit,
2386 uint32_t etype_list,
2387 const char *dns_domain_name)
2390 char *samAccountName = NULL;
2391 char *controlstr = NULL;
2392 TALLOC_CTX *ctx = NULL;
2394 char *machine_escaped = NULL;
2395 char *dns_hostname = NULL;
2396 char *new_dn = NULL;
2397 char *utf8_pw = NULL;
2398 size_t utf8_pw_len = 0;
2399 char *utf16_pw = NULL;
2400 size_t utf16_pw_len = 0;
2401 struct berval machine_pw_val;
2403 const char **spn_array = NULL;
2404 size_t num_spns = 0;
2405 const char *spn_prefix[] = {
2407 "RestrictedKrbHost",
2410 LDAPMessage *res = NULL;
2411 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2413 ctx = talloc_init("ads_add_machine_acct");
2415 return ADS_ERROR(LDAP_NO_MEMORY);
2418 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2419 if (machine_escaped == NULL) {
2420 ret = ADS_ERROR(LDAP_NO_MEMORY);
2424 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2425 if (utf8_pw == NULL) {
2426 ret = ADS_ERROR(LDAP_NO_MEMORY);
2429 utf8_pw_len = strlen(utf8_pw);
2431 ok = convert_string_talloc(ctx,
2432 CH_UTF8, CH_UTF16MUNGED,
2433 utf8_pw, utf8_pw_len,
2434 (void *)&utf16_pw, &utf16_pw_len);
2436 ret = ADS_ERROR(LDAP_NO_MEMORY);
2440 machine_pw_val = (struct berval) {
2442 .bv_len = utf16_pw_len,
2445 /* Check if the machine account already exists. */
2446 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2447 if (ADS_ERR_OK(ret)) {
2448 /* Change the machine account password */
2449 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2450 ads_msgfree(ads, res);
2454 ads_msgfree(ads, res);
2456 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2457 if (new_dn == NULL) {
2458 ret = ADS_ERROR(LDAP_NO_MEMORY);
2462 /* Create machine account */
2464 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2465 if (samAccountName == NULL) {
2466 ret = ADS_ERROR(LDAP_NO_MEMORY);
2470 dns_hostname = talloc_asprintf(ctx,
2474 if (dns_hostname == NULL) {
2475 ret = ADS_ERROR(LDAP_NO_MEMORY);
2479 /* Add dns_hostname SPNs */
2480 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2481 char *spn = talloc_asprintf(ctx,
2486 ret = ADS_ERROR(LDAP_NO_MEMORY);
2490 ok = add_string_to_array(spn_array,
2495 ret = ADS_ERROR(LDAP_NO_MEMORY);
2500 /* Add machine_name SPNs */
2501 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2502 char *spn = talloc_asprintf(ctx,
2507 ret = ADS_ERROR(LDAP_NO_MEMORY);
2511 ok = add_string_to_array(spn_array,
2516 ret = ADS_ERROR(LDAP_NO_MEMORY);
2521 /* Make sure to NULL terminate the array */
2522 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2523 if (spn_array == NULL) {
2524 ret = ADS_ERROR(LDAP_NO_MEMORY);
2527 spn_array[num_spns] = NULL;
2529 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2530 if (controlstr == NULL) {
2531 ret = ADS_ERROR(LDAP_NO_MEMORY);
2535 mods = ads_init_mods(ctx);
2537 ret = ADS_ERROR(LDAP_NO_MEMORY);
2541 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2542 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2543 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2544 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2545 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2546 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2548 ret = ads_gen_add(ads, new_dn, mods);
2551 SAFE_FREE(machine_escaped);
2552 talloc_destroy(ctx);
2558 * move a machine account to another OU on the ADS server
2559 * @param ads - An intialized ADS_STRUCT
2560 * @param machine_name - the NetBIOS machine name of this account.
2561 * @param org_unit - The LDAP path in which to place this account
2562 * @param moved - whether we moved the machine account (optional)
2563 * @return 0 upon success, or non-zero otherwise
2566 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2567 const char *org_unit, bool *moved)
2571 LDAPMessage *res = NULL;
2572 char *filter = NULL;
2573 char *computer_dn = NULL;
2575 char *computer_rdn = NULL;
2576 bool need_move = False;
2578 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2579 rc = ADS_ERROR(LDAP_NO_MEMORY);
2583 /* Find pre-existing machine */
2584 rc = ads_search(ads, &res, filter, NULL);
2585 if (!ADS_ERR_OK(rc)) {
2589 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2591 rc = ADS_ERROR(LDAP_NO_MEMORY);
2595 parent_dn = ads_parent_dn(computer_dn);
2596 if (strequal(parent_dn, org_unit)) {
2602 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2603 rc = ADS_ERROR(LDAP_NO_MEMORY);
2607 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2608 org_unit, 1, NULL, NULL);
2609 rc = ADS_ERROR(ldap_status);
2612 ads_msgfree(ads, res);
2614 TALLOC_FREE(computer_dn);
2615 SAFE_FREE(computer_rdn);
2617 if (!ADS_ERR_OK(rc)) {
2629 dump a binary result from ldap
2631 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2634 for (i=0; values[i]; i++) {
2636 printf("%s: ", field);
2637 for (j=0; j<values[i]->bv_len; j++) {
2638 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2644 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2647 for (i=0; values[i]; i++) {
2649 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2652 status = GUID_from_ndr_blob(&in, &guid);
2653 if (NT_STATUS_IS_OK(status)) {
2654 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2656 printf("%s: INVALID GUID\n", field);
2662 dump a sid result from ldap
2664 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2667 for (i=0; values[i]; i++) {
2670 struct dom_sid_buf tmp;
2671 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2672 values[i]->bv_len, &sid);
2676 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2681 dump ntSecurityDescriptor
2683 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2685 TALLOC_CTX *frame = talloc_stackframe();
2686 struct security_descriptor *psd;
2689 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2690 values[0]->bv_len, &psd);
2691 if (!NT_STATUS_IS_OK(status)) {
2692 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2693 nt_errstr(status)));
2699 ads_disp_sd(ads, talloc_tos(), psd);
2706 dump a string result from ldap
2708 static void dump_string(const char *field, char **values)
2711 for (i=0; values[i]; i++) {
2712 printf("%s: %s\n", field, values[i]);
2717 dump a field from LDAP on stdout
2721 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2726 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2728 {"objectGUID", False, dump_guid},
2729 {"netbootGUID", False, dump_guid},
2730 {"nTSecurityDescriptor", False, dump_sd},
2731 {"dnsRecord", False, dump_binary},
2732 {"objectSid", False, dump_sid},
2733 {"tokenGroups", False, dump_sid},
2734 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2735 {"tokengroupsGlobalandUniversal", False, dump_sid},
2736 {"mS-DS-CreatorSID", False, dump_sid},
2737 {"msExchMailboxGuid", False, dump_guid},
2742 if (!field) { /* must be end of an entry */
2747 for (i=0; handlers[i].name; i++) {
2748 if (strcasecmp_m(handlers[i].name, field) == 0) {
2749 if (!values) /* first time, indicate string or not */
2750 return handlers[i].string;
2751 handlers[i].handler(ads, field, (struct berval **) values);
2755 if (!handlers[i].name) {
2756 if (!values) /* first time, indicate string conversion */
2758 dump_string(field, (char **)values);
2764 * Dump a result from LDAP on stdout
2765 * used for debugging
2766 * @param ads connection to ads server
2767 * @param res Results to dump
2770 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2772 ads_process_results(ads, res, ads_dump_field, NULL);
2776 * Walk through results, calling a function for each entry found.
2777 * The function receives a field name, a berval * array of values,
2778 * and a data area passed through from the start. The function is
2779 * called once with null for field and values at the end of each
2781 * @param ads connection to ads server
2782 * @param res Results to process
2783 * @param fn Function for processing each result
2784 * @param data_area user-defined area to pass to function
2786 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2787 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2792 size_t converted_size;
2794 if (!(ctx = talloc_init("ads_process_results")))
2797 for (msg = ads_first_entry(ads, res); msg;
2798 msg = ads_next_entry(ads, msg)) {
2802 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2803 (LDAPMessage *)msg,&b);
2805 utf8_field=ldap_next_attribute(ads->ldap.ld,
2806 (LDAPMessage *)msg,b)) {
2807 struct berval **ber_vals;
2813 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2816 DEBUG(0,("ads_process_results: "
2817 "pull_utf8_talloc failed: %s",
2821 string = fn(ads, field, NULL, data_area);
2826 utf8_vals = ldap_get_values(ads->ldap.ld,
2827 (LDAPMessage *)msg, field);
2828 p = discard_const_p(const char *, utf8_vals);
2829 str_vals = ads_pull_strvals(ctx, p);
2830 fn(ads, field, (void **) str_vals, data_area);
2831 ldap_value_free(utf8_vals);
2833 ber_vals = ldap_get_values_len(ads->ldap.ld,
2834 (LDAPMessage *)msg, field);
2835 fn(ads, field, (void **) ber_vals, data_area);
2837 ldap_value_free_len(ber_vals);
2839 ldap_memfree(utf8_field);
2842 talloc_free_children(ctx);
2843 fn(ads, NULL, NULL, data_area); /* completed an entry */
2846 talloc_destroy(ctx);
2850 * count how many replies are in a LDAPMessage
2851 * @param ads connection to ads server
2852 * @param res Results to count
2853 * @return number of replies
2855 int ads_count_replies(ADS_STRUCT *ads, void *res)
2857 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2861 * pull the first entry from a ADS result
2862 * @param ads connection to ads server
2863 * @param res Results of search
2864 * @return first entry from result
2866 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2868 return ldap_first_entry(ads->ldap.ld, res);
2872 * pull the next entry from a ADS result
2873 * @param ads connection to ads server
2874 * @param res Results of search
2875 * @return next entry from result
2877 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2879 return ldap_next_entry(ads->ldap.ld, res);
2883 * pull the first message from a ADS result
2884 * @param ads connection to ads server
2885 * @param res Results of search
2886 * @return first message from result
2888 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2890 return ldap_first_message(ads->ldap.ld, res);
2894 * pull the next message from a ADS result
2895 * @param ads connection to ads server
2896 * @param res Results of search
2897 * @return next message from result
2899 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2901 return ldap_next_message(ads->ldap.ld, res);
2905 * pull a single string from a ADS result
2906 * @param ads connection to ads server
2907 * @param mem_ctx TALLOC_CTX to use for allocating result string
2908 * @param msg Results of search
2909 * @param field Attribute to retrieve
2910 * @return Result string in talloc context
2912 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2918 size_t converted_size;
2920 values = ldap_get_values(ads->ldap.ld, msg, field);
2924 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2929 ldap_value_free(values);
2934 * pull an array of strings from a ADS result
2935 * @param ads connection to ads server
2936 * @param mem_ctx TALLOC_CTX to use for allocating result string
2937 * @param msg Results of search
2938 * @param field Attribute to retrieve
2939 * @return Result strings in talloc context
2941 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2942 LDAPMessage *msg, const char *field,
2947 size_t i, converted_size;
2949 values = ldap_get_values(ads->ldap.ld, msg, field);
2953 *num_values = ldap_count_values(values);
2955 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2957 ldap_value_free(values);
2961 for (i=0;i<*num_values;i++) {
2962 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2965 ldap_value_free(values);
2971 ldap_value_free(values);
2976 * pull an array of strings from a ADS result
2977 * (handle large multivalue attributes with range retrieval)
2978 * @param ads connection to ads server
2979 * @param mem_ctx TALLOC_CTX to use for allocating result string
2980 * @param msg Results of search
2981 * @param field Attribute to retrieve
2982 * @param current_strings strings returned by a previous call to this function
2983 * @param next_attribute The next query should ask for this attribute
2984 * @param num_values How many values did we get this time?
2985 * @param more_values Are there more values to get?
2986 * @return Result strings in talloc context
2988 char **ads_pull_strings_range(ADS_STRUCT *ads,
2989 TALLOC_CTX *mem_ctx,
2990 LDAPMessage *msg, const char *field,
2991 char **current_strings,
2992 const char **next_attribute,
2993 size_t *num_strings,
2997 char *expected_range_attrib, *range_attr;
2998 BerElement *ptr = NULL;
3001 size_t num_new_strings;
3002 unsigned long int range_start;
3003 unsigned long int range_end;
3005 /* we might have been given the whole lot anyway */
3006 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3007 *more_strings = False;
3011 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3013 /* look for Range result */
3014 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3016 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3017 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3018 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3026 /* nothing here - this field is just empty */
3027 *more_strings = False;
3031 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3032 &range_start, &range_end) == 2) {
3033 *more_strings = True;
3035 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3036 &range_start) == 1) {
3037 *more_strings = False;
3039 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
3041 ldap_memfree(range_attr);
3042 *more_strings = False;
3047 if ((*num_strings) != range_start) {
3048 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3049 " - aborting range retreival\n",
3050 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3051 ldap_memfree(range_attr);
3052 *more_strings = False;
3056 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3058 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3059 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3060 "strings in this bunch, but we only got %lu - aborting range retreival\n",
3061 range_attr, (unsigned long int)range_end - range_start + 1,
3062 (unsigned long int)num_new_strings));
3063 ldap_memfree(range_attr);
3064 *more_strings = False;
3068 strings = talloc_realloc(mem_ctx, current_strings, char *,
3069 *num_strings + num_new_strings);
3071 if (strings == NULL) {
3072 ldap_memfree(range_attr);
3073 *more_strings = False;
3077 if (new_strings && num_new_strings) {
3078 memcpy(&strings[*num_strings], new_strings,
3079 sizeof(*new_strings) * num_new_strings);
3082 (*num_strings) += num_new_strings;
3084 if (*more_strings) {
3085 *next_attribute = talloc_asprintf(mem_ctx,
3090 if (!*next_attribute) {
3091 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3092 ldap_memfree(range_attr);
3093 *more_strings = False;
3098 ldap_memfree(range_attr);
3104 * pull a single uint32_t from a ADS result
3105 * @param ads connection to ads server
3106 * @param msg Results of search
3107 * @param field Attribute to retrieve
3108 * @param v Pointer to int to store result
3109 * @return boolean inidicating success
3111 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3116 values = ldap_get_values(ads->ldap.ld, msg, field);
3120 ldap_value_free(values);
3124 *v = atoi(values[0]);
3125 ldap_value_free(values);
3130 * pull a single objectGUID from an ADS result
3131 * @param ads connection to ADS server
3132 * @param msg results of search
3133 * @param guid 37-byte area to receive text guid
3134 * @return boolean indicating success
3136 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3141 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3146 status = GUID_from_ndr_blob(&blob, guid);
3147 talloc_free(blob.data);
3148 return NT_STATUS_IS_OK(status);
3153 * pull a single struct dom_sid from a ADS result
3154 * @param ads connection to ads server
3155 * @param msg Results of search
3156 * @param field Attribute to retrieve
3157 * @param sid Pointer to sid to store result
3158 * @return boolean inidicating success
3160 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3161 struct dom_sid *sid)
3163 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3167 * pull an array of struct dom_sids from a ADS result
3168 * @param ads connection to ads server
3169 * @param mem_ctx TALLOC_CTX for allocating sid array
3170 * @param msg Results of search
3171 * @param field Attribute to retrieve
3172 * @param sids pointer to sid array to allocate
3173 * @return the count of SIDs pulled
3175 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3176 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3178 struct berval **values;
3181 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3186 for (i=0; values[i]; i++)
3190 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3192 ldap_value_free_len(values);
3200 for (i=0; values[i]; i++) {
3202 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3203 values[i]->bv_len, &(*sids)[count]);
3205 struct dom_sid_buf buf;
3206 DBG_DEBUG("pulling SID: %s\n",
3207 dom_sid_str_buf(&(*sids)[count], &buf));
3212 ldap_value_free_len(values);
3217 * pull a struct security_descriptor from a ADS result
3218 * @param ads connection to ads server
3219 * @param mem_ctx TALLOC_CTX for allocating sid array
3220 * @param msg Results of search
3221 * @param field Attribute to retrieve
3222 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3223 * @return boolean inidicating success
3225 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3226 LDAPMessage *msg, const char *field,
3227 struct security_descriptor **sd)
3229 struct berval **values;
3232 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3234 if (!values) return false;
3238 status = unmarshall_sec_desc(mem_ctx,
3239 (uint8_t *)values[0]->bv_val,
3240 values[0]->bv_len, sd);
3241 if (!NT_STATUS_IS_OK(status)) {
3242 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3243 nt_errstr(status)));
3248 ldap_value_free_len(values);
3253 * in order to support usernames longer than 21 characters we need to
3254 * use both the sAMAccountName and the userPrincipalName attributes
3255 * It seems that not all users have the userPrincipalName attribute set
3257 * @param ads connection to ads server
3258 * @param mem_ctx TALLOC_CTX for allocating sid array
3259 * @param msg Results of search
3260 * @return the username
3262 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3268 /* lookup_name() only works on the sAMAccountName to
3269 returning the username portion of userPrincipalName
3270 breaks winbindd_getpwnam() */
3272 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3273 if (ret && (p = strchr_m(ret, '@'))) {
3278 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3283 * find the update serial number - this is the core of the ldap cache
3284 * @param ads connection to ads server
3285 * @param ads connection to ADS server
3286 * @param usn Pointer to retrieved update serial number
3287 * @return status of search
3289 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3291 const char *attrs[] = {"highestCommittedUSN", NULL};
3295 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3296 if (!ADS_ERR_OK(status))
3299 if (ads_count_replies(ads, res) != 1) {
3300 ads_msgfree(ads, res);
3301 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3304 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3305 ads_msgfree(ads, res);
3306 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3309 ads_msgfree(ads, res);
3313 /* parse a ADS timestring - typical string is
3314 '20020917091222.0Z0' which means 09:12.22 17th September
3316 static time_t ads_parse_time(const char *str)
3322 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3323 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3324 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3333 /********************************************************************
3334 ********************************************************************/
3336 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3338 const char *attrs[] = {"currentTime", NULL};
3342 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3343 ADS_STRUCT *ads_s = ads;
3345 /* establish a new ldap tcp session if necessary */
3347 if ( !ads->ldap.ld ) {
3349 * ADS_STRUCT may be being reused after a
3350 * DC lookup, so ads->ldap.ss may already have a
3351 * good address. If not, re-initialize the passed-in
3352 * ADS_STRUCT with the given server.XXXX parameters.
3354 * Note that this doesn't depend on
3355 * ads->server.ldap_server != NULL,
3356 * as the case where ads->server.ldap_server==NULL and
3357 * ads->ldap.ss != zero_address is precisely the DC
3358 * lookup case where ads->ldap.ss was found by going
3359 * through ads_find_dc() again we want to avoid repeating.
3361 if (is_zero_addr(&ads->ldap.ss)) {
3362 ads_s = ads_init(tmp_ctx,
3364 ads->server.workgroup,
3365 ads->server.ldap_server,
3367 if (ads_s == NULL) {
3368 status = ADS_ERROR(LDAP_NO_MEMORY);
3374 * Reset ads->config.flags as it can contain the flags
3375 * returned by the previous CLDAP ping when reusing the struct.
3377 ads_s->config.flags = 0;
3379 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3380 status = ads_connect( ads_s );
3381 if ( !ADS_ERR_OK(status))
3385 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3386 if (!ADS_ERR_OK(status)) {
3390 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3392 ads_msgfree(ads_s, res);
3393 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3397 /* but save the time and offset in the original ADS_STRUCT */
3399 ads->config.current_time = ads_parse_time(timestr);
3401 if (ads->config.current_time != 0) {
3402 ads->auth.time_offset = ads->config.current_time - time(NULL);
3403 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3406 ads_msgfree(ads, res);
3408 status = ADS_SUCCESS;
3411 TALLOC_FREE(tmp_ctx);
3416 /********************************************************************
3417 ********************************************************************/
3419 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3421 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3422 const char *attrs[] = {"domainFunctionality", NULL};
3425 ADS_STRUCT *ads_s = ads;
3427 *val = DS_DOMAIN_FUNCTION_2000;
3429 /* establish a new ldap tcp session if necessary */
3431 if ( !ads->ldap.ld ) {
3433 * ADS_STRUCT may be being reused after a
3434 * DC lookup, so ads->ldap.ss may already have a
3435 * good address. If not, re-initialize the passed-in
3436 * ADS_STRUCT with the given server.XXXX parameters.
3438 * Note that this doesn't depend on
3439 * ads->server.ldap_server != NULL,
3440 * as the case where ads->server.ldap_server==NULL and
3441 * ads->ldap.ss != zero_address is precisely the DC
3442 * lookup case where ads->ldap.ss was found by going
3443 * through ads_find_dc() again we want to avoid repeating.
3445 if (is_zero_addr(&ads->ldap.ss)) {
3446 ads_s = ads_init(tmp_ctx,
3448 ads->server.workgroup,
3449 ads->server.ldap_server,
3451 if (ads_s == NULL ) {
3452 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3458 * Reset ads->config.flags as it can contain the flags
3459 * returned by the previous CLDAP ping when reusing the struct.
3461 ads_s->config.flags = 0;
3463 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3464 status = ads_connect( ads_s );
3465 if ( !ADS_ERR_OK(status))
3469 /* If the attribute does not exist assume it is a Windows 2000
3470 functional domain */
3472 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3473 if (!ADS_ERR_OK(status)) {
3474 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3475 status = ADS_SUCCESS;
3480 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3481 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3483 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3486 ads_msgfree(ads_s, res);
3489 TALLOC_FREE(tmp_ctx);
3495 * find the domain sid for our domain
3496 * @param ads connection to ads server
3497 * @param sid Pointer to domain sid
3498 * @return status of search
3500 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3502 const char *attrs[] = {"objectSid", NULL};
3506 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3508 if (!ADS_ERR_OK(rc)) return rc;
3509 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3510 ads_msgfree(ads, res);
3511 return ADS_ERROR_SYSTEM(ENOENT);
3513 ads_msgfree(ads, res);
3519 * find our site name
3520 * @param ads connection to ads server
3521 * @param mem_ctx Pointer to talloc context
3522 * @param site_name Pointer to the sitename
3523 * @return status of search
3525 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3529 const char *dn, *service_name;
3530 const char *attrs[] = { "dsServiceName", NULL };
3532 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3533 if (!ADS_ERR_OK(status)) {
3537 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3538 if (service_name == NULL) {
3539 ads_msgfree(ads, res);
3540 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3543 ads_msgfree(ads, res);
3545 /* go up three levels */
3546 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3548 return ADS_ERROR(LDAP_NO_MEMORY);
3551 *site_name = talloc_strdup(mem_ctx, dn);
3552 if (*site_name == NULL) {
3553 return ADS_ERROR(LDAP_NO_MEMORY);
3558 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3563 * find the site dn where a machine resides
3564 * @param ads connection to ads server
3565 * @param mem_ctx Pointer to talloc context
3566 * @param computer_name name of the machine
3567 * @param site_name Pointer to the sitename
3568 * @return status of search
3570 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3574 const char *parent, *filter;
3575 char *config_context = NULL;
3578 /* shortcut a query */
3579 if (strequal(computer_name, ads->config.ldap_server_name)) {
3580 return ads_site_dn(ads, mem_ctx, site_dn);
3583 status = ads_config_path(ads, mem_ctx, &config_context);
3584 if (!ADS_ERR_OK(status)) {
3588 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3589 if (filter == NULL) {
3590 return ADS_ERROR(LDAP_NO_MEMORY);
3593 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3594 filter, NULL, &res);
3595 if (!ADS_ERR_OK(status)) {
3599 if (ads_count_replies(ads, res) != 1) {
3600 ads_msgfree(ads, res);
3601 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3604 dn = ads_get_dn(ads, mem_ctx, res);
3606 ads_msgfree(ads, res);
3607 return ADS_ERROR(LDAP_NO_MEMORY);
3610 /* go up three levels */
3611 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3612 if (parent == NULL) {
3613 ads_msgfree(ads, res);
3615 return ADS_ERROR(LDAP_NO_MEMORY);
3618 *site_dn = talloc_strdup(mem_ctx, parent);
3619 if (*site_dn == NULL) {
3620 ads_msgfree(ads, res);
3622 return ADS_ERROR(LDAP_NO_MEMORY);
3626 ads_msgfree(ads, res);
3632 * get the upn suffixes for a domain
3633 * @param ads connection to ads server
3634 * @param mem_ctx Pointer to talloc context
3635 * @param suffixes Pointer to an array of suffixes
3636 * @param num_suffixes Pointer to the number of suffixes
3637 * @return status of search
3639 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3644 char *config_context = NULL;
3645 const char *attrs[] = { "uPNSuffixes", NULL };
3647 status = ads_config_path(ads, mem_ctx, &config_context);
3648 if (!ADS_ERR_OK(status)) {
3652 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3654 return ADS_ERROR(LDAP_NO_MEMORY);
3657 status = ads_search_dn(ads, &res, base, attrs);
3658 if (!ADS_ERR_OK(status)) {
3662 if (ads_count_replies(ads, res) != 1) {
3663 ads_msgfree(ads, res);
3664 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3667 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3668 if ((*suffixes) == NULL) {
3669 ads_msgfree(ads, res);
3670 return ADS_ERROR(LDAP_NO_MEMORY);
3673 ads_msgfree(ads, res);
3679 * get the joinable ous for a domain
3680 * @param ads connection to ads server
3681 * @param mem_ctx Pointer to talloc context
3682 * @param ous Pointer to an array of ous
3683 * @param num_ous Pointer to the number of ous
3684 * @return status of search
3686 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3687 TALLOC_CTX *mem_ctx,
3692 LDAPMessage *res = NULL;
3693 LDAPMessage *msg = NULL;
3694 const char *attrs[] = { "dn", NULL };
3697 status = ads_search(ads, &res,
3698 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3700 if (!ADS_ERR_OK(status)) {
3704 count = ads_count_replies(ads, res);
3706 ads_msgfree(ads, res);
3707 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3710 for (msg = ads_first_entry(ads, res); msg;
3711 msg = ads_next_entry(ads, msg)) {
3712 const char **p = discard_const_p(const char *, *ous);
3715 dn = ads_get_dn(ads, talloc_tos(), msg);
3717 ads_msgfree(ads, res);
3718 return ADS_ERROR(LDAP_NO_MEMORY);
3721 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3723 ads_msgfree(ads, res);
3724 return ADS_ERROR(LDAP_NO_MEMORY);
3728 *ous = discard_const_p(char *, p);
3731 ads_msgfree(ads, res);
3738 * pull a struct dom_sid from an extended dn string
3739 * @param mem_ctx TALLOC_CTX
3740 * @param extended_dn string
3741 * @param flags string type of extended_dn
3742 * @param sid pointer to a struct dom_sid
3743 * @return NT_STATUS_OK on success,
3744 * NT_INVALID_PARAMETER on error,
3745 * NT_STATUS_NOT_FOUND if no SID present
3747 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3748 const char *extended_dn,
3749 enum ads_extended_dn_flags flags,
3750 struct dom_sid *sid)
3755 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3758 /* otherwise extended_dn gets stripped off */
3759 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3760 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3763 * ADS_EXTENDED_DN_HEX_STRING:
3764 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3766 * ADS_EXTENDED_DN_STRING (only with w2k3):
3767 * <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
3769 * Object with no SID, such as an Exchange Public Folder
3770 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3773 p = strchr(dn, ';');
3775 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3778 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3779 DEBUG(5,("No SID present in extended dn\n"));
3780 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3783 p += strlen(";<SID=");
3787 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3792 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3796 case ADS_EXTENDED_DN_STRING:
3797 if (!string_to_sid(sid, p)) {
3798 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3801 case ADS_EXTENDED_DN_HEX_STRING: {
3806 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3808 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3811 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3813 DEBUG(10,("failed to parse sid\n"));
3814 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3819 DEBUG(10,("unknown extended dn format\n"));
3820 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3823 return ADS_ERROR_NT(NT_STATUS_OK);
3826 /********************************************************************
3827 ********************************************************************/
3829 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3831 LDAPMessage *res = NULL;
3836 status = ads_find_machine_acct(ads, &res, machine_name);
3837 if (!ADS_ERR_OK(status)) {
3838 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3839 lp_netbios_name()));
3843 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3844 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3848 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3849 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3853 ads_msgfree(ads, res);
3858 /********************************************************************
3859 ********************************************************************/
3861 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3862 LDAPMessage *msg, size_t *num_values)
3864 const char *field = "msDS-AdditionalDnsHostName";
3865 struct berval **values = NULL;
3867 size_t i, converted_size;
3870 * Windows DC implicitly adds a short name for each FQDN added to
3871 * msDS-AdditionalDnsHostName, but it comes with a strage binary
3872 * suffix "\0$" which we should ignore (see bug #14406).
3875 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3876 if (values == NULL) {
3880 *num_values = ldap_count_values_len(values);
3882 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3884 ldap_value_free_len(values);
3888 for (i = 0; i < *num_values; i++) {
3890 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
3892 strnlen(values[i]->bv_val,
3894 &ret[i], &converted_size)) {
3895 ldap_value_free_len(values);
3901 ldap_value_free_len(values);
3905 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
3907 const char *machine_name,
3908 char ***hostnames_array,
3909 size_t *num_hostnames)
3912 LDAPMessage *res = NULL;
3915 status = ads_find_machine_acct(ads,
3918 if (!ADS_ERR_OK(status)) {
3919 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
3924 count = ads_count_replies(ads, res);
3926 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3930 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
3931 if (*hostnames_array == NULL) {
3932 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
3934 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3939 ads_msgfree(ads, res);
3944 /********************************************************************
3945 ********************************************************************/
3947 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3949 LDAPMessage *res = NULL;
3954 status = ads_find_machine_acct(ads, &res, machine_name);
3955 if (!ADS_ERR_OK(status)) {
3956 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3957 lp_netbios_name()));
3961 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3962 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3966 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3967 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3971 ads_msgfree(ads, res);
3976 /********************************************************************
3977 ********************************************************************/
3979 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3981 LDAPMessage *res = NULL;
3987 status = ads_find_machine_acct(ads, &res, machine_name);
3988 if (!ADS_ERR_OK(status)) {
3989 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3990 lp_netbios_name()));
3994 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3995 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3999 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4000 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4004 ads_msgfree(ads, res);
4006 ok = (strlen(name) > 0);
4014 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4017 * Join a machine to a realm
4018 * Creates the machine account and sets the machine password
4019 * @param ads connection to ads server
4020 * @param machine name of host to add
4021 * @param org_unit Organizational unit to place machine in
4022 * @return status of join
4024 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4025 uint32_t account_type, const char *org_unit)
4028 LDAPMessage *res = NULL;
4031 /* machine name must be lowercase */
4032 machine = SMB_STRDUP(machine_name);
4033 strlower_m(machine);
4036 status = ads_find_machine_acct(ads, (void **)&res, machine);
4037 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4038 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4039 status = ads_leave_realm(ads, machine);
4040 if (!ADS_ERR_OK(status)) {
4041 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4042 machine, ads->config.realm));
4047 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4048 if (!ADS_ERR_OK(status)) {
4049 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4054 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4055 if (!ADS_ERR_OK(status)) {
4056 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4062 ads_msgfree(ads, res);
4069 * Delete a machine from the realm
4070 * @param ads connection to ads server
4071 * @param hostname Machine to remove
4072 * @return status of delete
4074 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4079 char *hostnameDN, *host;
4081 LDAPControl ldap_control;
4082 LDAPControl * pldap_control[2] = {NULL, NULL};
4084 pldap_control[0] = &ldap_control;
4085 memset(&ldap_control, 0, sizeof(LDAPControl));
4086 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4088 /* hostname must be lowercase */
4089 host = SMB_STRDUP(hostname);
4090 if (!strlower_m(host)) {
4092 return ADS_ERROR_SYSTEM(EINVAL);
4095 status = ads_find_machine_acct(ads, &res, host);
4096 if (!ADS_ERR_OK(status)) {
4097 DEBUG(0, ("Host account for %s does not exist.\n", host));
4102 msg = ads_first_entry(ads, res);
4105 return ADS_ERROR_SYSTEM(ENOENT);
4108 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4109 if (hostnameDN == NULL) {
4111 return ADS_ERROR_SYSTEM(ENOENT);
4114 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4116 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4118 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4121 if (rc != LDAP_SUCCESS) {
4122 const char *attrs[] = { "cn", NULL };
4123 LDAPMessage *msg_sub;
4125 /* we only search with scope ONE, we do not expect any further
4126 * objects to be created deeper */
4128 status = ads_do_search_retry(ads, hostnameDN,
4129 LDAP_SCOPE_ONELEVEL,
4130 "(objectclass=*)", attrs, &res);
4132 if (!ADS_ERR_OK(status)) {
4134 TALLOC_FREE(hostnameDN);
4138 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4139 msg_sub = ads_next_entry(ads, msg_sub)) {
4143 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4145 TALLOC_FREE(hostnameDN);
4146 return ADS_ERROR(LDAP_NO_MEMORY);
4149 status = ads_del_dn(ads, dn);
4150 if (!ADS_ERR_OK(status)) {
4151 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4154 TALLOC_FREE(hostnameDN);
4161 /* there should be no subordinate objects anymore */
4162 status = ads_do_search_retry(ads, hostnameDN,
4163 LDAP_SCOPE_ONELEVEL,
4164 "(objectclass=*)", attrs, &res);
4166 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4168 TALLOC_FREE(hostnameDN);
4172 /* delete hostnameDN now */
4173 status = ads_del_dn(ads, hostnameDN);
4174 if (!ADS_ERR_OK(status)) {
4176 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4177 TALLOC_FREE(hostnameDN);
4182 TALLOC_FREE(hostnameDN);
4184 status = ads_find_machine_acct(ads, &res, host);
4185 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4186 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4187 DEBUG(3, ("Failed to remove host account.\n"));
4197 * pull all token-sids from an LDAP dn
4198 * @param ads connection to ads server
4199 * @param mem_ctx TALLOC_CTX for allocating sid array
4200 * @param dn of LDAP object
4201 * @param user_sid pointer to struct dom_sid (objectSid)
4202 * @param primary_group_sid pointer to struct dom_sid (self composed)
4203 * @param sids pointer to sid array to allocate
4204 * @param num_sids counter of SIDs pulled
4205 * @return status of token query
4207 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4208 TALLOC_CTX *mem_ctx,
4210 struct dom_sid *user_sid,
4211 struct dom_sid *primary_group_sid,
4212 struct dom_sid **sids,
4216 LDAPMessage *res = NULL;
4218 size_t tmp_num_sids;
4219 struct dom_sid *tmp_sids;
4220 struct dom_sid tmp_user_sid;
4221 struct dom_sid tmp_primary_group_sid;
4223 const char *attrs[] = {
4230 status = ads_search_retry_dn(ads, &res, dn, attrs);
4231 if (!ADS_ERR_OK(status)) {
4235 count = ads_count_replies(ads, res);
4237 ads_msgfree(ads, res);
4238 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4241 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4242 ads_msgfree(ads, res);
4243 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4246 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4247 ads_msgfree(ads, res);
4248 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4252 /* hack to compose the primary group sid without knowing the
4255 struct dom_sid domsid;
4257 sid_copy(&domsid, &tmp_user_sid);
4259 if (!sid_split_rid(&domsid, NULL)) {
4260 ads_msgfree(ads, res);
4261 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4264 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4265 ads_msgfree(ads, res);
4266 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4270 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4272 if (tmp_num_sids == 0 || !tmp_sids) {
4273 ads_msgfree(ads, res);
4274 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4278 *num_sids = tmp_num_sids;
4286 *user_sid = tmp_user_sid;
4289 if (primary_group_sid) {
4290 *primary_group_sid = tmp_primary_group_sid;
4293 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4295 ads_msgfree(ads, res);
4296 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4300 * Find a sAMAccoutName in LDAP
4301 * @param ads connection to ads server
4302 * @param mem_ctx TALLOC_CTX for allocating sid array
4303 * @param samaccountname to search
4304 * @param uac_ret uint32_t pointer userAccountControl attribute value
4305 * @param dn_ret pointer to dn
4306 * @return status of token query
4308 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4309 TALLOC_CTX *mem_ctx,
4310 const char *samaccountname,
4312 const char **dn_ret)
4315 const char *attrs[] = { "userAccountControl", NULL };
4317 LDAPMessage *res = NULL;
4321 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4323 if (filter == NULL) {
4324 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4328 status = ads_do_search_all(ads, ads->config.bind_path,
4330 filter, attrs, &res);
4332 if (!ADS_ERR_OK(status)) {
4336 if (ads_count_replies(ads, res) != 1) {
4337 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4341 dn = ads_get_dn(ads, talloc_tos(), res);
4343 status = ADS_ERROR(LDAP_NO_MEMORY);
4347 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4348 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4357 *dn_ret = talloc_strdup(mem_ctx, dn);
4359 status = ADS_ERROR(LDAP_NO_MEMORY);
4365 ads_msgfree(ads, res);
4371 * find our configuration path
4372 * @param ads connection to ads server
4373 * @param mem_ctx Pointer to talloc context
4374 * @param config_path Pointer to the config path
4375 * @return status of search
4377 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4378 TALLOC_CTX *mem_ctx,
4382 LDAPMessage *res = NULL;
4383 const char *config_context = NULL;
4384 const char *attrs[] = { "configurationNamingContext", NULL };
4386 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4387 "(objectclass=*)", attrs, &res);
4388 if (!ADS_ERR_OK(status)) {
4392 config_context = ads_pull_string(ads, mem_ctx, res,
4393 "configurationNamingContext");
4394 ads_msgfree(ads, res);
4395 if (!config_context) {
4396 return ADS_ERROR(LDAP_NO_MEMORY);
4400 *config_path = talloc_strdup(mem_ctx, config_context);
4401 if (!*config_path) {
4402 return ADS_ERROR(LDAP_NO_MEMORY);
4406 return ADS_ERROR(LDAP_SUCCESS);
4410 * find the displayName of an extended right
4411 * @param ads connection to ads server
4412 * @param config_path The config path
4413 * @param mem_ctx Pointer to talloc context
4414 * @param GUID struct of the rightsGUID
4415 * @return status of search
4417 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4418 const char *config_path,
4419 TALLOC_CTX *mem_ctx,
4420 const struct GUID *rights_guid)
4423 LDAPMessage *res = NULL;
4425 const char *attrs[] = { "displayName", NULL };
4426 const char *result = NULL;
4429 if (!ads || !mem_ctx || !rights_guid) {
4433 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4434 GUID_string(mem_ctx, rights_guid));
4439 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4444 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4446 if (!ADS_ERR_OK(rc)) {
4450 if (ads_count_replies(ads, res) != 1) {
4454 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4457 ads_msgfree(ads, res);
4462 * verify or build and verify an account ou
4463 * @param mem_ctx Pointer to talloc context
4464 * @param ads connection to ads server
4466 * @return status of search
4469 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4471 const char **account_ou)
4477 if (account_ou == NULL) {
4478 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4481 if (*account_ou != NULL) {
4482 exploded_dn = ldap_explode_dn(*account_ou, 0);
4484 ldap_value_free(exploded_dn);
4489 ou_string = ads_ou_string(ads, *account_ou);
4491 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4494 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4495 ads->config.bind_path);
4496 SAFE_FREE(ou_string);
4499 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4502 exploded_dn = ldap_explode_dn(name, 0);
4504 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4506 ldap_value_free(exploded_dn);