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/tsocket/tsocket.h"
29 #include "../lib/addns/dnsquery.h"
30 #include "../libds/common/flags.h"
32 #include "../libcli/security/security.h"
33 #include "../librpc/gen_ndr/netlogon.h"
34 #include "lib/param/loadparm.h"
35 #include "libsmb/namequery.h"
36 #include "../librpc/gen_ndr/ndr_ads.h"
42 * @brief basic ldap client-side routines for ads server communications
44 * The routines contained here should do the necessary ldap calls for
47 * Important note: attribute names passed into ads_ routines must
48 * already be in UTF-8 format. We do not convert them because in almost
49 * all cases, they are just ascii (which is represented with the same
50 * codepoints in UTF-8). This may have to change at some point
54 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
56 static SIG_ATOMIC_T gotalarm;
58 /***************************************************************
59 Signal function to tell us we timed out.
60 ****************************************************************/
62 static void gotalarm_sig(int signum)
67 LDAP *ldap_open_with_timeout(const char *server,
68 struct sockaddr_storage *ss,
69 int port, unsigned int to)
75 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
76 "%u seconds\n", server, port, to));
81 CatchSignal(SIGALRM, gotalarm_sig);
83 /* End setup timeout. */
86 if ( strchr_m(server, ':') ) {
88 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
91 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
97 #ifdef HAVE_LDAP_INIT_FD
100 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
101 unsigned timeout_ms = 1000 * to;
103 status = open_socket_out(ss, port, timeout_ms, &fd);
104 if (!NT_STATUS_IS_OK(status)) {
105 DEBUG(3, ("open_socket_out: failed to open socket\n"));
109 /* define LDAP_PROTO_TCP from openldap.h if required */
110 #ifndef LDAP_PROTO_TCP
111 #define LDAP_PROTO_TCP 1
113 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
115 #elif defined(HAVE_LDAP_INITIALIZE)
116 ldap_err = ldap_initialize(&ldp, uri);
118 ldp = ldap_open(server, port);
120 ldap_err = LDAP_SUCCESS;
122 ldap_err = LDAP_OTHER;
125 if (ldap_err != LDAP_SUCCESS) {
126 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
127 uri, ldap_err2string(ldap_err)));
129 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
133 /* Teardown timeout. */
135 CatchSignal(SIGALRM, SIG_IGN);
141 static int ldap_search_with_timeout(LDAP *ld,
142 LDAP_CONST char *base,
144 LDAP_CONST char *filter,
147 LDAPControl **sctrls,
148 LDAPControl **cctrls,
152 int to = lp_ldap_timeout();
153 struct timeval timeout;
154 struct timeval *timeout_ptr = NULL;
157 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
163 timeout_ptr = &timeout;
165 /* Setup alarm timeout. */
166 CatchSignal(SIGALRM, gotalarm_sig);
167 /* Make the alarm time one second beyond
168 the timeout we're setting for the
169 remote search timeout, to allow that
170 to fire in preference. */
172 /* End setup timeout. */
176 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
177 attrsonly, sctrls, cctrls, timeout_ptr,
181 /* Teardown alarm timeout. */
182 CatchSignal(SIGALRM, SIG_IGN);
187 return LDAP_TIMELIMIT_EXCEEDED;
190 * A bug in OpenLDAP means ldap_search_ext_s can return
191 * LDAP_SUCCESS but with a NULL res pointer. Cope with
192 * this. See bug #6279 for details. JRA.
196 return LDAP_TIMELIMIT_EXCEEDED;
202 /**********************************************
203 Do client and server sitename match ?
204 **********************************************/
206 bool ads_sitename_match(ADS_STRUCT *ads)
208 if (ads->config.server_site_name == NULL &&
209 ads->config.client_site_name == NULL ) {
210 DEBUG(10,("ads_sitename_match: both null\n"));
213 if (ads->config.server_site_name &&
214 ads->config.client_site_name &&
215 strequal(ads->config.server_site_name,
216 ads->config.client_site_name)) {
217 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
220 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
221 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
222 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
226 /**********************************************
227 Is this the closest DC ?
228 **********************************************/
230 bool ads_closest_dc(ADS_STRUCT *ads)
232 if (ads->config.flags & NBT_SERVER_CLOSEST) {
233 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
237 /* not sure if this can ever happen */
238 if (ads_sitename_match(ads)) {
239 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
243 if (ads->config.client_site_name == NULL) {
244 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
248 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
249 ads->config.ldap_server_name));
254 static bool ads_fill_cldap_reply(ADS_STRUCT *ads,
256 const struct sockaddr_storage *ss,
257 const struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply)
259 TALLOC_CTX *frame = talloc_stackframe();
261 char addr[INET6_ADDRSTRLEN];
265 print_sockaddr(addr, sizeof(addr), ss);
267 /* Check the CLDAP reply flags */
269 if (!(cldap_reply->server_type & NBT_SERVER_LDAP)) {
270 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
276 /* Fill in the ads->config values */
278 ADS_TALLOC_CONST_FREE(ads->config.realm);
279 ADS_TALLOC_CONST_FREE(ads->config.bind_path);
280 ADS_TALLOC_CONST_FREE(ads->config.ldap_server_name);
281 ADS_TALLOC_CONST_FREE(ads->config.server_site_name);
282 ADS_TALLOC_CONST_FREE(ads->config.client_site_name);
283 ADS_TALLOC_CONST_FREE(ads->server.workgroup);
285 if (!check_cldap_reply_required_flags(cldap_reply->server_type,
286 ads->config.flags)) {
291 ads->config.ldap_server_name = talloc_strdup(ads,
292 cldap_reply->pdc_dns_name);
293 if (ads->config.ldap_server_name == NULL) {
294 DBG_WARNING("Out of memory\n");
299 ads->config.realm = talloc_asprintf_strupper_m(ads,
301 cldap_reply->dns_domain);
302 if (ads->config.realm == NULL) {
303 DBG_WARNING("Out of memory\n");
308 status = ads_build_dn(ads->config.realm, ads, &dn);
309 if (!ADS_ERR_OK(status)) {
310 DBG_DEBUG("Failed to build bind path: %s\n",
315 ads->config.bind_path = dn;
317 if (*cldap_reply->server_site) {
318 ads->config.server_site_name =
319 talloc_strdup(ads, cldap_reply->server_site);
320 if (ads->config.server_site_name == NULL) {
321 DBG_WARNING("Out of memory\n");
327 if (*cldap_reply->client_site) {
328 ads->config.client_site_name =
329 talloc_strdup(ads, cldap_reply->client_site);
330 if (ads->config.client_site_name == NULL) {
331 DBG_WARNING("Out of memory\n");
337 ads->server.workgroup = talloc_strdup(ads, cldap_reply->domain_name);
338 if (ads->server.workgroup == NULL) {
339 DBG_WARNING("Out of memory\n");
344 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
347 /* Store our site name. */
348 sitename_store(cldap_reply->domain_name, cldap_reply->client_site);
349 sitename_store(cldap_reply->dns_domain, cldap_reply->client_site);
351 /* Leave this until last so that the flags are not clobbered */
352 ads->config.flags = cldap_reply->server_type;
363 try a connection to a given ldap server, returning True and setting the servers IP
364 in the ads struct if successful
366 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
367 struct sockaddr_storage *ss)
369 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply = {};
370 TALLOC_CTX *frame = talloc_stackframe();
372 char addr[INET6_ADDRSTRLEN] = { 0, };
379 print_sockaddr(addr, sizeof(addr), ss);
381 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
382 addr, ads->server.realm);
384 ok = ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply);
386 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
387 addr, ads->server.realm);
392 ok = ads_fill_cldap_reply(ads, gc, ss, &cldap_reply);
394 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
395 addr, ads->server.realm);
404 /**********************************************************************
405 send a cldap ping to list of servers, one at a time, until one of
406 them answers it's an ldap server. Record success in the ADS_STRUCT.
407 Take note of and update negative connection cache.
408 **********************************************************************/
410 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
412 struct samba_sockaddr *sa_list,
415 TALLOC_CTX *frame = talloc_stackframe();
416 struct timeval endtime = timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
417 uint32_t nt_version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
418 struct tsocket_address **ts_list = NULL;
419 const struct tsocket_address * const *ts_list_const = NULL;
420 struct samba_sockaddr **req_sa_list = NULL;
421 struct netlogon_samlogon_response **responses = NULL;
422 size_t num_requests = 0;
428 ts_list = talloc_zero_array(frame,
429 struct tsocket_address *,
431 if (ts_list == NULL) {
433 return NT_STATUS_NO_MEMORY;
436 req_sa_list = talloc_zero_array(frame,
437 struct samba_sockaddr *,
439 if (req_sa_list == NULL) {
441 return NT_STATUS_NO_MEMORY;
446 * The retry loop is bound by the timeout
451 for (i = 0; i < count; i++) {
452 char server[INET6_ADDRSTRLEN];
455 if (is_zero_addr(&sa_list[i].u.ss)) {
459 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
461 status = check_negative_conn_cache(domain, server);
462 if (!NT_STATUS_IS_OK(status)) {
466 ret = tsocket_address_inet_from_strings(ts_list, "ip",
468 &ts_list[num_requests]);
470 status = map_nt_error_from_unix(errno);
471 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
472 server, nt_errstr(status));
477 req_sa_list[num_requests] = &sa_list[i];
481 if (num_requests == 0) {
482 status = NT_STATUS_NO_LOGON_SERVERS;
483 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
484 domain, num_requests, count, nt_errstr(status));
489 ts_list_const = (const struct tsocket_address * const *)ts_list;
491 status = cldap_multi_netlogon(frame,
492 ts_list_const, num_requests,
493 ads->server.realm, NULL,
495 1, endtime, &responses);
496 if (!NT_STATUS_IS_OK(status)) {
497 DBG_WARNING("cldap_multi_netlogon(realm=%s, num_requests=%zu) "
498 "for count[%zu] - %s\n",
503 return NT_STATUS_NO_LOGON_SERVERS;
506 for (i = 0; i < num_requests; i++) {
507 struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply = NULL;
508 char server[INET6_ADDRSTRLEN];
510 if (responses[i] == NULL) {
514 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
516 if (responses[i]->ntver != NETLOGON_NT_VERSION_5EX) {
517 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
519 responses[i]->ntver, server);
523 cldap_reply = &responses[i]->data.nt5_ex;
525 /* Returns ok only if it matches the correct server type */
526 ok = ads_fill_cldap_reply(ads,
528 &req_sa_list[i]->u.ss,
531 DBG_DEBUG("realm[%s]: selected %s => %s\n",
533 server, cldap_reply->pdc_dns_name);
534 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
535 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
542 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
544 server, cldap_reply->pdc_dns_name);
545 if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
546 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
549 add_failed_connection_entry(domain, server,
550 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID);
557 expired = timeval_expired(&endtime);
563 /* keep track of failures as all were not suitable */
564 for (i = 0; i < num_requests; i++) {
565 char server[INET6_ADDRSTRLEN];
567 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
569 add_failed_connection_entry(domain, server,
570 NT_STATUS_UNSUCCESSFUL);
573 status = NT_STATUS_NO_LOGON_SERVERS;
574 DBG_WARNING("realm[%s] no valid response "
575 "num_requests[%zu] for count[%zu] - %s\n",
577 num_requests, count, nt_errstr(status));
579 return NT_STATUS_NO_LOGON_SERVERS;
582 /***************************************************************************
583 resolve a name and perform an "ldap ping" using NetBIOS and related methods
584 ****************************************************************************/
586 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
587 const char *domain, const char *realm)
591 struct samba_sockaddr *sa_list = NULL;
594 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
597 status = get_sorted_dc_list(talloc_tos(),
603 if (!NT_STATUS_IS_OK(status)) {
607 /* remove servers which are known to be dead based on
608 the corresponding DNS method */
610 for (i = 0; i < count; ++i) {
611 char server[INET6_ADDRSTRLEN];
613 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
616 check_negative_conn_cache(realm, server))) {
617 /* Ensure we add the workgroup name for this
618 IP address as negative too. */
619 add_failed_connection_entry(
621 NT_STATUS_UNSUCCESSFUL);
626 status = cldap_ping_list(ads, domain, sa_list, count);
628 TALLOC_FREE(sa_list);
634 /**********************************************************************
635 resolve a name and perform an "ldap ping" using DNS
636 **********************************************************************/
638 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
642 struct samba_sockaddr *sa_list = NULL;
645 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
648 status = get_sorted_dc_list(talloc_tos(),
654 if (!NT_STATUS_IS_OK(status)) {
655 TALLOC_FREE(sa_list);
659 status = cldap_ping_list(ads, realm, sa_list, count);
661 TALLOC_FREE(sa_list);
666 /**********************************************************************
667 Try to find an AD dc using our internal name resolution routines
668 Try the realm first and then then workgroup name if netbios is not
670 **********************************************************************/
672 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
674 const char *c_domain = "";
676 bool use_own_domain = False;
677 char *sitename = NULL;
678 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
681 /* if the realm and workgroup are both empty, assume they are ours */
684 c_realm = ads->server.realm;
690 /* special case where no realm and no workgroup means our own */
691 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
692 use_own_domain = True;
693 c_realm = lp_realm();
697 if (!lp_disable_netbios()) {
698 if (use_own_domain) {
699 c_domain = lp_workgroup();
701 c_domain = ads->server.workgroup;
702 if (!*c_realm && (!c_domain || !*c_domain)) {
703 c_domain = lp_workgroup();
712 if (!*c_realm && !*c_domain) {
713 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
715 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
719 * In case of LDAP we use get_dc_name() as that
720 * creates the custom krb5.conf file
722 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
724 struct sockaddr_storage ip_out;
726 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
727 " and falling back to domain '%s'\n",
730 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
732 if (is_zero_addr(&ip_out)) {
733 return NT_STATUS_NO_LOGON_SERVERS;
737 * we call ads_try_connect() to fill in the
738 * ads->config details
740 ok = ads_try_connect(ads, false, &ip_out);
746 return NT_STATUS_NO_LOGON_SERVERS;
750 sitename = sitename_fetch(talloc_tos(), c_realm);
751 status = resolve_and_ping_dns(ads, sitename, c_realm);
753 if (NT_STATUS_IS_OK(status)) {
754 TALLOC_FREE(sitename);
758 /* In case we failed to contact one of our closest DC on our
760 * need to try to find another DC, retry with a site-less SRV
765 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
766 "our site (%s), Trying to find another DC "
767 "for realm '%s' (domain '%s')\n",
768 sitename, c_realm, c_domain));
769 namecache_delete(c_realm, 0x1C);
771 resolve_and_ping_dns(ads, NULL, c_realm);
773 if (NT_STATUS_IS_OK(status)) {
774 TALLOC_FREE(sitename);
779 TALLOC_FREE(sitename);
782 /* try netbios as fallback - if permitted,
783 or if configuration specifically requests it */
786 DEBUG(3, ("ads_find_dc: falling back to netbios "
787 "name resolution for domain '%s' (realm '%s')\n",
791 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
792 if (NT_STATUS_IS_OK(status)) {
797 DEBUG(1, ("ads_find_dc: "
798 "name resolution for realm '%s' (domain '%s') failed: %s\n",
799 c_realm, c_domain, nt_errstr(status)));
803 * Connect to the LDAP server
804 * @param ads Pointer to an existing ADS_STRUCT
805 * @return status of connection
807 ADS_STATUS ads_connect(ADS_STRUCT *ads)
809 int version = LDAP_VERSION3;
812 char addr[INET6_ADDRSTRLEN];
813 struct sockaddr_storage existing_ss;
815 zero_sockaddr(&existing_ss);
818 * ads_connect can be passed in a reused ADS_STRUCT
819 * with an existing non-zero ads->ldap.ss IP address
820 * that was stored by going through ads_find_dc()
821 * if ads->server.ldap_server was NULL.
823 * If ads->server.ldap_server is still NULL but
824 * the target address isn't the zero address, then
825 * store that address off off before zeroing out
826 * ads->ldap so we don't keep doing multiple calls
827 * to ads_find_dc() in the reuse case.
829 * If a caller wants a clean ADS_STRUCT they
830 * will TALLOC_FREE it and allocate a new one
831 * by calling ads_init(), which ensures
832 * ads->ldap.ss is a properly zero'ed out valid IP
835 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
836 /* Save off the address we previously found by ads_find_dc(). */
837 existing_ss = ads->ldap.ss;
841 ZERO_STRUCT(ads->ldap_wrap_data);
842 ads->ldap.last_attempt = time_mono(NULL);
843 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
845 /* try with a user specified server */
847 if (DEBUGLEVEL >= 11) {
848 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
849 DEBUG(11,("ads_connect: entering\n"));
850 DEBUGADD(11,("%s\n", s));
854 if (ads->server.ldap_server) {
856 struct sockaddr_storage ss;
858 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
860 DEBUG(5,("ads_connect: unable to resolve name %s\n",
861 ads->server.ldap_server));
862 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
866 if (is_zero_addr(&ss)) {
867 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
871 ok = ads_try_connect(ads, ads->server.gc, &ss);
876 /* The choice of which GC use is handled one level up in
877 ads_connect_gc(). If we continue on from here with
878 ads_find_dc() we will get GC searches on port 389 which
879 doesn't work. --jerry */
881 if (ads->server.gc == true) {
882 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
885 if (ads->server.no_fallback) {
886 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
891 if (!is_zero_addr(&existing_ss)) {
892 /* We saved off who we should talk to. */
893 bool ok = ads_try_connect(ads,
900 * Keep trying to find a server and fall through
901 * into ads_find_dc() again.
905 ntstatus = ads_find_dc(ads);
906 if (NT_STATUS_IS_OK(ntstatus)) {
910 status = ADS_ERROR_NT(ntstatus);
915 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
916 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
918 if (!ads->auth.user_name) {
919 /* Must use the userPrincipalName value here or sAMAccountName
920 and not servicePrincipalName; found by Guenther Deschner */
921 ads->auth.user_name = talloc_asprintf(ads,
924 if (ads->auth.user_name == NULL) {
925 DBG_ERR("talloc_asprintf failed\n");
926 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
931 if (ads->auth.realm == NULL) {
932 ads->auth.realm = talloc_strdup(ads, ads->config.realm);
933 if (ads->auth.realm == NULL) {
934 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
939 if (!ads->auth.kdc_server) {
940 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
941 ads->auth.kdc_server = talloc_strdup(ads, addr);
942 if (ads->auth.kdc_server == NULL) {
943 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
948 /* If the caller() requested no LDAP bind, then we are done */
950 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
951 status = ADS_SUCCESS;
955 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
956 if (!ads->ldap_wrap_data.mem_ctx) {
957 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
961 /* Otherwise setup the TCP LDAP session */
963 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
965 ads->ldap.port, lp_ldap_timeout());
966 if (ads->ldap.ld == NULL) {
967 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
970 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
972 /* cache the successful connection for workgroup and realm */
973 if (ads_closest_dc(ads)) {
974 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
975 saf_store( ads->server.realm, ads->config.ldap_server_name);
978 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
980 /* fill in the current time and offsets */
982 status = ads_current_time( ads );
983 if ( !ADS_ERR_OK(status) ) {
987 /* Now do the bind */
989 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
990 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
994 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
995 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
999 status = ads_sasl_bind(ads);
1002 if (DEBUGLEVEL >= 11) {
1003 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1004 DEBUG(11,("ads_connect: leaving with: %s\n",
1005 ads_errstr(status)));
1006 DEBUGADD(11,("%s\n", s));
1014 * Connect to the LDAP server using given credentials
1015 * @param ads Pointer to an existing ADS_STRUCT
1016 * @return status of connection
1018 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
1020 ads->auth.flags |= ADS_AUTH_USER_CREDS;
1022 return ads_connect(ads);
1026 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1027 * @param ads Pointer to an existing ADS_STRUCT
1029 * Sets the ads->ldap.ss to a valid
1030 * zero ip address that can be detected by
1031 * our is_zero_addr() function. Otherwise
1032 * it is left as AF_UNSPEC (0).
1034 void ads_zero_ldap(ADS_STRUCT *ads)
1036 ZERO_STRUCT(ads->ldap);
1038 * Initialize the sockaddr_storage so we can use
1039 * sockaddr test functions against it.
1041 zero_sockaddr(&ads->ldap.ss);
1045 * Disconnect the LDAP server
1046 * @param ads Pointer to an existing ADS_STRUCT
1048 void ads_disconnect(ADS_STRUCT *ads)
1051 ldap_unbind(ads->ldap.ld);
1052 ads->ldap.ld = NULL;
1054 if (ads->ldap_wrap_data.wrap_ops &&
1055 ads->ldap_wrap_data.wrap_ops->disconnect) {
1056 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1058 if (ads->ldap_wrap_data.mem_ctx) {
1059 talloc_free(ads->ldap_wrap_data.mem_ctx);
1062 ZERO_STRUCT(ads->ldap_wrap_data);
1066 Duplicate a struct berval into talloc'ed memory
1068 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1070 struct berval *value;
1072 if (!in_val) return NULL;
1074 value = talloc_zero(ctx, struct berval);
1077 if (in_val->bv_len == 0) return value;
1079 value->bv_len = in_val->bv_len;
1080 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1086 Make a values list out of an array of (struct berval *)
1088 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1089 const struct berval **in_vals)
1091 struct berval **values;
1094 if (!in_vals) return NULL;
1095 for (i=0; in_vals[i]; i++)
1096 ; /* count values */
1097 values = talloc_zero_array(ctx, struct berval *, i+1);
1098 if (!values) return NULL;
1100 for (i=0; in_vals[i]; i++) {
1101 values[i] = dup_berval(ctx, in_vals[i]);
1107 UTF8-encode a values list out of an array of (char *)
1109 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1115 if (!in_vals) return NULL;
1116 for (i=0; in_vals[i]; i++)
1117 ; /* count values */
1118 values = talloc_zero_array(ctx, char *, i+1);
1119 if (!values) return NULL;
1121 for (i=0; in_vals[i]; i++) {
1122 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1123 TALLOC_FREE(values);
1131 Pull a (char *) array out of a UTF8-encoded values list
1133 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1137 size_t converted_size;
1139 if (!in_vals) return NULL;
1140 for (i=0; in_vals[i]; i++)
1141 ; /* count values */
1142 values = talloc_zero_array(ctx, char *, i+1);
1143 if (!values) return NULL;
1145 for (i=0; in_vals[i]; i++) {
1146 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1148 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1149 "%s\n", strerror(errno)));
1156 * Do a search with paged results. cookie must be null on the first
1157 * call, and then returned on each subsequent call. It will be null
1158 * again when the entire search is complete
1159 * @param ads connection to ads server
1160 * @param bind_path Base dn for the search
1161 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1162 * @param expr Search expression - specified in local charset
1163 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1164 * @param res ** which will contain results - free res* with ads_msgfree()
1165 * @param count Number of entries retrieved on this page
1166 * @param cookie The paged results cookie to be returned on subsequent calls
1167 * @return status of search
1169 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1170 const char *bind_path,
1171 int scope, const char *expr,
1172 const char **attrs, void *args,
1174 int *count, struct berval **cookie)
1177 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1178 size_t converted_size;
1179 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1180 BerElement *cookie_be = NULL;
1181 struct berval *cookie_bv= NULL;
1182 BerElement *ext_be = NULL;
1183 struct berval *ext_bv= NULL;
1186 ads_control *external_control = (ads_control *) args;
1190 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1191 return ADS_ERROR(LDAP_NO_MEMORY);
1193 /* 0 means the conversion worked but the result was empty
1194 so we only fail if it's -1. In any case, it always
1195 at least nulls out the dest */
1196 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1197 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1199 rc = LDAP_NO_MEMORY;
1203 if (!attrs || !(*attrs))
1204 search_attrs = NULL;
1206 /* This would be the utf8-encoded version...*/
1207 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1208 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1209 rc = LDAP_NO_MEMORY;
1214 /* Paged results only available on ldap v3 or later */
1215 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1216 if (version < LDAP_VERSION3) {
1217 rc = LDAP_NOT_SUPPORTED;
1221 cookie_be = ber_alloc_t(LBER_USE_DER);
1223 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1224 ber_bvfree(*cookie); /* don't need it from last time */
1227 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1229 ber_flatten(cookie_be, &cookie_bv);
1230 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1231 PagedResults.ldctl_iscritical = (char) 1;
1232 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1233 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1235 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1236 NoReferrals.ldctl_iscritical = (char) 0;
1237 NoReferrals.ldctl_value.bv_len = 0;
1238 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1240 if (external_control &&
1241 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1242 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1244 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1245 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1247 /* win2k does not accept a ldctl_value being passed in */
1249 if (external_control->val != 0) {
1251 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1252 rc = LDAP_NO_MEMORY;
1256 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1257 rc = LDAP_NO_MEMORY;
1260 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1261 rc = LDAP_NO_MEMORY;
1265 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1266 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1269 ExternalCtrl.ldctl_value.bv_len = 0;
1270 ExternalCtrl.ldctl_value.bv_val = NULL;
1273 controls[0] = &NoReferrals;
1274 controls[1] = &PagedResults;
1275 controls[2] = &ExternalCtrl;
1279 controls[0] = &NoReferrals;
1280 controls[1] = &PagedResults;
1284 /* we need to disable referrals as the openldap libs don't
1285 handle them and paged results at the same time. Using them
1286 together results in the result record containing the server
1287 page control being removed from the result list (tridge/jmcd)
1289 leaving this in despite the control that says don't generate
1290 referrals, in case the server doesn't support it (jmcd)
1292 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1294 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1295 search_attrs, 0, controls,
1296 NULL, LDAP_NO_LIMIT,
1297 (LDAPMessage **)res);
1299 ber_free(cookie_be, 1);
1300 ber_bvfree(cookie_bv);
1303 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1304 ldap_err2string(rc)));
1305 if (rc == LDAP_OTHER) {
1309 ret = ldap_parse_result(ads->ldap.ld,
1317 if (ret == LDAP_SUCCESS) {
1318 DEBUG(3, ("ldap_search_with_timeout(%s) "
1319 "error: %s\n", expr, ldap_errmsg));
1320 ldap_memfree(ldap_errmsg);
1326 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1327 NULL, &rcontrols, 0);
1333 for (i=0; rcontrols[i]; i++) {
1334 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1335 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1336 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1338 /* the berval is the cookie, but must be freed when
1340 if (cookie_bv->bv_len) /* still more to do */
1341 *cookie=ber_bvdup(cookie_bv);
1344 ber_bvfree(cookie_bv);
1345 ber_free(cookie_be, 1);
1349 ldap_controls_free(rcontrols);
1352 talloc_destroy(ctx);
1355 ber_free(ext_be, 1);
1362 if (rc != LDAP_SUCCESS && *res != NULL) {
1363 ads_msgfree(ads, *res);
1367 /* if/when we decide to utf8-encode attrs, take out this next line */
1368 TALLOC_FREE(search_attrs);
1370 return ADS_ERROR(rc);
1373 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1374 int scope, const char *expr,
1375 const char **attrs, LDAPMessage **res,
1376 int *count, struct berval **cookie)
1378 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1383 * Get all results for a search. This uses ads_do_paged_search() to return
1384 * all entries in a large search.
1385 * @param ads connection to ads server
1386 * @param bind_path Base dn for the search
1387 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1388 * @param expr Search expression
1389 * @param attrs Attributes to retrieve
1390 * @param res ** which will contain results - free res* with ads_msgfree()
1391 * @return status of search
1393 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1394 int scope, const char *expr,
1395 const char **attrs, void *args,
1398 struct berval *cookie = NULL;
1403 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1406 if (!ADS_ERR_OK(status))
1409 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1411 LDAPMessage *res2 = NULL;
1412 LDAPMessage *msg, *next;
1414 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1415 attrs, args, &res2, &count, &cookie);
1416 if (!ADS_ERR_OK(status)) {
1420 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1421 that this works on all ldap libs, but I have only tested with openldap */
1422 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1423 next = ads_next_message(ads, msg);
1424 ldap_add_result_entry((LDAPMessage **)res, msg);
1426 /* note that we do not free res2, as the memory is now
1427 part of the main returned list */
1430 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1431 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1437 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1438 int scope, const char *expr,
1439 const char **attrs, LDAPMessage **res)
1441 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1444 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1445 int scope, const char *expr,
1446 const char **attrs, uint32_t sd_flags,
1451 args.control = ADS_SD_FLAGS_OID;
1452 args.val = sd_flags;
1453 args.critical = True;
1455 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1460 * Run a function on all results for a search. Uses ads_do_paged_search() and
1461 * runs the function as each page is returned, using ads_process_results()
1462 * @param ads connection to ads server
1463 * @param bind_path Base dn for the search
1464 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1465 * @param expr Search expression - specified in local charset
1466 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1467 * @param fn Function which takes attr name, values list, and data_area
1468 * @param data_area Pointer which is passed to function on each call
1469 * @return status of search
1471 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1472 int scope, const char *expr, const char **attrs,
1473 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1476 struct berval *cookie = NULL;
1481 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1484 if (!ADS_ERR_OK(status)) return status;
1486 ads_process_results(ads, res, fn, data_area);
1487 ads_msgfree(ads, res);
1490 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1491 &res, &count, &cookie);
1493 if (!ADS_ERR_OK(status)) break;
1495 ads_process_results(ads, res, fn, data_area);
1496 ads_msgfree(ads, res);
1503 * Do a search with a timeout.
1504 * @param ads connection to ads server
1505 * @param bind_path Base dn for the search
1506 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1507 * @param expr Search expression
1508 * @param attrs Attributes to retrieve
1509 * @param res ** which will contain results - free res* with ads_msgfree()
1510 * @return status of search
1512 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1514 const char **attrs, LDAPMessage **res)
1517 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1518 size_t converted_size;
1522 if (!(ctx = talloc_init("ads_do_search"))) {
1523 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1524 return ADS_ERROR(LDAP_NO_MEMORY);
1527 /* 0 means the conversion worked but the result was empty
1528 so we only fail if it's negative. In any case, it always
1529 at least nulls out the dest */
1530 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1531 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1533 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1534 rc = LDAP_NO_MEMORY;
1538 if (!attrs || !(*attrs))
1539 search_attrs = NULL;
1541 /* This would be the utf8-encoded version...*/
1542 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1543 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1545 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1546 rc = LDAP_NO_MEMORY;
1551 /* see the note in ads_do_paged_search - we *must* disable referrals */
1552 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1554 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1555 search_attrs, 0, NULL, NULL,
1557 (LDAPMessage **)res);
1559 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1560 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1565 talloc_destroy(ctx);
1566 /* if/when we decide to utf8-encode attrs, take out this next line */
1567 TALLOC_FREE(search_attrs);
1568 return ADS_ERROR(rc);
1571 * Do a general ADS search
1572 * @param ads connection to ads server
1573 * @param res ** which will contain results - free res* with ads_msgfree()
1574 * @param expr Search expression
1575 * @param attrs Attributes to retrieve
1576 * @return status of search
1578 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1579 const char *expr, const char **attrs)
1581 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1586 * Do a search on a specific DistinguishedName
1587 * @param ads connection to ads server
1588 * @param res ** which will contain results - free res* with ads_msgfree()
1589 * @param dn DistinguishName to search
1590 * @param attrs Attributes to retrieve
1591 * @return status of search
1593 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1594 const char *dn, const char **attrs)
1596 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1601 * Free up memory from a ads_search
1602 * @param ads connection to ads server
1603 * @param msg Search results to free
1605 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1612 * Get a dn from search results
1613 * @param ads connection to ads server
1614 * @param msg Search result
1617 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1619 char *utf8_dn, *unix_dn;
1620 size_t converted_size;
1622 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1625 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1629 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1630 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1634 ldap_memfree(utf8_dn);
1639 * Get the parent from a dn
1640 * @param dn the dn to return the parent from
1641 * @return parent dn string
1643 char *ads_parent_dn(const char *dn)
1651 p = strchr(dn, ',');
1661 * Find a machine account given a hostname
1662 * @param ads connection to ads server
1663 * @param res ** which will contain results - free res* with ads_msgfree()
1664 * @param host Hostname to search for
1665 * @return status of search
1667 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1668 const char *machine)
1672 const char *attrs[] = {
1673 /* This is how Windows checks for machine accounts */
1676 "userAccountControl",
1678 "ServicePrincipalName",
1679 "userPrincipalName",
1682 /* Additional attributes Samba checks */
1683 "msDS-AdditionalDnsHostName",
1684 "msDS-SupportedEncryptionTypes",
1685 "nTSecurityDescriptor",
1690 TALLOC_CTX *frame = talloc_stackframe();
1694 /* the easiest way to find a machine account anywhere in the tree
1695 is to look for hostname$ */
1696 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1698 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1702 status = ads_search(ads, res, expr, attrs);
1703 if (ADS_ERR_OK(status)) {
1704 if (ads_count_replies(ads, *res) != 1) {
1705 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1715 * Initialize a list of mods to be used in a modify request
1716 * @param ctx An initialized TALLOC_CTX
1717 * @return allocated ADS_MODLIST
1719 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1721 #define ADS_MODLIST_ALLOC_SIZE 10
1724 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1725 /* -1 is safety to make sure we don't go over the end.
1726 need to reset it to NULL before doing ldap modify */
1727 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1729 return (ADS_MODLIST)mods;
1734 add an attribute to the list, with values list already constructed
1736 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1737 int mod_op, const char *name,
1738 const void *_invals)
1741 LDAPMod **modlist = (LDAPMod **) *mods;
1742 struct berval **ber_values = NULL;
1743 char **char_values = NULL;
1746 mod_op = LDAP_MOD_DELETE;
1748 if (mod_op & LDAP_MOD_BVALUES) {
1749 const struct berval **b;
1750 b = discard_const_p(const struct berval *, _invals);
1751 ber_values = ads_dup_values(ctx, b);
1754 c = discard_const_p(const char *, _invals);
1755 char_values = ads_push_strvals(ctx, c);
1759 /* find the first empty slot */
1760 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1762 if (modlist[curmod] == (LDAPMod *) -1) {
1763 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1764 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1765 return ADS_ERROR(LDAP_NO_MEMORY);
1766 memset(&modlist[curmod], 0,
1767 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1768 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1769 *mods = (ADS_MODLIST)modlist;
1772 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1773 return ADS_ERROR(LDAP_NO_MEMORY);
1774 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1775 if (mod_op & LDAP_MOD_BVALUES) {
1776 modlist[curmod]->mod_bvalues = ber_values;
1777 } else if (mod_op & LDAP_MOD_DELETE) {
1778 modlist[curmod]->mod_values = NULL;
1780 modlist[curmod]->mod_values = char_values;
1783 modlist[curmod]->mod_op = mod_op;
1784 return ADS_ERROR(LDAP_SUCCESS);
1788 * Add a single string value to a mod list
1789 * @param ctx An initialized TALLOC_CTX
1790 * @param mods An initialized ADS_MODLIST
1791 * @param name The attribute name to add
1792 * @param val The value to add - NULL means DELETE
1793 * @return ADS STATUS indicating success of add
1795 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1796 const char *name, const char *val)
1798 const char *values[2];
1804 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1805 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1809 * Add an array of string values to a mod list
1810 * @param ctx An initialized TALLOC_CTX
1811 * @param mods An initialized ADS_MODLIST
1812 * @param name The attribute name to add
1813 * @param vals The array of string values to add - NULL means DELETE
1814 * @return ADS STATUS indicating success of add
1816 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1817 const char *name, const char **vals)
1820 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1821 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1822 name, (const void **) vals);
1826 * Add a single ber-encoded value to a mod list
1827 * @param ctx An initialized TALLOC_CTX
1828 * @param mods An initialized ADS_MODLIST
1829 * @param name The attribute name to add
1830 * @param val The value to add - NULL means DELETE
1831 * @return ADS STATUS indicating success of add
1833 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1834 const char *name, const struct berval *val)
1836 const struct berval *values[2];
1841 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1842 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1843 name, (const void **) values);
1846 static void ads_print_error(int ret, LDAP *ld)
1849 char *ld_error = NULL;
1850 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1851 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1853 ldap_err2string(ret),
1855 SAFE_FREE(ld_error);
1860 * Perform an ldap modify
1861 * @param ads connection to ads server
1862 * @param mod_dn DistinguishedName to modify
1863 * @param mods list of modifications to perform
1864 * @return status of modify
1866 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1869 char *utf8_dn = NULL;
1870 size_t converted_size;
1872 this control is needed to modify that contains a currently
1873 non-existent attribute (but allowable for the object) to run
1875 LDAPControl PermitModify = {
1876 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1879 LDAPControl *controls[2];
1881 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1883 controls[0] = &PermitModify;
1886 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1887 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1890 /* find the end of the list, marked by NULL or -1 */
1891 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1892 /* make sure the end of the list is NULL */
1894 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1895 (LDAPMod **) mods, controls, NULL);
1896 ads_print_error(ret, ads->ldap.ld);
1897 TALLOC_FREE(utf8_dn);
1898 return ADS_ERROR(ret);
1902 * Perform an ldap add
1903 * @param ads connection to ads server
1904 * @param new_dn DistinguishedName to add
1905 * @param mods list of attributes and values for DN
1906 * @return status of add
1908 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1911 char *utf8_dn = NULL;
1912 size_t converted_size;
1914 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1916 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1917 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
1918 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1921 /* find the end of the list, marked by NULL or -1 */
1922 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1923 /* make sure the end of the list is NULL */
1926 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1927 ads_print_error(ret, ads->ldap.ld);
1928 TALLOC_FREE(utf8_dn);
1929 return ADS_ERROR(ret);
1933 * Delete a DistinguishedName
1934 * @param ads connection to ads server
1935 * @param new_dn DistinguishedName to delete
1936 * @return status of delete
1938 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1941 char *utf8_dn = NULL;
1942 size_t converted_size;
1943 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1944 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
1945 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1948 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1950 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1951 ads_print_error(ret, ads->ldap.ld);
1952 TALLOC_FREE(utf8_dn);
1953 return ADS_ERROR(ret);
1957 * Build an org unit string
1958 * if org unit is Computers or blank then assume a container, otherwise
1959 * assume a / separated list of organisational units.
1960 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1961 * @param ads connection to ads server
1962 * @param org_unit Organizational unit
1963 * @return org unit string - caller must free
1965 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1971 if (!org_unit || !*org_unit) {
1973 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1975 /* samba4 might not yet respond to a wellknownobject-query */
1976 return ret ? ret : SMB_STRDUP("cn=Computers");
1979 if (strequal(org_unit, "Computers")) {
1980 return SMB_STRDUP("cn=Computers");
1983 /* jmcd: removed "\\" from the separation chars, because it is
1984 needed as an escape for chars like '#' which are valid in an
1986 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
1987 if (!ADS_ERR_OK(status)) {
1995 * Get a org unit string for a well-known GUID
1996 * @param ads connection to ads server
1997 * @param wknguid Well known GUID
1998 * @return org unit string - caller must free
2000 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2003 LDAPMessage *res = NULL;
2004 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2005 **bind_dn_exp = NULL;
2006 const char *attrs[] = {"distinguishedName", NULL};
2007 int new_ln, wkn_ln, bind_ln, i;
2009 if (wknguid == NULL) {
2013 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2014 DEBUG(1, ("asprintf failed!\n"));
2018 status = ads_search_dn(ads, &res, base, attrs);
2019 if (!ADS_ERR_OK(status)) {
2020 DEBUG(1,("Failed while searching for: %s\n", base));
2024 if (ads_count_replies(ads, res) != 1) {
2028 /* substitute the bind-path from the well-known-guid-search result */
2029 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2034 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2039 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2044 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2046 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2049 new_ln = wkn_ln - bind_ln;
2051 ret = SMB_STRDUP(wkn_dn_exp[0]);
2056 for (i=1; i < new_ln; i++) {
2059 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2065 ret = SMB_STRDUP(s);
2074 ads_msgfree(ads, res);
2075 TALLOC_FREE(wkn_dn);
2077 ldap_value_free(wkn_dn_exp);
2080 ldap_value_free(bind_dn_exp);
2087 * Adds (appends) an item to an attribute array, rather then
2088 * replacing the whole list
2089 * @param ctx An initialized TALLOC_CTX
2090 * @param mods An initialized ADS_MODLIST
2091 * @param name name of the ldap attribute to append to
2092 * @param vals an array of values to add
2093 * @return status of addition
2096 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2097 const char *name, const char **vals)
2099 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2100 (const void *) vals);
2104 * Determines the an account's current KVNO via an LDAP lookup
2105 * @param ads An initialized ADS_STRUCT
2106 * @param account_name the NT samaccountname.
2107 * @return the kvno for the account, or -1 in case of a failure.
2110 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
2112 LDAPMessage *res = NULL;
2113 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
2115 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
2116 char *dn_string = NULL;
2119 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
2120 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
2123 ret = ads_search(ads, &res, filter, attrs);
2125 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
2126 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
2127 ads_msgfree(ads, res);
2131 dn_string = ads_get_dn(ads, talloc_tos(), res);
2133 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2134 ads_msgfree(ads, res);
2137 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
2138 TALLOC_FREE(dn_string);
2140 /* ---------------------------------------------------------
2141 * 0 is returned as a default KVNO from this point on...
2142 * This is done because Windows 2000 does not support key
2143 * version numbers. Chances are that a failure in the next
2144 * step is simply due to Windows 2000 being used for a
2145 * domain controller. */
2148 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2149 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2150 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2151 ads_msgfree(ads, res);
2156 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2157 ads_msgfree(ads, res);
2162 * Determines the computer account's current KVNO via an LDAP lookup
2163 * @param ads An initialized ADS_STRUCT
2164 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2165 * @return the kvno for the computer account, or -1 in case of a failure.
2168 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2170 char *computer_account = NULL;
2173 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2177 kvno = ads_get_kvno(ads, computer_account);
2178 free(computer_account);
2184 * This clears out all registered spn's for a given hostname
2185 * @param ads An initilaized ADS_STRUCT
2186 * @param machine_name the NetBIOS name of the computer.
2187 * @return 0 upon success, non-zero otherwise.
2190 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2193 LDAPMessage *res = NULL;
2195 const char *servicePrincipalName[1] = {NULL};
2197 char *dn_string = NULL;
2199 ret = ads_find_machine_acct(ads, &res, machine_name);
2200 if (!ADS_ERR_OK(ret)) {
2201 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2202 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2203 ads_msgfree(ads, res);
2207 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2208 ctx = talloc_init("ads_clear_service_principal_names");
2210 ads_msgfree(ads, res);
2211 return ADS_ERROR(LDAP_NO_MEMORY);
2214 if (!(mods = ads_init_mods(ctx))) {
2215 talloc_destroy(ctx);
2216 ads_msgfree(ads, res);
2217 return ADS_ERROR(LDAP_NO_MEMORY);
2219 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2220 if (!ADS_ERR_OK(ret)) {
2221 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2222 ads_msgfree(ads, res);
2223 talloc_destroy(ctx);
2226 dn_string = ads_get_dn(ads, talloc_tos(), res);
2228 talloc_destroy(ctx);
2229 ads_msgfree(ads, res);
2230 return ADS_ERROR(LDAP_NO_MEMORY);
2232 ret = ads_gen_mod(ads, dn_string, mods);
2233 TALLOC_FREE(dn_string);
2234 if (!ADS_ERR_OK(ret)) {
2235 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2237 ads_msgfree(ads, res);
2238 talloc_destroy(ctx);
2242 ads_msgfree(ads, res);
2243 talloc_destroy(ctx);
2248 * @brief Search for an element in a string array.
2250 * @param[in] el_array The string array to search.
2252 * @param[in] num_el The number of elements in the string array.
2254 * @param[in] el The string to search.
2256 * @return True if found, false if not.
2258 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2262 if (el_array == NULL || num_el == 0 || el == NULL) {
2266 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2269 cmp = strcasecmp_m(el_array[i], el);
2279 * @brief This gets the service principal names of an existing computer account.
2281 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2283 * @param[in] ads The ADS context to use.
2285 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2286 * identify the computer account.
2288 * @param[in] spn_array A pointer to store the array for SPNs.
2290 * @param[in] num_spns The number of principals stored in the array.
2292 * @return 0 on success, or a ADS error if a failure occurred.
2294 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2296 const char *machine_name,
2301 LDAPMessage *res = NULL;
2304 status = ads_find_machine_acct(ads,
2307 if (!ADS_ERR_OK(status)) {
2308 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2313 count = ads_count_replies(ads, res);
2315 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2319 *spn_array = ads_pull_strings(ads,
2322 "servicePrincipalName",
2324 if (*spn_array == NULL) {
2325 DEBUG(1, ("Host account for %s does not have service principal "
2328 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2333 ads_msgfree(ads, res);
2339 * This adds a service principal name to an existing computer account
2340 * (found by hostname) in AD.
2341 * @param ads An initialized ADS_STRUCT
2342 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2343 * @param spns An array or strings for the service principals to add,
2344 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2345 * @return 0 upon success, or non-zero if a failure occurs
2348 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2349 const char *machine_name,
2354 LDAPMessage *res = NULL;
2356 char *dn_string = NULL;
2357 const char **servicePrincipalName = spns;
2359 ret = ads_find_machine_acct(ads, &res, machine_name);
2360 if (!ADS_ERR_OK(ret)) {
2361 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2363 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2364 ads_msgfree(ads, res);
2368 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2369 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2370 ads_msgfree(ads, res);
2371 return ADS_ERROR(LDAP_NO_MEMORY);
2374 DEBUG(5,("ads_add_service_principal_name: INFO: "
2375 "Adding %s to host %s\n",
2376 spns[0] ? "N/A" : spns[0], machine_name));
2379 DEBUG(5,("ads_add_service_principal_name: INFO: "
2380 "Adding %s to host %s\n",
2381 spns[1] ? "N/A" : spns[1], machine_name));
2383 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2384 ret = ADS_ERROR(LDAP_NO_MEMORY);
2388 ret = ads_add_strlist(ctx,
2390 "servicePrincipalName",
2391 servicePrincipalName);
2392 if (!ADS_ERR_OK(ret)) {
2393 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2397 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2398 ret = ADS_ERROR(LDAP_NO_MEMORY);
2402 ret = ads_gen_mod(ads, dn_string, mods);
2403 if (!ADS_ERR_OK(ret)) {
2404 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2410 ads_msgfree(ads, res);
2414 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2417 uint32_t acct_ctrl = 0;
2420 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2428 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2430 const struct berval *machine_pw_val)
2434 TALLOC_CTX *frame = talloc_stackframe();
2435 uint32_t acct_control;
2436 char *control_str = NULL;
2437 const char *attrs[] = {
2441 LDAPMessage *res = NULL;
2444 dn = ads_get_dn(ads, frame, msg);
2446 ret = ADS_ERROR(LDAP_NO_MEMORY);
2450 acct_control = ads_get_acct_ctrl(ads, msg);
2451 if (acct_control == 0) {
2452 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2457 * Changing the password, disables the account. So we need to change the
2458 * userAccountControl flags to enable it again.
2460 mods = ads_init_mods(frame);
2462 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2466 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2468 ret = ads_gen_mod(ads, dn, mods);
2469 if (!ADS_ERR_OK(ret)) {
2475 * To activate the account, we need to disable and enable it.
2477 acct_control |= UF_ACCOUNTDISABLE;
2479 control_str = talloc_asprintf(frame, "%u", acct_control);
2480 if (control_str == NULL) {
2481 ret = ADS_ERROR(LDAP_NO_MEMORY);
2485 mods = ads_init_mods(frame);
2487 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2491 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2493 ret = ads_gen_mod(ads, dn, mods);
2494 if (!ADS_ERR_OK(ret)) {
2498 TALLOC_FREE(control_str);
2501 * Enable the account again.
2503 acct_control &= ~UF_ACCOUNTDISABLE;
2505 control_str = talloc_asprintf(frame, "%u", acct_control);
2506 if (control_str == NULL) {
2507 ret = ADS_ERROR(LDAP_NO_MEMORY);
2511 mods = ads_init_mods(frame);
2513 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2517 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2519 ret = ads_gen_mod(ads, dn, mods);
2520 if (!ADS_ERR_OK(ret)) {
2524 TALLOC_FREE(control_str);
2526 ret = ads_search_dn(ads, &res, dn, attrs);
2527 ads_msgfree(ads, res);
2536 * adds a machine account to the ADS server
2537 * @param ads An initialized ADS_STRUCT
2538 * @param machine_name - the NetBIOS machine name of this account.
2539 * @param account_type A number indicating the type of account to create
2540 * @param org_unit The LDAP path in which to place this account
2541 * @return 0 upon success, or non-zero otherwise
2544 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2545 const char *machine_name,
2546 const char *machine_password,
2547 const char *org_unit,
2548 uint32_t etype_list,
2549 const char *dns_domain_name)
2552 char *samAccountName = NULL;
2553 char *controlstr = NULL;
2554 TALLOC_CTX *ctx = NULL;
2556 char *machine_escaped = NULL;
2557 char *dns_hostname = NULL;
2558 char *new_dn = NULL;
2559 char *utf8_pw = NULL;
2560 size_t utf8_pw_len = 0;
2561 char *utf16_pw = NULL;
2562 size_t utf16_pw_len = 0;
2563 struct berval machine_pw_val;
2565 const char **spn_array = NULL;
2566 size_t num_spns = 0;
2567 const char *spn_prefix[] = {
2569 "RestrictedKrbHost",
2572 LDAPMessage *res = NULL;
2573 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2575 ctx = talloc_init("ads_add_machine_acct");
2577 return ADS_ERROR(LDAP_NO_MEMORY);
2580 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2581 if (machine_escaped == NULL) {
2582 ret = ADS_ERROR(LDAP_NO_MEMORY);
2586 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2587 if (utf8_pw == NULL) {
2588 ret = ADS_ERROR(LDAP_NO_MEMORY);
2591 utf8_pw_len = strlen(utf8_pw);
2593 ok = convert_string_talloc(ctx,
2594 CH_UTF8, CH_UTF16MUNGED,
2595 utf8_pw, utf8_pw_len,
2596 (void *)&utf16_pw, &utf16_pw_len);
2598 ret = ADS_ERROR(LDAP_NO_MEMORY);
2602 machine_pw_val = (struct berval) {
2604 .bv_len = utf16_pw_len,
2607 /* Check if the machine account already exists. */
2608 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2609 if (ADS_ERR_OK(ret)) {
2610 /* Change the machine account password */
2611 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2612 ads_msgfree(ads, res);
2616 ads_msgfree(ads, res);
2618 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2619 if (new_dn == NULL) {
2620 ret = ADS_ERROR(LDAP_NO_MEMORY);
2624 /* Create machine account */
2626 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2627 if (samAccountName == NULL) {
2628 ret = ADS_ERROR(LDAP_NO_MEMORY);
2632 dns_hostname = talloc_asprintf(ctx,
2636 if (dns_hostname == NULL) {
2637 ret = ADS_ERROR(LDAP_NO_MEMORY);
2641 /* Add dns_hostname SPNs */
2642 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2643 char *spn = talloc_asprintf(ctx,
2648 ret = ADS_ERROR(LDAP_NO_MEMORY);
2652 ok = add_string_to_array(spn_array,
2657 ret = ADS_ERROR(LDAP_NO_MEMORY);
2662 /* Add machine_name SPNs */
2663 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2664 char *spn = talloc_asprintf(ctx,
2669 ret = ADS_ERROR(LDAP_NO_MEMORY);
2673 ok = add_string_to_array(spn_array,
2678 ret = ADS_ERROR(LDAP_NO_MEMORY);
2683 /* Make sure to NULL terminate the array */
2684 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2685 if (spn_array == NULL) {
2686 ret = ADS_ERROR(LDAP_NO_MEMORY);
2689 spn_array[num_spns] = NULL;
2691 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2692 if (controlstr == NULL) {
2693 ret = ADS_ERROR(LDAP_NO_MEMORY);
2697 mods = ads_init_mods(ctx);
2699 ret = ADS_ERROR(LDAP_NO_MEMORY);
2703 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2704 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2705 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2706 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2707 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2708 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2710 ret = ads_gen_add(ads, new_dn, mods);
2713 SAFE_FREE(machine_escaped);
2714 talloc_destroy(ctx);
2720 * move a machine account to another OU on the ADS server
2721 * @param ads - An initialized ADS_STRUCT
2722 * @param machine_name - the NetBIOS machine name of this account.
2723 * @param org_unit - The LDAP path in which to place this account
2724 * @param moved - whether we moved the machine account (optional)
2725 * @return 0 upon success, or non-zero otherwise
2728 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2729 const char *org_unit, bool *moved)
2733 LDAPMessage *res = NULL;
2734 char *filter = NULL;
2735 char *computer_dn = NULL;
2737 char *computer_rdn = NULL;
2738 bool need_move = False;
2740 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2741 rc = ADS_ERROR(LDAP_NO_MEMORY);
2745 /* Find pre-existing machine */
2746 rc = ads_search(ads, &res, filter, NULL);
2747 if (!ADS_ERR_OK(rc)) {
2751 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2753 rc = ADS_ERROR(LDAP_NO_MEMORY);
2757 parent_dn = ads_parent_dn(computer_dn);
2758 if (strequal(parent_dn, org_unit)) {
2764 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2765 rc = ADS_ERROR(LDAP_NO_MEMORY);
2769 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2770 org_unit, 1, NULL, NULL);
2771 rc = ADS_ERROR(ldap_status);
2774 ads_msgfree(ads, res);
2776 TALLOC_FREE(computer_dn);
2777 SAFE_FREE(computer_rdn);
2779 if (!ADS_ERR_OK(rc)) {
2791 dump a binary result from ldap
2793 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2796 for (i=0; values[i]; i++) {
2798 printf("%s: ", field);
2799 for (j=0; j<values[i]->bv_len; j++) {
2800 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2806 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2809 for (i=0; values[i]; i++) {
2811 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2814 status = GUID_from_ndr_blob(&in, &guid);
2815 if (NT_STATUS_IS_OK(status)) {
2816 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2818 printf("%s: INVALID GUID\n", field);
2824 dump a sid result from ldap
2826 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2829 for (i=0; values[i]; i++) {
2832 struct dom_sid_buf tmp;
2833 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2834 values[i]->bv_len, &sid);
2838 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2843 dump ntSecurityDescriptor
2845 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2847 TALLOC_CTX *frame = talloc_stackframe();
2848 struct security_descriptor *psd;
2851 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2852 values[0]->bv_len, &psd);
2853 if (!NT_STATUS_IS_OK(status)) {
2854 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2855 nt_errstr(status)));
2861 ads_disp_sd(ads, talloc_tos(), psd);
2868 dump a string result from ldap
2870 static void dump_string(const char *field, char **values)
2873 for (i=0; values[i]; i++) {
2874 printf("%s: %s\n", field, values[i]);
2879 dump a field from LDAP on stdout
2883 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2888 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2890 {"objectGUID", False, dump_guid},
2891 {"netbootGUID", False, dump_guid},
2892 {"nTSecurityDescriptor", False, dump_sd},
2893 {"dnsRecord", False, dump_binary},
2894 {"objectSid", False, dump_sid},
2895 {"tokenGroups", False, dump_sid},
2896 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2897 {"tokengroupsGlobalandUniversal", False, dump_sid},
2898 {"mS-DS-CreatorSID", False, dump_sid},
2899 {"msExchMailboxGuid", False, dump_guid},
2904 if (!field) { /* must be end of an entry */
2909 for (i=0; handlers[i].name; i++) {
2910 if (strcasecmp_m(handlers[i].name, field) == 0) {
2911 if (!values) /* first time, indicate string or not */
2912 return handlers[i].string;
2913 handlers[i].handler(ads, field, (struct berval **) values);
2917 if (!handlers[i].name) {
2918 if (!values) /* first time, indicate string conversion */
2920 dump_string(field, (char **)values);
2926 * Dump a result from LDAP on stdout
2927 * used for debugging
2928 * @param ads connection to ads server
2929 * @param res Results to dump
2932 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2934 ads_process_results(ads, res, ads_dump_field, NULL);
2938 * Walk through results, calling a function for each entry found.
2939 * The function receives a field name, a berval * array of values,
2940 * and a data area passed through from the start. The function is
2941 * called once with null for field and values at the end of each
2943 * @param ads connection to ads server
2944 * @param res Results to process
2945 * @param fn Function for processing each result
2946 * @param data_area user-defined area to pass to function
2948 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2949 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2954 size_t converted_size;
2956 if (!(ctx = talloc_init("ads_process_results")))
2959 for (msg = ads_first_entry(ads, res); msg;
2960 msg = ads_next_entry(ads, msg)) {
2964 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2965 (LDAPMessage *)msg,&b);
2967 utf8_field=ldap_next_attribute(ads->ldap.ld,
2968 (LDAPMessage *)msg,b)) {
2969 struct berval **ber_vals;
2975 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2978 DEBUG(0,("ads_process_results: "
2979 "pull_utf8_talloc failed: %s\n",
2983 string = fn(ads, field, NULL, data_area);
2988 utf8_vals = ldap_get_values(ads->ldap.ld,
2989 (LDAPMessage *)msg, field);
2990 p = discard_const_p(const char *, utf8_vals);
2991 str_vals = ads_pull_strvals(ctx, p);
2992 fn(ads, field, (void **) str_vals, data_area);
2993 ldap_value_free(utf8_vals);
2995 ber_vals = ldap_get_values_len(ads->ldap.ld,
2996 (LDAPMessage *)msg, field);
2997 fn(ads, field, (void **) ber_vals, data_area);
2999 ldap_value_free_len(ber_vals);
3001 ldap_memfree(utf8_field);
3004 talloc_free_children(ctx);
3005 fn(ads, NULL, NULL, data_area); /* completed an entry */
3008 talloc_destroy(ctx);
3012 * count how many replies are in a LDAPMessage
3013 * @param ads connection to ads server
3014 * @param res Results to count
3015 * @return number of replies
3017 int ads_count_replies(ADS_STRUCT *ads, void *res)
3019 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3023 * pull the first entry from a ADS result
3024 * @param ads connection to ads server
3025 * @param res Results of search
3026 * @return first entry from result
3028 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3030 return ldap_first_entry(ads->ldap.ld, res);
3034 * pull the next entry from a ADS result
3035 * @param ads connection to ads server
3036 * @param res Results of search
3037 * @return next entry from result
3039 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3041 return ldap_next_entry(ads->ldap.ld, res);
3045 * pull the first message from a ADS result
3046 * @param ads connection to ads server
3047 * @param res Results of search
3048 * @return first message from result
3050 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3052 return ldap_first_message(ads->ldap.ld, res);
3056 * pull the next message from a ADS result
3057 * @param ads connection to ads server
3058 * @param res Results of search
3059 * @return next message from result
3061 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3063 return ldap_next_message(ads->ldap.ld, res);
3067 * pull a single string from a ADS result
3068 * @param ads connection to ads server
3069 * @param mem_ctx TALLOC_CTX to use for allocating result string
3070 * @param msg Results of search
3071 * @param field Attribute to retrieve
3072 * @return Result string in talloc context
3074 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3080 size_t converted_size;
3082 values = ldap_get_values(ads->ldap.ld, msg, field);
3086 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3091 ldap_value_free(values);
3096 * pull an array of strings from a ADS result
3097 * @param ads connection to ads server
3098 * @param mem_ctx TALLOC_CTX to use for allocating result string
3099 * @param msg Results of search
3100 * @param field Attribute to retrieve
3101 * @return Result strings in talloc context
3103 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3104 LDAPMessage *msg, const char *field,
3109 size_t i, converted_size;
3111 values = ldap_get_values(ads->ldap.ld, msg, field);
3115 *num_values = ldap_count_values(values);
3117 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3119 ldap_value_free(values);
3123 for (i=0;i<*num_values;i++) {
3124 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3127 ldap_value_free(values);
3133 ldap_value_free(values);
3138 * pull an array of strings from a ADS result
3139 * (handle large multivalue attributes with range retrieval)
3140 * @param ads connection to ads server
3141 * @param mem_ctx TALLOC_CTX to use for allocating result string
3142 * @param msg Results of search
3143 * @param field Attribute to retrieve
3144 * @param current_strings strings returned by a previous call to this function
3145 * @param next_attribute The next query should ask for this attribute
3146 * @param num_values How many values did we get this time?
3147 * @param more_values Are there more values to get?
3148 * @return Result strings in talloc context
3150 char **ads_pull_strings_range(ADS_STRUCT *ads,
3151 TALLOC_CTX *mem_ctx,
3152 LDAPMessage *msg, const char *field,
3153 char **current_strings,
3154 const char **next_attribute,
3155 size_t *num_strings,
3159 char *expected_range_attrib, *range_attr;
3160 BerElement *ptr = NULL;
3163 size_t num_new_strings;
3164 unsigned long int range_start;
3165 unsigned long int range_end;
3167 /* we might have been given the whole lot anyway */
3168 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3169 *more_strings = False;
3173 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3175 /* look for Range result */
3176 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3178 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3179 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3180 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3188 /* nothing here - this field is just empty */
3189 *more_strings = False;
3193 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3194 &range_start, &range_end) == 2) {
3195 *more_strings = True;
3197 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3198 &range_start) == 1) {
3199 *more_strings = False;
3201 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
3203 ldap_memfree(range_attr);
3204 *more_strings = False;
3209 if ((*num_strings) != range_start) {
3210 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3211 " - aborting range retrieval\n",
3212 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3213 ldap_memfree(range_attr);
3214 *more_strings = False;
3218 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3220 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3221 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3222 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3223 range_attr, (unsigned long int)range_end - range_start + 1,
3224 (unsigned long int)num_new_strings));
3225 ldap_memfree(range_attr);
3226 *more_strings = False;
3230 strings = talloc_realloc(mem_ctx, current_strings, char *,
3231 *num_strings + num_new_strings);
3233 if (strings == NULL) {
3234 ldap_memfree(range_attr);
3235 *more_strings = False;
3239 if (new_strings && num_new_strings) {
3240 memcpy(&strings[*num_strings], new_strings,
3241 sizeof(*new_strings) * num_new_strings);
3244 (*num_strings) += num_new_strings;
3246 if (*more_strings) {
3247 *next_attribute = talloc_asprintf(mem_ctx,
3252 if (!*next_attribute) {
3253 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3254 ldap_memfree(range_attr);
3255 *more_strings = False;
3260 ldap_memfree(range_attr);
3266 * pull a single uint32_t from a ADS result
3267 * @param ads connection to ads server
3268 * @param msg Results of search
3269 * @param field Attribute to retrieve
3270 * @param v Pointer to int to store result
3271 * @return boolean indicating success
3273 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3278 values = ldap_get_values(ads->ldap.ld, msg, field);
3282 ldap_value_free(values);
3286 *v = atoi(values[0]);
3287 ldap_value_free(values);
3292 * pull a single objectGUID from an ADS result
3293 * @param ads connection to ADS server
3294 * @param msg results of search
3295 * @param guid 37-byte area to receive text guid
3296 * @return boolean indicating success
3298 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3303 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3308 status = GUID_from_ndr_blob(&blob, guid);
3309 talloc_free(blob.data);
3310 return NT_STATUS_IS_OK(status);
3315 * pull a single struct dom_sid from a ADS result
3316 * @param ads connection to ads server
3317 * @param msg Results of search
3318 * @param field Attribute to retrieve
3319 * @param sid Pointer to sid to store result
3320 * @return boolean indicating success
3322 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3323 struct dom_sid *sid)
3325 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3329 * pull an array of struct dom_sids from a ADS result
3330 * @param ads connection to ads server
3331 * @param mem_ctx TALLOC_CTX for allocating sid array
3332 * @param msg Results of search
3333 * @param field Attribute to retrieve
3334 * @param sids pointer to sid array to allocate
3335 * @return the count of SIDs pulled
3337 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3338 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3340 struct berval **values;
3343 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3348 for (i=0; values[i]; i++)
3352 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3354 ldap_value_free_len(values);
3362 for (i=0; values[i]; i++) {
3364 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3365 values[i]->bv_len, &(*sids)[count]);
3367 struct dom_sid_buf buf;
3368 DBG_DEBUG("pulling SID: %s\n",
3369 dom_sid_str_buf(&(*sids)[count], &buf));
3374 ldap_value_free_len(values);
3379 * pull a struct security_descriptor from a ADS result
3380 * @param ads connection to ads server
3381 * @param mem_ctx TALLOC_CTX for allocating sid array
3382 * @param msg Results of search
3383 * @param field Attribute to retrieve
3384 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3385 * @return boolean indicating success
3387 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3388 LDAPMessage *msg, const char *field,
3389 struct security_descriptor **sd)
3391 struct berval **values;
3394 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3396 if (!values) return false;
3400 status = unmarshall_sec_desc(mem_ctx,
3401 (uint8_t *)values[0]->bv_val,
3402 values[0]->bv_len, sd);
3403 if (!NT_STATUS_IS_OK(status)) {
3404 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3405 nt_errstr(status)));
3410 ldap_value_free_len(values);
3415 * in order to support usernames longer than 21 characters we need to
3416 * use both the sAMAccountName and the userPrincipalName attributes
3417 * It seems that not all users have the userPrincipalName attribute set
3419 * @param ads connection to ads server
3420 * @param mem_ctx TALLOC_CTX for allocating sid array
3421 * @param msg Results of search
3422 * @return the username
3424 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3430 /* lookup_name() only works on the sAMAccountName to
3431 returning the username portion of userPrincipalName
3432 breaks winbindd_getpwnam() */
3434 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3435 if (ret && (p = strchr_m(ret, '@'))) {
3440 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3445 * find the update serial number - this is the core of the ldap cache
3446 * @param ads connection to ads server
3447 * @param ads connection to ADS server
3448 * @param usn Pointer to retrieved update serial number
3449 * @return status of search
3451 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3453 const char *attrs[] = {"highestCommittedUSN", NULL};
3457 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3458 if (!ADS_ERR_OK(status))
3461 if (ads_count_replies(ads, res) != 1) {
3462 ads_msgfree(ads, res);
3463 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3466 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3467 ads_msgfree(ads, res);
3468 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3471 ads_msgfree(ads, res);
3475 /* parse a ADS timestring - typical string is
3476 '20020917091222.0Z0' which means 09:12.22 17th September
3478 static time_t ads_parse_time(const char *str)
3484 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3485 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3486 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3495 /********************************************************************
3496 ********************************************************************/
3498 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3500 const char *attrs[] = {"currentTime", NULL};
3504 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3505 ADS_STRUCT *ads_s = ads;
3507 /* establish a new ldap tcp session if necessary */
3509 if ( !ads->ldap.ld ) {
3511 * ADS_STRUCT may be being reused after a
3512 * DC lookup, so ads->ldap.ss may already have a
3513 * good address. If not, re-initialize the passed-in
3514 * ADS_STRUCT with the given server.XXXX parameters.
3516 * Note that this doesn't depend on
3517 * ads->server.ldap_server != NULL,
3518 * as the case where ads->server.ldap_server==NULL and
3519 * ads->ldap.ss != zero_address is precisely the DC
3520 * lookup case where ads->ldap.ss was found by going
3521 * through ads_find_dc() again we want to avoid repeating.
3523 if (is_zero_addr(&ads->ldap.ss)) {
3524 ads_s = ads_init(tmp_ctx,
3526 ads->server.workgroup,
3527 ads->server.ldap_server,
3529 if (ads_s == NULL) {
3530 status = ADS_ERROR(LDAP_NO_MEMORY);
3536 * Reset ads->config.flags as it can contain the flags
3537 * returned by the previous CLDAP ping when reusing the struct.
3539 ads_s->config.flags = 0;
3541 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3542 status = ads_connect( ads_s );
3543 if ( !ADS_ERR_OK(status))
3547 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3548 if (!ADS_ERR_OK(status)) {
3552 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3554 ads_msgfree(ads_s, res);
3555 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3559 /* but save the time and offset in the original ADS_STRUCT */
3561 ads->config.current_time = ads_parse_time(timestr);
3563 if (ads->config.current_time != 0) {
3564 ads->auth.time_offset = ads->config.current_time - time(NULL);
3565 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3568 ads_msgfree(ads, res);
3570 status = ADS_SUCCESS;
3573 TALLOC_FREE(tmp_ctx);
3578 /********************************************************************
3579 ********************************************************************/
3581 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3583 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3584 const char *attrs[] = {"domainFunctionality", NULL};
3587 ADS_STRUCT *ads_s = ads;
3589 *val = DS_DOMAIN_FUNCTION_2000;
3591 /* establish a new ldap tcp session if necessary */
3593 if ( !ads->ldap.ld ) {
3595 * ADS_STRUCT may be being reused after a
3596 * DC lookup, so ads->ldap.ss may already have a
3597 * good address. If not, re-initialize the passed-in
3598 * ADS_STRUCT with the given server.XXXX parameters.
3600 * Note that this doesn't depend on
3601 * ads->server.ldap_server != NULL,
3602 * as the case where ads->server.ldap_server==NULL and
3603 * ads->ldap.ss != zero_address is precisely the DC
3604 * lookup case where ads->ldap.ss was found by going
3605 * through ads_find_dc() again we want to avoid repeating.
3607 if (is_zero_addr(&ads->ldap.ss)) {
3608 ads_s = ads_init(tmp_ctx,
3610 ads->server.workgroup,
3611 ads->server.ldap_server,
3613 if (ads_s == NULL ) {
3614 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3620 * Reset ads->config.flags as it can contain the flags
3621 * returned by the previous CLDAP ping when reusing the struct.
3623 ads_s->config.flags = 0;
3625 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3626 status = ads_connect( ads_s );
3627 if ( !ADS_ERR_OK(status))
3631 /* If the attribute does not exist assume it is a Windows 2000
3632 functional domain */
3634 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3635 if (!ADS_ERR_OK(status)) {
3636 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3637 status = ADS_SUCCESS;
3642 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3643 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3645 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3648 ads_msgfree(ads_s, res);
3651 TALLOC_FREE(tmp_ctx);
3657 * find the domain sid for our domain
3658 * @param ads connection to ads server
3659 * @param sid Pointer to domain sid
3660 * @return status of search
3662 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3664 const char *attrs[] = {"objectSid", NULL};
3668 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3670 if (!ADS_ERR_OK(rc)) return rc;
3671 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3672 ads_msgfree(ads, res);
3673 return ADS_ERROR_SYSTEM(ENOENT);
3675 ads_msgfree(ads, res);
3681 * find our site name
3682 * @param ads connection to ads server
3683 * @param mem_ctx Pointer to talloc context
3684 * @param site_name Pointer to the sitename
3685 * @return status of search
3687 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3691 const char *dn, *service_name;
3692 const char *attrs[] = { "dsServiceName", NULL };
3694 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3695 if (!ADS_ERR_OK(status)) {
3699 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3700 if (service_name == NULL) {
3701 ads_msgfree(ads, res);
3702 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3705 ads_msgfree(ads, res);
3707 /* go up three levels */
3708 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3710 return ADS_ERROR(LDAP_NO_MEMORY);
3713 *site_name = talloc_strdup(mem_ctx, dn);
3714 if (*site_name == NULL) {
3715 return ADS_ERROR(LDAP_NO_MEMORY);
3720 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3725 * find the site dn where a machine resides
3726 * @param ads connection to ads server
3727 * @param mem_ctx Pointer to talloc context
3728 * @param computer_name name of the machine
3729 * @param site_name Pointer to the sitename
3730 * @return status of search
3732 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3736 const char *parent, *filter;
3737 char *config_context = NULL;
3740 /* shortcut a query */
3741 if (strequal(computer_name, ads->config.ldap_server_name)) {
3742 return ads_site_dn(ads, mem_ctx, site_dn);
3745 status = ads_config_path(ads, mem_ctx, &config_context);
3746 if (!ADS_ERR_OK(status)) {
3750 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3751 if (filter == NULL) {
3752 return ADS_ERROR(LDAP_NO_MEMORY);
3755 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3756 filter, NULL, &res);
3757 if (!ADS_ERR_OK(status)) {
3761 if (ads_count_replies(ads, res) != 1) {
3762 ads_msgfree(ads, res);
3763 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3766 dn = ads_get_dn(ads, mem_ctx, res);
3768 ads_msgfree(ads, res);
3769 return ADS_ERROR(LDAP_NO_MEMORY);
3772 /* go up three levels */
3773 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3774 if (parent == NULL) {
3775 ads_msgfree(ads, res);
3777 return ADS_ERROR(LDAP_NO_MEMORY);
3780 *site_dn = talloc_strdup(mem_ctx, parent);
3781 if (*site_dn == NULL) {
3782 ads_msgfree(ads, res);
3784 return ADS_ERROR(LDAP_NO_MEMORY);
3788 ads_msgfree(ads, res);
3794 * get the upn suffixes for a domain
3795 * @param ads connection to ads server
3796 * @param mem_ctx Pointer to talloc context
3797 * @param suffixes Pointer to an array of suffixes
3798 * @param num_suffixes Pointer to the number of suffixes
3799 * @return status of search
3801 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3806 char *config_context = NULL;
3807 const char *attrs[] = { "uPNSuffixes", NULL };
3809 status = ads_config_path(ads, mem_ctx, &config_context);
3810 if (!ADS_ERR_OK(status)) {
3814 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3816 return ADS_ERROR(LDAP_NO_MEMORY);
3819 status = ads_search_dn(ads, &res, base, attrs);
3820 if (!ADS_ERR_OK(status)) {
3824 if (ads_count_replies(ads, res) != 1) {
3825 ads_msgfree(ads, res);
3826 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3829 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3830 if ((*suffixes) == NULL) {
3831 ads_msgfree(ads, res);
3832 return ADS_ERROR(LDAP_NO_MEMORY);
3835 ads_msgfree(ads, res);
3841 * get the joinable ous for a domain
3842 * @param ads connection to ads server
3843 * @param mem_ctx Pointer to talloc context
3844 * @param ous Pointer to an array of ous
3845 * @param num_ous Pointer to the number of ous
3846 * @return status of search
3848 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3849 TALLOC_CTX *mem_ctx,
3854 LDAPMessage *res = NULL;
3855 LDAPMessage *msg = NULL;
3856 const char *attrs[] = { "dn", NULL };
3859 status = ads_search(ads, &res,
3860 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3862 if (!ADS_ERR_OK(status)) {
3866 count = ads_count_replies(ads, res);
3868 ads_msgfree(ads, res);
3869 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3872 for (msg = ads_first_entry(ads, res); msg;
3873 msg = ads_next_entry(ads, msg)) {
3874 const char **p = discard_const_p(const char *, *ous);
3877 dn = ads_get_dn(ads, talloc_tos(), msg);
3879 ads_msgfree(ads, res);
3880 return ADS_ERROR(LDAP_NO_MEMORY);
3883 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3885 ads_msgfree(ads, res);
3886 return ADS_ERROR(LDAP_NO_MEMORY);
3890 *ous = discard_const_p(char *, p);
3893 ads_msgfree(ads, res);
3900 * pull a struct dom_sid from an extended dn string
3901 * @param mem_ctx TALLOC_CTX
3902 * @param extended_dn string
3903 * @param flags string type of extended_dn
3904 * @param sid pointer to a struct dom_sid
3905 * @return NT_STATUS_OK on success,
3906 * NT_INVALID_PARAMETER on error,
3907 * NT_STATUS_NOT_FOUND if no SID present
3909 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3910 const char *extended_dn,
3911 enum ads_extended_dn_flags flags,
3912 struct dom_sid *sid)
3917 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3920 /* otherwise extended_dn gets stripped off */
3921 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3922 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3925 * ADS_EXTENDED_DN_HEX_STRING:
3926 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3928 * ADS_EXTENDED_DN_STRING (only with w2k3):
3929 * <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
3931 * Object with no SID, such as an Exchange Public Folder
3932 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3935 p = strchr(dn, ';');
3937 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3940 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3941 DEBUG(5,("No SID present in extended dn\n"));
3942 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3945 p += strlen(";<SID=");
3949 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3954 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3958 case ADS_EXTENDED_DN_STRING:
3959 if (!string_to_sid(sid, p)) {
3960 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3963 case ADS_EXTENDED_DN_HEX_STRING: {
3968 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3970 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3973 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3975 DEBUG(10,("failed to parse sid\n"));
3976 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3981 DEBUG(10,("unknown extended dn format\n"));
3982 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3985 return ADS_ERROR_NT(NT_STATUS_OK);
3988 /********************************************************************
3989 ********************************************************************/
3991 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3993 LDAPMessage *res = NULL;
3998 status = ads_find_machine_acct(ads, &res, machine_name);
3999 if (!ADS_ERR_OK(status)) {
4000 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4001 lp_netbios_name()));
4005 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4006 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4010 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4011 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4015 ads_msgfree(ads, res);
4020 /********************************************************************
4021 ********************************************************************/
4023 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4024 LDAPMessage *msg, size_t *num_values)
4026 const char *field = "msDS-AdditionalDnsHostName";
4027 struct berval **values = NULL;
4029 size_t i, converted_size;
4032 * Windows DC implicitly adds a short name for each FQDN added to
4033 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4034 * suffix "\0$" which we should ignore (see bug #14406).
4037 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4038 if (values == NULL) {
4042 *num_values = ldap_count_values_len(values);
4044 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4046 ldap_value_free_len(values);
4050 for (i = 0; i < *num_values; i++) {
4052 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4054 strnlen(values[i]->bv_val,
4056 &ret[i], &converted_size)) {
4057 ldap_value_free_len(values);
4063 ldap_value_free_len(values);
4067 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4069 const char *machine_name,
4070 char ***hostnames_array,
4071 size_t *num_hostnames)
4074 LDAPMessage *res = NULL;
4077 status = ads_find_machine_acct(ads,
4080 if (!ADS_ERR_OK(status)) {
4081 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4086 count = ads_count_replies(ads, res);
4088 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4092 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4093 if (*hostnames_array == NULL) {
4094 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4096 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4101 ads_msgfree(ads, res);
4106 /********************************************************************
4107 ********************************************************************/
4109 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4111 LDAPMessage *res = NULL;
4116 status = ads_find_machine_acct(ads, &res, machine_name);
4117 if (!ADS_ERR_OK(status)) {
4118 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4119 lp_netbios_name()));
4123 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4124 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4128 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4129 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4133 ads_msgfree(ads, res);
4138 /********************************************************************
4139 ********************************************************************/
4141 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4143 LDAPMessage *res = NULL;
4149 status = ads_find_machine_acct(ads, &res, machine_name);
4150 if (!ADS_ERR_OK(status)) {
4151 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4152 lp_netbios_name()));
4156 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4157 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4161 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4162 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4166 ads_msgfree(ads, res);
4168 ok = (strlen(name) > 0);
4176 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4179 * Join a machine to a realm
4180 * Creates the machine account and sets the machine password
4181 * @param ads connection to ads server
4182 * @param machine name of host to add
4183 * @param org_unit Organizational unit to place machine in
4184 * @return status of join
4186 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4187 uint32_t account_type, const char *org_unit)
4190 LDAPMessage *res = NULL;
4193 /* machine name must be lowercase */
4194 machine = SMB_STRDUP(machine_name);
4195 strlower_m(machine);
4198 status = ads_find_machine_acct(ads, (void **)&res, machine);
4199 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4200 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4201 status = ads_leave_realm(ads, machine);
4202 if (!ADS_ERR_OK(status)) {
4203 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4204 machine, ads->config.realm));
4209 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4210 if (!ADS_ERR_OK(status)) {
4211 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4216 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4217 if (!ADS_ERR_OK(status)) {
4218 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4224 ads_msgfree(ads, res);
4231 * Delete a machine from the realm
4232 * @param ads connection to ads server
4233 * @param hostname Machine to remove
4234 * @return status of delete
4236 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4241 char *hostnameDN, *host;
4243 LDAPControl ldap_control;
4244 LDAPControl * pldap_control[2] = {NULL, NULL};
4246 pldap_control[0] = &ldap_control;
4247 memset(&ldap_control, 0, sizeof(LDAPControl));
4248 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4250 /* hostname must be lowercase */
4251 host = SMB_STRDUP(hostname);
4252 if (!strlower_m(host)) {
4254 return ADS_ERROR_SYSTEM(EINVAL);
4257 status = ads_find_machine_acct(ads, &res, host);
4258 if (!ADS_ERR_OK(status)) {
4259 DEBUG(0, ("Host account for %s does not exist.\n", host));
4264 msg = ads_first_entry(ads, res);
4267 return ADS_ERROR_SYSTEM(ENOENT);
4270 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4271 if (hostnameDN == NULL) {
4273 return ADS_ERROR_SYSTEM(ENOENT);
4276 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4278 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4280 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4283 if (rc != LDAP_SUCCESS) {
4284 const char *attrs[] = { "cn", NULL };
4285 LDAPMessage *msg_sub;
4287 /* we only search with scope ONE, we do not expect any further
4288 * objects to be created deeper */
4290 status = ads_do_search_retry(ads, hostnameDN,
4291 LDAP_SCOPE_ONELEVEL,
4292 "(objectclass=*)", attrs, &res);
4294 if (!ADS_ERR_OK(status)) {
4296 TALLOC_FREE(hostnameDN);
4300 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4301 msg_sub = ads_next_entry(ads, msg_sub)) {
4305 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4307 TALLOC_FREE(hostnameDN);
4308 return ADS_ERROR(LDAP_NO_MEMORY);
4311 status = ads_del_dn(ads, dn);
4312 if (!ADS_ERR_OK(status)) {
4313 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4316 TALLOC_FREE(hostnameDN);
4323 /* there should be no subordinate objects anymore */
4324 status = ads_do_search_retry(ads, hostnameDN,
4325 LDAP_SCOPE_ONELEVEL,
4326 "(objectclass=*)", attrs, &res);
4328 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4330 TALLOC_FREE(hostnameDN);
4334 /* delete hostnameDN now */
4335 status = ads_del_dn(ads, hostnameDN);
4336 if (!ADS_ERR_OK(status)) {
4338 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4339 TALLOC_FREE(hostnameDN);
4344 TALLOC_FREE(hostnameDN);
4346 status = ads_find_machine_acct(ads, &res, host);
4347 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4348 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4349 DEBUG(3, ("Failed to remove host account.\n"));
4359 * pull all token-sids from an LDAP dn
4360 * @param ads connection to ads server
4361 * @param mem_ctx TALLOC_CTX for allocating sid array
4362 * @param dn of LDAP object
4363 * @param user_sid pointer to struct dom_sid (objectSid)
4364 * @param primary_group_sid pointer to struct dom_sid (self composed)
4365 * @param sids pointer to sid array to allocate
4366 * @param num_sids counter of SIDs pulled
4367 * @return status of token query
4369 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4370 TALLOC_CTX *mem_ctx,
4372 struct dom_sid *user_sid,
4373 struct dom_sid *primary_group_sid,
4374 struct dom_sid **sids,
4378 LDAPMessage *res = NULL;
4380 size_t tmp_num_sids;
4381 struct dom_sid *tmp_sids;
4382 struct dom_sid tmp_user_sid;
4383 struct dom_sid tmp_primary_group_sid;
4385 const char *attrs[] = {
4392 status = ads_search_retry_dn(ads, &res, dn, attrs);
4393 if (!ADS_ERR_OK(status)) {
4397 count = ads_count_replies(ads, res);
4399 ads_msgfree(ads, res);
4400 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4403 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4404 ads_msgfree(ads, res);
4405 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4408 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4409 ads_msgfree(ads, res);
4410 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4414 /* hack to compose the primary group sid without knowing the
4417 struct dom_sid domsid;
4419 sid_copy(&domsid, &tmp_user_sid);
4421 if (!sid_split_rid(&domsid, NULL)) {
4422 ads_msgfree(ads, res);
4423 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4426 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4427 ads_msgfree(ads, res);
4428 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4432 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4434 if (tmp_num_sids == 0 || !tmp_sids) {
4435 ads_msgfree(ads, res);
4436 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4440 *num_sids = tmp_num_sids;
4448 *user_sid = tmp_user_sid;
4451 if (primary_group_sid) {
4452 *primary_group_sid = tmp_primary_group_sid;
4455 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4457 ads_msgfree(ads, res);
4458 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4462 * Find a sAMAccoutName in LDAP
4463 * @param ads connection to ads server
4464 * @param mem_ctx TALLOC_CTX for allocating sid array
4465 * @param samaccountname to search
4466 * @param uac_ret uint32_t pointer userAccountControl attribute value
4467 * @param dn_ret pointer to dn
4468 * @return status of token query
4470 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4471 TALLOC_CTX *mem_ctx,
4472 const char *samaccountname,
4474 const char **dn_ret)
4477 const char *attrs[] = { "userAccountControl", NULL };
4479 LDAPMessage *res = NULL;
4483 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4485 if (filter == NULL) {
4486 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4490 status = ads_do_search_all(ads, ads->config.bind_path,
4492 filter, attrs, &res);
4494 if (!ADS_ERR_OK(status)) {
4498 if (ads_count_replies(ads, res) != 1) {
4499 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4503 dn = ads_get_dn(ads, talloc_tos(), res);
4505 status = ADS_ERROR(LDAP_NO_MEMORY);
4509 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4510 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4519 *dn_ret = talloc_strdup(mem_ctx, dn);
4521 status = ADS_ERROR(LDAP_NO_MEMORY);
4527 ads_msgfree(ads, res);
4533 * find our configuration path
4534 * @param ads connection to ads server
4535 * @param mem_ctx Pointer to talloc context
4536 * @param config_path Pointer to the config path
4537 * @return status of search
4539 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4540 TALLOC_CTX *mem_ctx,
4544 LDAPMessage *res = NULL;
4545 const char *config_context = NULL;
4546 const char *attrs[] = { "configurationNamingContext", NULL };
4548 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4549 "(objectclass=*)", attrs, &res);
4550 if (!ADS_ERR_OK(status)) {
4554 config_context = ads_pull_string(ads, mem_ctx, res,
4555 "configurationNamingContext");
4556 ads_msgfree(ads, res);
4557 if (!config_context) {
4558 return ADS_ERROR(LDAP_NO_MEMORY);
4562 *config_path = talloc_strdup(mem_ctx, config_context);
4563 if (!*config_path) {
4564 return ADS_ERROR(LDAP_NO_MEMORY);
4568 return ADS_ERROR(LDAP_SUCCESS);
4572 * find the displayName of an extended right
4573 * @param ads connection to ads server
4574 * @param config_path The config path
4575 * @param mem_ctx Pointer to talloc context
4576 * @param GUID struct of the rightsGUID
4577 * @return status of search
4579 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4580 const char *config_path,
4581 TALLOC_CTX *mem_ctx,
4582 const struct GUID *rights_guid)
4585 LDAPMessage *res = NULL;
4587 const char *attrs[] = { "displayName", NULL };
4588 const char *result = NULL;
4591 if (!ads || !mem_ctx || !rights_guid) {
4595 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4596 GUID_string(mem_ctx, rights_guid));
4601 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4606 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4608 if (!ADS_ERR_OK(rc)) {
4612 if (ads_count_replies(ads, res) != 1) {
4616 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4619 ads_msgfree(ads, res);
4624 * verify or build and verify an account ou
4625 * @param mem_ctx Pointer to talloc context
4626 * @param ads connection to ads server
4628 * @return status of search
4631 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4633 const char **account_ou)
4639 if (account_ou == NULL) {
4640 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4643 if (*account_ou != NULL) {
4644 exploded_dn = ldap_explode_dn(*account_ou, 0);
4646 ldap_value_free(exploded_dn);
4651 ou_string = ads_ou_string(ads, *account_ou);
4653 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4656 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4657 ads->config.bind_path);
4658 SAFE_FREE(ou_string);
4661 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4664 exploded_dn = ldap_explode_dn(name, 0);
4666 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4668 ldap_value_free(exploded_dn);