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 DBG_DEBUG("ldap_search: base => [%s], filter => [%s], scope => [%d]\n",
162 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
168 timeout_ptr = &timeout;
170 /* Setup alarm timeout. */
171 CatchSignal(SIGALRM, gotalarm_sig);
172 /* Make the alarm time one second beyond
173 the timeout we're setting for the
174 remote search timeout, to allow that
175 to fire in preference. */
177 /* End setup timeout. */
181 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
182 attrsonly, sctrls, cctrls, timeout_ptr,
186 /* Teardown alarm timeout. */
187 CatchSignal(SIGALRM, SIG_IGN);
192 return LDAP_TIMELIMIT_EXCEEDED;
195 * A bug in OpenLDAP means ldap_search_ext_s can return
196 * LDAP_SUCCESS but with a NULL res pointer. Cope with
197 * this. See bug #6279 for details. JRA.
201 return LDAP_TIMELIMIT_EXCEEDED;
207 /**********************************************
208 Do client and server sitename match ?
209 **********************************************/
211 bool ads_sitename_match(ADS_STRUCT *ads)
213 if (ads->config.server_site_name == NULL &&
214 ads->config.client_site_name == NULL ) {
215 DEBUG(10,("ads_sitename_match: both null\n"));
218 if (ads->config.server_site_name &&
219 ads->config.client_site_name &&
220 strequal(ads->config.server_site_name,
221 ads->config.client_site_name)) {
222 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
225 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
226 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
227 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
231 /**********************************************
232 Is this the closest DC ?
233 **********************************************/
235 bool ads_closest_dc(ADS_STRUCT *ads)
237 if (ads->config.flags & NBT_SERVER_CLOSEST) {
238 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
242 /* not sure if this can ever happen */
243 if (ads_sitename_match(ads)) {
244 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
248 if (ads->config.client_site_name == NULL) {
249 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
253 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
254 ads->config.ldap_server_name));
259 static bool ads_fill_cldap_reply(ADS_STRUCT *ads,
261 const struct sockaddr_storage *ss,
262 const struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply)
264 TALLOC_CTX *frame = talloc_stackframe();
266 char addr[INET6_ADDRSTRLEN];
270 print_sockaddr(addr, sizeof(addr), ss);
272 /* Check the CLDAP reply flags */
274 if (!(cldap_reply->server_type & NBT_SERVER_LDAP)) {
275 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
281 /* Fill in the ads->config values */
283 ADS_TALLOC_CONST_FREE(ads->config.workgroup);
284 ADS_TALLOC_CONST_FREE(ads->config.realm);
285 ADS_TALLOC_CONST_FREE(ads->config.bind_path);
286 ADS_TALLOC_CONST_FREE(ads->config.ldap_server_name);
287 ADS_TALLOC_CONST_FREE(ads->config.server_site_name);
288 ADS_TALLOC_CONST_FREE(ads->config.client_site_name);
290 if (!check_cldap_reply_required_flags(cldap_reply->server_type,
291 ads->config.flags)) {
296 ads->config.ldap_server_name = talloc_strdup(ads,
297 cldap_reply->pdc_dns_name);
298 if (ads->config.ldap_server_name == NULL) {
299 DBG_WARNING("Out of memory\n");
304 ads->config.workgroup = talloc_strdup(ads, cldap_reply->domain_name);
305 if (ads->config.workgroup == NULL) {
306 DBG_WARNING("Out of memory\n");
311 ads->config.realm = talloc_asprintf_strupper_m(ads,
313 cldap_reply->dns_domain);
314 if (ads->config.realm == NULL) {
315 DBG_WARNING("Out of memory\n");
320 status = ads_build_dn(ads->config.realm, ads, &dn);
321 if (!ADS_ERR_OK(status)) {
322 DBG_DEBUG("Failed to build bind path: %s\n",
327 ads->config.bind_path = dn;
329 if (*cldap_reply->server_site) {
330 ads->config.server_site_name =
331 talloc_strdup(ads, cldap_reply->server_site);
332 if (ads->config.server_site_name == NULL) {
333 DBG_WARNING("Out of memory\n");
339 if (*cldap_reply->client_site) {
340 ads->config.client_site_name =
341 talloc_strdup(ads, cldap_reply->client_site);
342 if (ads->config.client_site_name == NULL) {
343 DBG_WARNING("Out of memory\n");
349 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
352 /* Store our site name. */
353 sitename_store(cldap_reply->domain_name, cldap_reply->client_site);
354 sitename_store(cldap_reply->dns_domain, cldap_reply->client_site);
356 /* Leave this until last so that the flags are not clobbered */
357 ads->config.flags = cldap_reply->server_type;
368 try a connection to a given ldap server, returning True and setting the servers IP
369 in the ads struct if successful
371 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
372 struct sockaddr_storage *ss)
374 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply = {};
375 TALLOC_CTX *frame = talloc_stackframe();
377 char addr[INET6_ADDRSTRLEN] = { 0, };
384 print_sockaddr(addr, sizeof(addr), ss);
386 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
387 addr, ads->server.realm);
389 ok = ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply);
391 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
392 addr, ads->server.realm);
397 ok = ads_fill_cldap_reply(ads, gc, ss, &cldap_reply);
399 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
400 addr, ads->server.realm);
409 /**********************************************************************
410 send a cldap ping to list of servers, one at a time, until one of
411 them answers it's an ldap server. Record success in the ADS_STRUCT.
412 Take note of and update negative connection cache.
413 **********************************************************************/
415 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
417 struct samba_sockaddr *sa_list,
420 TALLOC_CTX *frame = talloc_stackframe();
421 struct timeval endtime = timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
422 uint32_t nt_version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
423 struct tsocket_address **ts_list = NULL;
424 const struct tsocket_address * const *ts_list_const = NULL;
425 struct samba_sockaddr **req_sa_list = NULL;
426 struct netlogon_samlogon_response **responses = NULL;
427 size_t num_requests = 0;
433 ts_list = talloc_zero_array(frame,
434 struct tsocket_address *,
436 if (ts_list == NULL) {
438 return NT_STATUS_NO_MEMORY;
441 req_sa_list = talloc_zero_array(frame,
442 struct samba_sockaddr *,
444 if (req_sa_list == NULL) {
446 return NT_STATUS_NO_MEMORY;
451 * The retry loop is bound by the timeout
456 for (i = 0; i < count; i++) {
457 char server[INET6_ADDRSTRLEN];
460 if (is_zero_addr(&sa_list[i].u.ss)) {
464 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
466 status = check_negative_conn_cache(domain, server);
467 if (!NT_STATUS_IS_OK(status)) {
471 ret = tsocket_address_inet_from_strings(ts_list, "ip",
473 &ts_list[num_requests]);
475 status = map_nt_error_from_unix(errno);
476 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
477 server, nt_errstr(status));
482 req_sa_list[num_requests] = &sa_list[i];
486 DBG_DEBUG("Try to create %zu netlogon connections for domain '%s' "
487 "(provided count of addresses was %zu).\n",
492 if (num_requests == 0) {
493 status = NT_STATUS_NO_LOGON_SERVERS;
494 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
495 domain, num_requests, count, nt_errstr(status));
500 ts_list_const = (const struct tsocket_address * const *)ts_list;
502 status = cldap_multi_netlogon(frame,
503 ts_list_const, num_requests,
504 ads->server.realm, NULL,
506 1, endtime, &responses);
507 if (!NT_STATUS_IS_OK(status)) {
508 DBG_WARNING("cldap_multi_netlogon(realm=%s, num_requests=%zu) "
509 "for count[%zu] - %s\n",
514 return NT_STATUS_NO_LOGON_SERVERS;
517 for (i = 0; i < num_requests; i++) {
518 struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply = NULL;
519 char server[INET6_ADDRSTRLEN];
521 if (responses[i] == NULL) {
525 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
527 if (responses[i]->ntver != NETLOGON_NT_VERSION_5EX) {
528 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
530 responses[i]->ntver, server);
534 cldap_reply = &responses[i]->data.nt5_ex;
536 /* Returns ok only if it matches the correct server type */
537 ok = ads_fill_cldap_reply(ads,
539 &req_sa_list[i]->u.ss,
542 DBG_DEBUG("realm[%s]: selected %s => %s\n",
544 server, cldap_reply->pdc_dns_name);
545 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
546 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
553 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
555 server, cldap_reply->pdc_dns_name);
556 if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
557 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
560 add_failed_connection_entry(domain, server,
561 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID);
568 expired = timeval_expired(&endtime);
574 /* keep track of failures as all were not suitable */
575 for (i = 0; i < num_requests; i++) {
576 char server[INET6_ADDRSTRLEN];
578 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
580 add_failed_connection_entry(domain, server,
581 NT_STATUS_UNSUCCESSFUL);
584 status = NT_STATUS_NO_LOGON_SERVERS;
585 DBG_WARNING("realm[%s] no valid response "
586 "num_requests[%zu] for count[%zu] - %s\n",
588 num_requests, count, nt_errstr(status));
590 return NT_STATUS_NO_LOGON_SERVERS;
593 /***************************************************************************
594 resolve a name and perform an "ldap ping" using NetBIOS and related methods
595 ****************************************************************************/
597 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
598 const char *domain, const char *realm)
602 struct samba_sockaddr *sa_list = NULL;
605 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
608 status = get_sorted_dc_list(talloc_tos(),
614 if (!NT_STATUS_IS_OK(status)) {
618 /* remove servers which are known to be dead based on
619 the corresponding DNS method */
621 for (i = 0; i < count; ++i) {
622 char server[INET6_ADDRSTRLEN];
624 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
627 check_negative_conn_cache(realm, server))) {
628 /* Ensure we add the workgroup name for this
629 IP address as negative too. */
630 add_failed_connection_entry(
632 NT_STATUS_UNSUCCESSFUL);
637 status = cldap_ping_list(ads, domain, sa_list, count);
639 TALLOC_FREE(sa_list);
645 /**********************************************************************
646 resolve a name and perform an "ldap ping" using DNS
647 **********************************************************************/
649 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
653 struct samba_sockaddr *sa_list = NULL;
656 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
659 status = get_sorted_dc_list(talloc_tos(),
665 if (!NT_STATUS_IS_OK(status)) {
666 TALLOC_FREE(sa_list);
670 status = cldap_ping_list(ads, realm, sa_list, count);
672 TALLOC_FREE(sa_list);
677 /**********************************************************************
678 Try to find an AD dc using our internal name resolution routines
679 Try the realm first and then the workgroup name if netbios is not
681 **********************************************************************/
683 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
685 const char *c_domain = "";
687 bool use_own_domain = False;
688 char *sitename = NULL;
689 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
692 /* if the realm and workgroup are both empty, assume they are ours */
695 c_realm = ads->server.realm;
701 /* special case where no realm and no workgroup means our own */
702 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
703 use_own_domain = True;
704 c_realm = lp_realm();
708 if (!lp_disable_netbios()) {
709 if (use_own_domain) {
710 c_domain = lp_workgroup();
712 c_domain = ads->server.workgroup;
713 if (!*c_realm && (!c_domain || !*c_domain)) {
714 c_domain = lp_workgroup();
723 if (!*c_realm && !*c_domain) {
724 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
726 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
730 * In case of LDAP we use get_dc_name() as that
731 * creates the custom krb5.conf file
733 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
735 struct sockaddr_storage ip_out;
737 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
738 " and falling back to domain '%s'\n",
741 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
743 if (is_zero_addr(&ip_out)) {
744 return NT_STATUS_NO_LOGON_SERVERS;
748 * we call ads_try_connect() to fill in the
749 * ads->config details
751 ok = ads_try_connect(ads, false, &ip_out);
757 return NT_STATUS_NO_LOGON_SERVERS;
761 sitename = sitename_fetch(talloc_tos(), c_realm);
762 status = resolve_and_ping_dns(ads, sitename, c_realm);
764 if (NT_STATUS_IS_OK(status)) {
765 TALLOC_FREE(sitename);
769 /* In case we failed to contact one of our closest DC on our
771 * need to try to find another DC, retry with a site-less SRV
776 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
777 "our site (%s), Trying to find another DC "
778 "for realm '%s' (domain '%s')\n",
779 sitename, c_realm, c_domain));
780 namecache_delete(c_realm, 0x1C);
782 resolve_and_ping_dns(ads, NULL, c_realm);
784 if (NT_STATUS_IS_OK(status)) {
785 TALLOC_FREE(sitename);
790 TALLOC_FREE(sitename);
793 /* try netbios as fallback - if permitted,
794 or if configuration specifically requests it */
797 DEBUG(3, ("ads_find_dc: falling back to netbios "
798 "name resolution for domain '%s' (realm '%s')\n",
802 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
803 if (NT_STATUS_IS_OK(status)) {
808 DEBUG(1, ("ads_find_dc: "
809 "name resolution for realm '%s' (domain '%s') failed: %s\n",
810 c_realm, c_domain, nt_errstr(status)));
814 * Connect to the LDAP server
815 * @param ads Pointer to an existing ADS_STRUCT
816 * @return status of connection
818 ADS_STATUS ads_connect(ADS_STRUCT *ads)
820 int version = LDAP_VERSION3;
823 char addr[INET6_ADDRSTRLEN];
824 struct sockaddr_storage existing_ss;
826 bool start_tls = false;
828 zero_sockaddr(&existing_ss);
831 * ads_connect can be passed in a reused ADS_STRUCT
832 * with an existing non-zero ads->ldap.ss IP address
833 * that was stored by going through ads_find_dc()
834 * if ads->server.ldap_server was NULL.
836 * If ads->server.ldap_server is still NULL but
837 * the target address isn't the zero address, then
838 * store that address off off before zeroing out
839 * ads->ldap so we don't keep doing multiple calls
840 * to ads_find_dc() in the reuse case.
842 * If a caller wants a clean ADS_STRUCT they
843 * will TALLOC_FREE it and allocate a new one
844 * by calling ads_init(), which ensures
845 * ads->ldap.ss is a properly zero'ed out valid IP
848 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
849 /* Save off the address we previously found by ads_find_dc(). */
850 existing_ss = ads->ldap.ss;
854 ZERO_STRUCT(ads->ldap_tls_data);
855 ZERO_STRUCT(ads->ldap_wrap_data);
856 ads->ldap.last_attempt = time_mono(NULL);
857 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
859 /* try with a user specified server */
861 if (DEBUGLEVEL >= 11) {
862 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
863 DEBUG(11,("ads_connect: entering\n"));
864 DEBUGADD(11,("%s\n", s));
868 if (ads->server.ldap_server) {
870 struct sockaddr_storage ss;
872 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
873 ads->server.ldap_server);
874 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
876 DEBUG(5,("ads_connect: unable to resolve name %s\n",
877 ads->server.ldap_server));
878 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
882 if (is_zero_addr(&ss)) {
883 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
887 ok = ads_try_connect(ads, ads->server.gc, &ss);
892 /* The choice of which GC use is handled one level up in
893 ads_connect_gc(). If we continue on from here with
894 ads_find_dc() we will get GC searches on port 389 which
895 doesn't work. --jerry */
897 if (ads->server.gc == true) {
898 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
901 if (ads->server.no_fallback) {
902 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
907 if (!is_zero_addr(&existing_ss)) {
908 /* We saved off who we should talk to. */
909 bool ok = ads_try_connect(ads,
916 * Keep trying to find a server and fall through
917 * into ads_find_dc() again.
919 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
920 "trying to find another DC.\n");
923 ntstatus = ads_find_dc(ads);
924 if (NT_STATUS_IS_OK(ntstatus)) {
928 status = ADS_ERROR_NT(ntstatus);
933 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
934 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
936 if (!ads->auth.user_name) {
937 /* Must use the userPrincipalName value here or sAMAccountName
938 and not servicePrincipalName; found by Guenther Deschner */
939 ads->auth.user_name = talloc_asprintf(ads,
942 if (ads->auth.user_name == NULL) {
943 DBG_ERR("talloc_asprintf failed\n");
944 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
949 if (ads->auth.realm == NULL) {
950 ads->auth.realm = talloc_strdup(ads, ads->config.realm);
951 if (ads->auth.realm == NULL) {
952 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
957 if (!ads->auth.kdc_server) {
958 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
959 ads->auth.kdc_server = talloc_strdup(ads, addr);
960 if (ads->auth.kdc_server == NULL) {
961 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
966 /* If the caller() requested no LDAP bind, then we are done */
968 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
969 status = ADS_SUCCESS;
973 ads->ldap_tls_data.mem_ctx = talloc_init("ads LDAP TLS connection memory");
974 if (!ads->ldap_tls_data.mem_ctx) {
975 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
979 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
980 if (!ads->ldap_wrap_data.mem_ctx) {
981 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
985 /* Otherwise setup the TCP LDAP session */
987 if (ads->auth.flags & ADS_AUTH_SASL_LDAPS) {
989 ads->ldap.port = 636;
990 } else if (ads->auth.flags & ADS_AUTH_SASL_STARTTLS) {
993 ads->ldap.port = 389;
995 ads->ldap.port = 389;
998 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
1000 ads->ldap.port, lp_ldap_timeout());
1001 if (ads->ldap.ld == NULL) {
1002 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
1005 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
1007 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1010 unsigned int to = lp_ldap_connection_timeout();
1011 struct berval *rspdata = NULL;
1012 char *rspoid = NULL;
1018 CatchSignal(SIGALRM, gotalarm_sig);
1020 /* End setup timeout. */
1023 rc = ldap_extended_operation_s(ads->ldap.ld,
1024 LDAP_EXOP_START_TLS,
1030 if (gotalarm != 0 && rc == LDAP_SUCCESS) {
1035 /* Teardown timeout. */
1037 CatchSignal(SIGALRM, SIG_IGN);
1040 if (rspoid != NULL) {
1041 ldap_memfree(rspoid);
1044 if (rspdata != NULL) {
1045 ber_bvfree(rspdata);
1048 if (rc != LDAP_SUCCESS) {
1049 status = ADS_ERROR_LDAP(rc);
1055 unsigned int to = lp_ldap_connection_timeout();
1060 CatchSignal(SIGALRM, gotalarm_sig);
1062 /* End setup timeout. */
1065 status = ads_setup_tls_wrapping(&ads->ldap_tls_data,
1067 ads->config.ldap_server_name);
1070 /* Teardown timeout. */
1072 CatchSignal(SIGALRM, SIG_IGN);
1075 if ( !ADS_ERR_OK(status) ) {
1080 /* cache the successful connection for workgroup and realm */
1081 if (ads_closest_dc(ads)) {
1082 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
1083 saf_store( ads->server.realm, ads->config.ldap_server_name);
1086 /* fill in the current time and offsets */
1088 status = ads_current_time( ads );
1089 if ( !ADS_ERR_OK(status) ) {
1093 /* Now do the bind */
1095 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
1096 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
1100 status = ads_sasl_bind(ads);
1103 if (DEBUGLEVEL >= 11) {
1104 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1105 DEBUG(11,("ads_connect: leaving with: %s\n",
1106 ads_errstr(status)));
1107 DEBUGADD(11,("%s\n", s));
1115 * Connect to the LDAP server using given credentials
1116 * @param ads Pointer to an existing ADS_STRUCT
1117 * @return status of connection
1119 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
1121 ads->auth.flags |= ADS_AUTH_USER_CREDS;
1123 return ads_connect(ads);
1127 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1128 * @param ads Pointer to an existing ADS_STRUCT
1130 * Sets the ads->ldap.ss to a valid
1131 * zero ip address that can be detected by
1132 * our is_zero_addr() function. Otherwise
1133 * it is left as AF_UNSPEC (0).
1135 void ads_zero_ldap(ADS_STRUCT *ads)
1137 ZERO_STRUCT(ads->ldap);
1139 * Initialize the sockaddr_storage so we can use
1140 * sockaddr test functions against it.
1142 zero_sockaddr(&ads->ldap.ss);
1146 * Disconnect the LDAP server
1147 * @param ads Pointer to an existing ADS_STRUCT
1149 void ads_disconnect(ADS_STRUCT *ads)
1152 ldap_unbind(ads->ldap.ld);
1153 ads->ldap.ld = NULL;
1155 if (ads->ldap_tls_data.mem_ctx) {
1156 talloc_free(ads->ldap_tls_data.mem_ctx);
1158 if (ads->ldap_wrap_data.wrap_ops &&
1159 ads->ldap_wrap_data.wrap_ops->disconnect) {
1160 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1162 if (ads->ldap_wrap_data.mem_ctx) {
1163 talloc_free(ads->ldap_wrap_data.mem_ctx);
1166 ZERO_STRUCT(ads->ldap_tls_data);
1167 ZERO_STRUCT(ads->ldap_wrap_data);
1171 Duplicate a struct berval into talloc'ed memory
1173 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1175 struct berval *value;
1177 if (!in_val) return NULL;
1179 value = talloc_zero(ctx, struct berval);
1182 if (in_val->bv_len == 0) return value;
1184 value->bv_len = in_val->bv_len;
1185 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1191 Make a values list out of an array of (struct berval *)
1193 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1194 const struct berval **in_vals)
1196 struct berval **values;
1199 if (!in_vals) return NULL;
1200 for (i=0; in_vals[i]; i++)
1201 ; /* count values */
1202 values = talloc_zero_array(ctx, struct berval *, i+1);
1203 if (!values) return NULL;
1205 for (i=0; in_vals[i]; i++) {
1206 values[i] = dup_berval(ctx, in_vals[i]);
1212 UTF8-encode a values list out of an array of (char *)
1214 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1220 if (!in_vals) return NULL;
1221 for (i=0; in_vals[i]; i++)
1222 ; /* count values */
1223 values = talloc_zero_array(ctx, char *, i+1);
1224 if (!values) return NULL;
1226 for (i=0; in_vals[i]; i++) {
1227 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1228 TALLOC_FREE(values);
1236 Pull a (char *) array out of a UTF8-encoded values list
1238 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1242 size_t converted_size;
1244 if (!in_vals) return NULL;
1245 for (i=0; in_vals[i]; i++)
1246 ; /* count values */
1247 values = talloc_zero_array(ctx, char *, i+1);
1248 if (!values) return NULL;
1250 for (i=0; in_vals[i]; i++) {
1251 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1253 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1254 "%s\n", strerror(errno)));
1261 * Do a search with paged results. cookie must be null on the first
1262 * call, and then returned on each subsequent call. It will be null
1263 * again when the entire search is complete
1264 * @param ads connection to ads server
1265 * @param bind_path Base dn for the search
1266 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1267 * @param expr Search expression - specified in local charset
1268 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1269 * @param res ** which will contain results - free res* with ads_msgfree()
1270 * @param count Number of entries retrieved on this page
1271 * @param cookie The paged results cookie to be returned on subsequent calls
1272 * @return status of search
1274 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1275 const char *bind_path,
1276 int scope, const char *expr,
1277 const char **attrs, void *args,
1279 int *count, struct berval **cookie)
1282 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1283 size_t converted_size;
1284 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1285 BerElement *cookie_be = NULL;
1286 struct berval *cookie_bv= NULL;
1287 BerElement *ext_be = NULL;
1288 struct berval *ext_bv= NULL;
1291 ads_control *external_control = (ads_control *) args;
1295 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1296 return ADS_ERROR(LDAP_NO_MEMORY);
1298 /* 0 means the conversion worked but the result was empty
1299 so we only fail if it's -1. In any case, it always
1300 at least nulls out the dest */
1301 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1302 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1304 rc = LDAP_NO_MEMORY;
1308 if (!attrs || !(*attrs))
1309 search_attrs = NULL;
1311 /* This would be the utf8-encoded version...*/
1312 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1313 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1314 rc = LDAP_NO_MEMORY;
1319 /* Paged results only available on ldap v3 or later */
1320 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1321 if (version < LDAP_VERSION3) {
1322 rc = LDAP_NOT_SUPPORTED;
1326 cookie_be = ber_alloc_t(LBER_USE_DER);
1328 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1329 ber_bvfree(*cookie); /* don't need it from last time */
1332 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1334 ber_flatten(cookie_be, &cookie_bv);
1335 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1336 PagedResults.ldctl_iscritical = (char) 1;
1337 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1338 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1340 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1341 NoReferrals.ldctl_iscritical = (char) 0;
1342 NoReferrals.ldctl_value.bv_len = 0;
1343 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1345 if (external_control &&
1346 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1347 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1349 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1350 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1352 /* win2k does not accept a ldctl_value being passed in */
1354 if (external_control->val != 0) {
1356 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1357 rc = LDAP_NO_MEMORY;
1361 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1362 rc = LDAP_NO_MEMORY;
1365 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1366 rc = LDAP_NO_MEMORY;
1370 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1371 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1374 ExternalCtrl.ldctl_value.bv_len = 0;
1375 ExternalCtrl.ldctl_value.bv_val = NULL;
1378 controls[0] = &NoReferrals;
1379 controls[1] = &PagedResults;
1380 controls[2] = &ExternalCtrl;
1384 controls[0] = &NoReferrals;
1385 controls[1] = &PagedResults;
1389 /* we need to disable referrals as the openldap libs don't
1390 handle them and paged results at the same time. Using them
1391 together results in the result record containing the server
1392 page control being removed from the result list (tridge/jmcd)
1394 leaving this in despite the control that says don't generate
1395 referrals, in case the server doesn't support it (jmcd)
1397 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1399 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1400 search_attrs, 0, controls,
1401 NULL, LDAP_NO_LIMIT,
1402 (LDAPMessage **)res);
1404 ber_free(cookie_be, 1);
1405 ber_bvfree(cookie_bv);
1408 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1409 ldap_err2string(rc)));
1410 if (rc == LDAP_OTHER) {
1414 ret = ldap_parse_result(ads->ldap.ld,
1422 if (ret == LDAP_SUCCESS) {
1423 DEBUG(3, ("ldap_search_with_timeout(%s) "
1424 "error: %s\n", expr, ldap_errmsg));
1425 ldap_memfree(ldap_errmsg);
1431 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1432 NULL, &rcontrols, 0);
1438 for (i=0; rcontrols[i]; i++) {
1439 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1440 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1441 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1443 /* the berval is the cookie, but must be freed when
1445 if (cookie_bv->bv_len) /* still more to do */
1446 *cookie=ber_bvdup(cookie_bv);
1449 ber_bvfree(cookie_bv);
1450 ber_free(cookie_be, 1);
1454 ldap_controls_free(rcontrols);
1457 talloc_destroy(ctx);
1460 ber_free(ext_be, 1);
1467 if (rc != LDAP_SUCCESS && *res != NULL) {
1468 ads_msgfree(ads, *res);
1472 /* if/when we decide to utf8-encode attrs, take out this next line */
1473 TALLOC_FREE(search_attrs);
1475 return ADS_ERROR(rc);
1478 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1479 int scope, const char *expr,
1480 const char **attrs, LDAPMessage **res,
1481 int *count, struct berval **cookie)
1483 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1488 * Get all results for a search. This uses ads_do_paged_search() to return
1489 * all entries in a large search.
1490 * @param ads connection to ads server
1491 * @param bind_path Base dn for the search
1492 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1493 * @param expr Search expression
1494 * @param attrs Attributes to retrieve
1495 * @param res ** which will contain results - free res* with ads_msgfree()
1496 * @return status of search
1498 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1499 int scope, const char *expr,
1500 const char **attrs, void *args,
1503 struct berval *cookie = NULL;
1508 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1511 if (!ADS_ERR_OK(status))
1514 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1516 LDAPMessage *res2 = NULL;
1517 LDAPMessage *msg, *next;
1519 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1520 attrs, args, &res2, &count, &cookie);
1521 if (!ADS_ERR_OK(status)) {
1525 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1526 that this works on all ldap libs, but I have only tested with openldap */
1527 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1528 next = ads_next_message(ads, msg);
1529 ldap_add_result_entry((LDAPMessage **)res, msg);
1531 /* note that we do not free res2, as the memory is now
1532 part of the main returned list */
1535 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1536 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1542 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1543 int scope, const char *expr,
1544 const char **attrs, LDAPMessage **res)
1546 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1549 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1550 int scope, const char *expr,
1551 const char **attrs, uint32_t sd_flags,
1556 args.control = ADS_SD_FLAGS_OID;
1557 args.val = sd_flags;
1558 args.critical = True;
1560 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1565 * Run a function on all results for a search. Uses ads_do_paged_search() and
1566 * runs the function as each page is returned, using ads_process_results()
1567 * @param ads connection to ads server
1568 * @param bind_path Base dn for the search
1569 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1570 * @param expr Search expression - specified in local charset
1571 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1572 * @param fn Function which takes attr name, values list, and data_area
1573 * @param data_area Pointer which is passed to function on each call
1574 * @return status of search
1576 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1577 int scope, const char *expr, const char **attrs,
1578 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1581 struct berval *cookie = NULL;
1586 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1589 if (!ADS_ERR_OK(status)) return status;
1591 ads_process_results(ads, res, fn, data_area);
1592 ads_msgfree(ads, res);
1595 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1596 &res, &count, &cookie);
1598 if (!ADS_ERR_OK(status)) break;
1600 ads_process_results(ads, res, fn, data_area);
1601 ads_msgfree(ads, res);
1608 * Do a search with a timeout.
1609 * @param ads connection to ads server
1610 * @param bind_path Base dn for the search
1611 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1612 * @param expr Search expression
1613 * @param attrs Attributes to retrieve
1614 * @param res ** which will contain results - free res* with ads_msgfree()
1615 * @return status of search
1617 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1619 const char **attrs, LDAPMessage **res)
1622 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1623 size_t converted_size;
1627 if (!(ctx = talloc_init("ads_do_search"))) {
1628 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1629 return ADS_ERROR(LDAP_NO_MEMORY);
1632 /* 0 means the conversion worked but the result was empty
1633 so we only fail if it's negative. In any case, it always
1634 at least nulls out the dest */
1635 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1636 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1638 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1639 rc = LDAP_NO_MEMORY;
1643 if (!attrs || !(*attrs))
1644 search_attrs = NULL;
1646 /* This would be the utf8-encoded version...*/
1647 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1648 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1650 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1651 rc = LDAP_NO_MEMORY;
1656 /* see the note in ads_do_paged_search - we *must* disable referrals */
1657 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1659 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1660 search_attrs, 0, NULL, NULL,
1662 (LDAPMessage **)res);
1664 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1665 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1670 talloc_destroy(ctx);
1671 /* if/when we decide to utf8-encode attrs, take out this next line */
1672 TALLOC_FREE(search_attrs);
1673 return ADS_ERROR(rc);
1676 * Do a general ADS search
1677 * @param ads connection to ads server
1678 * @param res ** which will contain results - free res* with ads_msgfree()
1679 * @param expr Search expression
1680 * @param attrs Attributes to retrieve
1681 * @return status of search
1683 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1684 const char *expr, const char **attrs)
1686 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1691 * Do a search on a specific DistinguishedName
1692 * @param ads connection to ads server
1693 * @param res ** which will contain results - free res* with ads_msgfree()
1694 * @param dn DistinguishedName to search
1695 * @param attrs Attributes to retrieve
1696 * @return status of search
1698 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1699 const char *dn, const char **attrs)
1701 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1706 * Free up memory from a ads_search
1707 * @param ads connection to ads server
1708 * @param msg Search results to free
1710 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1717 * Get a dn from search results
1718 * @param ads connection to ads server
1719 * @param msg Search result
1722 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1724 char *utf8_dn, *unix_dn;
1725 size_t converted_size;
1727 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1730 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1734 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1735 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1739 ldap_memfree(utf8_dn);
1744 * Get the parent from a dn
1745 * @param dn the dn to return the parent from
1746 * @return parent dn string
1748 char *ads_parent_dn(const char *dn)
1756 p = strchr(dn, ',');
1766 * Find a machine account given a hostname
1767 * @param ads connection to ads server
1768 * @param res ** which will contain results - free res* with ads_msgfree()
1769 * @param host Hostname to search for
1770 * @return status of search
1772 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1773 const char *machine)
1777 const char *attrs[] = {
1778 /* This is how Windows checks for machine accounts */
1781 "userAccountControl",
1783 "ServicePrincipalName",
1784 "userPrincipalName",
1786 /* Additional attributes Samba checks */
1787 "msDS-AdditionalDnsHostName",
1788 "msDS-SupportedEncryptionTypes",
1789 "nTSecurityDescriptor",
1794 TALLOC_CTX *frame = talloc_stackframe();
1798 /* the easiest way to find a machine account anywhere in the tree
1799 is to look for hostname$ */
1800 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1802 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1806 status = ads_search(ads, res, expr, attrs);
1807 if (ADS_ERR_OK(status)) {
1808 if (ads_count_replies(ads, *res) != 1) {
1809 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1819 * Initialize a list of mods to be used in a modify request
1820 * @param ctx An initialized TALLOC_CTX
1821 * @return allocated ADS_MODLIST
1823 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1825 #define ADS_MODLIST_ALLOC_SIZE 10
1828 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1829 /* -1 is safety to make sure we don't go over the end.
1830 need to reset it to NULL before doing ldap modify */
1831 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1833 return (ADS_MODLIST)mods;
1838 add an attribute to the list, with values list already constructed
1840 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1841 int mod_op, const char *name,
1842 const void *_invals)
1845 LDAPMod **modlist = (LDAPMod **) *mods;
1846 struct berval **ber_values = NULL;
1847 char **char_values = NULL;
1850 mod_op = LDAP_MOD_DELETE;
1852 if (mod_op & LDAP_MOD_BVALUES) {
1853 const struct berval **b;
1854 b = discard_const_p(const struct berval *, _invals);
1855 ber_values = ads_dup_values(ctx, b);
1858 c = discard_const_p(const char *, _invals);
1859 char_values = ads_push_strvals(ctx, c);
1863 /* find the first empty slot */
1864 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1866 if (modlist[curmod] == (LDAPMod *) -1) {
1867 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1868 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1869 return ADS_ERROR(LDAP_NO_MEMORY);
1870 memset(&modlist[curmod], 0,
1871 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1872 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1873 *mods = (ADS_MODLIST)modlist;
1876 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1877 return ADS_ERROR(LDAP_NO_MEMORY);
1878 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1879 if (mod_op & LDAP_MOD_BVALUES) {
1880 modlist[curmod]->mod_bvalues = ber_values;
1881 } else if (mod_op & LDAP_MOD_DELETE) {
1882 modlist[curmod]->mod_values = NULL;
1884 modlist[curmod]->mod_values = char_values;
1887 modlist[curmod]->mod_op = mod_op;
1888 return ADS_ERROR(LDAP_SUCCESS);
1892 * Add a single string value to a mod list
1893 * @param ctx An initialized TALLOC_CTX
1894 * @param mods An initialized ADS_MODLIST
1895 * @param name The attribute name to add
1896 * @param val The value to add - NULL means DELETE
1897 * @return ADS STATUS indicating success of add
1899 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1900 const char *name, const char *val)
1902 const char *values[2];
1908 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1909 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1913 * Add an array of string values to a mod list
1914 * @param ctx An initialized TALLOC_CTX
1915 * @param mods An initialized ADS_MODLIST
1916 * @param name The attribute name to add
1917 * @param vals The array of string values to add - NULL means DELETE
1918 * @return ADS STATUS indicating success of add
1920 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1921 const char *name, const char **vals)
1924 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1925 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1926 name, (const void **) vals);
1930 * Add a single ber-encoded value to a mod list
1931 * @param ctx An initialized TALLOC_CTX
1932 * @param mods An initialized ADS_MODLIST
1933 * @param name The attribute name to add
1934 * @param val The value to add - NULL means DELETE
1935 * @return ADS STATUS indicating success of add
1937 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1938 const char *name, const struct berval *val)
1940 const struct berval *values[2];
1945 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1946 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1947 name, (const void **) values);
1950 static void ads_print_error(int ret, LDAP *ld)
1953 char *ld_error = NULL;
1954 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1955 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1957 ldap_err2string(ret),
1959 SAFE_FREE(ld_error);
1964 * Perform an ldap modify
1965 * @param ads connection to ads server
1966 * @param mod_dn DistinguishedName to modify
1967 * @param mods list of modifications to perform
1968 * @return status of modify
1970 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1973 char *utf8_dn = NULL;
1974 size_t converted_size;
1976 this control is needed to modify that contains a currently
1977 non-existent attribute (but allowable for the object) to run
1979 LDAPControl PermitModify = {
1980 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1983 LDAPControl *controls[2];
1985 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1987 controls[0] = &PermitModify;
1990 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1991 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1994 /* find the end of the list, marked by NULL or -1 */
1995 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1996 /* make sure the end of the list is NULL */
1998 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1999 (LDAPMod **) mods, controls, NULL);
2000 ads_print_error(ret, ads->ldap.ld);
2001 TALLOC_FREE(utf8_dn);
2002 return ADS_ERROR(ret);
2006 * Perform an ldap add
2007 * @param ads connection to ads server
2008 * @param new_dn DistinguishedName to add
2009 * @param mods list of attributes and values for DN
2010 * @return status of add
2012 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
2015 char *utf8_dn = NULL;
2016 size_t converted_size;
2018 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
2020 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
2021 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2022 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2025 /* find the end of the list, marked by NULL or -1 */
2026 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2027 /* make sure the end of the list is NULL */
2030 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
2031 ads_print_error(ret, ads->ldap.ld);
2032 TALLOC_FREE(utf8_dn);
2033 return ADS_ERROR(ret);
2037 * Delete a DistinguishedName
2038 * @param ads connection to ads server
2039 * @param new_dn DistinguishedName to delete
2040 * @return status of delete
2042 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
2045 char *utf8_dn = NULL;
2046 size_t converted_size;
2047 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
2048 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2049 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2052 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
2054 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
2055 ads_print_error(ret, ads->ldap.ld);
2056 TALLOC_FREE(utf8_dn);
2057 return ADS_ERROR(ret);
2061 * Build an org unit string
2062 * if org unit is Computers or blank then assume a container, otherwise
2063 * assume a / separated list of organisational units.
2064 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2065 * @param ads connection to ads server
2066 * @param org_unit Organizational unit
2067 * @return org unit string - caller must free
2069 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
2075 if (!org_unit || !*org_unit) {
2077 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
2079 /* samba4 might not yet respond to a wellknownobject-query */
2080 return ret ? ret : SMB_STRDUP("cn=Computers");
2083 if (strequal(org_unit, "Computers")) {
2084 return SMB_STRDUP("cn=Computers");
2087 /* jmcd: removed "\\" from the separation chars, because it is
2088 needed as an escape for chars like '#' which are valid in an
2090 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
2091 if (!ADS_ERR_OK(status)) {
2099 * Get a org unit string for a well-known GUID
2100 * @param ads connection to ads server
2101 * @param wknguid Well known GUID
2102 * @return org unit string - caller must free
2104 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2107 LDAPMessage *res = NULL;
2108 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2109 **bind_dn_exp = NULL;
2110 const char *attrs[] = {"distinguishedName", NULL};
2111 int new_ln, wkn_ln, bind_ln, i;
2113 if (wknguid == NULL) {
2117 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2118 DEBUG(1, ("asprintf failed!\n"));
2122 status = ads_search_dn(ads, &res, base, attrs);
2123 if (!ADS_ERR_OK(status)) {
2124 DEBUG(1,("Failed while searching for: %s\n", base));
2128 if (ads_count_replies(ads, res) != 1) {
2132 /* substitute the bind-path from the well-known-guid-search result */
2133 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2138 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2143 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2148 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2150 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2153 new_ln = wkn_ln - bind_ln;
2155 ret = SMB_STRDUP(wkn_dn_exp[0]);
2160 for (i=1; i < new_ln; i++) {
2163 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2169 ret = SMB_STRDUP(s);
2178 ads_msgfree(ads, res);
2179 TALLOC_FREE(wkn_dn);
2181 ldap_value_free(wkn_dn_exp);
2184 ldap_value_free(bind_dn_exp);
2191 * Adds (appends) an item to an attribute array, rather then
2192 * replacing the whole list
2193 * @param ctx An initialized TALLOC_CTX
2194 * @param mods An initialized ADS_MODLIST
2195 * @param name name of the ldap attribute to append to
2196 * @param vals an array of values to add
2197 * @return status of addition
2200 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2201 const char *name, const char **vals)
2203 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2204 (const void *) vals);
2208 * Determines the an account's current KVNO via an LDAP lookup
2209 * @param ads An initialized ADS_STRUCT
2210 * @param account_name the NT samaccountname.
2211 * @return the kvno for the account, or -1 in case of a failure.
2214 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
2216 LDAPMessage *res = NULL;
2217 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
2219 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
2220 char *dn_string = NULL;
2223 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
2224 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
2227 ret = ads_search(ads, &res, filter, attrs);
2229 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
2230 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
2231 ads_msgfree(ads, res);
2235 dn_string = ads_get_dn(ads, talloc_tos(), res);
2237 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2238 ads_msgfree(ads, res);
2241 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
2242 TALLOC_FREE(dn_string);
2244 /* ---------------------------------------------------------
2245 * 0 is returned as a default KVNO from this point on...
2246 * This is done because Windows 2000 does not support key
2247 * version numbers. Chances are that a failure in the next
2248 * step is simply due to Windows 2000 being used for a
2249 * domain controller. */
2252 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2253 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2254 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2255 ads_msgfree(ads, res);
2260 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2261 ads_msgfree(ads, res);
2266 * Determines the computer account's current KVNO via an LDAP lookup
2267 * @param ads An initialized ADS_STRUCT
2268 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2269 * @return the kvno for the computer account, or -1 in case of a failure.
2272 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2274 char *computer_account = NULL;
2277 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2281 kvno = ads_get_kvno(ads, computer_account);
2282 free(computer_account);
2288 * This clears out all registered spn's for a given hostname
2289 * @param ads An initialized ADS_STRUCT
2290 * @param machine_name the NetBIOS name of the computer.
2291 * @return 0 upon success, non-zero otherwise.
2294 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2297 LDAPMessage *res = NULL;
2299 const char *servicePrincipalName[1] = {NULL};
2301 char *dn_string = NULL;
2303 ret = ads_find_machine_acct(ads, &res, machine_name);
2304 if (!ADS_ERR_OK(ret)) {
2305 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2306 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2307 ads_msgfree(ads, res);
2311 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2312 ctx = talloc_init("ads_clear_service_principal_names");
2314 ads_msgfree(ads, res);
2315 return ADS_ERROR(LDAP_NO_MEMORY);
2318 if (!(mods = ads_init_mods(ctx))) {
2319 talloc_destroy(ctx);
2320 ads_msgfree(ads, res);
2321 return ADS_ERROR(LDAP_NO_MEMORY);
2323 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2324 if (!ADS_ERR_OK(ret)) {
2325 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2326 ads_msgfree(ads, res);
2327 talloc_destroy(ctx);
2330 dn_string = ads_get_dn(ads, talloc_tos(), res);
2332 talloc_destroy(ctx);
2333 ads_msgfree(ads, res);
2334 return ADS_ERROR(LDAP_NO_MEMORY);
2336 ret = ads_gen_mod(ads, dn_string, mods);
2337 TALLOC_FREE(dn_string);
2338 if (!ADS_ERR_OK(ret)) {
2339 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2341 ads_msgfree(ads, res);
2342 talloc_destroy(ctx);
2346 ads_msgfree(ads, res);
2347 talloc_destroy(ctx);
2352 * @brief Search for an element in a string array.
2354 * @param[in] el_array The string array to search.
2356 * @param[in] num_el The number of elements in the string array.
2358 * @param[in] el The string to search.
2360 * @return True if found, false if not.
2362 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2366 if (el_array == NULL || num_el == 0 || el == NULL) {
2370 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2373 cmp = strcasecmp_m(el_array[i], el);
2383 * @brief This gets the service principal names of an existing computer account.
2385 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2387 * @param[in] ads The ADS context to use.
2389 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2390 * identify the computer account.
2392 * @param[in] spn_array A pointer to store the array for SPNs.
2394 * @param[in] num_spns The number of principals stored in the array.
2396 * @return 0 on success, or a ADS error if a failure occurred.
2398 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2400 const char *machine_name,
2405 LDAPMessage *res = NULL;
2408 status = ads_find_machine_acct(ads,
2411 if (!ADS_ERR_OK(status)) {
2412 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2417 count = ads_count_replies(ads, res);
2419 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2423 *spn_array = ads_pull_strings(ads,
2426 "servicePrincipalName",
2428 if (*spn_array == NULL) {
2429 DEBUG(1, ("Host account for %s does not have service principal "
2432 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2437 ads_msgfree(ads, res);
2443 * This adds a service principal name to an existing computer account
2444 * (found by hostname) in AD.
2445 * @param ads An initialized ADS_STRUCT
2446 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2447 * @param spns An array or strings for the service principals to add,
2448 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2449 * @return 0 upon success, or non-zero if a failure occurs
2452 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2453 const char *machine_name,
2458 LDAPMessage *res = NULL;
2460 char *dn_string = NULL;
2461 const char **servicePrincipalName = spns;
2463 ret = ads_find_machine_acct(ads, &res, machine_name);
2464 if (!ADS_ERR_OK(ret)) {
2465 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2467 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2468 ads_msgfree(ads, res);
2472 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2473 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2474 ads_msgfree(ads, res);
2475 return ADS_ERROR(LDAP_NO_MEMORY);
2478 DEBUG(5,("ads_add_service_principal_name: INFO: "
2479 "Adding %s to host %s\n",
2480 spns[0] ? "N/A" : spns[0], machine_name));
2483 DEBUG(5,("ads_add_service_principal_name: INFO: "
2484 "Adding %s to host %s\n",
2485 spns[1] ? "N/A" : spns[1], machine_name));
2487 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2488 ret = ADS_ERROR(LDAP_NO_MEMORY);
2492 ret = ads_add_strlist(ctx,
2494 "servicePrincipalName",
2495 servicePrincipalName);
2496 if (!ADS_ERR_OK(ret)) {
2497 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2501 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2502 ret = ADS_ERROR(LDAP_NO_MEMORY);
2506 ret = ads_gen_mod(ads, dn_string, mods);
2507 if (!ADS_ERR_OK(ret)) {
2508 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2514 ads_msgfree(ads, res);
2518 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2521 uint32_t acct_ctrl = 0;
2524 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2532 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2534 const struct berval *machine_pw_val)
2538 TALLOC_CTX *frame = talloc_stackframe();
2539 uint32_t acct_control;
2540 char *control_str = NULL;
2541 const char *attrs[] = {
2545 LDAPMessage *res = NULL;
2548 dn = ads_get_dn(ads, frame, msg);
2550 ret = ADS_ERROR(LDAP_NO_MEMORY);
2554 acct_control = ads_get_acct_ctrl(ads, msg);
2555 if (acct_control == 0) {
2556 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2561 * Changing the password, disables the account. So we need to change the
2562 * userAccountControl flags to enable it again.
2564 mods = ads_init_mods(frame);
2566 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2570 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2572 ret = ads_gen_mod(ads, dn, mods);
2573 if (!ADS_ERR_OK(ret)) {
2579 * To activate the account, we need to disable and enable it.
2581 acct_control |= UF_ACCOUNTDISABLE;
2583 control_str = talloc_asprintf(frame, "%u", acct_control);
2584 if (control_str == NULL) {
2585 ret = ADS_ERROR(LDAP_NO_MEMORY);
2589 mods = ads_init_mods(frame);
2591 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2595 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2597 ret = ads_gen_mod(ads, dn, mods);
2598 if (!ADS_ERR_OK(ret)) {
2602 TALLOC_FREE(control_str);
2605 * Enable the account again.
2607 acct_control &= ~UF_ACCOUNTDISABLE;
2609 control_str = talloc_asprintf(frame, "%u", acct_control);
2610 if (control_str == NULL) {
2611 ret = ADS_ERROR(LDAP_NO_MEMORY);
2615 mods = ads_init_mods(frame);
2617 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2621 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2623 ret = ads_gen_mod(ads, dn, mods);
2624 if (!ADS_ERR_OK(ret)) {
2628 TALLOC_FREE(control_str);
2630 ret = ads_search_dn(ads, &res, dn, attrs);
2631 ads_msgfree(ads, res);
2640 * adds a machine account to the ADS server
2641 * @param ads An initialized ADS_STRUCT
2642 * @param machine_name - the NetBIOS machine name of this account.
2643 * @param account_type A number indicating the type of account to create
2644 * @param org_unit The LDAP path in which to place this account
2645 * @return 0 upon success, or non-zero otherwise
2648 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2649 const char *machine_name,
2650 const char *machine_password,
2651 const char *org_unit,
2652 uint32_t etype_list,
2653 const char *dns_domain_name)
2656 char *samAccountName = NULL;
2657 char *controlstr = NULL;
2658 TALLOC_CTX *ctx = NULL;
2660 char *machine_escaped = NULL;
2661 char *dns_hostname = NULL;
2662 char *new_dn = NULL;
2663 char *utf8_pw = NULL;
2664 size_t utf8_pw_len = 0;
2665 char *utf16_pw = NULL;
2666 size_t utf16_pw_len = 0;
2667 struct berval machine_pw_val;
2669 const char **spn_array = NULL;
2670 size_t num_spns = 0;
2671 const char *spn_prefix[] = {
2673 "RestrictedKrbHost",
2676 LDAPMessage *res = NULL;
2677 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2679 ctx = talloc_init("ads_add_machine_acct");
2681 return ADS_ERROR(LDAP_NO_MEMORY);
2684 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2685 if (machine_escaped == NULL) {
2686 ret = ADS_ERROR(LDAP_NO_MEMORY);
2690 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2691 if (utf8_pw == NULL) {
2692 ret = ADS_ERROR(LDAP_NO_MEMORY);
2695 utf8_pw_len = strlen(utf8_pw);
2697 ok = convert_string_talloc(ctx,
2698 CH_UTF8, CH_UTF16MUNGED,
2699 utf8_pw, utf8_pw_len,
2700 (void *)&utf16_pw, &utf16_pw_len);
2702 ret = ADS_ERROR(LDAP_NO_MEMORY);
2706 machine_pw_val = (struct berval) {
2708 .bv_len = utf16_pw_len,
2711 /* Check if the machine account already exists. */
2712 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2713 if (ADS_ERR_OK(ret)) {
2714 /* Change the machine account password */
2715 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2716 ads_msgfree(ads, res);
2720 ads_msgfree(ads, res);
2722 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2723 if (new_dn == NULL) {
2724 ret = ADS_ERROR(LDAP_NO_MEMORY);
2728 /* Create machine account */
2730 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2731 if (samAccountName == NULL) {
2732 ret = ADS_ERROR(LDAP_NO_MEMORY);
2736 dns_hostname = talloc_asprintf(ctx,
2740 if (dns_hostname == NULL) {
2741 ret = ADS_ERROR(LDAP_NO_MEMORY);
2745 /* Add dns_hostname SPNs */
2746 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2747 char *spn = talloc_asprintf(ctx,
2752 ret = ADS_ERROR(LDAP_NO_MEMORY);
2756 ok = add_string_to_array(ctx,
2761 ret = ADS_ERROR(LDAP_NO_MEMORY);
2766 /* Add machine_name SPNs */
2767 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2768 char *spn = talloc_asprintf(ctx,
2773 ret = ADS_ERROR(LDAP_NO_MEMORY);
2777 ok = add_string_to_array(ctx,
2782 ret = ADS_ERROR(LDAP_NO_MEMORY);
2787 /* Make sure to NULL terminate the array */
2788 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2789 if (spn_array == NULL) {
2790 ret = ADS_ERROR(LDAP_NO_MEMORY);
2793 spn_array[num_spns] = NULL;
2795 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2796 if (controlstr == NULL) {
2797 ret = ADS_ERROR(LDAP_NO_MEMORY);
2801 mods = ads_init_mods(ctx);
2803 ret = ADS_ERROR(LDAP_NO_MEMORY);
2807 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2808 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2809 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2810 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2811 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2812 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2814 ret = ads_gen_add(ads, new_dn, mods);
2817 SAFE_FREE(machine_escaped);
2818 talloc_destroy(ctx);
2824 * move a machine account to another OU on the ADS server
2825 * @param ads - An initialized ADS_STRUCT
2826 * @param machine_name - the NetBIOS machine name of this account.
2827 * @param org_unit - The LDAP path in which to place this account
2828 * @param moved - whether we moved the machine account (optional)
2829 * @return 0 upon success, or non-zero otherwise
2832 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2833 const char *org_unit, bool *moved)
2837 LDAPMessage *res = NULL;
2838 char *filter = NULL;
2839 char *computer_dn = NULL;
2841 char *computer_rdn = NULL;
2842 bool need_move = False;
2844 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2845 rc = ADS_ERROR(LDAP_NO_MEMORY);
2849 /* Find pre-existing machine */
2850 rc = ads_search(ads, &res, filter, NULL);
2851 if (!ADS_ERR_OK(rc)) {
2855 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2857 rc = ADS_ERROR(LDAP_NO_MEMORY);
2861 parent_dn = ads_parent_dn(computer_dn);
2862 if (strequal(parent_dn, org_unit)) {
2868 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2869 rc = ADS_ERROR(LDAP_NO_MEMORY);
2873 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2874 org_unit, 1, NULL, NULL);
2875 rc = ADS_ERROR(ldap_status);
2878 ads_msgfree(ads, res);
2880 TALLOC_FREE(computer_dn);
2881 SAFE_FREE(computer_rdn);
2883 if (!ADS_ERR_OK(rc)) {
2895 dump a binary result from ldap
2897 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2900 for (i=0; values[i]; i++) {
2902 printf("%s: ", field);
2903 for (j=0; j<values[i]->bv_len; j++) {
2904 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2910 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2913 for (i=0; values[i]; i++) {
2915 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2918 status = GUID_from_ndr_blob(&in, &guid);
2919 if (NT_STATUS_IS_OK(status)) {
2920 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2922 printf("%s: INVALID GUID\n", field);
2928 dump a sid result from ldap
2930 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2933 for (i=0; values[i]; i++) {
2936 struct dom_sid_buf tmp;
2937 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2938 values[i]->bv_len, &sid);
2942 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2947 dump ntSecurityDescriptor
2949 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2951 TALLOC_CTX *frame = talloc_stackframe();
2952 struct security_descriptor *psd;
2955 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2956 values[0]->bv_len, &psd);
2957 if (!NT_STATUS_IS_OK(status)) {
2958 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2959 nt_errstr(status)));
2965 ads_disp_sd(ads, talloc_tos(), psd);
2972 dump a string result from ldap
2974 static void dump_string(const char *field, char **values)
2977 for (i=0; values[i]; i++) {
2978 printf("%s: %s\n", field, values[i]);
2983 dump a field from LDAP on stdout
2987 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2992 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2994 {"objectGUID", False, dump_guid},
2995 {"netbootGUID", False, dump_guid},
2996 {"nTSecurityDescriptor", False, dump_sd},
2997 {"dnsRecord", False, dump_binary},
2998 {"objectSid", False, dump_sid},
2999 {"securityIdentifier", False, dump_sid},
3000 {"tokenGroups", False, dump_sid},
3001 {"tokenGroupsNoGCAcceptable", False, dump_sid},
3002 {"tokengroupsGlobalandUniversal", False, dump_sid},
3003 {"mS-DS-CreatorSID", False, dump_sid},
3004 {"msExchMailboxGuid", False, dump_guid},
3005 {"msDS-TrustForestTrustInfo", False, dump_binary},
3010 if (!field) { /* must be end of an entry */
3015 for (i=0; handlers[i].name; i++) {
3016 if (strcasecmp_m(handlers[i].name, field) == 0) {
3017 if (!values) /* first time, indicate string or not */
3018 return handlers[i].string;
3019 handlers[i].handler(ads, field, (struct berval **) values);
3023 if (!handlers[i].name) {
3024 if (!values) /* first time, indicate string conversion */
3026 dump_string(field, (char **)values);
3032 * Dump a result from LDAP on stdout
3033 * used for debugging
3034 * @param ads connection to ads server
3035 * @param res Results to dump
3038 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
3040 ads_process_results(ads, res, ads_dump_field, NULL);
3044 * Walk through results, calling a function for each entry found.
3045 * The function receives a field name, a berval * array of values,
3046 * and a data area passed through from the start. The function is
3047 * called once with null for field and values at the end of each
3049 * @param ads connection to ads server
3050 * @param res Results to process
3051 * @param fn Function for processing each result
3052 * @param data_area user-defined area to pass to function
3054 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
3055 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
3060 size_t converted_size;
3062 if (!(ctx = talloc_init("ads_process_results")))
3065 for (msg = ads_first_entry(ads, res); msg;
3066 msg = ads_next_entry(ads, msg)) {
3070 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
3071 (LDAPMessage *)msg,&b);
3073 utf8_field=ldap_next_attribute(ads->ldap.ld,
3074 (LDAPMessage *)msg,b)) {
3075 struct berval **ber_vals;
3081 if (!pull_utf8_talloc(ctx, &field, utf8_field,
3084 DEBUG(0,("ads_process_results: "
3085 "pull_utf8_talloc failed: %s\n",
3089 string = fn(ads, field, NULL, data_area);
3094 utf8_vals = ldap_get_values(ads->ldap.ld,
3095 (LDAPMessage *)msg, field);
3096 p = discard_const_p(const char *, utf8_vals);
3097 str_vals = ads_pull_strvals(ctx, p);
3098 fn(ads, field, (void **) str_vals, data_area);
3099 ldap_value_free(utf8_vals);
3101 ber_vals = ldap_get_values_len(ads->ldap.ld,
3102 (LDAPMessage *)msg, field);
3103 fn(ads, field, (void **) ber_vals, data_area);
3105 ldap_value_free_len(ber_vals);
3107 ldap_memfree(utf8_field);
3110 talloc_free_children(ctx);
3111 fn(ads, NULL, NULL, data_area); /* completed an entry */
3114 talloc_destroy(ctx);
3118 * count how many replies are in a LDAPMessage
3119 * @param ads connection to ads server
3120 * @param res Results to count
3121 * @return number of replies
3123 int ads_count_replies(ADS_STRUCT *ads, void *res)
3125 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3129 * pull the first entry from a ADS result
3130 * @param ads connection to ads server
3131 * @param res Results of search
3132 * @return first entry from result
3134 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3136 return ldap_first_entry(ads->ldap.ld, res);
3140 * pull the next entry from a ADS result
3141 * @param ads connection to ads server
3142 * @param res Results of search
3143 * @return next entry from result
3145 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3147 return ldap_next_entry(ads->ldap.ld, res);
3151 * pull the first message from a ADS result
3152 * @param ads connection to ads server
3153 * @param res Results of search
3154 * @return first message from result
3156 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3158 return ldap_first_message(ads->ldap.ld, res);
3162 * pull the next message from a ADS result
3163 * @param ads connection to ads server
3164 * @param res Results of search
3165 * @return next message from result
3167 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3169 return ldap_next_message(ads->ldap.ld, res);
3173 * pull a single string from a ADS result
3174 * @param ads connection to ads server
3175 * @param mem_ctx TALLOC_CTX to use for allocating result string
3176 * @param msg Results of search
3177 * @param field Attribute to retrieve
3178 * @return Result string in talloc context
3180 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3186 size_t converted_size;
3188 values = ldap_get_values(ads->ldap.ld, msg, field);
3192 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3197 ldap_value_free(values);
3202 * pull an array of strings from a ADS result
3203 * @param ads connection to ads server
3204 * @param mem_ctx TALLOC_CTX to use for allocating result string
3205 * @param msg Results of search
3206 * @param field Attribute to retrieve
3207 * @return Result strings in talloc context
3209 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3210 LDAPMessage *msg, const char *field,
3215 size_t i, converted_size;
3217 values = ldap_get_values(ads->ldap.ld, msg, field);
3221 *num_values = ldap_count_values(values);
3223 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3225 ldap_value_free(values);
3229 for (i=0;i<*num_values;i++) {
3230 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3233 ldap_value_free(values);
3239 ldap_value_free(values);
3244 * pull an array of strings from a ADS result
3245 * (handle large multivalue attributes with range retrieval)
3246 * @param ads connection to ads server
3247 * @param mem_ctx TALLOC_CTX to use for allocating result string
3248 * @param msg Results of search
3249 * @param field Attribute to retrieve
3250 * @param current_strings strings returned by a previous call to this function
3251 * @param next_attribute The next query should ask for this attribute
3252 * @param num_values How many values did we get this time?
3253 * @param more_values Are there more values to get?
3254 * @return Result strings in talloc context
3256 char **ads_pull_strings_range(ADS_STRUCT *ads,
3257 TALLOC_CTX *mem_ctx,
3258 LDAPMessage *msg, const char *field,
3259 char **current_strings,
3260 const char **next_attribute,
3261 size_t *num_strings,
3265 char *expected_range_attrib, *range_attr = NULL;
3266 BerElement *ptr = NULL;
3269 size_t num_new_strings;
3270 unsigned long int range_start;
3271 unsigned long int range_end;
3273 /* we might have been given the whole lot anyway */
3274 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3275 *more_strings = False;
3279 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3281 /* look for Range result */
3282 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3284 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3285 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3286 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3294 /* nothing here - this field is just empty */
3295 *more_strings = False;
3299 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3300 &range_start, &range_end) == 2) {
3301 *more_strings = True;
3303 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3304 &range_start) == 1) {
3305 *more_strings = False;
3307 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3309 ldap_memfree(range_attr);
3310 *more_strings = False;
3315 if ((*num_strings) != range_start) {
3316 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3317 " - aborting range retrieval\n",
3318 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3319 ldap_memfree(range_attr);
3320 *more_strings = False;
3324 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3326 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3327 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3328 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3329 range_attr, (unsigned long int)range_end - range_start + 1,
3330 (unsigned long int)num_new_strings));
3331 ldap_memfree(range_attr);
3332 *more_strings = False;
3336 strings = talloc_realloc(mem_ctx, current_strings, char *,
3337 *num_strings + num_new_strings);
3339 if (strings == NULL) {
3340 ldap_memfree(range_attr);
3341 *more_strings = False;
3345 if (new_strings && num_new_strings) {
3346 memcpy(&strings[*num_strings], new_strings,
3347 sizeof(*new_strings) * num_new_strings);
3350 (*num_strings) += num_new_strings;
3352 if (*more_strings) {
3353 *next_attribute = talloc_asprintf(mem_ctx,
3358 if (!*next_attribute) {
3359 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3360 ldap_memfree(range_attr);
3361 *more_strings = False;
3366 ldap_memfree(range_attr);
3372 * pull a single uint32_t from a ADS result
3373 * @param ads connection to ads server
3374 * @param msg Results of search
3375 * @param field Attribute to retrieve
3376 * @param v Pointer to int to store result
3377 * @return boolean indicating success
3379 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3384 values = ldap_get_values(ads->ldap.ld, msg, field);
3388 ldap_value_free(values);
3392 *v = atoi(values[0]);
3393 ldap_value_free(values);
3398 * pull a single objectGUID from an ADS result
3399 * @param ads connection to ADS server
3400 * @param msg results of search
3401 * @param guid 37-byte area to receive text guid
3402 * @return boolean indicating success
3404 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3409 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3414 status = GUID_from_ndr_blob(&blob, guid);
3415 talloc_free(blob.data);
3416 return NT_STATUS_IS_OK(status);
3421 * pull a single struct dom_sid from a ADS result
3422 * @param ads connection to ads server
3423 * @param msg Results of search
3424 * @param field Attribute to retrieve
3425 * @param sid Pointer to sid to store result
3426 * @return boolean indicating success
3428 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3429 struct dom_sid *sid)
3431 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3435 * pull an array of struct dom_sids from a ADS result
3436 * @param ads connection to ads server
3437 * @param mem_ctx TALLOC_CTX for allocating sid array
3438 * @param msg Results of search
3439 * @param field Attribute to retrieve
3440 * @param sids pointer to sid array to allocate
3441 * @return the count of SIDs pulled
3443 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3444 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3446 struct berval **values;
3449 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3454 for (i=0; values[i]; i++)
3458 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3460 ldap_value_free_len(values);
3468 for (i=0; values[i]; i++) {
3470 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3471 values[i]->bv_len, &(*sids)[count]);
3473 struct dom_sid_buf buf;
3474 DBG_DEBUG("pulling SID: %s\n",
3475 dom_sid_str_buf(&(*sids)[count], &buf));
3480 ldap_value_free_len(values);
3485 * pull a struct security_descriptor from a ADS result
3486 * @param ads connection to ads server
3487 * @param mem_ctx TALLOC_CTX for allocating sid array
3488 * @param msg Results of search
3489 * @param field Attribute to retrieve
3490 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3491 * @return boolean indicating success
3493 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3494 LDAPMessage *msg, const char *field,
3495 struct security_descriptor **sd)
3497 struct berval **values;
3500 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3502 if (!values) return false;
3506 status = unmarshall_sec_desc(mem_ctx,
3507 (uint8_t *)values[0]->bv_val,
3508 values[0]->bv_len, sd);
3509 if (!NT_STATUS_IS_OK(status)) {
3510 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3511 nt_errstr(status)));
3516 ldap_value_free_len(values);
3521 * in order to support usernames longer than 21 characters we need to
3522 * use both the sAMAccountName and the userPrincipalName attributes
3523 * It seems that not all users have the userPrincipalName attribute set
3525 * @param ads connection to ads server
3526 * @param mem_ctx TALLOC_CTX for allocating sid array
3527 * @param msg Results of search
3528 * @return the username
3530 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3536 /* lookup_name() only works on the sAMAccountName to
3537 returning the username portion of userPrincipalName
3538 breaks winbindd_getpwnam() */
3540 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3541 if (ret && (p = strchr_m(ret, '@'))) {
3546 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3551 * find the update serial number - this is the core of the ldap cache
3552 * @param ads connection to ads server
3553 * @param ads connection to ADS server
3554 * @param usn Pointer to retrieved update serial number
3555 * @return status of search
3557 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3559 const char *attrs[] = {"highestCommittedUSN", NULL};
3563 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3564 if (!ADS_ERR_OK(status))
3567 if (ads_count_replies(ads, res) != 1) {
3568 ads_msgfree(ads, res);
3569 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3572 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3573 ads_msgfree(ads, res);
3574 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3577 ads_msgfree(ads, res);
3581 /* parse a ADS timestring - typical string is
3582 '20020917091222.0Z0' which means 09:12.22 17th September
3584 static time_t ads_parse_time(const char *str)
3590 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3591 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3592 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3601 /********************************************************************
3602 ********************************************************************/
3604 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3606 const char *attrs[] = {"currentTime", NULL};
3610 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3611 ADS_STRUCT *ads_s = ads;
3613 /* establish a new ldap tcp session if necessary */
3615 if ( !ads->ldap.ld ) {
3617 * ADS_STRUCT may be being reused after a
3618 * DC lookup, so ads->ldap.ss may already have a
3619 * good address. If not, re-initialize the passed-in
3620 * ADS_STRUCT with the given server.XXXX parameters.
3622 * Note that this doesn't depend on
3623 * ads->server.ldap_server != NULL,
3624 * as the case where ads->server.ldap_server==NULL and
3625 * ads->ldap.ss != zero_address is precisely the DC
3626 * lookup case where ads->ldap.ss was found by going
3627 * through ads_find_dc() again we want to avoid repeating.
3629 if (is_zero_addr(&ads->ldap.ss)) {
3630 ads_s = ads_init(tmp_ctx,
3632 ads->server.workgroup,
3633 ads->server.ldap_server,
3635 if (ads_s == NULL) {
3636 status = ADS_ERROR(LDAP_NO_MEMORY);
3642 * Reset ads->config.flags as it can contain the flags
3643 * returned by the previous CLDAP ping when reusing the struct.
3645 ads_s->config.flags = 0;
3647 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3648 status = ads_connect( ads_s );
3649 if ( !ADS_ERR_OK(status))
3653 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3654 if (!ADS_ERR_OK(status)) {
3658 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3660 ads_msgfree(ads_s, res);
3661 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3665 /* but save the time and offset in the original ADS_STRUCT */
3667 ads->config.current_time = ads_parse_time(timestr);
3669 if (ads->config.current_time != 0) {
3670 ads->auth.time_offset = ads->config.current_time - time(NULL);
3671 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3674 ads_msgfree(ads, res);
3676 status = ADS_SUCCESS;
3679 TALLOC_FREE(tmp_ctx);
3684 /********************************************************************
3685 ********************************************************************/
3687 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3689 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3690 const char *attrs[] = {"domainFunctionality", NULL};
3693 ADS_STRUCT *ads_s = ads;
3695 *val = DS_DOMAIN_FUNCTION_2000;
3697 /* establish a new ldap tcp session if necessary */
3699 if ( !ads->ldap.ld ) {
3701 * ADS_STRUCT may be being reused after a
3702 * DC lookup, so ads->ldap.ss may already have a
3703 * good address. If not, re-initialize the passed-in
3704 * ADS_STRUCT with the given server.XXXX parameters.
3706 * Note that this doesn't depend on
3707 * ads->server.ldap_server != NULL,
3708 * as the case where ads->server.ldap_server==NULL and
3709 * ads->ldap.ss != zero_address is precisely the DC
3710 * lookup case where ads->ldap.ss was found by going
3711 * through ads_find_dc() again we want to avoid repeating.
3713 if (is_zero_addr(&ads->ldap.ss)) {
3714 ads_s = ads_init(tmp_ctx,
3716 ads->server.workgroup,
3717 ads->server.ldap_server,
3719 if (ads_s == NULL ) {
3720 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3726 * Reset ads->config.flags as it can contain the flags
3727 * returned by the previous CLDAP ping when reusing the struct.
3729 ads_s->config.flags = 0;
3731 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3732 status = ads_connect( ads_s );
3733 if ( !ADS_ERR_OK(status))
3737 /* If the attribute does not exist assume it is a Windows 2000
3738 functional domain */
3740 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3741 if (!ADS_ERR_OK(status)) {
3742 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3743 status = ADS_SUCCESS;
3748 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3749 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3751 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3754 ads_msgfree(ads_s, res);
3757 TALLOC_FREE(tmp_ctx);
3763 * find the domain sid for our domain
3764 * @param ads connection to ads server
3765 * @param sid Pointer to domain sid
3766 * @return status of search
3768 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3770 const char *attrs[] = {"objectSid", NULL};
3774 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3776 if (!ADS_ERR_OK(rc)) return rc;
3777 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3778 ads_msgfree(ads, res);
3779 return ADS_ERROR_SYSTEM(ENOENT);
3781 ads_msgfree(ads, res);
3787 * find our site name
3788 * @param ads connection to ads server
3789 * @param mem_ctx Pointer to talloc context
3790 * @param site_name Pointer to the sitename
3791 * @return status of search
3793 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3797 const char *dn, *service_name;
3798 const char *attrs[] = { "dsServiceName", NULL };
3800 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3801 if (!ADS_ERR_OK(status)) {
3805 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3806 if (service_name == NULL) {
3807 ads_msgfree(ads, res);
3808 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3811 ads_msgfree(ads, res);
3813 /* go up three levels */
3814 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3816 return ADS_ERROR(LDAP_NO_MEMORY);
3819 *site_name = talloc_strdup(mem_ctx, dn);
3820 if (*site_name == NULL) {
3821 return ADS_ERROR(LDAP_NO_MEMORY);
3826 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3831 * find the site dn where a machine resides
3832 * @param ads connection to ads server
3833 * @param mem_ctx Pointer to talloc context
3834 * @param computer_name name of the machine
3835 * @param site_name Pointer to the sitename
3836 * @return status of search
3838 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3842 const char *parent, *filter;
3843 char *config_context = NULL;
3846 /* shortcut a query */
3847 if (strequal(computer_name, ads->config.ldap_server_name)) {
3848 return ads_site_dn(ads, mem_ctx, site_dn);
3851 status = ads_config_path(ads, mem_ctx, &config_context);
3852 if (!ADS_ERR_OK(status)) {
3856 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3857 if (filter == NULL) {
3858 return ADS_ERROR(LDAP_NO_MEMORY);
3861 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3862 filter, NULL, &res);
3863 if (!ADS_ERR_OK(status)) {
3867 if (ads_count_replies(ads, res) != 1) {
3868 ads_msgfree(ads, res);
3869 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3872 dn = ads_get_dn(ads, mem_ctx, res);
3874 ads_msgfree(ads, res);
3875 return ADS_ERROR(LDAP_NO_MEMORY);
3878 /* go up three levels */
3879 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3880 if (parent == NULL) {
3881 ads_msgfree(ads, res);
3883 return ADS_ERROR(LDAP_NO_MEMORY);
3886 *site_dn = talloc_strdup(mem_ctx, parent);
3887 if (*site_dn == NULL) {
3888 ads_msgfree(ads, res);
3890 return ADS_ERROR(LDAP_NO_MEMORY);
3894 ads_msgfree(ads, res);
3900 * get the upn suffixes for a domain
3901 * @param ads connection to ads server
3902 * @param mem_ctx Pointer to talloc context
3903 * @param suffixes Pointer to an array of suffixes
3904 * @param num_suffixes Pointer to the number of suffixes
3905 * @return status of search
3907 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3912 char *config_context = NULL;
3913 const char *attrs[] = { "uPNSuffixes", NULL };
3915 status = ads_config_path(ads, mem_ctx, &config_context);
3916 if (!ADS_ERR_OK(status)) {
3920 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3922 return ADS_ERROR(LDAP_NO_MEMORY);
3925 status = ads_search_dn(ads, &res, base, attrs);
3926 if (!ADS_ERR_OK(status)) {
3930 if (ads_count_replies(ads, res) != 1) {
3931 ads_msgfree(ads, res);
3932 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3935 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3936 if ((*suffixes) == NULL) {
3937 ads_msgfree(ads, res);
3938 return ADS_ERROR(LDAP_NO_MEMORY);
3941 ads_msgfree(ads, res);
3947 * get the joinable ous for a domain
3948 * @param ads connection to ads server
3949 * @param mem_ctx Pointer to talloc context
3950 * @param ous Pointer to an array of ous
3951 * @param num_ous Pointer to the number of ous
3952 * @return status of search
3954 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3955 TALLOC_CTX *mem_ctx,
3960 LDAPMessage *res = NULL;
3961 LDAPMessage *msg = NULL;
3962 const char *attrs[] = { "dn", NULL };
3965 status = ads_search(ads, &res,
3966 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3968 if (!ADS_ERR_OK(status)) {
3972 count = ads_count_replies(ads, res);
3974 ads_msgfree(ads, res);
3975 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3978 for (msg = ads_first_entry(ads, res); msg;
3979 msg = ads_next_entry(ads, msg)) {
3980 const char **p = discard_const_p(const char *, *ous);
3983 dn = ads_get_dn(ads, talloc_tos(), msg);
3985 ads_msgfree(ads, res);
3986 return ADS_ERROR(LDAP_NO_MEMORY);
3989 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3991 ads_msgfree(ads, res);
3992 return ADS_ERROR(LDAP_NO_MEMORY);
3996 *ous = discard_const_p(char *, p);
3999 ads_msgfree(ads, res);
4006 * pull a struct dom_sid from an extended dn string
4007 * @param mem_ctx TALLOC_CTX
4008 * @param extended_dn string
4009 * @param flags string type of extended_dn
4010 * @param sid pointer to a struct dom_sid
4011 * @return NT_STATUS_OK on success,
4012 * NT_INVALID_PARAMETER on error,
4013 * NT_STATUS_NOT_FOUND if no SID present
4015 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
4016 const char *extended_dn,
4017 enum ads_extended_dn_flags flags,
4018 struct dom_sid *sid)
4023 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4026 /* otherwise extended_dn gets stripped off */
4027 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
4028 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4031 * ADS_EXTENDED_DN_HEX_STRING:
4032 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4034 * ADS_EXTENDED_DN_STRING (only with w2k3):
4035 * <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
4037 * Object with no SID, such as an Exchange Public Folder
4038 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4041 p = strchr(dn, ';');
4043 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4046 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
4047 DEBUG(5,("No SID present in extended dn\n"));
4048 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
4051 p += strlen(";<SID=");
4055 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4060 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
4064 case ADS_EXTENDED_DN_STRING:
4065 if (!string_to_sid(sid, p)) {
4066 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4069 case ADS_EXTENDED_DN_HEX_STRING: {
4074 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
4076 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4079 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
4081 DEBUG(10,("failed to parse sid\n"));
4082 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4087 DEBUG(10,("unknown extended dn format\n"));
4088 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4091 return ADS_ERROR_NT(NT_STATUS_OK);
4094 /********************************************************************
4095 ********************************************************************/
4097 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4099 LDAPMessage *res = NULL;
4104 status = ads_find_machine_acct(ads, &res, machine_name);
4105 if (!ADS_ERR_OK(status)) {
4106 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4107 lp_netbios_name()));
4111 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4112 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4116 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4117 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4121 ads_msgfree(ads, res);
4126 /********************************************************************
4127 ********************************************************************/
4129 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4130 LDAPMessage *msg, size_t *num_values)
4132 const char *field = "msDS-AdditionalDnsHostName";
4133 struct berval **values = NULL;
4135 size_t i, converted_size;
4138 * Windows DC implicitly adds a short name for each FQDN added to
4139 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4140 * suffix "\0$" which we should ignore (see bug #14406).
4143 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4144 if (values == NULL) {
4148 *num_values = ldap_count_values_len(values);
4150 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4152 ldap_value_free_len(values);
4156 for (i = 0; i < *num_values; i++) {
4158 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4160 strnlen(values[i]->bv_val,
4162 &ret[i], &converted_size)) {
4163 ldap_value_free_len(values);
4169 ldap_value_free_len(values);
4173 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4175 const char *machine_name,
4176 char ***hostnames_array,
4177 size_t *num_hostnames)
4180 LDAPMessage *res = NULL;
4183 status = ads_find_machine_acct(ads,
4186 if (!ADS_ERR_OK(status)) {
4187 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4192 count = ads_count_replies(ads, res);
4194 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4198 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4199 if (*hostnames_array == NULL) {
4200 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4202 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4207 ads_msgfree(ads, res);
4212 /********************************************************************
4213 ********************************************************************/
4215 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4217 LDAPMessage *res = NULL;
4222 status = ads_find_machine_acct(ads, &res, machine_name);
4223 if (!ADS_ERR_OK(status)) {
4224 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4225 lp_netbios_name()));
4229 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4230 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4234 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4235 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4239 ads_msgfree(ads, res);
4244 /********************************************************************
4245 ********************************************************************/
4247 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4249 LDAPMessage *res = NULL;
4255 status = ads_find_machine_acct(ads, &res, machine_name);
4256 if (!ADS_ERR_OK(status)) {
4257 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4258 lp_netbios_name()));
4262 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4263 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4267 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4268 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4272 ads_msgfree(ads, res);
4274 ok = (strlen(name) > 0);
4282 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4285 * Join a machine to a realm
4286 * Creates the machine account and sets the machine password
4287 * @param ads connection to ads server
4288 * @param machine name of host to add
4289 * @param org_unit Organizational unit to place machine in
4290 * @return status of join
4292 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4293 uint32_t account_type, const char *org_unit)
4296 LDAPMessage *res = NULL;
4299 /* machine name must be lowercase */
4300 machine = SMB_STRDUP(machine_name);
4301 strlower_m(machine);
4304 status = ads_find_machine_acct(ads, (void **)&res, machine);
4305 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4306 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4307 status = ads_leave_realm(ads, machine);
4308 if (!ADS_ERR_OK(status)) {
4309 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4310 machine, ads->config.realm));
4315 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4316 if (!ADS_ERR_OK(status)) {
4317 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4322 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4323 if (!ADS_ERR_OK(status)) {
4324 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4330 ads_msgfree(ads, res);
4337 * Delete a machine from the realm
4338 * @param ads connection to ads server
4339 * @param hostname Machine to remove
4340 * @return status of delete
4342 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4347 char *hostnameDN, *host;
4349 LDAPControl ldap_control;
4350 LDAPControl * pldap_control[2] = {NULL, NULL};
4352 pldap_control[0] = &ldap_control;
4353 memset(&ldap_control, 0, sizeof(LDAPControl));
4354 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4356 /* hostname must be lowercase */
4357 host = SMB_STRDUP(hostname);
4358 if (!strlower_m(host)) {
4360 return ADS_ERROR_SYSTEM(EINVAL);
4363 status = ads_find_machine_acct(ads, &res, host);
4364 if (!ADS_ERR_OK(status)) {
4365 DEBUG(0, ("Host account for %s does not exist.\n", host));
4370 msg = ads_first_entry(ads, res);
4373 return ADS_ERROR_SYSTEM(ENOENT);
4376 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4377 if (hostnameDN == NULL) {
4379 return ADS_ERROR_SYSTEM(ENOENT);
4382 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4384 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4386 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4389 if (rc != LDAP_SUCCESS) {
4390 const char *attrs[] = { "cn", NULL };
4391 LDAPMessage *msg_sub;
4393 /* we only search with scope ONE, we do not expect any further
4394 * objects to be created deeper */
4396 status = ads_do_search_retry(ads, hostnameDN,
4397 LDAP_SCOPE_ONELEVEL,
4398 "(objectclass=*)", attrs, &res);
4400 if (!ADS_ERR_OK(status)) {
4402 TALLOC_FREE(hostnameDN);
4406 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4407 msg_sub = ads_next_entry(ads, msg_sub)) {
4411 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4413 TALLOC_FREE(hostnameDN);
4414 return ADS_ERROR(LDAP_NO_MEMORY);
4417 status = ads_del_dn(ads, dn);
4418 if (!ADS_ERR_OK(status)) {
4419 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4422 TALLOC_FREE(hostnameDN);
4429 /* there should be no subordinate objects anymore */
4430 status = ads_do_search_retry(ads, hostnameDN,
4431 LDAP_SCOPE_ONELEVEL,
4432 "(objectclass=*)", attrs, &res);
4434 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4436 TALLOC_FREE(hostnameDN);
4440 /* delete hostnameDN now */
4441 status = ads_del_dn(ads, hostnameDN);
4442 if (!ADS_ERR_OK(status)) {
4444 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4445 TALLOC_FREE(hostnameDN);
4450 TALLOC_FREE(hostnameDN);
4452 status = ads_find_machine_acct(ads, &res, host);
4453 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4454 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4455 DEBUG(3, ("Failed to remove host account.\n"));
4465 * pull all token-sids from an LDAP dn
4466 * @param ads connection to ads server
4467 * @param mem_ctx TALLOC_CTX for allocating sid array
4468 * @param dn of LDAP object
4469 * @param user_sid pointer to struct dom_sid (objectSid)
4470 * @param primary_group_sid pointer to struct dom_sid (self composed)
4471 * @param sids pointer to sid array to allocate
4472 * @param num_sids counter of SIDs pulled
4473 * @return status of token query
4475 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4476 TALLOC_CTX *mem_ctx,
4478 struct dom_sid *user_sid,
4479 struct dom_sid *primary_group_sid,
4480 struct dom_sid **sids,
4484 LDAPMessage *res = NULL;
4486 size_t tmp_num_sids;
4487 struct dom_sid *tmp_sids;
4488 struct dom_sid tmp_user_sid;
4489 struct dom_sid tmp_primary_group_sid;
4491 const char *attrs[] = {
4498 status = ads_search_retry_dn(ads, &res, dn, attrs);
4499 if (!ADS_ERR_OK(status)) {
4503 count = ads_count_replies(ads, res);
4505 ads_msgfree(ads, res);
4506 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4509 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4510 ads_msgfree(ads, res);
4511 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4514 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4515 ads_msgfree(ads, res);
4516 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4520 /* hack to compose the primary group sid without knowing the
4523 struct dom_sid domsid;
4525 sid_copy(&domsid, &tmp_user_sid);
4527 if (!sid_split_rid(&domsid, NULL)) {
4528 ads_msgfree(ads, res);
4529 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4532 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4533 ads_msgfree(ads, res);
4534 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4538 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4540 if (tmp_num_sids == 0 || !tmp_sids) {
4541 ads_msgfree(ads, res);
4542 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4546 *num_sids = tmp_num_sids;
4554 *user_sid = tmp_user_sid;
4557 if (primary_group_sid) {
4558 *primary_group_sid = tmp_primary_group_sid;
4561 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4563 ads_msgfree(ads, res);
4564 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4568 * Find a sAMAccountName in LDAP
4569 * @param ads connection to ads server
4570 * @param mem_ctx TALLOC_CTX for allocating sid array
4571 * @param samaccountname to search
4572 * @param uac_ret uint32_t pointer userAccountControl attribute value
4573 * @param dn_ret pointer to dn
4574 * @return status of token query
4576 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4577 TALLOC_CTX *mem_ctx,
4578 const char *samaccountname,
4580 const char **dn_ret)
4583 const char *attrs[] = { "userAccountControl", NULL };
4585 LDAPMessage *res = NULL;
4589 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4591 if (filter == NULL) {
4592 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4596 status = ads_do_search_all(ads, ads->config.bind_path,
4598 filter, attrs, &res);
4600 if (!ADS_ERR_OK(status)) {
4604 if (ads_count_replies(ads, res) != 1) {
4605 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4609 dn = ads_get_dn(ads, talloc_tos(), res);
4611 status = ADS_ERROR(LDAP_NO_MEMORY);
4615 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4616 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4625 *dn_ret = talloc_strdup(mem_ctx, dn);
4627 status = ADS_ERROR(LDAP_NO_MEMORY);
4633 ads_msgfree(ads, res);
4639 * find our configuration path
4640 * @param ads connection to ads server
4641 * @param mem_ctx Pointer to talloc context
4642 * @param config_path Pointer to the config path
4643 * @return status of search
4645 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4646 TALLOC_CTX *mem_ctx,
4650 LDAPMessage *res = NULL;
4651 const char *config_context = NULL;
4652 const char *attrs[] = { "configurationNamingContext", NULL };
4654 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4655 "(objectclass=*)", attrs, &res);
4656 if (!ADS_ERR_OK(status)) {
4660 config_context = ads_pull_string(ads, mem_ctx, res,
4661 "configurationNamingContext");
4662 ads_msgfree(ads, res);
4663 if (!config_context) {
4664 return ADS_ERROR(LDAP_NO_MEMORY);
4668 *config_path = talloc_strdup(mem_ctx, config_context);
4669 if (!*config_path) {
4670 return ADS_ERROR(LDAP_NO_MEMORY);
4674 return ADS_ERROR(LDAP_SUCCESS);
4678 * find the displayName of an extended right
4679 * @param ads connection to ads server
4680 * @param config_path The config path
4681 * @param mem_ctx Pointer to talloc context
4682 * @param GUID struct of the rightsGUID
4683 * @return status of search
4685 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4686 const char *config_path,
4687 TALLOC_CTX *mem_ctx,
4688 const struct GUID *rights_guid)
4691 LDAPMessage *res = NULL;
4693 const char *attrs[] = { "displayName", NULL };
4694 const char *result = NULL;
4697 if (!ads || !mem_ctx || !rights_guid) {
4701 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4702 GUID_string(mem_ctx, rights_guid));
4707 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4712 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4714 if (!ADS_ERR_OK(rc)) {
4718 if (ads_count_replies(ads, res) != 1) {
4722 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4725 ads_msgfree(ads, res);
4730 * verify or build and verify an account ou
4731 * @param mem_ctx Pointer to talloc context
4732 * @param ads connection to ads server
4734 * @return status of search
4737 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4739 const char **account_ou)
4745 if (account_ou == NULL) {
4746 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4749 if (*account_ou != NULL) {
4750 exploded_dn = ldap_explode_dn(*account_ou, 0);
4752 ldap_value_free(exploded_dn);
4757 ou_string = ads_ou_string(ads, *account_ou);
4759 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4762 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4763 ads->config.bind_path);
4764 SAFE_FREE(ou_string);
4767 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4770 exploded_dn = ldap_explode_dn(name, 0);
4772 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4774 ldap_value_free(exploded_dn);