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));
252 static bool ads_fill_cldap_reply(ADS_STRUCT *ads,
254 const struct sockaddr_storage *ss,
255 const struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply)
257 TALLOC_CTX *frame = talloc_stackframe();
259 char addr[INET6_ADDRSTRLEN];
262 print_sockaddr(addr, sizeof(addr), ss);
264 /* Check the CLDAP reply flags */
266 if (!(cldap_reply->server_type & NBT_SERVER_LDAP)) {
267 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
273 /* Fill in the ads->config values */
275 TALLOC_FREE(ads->config.realm);
276 TALLOC_FREE(ads->config.bind_path);
277 TALLOC_FREE(ads->config.ldap_server_name);
278 TALLOC_FREE(ads->config.server_site_name);
279 TALLOC_FREE(ads->config.client_site_name);
280 TALLOC_FREE(ads->server.workgroup);
282 if (!check_cldap_reply_required_flags(cldap_reply->server_type,
283 ads->config.flags)) {
288 ads->config.ldap_server_name = talloc_strdup(ads,
289 cldap_reply->pdc_dns_name);
290 if (ads->config.ldap_server_name == NULL) {
291 DBG_WARNING("Out of memory\n");
296 ads->config.realm = talloc_asprintf_strupper_m(ads,
298 cldap_reply->dns_domain);
299 if (ads->config.realm == NULL) {
300 DBG_WARNING("Out of memory\n");
305 status = ads_build_dn(ads->config.realm, ads, &ads->config.bind_path);
306 if (!ADS_ERR_OK(status)) {
307 DBG_DEBUG("Failed to build bind path: %s\n",
313 if (*cldap_reply->server_site) {
314 ads->config.server_site_name =
315 talloc_strdup(ads, cldap_reply->server_site);
316 if (ads->config.server_site_name == NULL) {
317 DBG_WARNING("Out of memory\n");
323 if (*cldap_reply->client_site) {
324 ads->config.client_site_name =
325 talloc_strdup(ads, cldap_reply->client_site);
326 if (ads->config.client_site_name == NULL) {
327 DBG_WARNING("Out of memory\n");
333 ads->server.workgroup = talloc_strdup(ads, cldap_reply->domain_name);
334 if (ads->server.workgroup == NULL) {
335 DBG_WARNING("Out of memory\n");
340 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
343 /* Store our site name. */
344 sitename_store(cldap_reply->domain_name, cldap_reply->client_site);
345 sitename_store(cldap_reply->dns_domain, cldap_reply->client_site);
347 /* Leave this until last so that the flags are not clobbered */
348 ads->config.flags = cldap_reply->server_type;
359 try a connection to a given ldap server, returning True and setting the servers IP
360 in the ads struct if successful
362 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
363 struct sockaddr_storage *ss)
365 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply = {};
366 TALLOC_CTX *frame = talloc_stackframe();
368 char addr[INET6_ADDRSTRLEN] = { 0, };
375 print_sockaddr(addr, sizeof(addr), ss);
377 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
378 addr, ads->server.realm);
380 ok = ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply);
382 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
383 addr, ads->server.realm);
388 ok = ads_fill_cldap_reply(ads, gc, ss, &cldap_reply);
390 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
391 addr, ads->server.realm);
400 /**********************************************************************
401 send a cldap ping to list of servers, one at a time, until one of
402 them answers it's an ldap server. Record success in the ADS_STRUCT.
403 Take note of and update negative connection cache.
404 **********************************************************************/
406 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
408 struct samba_sockaddr *sa_list,
414 for (i = 0; i < count; i++) {
415 char server[INET6_ADDRSTRLEN];
417 if (is_zero_addr(&sa_list[i].u.ss)) {
421 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
423 if (!NT_STATUS_IS_OK(
424 check_negative_conn_cache(domain, server)))
427 /* Returns ok only if it matches the correct server type */
428 ok = ads_try_connect(ads, false, &sa_list[i].u.ss);
434 /* keep track of failures */
435 add_failed_connection_entry(domain, server,
436 NT_STATUS_UNSUCCESSFUL);
439 return NT_STATUS_NO_LOGON_SERVERS;
442 /***************************************************************************
443 resolve a name and perform an "ldap ping" using NetBIOS and related methods
444 ****************************************************************************/
446 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
447 const char *domain, const char *realm)
451 struct samba_sockaddr *sa_list = NULL;
454 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
457 status = get_sorted_dc_list(talloc_tos(),
463 if (!NT_STATUS_IS_OK(status)) {
467 /* remove servers which are known to be dead based on
468 the corresponding DNS method */
470 for (i = 0; i < count; ++i) {
471 char server[INET6_ADDRSTRLEN];
473 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
476 check_negative_conn_cache(realm, server))) {
477 /* Ensure we add the workgroup name for this
478 IP address as negative too. */
479 add_failed_connection_entry(
481 NT_STATUS_UNSUCCESSFUL);
486 status = cldap_ping_list(ads, domain, sa_list, count);
488 TALLOC_FREE(sa_list);
494 /**********************************************************************
495 resolve a name and perform an "ldap ping" using DNS
496 **********************************************************************/
498 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
502 struct samba_sockaddr *sa_list = NULL;
505 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
508 status = get_sorted_dc_list(talloc_tos(),
514 if (!NT_STATUS_IS_OK(status)) {
515 TALLOC_FREE(sa_list);
519 status = cldap_ping_list(ads, realm, sa_list, count);
521 TALLOC_FREE(sa_list);
526 /**********************************************************************
527 Try to find an AD dc using our internal name resolution routines
528 Try the realm first and then then workgroup name if netbios is not
530 **********************************************************************/
532 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
534 const char *c_domain = "";
536 bool use_own_domain = False;
537 char *sitename = NULL;
538 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
541 /* if the realm and workgroup are both empty, assume they are ours */
544 c_realm = ads->server.realm;
550 /* special case where no realm and no workgroup means our own */
551 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
552 use_own_domain = True;
553 c_realm = lp_realm();
557 if (!lp_disable_netbios()) {
558 if (use_own_domain) {
559 c_domain = lp_workgroup();
561 c_domain = ads->server.workgroup;
562 if (!*c_realm && (!c_domain || !*c_domain)) {
563 c_domain = lp_workgroup();
572 if (!*c_realm && !*c_domain) {
573 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
575 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
579 * In case of LDAP we use get_dc_name() as that
580 * creates the custom krb5.conf file
582 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
584 struct sockaddr_storage ip_out;
586 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
587 " and falling back to domain '%s'\n",
590 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
592 if (is_zero_addr(&ip_out)) {
593 return NT_STATUS_NO_LOGON_SERVERS;
597 * we call ads_try_connect() to fill in the
598 * ads->config details
600 ok = ads_try_connect(ads, false, &ip_out);
606 return NT_STATUS_NO_LOGON_SERVERS;
610 sitename = sitename_fetch(talloc_tos(), c_realm);
611 status = resolve_and_ping_dns(ads, sitename, c_realm);
613 if (NT_STATUS_IS_OK(status)) {
614 TALLOC_FREE(sitename);
618 /* In case we failed to contact one of our closest DC on our
620 * need to try to find another DC, retry with a site-less SRV
625 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
626 "our site (%s), Trying to find another DC "
627 "for realm '%s' (domain '%s')\n",
628 sitename, c_realm, c_domain));
629 namecache_delete(c_realm, 0x1C);
631 resolve_and_ping_dns(ads, NULL, c_realm);
633 if (NT_STATUS_IS_OK(status)) {
634 TALLOC_FREE(sitename);
639 TALLOC_FREE(sitename);
642 /* try netbios as fallback - if permitted,
643 or if configuration specifically requests it */
646 DEBUG(3, ("ads_find_dc: falling back to netbios "
647 "name resolution for domain '%s' (realm '%s')\n",
651 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
652 if (NT_STATUS_IS_OK(status)) {
657 DEBUG(1, ("ads_find_dc: "
658 "name resolution for realm '%s' (domain '%s') failed: %s\n",
659 c_realm, c_domain, nt_errstr(status)));
663 * Connect to the LDAP server
664 * @param ads Pointer to an existing ADS_STRUCT
665 * @return status of connection
667 ADS_STATUS ads_connect(ADS_STRUCT *ads)
669 int version = LDAP_VERSION3;
672 char addr[INET6_ADDRSTRLEN];
673 struct sockaddr_storage existing_ss;
675 zero_sockaddr(&existing_ss);
678 * ads_connect can be passed in a reused ADS_STRUCT
679 * with an existing non-zero ads->ldap.ss IP address
680 * that was stored by going through ads_find_dc()
681 * if ads->server.ldap_server was NULL.
683 * If ads->server.ldap_server is still NULL but
684 * the target address isn't the zero address, then
685 * store that address off off before zeroing out
686 * ads->ldap so we don't keep doing multiple calls
687 * to ads_find_dc() in the reuse case.
689 * If a caller wants a clean ADS_STRUCT they
690 * will TALLOC_FREE it and allocate a new one
691 * by calling ads_init(), which ensures
692 * ads->ldap.ss is a properly zero'ed out valid IP
695 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
696 /* Save off the address we previously found by ads_find_dc(). */
697 existing_ss = ads->ldap.ss;
701 ZERO_STRUCT(ads->ldap_wrap_data);
702 ads->ldap.last_attempt = time_mono(NULL);
703 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
705 /* try with a user specified server */
707 if (DEBUGLEVEL >= 11) {
708 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
709 DEBUG(11,("ads_connect: entering\n"));
710 DEBUGADD(11,("%s\n", s));
714 if (ads->server.ldap_server) {
716 struct sockaddr_storage ss;
718 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
720 DEBUG(5,("ads_connect: unable to resolve name %s\n",
721 ads->server.ldap_server));
722 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
726 if (is_zero_addr(&ss)) {
727 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
731 ok = ads_try_connect(ads, ads->server.gc, &ss);
736 /* The choice of which GC use is handled one level up in
737 ads_connect_gc(). If we continue on from here with
738 ads_find_dc() we will get GC searches on port 389 which
739 doesn't work. --jerry */
741 if (ads->server.gc == true) {
742 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
745 if (ads->server.no_fallback) {
746 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
751 if (!is_zero_addr(&existing_ss)) {
752 /* We saved off who we should talk to. */
753 bool ok = ads_try_connect(ads,
760 * Keep trying to find a server and fall through
761 * into ads_find_dc() again.
765 ntstatus = ads_find_dc(ads);
766 if (NT_STATUS_IS_OK(ntstatus)) {
770 status = ADS_ERROR_NT(ntstatus);
775 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
776 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
778 if (!ads->auth.user_name) {
779 /* Must use the userPrincipalName value here or sAMAccountName
780 and not servicePrincipalName; found by Guenther Deschner */
781 ads->auth.user_name = talloc_asprintf(ads,
784 if (ads->auth.user_name == NULL) {
785 DBG_ERR("talloc_asprintf failed\n");
786 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
791 if (ads->auth.realm == NULL) {
792 ads->auth.realm = talloc_strdup(ads, ads->config.realm);
793 if (ads->auth.realm == NULL) {
794 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
799 if (!ads->auth.kdc_server) {
800 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
801 ads->auth.kdc_server = talloc_strdup(ads, addr);
802 if (ads->auth.kdc_server == NULL) {
803 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
808 /* If the caller() requested no LDAP bind, then we are done */
810 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
811 status = ADS_SUCCESS;
815 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
816 if (!ads->ldap_wrap_data.mem_ctx) {
817 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
821 /* Otherwise setup the TCP LDAP session */
823 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
825 ads->ldap.port, lp_ldap_timeout());
826 if (ads->ldap.ld == NULL) {
827 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
830 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
832 /* cache the successful connection for workgroup and realm */
833 if (ads_closest_dc(ads)) {
834 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
835 saf_store( ads->server.realm, ads->config.ldap_server_name);
838 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
840 /* fill in the current time and offsets */
842 status = ads_current_time( ads );
843 if ( !ADS_ERR_OK(status) ) {
847 /* Now do the bind */
849 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
850 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
854 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
855 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
859 status = ads_sasl_bind(ads);
862 if (DEBUGLEVEL >= 11) {
863 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
864 DEBUG(11,("ads_connect: leaving with: %s\n",
865 ads_errstr(status)));
866 DEBUGADD(11,("%s\n", s));
874 * Connect to the LDAP server using given credentials
875 * @param ads Pointer to an existing ADS_STRUCT
876 * @return status of connection
878 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
880 ads->auth.flags |= ADS_AUTH_USER_CREDS;
882 return ads_connect(ads);
886 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
887 * @param ads Pointer to an existing ADS_STRUCT
889 * Sets the ads->ldap.ss to a valid
890 * zero ip address that can be detected by
891 * our is_zero_addr() function. Otherwise
892 * it is left as AF_UNSPEC (0).
894 void ads_zero_ldap(ADS_STRUCT *ads)
896 ZERO_STRUCT(ads->ldap);
898 * Initialize the sockaddr_storage so we can use
899 * sockaddr test functions against it.
901 zero_sockaddr(&ads->ldap.ss);
905 * Disconnect the LDAP server
906 * @param ads Pointer to an existing ADS_STRUCT
908 void ads_disconnect(ADS_STRUCT *ads)
911 ldap_unbind(ads->ldap.ld);
914 if (ads->ldap_wrap_data.wrap_ops &&
915 ads->ldap_wrap_data.wrap_ops->disconnect) {
916 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
918 if (ads->ldap_wrap_data.mem_ctx) {
919 talloc_free(ads->ldap_wrap_data.mem_ctx);
922 ZERO_STRUCT(ads->ldap_wrap_data);
926 Duplicate a struct berval into talloc'ed memory
928 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
930 struct berval *value;
932 if (!in_val) return NULL;
934 value = talloc_zero(ctx, struct berval);
937 if (in_val->bv_len == 0) return value;
939 value->bv_len = in_val->bv_len;
940 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
946 Make a values list out of an array of (struct berval *)
948 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
949 const struct berval **in_vals)
951 struct berval **values;
954 if (!in_vals) return NULL;
955 for (i=0; in_vals[i]; i++)
957 values = talloc_zero_array(ctx, struct berval *, i+1);
958 if (!values) return NULL;
960 for (i=0; in_vals[i]; i++) {
961 values[i] = dup_berval(ctx, in_vals[i]);
967 UTF8-encode a values list out of an array of (char *)
969 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
975 if (!in_vals) return NULL;
976 for (i=0; in_vals[i]; i++)
978 values = talloc_zero_array(ctx, char *, i+1);
979 if (!values) return NULL;
981 for (i=0; in_vals[i]; i++) {
982 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
991 Pull a (char *) array out of a UTF8-encoded values list
993 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
997 size_t converted_size;
999 if (!in_vals) return NULL;
1000 for (i=0; in_vals[i]; i++)
1001 ; /* count values */
1002 values = talloc_zero_array(ctx, char *, i+1);
1003 if (!values) return NULL;
1005 for (i=0; in_vals[i]; i++) {
1006 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1008 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1009 "%s", strerror(errno)));
1016 * Do a search with paged results. cookie must be null on the first
1017 * call, and then returned on each subsequent call. It will be null
1018 * again when the entire search is complete
1019 * @param ads connection to ads server
1020 * @param bind_path Base dn for the search
1021 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1022 * @param expr Search expression - specified in local charset
1023 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1024 * @param res ** which will contain results - free res* with ads_msgfree()
1025 * @param count Number of entries retrieved on this page
1026 * @param cookie The paged results cookie to be returned on subsequent calls
1027 * @return status of search
1029 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1030 const char *bind_path,
1031 int scope, const char *expr,
1032 const char **attrs, void *args,
1034 int *count, struct berval **cookie)
1037 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1038 size_t converted_size;
1039 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1040 BerElement *cookie_be = NULL;
1041 struct berval *cookie_bv= NULL;
1042 BerElement *ext_be = NULL;
1043 struct berval *ext_bv= NULL;
1046 ads_control *external_control = (ads_control *) args;
1050 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1051 return ADS_ERROR(LDAP_NO_MEMORY);
1053 /* 0 means the conversion worked but the result was empty
1054 so we only fail if it's -1. In any case, it always
1055 at least nulls out the dest */
1056 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1057 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1059 rc = LDAP_NO_MEMORY;
1063 if (!attrs || !(*attrs))
1064 search_attrs = NULL;
1066 /* This would be the utf8-encoded version...*/
1067 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1068 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1069 rc = LDAP_NO_MEMORY;
1074 /* Paged results only available on ldap v3 or later */
1075 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1076 if (version < LDAP_VERSION3) {
1077 rc = LDAP_NOT_SUPPORTED;
1081 cookie_be = ber_alloc_t(LBER_USE_DER);
1083 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1084 ber_bvfree(*cookie); /* don't need it from last time */
1087 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1089 ber_flatten(cookie_be, &cookie_bv);
1090 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1091 PagedResults.ldctl_iscritical = (char) 1;
1092 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1093 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1095 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1096 NoReferrals.ldctl_iscritical = (char) 0;
1097 NoReferrals.ldctl_value.bv_len = 0;
1098 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1100 if (external_control &&
1101 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1102 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1104 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1105 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1107 /* win2k does not accept a ldctl_value beeing passed in */
1109 if (external_control->val != 0) {
1111 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1112 rc = LDAP_NO_MEMORY;
1116 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1117 rc = LDAP_NO_MEMORY;
1120 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1121 rc = LDAP_NO_MEMORY;
1125 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1126 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1129 ExternalCtrl.ldctl_value.bv_len = 0;
1130 ExternalCtrl.ldctl_value.bv_val = NULL;
1133 controls[0] = &NoReferrals;
1134 controls[1] = &PagedResults;
1135 controls[2] = &ExternalCtrl;
1139 controls[0] = &NoReferrals;
1140 controls[1] = &PagedResults;
1144 /* we need to disable referrals as the openldap libs don't
1145 handle them and paged results at the same time. Using them
1146 together results in the result record containing the server
1147 page control being removed from the result list (tridge/jmcd)
1149 leaving this in despite the control that says don't generate
1150 referrals, in case the server doesn't support it (jmcd)
1152 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1154 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1155 search_attrs, 0, controls,
1156 NULL, LDAP_NO_LIMIT,
1157 (LDAPMessage **)res);
1159 ber_free(cookie_be, 1);
1160 ber_bvfree(cookie_bv);
1163 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1164 ldap_err2string(rc)));
1165 if (rc == LDAP_OTHER) {
1169 ret = ldap_parse_result(ads->ldap.ld,
1177 if (ret == LDAP_SUCCESS) {
1178 DEBUG(3, ("ldap_search_with_timeout(%s) "
1179 "error: %s\n", expr, ldap_errmsg));
1180 ldap_memfree(ldap_errmsg);
1186 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1187 NULL, &rcontrols, 0);
1193 for (i=0; rcontrols[i]; i++) {
1194 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1195 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1196 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1198 /* the berval is the cookie, but must be freed when
1200 if (cookie_bv->bv_len) /* still more to do */
1201 *cookie=ber_bvdup(cookie_bv);
1204 ber_bvfree(cookie_bv);
1205 ber_free(cookie_be, 1);
1209 ldap_controls_free(rcontrols);
1212 talloc_destroy(ctx);
1215 ber_free(ext_be, 1);
1222 if (rc != LDAP_SUCCESS && *res != NULL) {
1223 ads_msgfree(ads, *res);
1227 /* if/when we decide to utf8-encode attrs, take out this next line */
1228 TALLOC_FREE(search_attrs);
1230 return ADS_ERROR(rc);
1233 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1234 int scope, const char *expr,
1235 const char **attrs, LDAPMessage **res,
1236 int *count, struct berval **cookie)
1238 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1243 * Get all results for a search. This uses ads_do_paged_search() to return
1244 * all entries in a large search.
1245 * @param ads connection to ads server
1246 * @param bind_path Base dn for the search
1247 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1248 * @param expr Search expression
1249 * @param attrs Attributes to retrieve
1250 * @param res ** which will contain results - free res* with ads_msgfree()
1251 * @return status of search
1253 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1254 int scope, const char *expr,
1255 const char **attrs, void *args,
1258 struct berval *cookie = NULL;
1263 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1266 if (!ADS_ERR_OK(status))
1269 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1271 LDAPMessage *res2 = NULL;
1272 LDAPMessage *msg, *next;
1274 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1275 attrs, args, &res2, &count, &cookie);
1276 if (!ADS_ERR_OK(status)) {
1280 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1281 that this works on all ldap libs, but I have only tested with openldap */
1282 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1283 next = ads_next_message(ads, msg);
1284 ldap_add_result_entry((LDAPMessage **)res, msg);
1286 /* note that we do not free res2, as the memory is now
1287 part of the main returned list */
1290 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1291 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1297 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1298 int scope, const char *expr,
1299 const char **attrs, LDAPMessage **res)
1301 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1304 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1305 int scope, const char *expr,
1306 const char **attrs, uint32_t sd_flags,
1311 args.control = ADS_SD_FLAGS_OID;
1312 args.val = sd_flags;
1313 args.critical = True;
1315 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1320 * Run a function on all results for a search. Uses ads_do_paged_search() and
1321 * runs the function as each page is returned, using ads_process_results()
1322 * @param ads connection to ads server
1323 * @param bind_path Base dn for the search
1324 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1325 * @param expr Search expression - specified in local charset
1326 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1327 * @param fn Function which takes attr name, values list, and data_area
1328 * @param data_area Pointer which is passed to function on each call
1329 * @return status of search
1331 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1332 int scope, const char *expr, const char **attrs,
1333 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1336 struct berval *cookie = NULL;
1341 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1344 if (!ADS_ERR_OK(status)) return status;
1346 ads_process_results(ads, res, fn, data_area);
1347 ads_msgfree(ads, res);
1350 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1351 &res, &count, &cookie);
1353 if (!ADS_ERR_OK(status)) break;
1355 ads_process_results(ads, res, fn, data_area);
1356 ads_msgfree(ads, res);
1363 * Do a search with a timeout.
1364 * @param ads connection to ads server
1365 * @param bind_path Base dn for the search
1366 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1367 * @param expr Search expression
1368 * @param attrs Attributes to retrieve
1369 * @param res ** which will contain results - free res* with ads_msgfree()
1370 * @return status of search
1372 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1374 const char **attrs, LDAPMessage **res)
1377 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1378 size_t converted_size;
1382 if (!(ctx = talloc_init("ads_do_search"))) {
1383 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1384 return ADS_ERROR(LDAP_NO_MEMORY);
1387 /* 0 means the conversion worked but the result was empty
1388 so we only fail if it's negative. In any case, it always
1389 at least nulls out the dest */
1390 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1391 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1393 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1394 rc = LDAP_NO_MEMORY;
1398 if (!attrs || !(*attrs))
1399 search_attrs = NULL;
1401 /* This would be the utf8-encoded version...*/
1402 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1403 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1405 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1406 rc = LDAP_NO_MEMORY;
1411 /* see the note in ads_do_paged_search - we *must* disable referrals */
1412 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1414 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1415 search_attrs, 0, NULL, NULL,
1417 (LDAPMessage **)res);
1419 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1420 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1425 talloc_destroy(ctx);
1426 /* if/when we decide to utf8-encode attrs, take out this next line */
1427 TALLOC_FREE(search_attrs);
1428 return ADS_ERROR(rc);
1431 * Do a general ADS search
1432 * @param ads connection to ads server
1433 * @param res ** which will contain results - free res* with ads_msgfree()
1434 * @param expr Search expression
1435 * @param attrs Attributes to retrieve
1436 * @return status of search
1438 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1439 const char *expr, const char **attrs)
1441 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1446 * Do a search on a specific DistinguishedName
1447 * @param ads connection to ads server
1448 * @param res ** which will contain results - free res* with ads_msgfree()
1449 * @param dn DistinguishName to search
1450 * @param attrs Attributes to retrieve
1451 * @return status of search
1453 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1454 const char *dn, const char **attrs)
1456 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1461 * Free up memory from a ads_search
1462 * @param ads connection to ads server
1463 * @param msg Search results to free
1465 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1472 * Get a dn from search results
1473 * @param ads connection to ads server
1474 * @param msg Search result
1477 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1479 char *utf8_dn, *unix_dn;
1480 size_t converted_size;
1482 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1485 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1489 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1490 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1494 ldap_memfree(utf8_dn);
1499 * Get the parent from a dn
1500 * @param dn the dn to return the parent from
1501 * @return parent dn string
1503 char *ads_parent_dn(const char *dn)
1511 p = strchr(dn, ',');
1521 * Find a machine account given a hostname
1522 * @param ads connection to ads server
1523 * @param res ** which will contain results - free res* with ads_msgfree()
1524 * @param host Hostname to search for
1525 * @return status of search
1527 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1528 const char *machine)
1532 const char *attrs[] = {
1533 /* This is how Windows checks for machine accounts */
1536 "userAccountControl",
1538 "ServicePrincipalName",
1539 "userPrincipalName",
1542 /* Additional attributes Samba checks */
1543 "msDS-AdditionalDnsHostName",
1544 "msDS-SupportedEncryptionTypes",
1545 "nTSecurityDescriptor",
1550 TALLOC_CTX *frame = talloc_stackframe();
1554 /* the easiest way to find a machine account anywhere in the tree
1555 is to look for hostname$ */
1556 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1558 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1562 status = ads_search(ads, res, expr, attrs);
1563 if (ADS_ERR_OK(status)) {
1564 if (ads_count_replies(ads, *res) != 1) {
1565 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1575 * Initialize a list of mods to be used in a modify request
1576 * @param ctx An initialized TALLOC_CTX
1577 * @return allocated ADS_MODLIST
1579 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1581 #define ADS_MODLIST_ALLOC_SIZE 10
1584 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1585 /* -1 is safety to make sure we don't go over the end.
1586 need to reset it to NULL before doing ldap modify */
1587 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1589 return (ADS_MODLIST)mods;
1594 add an attribute to the list, with values list already constructed
1596 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1597 int mod_op, const char *name,
1598 const void *_invals)
1601 LDAPMod **modlist = (LDAPMod **) *mods;
1602 struct berval **ber_values = NULL;
1603 char **char_values = NULL;
1606 mod_op = LDAP_MOD_DELETE;
1608 if (mod_op & LDAP_MOD_BVALUES) {
1609 const struct berval **b;
1610 b = discard_const_p(const struct berval *, _invals);
1611 ber_values = ads_dup_values(ctx, b);
1614 c = discard_const_p(const char *, _invals);
1615 char_values = ads_push_strvals(ctx, c);
1619 /* find the first empty slot */
1620 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1622 if (modlist[curmod] == (LDAPMod *) -1) {
1623 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1624 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1625 return ADS_ERROR(LDAP_NO_MEMORY);
1626 memset(&modlist[curmod], 0,
1627 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1628 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1629 *mods = (ADS_MODLIST)modlist;
1632 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1633 return ADS_ERROR(LDAP_NO_MEMORY);
1634 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1635 if (mod_op & LDAP_MOD_BVALUES) {
1636 modlist[curmod]->mod_bvalues = ber_values;
1637 } else if (mod_op & LDAP_MOD_DELETE) {
1638 modlist[curmod]->mod_values = NULL;
1640 modlist[curmod]->mod_values = char_values;
1643 modlist[curmod]->mod_op = mod_op;
1644 return ADS_ERROR(LDAP_SUCCESS);
1648 * Add a single string value to a mod list
1649 * @param ctx An initialized TALLOC_CTX
1650 * @param mods An initialized ADS_MODLIST
1651 * @param name The attribute name to add
1652 * @param val The value to add - NULL means DELETE
1653 * @return ADS STATUS indicating success of add
1655 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1656 const char *name, const char *val)
1658 const char *values[2];
1664 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1665 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1669 * Add an array of string values to a mod list
1670 * @param ctx An initialized TALLOC_CTX
1671 * @param mods An initialized ADS_MODLIST
1672 * @param name The attribute name to add
1673 * @param vals The array of string values to add - NULL means DELETE
1674 * @return ADS STATUS indicating success of add
1676 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1677 const char *name, const char **vals)
1680 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1681 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1682 name, (const void **) vals);
1686 * Add a single ber-encoded value to a mod list
1687 * @param ctx An initialized TALLOC_CTX
1688 * @param mods An initialized ADS_MODLIST
1689 * @param name The attribute name to add
1690 * @param val The value to add - NULL means DELETE
1691 * @return ADS STATUS indicating success of add
1693 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1694 const char *name, const struct berval *val)
1696 const struct berval *values[2];
1701 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1702 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1703 name, (const void **) values);
1706 static void ads_print_error(int ret, LDAP *ld)
1709 char *ld_error = NULL;
1710 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1711 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1713 ldap_err2string(ret),
1715 SAFE_FREE(ld_error);
1720 * Perform an ldap modify
1721 * @param ads connection to ads server
1722 * @param mod_dn DistinguishedName to modify
1723 * @param mods list of modifications to perform
1724 * @return status of modify
1726 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1729 char *utf8_dn = NULL;
1730 size_t converted_size;
1732 this control is needed to modify that contains a currently
1733 non-existent attribute (but allowable for the object) to run
1735 LDAPControl PermitModify = {
1736 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1739 LDAPControl *controls[2];
1741 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1743 controls[0] = &PermitModify;
1746 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1747 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1750 /* find the end of the list, marked by NULL or -1 */
1751 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1752 /* make sure the end of the list is NULL */
1754 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1755 (LDAPMod **) mods, controls, NULL);
1756 ads_print_error(ret, ads->ldap.ld);
1757 TALLOC_FREE(utf8_dn);
1758 return ADS_ERROR(ret);
1762 * Perform an ldap add
1763 * @param ads connection to ads server
1764 * @param new_dn DistinguishedName to add
1765 * @param mods list of attributes and values for DN
1766 * @return status of add
1768 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1771 char *utf8_dn = NULL;
1772 size_t converted_size;
1774 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1776 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1777 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1778 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1781 /* find the end of the list, marked by NULL or -1 */
1782 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1783 /* make sure the end of the list is NULL */
1786 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1787 ads_print_error(ret, ads->ldap.ld);
1788 TALLOC_FREE(utf8_dn);
1789 return ADS_ERROR(ret);
1793 * Delete a DistinguishedName
1794 * @param ads connection to ads server
1795 * @param new_dn DistinguishedName to delete
1796 * @return status of delete
1798 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1801 char *utf8_dn = NULL;
1802 size_t converted_size;
1803 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1804 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1805 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1808 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1810 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1811 ads_print_error(ret, ads->ldap.ld);
1812 TALLOC_FREE(utf8_dn);
1813 return ADS_ERROR(ret);
1817 * Build an org unit string
1818 * if org unit is Computers or blank then assume a container, otherwise
1819 * assume a / separated list of organisational units.
1820 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1821 * @param ads connection to ads server
1822 * @param org_unit Organizational unit
1823 * @return org unit string - caller must free
1825 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1831 if (!org_unit || !*org_unit) {
1833 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1835 /* samba4 might not yet respond to a wellknownobject-query */
1836 return ret ? ret : SMB_STRDUP("cn=Computers");
1839 if (strequal(org_unit, "Computers")) {
1840 return SMB_STRDUP("cn=Computers");
1843 /* jmcd: removed "\\" from the separation chars, because it is
1844 needed as an escape for chars like '#' which are valid in an
1846 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
1847 if (!ADS_ERR_OK(status)) {
1855 * Get a org unit string for a well-known GUID
1856 * @param ads connection to ads server
1857 * @param wknguid Well known GUID
1858 * @return org unit string - caller must free
1860 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1863 LDAPMessage *res = NULL;
1864 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1865 **bind_dn_exp = NULL;
1866 const char *attrs[] = {"distinguishedName", NULL};
1867 int new_ln, wkn_ln, bind_ln, i;
1869 if (wknguid == NULL) {
1873 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1874 DEBUG(1, ("asprintf failed!\n"));
1878 status = ads_search_dn(ads, &res, base, attrs);
1879 if (!ADS_ERR_OK(status)) {
1880 DEBUG(1,("Failed while searching for: %s\n", base));
1884 if (ads_count_replies(ads, res) != 1) {
1888 /* substitute the bind-path from the well-known-guid-search result */
1889 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1894 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1899 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1904 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1906 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1909 new_ln = wkn_ln - bind_ln;
1911 ret = SMB_STRDUP(wkn_dn_exp[0]);
1916 for (i=1; i < new_ln; i++) {
1919 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1925 ret = SMB_STRDUP(s);
1934 ads_msgfree(ads, res);
1935 TALLOC_FREE(wkn_dn);
1937 ldap_value_free(wkn_dn_exp);
1940 ldap_value_free(bind_dn_exp);
1947 * Adds (appends) an item to an attribute array, rather then
1948 * replacing the whole list
1949 * @param ctx An initialized TALLOC_CTX
1950 * @param mods An initialized ADS_MODLIST
1951 * @param name name of the ldap attribute to append to
1952 * @param vals an array of values to add
1953 * @return status of addition
1956 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1957 const char *name, const char **vals)
1959 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1960 (const void *) vals);
1964 * Determines the an account's current KVNO via an LDAP lookup
1965 * @param ads An initialized ADS_STRUCT
1966 * @param account_name the NT samaccountname.
1967 * @return the kvno for the account, or -1 in case of a failure.
1970 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1972 LDAPMessage *res = NULL;
1973 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1975 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1976 char *dn_string = NULL;
1979 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1980 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1983 ret = ads_search(ads, &res, filter, attrs);
1985 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1986 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1987 ads_msgfree(ads, res);
1991 dn_string = ads_get_dn(ads, talloc_tos(), res);
1993 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1994 ads_msgfree(ads, res);
1997 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1998 TALLOC_FREE(dn_string);
2000 /* ---------------------------------------------------------
2001 * 0 is returned as a default KVNO from this point on...
2002 * This is done because Windows 2000 does not support key
2003 * version numbers. Chances are that a failure in the next
2004 * step is simply due to Windows 2000 being used for a
2005 * domain controller. */
2008 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2009 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2010 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2011 ads_msgfree(ads, res);
2016 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2017 ads_msgfree(ads, res);
2022 * Determines the computer account's current KVNO via an LDAP lookup
2023 * @param ads An initialized ADS_STRUCT
2024 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2025 * @return the kvno for the computer account, or -1 in case of a failure.
2028 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2030 char *computer_account = NULL;
2033 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2037 kvno = ads_get_kvno(ads, computer_account);
2038 free(computer_account);
2044 * This clears out all registered spn's for a given hostname
2045 * @param ads An initilaized ADS_STRUCT
2046 * @param machine_name the NetBIOS name of the computer.
2047 * @return 0 upon success, non-zero otherwise.
2050 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2053 LDAPMessage *res = NULL;
2055 const char *servicePrincipalName[1] = {NULL};
2057 char *dn_string = NULL;
2059 ret = ads_find_machine_acct(ads, &res, machine_name);
2060 if (!ADS_ERR_OK(ret)) {
2061 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2062 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2063 ads_msgfree(ads, res);
2067 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2068 ctx = talloc_init("ads_clear_service_principal_names");
2070 ads_msgfree(ads, res);
2071 return ADS_ERROR(LDAP_NO_MEMORY);
2074 if (!(mods = ads_init_mods(ctx))) {
2075 talloc_destroy(ctx);
2076 ads_msgfree(ads, res);
2077 return ADS_ERROR(LDAP_NO_MEMORY);
2079 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2080 if (!ADS_ERR_OK(ret)) {
2081 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2082 ads_msgfree(ads, res);
2083 talloc_destroy(ctx);
2086 dn_string = ads_get_dn(ads, talloc_tos(), res);
2088 talloc_destroy(ctx);
2089 ads_msgfree(ads, res);
2090 return ADS_ERROR(LDAP_NO_MEMORY);
2092 ret = ads_gen_mod(ads, dn_string, mods);
2093 TALLOC_FREE(dn_string);
2094 if (!ADS_ERR_OK(ret)) {
2095 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2097 ads_msgfree(ads, res);
2098 talloc_destroy(ctx);
2102 ads_msgfree(ads, res);
2103 talloc_destroy(ctx);
2108 * @brief Search for an element in a string array.
2110 * @param[in] el_array The string array to search.
2112 * @param[in] num_el The number of elements in the string array.
2114 * @param[in] el The string to search.
2116 * @return True if found, false if not.
2118 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2122 if (el_array == NULL || num_el == 0 || el == NULL) {
2126 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2129 cmp = strcasecmp_m(el_array[i], el);
2139 * @brief This gets the service principal names of an existing computer account.
2141 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2143 * @param[in] ads The ADS context to use.
2145 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2146 * identify the computer account.
2148 * @param[in] spn_array A pointer to store the array for SPNs.
2150 * @param[in] num_spns The number of principals stored in the array.
2152 * @return 0 on success, or a ADS error if a failure occurred.
2154 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2156 const char *machine_name,
2161 LDAPMessage *res = NULL;
2164 status = ads_find_machine_acct(ads,
2167 if (!ADS_ERR_OK(status)) {
2168 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2173 count = ads_count_replies(ads, res);
2175 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2179 *spn_array = ads_pull_strings(ads,
2182 "servicePrincipalName",
2184 if (*spn_array == NULL) {
2185 DEBUG(1, ("Host account for %s does not have service principal "
2188 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2193 ads_msgfree(ads, res);
2199 * This adds a service principal name to an existing computer account
2200 * (found by hostname) in AD.
2201 * @param ads An initialized ADS_STRUCT
2202 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2203 * @param spns An array or strings for the service principals to add,
2204 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2205 * @return 0 upon sucess, or non-zero if a failure occurs
2208 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2209 const char *machine_name,
2214 LDAPMessage *res = NULL;
2216 char *dn_string = NULL;
2217 const char **servicePrincipalName = spns;
2219 ret = ads_find_machine_acct(ads, &res, machine_name);
2220 if (!ADS_ERR_OK(ret)) {
2221 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2223 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2224 ads_msgfree(ads, res);
2228 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2229 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2230 ads_msgfree(ads, res);
2231 return ADS_ERROR(LDAP_NO_MEMORY);
2234 DEBUG(5,("ads_add_service_principal_name: INFO: "
2235 "Adding %s to host %s\n",
2236 spns[0] ? "N/A" : spns[0], machine_name));
2239 DEBUG(5,("ads_add_service_principal_name: INFO: "
2240 "Adding %s to host %s\n",
2241 spns[1] ? "N/A" : spns[1], machine_name));
2243 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2244 ret = ADS_ERROR(LDAP_NO_MEMORY);
2248 ret = ads_add_strlist(ctx,
2250 "servicePrincipalName",
2251 servicePrincipalName);
2252 if (!ADS_ERR_OK(ret)) {
2253 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2257 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2258 ret = ADS_ERROR(LDAP_NO_MEMORY);
2262 ret = ads_gen_mod(ads, dn_string, mods);
2263 if (!ADS_ERR_OK(ret)) {
2264 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2270 ads_msgfree(ads, res);
2274 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2277 uint32_t acct_ctrl = 0;
2280 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2288 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2290 const struct berval *machine_pw_val)
2294 TALLOC_CTX *frame = talloc_stackframe();
2295 uint32_t acct_control;
2296 char *control_str = NULL;
2297 const char *attrs[] = {
2301 LDAPMessage *res = NULL;
2304 dn = ads_get_dn(ads, frame, msg);
2306 ret = ADS_ERROR(LDAP_NO_MEMORY);
2310 acct_control = ads_get_acct_ctrl(ads, msg);
2311 if (acct_control == 0) {
2312 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2317 * Changing the password, disables the account. So we need to change the
2318 * userAccountControl flags to enable it again.
2320 mods = ads_init_mods(frame);
2322 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2326 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2328 ret = ads_gen_mod(ads, dn, mods);
2329 if (!ADS_ERR_OK(ret)) {
2335 * To activate the account, we need to disable and enable it.
2337 acct_control |= UF_ACCOUNTDISABLE;
2339 control_str = talloc_asprintf(frame, "%u", acct_control);
2340 if (control_str == NULL) {
2341 ret = ADS_ERROR(LDAP_NO_MEMORY);
2345 mods = ads_init_mods(frame);
2347 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2351 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2353 ret = ads_gen_mod(ads, dn, mods);
2354 if (!ADS_ERR_OK(ret)) {
2358 TALLOC_FREE(control_str);
2361 * Enable the account again.
2363 acct_control &= ~UF_ACCOUNTDISABLE;
2365 control_str = talloc_asprintf(frame, "%u", acct_control);
2366 if (control_str == NULL) {
2367 ret = ADS_ERROR(LDAP_NO_MEMORY);
2371 mods = ads_init_mods(frame);
2373 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2377 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2379 ret = ads_gen_mod(ads, dn, mods);
2380 if (!ADS_ERR_OK(ret)) {
2384 TALLOC_FREE(control_str);
2386 ret = ads_search_dn(ads, &res, dn, attrs);
2387 ads_msgfree(ads, res);
2396 * adds a machine account to the ADS server
2397 * @param ads An intialized ADS_STRUCT
2398 * @param machine_name - the NetBIOS machine name of this account.
2399 * @param account_type A number indicating the type of account to create
2400 * @param org_unit The LDAP path in which to place this account
2401 * @return 0 upon success, or non-zero otherwise
2404 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2405 const char *machine_name,
2406 const char *machine_password,
2407 const char *org_unit,
2408 uint32_t etype_list,
2409 const char *dns_domain_name)
2412 char *samAccountName = NULL;
2413 char *controlstr = NULL;
2414 TALLOC_CTX *ctx = NULL;
2416 char *machine_escaped = NULL;
2417 char *dns_hostname = NULL;
2418 char *new_dn = NULL;
2419 char *utf8_pw = NULL;
2420 size_t utf8_pw_len = 0;
2421 char *utf16_pw = NULL;
2422 size_t utf16_pw_len = 0;
2423 struct berval machine_pw_val;
2425 const char **spn_array = NULL;
2426 size_t num_spns = 0;
2427 const char *spn_prefix[] = {
2429 "RestrictedKrbHost",
2432 LDAPMessage *res = NULL;
2433 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2435 ctx = talloc_init("ads_add_machine_acct");
2437 return ADS_ERROR(LDAP_NO_MEMORY);
2440 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2441 if (machine_escaped == NULL) {
2442 ret = ADS_ERROR(LDAP_NO_MEMORY);
2446 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2447 if (utf8_pw == NULL) {
2448 ret = ADS_ERROR(LDAP_NO_MEMORY);
2451 utf8_pw_len = strlen(utf8_pw);
2453 ok = convert_string_talloc(ctx,
2454 CH_UTF8, CH_UTF16MUNGED,
2455 utf8_pw, utf8_pw_len,
2456 (void *)&utf16_pw, &utf16_pw_len);
2458 ret = ADS_ERROR(LDAP_NO_MEMORY);
2462 machine_pw_val = (struct berval) {
2464 .bv_len = utf16_pw_len,
2467 /* Check if the machine account already exists. */
2468 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2469 if (ADS_ERR_OK(ret)) {
2470 /* Change the machine account password */
2471 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2472 ads_msgfree(ads, res);
2476 ads_msgfree(ads, res);
2478 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2479 if (new_dn == NULL) {
2480 ret = ADS_ERROR(LDAP_NO_MEMORY);
2484 /* Create machine account */
2486 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2487 if (samAccountName == NULL) {
2488 ret = ADS_ERROR(LDAP_NO_MEMORY);
2492 dns_hostname = talloc_asprintf(ctx,
2496 if (dns_hostname == NULL) {
2497 ret = ADS_ERROR(LDAP_NO_MEMORY);
2501 /* Add dns_hostname SPNs */
2502 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2503 char *spn = talloc_asprintf(ctx,
2508 ret = ADS_ERROR(LDAP_NO_MEMORY);
2512 ok = add_string_to_array(spn_array,
2517 ret = ADS_ERROR(LDAP_NO_MEMORY);
2522 /* Add machine_name SPNs */
2523 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2524 char *spn = talloc_asprintf(ctx,
2529 ret = ADS_ERROR(LDAP_NO_MEMORY);
2533 ok = add_string_to_array(spn_array,
2538 ret = ADS_ERROR(LDAP_NO_MEMORY);
2543 /* Make sure to NULL terminate the array */
2544 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2545 if (spn_array == NULL) {
2546 ret = ADS_ERROR(LDAP_NO_MEMORY);
2549 spn_array[num_spns] = NULL;
2551 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2552 if (controlstr == NULL) {
2553 ret = ADS_ERROR(LDAP_NO_MEMORY);
2557 mods = ads_init_mods(ctx);
2559 ret = ADS_ERROR(LDAP_NO_MEMORY);
2563 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2564 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2565 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2566 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2567 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2568 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2570 ret = ads_gen_add(ads, new_dn, mods);
2573 SAFE_FREE(machine_escaped);
2574 talloc_destroy(ctx);
2580 * move a machine account to another OU on the ADS server
2581 * @param ads - An intialized ADS_STRUCT
2582 * @param machine_name - the NetBIOS machine name of this account.
2583 * @param org_unit - The LDAP path in which to place this account
2584 * @param moved - whether we moved the machine account (optional)
2585 * @return 0 upon success, or non-zero otherwise
2588 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2589 const char *org_unit, bool *moved)
2593 LDAPMessage *res = NULL;
2594 char *filter = NULL;
2595 char *computer_dn = NULL;
2597 char *computer_rdn = NULL;
2598 bool need_move = False;
2600 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2601 rc = ADS_ERROR(LDAP_NO_MEMORY);
2605 /* Find pre-existing machine */
2606 rc = ads_search(ads, &res, filter, NULL);
2607 if (!ADS_ERR_OK(rc)) {
2611 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2613 rc = ADS_ERROR(LDAP_NO_MEMORY);
2617 parent_dn = ads_parent_dn(computer_dn);
2618 if (strequal(parent_dn, org_unit)) {
2624 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2625 rc = ADS_ERROR(LDAP_NO_MEMORY);
2629 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2630 org_unit, 1, NULL, NULL);
2631 rc = ADS_ERROR(ldap_status);
2634 ads_msgfree(ads, res);
2636 TALLOC_FREE(computer_dn);
2637 SAFE_FREE(computer_rdn);
2639 if (!ADS_ERR_OK(rc)) {
2651 dump a binary result from ldap
2653 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2656 for (i=0; values[i]; i++) {
2658 printf("%s: ", field);
2659 for (j=0; j<values[i]->bv_len; j++) {
2660 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2666 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2669 for (i=0; values[i]; i++) {
2671 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2674 status = GUID_from_ndr_blob(&in, &guid);
2675 if (NT_STATUS_IS_OK(status)) {
2676 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2678 printf("%s: INVALID GUID\n", field);
2684 dump a sid result from ldap
2686 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2689 for (i=0; values[i]; i++) {
2692 struct dom_sid_buf tmp;
2693 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2694 values[i]->bv_len, &sid);
2698 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2703 dump ntSecurityDescriptor
2705 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2707 TALLOC_CTX *frame = talloc_stackframe();
2708 struct security_descriptor *psd;
2711 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2712 values[0]->bv_len, &psd);
2713 if (!NT_STATUS_IS_OK(status)) {
2714 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2715 nt_errstr(status)));
2721 ads_disp_sd(ads, talloc_tos(), psd);
2728 dump a string result from ldap
2730 static void dump_string(const char *field, char **values)
2733 for (i=0; values[i]; i++) {
2734 printf("%s: %s\n", field, values[i]);
2739 dump a field from LDAP on stdout
2743 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2748 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2750 {"objectGUID", False, dump_guid},
2751 {"netbootGUID", False, dump_guid},
2752 {"nTSecurityDescriptor", False, dump_sd},
2753 {"dnsRecord", False, dump_binary},
2754 {"objectSid", False, dump_sid},
2755 {"tokenGroups", False, dump_sid},
2756 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2757 {"tokengroupsGlobalandUniversal", False, dump_sid},
2758 {"mS-DS-CreatorSID", False, dump_sid},
2759 {"msExchMailboxGuid", False, dump_guid},
2764 if (!field) { /* must be end of an entry */
2769 for (i=0; handlers[i].name; i++) {
2770 if (strcasecmp_m(handlers[i].name, field) == 0) {
2771 if (!values) /* first time, indicate string or not */
2772 return handlers[i].string;
2773 handlers[i].handler(ads, field, (struct berval **) values);
2777 if (!handlers[i].name) {
2778 if (!values) /* first time, indicate string conversion */
2780 dump_string(field, (char **)values);
2786 * Dump a result from LDAP on stdout
2787 * used for debugging
2788 * @param ads connection to ads server
2789 * @param res Results to dump
2792 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2794 ads_process_results(ads, res, ads_dump_field, NULL);
2798 * Walk through results, calling a function for each entry found.
2799 * The function receives a field name, a berval * array of values,
2800 * and a data area passed through from the start. The function is
2801 * called once with null for field and values at the end of each
2803 * @param ads connection to ads server
2804 * @param res Results to process
2805 * @param fn Function for processing each result
2806 * @param data_area user-defined area to pass to function
2808 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2809 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2814 size_t converted_size;
2816 if (!(ctx = talloc_init("ads_process_results")))
2819 for (msg = ads_first_entry(ads, res); msg;
2820 msg = ads_next_entry(ads, msg)) {
2824 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2825 (LDAPMessage *)msg,&b);
2827 utf8_field=ldap_next_attribute(ads->ldap.ld,
2828 (LDAPMessage *)msg,b)) {
2829 struct berval **ber_vals;
2835 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2838 DEBUG(0,("ads_process_results: "
2839 "pull_utf8_talloc failed: %s",
2843 string = fn(ads, field, NULL, data_area);
2848 utf8_vals = ldap_get_values(ads->ldap.ld,
2849 (LDAPMessage *)msg, field);
2850 p = discard_const_p(const char *, utf8_vals);
2851 str_vals = ads_pull_strvals(ctx, p);
2852 fn(ads, field, (void **) str_vals, data_area);
2853 ldap_value_free(utf8_vals);
2855 ber_vals = ldap_get_values_len(ads->ldap.ld,
2856 (LDAPMessage *)msg, field);
2857 fn(ads, field, (void **) ber_vals, data_area);
2859 ldap_value_free_len(ber_vals);
2861 ldap_memfree(utf8_field);
2864 talloc_free_children(ctx);
2865 fn(ads, NULL, NULL, data_area); /* completed an entry */
2868 talloc_destroy(ctx);
2872 * count how many replies are in a LDAPMessage
2873 * @param ads connection to ads server
2874 * @param res Results to count
2875 * @return number of replies
2877 int ads_count_replies(ADS_STRUCT *ads, void *res)
2879 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2883 * pull the first entry from a ADS result
2884 * @param ads connection to ads server
2885 * @param res Results of search
2886 * @return first entry from result
2888 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2890 return ldap_first_entry(ads->ldap.ld, res);
2894 * pull the next entry from a ADS result
2895 * @param ads connection to ads server
2896 * @param res Results of search
2897 * @return next entry from result
2899 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2901 return ldap_next_entry(ads->ldap.ld, res);
2905 * pull the first message from a ADS result
2906 * @param ads connection to ads server
2907 * @param res Results of search
2908 * @return first message from result
2910 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2912 return ldap_first_message(ads->ldap.ld, res);
2916 * pull the next message from a ADS result
2917 * @param ads connection to ads server
2918 * @param res Results of search
2919 * @return next message from result
2921 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2923 return ldap_next_message(ads->ldap.ld, res);
2927 * pull a single string from a ADS result
2928 * @param ads connection to ads server
2929 * @param mem_ctx TALLOC_CTX to use for allocating result string
2930 * @param msg Results of search
2931 * @param field Attribute to retrieve
2932 * @return Result string in talloc context
2934 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2940 size_t converted_size;
2942 values = ldap_get_values(ads->ldap.ld, msg, field);
2946 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2951 ldap_value_free(values);
2956 * pull an array of strings from a ADS result
2957 * @param ads connection to ads server
2958 * @param mem_ctx TALLOC_CTX to use for allocating result string
2959 * @param msg Results of search
2960 * @param field Attribute to retrieve
2961 * @return Result strings in talloc context
2963 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2964 LDAPMessage *msg, const char *field,
2969 size_t i, converted_size;
2971 values = ldap_get_values(ads->ldap.ld, msg, field);
2975 *num_values = ldap_count_values(values);
2977 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2979 ldap_value_free(values);
2983 for (i=0;i<*num_values;i++) {
2984 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2987 ldap_value_free(values);
2993 ldap_value_free(values);
2998 * pull an array of strings from a ADS result
2999 * (handle large multivalue attributes with range retrieval)
3000 * @param ads connection to ads server
3001 * @param mem_ctx TALLOC_CTX to use for allocating result string
3002 * @param msg Results of search
3003 * @param field Attribute to retrieve
3004 * @param current_strings strings returned by a previous call to this function
3005 * @param next_attribute The next query should ask for this attribute
3006 * @param num_values How many values did we get this time?
3007 * @param more_values Are there more values to get?
3008 * @return Result strings in talloc context
3010 char **ads_pull_strings_range(ADS_STRUCT *ads,
3011 TALLOC_CTX *mem_ctx,
3012 LDAPMessage *msg, const char *field,
3013 char **current_strings,
3014 const char **next_attribute,
3015 size_t *num_strings,
3019 char *expected_range_attrib, *range_attr;
3020 BerElement *ptr = NULL;
3023 size_t num_new_strings;
3024 unsigned long int range_start;
3025 unsigned long int range_end;
3027 /* we might have been given the whole lot anyway */
3028 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3029 *more_strings = False;
3033 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3035 /* look for Range result */
3036 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3038 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3039 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3040 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3048 /* nothing here - this field is just empty */
3049 *more_strings = False;
3053 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3054 &range_start, &range_end) == 2) {
3055 *more_strings = True;
3057 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3058 &range_start) == 1) {
3059 *more_strings = False;
3061 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
3063 ldap_memfree(range_attr);
3064 *more_strings = False;
3069 if ((*num_strings) != range_start) {
3070 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3071 " - aborting range retreival\n",
3072 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3073 ldap_memfree(range_attr);
3074 *more_strings = False;
3078 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3080 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3081 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3082 "strings in this bunch, but we only got %lu - aborting range retreival\n",
3083 range_attr, (unsigned long int)range_end - range_start + 1,
3084 (unsigned long int)num_new_strings));
3085 ldap_memfree(range_attr);
3086 *more_strings = False;
3090 strings = talloc_realloc(mem_ctx, current_strings, char *,
3091 *num_strings + num_new_strings);
3093 if (strings == NULL) {
3094 ldap_memfree(range_attr);
3095 *more_strings = False;
3099 if (new_strings && num_new_strings) {
3100 memcpy(&strings[*num_strings], new_strings,
3101 sizeof(*new_strings) * num_new_strings);
3104 (*num_strings) += num_new_strings;
3106 if (*more_strings) {
3107 *next_attribute = talloc_asprintf(mem_ctx,
3112 if (!*next_attribute) {
3113 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3114 ldap_memfree(range_attr);
3115 *more_strings = False;
3120 ldap_memfree(range_attr);
3126 * pull a single uint32_t from a ADS result
3127 * @param ads connection to ads server
3128 * @param msg Results of search
3129 * @param field Attribute to retrieve
3130 * @param v Pointer to int to store result
3131 * @return boolean inidicating success
3133 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3138 values = ldap_get_values(ads->ldap.ld, msg, field);
3142 ldap_value_free(values);
3146 *v = atoi(values[0]);
3147 ldap_value_free(values);
3152 * pull a single objectGUID from an ADS result
3153 * @param ads connection to ADS server
3154 * @param msg results of search
3155 * @param guid 37-byte area to receive text guid
3156 * @return boolean indicating success
3158 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3163 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3168 status = GUID_from_ndr_blob(&blob, guid);
3169 talloc_free(blob.data);
3170 return NT_STATUS_IS_OK(status);
3175 * pull a single struct dom_sid from a ADS result
3176 * @param ads connection to ads server
3177 * @param msg Results of search
3178 * @param field Attribute to retrieve
3179 * @param sid Pointer to sid to store result
3180 * @return boolean inidicating success
3182 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3183 struct dom_sid *sid)
3185 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3189 * pull an array of struct dom_sids from a ADS result
3190 * @param ads connection to ads server
3191 * @param mem_ctx TALLOC_CTX for allocating sid array
3192 * @param msg Results of search
3193 * @param field Attribute to retrieve
3194 * @param sids pointer to sid array to allocate
3195 * @return the count of SIDs pulled
3197 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3198 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3200 struct berval **values;
3203 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3208 for (i=0; values[i]; i++)
3212 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3214 ldap_value_free_len(values);
3222 for (i=0; values[i]; i++) {
3224 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3225 values[i]->bv_len, &(*sids)[count]);
3227 struct dom_sid_buf buf;
3228 DBG_DEBUG("pulling SID: %s\n",
3229 dom_sid_str_buf(&(*sids)[count], &buf));
3234 ldap_value_free_len(values);
3239 * pull a struct security_descriptor from a ADS result
3240 * @param ads connection to ads server
3241 * @param mem_ctx TALLOC_CTX for allocating sid array
3242 * @param msg Results of search
3243 * @param field Attribute to retrieve
3244 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3245 * @return boolean inidicating success
3247 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3248 LDAPMessage *msg, const char *field,
3249 struct security_descriptor **sd)
3251 struct berval **values;
3254 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3256 if (!values) return false;
3260 status = unmarshall_sec_desc(mem_ctx,
3261 (uint8_t *)values[0]->bv_val,
3262 values[0]->bv_len, sd);
3263 if (!NT_STATUS_IS_OK(status)) {
3264 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3265 nt_errstr(status)));
3270 ldap_value_free_len(values);
3275 * in order to support usernames longer than 21 characters we need to
3276 * use both the sAMAccountName and the userPrincipalName attributes
3277 * It seems that not all users have the userPrincipalName attribute set
3279 * @param ads connection to ads server
3280 * @param mem_ctx TALLOC_CTX for allocating sid array
3281 * @param msg Results of search
3282 * @return the username
3284 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3290 /* lookup_name() only works on the sAMAccountName to
3291 returning the username portion of userPrincipalName
3292 breaks winbindd_getpwnam() */
3294 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3295 if (ret && (p = strchr_m(ret, '@'))) {
3300 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3305 * find the update serial number - this is the core of the ldap cache
3306 * @param ads connection to ads server
3307 * @param ads connection to ADS server
3308 * @param usn Pointer to retrieved update serial number
3309 * @return status of search
3311 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3313 const char *attrs[] = {"highestCommittedUSN", NULL};
3317 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3318 if (!ADS_ERR_OK(status))
3321 if (ads_count_replies(ads, res) != 1) {
3322 ads_msgfree(ads, res);
3323 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3326 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3327 ads_msgfree(ads, res);
3328 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3331 ads_msgfree(ads, res);
3335 /* parse a ADS timestring - typical string is
3336 '20020917091222.0Z0' which means 09:12.22 17th September
3338 static time_t ads_parse_time(const char *str)
3344 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3345 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3346 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3355 /********************************************************************
3356 ********************************************************************/
3358 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3360 const char *attrs[] = {"currentTime", NULL};
3364 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3365 ADS_STRUCT *ads_s = ads;
3367 /* establish a new ldap tcp session if necessary */
3369 if ( !ads->ldap.ld ) {
3371 * ADS_STRUCT may be being reused after a
3372 * DC lookup, so ads->ldap.ss may already have a
3373 * good address. If not, re-initialize the passed-in
3374 * ADS_STRUCT with the given server.XXXX parameters.
3376 * Note that this doesn't depend on
3377 * ads->server.ldap_server != NULL,
3378 * as the case where ads->server.ldap_server==NULL and
3379 * ads->ldap.ss != zero_address is precisely the DC
3380 * lookup case where ads->ldap.ss was found by going
3381 * through ads_find_dc() again we want to avoid repeating.
3383 if (is_zero_addr(&ads->ldap.ss)) {
3384 ads_s = ads_init(tmp_ctx,
3386 ads->server.workgroup,
3387 ads->server.ldap_server,
3389 if (ads_s == NULL) {
3390 status = ADS_ERROR(LDAP_NO_MEMORY);
3396 * Reset ads->config.flags as it can contain the flags
3397 * returned by the previous CLDAP ping when reusing the struct.
3399 ads_s->config.flags = 0;
3401 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3402 status = ads_connect( ads_s );
3403 if ( !ADS_ERR_OK(status))
3407 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3408 if (!ADS_ERR_OK(status)) {
3412 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3414 ads_msgfree(ads_s, res);
3415 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3419 /* but save the time and offset in the original ADS_STRUCT */
3421 ads->config.current_time = ads_parse_time(timestr);
3423 if (ads->config.current_time != 0) {
3424 ads->auth.time_offset = ads->config.current_time - time(NULL);
3425 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3428 ads_msgfree(ads, res);
3430 status = ADS_SUCCESS;
3433 TALLOC_FREE(tmp_ctx);
3438 /********************************************************************
3439 ********************************************************************/
3441 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3443 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3444 const char *attrs[] = {"domainFunctionality", NULL};
3447 ADS_STRUCT *ads_s = ads;
3449 *val = DS_DOMAIN_FUNCTION_2000;
3451 /* establish a new ldap tcp session if necessary */
3453 if ( !ads->ldap.ld ) {
3455 * ADS_STRUCT may be being reused after a
3456 * DC lookup, so ads->ldap.ss may already have a
3457 * good address. If not, re-initialize the passed-in
3458 * ADS_STRUCT with the given server.XXXX parameters.
3460 * Note that this doesn't depend on
3461 * ads->server.ldap_server != NULL,
3462 * as the case where ads->server.ldap_server==NULL and
3463 * ads->ldap.ss != zero_address is precisely the DC
3464 * lookup case where ads->ldap.ss was found by going
3465 * through ads_find_dc() again we want to avoid repeating.
3467 if (is_zero_addr(&ads->ldap.ss)) {
3468 ads_s = ads_init(tmp_ctx,
3470 ads->server.workgroup,
3471 ads->server.ldap_server,
3473 if (ads_s == NULL ) {
3474 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3480 * Reset ads->config.flags as it can contain the flags
3481 * returned by the previous CLDAP ping when reusing the struct.
3483 ads_s->config.flags = 0;
3485 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3486 status = ads_connect( ads_s );
3487 if ( !ADS_ERR_OK(status))
3491 /* If the attribute does not exist assume it is a Windows 2000
3492 functional domain */
3494 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3495 if (!ADS_ERR_OK(status)) {
3496 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3497 status = ADS_SUCCESS;
3502 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3503 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3505 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3508 ads_msgfree(ads_s, res);
3511 TALLOC_FREE(tmp_ctx);
3517 * find the domain sid for our domain
3518 * @param ads connection to ads server
3519 * @param sid Pointer to domain sid
3520 * @return status of search
3522 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3524 const char *attrs[] = {"objectSid", NULL};
3528 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3530 if (!ADS_ERR_OK(rc)) return rc;
3531 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3532 ads_msgfree(ads, res);
3533 return ADS_ERROR_SYSTEM(ENOENT);
3535 ads_msgfree(ads, res);
3541 * find our site name
3542 * @param ads connection to ads server
3543 * @param mem_ctx Pointer to talloc context
3544 * @param site_name Pointer to the sitename
3545 * @return status of search
3547 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3551 const char *dn, *service_name;
3552 const char *attrs[] = { "dsServiceName", NULL };
3554 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3555 if (!ADS_ERR_OK(status)) {
3559 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3560 if (service_name == NULL) {
3561 ads_msgfree(ads, res);
3562 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3565 ads_msgfree(ads, res);
3567 /* go up three levels */
3568 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3570 return ADS_ERROR(LDAP_NO_MEMORY);
3573 *site_name = talloc_strdup(mem_ctx, dn);
3574 if (*site_name == NULL) {
3575 return ADS_ERROR(LDAP_NO_MEMORY);
3580 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3585 * find the site dn where a machine resides
3586 * @param ads connection to ads server
3587 * @param mem_ctx Pointer to talloc context
3588 * @param computer_name name of the machine
3589 * @param site_name Pointer to the sitename
3590 * @return status of search
3592 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3596 const char *parent, *filter;
3597 char *config_context = NULL;
3600 /* shortcut a query */
3601 if (strequal(computer_name, ads->config.ldap_server_name)) {
3602 return ads_site_dn(ads, mem_ctx, site_dn);
3605 status = ads_config_path(ads, mem_ctx, &config_context);
3606 if (!ADS_ERR_OK(status)) {
3610 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3611 if (filter == NULL) {
3612 return ADS_ERROR(LDAP_NO_MEMORY);
3615 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3616 filter, NULL, &res);
3617 if (!ADS_ERR_OK(status)) {
3621 if (ads_count_replies(ads, res) != 1) {
3622 ads_msgfree(ads, res);
3623 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3626 dn = ads_get_dn(ads, mem_ctx, res);
3628 ads_msgfree(ads, res);
3629 return ADS_ERROR(LDAP_NO_MEMORY);
3632 /* go up three levels */
3633 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3634 if (parent == NULL) {
3635 ads_msgfree(ads, res);
3637 return ADS_ERROR(LDAP_NO_MEMORY);
3640 *site_dn = talloc_strdup(mem_ctx, parent);
3641 if (*site_dn == NULL) {
3642 ads_msgfree(ads, res);
3644 return ADS_ERROR(LDAP_NO_MEMORY);
3648 ads_msgfree(ads, res);
3654 * get the upn suffixes for a domain
3655 * @param ads connection to ads server
3656 * @param mem_ctx Pointer to talloc context
3657 * @param suffixes Pointer to an array of suffixes
3658 * @param num_suffixes Pointer to the number of suffixes
3659 * @return status of search
3661 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3666 char *config_context = NULL;
3667 const char *attrs[] = { "uPNSuffixes", NULL };
3669 status = ads_config_path(ads, mem_ctx, &config_context);
3670 if (!ADS_ERR_OK(status)) {
3674 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3676 return ADS_ERROR(LDAP_NO_MEMORY);
3679 status = ads_search_dn(ads, &res, base, attrs);
3680 if (!ADS_ERR_OK(status)) {
3684 if (ads_count_replies(ads, res) != 1) {
3685 ads_msgfree(ads, res);
3686 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3689 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3690 if ((*suffixes) == NULL) {
3691 ads_msgfree(ads, res);
3692 return ADS_ERROR(LDAP_NO_MEMORY);
3695 ads_msgfree(ads, res);
3701 * get the joinable ous for a domain
3702 * @param ads connection to ads server
3703 * @param mem_ctx Pointer to talloc context
3704 * @param ous Pointer to an array of ous
3705 * @param num_ous Pointer to the number of ous
3706 * @return status of search
3708 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3709 TALLOC_CTX *mem_ctx,
3714 LDAPMessage *res = NULL;
3715 LDAPMessage *msg = NULL;
3716 const char *attrs[] = { "dn", NULL };
3719 status = ads_search(ads, &res,
3720 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3722 if (!ADS_ERR_OK(status)) {
3726 count = ads_count_replies(ads, res);
3728 ads_msgfree(ads, res);
3729 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3732 for (msg = ads_first_entry(ads, res); msg;
3733 msg = ads_next_entry(ads, msg)) {
3734 const char **p = discard_const_p(const char *, *ous);
3737 dn = ads_get_dn(ads, talloc_tos(), msg);
3739 ads_msgfree(ads, res);
3740 return ADS_ERROR(LDAP_NO_MEMORY);
3743 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3745 ads_msgfree(ads, res);
3746 return ADS_ERROR(LDAP_NO_MEMORY);
3750 *ous = discard_const_p(char *, p);
3753 ads_msgfree(ads, res);
3760 * pull a struct dom_sid from an extended dn string
3761 * @param mem_ctx TALLOC_CTX
3762 * @param extended_dn string
3763 * @param flags string type of extended_dn
3764 * @param sid pointer to a struct dom_sid
3765 * @return NT_STATUS_OK on success,
3766 * NT_INVALID_PARAMETER on error,
3767 * NT_STATUS_NOT_FOUND if no SID present
3769 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3770 const char *extended_dn,
3771 enum ads_extended_dn_flags flags,
3772 struct dom_sid *sid)
3777 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3780 /* otherwise extended_dn gets stripped off */
3781 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3782 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3785 * ADS_EXTENDED_DN_HEX_STRING:
3786 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3788 * ADS_EXTENDED_DN_STRING (only with w2k3):
3789 * <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
3791 * Object with no SID, such as an Exchange Public Folder
3792 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3795 p = strchr(dn, ';');
3797 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3800 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3801 DEBUG(5,("No SID present in extended dn\n"));
3802 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3805 p += strlen(";<SID=");
3809 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3814 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3818 case ADS_EXTENDED_DN_STRING:
3819 if (!string_to_sid(sid, p)) {
3820 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3823 case ADS_EXTENDED_DN_HEX_STRING: {
3828 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3830 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3833 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3835 DEBUG(10,("failed to parse sid\n"));
3836 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3841 DEBUG(10,("unknown extended dn format\n"));
3842 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3845 return ADS_ERROR_NT(NT_STATUS_OK);
3848 /********************************************************************
3849 ********************************************************************/
3851 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3853 LDAPMessage *res = NULL;
3858 status = ads_find_machine_acct(ads, &res, machine_name);
3859 if (!ADS_ERR_OK(status)) {
3860 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3861 lp_netbios_name()));
3865 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3866 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3870 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3871 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3875 ads_msgfree(ads, res);
3880 /********************************************************************
3881 ********************************************************************/
3883 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3884 LDAPMessage *msg, size_t *num_values)
3886 const char *field = "msDS-AdditionalDnsHostName";
3887 struct berval **values = NULL;
3889 size_t i, converted_size;
3892 * Windows DC implicitly adds a short name for each FQDN added to
3893 * msDS-AdditionalDnsHostName, but it comes with a strage binary
3894 * suffix "\0$" which we should ignore (see bug #14406).
3897 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3898 if (values == NULL) {
3902 *num_values = ldap_count_values_len(values);
3904 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3906 ldap_value_free_len(values);
3910 for (i = 0; i < *num_values; i++) {
3912 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
3914 strnlen(values[i]->bv_val,
3916 &ret[i], &converted_size)) {
3917 ldap_value_free_len(values);
3923 ldap_value_free_len(values);
3927 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
3929 const char *machine_name,
3930 char ***hostnames_array,
3931 size_t *num_hostnames)
3934 LDAPMessage *res = NULL;
3937 status = ads_find_machine_acct(ads,
3940 if (!ADS_ERR_OK(status)) {
3941 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
3946 count = ads_count_replies(ads, res);
3948 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3952 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
3953 if (*hostnames_array == NULL) {
3954 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
3956 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3961 ads_msgfree(ads, res);
3966 /********************************************************************
3967 ********************************************************************/
3969 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3971 LDAPMessage *res = NULL;
3976 status = ads_find_machine_acct(ads, &res, machine_name);
3977 if (!ADS_ERR_OK(status)) {
3978 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3979 lp_netbios_name()));
3983 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3984 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3988 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3989 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3993 ads_msgfree(ads, res);
3998 /********************************************************************
3999 ********************************************************************/
4001 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4003 LDAPMessage *res = NULL;
4009 status = ads_find_machine_acct(ads, &res, machine_name);
4010 if (!ADS_ERR_OK(status)) {
4011 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4012 lp_netbios_name()));
4016 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4017 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4021 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4022 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4026 ads_msgfree(ads, res);
4028 ok = (strlen(name) > 0);
4036 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4039 * Join a machine to a realm
4040 * Creates the machine account and sets the machine password
4041 * @param ads connection to ads server
4042 * @param machine name of host to add
4043 * @param org_unit Organizational unit to place machine in
4044 * @return status of join
4046 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4047 uint32_t account_type, const char *org_unit)
4050 LDAPMessage *res = NULL;
4053 /* machine name must be lowercase */
4054 machine = SMB_STRDUP(machine_name);
4055 strlower_m(machine);
4058 status = ads_find_machine_acct(ads, (void **)&res, machine);
4059 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4060 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4061 status = ads_leave_realm(ads, machine);
4062 if (!ADS_ERR_OK(status)) {
4063 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4064 machine, ads->config.realm));
4069 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4070 if (!ADS_ERR_OK(status)) {
4071 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4076 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4077 if (!ADS_ERR_OK(status)) {
4078 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4084 ads_msgfree(ads, res);
4091 * Delete a machine from the realm
4092 * @param ads connection to ads server
4093 * @param hostname Machine to remove
4094 * @return status of delete
4096 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4101 char *hostnameDN, *host;
4103 LDAPControl ldap_control;
4104 LDAPControl * pldap_control[2] = {NULL, NULL};
4106 pldap_control[0] = &ldap_control;
4107 memset(&ldap_control, 0, sizeof(LDAPControl));
4108 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4110 /* hostname must be lowercase */
4111 host = SMB_STRDUP(hostname);
4112 if (!strlower_m(host)) {
4114 return ADS_ERROR_SYSTEM(EINVAL);
4117 status = ads_find_machine_acct(ads, &res, host);
4118 if (!ADS_ERR_OK(status)) {
4119 DEBUG(0, ("Host account for %s does not exist.\n", host));
4124 msg = ads_first_entry(ads, res);
4127 return ADS_ERROR_SYSTEM(ENOENT);
4130 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4131 if (hostnameDN == NULL) {
4133 return ADS_ERROR_SYSTEM(ENOENT);
4136 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4138 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4140 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4143 if (rc != LDAP_SUCCESS) {
4144 const char *attrs[] = { "cn", NULL };
4145 LDAPMessage *msg_sub;
4147 /* we only search with scope ONE, we do not expect any further
4148 * objects to be created deeper */
4150 status = ads_do_search_retry(ads, hostnameDN,
4151 LDAP_SCOPE_ONELEVEL,
4152 "(objectclass=*)", attrs, &res);
4154 if (!ADS_ERR_OK(status)) {
4156 TALLOC_FREE(hostnameDN);
4160 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4161 msg_sub = ads_next_entry(ads, msg_sub)) {
4165 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4167 TALLOC_FREE(hostnameDN);
4168 return ADS_ERROR(LDAP_NO_MEMORY);
4171 status = ads_del_dn(ads, dn);
4172 if (!ADS_ERR_OK(status)) {
4173 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4176 TALLOC_FREE(hostnameDN);
4183 /* there should be no subordinate objects anymore */
4184 status = ads_do_search_retry(ads, hostnameDN,
4185 LDAP_SCOPE_ONELEVEL,
4186 "(objectclass=*)", attrs, &res);
4188 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4190 TALLOC_FREE(hostnameDN);
4194 /* delete hostnameDN now */
4195 status = ads_del_dn(ads, hostnameDN);
4196 if (!ADS_ERR_OK(status)) {
4198 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4199 TALLOC_FREE(hostnameDN);
4204 TALLOC_FREE(hostnameDN);
4206 status = ads_find_machine_acct(ads, &res, host);
4207 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4208 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4209 DEBUG(3, ("Failed to remove host account.\n"));
4219 * pull all token-sids from an LDAP dn
4220 * @param ads connection to ads server
4221 * @param mem_ctx TALLOC_CTX for allocating sid array
4222 * @param dn of LDAP object
4223 * @param user_sid pointer to struct dom_sid (objectSid)
4224 * @param primary_group_sid pointer to struct dom_sid (self composed)
4225 * @param sids pointer to sid array to allocate
4226 * @param num_sids counter of SIDs pulled
4227 * @return status of token query
4229 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4230 TALLOC_CTX *mem_ctx,
4232 struct dom_sid *user_sid,
4233 struct dom_sid *primary_group_sid,
4234 struct dom_sid **sids,
4238 LDAPMessage *res = NULL;
4240 size_t tmp_num_sids;
4241 struct dom_sid *tmp_sids;
4242 struct dom_sid tmp_user_sid;
4243 struct dom_sid tmp_primary_group_sid;
4245 const char *attrs[] = {
4252 status = ads_search_retry_dn(ads, &res, dn, attrs);
4253 if (!ADS_ERR_OK(status)) {
4257 count = ads_count_replies(ads, res);
4259 ads_msgfree(ads, res);
4260 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4263 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4264 ads_msgfree(ads, res);
4265 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4268 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4269 ads_msgfree(ads, res);
4270 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4274 /* hack to compose the primary group sid without knowing the
4277 struct dom_sid domsid;
4279 sid_copy(&domsid, &tmp_user_sid);
4281 if (!sid_split_rid(&domsid, NULL)) {
4282 ads_msgfree(ads, res);
4283 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4286 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4287 ads_msgfree(ads, res);
4288 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4292 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4294 if (tmp_num_sids == 0 || !tmp_sids) {
4295 ads_msgfree(ads, res);
4296 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4300 *num_sids = tmp_num_sids;
4308 *user_sid = tmp_user_sid;
4311 if (primary_group_sid) {
4312 *primary_group_sid = tmp_primary_group_sid;
4315 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4317 ads_msgfree(ads, res);
4318 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4322 * Find a sAMAccoutName in LDAP
4323 * @param ads connection to ads server
4324 * @param mem_ctx TALLOC_CTX for allocating sid array
4325 * @param samaccountname to search
4326 * @param uac_ret uint32_t pointer userAccountControl attribute value
4327 * @param dn_ret pointer to dn
4328 * @return status of token query
4330 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4331 TALLOC_CTX *mem_ctx,
4332 const char *samaccountname,
4334 const char **dn_ret)
4337 const char *attrs[] = { "userAccountControl", NULL };
4339 LDAPMessage *res = NULL;
4343 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4345 if (filter == NULL) {
4346 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4350 status = ads_do_search_all(ads, ads->config.bind_path,
4352 filter, attrs, &res);
4354 if (!ADS_ERR_OK(status)) {
4358 if (ads_count_replies(ads, res) != 1) {
4359 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4363 dn = ads_get_dn(ads, talloc_tos(), res);
4365 status = ADS_ERROR(LDAP_NO_MEMORY);
4369 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4370 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4379 *dn_ret = talloc_strdup(mem_ctx, dn);
4381 status = ADS_ERROR(LDAP_NO_MEMORY);
4387 ads_msgfree(ads, res);
4393 * find our configuration path
4394 * @param ads connection to ads server
4395 * @param mem_ctx Pointer to talloc context
4396 * @param config_path Pointer to the config path
4397 * @return status of search
4399 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4400 TALLOC_CTX *mem_ctx,
4404 LDAPMessage *res = NULL;
4405 const char *config_context = NULL;
4406 const char *attrs[] = { "configurationNamingContext", NULL };
4408 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4409 "(objectclass=*)", attrs, &res);
4410 if (!ADS_ERR_OK(status)) {
4414 config_context = ads_pull_string(ads, mem_ctx, res,
4415 "configurationNamingContext");
4416 ads_msgfree(ads, res);
4417 if (!config_context) {
4418 return ADS_ERROR(LDAP_NO_MEMORY);
4422 *config_path = talloc_strdup(mem_ctx, config_context);
4423 if (!*config_path) {
4424 return ADS_ERROR(LDAP_NO_MEMORY);
4428 return ADS_ERROR(LDAP_SUCCESS);
4432 * find the displayName of an extended right
4433 * @param ads connection to ads server
4434 * @param config_path The config path
4435 * @param mem_ctx Pointer to talloc context
4436 * @param GUID struct of the rightsGUID
4437 * @return status of search
4439 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4440 const char *config_path,
4441 TALLOC_CTX *mem_ctx,
4442 const struct GUID *rights_guid)
4445 LDAPMessage *res = NULL;
4447 const char *attrs[] = { "displayName", NULL };
4448 const char *result = NULL;
4451 if (!ads || !mem_ctx || !rights_guid) {
4455 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4456 GUID_string(mem_ctx, rights_guid));
4461 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4466 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4468 if (!ADS_ERR_OK(rc)) {
4472 if (ads_count_replies(ads, res) != 1) {
4476 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4479 ads_msgfree(ads, res);
4484 * verify or build and verify an account ou
4485 * @param mem_ctx Pointer to talloc context
4486 * @param ads connection to ads server
4488 * @return status of search
4491 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4493 const char **account_ou)
4499 if (account_ou == NULL) {
4500 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4503 if (*account_ou != NULL) {
4504 exploded_dn = ldap_explode_dn(*account_ou, 0);
4506 ldap_value_free(exploded_dn);
4511 ou_string = ads_ou_string(ads, *account_ou);
4513 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4516 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4517 ads->config.bind_path);
4518 SAFE_FREE(ou_string);
4521 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4524 exploded_dn = ldap_explode_dn(name, 0);
4526 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4528 ldap_value_free(exploded_dn);