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 zero_sockaddr(&existing_ss);
829 * ads_connect can be passed in a reused ADS_STRUCT
830 * with an existing non-zero ads->ldap.ss IP address
831 * that was stored by going through ads_find_dc()
832 * if ads->server.ldap_server was NULL.
834 * If ads->server.ldap_server is still NULL but
835 * the target address isn't the zero address, then
836 * store that address off off before zeroing out
837 * ads->ldap so we don't keep doing multiple calls
838 * to ads_find_dc() in the reuse case.
840 * If a caller wants a clean ADS_STRUCT they
841 * will TALLOC_FREE it and allocate a new one
842 * by calling ads_init(), which ensures
843 * ads->ldap.ss is a properly zero'ed out valid IP
846 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
847 /* Save off the address we previously found by ads_find_dc(). */
848 existing_ss = ads->ldap.ss;
852 ZERO_STRUCT(ads->ldap_wrap_data);
853 ads->ldap.last_attempt = time_mono(NULL);
854 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
856 /* try with a user specified server */
858 if (DEBUGLEVEL >= 11) {
859 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
860 DEBUG(11,("ads_connect: entering\n"));
861 DEBUGADD(11,("%s\n", s));
865 if (ads->server.ldap_server) {
867 struct sockaddr_storage ss;
869 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
870 ads->server.ldap_server);
871 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
873 DEBUG(5,("ads_connect: unable to resolve name %s\n",
874 ads->server.ldap_server));
875 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
879 if (is_zero_addr(&ss)) {
880 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
884 ok = ads_try_connect(ads, ads->server.gc, &ss);
889 /* The choice of which GC use is handled one level up in
890 ads_connect_gc(). If we continue on from here with
891 ads_find_dc() we will get GC searches on port 389 which
892 doesn't work. --jerry */
894 if (ads->server.gc == true) {
895 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
898 if (ads->server.no_fallback) {
899 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
904 if (!is_zero_addr(&existing_ss)) {
905 /* We saved off who we should talk to. */
906 bool ok = ads_try_connect(ads,
913 * Keep trying to find a server and fall through
914 * into ads_find_dc() again.
916 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
917 "trying to find another DC.\n");
920 ntstatus = ads_find_dc(ads);
921 if (NT_STATUS_IS_OK(ntstatus)) {
925 status = ADS_ERROR_NT(ntstatus);
930 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
931 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
933 if (!ads->auth.user_name) {
934 /* Must use the userPrincipalName value here or sAMAccountName
935 and not servicePrincipalName; found by Guenther Deschner */
936 ads->auth.user_name = talloc_asprintf(ads,
939 if (ads->auth.user_name == NULL) {
940 DBG_ERR("talloc_asprintf failed\n");
941 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
946 if (ads->auth.realm == NULL) {
947 ads->auth.realm = talloc_strdup(ads, ads->config.realm);
948 if (ads->auth.realm == NULL) {
949 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
954 if (!ads->auth.kdc_server) {
955 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
956 ads->auth.kdc_server = talloc_strdup(ads, addr);
957 if (ads->auth.kdc_server == NULL) {
958 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
963 /* If the caller() requested no LDAP bind, then we are done */
965 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
966 status = ADS_SUCCESS;
970 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
971 if (!ads->ldap_wrap_data.mem_ctx) {
972 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
976 /* Otherwise setup the TCP LDAP session */
978 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
980 ads->ldap.port, lp_ldap_timeout());
981 if (ads->ldap.ld == NULL) {
982 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
985 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
987 /* cache the successful connection for workgroup and realm */
988 if (ads_closest_dc(ads)) {
989 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
990 saf_store( ads->server.realm, ads->config.ldap_server_name);
993 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
995 /* fill in the current time and offsets */
997 status = ads_current_time( ads );
998 if ( !ADS_ERR_OK(status) ) {
1002 /* Now do the bind */
1004 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
1005 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
1009 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
1010 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
1014 status = ads_sasl_bind(ads);
1017 if (DEBUGLEVEL >= 11) {
1018 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1019 DEBUG(11,("ads_connect: leaving with: %s\n",
1020 ads_errstr(status)));
1021 DEBUGADD(11,("%s\n", s));
1029 * Connect to the LDAP server using given credentials
1030 * @param ads Pointer to an existing ADS_STRUCT
1031 * @return status of connection
1033 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
1035 ads->auth.flags |= ADS_AUTH_USER_CREDS;
1037 return ads_connect(ads);
1041 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1042 * @param ads Pointer to an existing ADS_STRUCT
1044 * Sets the ads->ldap.ss to a valid
1045 * zero ip address that can be detected by
1046 * our is_zero_addr() function. Otherwise
1047 * it is left as AF_UNSPEC (0).
1049 void ads_zero_ldap(ADS_STRUCT *ads)
1051 ZERO_STRUCT(ads->ldap);
1053 * Initialize the sockaddr_storage so we can use
1054 * sockaddr test functions against it.
1056 zero_sockaddr(&ads->ldap.ss);
1060 * Disconnect the LDAP server
1061 * @param ads Pointer to an existing ADS_STRUCT
1063 void ads_disconnect(ADS_STRUCT *ads)
1066 ldap_unbind(ads->ldap.ld);
1067 ads->ldap.ld = NULL;
1069 if (ads->ldap_wrap_data.wrap_ops &&
1070 ads->ldap_wrap_data.wrap_ops->disconnect) {
1071 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1073 if (ads->ldap_wrap_data.mem_ctx) {
1074 talloc_free(ads->ldap_wrap_data.mem_ctx);
1077 ZERO_STRUCT(ads->ldap_wrap_data);
1081 Duplicate a struct berval into talloc'ed memory
1083 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1085 struct berval *value;
1087 if (!in_val) return NULL;
1089 value = talloc_zero(ctx, struct berval);
1092 if (in_val->bv_len == 0) return value;
1094 value->bv_len = in_val->bv_len;
1095 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1101 Make a values list out of an array of (struct berval *)
1103 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1104 const struct berval **in_vals)
1106 struct berval **values;
1109 if (!in_vals) return NULL;
1110 for (i=0; in_vals[i]; i++)
1111 ; /* count values */
1112 values = talloc_zero_array(ctx, struct berval *, i+1);
1113 if (!values) return NULL;
1115 for (i=0; in_vals[i]; i++) {
1116 values[i] = dup_berval(ctx, in_vals[i]);
1122 UTF8-encode a values list out of an array of (char *)
1124 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1130 if (!in_vals) return NULL;
1131 for (i=0; in_vals[i]; i++)
1132 ; /* count values */
1133 values = talloc_zero_array(ctx, char *, i+1);
1134 if (!values) return NULL;
1136 for (i=0; in_vals[i]; i++) {
1137 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1138 TALLOC_FREE(values);
1146 Pull a (char *) array out of a UTF8-encoded values list
1148 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1152 size_t converted_size;
1154 if (!in_vals) return NULL;
1155 for (i=0; in_vals[i]; i++)
1156 ; /* count values */
1157 values = talloc_zero_array(ctx, char *, i+1);
1158 if (!values) return NULL;
1160 for (i=0; in_vals[i]; i++) {
1161 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1163 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1164 "%s\n", strerror(errno)));
1171 * Do a search with paged results. cookie must be null on the first
1172 * call, and then returned on each subsequent call. It will be null
1173 * again when the entire search is complete
1174 * @param ads connection to ads server
1175 * @param bind_path Base dn for the search
1176 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1177 * @param expr Search expression - specified in local charset
1178 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1179 * @param res ** which will contain results - free res* with ads_msgfree()
1180 * @param count Number of entries retrieved on this page
1181 * @param cookie The paged results cookie to be returned on subsequent calls
1182 * @return status of search
1184 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1185 const char *bind_path,
1186 int scope, const char *expr,
1187 const char **attrs, void *args,
1189 int *count, struct berval **cookie)
1192 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1193 size_t converted_size;
1194 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1195 BerElement *cookie_be = NULL;
1196 struct berval *cookie_bv= NULL;
1197 BerElement *ext_be = NULL;
1198 struct berval *ext_bv= NULL;
1201 ads_control *external_control = (ads_control *) args;
1205 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1206 return ADS_ERROR(LDAP_NO_MEMORY);
1208 /* 0 means the conversion worked but the result was empty
1209 so we only fail if it's -1. In any case, it always
1210 at least nulls out the dest */
1211 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1212 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1214 rc = LDAP_NO_MEMORY;
1218 if (!attrs || !(*attrs))
1219 search_attrs = NULL;
1221 /* This would be the utf8-encoded version...*/
1222 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1223 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1224 rc = LDAP_NO_MEMORY;
1229 /* Paged results only available on ldap v3 or later */
1230 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1231 if (version < LDAP_VERSION3) {
1232 rc = LDAP_NOT_SUPPORTED;
1236 cookie_be = ber_alloc_t(LBER_USE_DER);
1238 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1239 ber_bvfree(*cookie); /* don't need it from last time */
1242 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1244 ber_flatten(cookie_be, &cookie_bv);
1245 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1246 PagedResults.ldctl_iscritical = (char) 1;
1247 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1248 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1250 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1251 NoReferrals.ldctl_iscritical = (char) 0;
1252 NoReferrals.ldctl_value.bv_len = 0;
1253 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1255 if (external_control &&
1256 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1257 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1259 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1260 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1262 /* win2k does not accept a ldctl_value being passed in */
1264 if (external_control->val != 0) {
1266 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1267 rc = LDAP_NO_MEMORY;
1271 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1272 rc = LDAP_NO_MEMORY;
1275 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1276 rc = LDAP_NO_MEMORY;
1280 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1281 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1284 ExternalCtrl.ldctl_value.bv_len = 0;
1285 ExternalCtrl.ldctl_value.bv_val = NULL;
1288 controls[0] = &NoReferrals;
1289 controls[1] = &PagedResults;
1290 controls[2] = &ExternalCtrl;
1294 controls[0] = &NoReferrals;
1295 controls[1] = &PagedResults;
1299 /* we need to disable referrals as the openldap libs don't
1300 handle them and paged results at the same time. Using them
1301 together results in the result record containing the server
1302 page control being removed from the result list (tridge/jmcd)
1304 leaving this in despite the control that says don't generate
1305 referrals, in case the server doesn't support it (jmcd)
1307 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1309 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1310 search_attrs, 0, controls,
1311 NULL, LDAP_NO_LIMIT,
1312 (LDAPMessage **)res);
1314 ber_free(cookie_be, 1);
1315 ber_bvfree(cookie_bv);
1318 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1319 ldap_err2string(rc)));
1320 if (rc == LDAP_OTHER) {
1324 ret = ldap_parse_result(ads->ldap.ld,
1332 if (ret == LDAP_SUCCESS) {
1333 DEBUG(3, ("ldap_search_with_timeout(%s) "
1334 "error: %s\n", expr, ldap_errmsg));
1335 ldap_memfree(ldap_errmsg);
1341 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1342 NULL, &rcontrols, 0);
1348 for (i=0; rcontrols[i]; i++) {
1349 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1350 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1351 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1353 /* the berval is the cookie, but must be freed when
1355 if (cookie_bv->bv_len) /* still more to do */
1356 *cookie=ber_bvdup(cookie_bv);
1359 ber_bvfree(cookie_bv);
1360 ber_free(cookie_be, 1);
1364 ldap_controls_free(rcontrols);
1367 talloc_destroy(ctx);
1370 ber_free(ext_be, 1);
1377 if (rc != LDAP_SUCCESS && *res != NULL) {
1378 ads_msgfree(ads, *res);
1382 /* if/when we decide to utf8-encode attrs, take out this next line */
1383 TALLOC_FREE(search_attrs);
1385 return ADS_ERROR(rc);
1388 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1389 int scope, const char *expr,
1390 const char **attrs, LDAPMessage **res,
1391 int *count, struct berval **cookie)
1393 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1398 * Get all results for a search. This uses ads_do_paged_search() to return
1399 * all entries in a large search.
1400 * @param ads connection to ads server
1401 * @param bind_path Base dn for the search
1402 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1403 * @param expr Search expression
1404 * @param attrs Attributes to retrieve
1405 * @param res ** which will contain results - free res* with ads_msgfree()
1406 * @return status of search
1408 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1409 int scope, const char *expr,
1410 const char **attrs, void *args,
1413 struct berval *cookie = NULL;
1418 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1421 if (!ADS_ERR_OK(status))
1424 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1426 LDAPMessage *res2 = NULL;
1427 LDAPMessage *msg, *next;
1429 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1430 attrs, args, &res2, &count, &cookie);
1431 if (!ADS_ERR_OK(status)) {
1435 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1436 that this works on all ldap libs, but I have only tested with openldap */
1437 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1438 next = ads_next_message(ads, msg);
1439 ldap_add_result_entry((LDAPMessage **)res, msg);
1441 /* note that we do not free res2, as the memory is now
1442 part of the main returned list */
1445 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1446 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1452 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1453 int scope, const char *expr,
1454 const char **attrs, LDAPMessage **res)
1456 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1459 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1460 int scope, const char *expr,
1461 const char **attrs, uint32_t sd_flags,
1466 args.control = ADS_SD_FLAGS_OID;
1467 args.val = sd_flags;
1468 args.critical = True;
1470 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1475 * Run a function on all results for a search. Uses ads_do_paged_search() and
1476 * runs the function as each page is returned, using ads_process_results()
1477 * @param ads connection to ads server
1478 * @param bind_path Base dn for the search
1479 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1480 * @param expr Search expression - specified in local charset
1481 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1482 * @param fn Function which takes attr name, values list, and data_area
1483 * @param data_area Pointer which is passed to function on each call
1484 * @return status of search
1486 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1487 int scope, const char *expr, const char **attrs,
1488 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1491 struct berval *cookie = NULL;
1496 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1499 if (!ADS_ERR_OK(status)) return status;
1501 ads_process_results(ads, res, fn, data_area);
1502 ads_msgfree(ads, res);
1505 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1506 &res, &count, &cookie);
1508 if (!ADS_ERR_OK(status)) break;
1510 ads_process_results(ads, res, fn, data_area);
1511 ads_msgfree(ads, res);
1518 * Do a search with a timeout.
1519 * @param ads connection to ads server
1520 * @param bind_path Base dn for the search
1521 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1522 * @param expr Search expression
1523 * @param attrs Attributes to retrieve
1524 * @param res ** which will contain results - free res* with ads_msgfree()
1525 * @return status of search
1527 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1529 const char **attrs, LDAPMessage **res)
1532 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1533 size_t converted_size;
1537 if (!(ctx = talloc_init("ads_do_search"))) {
1538 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1539 return ADS_ERROR(LDAP_NO_MEMORY);
1542 /* 0 means the conversion worked but the result was empty
1543 so we only fail if it's negative. In any case, it always
1544 at least nulls out the dest */
1545 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1546 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1548 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1549 rc = LDAP_NO_MEMORY;
1553 if (!attrs || !(*attrs))
1554 search_attrs = NULL;
1556 /* This would be the utf8-encoded version...*/
1557 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1558 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1560 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1561 rc = LDAP_NO_MEMORY;
1566 /* see the note in ads_do_paged_search - we *must* disable referrals */
1567 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1569 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1570 search_attrs, 0, NULL, NULL,
1572 (LDAPMessage **)res);
1574 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1575 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1580 talloc_destroy(ctx);
1581 /* if/when we decide to utf8-encode attrs, take out this next line */
1582 TALLOC_FREE(search_attrs);
1583 return ADS_ERROR(rc);
1586 * Do a general ADS search
1587 * @param ads connection to ads server
1588 * @param res ** which will contain results - free res* with ads_msgfree()
1589 * @param expr Search expression
1590 * @param attrs Attributes to retrieve
1591 * @return status of search
1593 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1594 const char *expr, const char **attrs)
1596 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1601 * Do a search on a specific DistinguishedName
1602 * @param ads connection to ads server
1603 * @param res ** which will contain results - free res* with ads_msgfree()
1604 * @param dn DistinguishedName to search
1605 * @param attrs Attributes to retrieve
1606 * @return status of search
1608 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1609 const char *dn, const char **attrs)
1611 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1616 * Free up memory from a ads_search
1617 * @param ads connection to ads server
1618 * @param msg Search results to free
1620 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1627 * Get a dn from search results
1628 * @param ads connection to ads server
1629 * @param msg Search result
1632 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1634 char *utf8_dn, *unix_dn;
1635 size_t converted_size;
1637 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1640 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1644 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1645 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1649 ldap_memfree(utf8_dn);
1654 * Get the parent from a dn
1655 * @param dn the dn to return the parent from
1656 * @return parent dn string
1658 char *ads_parent_dn(const char *dn)
1666 p = strchr(dn, ',');
1676 * Find a machine account given a hostname
1677 * @param ads connection to ads server
1678 * @param res ** which will contain results - free res* with ads_msgfree()
1679 * @param host Hostname to search for
1680 * @return status of search
1682 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1683 const char *machine)
1687 const char *attrs[] = {
1688 /* This is how Windows checks for machine accounts */
1691 "userAccountControl",
1693 "ServicePrincipalName",
1694 "userPrincipalName",
1696 /* Additional attributes Samba checks */
1697 "msDS-AdditionalDnsHostName",
1698 "msDS-SupportedEncryptionTypes",
1699 "nTSecurityDescriptor",
1704 TALLOC_CTX *frame = talloc_stackframe();
1708 /* the easiest way to find a machine account anywhere in the tree
1709 is to look for hostname$ */
1710 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1712 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1716 status = ads_search(ads, res, expr, attrs);
1717 if (ADS_ERR_OK(status)) {
1718 if (ads_count_replies(ads, *res) != 1) {
1719 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1729 * Initialize a list of mods to be used in a modify request
1730 * @param ctx An initialized TALLOC_CTX
1731 * @return allocated ADS_MODLIST
1733 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1735 #define ADS_MODLIST_ALLOC_SIZE 10
1738 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1739 /* -1 is safety to make sure we don't go over the end.
1740 need to reset it to NULL before doing ldap modify */
1741 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1743 return (ADS_MODLIST)mods;
1748 add an attribute to the list, with values list already constructed
1750 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1751 int mod_op, const char *name,
1752 const void *_invals)
1755 LDAPMod **modlist = (LDAPMod **) *mods;
1756 struct berval **ber_values = NULL;
1757 char **char_values = NULL;
1760 mod_op = LDAP_MOD_DELETE;
1762 if (mod_op & LDAP_MOD_BVALUES) {
1763 const struct berval **b;
1764 b = discard_const_p(const struct berval *, _invals);
1765 ber_values = ads_dup_values(ctx, b);
1768 c = discard_const_p(const char *, _invals);
1769 char_values = ads_push_strvals(ctx, c);
1773 /* find the first empty slot */
1774 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1776 if (modlist[curmod] == (LDAPMod *) -1) {
1777 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1778 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1779 return ADS_ERROR(LDAP_NO_MEMORY);
1780 memset(&modlist[curmod], 0,
1781 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1782 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1783 *mods = (ADS_MODLIST)modlist;
1786 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1787 return ADS_ERROR(LDAP_NO_MEMORY);
1788 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1789 if (mod_op & LDAP_MOD_BVALUES) {
1790 modlist[curmod]->mod_bvalues = ber_values;
1791 } else if (mod_op & LDAP_MOD_DELETE) {
1792 modlist[curmod]->mod_values = NULL;
1794 modlist[curmod]->mod_values = char_values;
1797 modlist[curmod]->mod_op = mod_op;
1798 return ADS_ERROR(LDAP_SUCCESS);
1802 * Add a single string value to a mod list
1803 * @param ctx An initialized TALLOC_CTX
1804 * @param mods An initialized ADS_MODLIST
1805 * @param name The attribute name to add
1806 * @param val The value to add - NULL means DELETE
1807 * @return ADS STATUS indicating success of add
1809 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1810 const char *name, const char *val)
1812 const char *values[2];
1818 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1819 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1823 * Add an array of string values to a mod list
1824 * @param ctx An initialized TALLOC_CTX
1825 * @param mods An initialized ADS_MODLIST
1826 * @param name The attribute name to add
1827 * @param vals The array of string values to add - NULL means DELETE
1828 * @return ADS STATUS indicating success of add
1830 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1831 const char *name, const char **vals)
1834 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1835 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1836 name, (const void **) vals);
1840 * Add a single ber-encoded value to a mod list
1841 * @param ctx An initialized TALLOC_CTX
1842 * @param mods An initialized ADS_MODLIST
1843 * @param name The attribute name to add
1844 * @param val The value to add - NULL means DELETE
1845 * @return ADS STATUS indicating success of add
1847 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1848 const char *name, const struct berval *val)
1850 const struct berval *values[2];
1855 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1856 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1857 name, (const void **) values);
1860 static void ads_print_error(int ret, LDAP *ld)
1863 char *ld_error = NULL;
1864 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1865 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1867 ldap_err2string(ret),
1869 SAFE_FREE(ld_error);
1874 * Perform an ldap modify
1875 * @param ads connection to ads server
1876 * @param mod_dn DistinguishedName to modify
1877 * @param mods list of modifications to perform
1878 * @return status of modify
1880 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1883 char *utf8_dn = NULL;
1884 size_t converted_size;
1886 this control is needed to modify that contains a currently
1887 non-existent attribute (but allowable for the object) to run
1889 LDAPControl PermitModify = {
1890 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1893 LDAPControl *controls[2];
1895 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1897 controls[0] = &PermitModify;
1900 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1901 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1904 /* find the end of the list, marked by NULL or -1 */
1905 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1906 /* make sure the end of the list is NULL */
1908 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1909 (LDAPMod **) mods, controls, NULL);
1910 ads_print_error(ret, ads->ldap.ld);
1911 TALLOC_FREE(utf8_dn);
1912 return ADS_ERROR(ret);
1916 * Perform an ldap add
1917 * @param ads connection to ads server
1918 * @param new_dn DistinguishedName to add
1919 * @param mods list of attributes and values for DN
1920 * @return status of add
1922 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1925 char *utf8_dn = NULL;
1926 size_t converted_size;
1928 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1930 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1931 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
1932 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1935 /* find the end of the list, marked by NULL or -1 */
1936 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1937 /* make sure the end of the list is NULL */
1940 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1941 ads_print_error(ret, ads->ldap.ld);
1942 TALLOC_FREE(utf8_dn);
1943 return ADS_ERROR(ret);
1947 * Delete a DistinguishedName
1948 * @param ads connection to ads server
1949 * @param new_dn DistinguishedName to delete
1950 * @return status of delete
1952 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1955 char *utf8_dn = NULL;
1956 size_t converted_size;
1957 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1958 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
1959 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1962 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1964 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1965 ads_print_error(ret, ads->ldap.ld);
1966 TALLOC_FREE(utf8_dn);
1967 return ADS_ERROR(ret);
1971 * Build an org unit string
1972 * if org unit is Computers or blank then assume a container, otherwise
1973 * assume a / separated list of organisational units.
1974 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1975 * @param ads connection to ads server
1976 * @param org_unit Organizational unit
1977 * @return org unit string - caller must free
1979 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1985 if (!org_unit || !*org_unit) {
1987 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1989 /* samba4 might not yet respond to a wellknownobject-query */
1990 return ret ? ret : SMB_STRDUP("cn=Computers");
1993 if (strequal(org_unit, "Computers")) {
1994 return SMB_STRDUP("cn=Computers");
1997 /* jmcd: removed "\\" from the separation chars, because it is
1998 needed as an escape for chars like '#' which are valid in an
2000 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
2001 if (!ADS_ERR_OK(status)) {
2009 * Get a org unit string for a well-known GUID
2010 * @param ads connection to ads server
2011 * @param wknguid Well known GUID
2012 * @return org unit string - caller must free
2014 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2017 LDAPMessage *res = NULL;
2018 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2019 **bind_dn_exp = NULL;
2020 const char *attrs[] = {"distinguishedName", NULL};
2021 int new_ln, wkn_ln, bind_ln, i;
2023 if (wknguid == NULL) {
2027 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2028 DEBUG(1, ("asprintf failed!\n"));
2032 status = ads_search_dn(ads, &res, base, attrs);
2033 if (!ADS_ERR_OK(status)) {
2034 DEBUG(1,("Failed while searching for: %s\n", base));
2038 if (ads_count_replies(ads, res) != 1) {
2042 /* substitute the bind-path from the well-known-guid-search result */
2043 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2048 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2053 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2058 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2060 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2063 new_ln = wkn_ln - bind_ln;
2065 ret = SMB_STRDUP(wkn_dn_exp[0]);
2070 for (i=1; i < new_ln; i++) {
2073 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2079 ret = SMB_STRDUP(s);
2088 ads_msgfree(ads, res);
2089 TALLOC_FREE(wkn_dn);
2091 ldap_value_free(wkn_dn_exp);
2094 ldap_value_free(bind_dn_exp);
2101 * Adds (appends) an item to an attribute array, rather then
2102 * replacing the whole list
2103 * @param ctx An initialized TALLOC_CTX
2104 * @param mods An initialized ADS_MODLIST
2105 * @param name name of the ldap attribute to append to
2106 * @param vals an array of values to add
2107 * @return status of addition
2110 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2111 const char *name, const char **vals)
2113 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2114 (const void *) vals);
2118 * Determines the an account's current KVNO via an LDAP lookup
2119 * @param ads An initialized ADS_STRUCT
2120 * @param account_name the NT samaccountname.
2121 * @return the kvno for the account, or -1 in case of a failure.
2124 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
2126 LDAPMessage *res = NULL;
2127 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
2129 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
2130 char *dn_string = NULL;
2133 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
2134 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
2137 ret = ads_search(ads, &res, filter, attrs);
2139 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
2140 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
2141 ads_msgfree(ads, res);
2145 dn_string = ads_get_dn(ads, talloc_tos(), res);
2147 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2148 ads_msgfree(ads, res);
2151 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
2152 TALLOC_FREE(dn_string);
2154 /* ---------------------------------------------------------
2155 * 0 is returned as a default KVNO from this point on...
2156 * This is done because Windows 2000 does not support key
2157 * version numbers. Chances are that a failure in the next
2158 * step is simply due to Windows 2000 being used for a
2159 * domain controller. */
2162 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2163 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2164 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2165 ads_msgfree(ads, res);
2170 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2171 ads_msgfree(ads, res);
2176 * Determines the computer account's current KVNO via an LDAP lookup
2177 * @param ads An initialized ADS_STRUCT
2178 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2179 * @return the kvno for the computer account, or -1 in case of a failure.
2182 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2184 char *computer_account = NULL;
2187 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2191 kvno = ads_get_kvno(ads, computer_account);
2192 free(computer_account);
2198 * This clears out all registered spn's for a given hostname
2199 * @param ads An initialized ADS_STRUCT
2200 * @param machine_name the NetBIOS name of the computer.
2201 * @return 0 upon success, non-zero otherwise.
2204 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2207 LDAPMessage *res = NULL;
2209 const char *servicePrincipalName[1] = {NULL};
2211 char *dn_string = NULL;
2213 ret = ads_find_machine_acct(ads, &res, machine_name);
2214 if (!ADS_ERR_OK(ret)) {
2215 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2216 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2217 ads_msgfree(ads, res);
2221 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2222 ctx = talloc_init("ads_clear_service_principal_names");
2224 ads_msgfree(ads, res);
2225 return ADS_ERROR(LDAP_NO_MEMORY);
2228 if (!(mods = ads_init_mods(ctx))) {
2229 talloc_destroy(ctx);
2230 ads_msgfree(ads, res);
2231 return ADS_ERROR(LDAP_NO_MEMORY);
2233 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2234 if (!ADS_ERR_OK(ret)) {
2235 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2236 ads_msgfree(ads, res);
2237 talloc_destroy(ctx);
2240 dn_string = ads_get_dn(ads, talloc_tos(), res);
2242 talloc_destroy(ctx);
2243 ads_msgfree(ads, res);
2244 return ADS_ERROR(LDAP_NO_MEMORY);
2246 ret = ads_gen_mod(ads, dn_string, mods);
2247 TALLOC_FREE(dn_string);
2248 if (!ADS_ERR_OK(ret)) {
2249 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2251 ads_msgfree(ads, res);
2252 talloc_destroy(ctx);
2256 ads_msgfree(ads, res);
2257 talloc_destroy(ctx);
2262 * @brief Search for an element in a string array.
2264 * @param[in] el_array The string array to search.
2266 * @param[in] num_el The number of elements in the string array.
2268 * @param[in] el The string to search.
2270 * @return True if found, false if not.
2272 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2276 if (el_array == NULL || num_el == 0 || el == NULL) {
2280 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2283 cmp = strcasecmp_m(el_array[i], el);
2293 * @brief This gets the service principal names of an existing computer account.
2295 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2297 * @param[in] ads The ADS context to use.
2299 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2300 * identify the computer account.
2302 * @param[in] spn_array A pointer to store the array for SPNs.
2304 * @param[in] num_spns The number of principals stored in the array.
2306 * @return 0 on success, or a ADS error if a failure occurred.
2308 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2310 const char *machine_name,
2315 LDAPMessage *res = NULL;
2318 status = ads_find_machine_acct(ads,
2321 if (!ADS_ERR_OK(status)) {
2322 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2327 count = ads_count_replies(ads, res);
2329 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2333 *spn_array = ads_pull_strings(ads,
2336 "servicePrincipalName",
2338 if (*spn_array == NULL) {
2339 DEBUG(1, ("Host account for %s does not have service principal "
2342 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2347 ads_msgfree(ads, res);
2353 * This adds a service principal name to an existing computer account
2354 * (found by hostname) in AD.
2355 * @param ads An initialized ADS_STRUCT
2356 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2357 * @param spns An array or strings for the service principals to add,
2358 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2359 * @return 0 upon success, or non-zero if a failure occurs
2362 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2363 const char *machine_name,
2368 LDAPMessage *res = NULL;
2370 char *dn_string = NULL;
2371 const char **servicePrincipalName = spns;
2373 ret = ads_find_machine_acct(ads, &res, machine_name);
2374 if (!ADS_ERR_OK(ret)) {
2375 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2377 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2378 ads_msgfree(ads, res);
2382 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2383 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2384 ads_msgfree(ads, res);
2385 return ADS_ERROR(LDAP_NO_MEMORY);
2388 DEBUG(5,("ads_add_service_principal_name: INFO: "
2389 "Adding %s to host %s\n",
2390 spns[0] ? "N/A" : spns[0], machine_name));
2393 DEBUG(5,("ads_add_service_principal_name: INFO: "
2394 "Adding %s to host %s\n",
2395 spns[1] ? "N/A" : spns[1], machine_name));
2397 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2398 ret = ADS_ERROR(LDAP_NO_MEMORY);
2402 ret = ads_add_strlist(ctx,
2404 "servicePrincipalName",
2405 servicePrincipalName);
2406 if (!ADS_ERR_OK(ret)) {
2407 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2411 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2412 ret = ADS_ERROR(LDAP_NO_MEMORY);
2416 ret = ads_gen_mod(ads, dn_string, mods);
2417 if (!ADS_ERR_OK(ret)) {
2418 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2424 ads_msgfree(ads, res);
2428 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2431 uint32_t acct_ctrl = 0;
2434 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2442 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2444 const struct berval *machine_pw_val)
2448 TALLOC_CTX *frame = talloc_stackframe();
2449 uint32_t acct_control;
2450 char *control_str = NULL;
2451 const char *attrs[] = {
2455 LDAPMessage *res = NULL;
2458 dn = ads_get_dn(ads, frame, msg);
2460 ret = ADS_ERROR(LDAP_NO_MEMORY);
2464 acct_control = ads_get_acct_ctrl(ads, msg);
2465 if (acct_control == 0) {
2466 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2471 * Changing the password, disables the account. So we need to change the
2472 * userAccountControl flags to enable it again.
2474 mods = ads_init_mods(frame);
2476 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2480 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2482 ret = ads_gen_mod(ads, dn, mods);
2483 if (!ADS_ERR_OK(ret)) {
2489 * To activate the account, we need to disable and enable it.
2491 acct_control |= UF_ACCOUNTDISABLE;
2493 control_str = talloc_asprintf(frame, "%u", acct_control);
2494 if (control_str == NULL) {
2495 ret = ADS_ERROR(LDAP_NO_MEMORY);
2499 mods = ads_init_mods(frame);
2501 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2505 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2507 ret = ads_gen_mod(ads, dn, mods);
2508 if (!ADS_ERR_OK(ret)) {
2512 TALLOC_FREE(control_str);
2515 * Enable the account again.
2517 acct_control &= ~UF_ACCOUNTDISABLE;
2519 control_str = talloc_asprintf(frame, "%u", acct_control);
2520 if (control_str == NULL) {
2521 ret = ADS_ERROR(LDAP_NO_MEMORY);
2525 mods = ads_init_mods(frame);
2527 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2531 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2533 ret = ads_gen_mod(ads, dn, mods);
2534 if (!ADS_ERR_OK(ret)) {
2538 TALLOC_FREE(control_str);
2540 ret = ads_search_dn(ads, &res, dn, attrs);
2541 ads_msgfree(ads, res);
2550 * adds a machine account to the ADS server
2551 * @param ads An initialized ADS_STRUCT
2552 * @param machine_name - the NetBIOS machine name of this account.
2553 * @param account_type A number indicating the type of account to create
2554 * @param org_unit The LDAP path in which to place this account
2555 * @return 0 upon success, or non-zero otherwise
2558 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2559 const char *machine_name,
2560 const char *machine_password,
2561 const char *org_unit,
2562 uint32_t etype_list,
2563 const char *dns_domain_name)
2566 char *samAccountName = NULL;
2567 char *controlstr = NULL;
2568 TALLOC_CTX *ctx = NULL;
2570 char *machine_escaped = NULL;
2571 char *dns_hostname = NULL;
2572 char *new_dn = NULL;
2573 char *utf8_pw = NULL;
2574 size_t utf8_pw_len = 0;
2575 char *utf16_pw = NULL;
2576 size_t utf16_pw_len = 0;
2577 struct berval machine_pw_val;
2579 const char **spn_array = NULL;
2580 size_t num_spns = 0;
2581 const char *spn_prefix[] = {
2583 "RestrictedKrbHost",
2586 LDAPMessage *res = NULL;
2587 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2589 ctx = talloc_init("ads_add_machine_acct");
2591 return ADS_ERROR(LDAP_NO_MEMORY);
2594 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2595 if (machine_escaped == NULL) {
2596 ret = ADS_ERROR(LDAP_NO_MEMORY);
2600 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2601 if (utf8_pw == NULL) {
2602 ret = ADS_ERROR(LDAP_NO_MEMORY);
2605 utf8_pw_len = strlen(utf8_pw);
2607 ok = convert_string_talloc(ctx,
2608 CH_UTF8, CH_UTF16MUNGED,
2609 utf8_pw, utf8_pw_len,
2610 (void *)&utf16_pw, &utf16_pw_len);
2612 ret = ADS_ERROR(LDAP_NO_MEMORY);
2616 machine_pw_val = (struct berval) {
2618 .bv_len = utf16_pw_len,
2621 /* Check if the machine account already exists. */
2622 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2623 if (ADS_ERR_OK(ret)) {
2624 /* Change the machine account password */
2625 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2626 ads_msgfree(ads, res);
2630 ads_msgfree(ads, res);
2632 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2633 if (new_dn == NULL) {
2634 ret = ADS_ERROR(LDAP_NO_MEMORY);
2638 /* Create machine account */
2640 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2641 if (samAccountName == NULL) {
2642 ret = ADS_ERROR(LDAP_NO_MEMORY);
2646 dns_hostname = talloc_asprintf(ctx,
2650 if (dns_hostname == NULL) {
2651 ret = ADS_ERROR(LDAP_NO_MEMORY);
2655 /* Add dns_hostname SPNs */
2656 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2657 char *spn = talloc_asprintf(ctx,
2662 ret = ADS_ERROR(LDAP_NO_MEMORY);
2666 ok = add_string_to_array(ctx,
2671 ret = ADS_ERROR(LDAP_NO_MEMORY);
2676 /* Add machine_name SPNs */
2677 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2678 char *spn = talloc_asprintf(ctx,
2683 ret = ADS_ERROR(LDAP_NO_MEMORY);
2687 ok = add_string_to_array(ctx,
2692 ret = ADS_ERROR(LDAP_NO_MEMORY);
2697 /* Make sure to NULL terminate the array */
2698 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2699 if (spn_array == NULL) {
2700 ret = ADS_ERROR(LDAP_NO_MEMORY);
2703 spn_array[num_spns] = NULL;
2705 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2706 if (controlstr == NULL) {
2707 ret = ADS_ERROR(LDAP_NO_MEMORY);
2711 mods = ads_init_mods(ctx);
2713 ret = ADS_ERROR(LDAP_NO_MEMORY);
2717 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2718 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2719 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2720 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2721 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2722 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2724 ret = ads_gen_add(ads, new_dn, mods);
2727 SAFE_FREE(machine_escaped);
2728 talloc_destroy(ctx);
2734 * move a machine account to another OU on the ADS server
2735 * @param ads - An initialized ADS_STRUCT
2736 * @param machine_name - the NetBIOS machine name of this account.
2737 * @param org_unit - The LDAP path in which to place this account
2738 * @param moved - whether we moved the machine account (optional)
2739 * @return 0 upon success, or non-zero otherwise
2742 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2743 const char *org_unit, bool *moved)
2747 LDAPMessage *res = NULL;
2748 char *filter = NULL;
2749 char *computer_dn = NULL;
2751 char *computer_rdn = NULL;
2752 bool need_move = False;
2754 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2755 rc = ADS_ERROR(LDAP_NO_MEMORY);
2759 /* Find pre-existing machine */
2760 rc = ads_search(ads, &res, filter, NULL);
2761 if (!ADS_ERR_OK(rc)) {
2765 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2767 rc = ADS_ERROR(LDAP_NO_MEMORY);
2771 parent_dn = ads_parent_dn(computer_dn);
2772 if (strequal(parent_dn, org_unit)) {
2778 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2779 rc = ADS_ERROR(LDAP_NO_MEMORY);
2783 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2784 org_unit, 1, NULL, NULL);
2785 rc = ADS_ERROR(ldap_status);
2788 ads_msgfree(ads, res);
2790 TALLOC_FREE(computer_dn);
2791 SAFE_FREE(computer_rdn);
2793 if (!ADS_ERR_OK(rc)) {
2805 dump a binary result from ldap
2807 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2810 for (i=0; values[i]; i++) {
2812 printf("%s: ", field);
2813 for (j=0; j<values[i]->bv_len; j++) {
2814 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2820 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2823 for (i=0; values[i]; i++) {
2825 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2828 status = GUID_from_ndr_blob(&in, &guid);
2829 if (NT_STATUS_IS_OK(status)) {
2830 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2832 printf("%s: INVALID GUID\n", field);
2838 dump a sid result from ldap
2840 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2843 for (i=0; values[i]; i++) {
2846 struct dom_sid_buf tmp;
2847 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2848 values[i]->bv_len, &sid);
2852 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2857 dump ntSecurityDescriptor
2859 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2861 TALLOC_CTX *frame = talloc_stackframe();
2862 struct security_descriptor *psd;
2865 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2866 values[0]->bv_len, &psd);
2867 if (!NT_STATUS_IS_OK(status)) {
2868 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2869 nt_errstr(status)));
2875 ads_disp_sd(ads, talloc_tos(), psd);
2882 dump a string result from ldap
2884 static void dump_string(const char *field, char **values)
2887 for (i=0; values[i]; i++) {
2888 printf("%s: %s\n", field, values[i]);
2893 dump a field from LDAP on stdout
2897 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2902 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2904 {"objectGUID", False, dump_guid},
2905 {"netbootGUID", False, dump_guid},
2906 {"nTSecurityDescriptor", False, dump_sd},
2907 {"dnsRecord", False, dump_binary},
2908 {"objectSid", False, dump_sid},
2909 {"securityIdentifier", False, dump_sid},
2910 {"tokenGroups", False, dump_sid},
2911 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2912 {"tokengroupsGlobalandUniversal", False, dump_sid},
2913 {"mS-DS-CreatorSID", False, dump_sid},
2914 {"msExchMailboxGuid", False, dump_guid},
2915 {"msDS-TrustForestTrustInfo", False, dump_binary},
2920 if (!field) { /* must be end of an entry */
2925 for (i=0; handlers[i].name; i++) {
2926 if (strcasecmp_m(handlers[i].name, field) == 0) {
2927 if (!values) /* first time, indicate string or not */
2928 return handlers[i].string;
2929 handlers[i].handler(ads, field, (struct berval **) values);
2933 if (!handlers[i].name) {
2934 if (!values) /* first time, indicate string conversion */
2936 dump_string(field, (char **)values);
2942 * Dump a result from LDAP on stdout
2943 * used for debugging
2944 * @param ads connection to ads server
2945 * @param res Results to dump
2948 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2950 ads_process_results(ads, res, ads_dump_field, NULL);
2954 * Walk through results, calling a function for each entry found.
2955 * The function receives a field name, a berval * array of values,
2956 * and a data area passed through from the start. The function is
2957 * called once with null for field and values at the end of each
2959 * @param ads connection to ads server
2960 * @param res Results to process
2961 * @param fn Function for processing each result
2962 * @param data_area user-defined area to pass to function
2964 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2965 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2970 size_t converted_size;
2972 if (!(ctx = talloc_init("ads_process_results")))
2975 for (msg = ads_first_entry(ads, res); msg;
2976 msg = ads_next_entry(ads, msg)) {
2980 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2981 (LDAPMessage *)msg,&b);
2983 utf8_field=ldap_next_attribute(ads->ldap.ld,
2984 (LDAPMessage *)msg,b)) {
2985 struct berval **ber_vals;
2991 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2994 DEBUG(0,("ads_process_results: "
2995 "pull_utf8_talloc failed: %s\n",
2999 string = fn(ads, field, NULL, data_area);
3004 utf8_vals = ldap_get_values(ads->ldap.ld,
3005 (LDAPMessage *)msg, field);
3006 p = discard_const_p(const char *, utf8_vals);
3007 str_vals = ads_pull_strvals(ctx, p);
3008 fn(ads, field, (void **) str_vals, data_area);
3009 ldap_value_free(utf8_vals);
3011 ber_vals = ldap_get_values_len(ads->ldap.ld,
3012 (LDAPMessage *)msg, field);
3013 fn(ads, field, (void **) ber_vals, data_area);
3015 ldap_value_free_len(ber_vals);
3017 ldap_memfree(utf8_field);
3020 talloc_free_children(ctx);
3021 fn(ads, NULL, NULL, data_area); /* completed an entry */
3024 talloc_destroy(ctx);
3028 * count how many replies are in a LDAPMessage
3029 * @param ads connection to ads server
3030 * @param res Results to count
3031 * @return number of replies
3033 int ads_count_replies(ADS_STRUCT *ads, void *res)
3035 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3039 * pull the first entry from a ADS result
3040 * @param ads connection to ads server
3041 * @param res Results of search
3042 * @return first entry from result
3044 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3046 return ldap_first_entry(ads->ldap.ld, res);
3050 * pull the next entry from a ADS result
3051 * @param ads connection to ads server
3052 * @param res Results of search
3053 * @return next entry from result
3055 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3057 return ldap_next_entry(ads->ldap.ld, res);
3061 * pull the first message from a ADS result
3062 * @param ads connection to ads server
3063 * @param res Results of search
3064 * @return first message from result
3066 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3068 return ldap_first_message(ads->ldap.ld, res);
3072 * pull the next message from a ADS result
3073 * @param ads connection to ads server
3074 * @param res Results of search
3075 * @return next message from result
3077 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3079 return ldap_next_message(ads->ldap.ld, res);
3083 * pull a single string from a ADS result
3084 * @param ads connection to ads server
3085 * @param mem_ctx TALLOC_CTX to use for allocating result string
3086 * @param msg Results of search
3087 * @param field Attribute to retrieve
3088 * @return Result string in talloc context
3090 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3096 size_t converted_size;
3098 values = ldap_get_values(ads->ldap.ld, msg, field);
3102 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3107 ldap_value_free(values);
3112 * pull an array of strings from a ADS result
3113 * @param ads connection to ads server
3114 * @param mem_ctx TALLOC_CTX to use for allocating result string
3115 * @param msg Results of search
3116 * @param field Attribute to retrieve
3117 * @return Result strings in talloc context
3119 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3120 LDAPMessage *msg, const char *field,
3125 size_t i, converted_size;
3127 values = ldap_get_values(ads->ldap.ld, msg, field);
3131 *num_values = ldap_count_values(values);
3133 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3135 ldap_value_free(values);
3139 for (i=0;i<*num_values;i++) {
3140 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3143 ldap_value_free(values);
3149 ldap_value_free(values);
3154 * pull an array of strings from a ADS result
3155 * (handle large multivalue attributes with range retrieval)
3156 * @param ads connection to ads server
3157 * @param mem_ctx TALLOC_CTX to use for allocating result string
3158 * @param msg Results of search
3159 * @param field Attribute to retrieve
3160 * @param current_strings strings returned by a previous call to this function
3161 * @param next_attribute The next query should ask for this attribute
3162 * @param num_values How many values did we get this time?
3163 * @param more_values Are there more values to get?
3164 * @return Result strings in talloc context
3166 char **ads_pull_strings_range(ADS_STRUCT *ads,
3167 TALLOC_CTX *mem_ctx,
3168 LDAPMessage *msg, const char *field,
3169 char **current_strings,
3170 const char **next_attribute,
3171 size_t *num_strings,
3175 char *expected_range_attrib, *range_attr = NULL;
3176 BerElement *ptr = NULL;
3179 size_t num_new_strings;
3180 unsigned long int range_start;
3181 unsigned long int range_end;
3183 /* we might have been given the whole lot anyway */
3184 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3185 *more_strings = False;
3189 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3191 /* look for Range result */
3192 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3194 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3195 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3196 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3204 /* nothing here - this field is just empty */
3205 *more_strings = False;
3209 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3210 &range_start, &range_end) == 2) {
3211 *more_strings = True;
3213 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3214 &range_start) == 1) {
3215 *more_strings = False;
3217 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3219 ldap_memfree(range_attr);
3220 *more_strings = False;
3225 if ((*num_strings) != range_start) {
3226 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3227 " - aborting range retrieval\n",
3228 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3229 ldap_memfree(range_attr);
3230 *more_strings = False;
3234 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3236 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3237 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3238 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3239 range_attr, (unsigned long int)range_end - range_start + 1,
3240 (unsigned long int)num_new_strings));
3241 ldap_memfree(range_attr);
3242 *more_strings = False;
3246 strings = talloc_realloc(mem_ctx, current_strings, char *,
3247 *num_strings + num_new_strings);
3249 if (strings == NULL) {
3250 ldap_memfree(range_attr);
3251 *more_strings = False;
3255 if (new_strings && num_new_strings) {
3256 memcpy(&strings[*num_strings], new_strings,
3257 sizeof(*new_strings) * num_new_strings);
3260 (*num_strings) += num_new_strings;
3262 if (*more_strings) {
3263 *next_attribute = talloc_asprintf(mem_ctx,
3268 if (!*next_attribute) {
3269 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3270 ldap_memfree(range_attr);
3271 *more_strings = False;
3276 ldap_memfree(range_attr);
3282 * pull a single uint32_t from a ADS result
3283 * @param ads connection to ads server
3284 * @param msg Results of search
3285 * @param field Attribute to retrieve
3286 * @param v Pointer to int to store result
3287 * @return boolean indicating success
3289 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3294 values = ldap_get_values(ads->ldap.ld, msg, field);
3298 ldap_value_free(values);
3302 *v = atoi(values[0]);
3303 ldap_value_free(values);
3308 * pull a single objectGUID from an ADS result
3309 * @param ads connection to ADS server
3310 * @param msg results of search
3311 * @param guid 37-byte area to receive text guid
3312 * @return boolean indicating success
3314 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3319 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3324 status = GUID_from_ndr_blob(&blob, guid);
3325 talloc_free(blob.data);
3326 return NT_STATUS_IS_OK(status);
3331 * pull a single struct dom_sid from a ADS result
3332 * @param ads connection to ads server
3333 * @param msg Results of search
3334 * @param field Attribute to retrieve
3335 * @param sid Pointer to sid to store result
3336 * @return boolean indicating success
3338 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3339 struct dom_sid *sid)
3341 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3345 * pull an array of struct dom_sids from a ADS result
3346 * @param ads connection to ads server
3347 * @param mem_ctx TALLOC_CTX for allocating sid array
3348 * @param msg Results of search
3349 * @param field Attribute to retrieve
3350 * @param sids pointer to sid array to allocate
3351 * @return the count of SIDs pulled
3353 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3354 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3356 struct berval **values;
3359 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3364 for (i=0; values[i]; i++)
3368 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3370 ldap_value_free_len(values);
3378 for (i=0; values[i]; i++) {
3380 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3381 values[i]->bv_len, &(*sids)[count]);
3383 struct dom_sid_buf buf;
3384 DBG_DEBUG("pulling SID: %s\n",
3385 dom_sid_str_buf(&(*sids)[count], &buf));
3390 ldap_value_free_len(values);
3395 * pull a struct security_descriptor from a ADS result
3396 * @param ads connection to ads server
3397 * @param mem_ctx TALLOC_CTX for allocating sid array
3398 * @param msg Results of search
3399 * @param field Attribute to retrieve
3400 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3401 * @return boolean indicating success
3403 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3404 LDAPMessage *msg, const char *field,
3405 struct security_descriptor **sd)
3407 struct berval **values;
3410 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3412 if (!values) return false;
3416 status = unmarshall_sec_desc(mem_ctx,
3417 (uint8_t *)values[0]->bv_val,
3418 values[0]->bv_len, sd);
3419 if (!NT_STATUS_IS_OK(status)) {
3420 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3421 nt_errstr(status)));
3426 ldap_value_free_len(values);
3431 * in order to support usernames longer than 21 characters we need to
3432 * use both the sAMAccountName and the userPrincipalName attributes
3433 * It seems that not all users have the userPrincipalName attribute set
3435 * @param ads connection to ads server
3436 * @param mem_ctx TALLOC_CTX for allocating sid array
3437 * @param msg Results of search
3438 * @return the username
3440 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3446 /* lookup_name() only works on the sAMAccountName to
3447 returning the username portion of userPrincipalName
3448 breaks winbindd_getpwnam() */
3450 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3451 if (ret && (p = strchr_m(ret, '@'))) {
3456 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3461 * find the update serial number - this is the core of the ldap cache
3462 * @param ads connection to ads server
3463 * @param ads connection to ADS server
3464 * @param usn Pointer to retrieved update serial number
3465 * @return status of search
3467 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3469 const char *attrs[] = {"highestCommittedUSN", NULL};
3473 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3474 if (!ADS_ERR_OK(status))
3477 if (ads_count_replies(ads, res) != 1) {
3478 ads_msgfree(ads, res);
3479 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3482 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3483 ads_msgfree(ads, res);
3484 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3487 ads_msgfree(ads, res);
3491 /* parse a ADS timestring - typical string is
3492 '20020917091222.0Z0' which means 09:12.22 17th September
3494 static time_t ads_parse_time(const char *str)
3500 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3501 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3502 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3511 /********************************************************************
3512 ********************************************************************/
3514 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3516 const char *attrs[] = {"currentTime", NULL};
3520 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3521 ADS_STRUCT *ads_s = ads;
3523 /* establish a new ldap tcp session if necessary */
3525 if ( !ads->ldap.ld ) {
3527 * ADS_STRUCT may be being reused after a
3528 * DC lookup, so ads->ldap.ss may already have a
3529 * good address. If not, re-initialize the passed-in
3530 * ADS_STRUCT with the given server.XXXX parameters.
3532 * Note that this doesn't depend on
3533 * ads->server.ldap_server != NULL,
3534 * as the case where ads->server.ldap_server==NULL and
3535 * ads->ldap.ss != zero_address is precisely the DC
3536 * lookup case where ads->ldap.ss was found by going
3537 * through ads_find_dc() again we want to avoid repeating.
3539 if (is_zero_addr(&ads->ldap.ss)) {
3540 ads_s = ads_init(tmp_ctx,
3542 ads->server.workgroup,
3543 ads->server.ldap_server,
3545 if (ads_s == NULL) {
3546 status = ADS_ERROR(LDAP_NO_MEMORY);
3552 * Reset ads->config.flags as it can contain the flags
3553 * returned by the previous CLDAP ping when reusing the struct.
3555 ads_s->config.flags = 0;
3557 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3558 status = ads_connect( ads_s );
3559 if ( !ADS_ERR_OK(status))
3563 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3564 if (!ADS_ERR_OK(status)) {
3568 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3570 ads_msgfree(ads_s, res);
3571 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3575 /* but save the time and offset in the original ADS_STRUCT */
3577 ads->config.current_time = ads_parse_time(timestr);
3579 if (ads->config.current_time != 0) {
3580 ads->auth.time_offset = ads->config.current_time - time(NULL);
3581 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3584 ads_msgfree(ads, res);
3586 status = ADS_SUCCESS;
3589 TALLOC_FREE(tmp_ctx);
3594 /********************************************************************
3595 ********************************************************************/
3597 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3599 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3600 const char *attrs[] = {"domainFunctionality", NULL};
3603 ADS_STRUCT *ads_s = ads;
3605 *val = DS_DOMAIN_FUNCTION_2000;
3607 /* establish a new ldap tcp session if necessary */
3609 if ( !ads->ldap.ld ) {
3611 * ADS_STRUCT may be being reused after a
3612 * DC lookup, so ads->ldap.ss may already have a
3613 * good address. If not, re-initialize the passed-in
3614 * ADS_STRUCT with the given server.XXXX parameters.
3616 * Note that this doesn't depend on
3617 * ads->server.ldap_server != NULL,
3618 * as the case where ads->server.ldap_server==NULL and
3619 * ads->ldap.ss != zero_address is precisely the DC
3620 * lookup case where ads->ldap.ss was found by going
3621 * through ads_find_dc() again we want to avoid repeating.
3623 if (is_zero_addr(&ads->ldap.ss)) {
3624 ads_s = ads_init(tmp_ctx,
3626 ads->server.workgroup,
3627 ads->server.ldap_server,
3629 if (ads_s == NULL ) {
3630 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3636 * Reset ads->config.flags as it can contain the flags
3637 * returned by the previous CLDAP ping when reusing the struct.
3639 ads_s->config.flags = 0;
3641 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3642 status = ads_connect( ads_s );
3643 if ( !ADS_ERR_OK(status))
3647 /* If the attribute does not exist assume it is a Windows 2000
3648 functional domain */
3650 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3651 if (!ADS_ERR_OK(status)) {
3652 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3653 status = ADS_SUCCESS;
3658 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3659 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3661 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3664 ads_msgfree(ads_s, res);
3667 TALLOC_FREE(tmp_ctx);
3673 * find the domain sid for our domain
3674 * @param ads connection to ads server
3675 * @param sid Pointer to domain sid
3676 * @return status of search
3678 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3680 const char *attrs[] = {"objectSid", NULL};
3684 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3686 if (!ADS_ERR_OK(rc)) return rc;
3687 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3688 ads_msgfree(ads, res);
3689 return ADS_ERROR_SYSTEM(ENOENT);
3691 ads_msgfree(ads, res);
3697 * find our site name
3698 * @param ads connection to ads server
3699 * @param mem_ctx Pointer to talloc context
3700 * @param site_name Pointer to the sitename
3701 * @return status of search
3703 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3707 const char *dn, *service_name;
3708 const char *attrs[] = { "dsServiceName", NULL };
3710 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3711 if (!ADS_ERR_OK(status)) {
3715 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3716 if (service_name == NULL) {
3717 ads_msgfree(ads, res);
3718 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3721 ads_msgfree(ads, res);
3723 /* go up three levels */
3724 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3726 return ADS_ERROR(LDAP_NO_MEMORY);
3729 *site_name = talloc_strdup(mem_ctx, dn);
3730 if (*site_name == NULL) {
3731 return ADS_ERROR(LDAP_NO_MEMORY);
3736 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3741 * find the site dn where a machine resides
3742 * @param ads connection to ads server
3743 * @param mem_ctx Pointer to talloc context
3744 * @param computer_name name of the machine
3745 * @param site_name Pointer to the sitename
3746 * @return status of search
3748 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3752 const char *parent, *filter;
3753 char *config_context = NULL;
3756 /* shortcut a query */
3757 if (strequal(computer_name, ads->config.ldap_server_name)) {
3758 return ads_site_dn(ads, mem_ctx, site_dn);
3761 status = ads_config_path(ads, mem_ctx, &config_context);
3762 if (!ADS_ERR_OK(status)) {
3766 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3767 if (filter == NULL) {
3768 return ADS_ERROR(LDAP_NO_MEMORY);
3771 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3772 filter, NULL, &res);
3773 if (!ADS_ERR_OK(status)) {
3777 if (ads_count_replies(ads, res) != 1) {
3778 ads_msgfree(ads, res);
3779 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3782 dn = ads_get_dn(ads, mem_ctx, res);
3784 ads_msgfree(ads, res);
3785 return ADS_ERROR(LDAP_NO_MEMORY);
3788 /* go up three levels */
3789 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3790 if (parent == NULL) {
3791 ads_msgfree(ads, res);
3793 return ADS_ERROR(LDAP_NO_MEMORY);
3796 *site_dn = talloc_strdup(mem_ctx, parent);
3797 if (*site_dn == NULL) {
3798 ads_msgfree(ads, res);
3800 return ADS_ERROR(LDAP_NO_MEMORY);
3804 ads_msgfree(ads, res);
3810 * get the upn suffixes for a domain
3811 * @param ads connection to ads server
3812 * @param mem_ctx Pointer to talloc context
3813 * @param suffixes Pointer to an array of suffixes
3814 * @param num_suffixes Pointer to the number of suffixes
3815 * @return status of search
3817 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3822 char *config_context = NULL;
3823 const char *attrs[] = { "uPNSuffixes", NULL };
3825 status = ads_config_path(ads, mem_ctx, &config_context);
3826 if (!ADS_ERR_OK(status)) {
3830 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3832 return ADS_ERROR(LDAP_NO_MEMORY);
3835 status = ads_search_dn(ads, &res, base, attrs);
3836 if (!ADS_ERR_OK(status)) {
3840 if (ads_count_replies(ads, res) != 1) {
3841 ads_msgfree(ads, res);
3842 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3845 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3846 if ((*suffixes) == NULL) {
3847 ads_msgfree(ads, res);
3848 return ADS_ERROR(LDAP_NO_MEMORY);
3851 ads_msgfree(ads, res);
3857 * get the joinable ous for a domain
3858 * @param ads connection to ads server
3859 * @param mem_ctx Pointer to talloc context
3860 * @param ous Pointer to an array of ous
3861 * @param num_ous Pointer to the number of ous
3862 * @return status of search
3864 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3865 TALLOC_CTX *mem_ctx,
3870 LDAPMessage *res = NULL;
3871 LDAPMessage *msg = NULL;
3872 const char *attrs[] = { "dn", NULL };
3875 status = ads_search(ads, &res,
3876 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3878 if (!ADS_ERR_OK(status)) {
3882 count = ads_count_replies(ads, res);
3884 ads_msgfree(ads, res);
3885 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3888 for (msg = ads_first_entry(ads, res); msg;
3889 msg = ads_next_entry(ads, msg)) {
3890 const char **p = discard_const_p(const char *, *ous);
3893 dn = ads_get_dn(ads, talloc_tos(), msg);
3895 ads_msgfree(ads, res);
3896 return ADS_ERROR(LDAP_NO_MEMORY);
3899 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3901 ads_msgfree(ads, res);
3902 return ADS_ERROR(LDAP_NO_MEMORY);
3906 *ous = discard_const_p(char *, p);
3909 ads_msgfree(ads, res);
3916 * pull a struct dom_sid from an extended dn string
3917 * @param mem_ctx TALLOC_CTX
3918 * @param extended_dn string
3919 * @param flags string type of extended_dn
3920 * @param sid pointer to a struct dom_sid
3921 * @return NT_STATUS_OK on success,
3922 * NT_INVALID_PARAMETER on error,
3923 * NT_STATUS_NOT_FOUND if no SID present
3925 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3926 const char *extended_dn,
3927 enum ads_extended_dn_flags flags,
3928 struct dom_sid *sid)
3933 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3936 /* otherwise extended_dn gets stripped off */
3937 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3938 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3941 * ADS_EXTENDED_DN_HEX_STRING:
3942 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3944 * ADS_EXTENDED_DN_STRING (only with w2k3):
3945 * <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
3947 * Object with no SID, such as an Exchange Public Folder
3948 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3951 p = strchr(dn, ';');
3953 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3956 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3957 DEBUG(5,("No SID present in extended dn\n"));
3958 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3961 p += strlen(";<SID=");
3965 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3970 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3974 case ADS_EXTENDED_DN_STRING:
3975 if (!string_to_sid(sid, p)) {
3976 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3979 case ADS_EXTENDED_DN_HEX_STRING: {
3984 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3986 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3989 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3991 DEBUG(10,("failed to parse sid\n"));
3992 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3997 DEBUG(10,("unknown extended dn format\n"));
3998 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4001 return ADS_ERROR_NT(NT_STATUS_OK);
4004 /********************************************************************
4005 ********************************************************************/
4007 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4009 LDAPMessage *res = NULL;
4014 status = ads_find_machine_acct(ads, &res, machine_name);
4015 if (!ADS_ERR_OK(status)) {
4016 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4017 lp_netbios_name()));
4021 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4022 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4026 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4027 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4031 ads_msgfree(ads, res);
4036 /********************************************************************
4037 ********************************************************************/
4039 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4040 LDAPMessage *msg, size_t *num_values)
4042 const char *field = "msDS-AdditionalDnsHostName";
4043 struct berval **values = NULL;
4045 size_t i, converted_size;
4048 * Windows DC implicitly adds a short name for each FQDN added to
4049 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4050 * suffix "\0$" which we should ignore (see bug #14406).
4053 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4054 if (values == NULL) {
4058 *num_values = ldap_count_values_len(values);
4060 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4062 ldap_value_free_len(values);
4066 for (i = 0; i < *num_values; i++) {
4068 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4070 strnlen(values[i]->bv_val,
4072 &ret[i], &converted_size)) {
4073 ldap_value_free_len(values);
4079 ldap_value_free_len(values);
4083 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4085 const char *machine_name,
4086 char ***hostnames_array,
4087 size_t *num_hostnames)
4090 LDAPMessage *res = NULL;
4093 status = ads_find_machine_acct(ads,
4096 if (!ADS_ERR_OK(status)) {
4097 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4102 count = ads_count_replies(ads, res);
4104 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4108 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4109 if (*hostnames_array == NULL) {
4110 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4112 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4117 ads_msgfree(ads, res);
4122 /********************************************************************
4123 ********************************************************************/
4125 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4127 LDAPMessage *res = NULL;
4132 status = ads_find_machine_acct(ads, &res, machine_name);
4133 if (!ADS_ERR_OK(status)) {
4134 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4135 lp_netbios_name()));
4139 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4140 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4144 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4145 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4149 ads_msgfree(ads, res);
4154 /********************************************************************
4155 ********************************************************************/
4157 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4159 LDAPMessage *res = NULL;
4165 status = ads_find_machine_acct(ads, &res, machine_name);
4166 if (!ADS_ERR_OK(status)) {
4167 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4168 lp_netbios_name()));
4172 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4173 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4177 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4178 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4182 ads_msgfree(ads, res);
4184 ok = (strlen(name) > 0);
4192 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4195 * Join a machine to a realm
4196 * Creates the machine account and sets the machine password
4197 * @param ads connection to ads server
4198 * @param machine name of host to add
4199 * @param org_unit Organizational unit to place machine in
4200 * @return status of join
4202 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4203 uint32_t account_type, const char *org_unit)
4206 LDAPMessage *res = NULL;
4209 /* machine name must be lowercase */
4210 machine = SMB_STRDUP(machine_name);
4211 strlower_m(machine);
4214 status = ads_find_machine_acct(ads, (void **)&res, machine);
4215 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4216 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4217 status = ads_leave_realm(ads, machine);
4218 if (!ADS_ERR_OK(status)) {
4219 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4220 machine, ads->config.realm));
4225 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4226 if (!ADS_ERR_OK(status)) {
4227 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4232 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4233 if (!ADS_ERR_OK(status)) {
4234 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4240 ads_msgfree(ads, res);
4247 * Delete a machine from the realm
4248 * @param ads connection to ads server
4249 * @param hostname Machine to remove
4250 * @return status of delete
4252 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4257 char *hostnameDN, *host;
4259 LDAPControl ldap_control;
4260 LDAPControl * pldap_control[2] = {NULL, NULL};
4262 pldap_control[0] = &ldap_control;
4263 memset(&ldap_control, 0, sizeof(LDAPControl));
4264 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4266 /* hostname must be lowercase */
4267 host = SMB_STRDUP(hostname);
4268 if (!strlower_m(host)) {
4270 return ADS_ERROR_SYSTEM(EINVAL);
4273 status = ads_find_machine_acct(ads, &res, host);
4274 if (!ADS_ERR_OK(status)) {
4275 DEBUG(0, ("Host account for %s does not exist.\n", host));
4280 msg = ads_first_entry(ads, res);
4283 return ADS_ERROR_SYSTEM(ENOENT);
4286 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4287 if (hostnameDN == NULL) {
4289 return ADS_ERROR_SYSTEM(ENOENT);
4292 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4294 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4296 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4299 if (rc != LDAP_SUCCESS) {
4300 const char *attrs[] = { "cn", NULL };
4301 LDAPMessage *msg_sub;
4303 /* we only search with scope ONE, we do not expect any further
4304 * objects to be created deeper */
4306 status = ads_do_search_retry(ads, hostnameDN,
4307 LDAP_SCOPE_ONELEVEL,
4308 "(objectclass=*)", attrs, &res);
4310 if (!ADS_ERR_OK(status)) {
4312 TALLOC_FREE(hostnameDN);
4316 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4317 msg_sub = ads_next_entry(ads, msg_sub)) {
4321 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4323 TALLOC_FREE(hostnameDN);
4324 return ADS_ERROR(LDAP_NO_MEMORY);
4327 status = ads_del_dn(ads, dn);
4328 if (!ADS_ERR_OK(status)) {
4329 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4332 TALLOC_FREE(hostnameDN);
4339 /* there should be no subordinate objects anymore */
4340 status = ads_do_search_retry(ads, hostnameDN,
4341 LDAP_SCOPE_ONELEVEL,
4342 "(objectclass=*)", attrs, &res);
4344 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4346 TALLOC_FREE(hostnameDN);
4350 /* delete hostnameDN now */
4351 status = ads_del_dn(ads, hostnameDN);
4352 if (!ADS_ERR_OK(status)) {
4354 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4355 TALLOC_FREE(hostnameDN);
4360 TALLOC_FREE(hostnameDN);
4362 status = ads_find_machine_acct(ads, &res, host);
4363 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4364 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4365 DEBUG(3, ("Failed to remove host account.\n"));
4375 * pull all token-sids from an LDAP dn
4376 * @param ads connection to ads server
4377 * @param mem_ctx TALLOC_CTX for allocating sid array
4378 * @param dn of LDAP object
4379 * @param user_sid pointer to struct dom_sid (objectSid)
4380 * @param primary_group_sid pointer to struct dom_sid (self composed)
4381 * @param sids pointer to sid array to allocate
4382 * @param num_sids counter of SIDs pulled
4383 * @return status of token query
4385 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4386 TALLOC_CTX *mem_ctx,
4388 struct dom_sid *user_sid,
4389 struct dom_sid *primary_group_sid,
4390 struct dom_sid **sids,
4394 LDAPMessage *res = NULL;
4396 size_t tmp_num_sids;
4397 struct dom_sid *tmp_sids;
4398 struct dom_sid tmp_user_sid;
4399 struct dom_sid tmp_primary_group_sid;
4401 const char *attrs[] = {
4408 status = ads_search_retry_dn(ads, &res, dn, attrs);
4409 if (!ADS_ERR_OK(status)) {
4413 count = ads_count_replies(ads, res);
4415 ads_msgfree(ads, res);
4416 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4419 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4420 ads_msgfree(ads, res);
4421 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4424 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4425 ads_msgfree(ads, res);
4426 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4430 /* hack to compose the primary group sid without knowing the
4433 struct dom_sid domsid;
4435 sid_copy(&domsid, &tmp_user_sid);
4437 if (!sid_split_rid(&domsid, NULL)) {
4438 ads_msgfree(ads, res);
4439 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4442 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4443 ads_msgfree(ads, res);
4444 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4448 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4450 if (tmp_num_sids == 0 || !tmp_sids) {
4451 ads_msgfree(ads, res);
4452 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4456 *num_sids = tmp_num_sids;
4464 *user_sid = tmp_user_sid;
4467 if (primary_group_sid) {
4468 *primary_group_sid = tmp_primary_group_sid;
4471 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4473 ads_msgfree(ads, res);
4474 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4478 * Find a sAMAccountName in LDAP
4479 * @param ads connection to ads server
4480 * @param mem_ctx TALLOC_CTX for allocating sid array
4481 * @param samaccountname to search
4482 * @param uac_ret uint32_t pointer userAccountControl attribute value
4483 * @param dn_ret pointer to dn
4484 * @return status of token query
4486 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4487 TALLOC_CTX *mem_ctx,
4488 const char *samaccountname,
4490 const char **dn_ret)
4493 const char *attrs[] = { "userAccountControl", NULL };
4495 LDAPMessage *res = NULL;
4499 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4501 if (filter == NULL) {
4502 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4506 status = ads_do_search_all(ads, ads->config.bind_path,
4508 filter, attrs, &res);
4510 if (!ADS_ERR_OK(status)) {
4514 if (ads_count_replies(ads, res) != 1) {
4515 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4519 dn = ads_get_dn(ads, talloc_tos(), res);
4521 status = ADS_ERROR(LDAP_NO_MEMORY);
4525 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4526 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4535 *dn_ret = talloc_strdup(mem_ctx, dn);
4537 status = ADS_ERROR(LDAP_NO_MEMORY);
4543 ads_msgfree(ads, res);
4549 * find our configuration path
4550 * @param ads connection to ads server
4551 * @param mem_ctx Pointer to talloc context
4552 * @param config_path Pointer to the config path
4553 * @return status of search
4555 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4556 TALLOC_CTX *mem_ctx,
4560 LDAPMessage *res = NULL;
4561 const char *config_context = NULL;
4562 const char *attrs[] = { "configurationNamingContext", NULL };
4564 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4565 "(objectclass=*)", attrs, &res);
4566 if (!ADS_ERR_OK(status)) {
4570 config_context = ads_pull_string(ads, mem_ctx, res,
4571 "configurationNamingContext");
4572 ads_msgfree(ads, res);
4573 if (!config_context) {
4574 return ADS_ERROR(LDAP_NO_MEMORY);
4578 *config_path = talloc_strdup(mem_ctx, config_context);
4579 if (!*config_path) {
4580 return ADS_ERROR(LDAP_NO_MEMORY);
4584 return ADS_ERROR(LDAP_SUCCESS);
4588 * find the displayName of an extended right
4589 * @param ads connection to ads server
4590 * @param config_path The config path
4591 * @param mem_ctx Pointer to talloc context
4592 * @param GUID struct of the rightsGUID
4593 * @return status of search
4595 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4596 const char *config_path,
4597 TALLOC_CTX *mem_ctx,
4598 const struct GUID *rights_guid)
4601 LDAPMessage *res = NULL;
4603 const char *attrs[] = { "displayName", NULL };
4604 const char *result = NULL;
4607 if (!ads || !mem_ctx || !rights_guid) {
4611 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4612 GUID_string(mem_ctx, rights_guid));
4617 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4622 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4624 if (!ADS_ERR_OK(rc)) {
4628 if (ads_count_replies(ads, res) != 1) {
4632 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4635 ads_msgfree(ads, res);
4640 * verify or build and verify an account ou
4641 * @param mem_ctx Pointer to talloc context
4642 * @param ads connection to ads server
4644 * @return status of search
4647 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4649 const char **account_ou)
4655 if (account_ou == NULL) {
4656 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4659 if (*account_ou != NULL) {
4660 exploded_dn = ldap_explode_dn(*account_ou, 0);
4662 ldap_value_free(exploded_dn);
4667 ou_string = ads_ou_string(ads, *account_ou);
4669 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4672 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4673 ads->config.bind_path);
4674 SAFE_FREE(ou_string);
4677 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4680 exploded_dn = ldap_explode_dn(name, 0);
4682 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4684 ldap_value_free(exploded_dn);