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"
37 #include "auth/credentials/credentials.h"
44 * @brief basic ldap client-side routines for ads server communications
46 * The routines contained here should do the necessary ldap calls for
49 * Important note: attribute names passed into ads_ routines must
50 * already be in UTF-8 format. We do not convert them because in almost
51 * all cases, they are just ascii (which is represented with the same
52 * codepoints in UTF-8). This may have to change at some point
56 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
58 static SIG_ATOMIC_T gotalarm;
60 /***************************************************************
61 Signal function to tell us we timed out.
62 ****************************************************************/
64 static void gotalarm_sig(int signum)
69 LDAP *ldap_open_with_timeout(const char *server,
70 struct sockaddr_storage *ss,
71 int port, unsigned int to)
77 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
78 "%u seconds\n", server, port, to));
83 CatchSignal(SIGALRM, gotalarm_sig);
85 /* End setup timeout. */
88 if ( strchr_m(server, ':') ) {
90 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
93 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
99 #ifdef HAVE_LDAP_INIT_FD
102 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
103 unsigned timeout_ms = 1000 * to;
105 status = open_socket_out(ss, port, timeout_ms, &fd);
106 if (!NT_STATUS_IS_OK(status)) {
107 DEBUG(3, ("open_socket_out: failed to open socket\n"));
111 /* define LDAP_PROTO_TCP from openldap.h if required */
112 #ifndef LDAP_PROTO_TCP
113 #define LDAP_PROTO_TCP 1
115 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
117 #elif defined(HAVE_LDAP_INITIALIZE)
118 ldap_err = ldap_initialize(&ldp, uri);
120 ldp = ldap_open(server, port);
122 ldap_err = LDAP_SUCCESS;
124 ldap_err = LDAP_OTHER;
127 if (ldap_err != LDAP_SUCCESS) {
128 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
129 uri, ldap_err2string(ldap_err)));
131 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
135 /* Teardown timeout. */
137 CatchSignal(SIGALRM, SIG_IGN);
143 static int ldap_search_with_timeout(LDAP *ld,
144 LDAP_CONST char *base,
146 LDAP_CONST char *filter,
149 LDAPControl **sctrls,
150 LDAPControl **cctrls,
154 int to = lp_ldap_timeout();
155 struct timeval timeout;
156 struct timeval *timeout_ptr = NULL;
159 DBG_DEBUG("ldap_search: base => [%s], filter => [%s], scope => [%d]\n",
164 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
170 timeout_ptr = &timeout;
172 /* Setup alarm timeout. */
173 CatchSignal(SIGALRM, gotalarm_sig);
174 /* Make the alarm time one second beyond
175 the timeout we're setting for the
176 remote search timeout, to allow that
177 to fire in preference. */
179 /* End setup timeout. */
183 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
184 attrsonly, sctrls, cctrls, timeout_ptr,
188 /* Teardown alarm timeout. */
189 CatchSignal(SIGALRM, SIG_IGN);
194 return LDAP_TIMELIMIT_EXCEEDED;
197 * A bug in OpenLDAP means ldap_search_ext_s can return
198 * LDAP_SUCCESS but with a NULL res pointer. Cope with
199 * this. See bug #6279 for details. JRA.
203 return LDAP_TIMELIMIT_EXCEEDED;
209 /**********************************************
210 Do client and server sitename match ?
211 **********************************************/
213 bool ads_sitename_match(ADS_STRUCT *ads)
215 if (ads->config.server_site_name == NULL &&
216 ads->config.client_site_name == NULL ) {
217 DEBUG(10,("ads_sitename_match: both null\n"));
220 if (ads->config.server_site_name &&
221 ads->config.client_site_name &&
222 strequal(ads->config.server_site_name,
223 ads->config.client_site_name)) {
224 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
227 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
228 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
229 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
233 /**********************************************
234 Is this the closest DC ?
235 **********************************************/
237 bool ads_closest_dc(ADS_STRUCT *ads)
239 if (ads->config.flags & NBT_SERVER_CLOSEST) {
240 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
244 /* not sure if this can ever happen */
245 if (ads_sitename_match(ads)) {
246 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
250 if (ads->config.client_site_name == NULL) {
251 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
255 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
256 ads->config.ldap_server_name));
261 static bool ads_fill_cldap_reply(ADS_STRUCT *ads,
263 const struct sockaddr_storage *ss,
264 const struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply)
266 TALLOC_CTX *frame = talloc_stackframe();
268 char addr[INET6_ADDRSTRLEN];
272 print_sockaddr(addr, sizeof(addr), ss);
274 /* Check the CLDAP reply flags */
276 if (!(cldap_reply->server_type & NBT_SERVER_LDAP)) {
277 DBG_WARNING("%s's CLDAP reply says it is not an LDAP server!\n",
283 /* Fill in the ads->config values */
285 ADS_TALLOC_CONST_FREE(ads->config.workgroup);
286 ADS_TALLOC_CONST_FREE(ads->config.realm);
287 ADS_TALLOC_CONST_FREE(ads->config.bind_path);
288 ADS_TALLOC_CONST_FREE(ads->config.ldap_server_name);
289 ADS_TALLOC_CONST_FREE(ads->config.server_site_name);
290 ADS_TALLOC_CONST_FREE(ads->config.client_site_name);
292 if (!check_cldap_reply_required_flags(cldap_reply->server_type,
293 ads->config.flags)) {
298 ads->config.ldap_server_name = talloc_strdup(ads,
299 cldap_reply->pdc_dns_name);
300 if (ads->config.ldap_server_name == NULL) {
301 DBG_WARNING("Out of memory\n");
306 ads->config.workgroup = talloc_strdup(ads, cldap_reply->domain_name);
307 if (ads->config.workgroup == NULL) {
308 DBG_WARNING("Out of memory\n");
313 ads->config.realm = talloc_asprintf_strupper_m(ads,
315 cldap_reply->dns_domain);
316 if (ads->config.realm == NULL) {
317 DBG_WARNING("Out of memory\n");
322 status = ads_build_dn(ads->config.realm, ads, &dn);
323 if (!ADS_ERR_OK(status)) {
324 DBG_DEBUG("Failed to build bind path: %s\n",
329 ads->config.bind_path = dn;
331 if (*cldap_reply->server_site) {
332 ads->config.server_site_name =
333 talloc_strdup(ads, cldap_reply->server_site);
334 if (ads->config.server_site_name == NULL) {
335 DBG_WARNING("Out of memory\n");
341 if (*cldap_reply->client_site) {
342 ads->config.client_site_name =
343 talloc_strdup(ads, cldap_reply->client_site);
344 if (ads->config.client_site_name == NULL) {
345 DBG_WARNING("Out of memory\n");
351 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
354 /* Store our site name. */
355 sitename_store(cldap_reply->domain_name, cldap_reply->client_site);
356 sitename_store(cldap_reply->dns_domain, cldap_reply->client_site);
358 /* Leave this until last so that the flags are not clobbered */
359 ads->config.flags = cldap_reply->server_type;
370 try a connection to a given ldap server, returning True and setting the servers IP
371 in the ads struct if successful
373 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
374 struct sockaddr_storage *ss)
376 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply = {};
377 TALLOC_CTX *frame = talloc_stackframe();
379 char addr[INET6_ADDRSTRLEN] = { 0, };
386 print_sockaddr(addr, sizeof(addr), ss);
388 DBG_INFO("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
389 addr, ads->server.realm);
391 ok = ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply);
393 DBG_NOTICE("ads_cldap_netlogon_5(%s, %s) failed.\n",
394 addr, ads->server.realm);
399 ok = ads_fill_cldap_reply(ads, gc, ss, &cldap_reply);
401 DBG_NOTICE("ads_fill_cldap_reply(%s, %s) failed.\n",
402 addr, ads->server.realm);
411 /**********************************************************************
412 send a cldap ping to list of servers, one at a time, until one of
413 them answers it's an ldap server. Record success in the ADS_STRUCT.
414 Take note of and update negative connection cache.
415 **********************************************************************/
417 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
419 struct samba_sockaddr *sa_list,
422 TALLOC_CTX *frame = talloc_stackframe();
423 struct timeval endtime = timeval_current_ofs(MAX(3,lp_ldap_timeout()/2), 0);
424 uint32_t nt_version = NETLOGON_NT_VERSION_5 | NETLOGON_NT_VERSION_5EX;
425 struct tsocket_address **ts_list = NULL;
426 const struct tsocket_address * const *ts_list_const = NULL;
427 struct samba_sockaddr **req_sa_list = NULL;
428 struct netlogon_samlogon_response **responses = NULL;
429 size_t num_requests = 0;
435 ts_list = talloc_zero_array(frame,
436 struct tsocket_address *,
438 if (ts_list == NULL) {
440 return NT_STATUS_NO_MEMORY;
443 req_sa_list = talloc_zero_array(frame,
444 struct samba_sockaddr *,
446 if (req_sa_list == NULL) {
448 return NT_STATUS_NO_MEMORY;
453 * The retry loop is bound by the timeout
458 for (i = 0; i < count; i++) {
459 char server[INET6_ADDRSTRLEN];
462 if (is_zero_addr(&sa_list[i].u.ss)) {
466 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
468 status = check_negative_conn_cache(domain, server);
469 if (!NT_STATUS_IS_OK(status)) {
473 ret = tsocket_address_inet_from_strings(ts_list, "ip",
475 &ts_list[num_requests]);
477 status = map_nt_error_from_unix(errno);
478 DBG_WARNING("Failed to create tsocket_address for %s - %s\n",
479 server, nt_errstr(status));
484 req_sa_list[num_requests] = &sa_list[i];
488 DBG_DEBUG("Try to create %zu netlogon connections for domain '%s' "
489 "(provided count of addresses was %zu).\n",
494 if (num_requests == 0) {
495 status = NT_STATUS_NO_LOGON_SERVERS;
496 DBG_WARNING("domain[%s] num_requests[%zu] for count[%zu] - %s\n",
497 domain, num_requests, count, nt_errstr(status));
502 ts_list_const = (const struct tsocket_address * const *)ts_list;
504 status = cldap_multi_netlogon(frame,
505 ts_list_const, num_requests,
506 ads->server.realm, NULL,
508 1, endtime, &responses);
509 if (!NT_STATUS_IS_OK(status)) {
510 DBG_WARNING("cldap_multi_netlogon(realm=%s, num_requests=%zu) "
511 "for count[%zu] - %s\n",
516 return NT_STATUS_NO_LOGON_SERVERS;
519 for (i = 0; i < num_requests; i++) {
520 struct NETLOGON_SAM_LOGON_RESPONSE_EX *cldap_reply = NULL;
521 char server[INET6_ADDRSTRLEN];
523 if (responses[i] == NULL) {
527 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
529 if (responses[i]->ntver != NETLOGON_NT_VERSION_5EX) {
530 DBG_NOTICE("realm=[%s] nt_version mismatch: 0x%08x for %s\n",
532 responses[i]->ntver, server);
536 cldap_reply = &responses[i]->data.nt5_ex;
538 /* Returns ok only if it matches the correct server type */
539 ok = ads_fill_cldap_reply(ads,
541 &req_sa_list[i]->u.ss,
544 DBG_DEBUG("realm[%s]: selected %s => %s\n",
546 server, cldap_reply->pdc_dns_name);
547 if (CHECK_DEBUGLVL(DBGLVL_DEBUG)) {
548 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
555 DBG_NOTICE("realm[%s] server %s %s - not usable\n",
557 server, cldap_reply->pdc_dns_name);
558 if (CHECK_DEBUGLVL(DBGLVL_NOTICE)) {
559 NDR_PRINT_DEBUG(NETLOGON_SAM_LOGON_RESPONSE_EX,
562 add_failed_connection_entry(domain, server,
563 NT_STATUS_CLIENT_SERVER_PARAMETERS_INVALID);
570 expired = timeval_expired(&endtime);
576 /* keep track of failures as all were not suitable */
577 for (i = 0; i < num_requests; i++) {
578 char server[INET6_ADDRSTRLEN];
580 print_sockaddr(server, sizeof(server), &req_sa_list[i]->u.ss);
582 add_failed_connection_entry(domain, server,
583 NT_STATUS_UNSUCCESSFUL);
586 status = NT_STATUS_NO_LOGON_SERVERS;
587 DBG_WARNING("realm[%s] no valid response "
588 "num_requests[%zu] for count[%zu] - %s\n",
590 num_requests, count, nt_errstr(status));
592 return NT_STATUS_NO_LOGON_SERVERS;
595 /***************************************************************************
596 resolve a name and perform an "ldap ping" using NetBIOS and related methods
597 ****************************************************************************/
599 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
600 const char *domain, const char *realm)
604 struct samba_sockaddr *sa_list = NULL;
607 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
610 status = get_sorted_dc_list(talloc_tos(),
616 if (!NT_STATUS_IS_OK(status)) {
620 /* remove servers which are known to be dead based on
621 the corresponding DNS method */
623 for (i = 0; i < count; ++i) {
624 char server[INET6_ADDRSTRLEN];
626 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
629 check_negative_conn_cache(realm, server))) {
630 /* Ensure we add the workgroup name for this
631 IP address as negative too. */
632 add_failed_connection_entry(
634 NT_STATUS_UNSUCCESSFUL);
639 status = cldap_ping_list(ads, domain, sa_list, count);
641 TALLOC_FREE(sa_list);
647 /**********************************************************************
648 resolve a name and perform an "ldap ping" using DNS
649 **********************************************************************/
651 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
655 struct samba_sockaddr *sa_list = NULL;
658 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
661 status = get_sorted_dc_list(talloc_tos(),
667 if (!NT_STATUS_IS_OK(status)) {
668 TALLOC_FREE(sa_list);
672 status = cldap_ping_list(ads, realm, sa_list, count);
674 TALLOC_FREE(sa_list);
679 /**********************************************************************
680 Try to find an AD dc using our internal name resolution routines
681 Try the realm first and then the workgroup name if netbios is not
683 **********************************************************************/
685 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
687 const char *c_domain = "";
689 bool use_own_domain = False;
690 char *sitename = NULL;
691 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
694 /* if the realm and workgroup are both empty, assume they are ours */
697 c_realm = ads->server.realm;
703 /* special case where no realm and no workgroup means our own */
704 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
705 use_own_domain = True;
706 c_realm = lp_realm();
710 if (!lp_disable_netbios()) {
711 if (use_own_domain) {
712 c_domain = lp_workgroup();
714 c_domain = ads->server.workgroup;
715 if (!*c_realm && (!c_domain || !*c_domain)) {
716 c_domain = lp_workgroup();
725 if (!*c_realm && !*c_domain) {
726 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
728 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
732 * In case of LDAP we use get_dc_name() as that
733 * creates the custom krb5.conf file
735 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
737 struct sockaddr_storage ip_out;
739 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
740 " and falling back to domain '%s'\n",
743 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
745 if (is_zero_addr(&ip_out)) {
746 return NT_STATUS_NO_LOGON_SERVERS;
750 * we call ads_try_connect() to fill in the
751 * ads->config details
753 ok = ads_try_connect(ads, false, &ip_out);
759 return NT_STATUS_NO_LOGON_SERVERS;
763 sitename = sitename_fetch(talloc_tos(), c_realm);
764 status = resolve_and_ping_dns(ads, sitename, c_realm);
766 if (NT_STATUS_IS_OK(status)) {
767 TALLOC_FREE(sitename);
771 /* In case we failed to contact one of our closest DC on our
773 * need to try to find another DC, retry with a site-less SRV
778 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
779 "our site (%s), Trying to find another DC "
780 "for realm '%s' (domain '%s')\n",
781 sitename, c_realm, c_domain));
782 namecache_delete(c_realm, 0x1C);
784 resolve_and_ping_dns(ads, NULL, c_realm);
786 if (NT_STATUS_IS_OK(status)) {
787 TALLOC_FREE(sitename);
792 TALLOC_FREE(sitename);
795 /* try netbios as fallback - if permitted,
796 or if configuration specifically requests it */
799 DEBUG(3, ("ads_find_dc: falling back to netbios "
800 "name resolution for domain '%s' (realm '%s')\n",
804 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
805 if (NT_STATUS_IS_OK(status)) {
810 DEBUG(1, ("ads_find_dc: "
811 "name resolution for realm '%s' (domain '%s') failed: %s\n",
812 c_realm, c_domain, nt_errstr(status)));
816 * Connect to the LDAP server
817 * @param ads Pointer to an existing ADS_STRUCT
818 * @return status of connection
820 ADS_STATUS ads_connect_creds(ADS_STRUCT *ads, struct cli_credentials *creds)
822 int version = LDAP_VERSION3;
825 char addr[INET6_ADDRSTRLEN];
826 struct sockaddr_storage existing_ss;
828 bool start_tls = false;
830 zero_sockaddr(&existing_ss);
832 SMB_ASSERT(creds != NULL);
835 * ads_connect can be passed in a reused ADS_STRUCT
836 * with an existing non-zero ads->ldap.ss IP address
837 * that was stored by going through ads_find_dc()
838 * if ads->server.ldap_server was NULL.
840 * If ads->server.ldap_server is still NULL but
841 * the target address isn't the zero address, then
842 * store that address off off before zeroing out
843 * ads->ldap so we don't keep doing multiple calls
844 * to ads_find_dc() in the reuse case.
846 * If a caller wants a clean ADS_STRUCT they
847 * will TALLOC_FREE it and allocate a new one
848 * by calling ads_init(), which ensures
849 * ads->ldap.ss is a properly zero'ed out valid IP
852 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
853 /* Save off the address we previously found by ads_find_dc(). */
854 existing_ss = ads->ldap.ss;
858 ZERO_STRUCT(ads->ldap_tls_data);
859 ZERO_STRUCT(ads->ldap_wrap_data);
860 ads->ldap.last_attempt = time_mono(NULL);
861 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
863 /* try with a user specified server */
865 if (DEBUGLEVEL >= 11) {
866 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
867 DEBUG(11,("ads_connect: entering\n"));
868 DEBUGADD(11,("%s\n", s));
872 if (ads->server.ldap_server) {
874 struct sockaddr_storage ss;
876 DBG_DEBUG("Resolving name of LDAP server '%s'.\n",
877 ads->server.ldap_server);
878 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
880 DEBUG(5,("ads_connect: unable to resolve name %s\n",
881 ads->server.ldap_server));
882 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
886 if (is_zero_addr(&ss)) {
887 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
891 ok = ads_try_connect(ads, ads->server.gc, &ss);
896 /* The choice of which GC use is handled one level up in
897 ads_connect_gc(). If we continue on from here with
898 ads_find_dc() we will get GC searches on port 389 which
899 doesn't work. --jerry */
901 if (ads->server.gc == true) {
902 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
905 if (ads->server.no_fallback) {
906 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
911 if (!is_zero_addr(&existing_ss)) {
912 /* We saved off who we should talk to. */
913 bool ok = ads_try_connect(ads,
920 * Keep trying to find a server and fall through
921 * into ads_find_dc() again.
923 DBG_DEBUG("Failed to connect to DC via LDAP server IP address, "
924 "trying to find another DC.\n");
927 ntstatus = ads_find_dc(ads);
928 if (NT_STATUS_IS_OK(ntstatus)) {
932 status = ADS_ERROR_NT(ntstatus);
937 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
938 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
940 if (!ads->auth.kdc_server) {
941 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
942 ads->auth.kdc_server = talloc_strdup(ads, addr);
943 if (ads->auth.kdc_server == NULL) {
944 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
949 /* If the caller() requested no LDAP bind, then we are done */
951 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
952 status = ADS_SUCCESS;
956 ads->ldap_tls_data.mem_ctx = talloc_init("ads LDAP TLS connection memory");
957 if (!ads->ldap_tls_data.mem_ctx) {
958 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
962 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
963 if (!ads->ldap_wrap_data.mem_ctx) {
964 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
968 /* Otherwise setup the TCP LDAP session */
970 if (ads->auth.flags & ADS_AUTH_SASL_LDAPS) {
972 ads->ldap.port = 636;
973 } else if (ads->auth.flags & ADS_AUTH_SASL_STARTTLS) {
976 ads->ldap.port = 389;
978 ads->ldap.port = 389;
981 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
983 ads->ldap.port, lp_ldap_timeout());
984 if (ads->ldap.ld == NULL) {
985 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
988 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
990 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
993 unsigned int to = lp_ldap_connection_timeout();
994 struct berval *rspdata = NULL;
1001 CatchSignal(SIGALRM, gotalarm_sig);
1003 /* End setup timeout. */
1006 rc = ldap_extended_operation_s(ads->ldap.ld,
1007 LDAP_EXOP_START_TLS,
1013 if (gotalarm != 0 && rc == LDAP_SUCCESS) {
1018 /* Teardown timeout. */
1020 CatchSignal(SIGALRM, SIG_IGN);
1023 if (rspoid != NULL) {
1024 ldap_memfree(rspoid);
1027 if (rspdata != NULL) {
1028 ber_bvfree(rspdata);
1031 if (rc != LDAP_SUCCESS) {
1032 status = ADS_ERROR_LDAP(rc);
1038 unsigned int to = lp_ldap_connection_timeout();
1043 CatchSignal(SIGALRM, gotalarm_sig);
1045 /* End setup timeout. */
1048 status = ads_setup_tls_wrapping(&ads->ldap_tls_data,
1050 ads->config.ldap_server_name);
1053 /* Teardown timeout. */
1055 CatchSignal(SIGALRM, SIG_IGN);
1058 if ( !ADS_ERR_OK(status) ) {
1063 /* cache the successful connection for workgroup and realm */
1064 if (ads_closest_dc(ads)) {
1065 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
1066 saf_store( ads->server.realm, ads->config.ldap_server_name);
1069 /* fill in the current time and offsets */
1071 status = ads_current_time( ads );
1072 if ( !ADS_ERR_OK(status) ) {
1076 /* Now do the bind */
1078 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
1079 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
1083 status = ads_sasl_bind(ads, creds);
1086 if (DEBUGLEVEL >= 11) {
1087 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
1088 DEBUG(11,("ads_connect: leaving with: %s\n",
1089 ads_errstr(status)));
1090 DEBUGADD(11,("%s\n", s));
1098 * Connect to the LDAP server using anonymous credentials
1099 * @param ads Pointer to an existing ADS_STRUCT
1100 * @return status of connection
1102 ADS_STATUS ads_connect_anon(ADS_STRUCT *ads)
1104 struct cli_credentials *creds = NULL;
1107 creds = cli_credentials_init_anon(talloc_tos());
1108 if (creds == NULL) {
1109 return ADS_ERROR_SYSTEM(errno);
1112 ads->auth.flags |= ADS_AUTH_ANON_BIND;
1113 status = ads_connect_creds(ads, creds);
1119 * Connect to the LDAP server using without a bind at all
1120 * @param ads Pointer to an existing ADS_STRUCT
1121 * @return status of connection
1123 ADS_STATUS ads_connect_no_bind(ADS_STRUCT *ads)
1125 ads->auth.flags |= ADS_AUTH_NO_BIND;
1126 return ads_connect_anon(ads);
1130 * Connect to the LDAP server using the machine account
1131 * @param ads Pointer to an existing ADS_STRUCT
1132 * @return status of connection
1134 ADS_STATUS ads_connect_machine(ADS_STRUCT *ads)
1136 struct cli_credentials *creds = NULL;
1140 ntstatus = pdb_get_trust_credentials(ads->server.workgroup,
1144 if (!NT_STATUS_IS_OK(ntstatus)) {
1145 return ADS_ERROR_NT(ntstatus);
1148 status = ads_connect_creds(ads, creds);
1154 * Connect to the LDAP server
1155 * @param ads Pointer to an existing ADS_STRUCT
1156 * @return status of connection
1158 ADS_STATUS ads_connect(ADS_STRUCT *ads)
1160 struct cli_credentials *creds = NULL;
1164 ntstatus = ads_legacy_creds(ads, talloc_tos(), &creds);
1165 if (!NT_STATUS_IS_OK(ntstatus)) {
1166 return ADS_ERROR_NT(ntstatus);
1169 status = ads_connect_creds(ads, creds);
1175 * Connect to the LDAP server using given credentials
1176 * @param ads Pointer to an existing ADS_STRUCT
1177 * @return status of connection
1179 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
1181 ads->auth.flags |= ADS_AUTH_USER_CREDS;
1183 return ads_connect(ads);
1187 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
1188 * @param ads Pointer to an existing ADS_STRUCT
1190 * Sets the ads->ldap.ss to a valid
1191 * zero ip address that can be detected by
1192 * our is_zero_addr() function. Otherwise
1193 * it is left as AF_UNSPEC (0).
1195 void ads_zero_ldap(ADS_STRUCT *ads)
1197 ZERO_STRUCT(ads->ldap);
1199 * Initialize the sockaddr_storage so we can use
1200 * sockaddr test functions against it.
1202 zero_sockaddr(&ads->ldap.ss);
1206 * Disconnect the LDAP server
1207 * @param ads Pointer to an existing ADS_STRUCT
1209 void ads_disconnect(ADS_STRUCT *ads)
1212 ldap_unbind(ads->ldap.ld);
1213 ads->ldap.ld = NULL;
1215 if (ads->ldap_tls_data.mem_ctx) {
1216 talloc_free(ads->ldap_tls_data.mem_ctx);
1218 if (ads->ldap_wrap_data.wrap_ops &&
1219 ads->ldap_wrap_data.wrap_ops->disconnect) {
1220 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
1222 if (ads->ldap_wrap_data.mem_ctx) {
1223 talloc_free(ads->ldap_wrap_data.mem_ctx);
1226 ZERO_STRUCT(ads->ldap_tls_data);
1227 ZERO_STRUCT(ads->ldap_wrap_data);
1231 Duplicate a struct berval into talloc'ed memory
1233 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
1235 struct berval *value;
1237 if (!in_val) return NULL;
1239 value = talloc_zero(ctx, struct berval);
1242 if (in_val->bv_len == 0) return value;
1244 value->bv_len = in_val->bv_len;
1245 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
1251 Make a values list out of an array of (struct berval *)
1253 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
1254 const struct berval **in_vals)
1256 struct berval **values;
1259 if (!in_vals) return NULL;
1260 for (i=0; in_vals[i]; i++)
1261 ; /* count values */
1262 values = talloc_zero_array(ctx, struct berval *, i+1);
1263 if (!values) return NULL;
1265 for (i=0; in_vals[i]; i++) {
1266 values[i] = dup_berval(ctx, in_vals[i]);
1272 UTF8-encode a values list out of an array of (char *)
1274 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
1280 if (!in_vals) return NULL;
1281 for (i=0; in_vals[i]; i++)
1282 ; /* count values */
1283 values = talloc_zero_array(ctx, char *, i+1);
1284 if (!values) return NULL;
1286 for (i=0; in_vals[i]; i++) {
1287 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
1288 TALLOC_FREE(values);
1296 Pull a (char *) array out of a UTF8-encoded values list
1298 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
1302 size_t converted_size;
1304 if (!in_vals) return NULL;
1305 for (i=0; in_vals[i]; i++)
1306 ; /* count values */
1307 values = talloc_zero_array(ctx, char *, i+1);
1308 if (!values) return NULL;
1310 for (i=0; in_vals[i]; i++) {
1311 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
1313 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
1314 "%s\n", strerror(errno)));
1321 * Do a search with paged results. cookie must be null on the first
1322 * call, and then returned on each subsequent call. It will be null
1323 * again when the entire search is complete
1324 * @param ads connection to ads server
1325 * @param bind_path Base dn for the search
1326 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1327 * @param expr Search expression - specified in local charset
1328 * @param attrs Attributes to retrieve - specified in utf8 or ascii
1329 * @param res ** which will contain results - free res* with ads_msgfree()
1330 * @param count Number of entries retrieved on this page
1331 * @param cookie The paged results cookie to be returned on subsequent calls
1332 * @return status of search
1334 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
1335 const char *bind_path,
1336 int scope, const char *expr,
1337 const char **attrs, void *args,
1339 int *count, struct berval **cookie)
1342 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1343 size_t converted_size;
1344 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
1345 BerElement *cookie_be = NULL;
1346 struct berval *cookie_bv= NULL;
1347 BerElement *ext_be = NULL;
1348 struct berval *ext_bv= NULL;
1351 ads_control *external_control = (ads_control *) args;
1355 if (!(ctx = talloc_init("ads_do_paged_search_args")))
1356 return ADS_ERROR(LDAP_NO_MEMORY);
1358 /* 0 means the conversion worked but the result was empty
1359 so we only fail if it's -1. In any case, it always
1360 at least nulls out the dest */
1361 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1362 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1364 rc = LDAP_NO_MEMORY;
1368 if (!attrs || !(*attrs))
1369 search_attrs = NULL;
1371 /* This would be the utf8-encoded version...*/
1372 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1373 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
1374 rc = LDAP_NO_MEMORY;
1379 /* Paged results only available on ldap v3 or later */
1380 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
1381 if (version < LDAP_VERSION3) {
1382 rc = LDAP_NOT_SUPPORTED;
1386 cookie_be = ber_alloc_t(LBER_USE_DER);
1388 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1389 ber_bvfree(*cookie); /* don't need it from last time */
1392 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1394 ber_flatten(cookie_be, &cookie_bv);
1395 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1396 PagedResults.ldctl_iscritical = (char) 1;
1397 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1398 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1400 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1401 NoReferrals.ldctl_iscritical = (char) 0;
1402 NoReferrals.ldctl_value.bv_len = 0;
1403 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1405 if (external_control &&
1406 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1407 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1409 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1410 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1412 /* win2k does not accept a ldctl_value being passed in */
1414 if (external_control->val != 0) {
1416 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1417 rc = LDAP_NO_MEMORY;
1421 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1422 rc = LDAP_NO_MEMORY;
1425 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1426 rc = LDAP_NO_MEMORY;
1430 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1431 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1434 ExternalCtrl.ldctl_value.bv_len = 0;
1435 ExternalCtrl.ldctl_value.bv_val = NULL;
1438 controls[0] = &NoReferrals;
1439 controls[1] = &PagedResults;
1440 controls[2] = &ExternalCtrl;
1444 controls[0] = &NoReferrals;
1445 controls[1] = &PagedResults;
1449 /* we need to disable referrals as the openldap libs don't
1450 handle them and paged results at the same time. Using them
1451 together results in the result record containing the server
1452 page control being removed from the result list (tridge/jmcd)
1454 leaving this in despite the control that says don't generate
1455 referrals, in case the server doesn't support it (jmcd)
1457 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1459 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1460 search_attrs, 0, controls,
1461 NULL, LDAP_NO_LIMIT,
1462 (LDAPMessage **)res);
1464 ber_free(cookie_be, 1);
1465 ber_bvfree(cookie_bv);
1468 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1469 ldap_err2string(rc)));
1470 if (rc == LDAP_OTHER) {
1474 ret = ldap_parse_result(ads->ldap.ld,
1482 if (ret == LDAP_SUCCESS) {
1483 DEBUG(3, ("ldap_search_with_timeout(%s) "
1484 "error: %s\n", expr, ldap_errmsg));
1485 ldap_memfree(ldap_errmsg);
1491 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1492 NULL, &rcontrols, 0);
1498 for (i=0; rcontrols[i]; i++) {
1499 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1500 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1501 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1503 /* the berval is the cookie, but must be freed when
1505 if (cookie_bv->bv_len) /* still more to do */
1506 *cookie=ber_bvdup(cookie_bv);
1509 ber_bvfree(cookie_bv);
1510 ber_free(cookie_be, 1);
1514 ldap_controls_free(rcontrols);
1517 talloc_destroy(ctx);
1520 ber_free(ext_be, 1);
1527 if (rc != LDAP_SUCCESS && *res != NULL) {
1528 ads_msgfree(ads, *res);
1532 /* if/when we decide to utf8-encode attrs, take out this next line */
1533 TALLOC_FREE(search_attrs);
1535 return ADS_ERROR(rc);
1538 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1539 int scope, const char *expr,
1540 const char **attrs, LDAPMessage **res,
1541 int *count, struct berval **cookie)
1543 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1548 * Get all results for a search. This uses ads_do_paged_search() to return
1549 * all entries in a large search.
1550 * @param ads connection to ads server
1551 * @param bind_path Base dn for the search
1552 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1553 * @param expr Search expression
1554 * @param attrs Attributes to retrieve
1555 * @param res ** which will contain results - free res* with ads_msgfree()
1556 * @return status of search
1558 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1559 int scope, const char *expr,
1560 const char **attrs, void *args,
1563 struct berval *cookie = NULL;
1568 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1571 if (!ADS_ERR_OK(status))
1574 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1576 LDAPMessage *res2 = NULL;
1577 LDAPMessage *msg, *next;
1579 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1580 attrs, args, &res2, &count, &cookie);
1581 if (!ADS_ERR_OK(status)) {
1585 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1586 that this works on all ldap libs, but I have only tested with openldap */
1587 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1588 next = ads_next_message(ads, msg);
1589 ldap_add_result_entry((LDAPMessage **)res, msg);
1591 /* note that we do not free res2, as the memory is now
1592 part of the main returned list */
1595 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1596 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1602 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1603 int scope, const char *expr,
1604 const char **attrs, LDAPMessage **res)
1606 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1609 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1610 int scope, const char *expr,
1611 const char **attrs, uint32_t sd_flags,
1616 args.control = ADS_SD_FLAGS_OID;
1617 args.val = sd_flags;
1618 args.critical = True;
1620 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1625 * Run a function on all results for a search. Uses ads_do_paged_search() and
1626 * runs the function as each page is returned, using ads_process_results()
1627 * @param ads connection to ads server
1628 * @param bind_path Base dn for the search
1629 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1630 * @param expr Search expression - specified in local charset
1631 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1632 * @param fn Function which takes attr name, values list, and data_area
1633 * @param data_area Pointer which is passed to function on each call
1634 * @return status of search
1636 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1637 int scope, const char *expr, const char **attrs,
1638 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1641 struct berval *cookie = NULL;
1646 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1649 if (!ADS_ERR_OK(status)) return status;
1651 ads_process_results(ads, res, fn, data_area);
1652 ads_msgfree(ads, res);
1655 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1656 &res, &count, &cookie);
1658 if (!ADS_ERR_OK(status)) break;
1660 ads_process_results(ads, res, fn, data_area);
1661 ads_msgfree(ads, res);
1668 * Do a search with a timeout.
1669 * @param ads connection to ads server
1670 * @param bind_path Base dn for the search
1671 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1672 * @param expr Search expression
1673 * @param attrs Attributes to retrieve
1674 * @param res ** which will contain results - free res* with ads_msgfree()
1675 * @return status of search
1677 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1679 const char **attrs, LDAPMessage **res)
1682 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1683 size_t converted_size;
1687 if (!(ctx = talloc_init("ads_do_search"))) {
1688 DEBUG(1,("ads_do_search: talloc_init() failed!\n"));
1689 return ADS_ERROR(LDAP_NO_MEMORY);
1692 /* 0 means the conversion worked but the result was empty
1693 so we only fail if it's negative. In any case, it always
1694 at least nulls out the dest */
1695 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1696 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1698 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!\n"));
1699 rc = LDAP_NO_MEMORY;
1703 if (!attrs || !(*attrs))
1704 search_attrs = NULL;
1706 /* This would be the utf8-encoded version...*/
1707 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1708 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1710 DEBUG(1,("ads_do_search: str_list_copy() failed!\n"));
1711 rc = LDAP_NO_MEMORY;
1716 /* see the note in ads_do_paged_search - we *must* disable referrals */
1717 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1719 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1720 search_attrs, 0, NULL, NULL,
1722 (LDAPMessage **)res);
1724 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1725 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1730 talloc_destroy(ctx);
1731 /* if/when we decide to utf8-encode attrs, take out this next line */
1732 TALLOC_FREE(search_attrs);
1733 return ADS_ERROR(rc);
1736 * Do a general ADS search
1737 * @param ads connection to ads server
1738 * @param res ** which will contain results - free res* with ads_msgfree()
1739 * @param expr Search expression
1740 * @param attrs Attributes to retrieve
1741 * @return status of search
1743 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1744 const char *expr, const char **attrs)
1746 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1751 * Do a search on a specific DistinguishedName
1752 * @param ads connection to ads server
1753 * @param res ** which will contain results - free res* with ads_msgfree()
1754 * @param dn DistinguishedName to search
1755 * @param attrs Attributes to retrieve
1756 * @return status of search
1758 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1759 const char *dn, const char **attrs)
1761 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1766 * Free up memory from a ads_search
1767 * @param ads connection to ads server
1768 * @param msg Search results to free
1770 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1777 * Get a dn from search results
1778 * @param ads connection to ads server
1779 * @param msg Search result
1782 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1784 char *utf8_dn, *unix_dn;
1785 size_t converted_size;
1787 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1790 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1794 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1795 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1799 ldap_memfree(utf8_dn);
1804 * Get the parent from a dn
1805 * @param dn the dn to return the parent from
1806 * @return parent dn string
1808 char *ads_parent_dn(const char *dn)
1816 p = strchr(dn, ',');
1826 * Find a machine account given a hostname
1827 * @param ads connection to ads server
1828 * @param res ** which will contain results - free res* with ads_msgfree()
1829 * @param host Hostname to search for
1830 * @return status of search
1832 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1833 const char *machine)
1837 const char *attrs[] = {
1838 /* This is how Windows checks for machine accounts */
1841 "userAccountControl",
1843 "ServicePrincipalName",
1844 "userPrincipalName",
1846 /* Additional attributes Samba checks */
1847 "msDS-AdditionalDnsHostName",
1848 "msDS-SupportedEncryptionTypes",
1849 "nTSecurityDescriptor",
1854 TALLOC_CTX *frame = talloc_stackframe();
1858 /* the easiest way to find a machine account anywhere in the tree
1859 is to look for hostname$ */
1860 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1862 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1866 status = ads_search(ads, res, expr, attrs);
1867 if (ADS_ERR_OK(status)) {
1868 if (ads_count_replies(ads, *res) != 1) {
1869 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1879 * Initialize a list of mods to be used in a modify request
1880 * @param ctx An initialized TALLOC_CTX
1881 * @return allocated ADS_MODLIST
1883 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1885 #define ADS_MODLIST_ALLOC_SIZE 10
1888 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1889 /* -1 is safety to make sure we don't go over the end.
1890 need to reset it to NULL before doing ldap modify */
1891 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1893 return (ADS_MODLIST)mods;
1898 add an attribute to the list, with values list already constructed
1900 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1901 int mod_op, const char *name,
1902 const void *_invals)
1905 LDAPMod **modlist = (LDAPMod **) *mods;
1906 struct berval **ber_values = NULL;
1907 char **char_values = NULL;
1910 mod_op = LDAP_MOD_DELETE;
1912 if (mod_op & LDAP_MOD_BVALUES) {
1913 const struct berval **b;
1914 b = discard_const_p(const struct berval *, _invals);
1915 ber_values = ads_dup_values(ctx, b);
1918 c = discard_const_p(const char *, _invals);
1919 char_values = ads_push_strvals(ctx, c);
1923 /* find the first empty slot */
1924 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1926 if (modlist[curmod] == (LDAPMod *) -1) {
1927 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1928 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1929 return ADS_ERROR(LDAP_NO_MEMORY);
1930 memset(&modlist[curmod], 0,
1931 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1932 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1933 *mods = (ADS_MODLIST)modlist;
1936 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1937 return ADS_ERROR(LDAP_NO_MEMORY);
1938 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1939 if (mod_op & LDAP_MOD_BVALUES) {
1940 modlist[curmod]->mod_bvalues = ber_values;
1941 } else if (mod_op & LDAP_MOD_DELETE) {
1942 modlist[curmod]->mod_values = NULL;
1944 modlist[curmod]->mod_values = char_values;
1947 modlist[curmod]->mod_op = mod_op;
1948 return ADS_ERROR(LDAP_SUCCESS);
1952 * Add a single string value to a mod list
1953 * @param ctx An initialized TALLOC_CTX
1954 * @param mods An initialized ADS_MODLIST
1955 * @param name The attribute name to add
1956 * @param val The value to add - NULL means DELETE
1957 * @return ADS STATUS indicating success of add
1959 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1960 const char *name, const char *val)
1962 const char *values[2];
1968 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1969 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1973 * Add an array of string values to a mod list
1974 * @param ctx An initialized TALLOC_CTX
1975 * @param mods An initialized ADS_MODLIST
1976 * @param name The attribute name to add
1977 * @param vals The array of string values to add - NULL means DELETE
1978 * @return ADS STATUS indicating success of add
1980 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1981 const char *name, const char **vals)
1984 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1985 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1986 name, (const void **) vals);
1990 * Add a single ber-encoded value to a mod list
1991 * @param ctx An initialized TALLOC_CTX
1992 * @param mods An initialized ADS_MODLIST
1993 * @param name The attribute name to add
1994 * @param val The value to add - NULL means DELETE
1995 * @return ADS STATUS indicating success of add
1997 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1998 const char *name, const struct berval *val)
2000 const struct berval *values[2];
2005 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
2006 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
2007 name, (const void *) values);
2010 static void ads_print_error(int ret, LDAP *ld)
2013 char *ld_error = NULL;
2014 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
2015 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
2017 ldap_err2string(ret),
2019 SAFE_FREE(ld_error);
2024 * Perform an ldap modify
2025 * @param ads connection to ads server
2026 * @param mod_dn DistinguishedName to modify
2027 * @param mods list of modifications to perform
2028 * @return status of modify
2030 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
2033 char *utf8_dn = NULL;
2034 size_t converted_size;
2036 this control is needed to modify that contains a currently
2037 non-existent attribute (but allowable for the object) to run
2039 LDAPControl PermitModify = {
2040 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
2043 LDAPControl *controls[2];
2045 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
2047 controls[0] = &PermitModify;
2050 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
2051 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2054 /* find the end of the list, marked by NULL or -1 */
2055 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2056 /* make sure the end of the list is NULL */
2058 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
2059 (LDAPMod **) mods, controls, NULL);
2060 ads_print_error(ret, ads->ldap.ld);
2061 TALLOC_FREE(utf8_dn);
2062 return ADS_ERROR(ret);
2066 * Perform an ldap add
2067 * @param ads connection to ads server
2068 * @param new_dn DistinguishedName to add
2069 * @param mods list of attributes and values for DN
2070 * @return status of add
2072 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
2075 char *utf8_dn = NULL;
2076 size_t converted_size;
2078 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
2080 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
2081 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!\n"));
2082 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2085 /* find the end of the list, marked by NULL or -1 */
2086 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
2087 /* make sure the end of the list is NULL */
2090 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
2091 ads_print_error(ret, ads->ldap.ld);
2092 TALLOC_FREE(utf8_dn);
2093 return ADS_ERROR(ret);
2097 * Delete a DistinguishedName
2098 * @param ads connection to ads server
2099 * @param new_dn DistinguishedName to delete
2100 * @return status of delete
2102 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
2105 char *utf8_dn = NULL;
2106 size_t converted_size;
2107 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
2108 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!\n"));
2109 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2112 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
2114 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
2115 ads_print_error(ret, ads->ldap.ld);
2116 TALLOC_FREE(utf8_dn);
2117 return ADS_ERROR(ret);
2121 * Build an org unit string
2122 * if org unit is Computers or blank then assume a container, otherwise
2123 * assume a / separated list of organisational units.
2124 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
2125 * @param ads connection to ads server
2126 * @param org_unit Organizational unit
2127 * @return org unit string - caller must free
2129 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
2135 if (!org_unit || !*org_unit) {
2137 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
2139 /* samba4 might not yet respond to a wellknownobject-query */
2140 return ret ? ret : SMB_STRDUP("cn=Computers");
2143 if (strequal(org_unit, "Computers")) {
2144 return SMB_STRDUP("cn=Computers");
2147 /* jmcd: removed "\\" from the separation chars, because it is
2148 needed as an escape for chars like '#' which are valid in an
2150 status = ads_build_path(org_unit, "/", "ou=", 1, &dn);
2151 if (!ADS_ERR_OK(status)) {
2159 * Get a org unit string for a well-known GUID
2160 * @param ads connection to ads server
2161 * @param wknguid Well known GUID
2162 * @return org unit string - caller must free
2164 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
2167 LDAPMessage *res = NULL;
2168 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
2169 **bind_dn_exp = NULL;
2170 const char *attrs[] = {"distinguishedName", NULL};
2171 int new_ln, wkn_ln, bind_ln, i;
2173 if (wknguid == NULL) {
2177 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
2178 DEBUG(1, ("asprintf failed!\n"));
2182 status = ads_search_dn(ads, &res, base, attrs);
2183 if (!ADS_ERR_OK(status)) {
2184 DEBUG(1,("Failed while searching for: %s\n", base));
2188 if (ads_count_replies(ads, res) != 1) {
2192 /* substitute the bind-path from the well-known-guid-search result */
2193 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
2198 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
2203 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
2208 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
2210 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
2213 new_ln = wkn_ln - bind_ln;
2215 ret = SMB_STRDUP(wkn_dn_exp[0]);
2220 for (i=1; i < new_ln; i++) {
2223 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
2229 ret = SMB_STRDUP(s);
2238 ads_msgfree(ads, res);
2239 TALLOC_FREE(wkn_dn);
2241 ldap_value_free(wkn_dn_exp);
2244 ldap_value_free(bind_dn_exp);
2251 * Adds (appends) an item to an attribute array, rather then
2252 * replacing the whole list
2253 * @param ctx An initialized TALLOC_CTX
2254 * @param mods An initialized ADS_MODLIST
2255 * @param name name of the ldap attribute to append to
2256 * @param vals an array of values to add
2257 * @return status of addition
2260 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
2261 const char *name, const char **vals)
2263 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
2264 (const void *) vals);
2268 * Determines the an account's current KVNO via an LDAP lookup
2269 * @param ads An initialized ADS_STRUCT
2270 * @param account_name the NT samaccountname.
2271 * @return the kvno for the account, or -1 in case of a failure.
2274 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
2276 LDAPMessage *res = NULL;
2277 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
2279 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
2280 char *dn_string = NULL;
2283 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
2284 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
2287 ret = ads_search(ads, &res, filter, attrs);
2289 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
2290 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
2291 ads_msgfree(ads, res);
2295 dn_string = ads_get_dn(ads, talloc_tos(), res);
2297 DEBUG(0,("ads_get_kvno: out of memory.\n"));
2298 ads_msgfree(ads, res);
2301 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
2302 TALLOC_FREE(dn_string);
2304 /* ---------------------------------------------------------
2305 * 0 is returned as a default KVNO from this point on...
2306 * This is done because Windows 2000 does not support key
2307 * version numbers. Chances are that a failure in the next
2308 * step is simply due to Windows 2000 being used for a
2309 * domain controller. */
2312 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
2313 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
2314 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
2315 ads_msgfree(ads, res);
2320 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
2321 ads_msgfree(ads, res);
2326 * Determines the computer account's current KVNO via an LDAP lookup
2327 * @param ads An initialized ADS_STRUCT
2328 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2329 * @return the kvno for the computer account, or -1 in case of a failure.
2332 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
2334 char *computer_account = NULL;
2337 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
2341 kvno = ads_get_kvno(ads, computer_account);
2342 free(computer_account);
2348 * This clears out all registered spn's for a given hostname
2349 * @param ads An initialized ADS_STRUCT
2350 * @param machine_name the NetBIOS name of the computer.
2351 * @return 0 upon success, non-zero otherwise.
2354 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
2357 LDAPMessage *res = NULL;
2359 const char *servicePrincipalName[1] = {NULL};
2361 char *dn_string = NULL;
2363 ret = ads_find_machine_acct(ads, &res, machine_name);
2364 if (!ADS_ERR_OK(ret)) {
2365 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
2366 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
2367 ads_msgfree(ads, res);
2371 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
2372 ctx = talloc_init("ads_clear_service_principal_names");
2374 ads_msgfree(ads, res);
2375 return ADS_ERROR(LDAP_NO_MEMORY);
2378 if (!(mods = ads_init_mods(ctx))) {
2379 talloc_destroy(ctx);
2380 ads_msgfree(ads, res);
2381 return ADS_ERROR(LDAP_NO_MEMORY);
2383 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2384 if (!ADS_ERR_OK(ret)) {
2385 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
2386 ads_msgfree(ads, res);
2387 talloc_destroy(ctx);
2390 dn_string = ads_get_dn(ads, talloc_tos(), res);
2392 talloc_destroy(ctx);
2393 ads_msgfree(ads, res);
2394 return ADS_ERROR(LDAP_NO_MEMORY);
2396 ret = ads_gen_mod(ads, dn_string, mods);
2397 TALLOC_FREE(dn_string);
2398 if (!ADS_ERR_OK(ret)) {
2399 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2401 ads_msgfree(ads, res);
2402 talloc_destroy(ctx);
2406 ads_msgfree(ads, res);
2407 talloc_destroy(ctx);
2412 * @brief Search for an element in a string array.
2414 * @param[in] el_array The string array to search.
2416 * @param[in] num_el The number of elements in the string array.
2418 * @param[in] el The string to search.
2420 * @return True if found, false if not.
2422 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2426 if (el_array == NULL || num_el == 0 || el == NULL) {
2430 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2433 cmp = strcasecmp_m(el_array[i], el);
2443 * @brief This gets the service principal names of an existing computer account.
2445 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2447 * @param[in] ads The ADS context to use.
2449 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2450 * identify the computer account.
2452 * @param[in] spn_array A pointer to store the array for SPNs.
2454 * @param[in] num_spns The number of principals stored in the array.
2456 * @return 0 on success, or a ADS error if a failure occurred.
2458 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2460 const char *machine_name,
2465 LDAPMessage *res = NULL;
2468 status = ads_find_machine_acct(ads,
2471 if (!ADS_ERR_OK(status)) {
2472 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2477 count = ads_count_replies(ads, res);
2479 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2483 *spn_array = ads_pull_strings(ads,
2486 "servicePrincipalName",
2488 if (*spn_array == NULL) {
2489 DEBUG(1, ("Host account for %s does not have service principal "
2492 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2497 ads_msgfree(ads, res);
2503 * This adds a service principal name to an existing computer account
2504 * (found by hostname) in AD.
2505 * @param ads An initialized ADS_STRUCT
2506 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2507 * @param spns An array or strings for the service principals to add,
2508 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2509 * @return 0 upon success, or non-zero if a failure occurs
2512 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2513 const char *machine_name,
2518 LDAPMessage *res = NULL;
2520 char *dn_string = NULL;
2521 const char **servicePrincipalName = spns;
2523 ret = ads_find_machine_acct(ads, &res, machine_name);
2524 if (!ADS_ERR_OK(ret)) {
2525 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2527 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2528 ads_msgfree(ads, res);
2532 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2533 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2534 ads_msgfree(ads, res);
2535 return ADS_ERROR(LDAP_NO_MEMORY);
2538 DEBUG(5,("ads_add_service_principal_name: INFO: "
2539 "Adding %s to host %s\n",
2540 spns[0] ? "N/A" : spns[0], machine_name));
2543 DEBUG(5,("ads_add_service_principal_name: INFO: "
2544 "Adding %s to host %s\n",
2545 spns[1] ? "N/A" : spns[1], machine_name));
2547 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2548 ret = ADS_ERROR(LDAP_NO_MEMORY);
2552 ret = ads_add_strlist(ctx,
2554 "servicePrincipalName",
2555 servicePrincipalName);
2556 if (!ADS_ERR_OK(ret)) {
2557 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2561 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2562 ret = ADS_ERROR(LDAP_NO_MEMORY);
2566 ret = ads_gen_mod(ads, dn_string, mods);
2567 if (!ADS_ERR_OK(ret)) {
2568 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2574 ads_msgfree(ads, res);
2578 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2581 uint32_t acct_ctrl = 0;
2584 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2592 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2594 const struct berval *machine_pw_val)
2598 TALLOC_CTX *frame = talloc_stackframe();
2599 uint32_t acct_control;
2600 char *control_str = NULL;
2601 const char *attrs[] = {
2605 LDAPMessage *res = NULL;
2608 dn = ads_get_dn(ads, frame, msg);
2610 ret = ADS_ERROR(LDAP_NO_MEMORY);
2614 acct_control = ads_get_acct_ctrl(ads, msg);
2615 if (acct_control == 0) {
2616 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2621 * Changing the password, disables the account. So we need to change the
2622 * userAccountControl flags to enable it again.
2624 mods = ads_init_mods(frame);
2626 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2630 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2632 ret = ads_gen_mod(ads, dn, mods);
2633 if (!ADS_ERR_OK(ret)) {
2639 * To activate the account, we need to disable and enable it.
2641 acct_control |= UF_ACCOUNTDISABLE;
2643 control_str = talloc_asprintf(frame, "%u", acct_control);
2644 if (control_str == NULL) {
2645 ret = ADS_ERROR(LDAP_NO_MEMORY);
2649 mods = ads_init_mods(frame);
2651 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2655 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2657 ret = ads_gen_mod(ads, dn, mods);
2658 if (!ADS_ERR_OK(ret)) {
2662 TALLOC_FREE(control_str);
2665 * Enable the account again.
2667 acct_control &= ~UF_ACCOUNTDISABLE;
2669 control_str = talloc_asprintf(frame, "%u", acct_control);
2670 if (control_str == NULL) {
2671 ret = ADS_ERROR(LDAP_NO_MEMORY);
2675 mods = ads_init_mods(frame);
2677 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2681 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2683 ret = ads_gen_mod(ads, dn, mods);
2684 if (!ADS_ERR_OK(ret)) {
2688 TALLOC_FREE(control_str);
2690 ret = ads_search_dn(ads, &res, dn, attrs);
2691 ads_msgfree(ads, res);
2700 * adds a machine account to the ADS server
2701 * @param ads An initialized ADS_STRUCT
2702 * @param machine_name - the NetBIOS machine name of this account.
2703 * @param account_type A number indicating the type of account to create
2704 * @param org_unit The LDAP path in which to place this account
2705 * @return 0 upon success, or non-zero otherwise
2708 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2709 const char *machine_name,
2710 const char *machine_password,
2711 const char *org_unit,
2712 uint32_t etype_list,
2713 const char *dns_domain_name)
2716 char *samAccountName = NULL;
2717 char *controlstr = NULL;
2718 TALLOC_CTX *ctx = NULL;
2720 char *machine_escaped = NULL;
2721 char *dns_hostname = NULL;
2722 char *new_dn = NULL;
2723 char *utf8_pw = NULL;
2724 size_t utf8_pw_len = 0;
2725 char *utf16_pw = NULL;
2726 size_t utf16_pw_len = 0;
2727 struct berval machine_pw_val;
2729 const char **spn_array = NULL;
2730 size_t num_spns = 0;
2731 const char *spn_prefix[] = {
2733 "RestrictedKrbHost",
2736 LDAPMessage *res = NULL;
2737 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2739 ctx = talloc_init("ads_add_machine_acct");
2741 return ADS_ERROR(LDAP_NO_MEMORY);
2744 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2745 if (machine_escaped == NULL) {
2746 ret = ADS_ERROR(LDAP_NO_MEMORY);
2750 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2751 if (utf8_pw == NULL) {
2752 ret = ADS_ERROR(LDAP_NO_MEMORY);
2755 utf8_pw_len = strlen(utf8_pw);
2757 ok = convert_string_talloc(ctx,
2758 CH_UTF8, CH_UTF16MUNGED,
2759 utf8_pw, utf8_pw_len,
2760 (void *)&utf16_pw, &utf16_pw_len);
2762 ret = ADS_ERROR(LDAP_NO_MEMORY);
2766 machine_pw_val = (struct berval) {
2768 .bv_len = utf16_pw_len,
2771 /* Check if the machine account already exists. */
2772 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2773 if (ADS_ERR_OK(ret)) {
2774 /* Change the machine account password */
2775 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2776 ads_msgfree(ads, res);
2780 ads_msgfree(ads, res);
2782 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2783 if (new_dn == NULL) {
2784 ret = ADS_ERROR(LDAP_NO_MEMORY);
2788 /* Create machine account */
2790 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2791 if (samAccountName == NULL) {
2792 ret = ADS_ERROR(LDAP_NO_MEMORY);
2796 dns_hostname = talloc_asprintf(ctx,
2800 if (dns_hostname == NULL) {
2801 ret = ADS_ERROR(LDAP_NO_MEMORY);
2805 /* Add dns_hostname SPNs */
2806 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2807 char *spn = talloc_asprintf(ctx,
2812 ret = ADS_ERROR(LDAP_NO_MEMORY);
2816 ok = add_string_to_array(ctx,
2821 ret = ADS_ERROR(LDAP_NO_MEMORY);
2826 /* Add machine_name SPNs */
2827 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2828 char *spn = talloc_asprintf(ctx,
2833 ret = ADS_ERROR(LDAP_NO_MEMORY);
2837 ok = add_string_to_array(ctx,
2842 ret = ADS_ERROR(LDAP_NO_MEMORY);
2847 /* Make sure to NULL terminate the array */
2848 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2849 if (spn_array == NULL) {
2850 ret = ADS_ERROR(LDAP_NO_MEMORY);
2853 spn_array[num_spns] = NULL;
2855 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2856 if (controlstr == NULL) {
2857 ret = ADS_ERROR(LDAP_NO_MEMORY);
2861 mods = ads_init_mods(ctx);
2863 ret = ADS_ERROR(LDAP_NO_MEMORY);
2867 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2868 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2869 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2870 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2871 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2872 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2874 ret = ads_gen_add(ads, new_dn, mods);
2877 SAFE_FREE(machine_escaped);
2878 talloc_destroy(ctx);
2884 * move a machine account to another OU on the ADS server
2885 * @param ads - An initialized ADS_STRUCT
2886 * @param machine_name - the NetBIOS machine name of this account.
2887 * @param org_unit - The LDAP path in which to place this account
2888 * @param moved - whether we moved the machine account (optional)
2889 * @return 0 upon success, or non-zero otherwise
2892 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2893 const char *org_unit, bool *moved)
2897 LDAPMessage *res = NULL;
2898 char *filter = NULL;
2899 char *computer_dn = NULL;
2901 char *computer_rdn = NULL;
2902 bool need_move = False;
2904 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2905 rc = ADS_ERROR(LDAP_NO_MEMORY);
2909 /* Find pre-existing machine */
2910 rc = ads_search(ads, &res, filter, NULL);
2911 if (!ADS_ERR_OK(rc)) {
2915 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2917 rc = ADS_ERROR(LDAP_NO_MEMORY);
2921 parent_dn = ads_parent_dn(computer_dn);
2922 if (strequal(parent_dn, org_unit)) {
2928 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2929 rc = ADS_ERROR(LDAP_NO_MEMORY);
2933 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2934 org_unit, 1, NULL, NULL);
2935 rc = ADS_ERROR(ldap_status);
2938 ads_msgfree(ads, res);
2940 TALLOC_FREE(computer_dn);
2941 SAFE_FREE(computer_rdn);
2943 if (!ADS_ERR_OK(rc)) {
2955 dump a binary result from ldap
2957 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2960 for (i=0; values[i]; i++) {
2962 printf("%s: ", field);
2963 for (j=0; j<values[i]->bv_len; j++) {
2964 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2970 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2973 for (i=0; values[i]; i++) {
2975 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2978 status = GUID_from_ndr_blob(&in, &guid);
2979 if (NT_STATUS_IS_OK(status)) {
2980 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2982 printf("%s: INVALID GUID\n", field);
2988 dump a sid result from ldap
2990 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2993 for (i=0; values[i]; i++) {
2996 struct dom_sid_buf tmp;
2997 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2998 values[i]->bv_len, &sid);
3002 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
3007 dump ntSecurityDescriptor
3009 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
3011 TALLOC_CTX *frame = talloc_stackframe();
3012 struct security_descriptor *psd;
3015 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
3016 values[0]->bv_len, &psd);
3017 if (!NT_STATUS_IS_OK(status)) {
3018 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3019 nt_errstr(status)));
3025 ads_disp_sd(ads, talloc_tos(), psd);
3032 dump a string result from ldap
3034 static void dump_string(const char *field, char **values)
3037 for (i=0; values[i]; i++) {
3038 printf("%s: %s\n", field, values[i]);
3043 dump a field from LDAP on stdout
3047 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
3052 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
3054 {"objectGUID", False, dump_guid},
3055 {"netbootGUID", False, dump_guid},
3056 {"nTSecurityDescriptor", False, dump_sd},
3057 {"dnsRecord", False, dump_binary},
3058 {"objectSid", False, dump_sid},
3059 {"securityIdentifier", False, dump_sid},
3060 {"tokenGroups", False, dump_sid},
3061 {"tokenGroupsNoGCAcceptable", False, dump_sid},
3062 {"tokengroupsGlobalandUniversal", False, dump_sid},
3063 {"mS-DS-CreatorSID", False, dump_sid},
3064 {"msExchMailboxGuid", False, dump_guid},
3065 {"msDS-TrustForestTrustInfo", False, dump_binary},
3070 if (!field) { /* must be end of an entry */
3075 for (i=0; handlers[i].name; i++) {
3076 if (strcasecmp_m(handlers[i].name, field) == 0) {
3077 if (!values) /* first time, indicate string or not */
3078 return handlers[i].string;
3079 handlers[i].handler(ads, field, (struct berval **) values);
3083 if (!handlers[i].name) {
3084 if (!values) /* first time, indicate string conversion */
3086 dump_string(field, (char **)values);
3092 * Dump a result from LDAP on stdout
3093 * used for debugging
3094 * @param ads connection to ads server
3095 * @param res Results to dump
3098 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
3100 ads_process_results(ads, res, ads_dump_field, NULL);
3104 * Walk through results, calling a function for each entry found.
3105 * The function receives a field name, a berval * array of values,
3106 * and a data area passed through from the start. The function is
3107 * called once with null for field and values at the end of each
3109 * @param ads connection to ads server
3110 * @param res Results to process
3111 * @param fn Function for processing each result
3112 * @param data_area user-defined area to pass to function
3114 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
3115 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
3120 size_t converted_size;
3122 if (!(ctx = talloc_init("ads_process_results")))
3125 for (msg = ads_first_entry(ads, res); msg;
3126 msg = ads_next_entry(ads, msg)) {
3130 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
3131 (LDAPMessage *)msg,&b);
3133 utf8_field=ldap_next_attribute(ads->ldap.ld,
3134 (LDAPMessage *)msg,b)) {
3135 struct berval **ber_vals;
3141 if (!pull_utf8_talloc(ctx, &field, utf8_field,
3144 DEBUG(0,("ads_process_results: "
3145 "pull_utf8_talloc failed: %s\n",
3149 string = fn(ads, field, NULL, data_area);
3154 utf8_vals = ldap_get_values(ads->ldap.ld,
3155 (LDAPMessage *)msg, field);
3156 p = discard_const_p(const char *, utf8_vals);
3157 str_vals = ads_pull_strvals(ctx, p);
3158 fn(ads, field, (void **) str_vals, data_area);
3159 ldap_value_free(utf8_vals);
3161 ber_vals = ldap_get_values_len(ads->ldap.ld,
3162 (LDAPMessage *)msg, field);
3163 fn(ads, field, (void **) ber_vals, data_area);
3165 ldap_value_free_len(ber_vals);
3167 ldap_memfree(utf8_field);
3170 talloc_free_children(ctx);
3171 fn(ads, NULL, NULL, data_area); /* completed an entry */
3174 talloc_destroy(ctx);
3178 * count how many replies are in a LDAPMessage
3179 * @param ads connection to ads server
3180 * @param res Results to count
3181 * @return number of replies
3183 int ads_count_replies(ADS_STRUCT *ads, void *res)
3185 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
3189 * pull the first entry from a ADS result
3190 * @param ads connection to ads server
3191 * @param res Results of search
3192 * @return first entry from result
3194 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
3196 return ldap_first_entry(ads->ldap.ld, res);
3200 * pull the next entry from a ADS result
3201 * @param ads connection to ads server
3202 * @param res Results of search
3203 * @return next entry from result
3205 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
3207 return ldap_next_entry(ads->ldap.ld, res);
3211 * pull the first message from a ADS result
3212 * @param ads connection to ads server
3213 * @param res Results of search
3214 * @return first message from result
3216 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
3218 return ldap_first_message(ads->ldap.ld, res);
3222 * pull the next message from a ADS result
3223 * @param ads connection to ads server
3224 * @param res Results of search
3225 * @return next message from result
3227 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
3229 return ldap_next_message(ads->ldap.ld, res);
3233 * pull a single string from a ADS result
3234 * @param ads connection to ads server
3235 * @param mem_ctx TALLOC_CTX to use for allocating result string
3236 * @param msg Results of search
3237 * @param field Attribute to retrieve
3238 * @return Result string in talloc context
3240 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
3246 size_t converted_size;
3248 values = ldap_get_values(ads->ldap.ld, msg, field);
3252 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
3257 ldap_value_free(values);
3262 * pull an array of strings from a ADS result
3263 * @param ads connection to ads server
3264 * @param mem_ctx TALLOC_CTX to use for allocating result string
3265 * @param msg Results of search
3266 * @param field Attribute to retrieve
3267 * @return Result strings in talloc context
3269 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3270 LDAPMessage *msg, const char *field,
3275 size_t i, converted_size;
3277 values = ldap_get_values(ads->ldap.ld, msg, field);
3281 *num_values = ldap_count_values(values);
3283 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3285 ldap_value_free(values);
3289 for (i=0;i<*num_values;i++) {
3290 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
3293 ldap_value_free(values);
3299 ldap_value_free(values);
3304 * pull an array of strings from a ADS result
3305 * (handle large multivalue attributes with range retrieval)
3306 * @param ads connection to ads server
3307 * @param mem_ctx TALLOC_CTX to use for allocating result string
3308 * @param msg Results of search
3309 * @param field Attribute to retrieve
3310 * @param current_strings strings returned by a previous call to this function
3311 * @param next_attribute The next query should ask for this attribute
3312 * @param num_values How many values did we get this time?
3313 * @param more_values Are there more values to get?
3314 * @return Result strings in talloc context
3316 char **ads_pull_strings_range(ADS_STRUCT *ads,
3317 TALLOC_CTX *mem_ctx,
3318 LDAPMessage *msg, const char *field,
3319 char **current_strings,
3320 const char **next_attribute,
3321 size_t *num_strings,
3325 char *expected_range_attrib, *range_attr = NULL;
3326 BerElement *ptr = NULL;
3329 size_t num_new_strings;
3330 unsigned long int range_start;
3331 unsigned long int range_end;
3333 /* we might have been given the whole lot anyway */
3334 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
3335 *more_strings = False;
3339 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
3341 /* look for Range result */
3342 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
3344 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
3345 /* we ignore the fact that this is utf8, as all attributes are ascii... */
3346 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
3354 /* nothing here - this field is just empty */
3355 *more_strings = False;
3359 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
3360 &range_start, &range_end) == 2) {
3361 *more_strings = True;
3363 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
3364 &range_start) == 1) {
3365 *more_strings = False;
3367 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attribute (%s)\n",
3369 ldap_memfree(range_attr);
3370 *more_strings = False;
3375 if ((*num_strings) != range_start) {
3376 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
3377 " - aborting range retrieval\n",
3378 range_attr, (unsigned int)(*num_strings) + 1, range_start));
3379 ldap_memfree(range_attr);
3380 *more_strings = False;
3384 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
3386 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
3387 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
3388 "strings in this bunch, but we only got %lu - aborting range retrieval\n",
3389 range_attr, (unsigned long int)range_end - range_start + 1,
3390 (unsigned long int)num_new_strings));
3391 ldap_memfree(range_attr);
3392 *more_strings = False;
3396 strings = talloc_realloc(mem_ctx, current_strings, char *,
3397 *num_strings + num_new_strings);
3399 if (strings == NULL) {
3400 ldap_memfree(range_attr);
3401 *more_strings = False;
3405 if (new_strings && num_new_strings) {
3406 memcpy(&strings[*num_strings], new_strings,
3407 sizeof(*new_strings) * num_new_strings);
3410 (*num_strings) += num_new_strings;
3412 if (*more_strings) {
3413 *next_attribute = talloc_asprintf(mem_ctx,
3418 if (!*next_attribute) {
3419 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3420 ldap_memfree(range_attr);
3421 *more_strings = False;
3426 ldap_memfree(range_attr);
3432 * pull a single uint32_t from a ADS result
3433 * @param ads connection to ads server
3434 * @param msg Results of search
3435 * @param field Attribute to retrieve
3436 * @param v Pointer to int to store result
3437 * @return boolean indicating success
3439 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3444 values = ldap_get_values(ads->ldap.ld, msg, field);
3448 ldap_value_free(values);
3452 *v = atoi(values[0]);
3453 ldap_value_free(values);
3458 * pull a single objectGUID from an ADS result
3459 * @param ads connection to ADS server
3460 * @param msg results of search
3461 * @param guid 37-byte area to receive text guid
3462 * @return boolean indicating success
3464 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3469 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3474 status = GUID_from_ndr_blob(&blob, guid);
3475 talloc_free(blob.data);
3476 return NT_STATUS_IS_OK(status);
3481 * pull a single struct dom_sid from a ADS result
3482 * @param ads connection to ads server
3483 * @param msg Results of search
3484 * @param field Attribute to retrieve
3485 * @param sid Pointer to sid to store result
3486 * @return boolean indicating success
3488 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3489 struct dom_sid *sid)
3491 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3495 * pull an array of struct dom_sids from a ADS result
3496 * @param ads connection to ads server
3497 * @param mem_ctx TALLOC_CTX for allocating sid array
3498 * @param msg Results of search
3499 * @param field Attribute to retrieve
3500 * @param sids pointer to sid array to allocate
3501 * @return the count of SIDs pulled
3503 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3504 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3506 struct berval **values;
3509 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3514 for (i=0; values[i]; i++)
3518 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3520 ldap_value_free_len(values);
3528 for (i=0; values[i]; i++) {
3530 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3531 values[i]->bv_len, &(*sids)[count]);
3533 struct dom_sid_buf buf;
3534 DBG_DEBUG("pulling SID: %s\n",
3535 dom_sid_str_buf(&(*sids)[count], &buf));
3540 ldap_value_free_len(values);
3545 * pull a struct security_descriptor from a ADS result
3546 * @param ads connection to ads server
3547 * @param mem_ctx TALLOC_CTX for allocating sid array
3548 * @param msg Results of search
3549 * @param field Attribute to retrieve
3550 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3551 * @return boolean indicating success
3553 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3554 LDAPMessage *msg, const char *field,
3555 struct security_descriptor **sd)
3557 struct berval **values;
3560 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3562 if (!values) return false;
3566 status = unmarshall_sec_desc(mem_ctx,
3567 (uint8_t *)values[0]->bv_val,
3568 values[0]->bv_len, sd);
3569 if (!NT_STATUS_IS_OK(status)) {
3570 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3571 nt_errstr(status)));
3576 ldap_value_free_len(values);
3581 * in order to support usernames longer than 21 characters we need to
3582 * use both the sAMAccountName and the userPrincipalName attributes
3583 * It seems that not all users have the userPrincipalName attribute set
3585 * @param ads connection to ads server
3586 * @param mem_ctx TALLOC_CTX for allocating sid array
3587 * @param msg Results of search
3588 * @return the username
3590 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3596 /* lookup_name() only works on the sAMAccountName to
3597 returning the username portion of userPrincipalName
3598 breaks winbindd_getpwnam() */
3600 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3601 if (ret && (p = strchr_m(ret, '@'))) {
3606 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3611 * find the update serial number - this is the core of the ldap cache
3612 * @param ads connection to ads server
3613 * @param ads connection to ADS server
3614 * @param usn Pointer to retrieved update serial number
3615 * @return status of search
3617 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3619 const char *attrs[] = {"highestCommittedUSN", NULL};
3623 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3624 if (!ADS_ERR_OK(status))
3627 if (ads_count_replies(ads, res) != 1) {
3628 ads_msgfree(ads, res);
3629 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3632 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3633 ads_msgfree(ads, res);
3634 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3637 ads_msgfree(ads, res);
3641 /* parse a ADS timestring - typical string is
3642 '20020917091222.0Z0' which means 09:12.22 17th September
3644 static time_t ads_parse_time(const char *str)
3650 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3651 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3652 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3661 /********************************************************************
3662 ********************************************************************/
3664 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3666 const char *attrs[] = {"currentTime", NULL};
3670 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3671 ADS_STRUCT *ads_s = ads;
3673 /* establish a new ldap tcp session if necessary */
3675 if ( !ads->ldap.ld ) {
3677 * ADS_STRUCT may be being reused after a
3678 * DC lookup, so ads->ldap.ss may already have a
3679 * good address. If not, re-initialize the passed-in
3680 * ADS_STRUCT with the given server.XXXX parameters.
3682 * Note that this doesn't depend on
3683 * ads->server.ldap_server != NULL,
3684 * as the case where ads->server.ldap_server==NULL and
3685 * ads->ldap.ss != zero_address is precisely the DC
3686 * lookup case where ads->ldap.ss was found by going
3687 * through ads_find_dc() again we want to avoid repeating.
3689 if (is_zero_addr(&ads->ldap.ss)) {
3690 ads_s = ads_init(tmp_ctx,
3692 ads->server.workgroup,
3693 ads->server.ldap_server,
3695 if (ads_s == NULL) {
3696 status = ADS_ERROR(LDAP_NO_MEMORY);
3702 * Reset ads->config.flags as it can contain the flags
3703 * returned by the previous CLDAP ping when reusing the struct.
3705 ads_s->config.flags = 0;
3707 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3708 status = ads_connect( ads_s );
3709 if ( !ADS_ERR_OK(status))
3713 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3714 if (!ADS_ERR_OK(status)) {
3718 timestr = ads_pull_string(ads_s, tmp_ctx, res, "currentTime");
3720 ads_msgfree(ads_s, res);
3721 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3725 /* but save the time and offset in the original ADS_STRUCT */
3727 ads->config.current_time = ads_parse_time(timestr);
3729 if (ads->config.current_time != 0) {
3730 ads->config.time_offset = ads->config.current_time - time(NULL);
3731 DBG_INFO("server time offset is %d seconds\n",
3732 ads->config.time_offset);
3734 ads->config.time_offset = 0;
3737 DBG_INFO("server time offset is %d seconds\n",
3738 ads->config.time_offset);
3740 ads_msgfree(ads, res);
3742 status = ADS_SUCCESS;
3745 TALLOC_FREE(tmp_ctx);
3750 /********************************************************************
3751 ********************************************************************/
3753 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3755 TALLOC_CTX *tmp_ctx = talloc_stackframe();
3756 const char *attrs[] = {"domainFunctionality", NULL};
3759 ADS_STRUCT *ads_s = ads;
3761 *val = DS_DOMAIN_FUNCTION_2000;
3763 /* establish a new ldap tcp session if necessary */
3765 if ( !ads->ldap.ld ) {
3767 * ADS_STRUCT may be being reused after a
3768 * DC lookup, so ads->ldap.ss may already have a
3769 * good address. If not, re-initialize the passed-in
3770 * ADS_STRUCT with the given server.XXXX parameters.
3772 * Note that this doesn't depend on
3773 * ads->server.ldap_server != NULL,
3774 * as the case where ads->server.ldap_server==NULL and
3775 * ads->ldap.ss != zero_address is precisely the DC
3776 * lookup case where ads->ldap.ss was found by going
3777 * through ads_find_dc() again we want to avoid repeating.
3779 if (is_zero_addr(&ads->ldap.ss)) {
3780 ads_s = ads_init(tmp_ctx,
3782 ads->server.workgroup,
3783 ads->server.ldap_server,
3785 if (ads_s == NULL ) {
3786 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3792 * Reset ads->config.flags as it can contain the flags
3793 * returned by the previous CLDAP ping when reusing the struct.
3795 ads_s->config.flags = 0;
3797 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3798 status = ads_connect( ads_s );
3799 if ( !ADS_ERR_OK(status))
3803 /* If the attribute does not exist assume it is a Windows 2000
3804 functional domain */
3806 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3807 if (!ADS_ERR_OK(status)) {
3808 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3809 status = ADS_SUCCESS;
3814 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3815 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3817 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3820 ads_msgfree(ads_s, res);
3823 TALLOC_FREE(tmp_ctx);
3829 * find the domain sid for our domain
3830 * @param ads connection to ads server
3831 * @param sid Pointer to domain sid
3832 * @return status of search
3834 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3836 const char *attrs[] = {"objectSid", NULL};
3840 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3842 if (!ADS_ERR_OK(rc)) return rc;
3843 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3844 ads_msgfree(ads, res);
3845 return ADS_ERROR_SYSTEM(ENOENT);
3847 ads_msgfree(ads, res);
3853 * find our site name
3854 * @param ads connection to ads server
3855 * @param mem_ctx Pointer to talloc context
3856 * @param site_name Pointer to the sitename
3857 * @return status of search
3859 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3863 const char *dn, *service_name;
3864 const char *attrs[] = { "dsServiceName", NULL };
3866 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3867 if (!ADS_ERR_OK(status)) {
3871 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3872 if (service_name == NULL) {
3873 ads_msgfree(ads, res);
3874 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3877 ads_msgfree(ads, res);
3879 /* go up three levels */
3880 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3882 return ADS_ERROR(LDAP_NO_MEMORY);
3885 *site_name = talloc_strdup(mem_ctx, dn);
3886 if (*site_name == NULL) {
3887 return ADS_ERROR(LDAP_NO_MEMORY);
3892 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3897 * find the site dn where a machine resides
3898 * @param ads connection to ads server
3899 * @param mem_ctx Pointer to talloc context
3900 * @param computer_name name of the machine
3901 * @param site_name Pointer to the sitename
3902 * @return status of search
3904 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3908 const char *parent, *filter;
3909 char *config_context = NULL;
3912 /* shortcut a query */
3913 if (strequal(computer_name, ads->config.ldap_server_name)) {
3914 return ads_site_dn(ads, mem_ctx, site_dn);
3917 status = ads_config_path(ads, mem_ctx, &config_context);
3918 if (!ADS_ERR_OK(status)) {
3922 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3923 if (filter == NULL) {
3924 return ADS_ERROR(LDAP_NO_MEMORY);
3927 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3928 filter, NULL, &res);
3929 if (!ADS_ERR_OK(status)) {
3933 if (ads_count_replies(ads, res) != 1) {
3934 ads_msgfree(ads, res);
3935 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3938 dn = ads_get_dn(ads, mem_ctx, res);
3940 ads_msgfree(ads, res);
3941 return ADS_ERROR(LDAP_NO_MEMORY);
3944 /* go up three levels */
3945 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3946 if (parent == NULL) {
3947 ads_msgfree(ads, res);
3949 return ADS_ERROR(LDAP_NO_MEMORY);
3952 *site_dn = talloc_strdup(mem_ctx, parent);
3953 if (*site_dn == NULL) {
3954 ads_msgfree(ads, res);
3956 return ADS_ERROR(LDAP_NO_MEMORY);
3960 ads_msgfree(ads, res);
3966 * get the upn suffixes for a domain
3967 * @param ads connection to ads server
3968 * @param mem_ctx Pointer to talloc context
3969 * @param suffixes Pointer to an array of suffixes
3970 * @param num_suffixes Pointer to the number of suffixes
3971 * @return status of search
3973 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3978 char *config_context = NULL;
3979 const char *attrs[] = { "uPNSuffixes", NULL };
3981 status = ads_config_path(ads, mem_ctx, &config_context);
3982 if (!ADS_ERR_OK(status)) {
3986 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3988 return ADS_ERROR(LDAP_NO_MEMORY);
3991 status = ads_search_dn(ads, &res, base, attrs);
3992 if (!ADS_ERR_OK(status)) {
3996 if (ads_count_replies(ads, res) != 1) {
3997 ads_msgfree(ads, res);
3998 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4001 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
4002 if ((*suffixes) == NULL) {
4003 ads_msgfree(ads, res);
4004 return ADS_ERROR(LDAP_NO_MEMORY);
4007 ads_msgfree(ads, res);
4013 * get the joinable ous for a domain
4014 * @param ads connection to ads server
4015 * @param mem_ctx Pointer to talloc context
4016 * @param ous Pointer to an array of ous
4017 * @param num_ous Pointer to the number of ous
4018 * @return status of search
4020 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
4021 TALLOC_CTX *mem_ctx,
4026 LDAPMessage *res = NULL;
4027 LDAPMessage *msg = NULL;
4028 const char *attrs[] = { "dn", NULL };
4031 status = ads_search(ads, &res,
4032 "(|(objectClass=domain)(objectclass=organizationalUnit))",
4034 if (!ADS_ERR_OK(status)) {
4038 count = ads_count_replies(ads, res);
4040 ads_msgfree(ads, res);
4041 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4044 for (msg = ads_first_entry(ads, res); msg;
4045 msg = ads_next_entry(ads, msg)) {
4046 const char **p = discard_const_p(const char *, *ous);
4049 dn = ads_get_dn(ads, talloc_tos(), msg);
4051 ads_msgfree(ads, res);
4052 return ADS_ERROR(LDAP_NO_MEMORY);
4055 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
4057 ads_msgfree(ads, res);
4058 return ADS_ERROR(LDAP_NO_MEMORY);
4062 *ous = discard_const_p(char *, p);
4065 ads_msgfree(ads, res);
4072 * pull a struct dom_sid from an extended dn string
4073 * @param mem_ctx TALLOC_CTX
4074 * @param extended_dn string
4075 * @param flags string type of extended_dn
4076 * @param sid pointer to a struct dom_sid
4077 * @return NT_STATUS_OK on success,
4078 * NT_INVALID_PARAMETER on error,
4079 * NT_STATUS_NOT_FOUND if no SID present
4081 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
4082 const char *extended_dn,
4083 enum ads_extended_dn_flags flags,
4084 struct dom_sid *sid)
4089 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4092 /* otherwise extended_dn gets stripped off */
4093 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
4094 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4097 * ADS_EXTENDED_DN_HEX_STRING:
4098 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
4100 * ADS_EXTENDED_DN_STRING (only with w2k3):
4101 * <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
4103 * Object with no SID, such as an Exchange Public Folder
4104 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
4107 p = strchr(dn, ';');
4109 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4112 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
4113 DEBUG(5,("No SID present in extended dn\n"));
4114 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
4117 p += strlen(";<SID=");
4121 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4126 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
4130 case ADS_EXTENDED_DN_STRING:
4131 if (!string_to_sid(sid, p)) {
4132 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4135 case ADS_EXTENDED_DN_HEX_STRING: {
4140 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
4142 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4145 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
4147 DEBUG(10,("failed to parse sid\n"));
4148 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4153 DEBUG(10,("unknown extended dn format\n"));
4154 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4157 return ADS_ERROR_NT(NT_STATUS_OK);
4160 /********************************************************************
4161 ********************************************************************/
4163 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4165 LDAPMessage *res = NULL;
4170 status = ads_find_machine_acct(ads, &res, machine_name);
4171 if (!ADS_ERR_OK(status)) {
4172 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
4173 lp_netbios_name()));
4177 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4178 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
4182 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
4183 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
4187 ads_msgfree(ads, res);
4192 /********************************************************************
4193 ********************************************************************/
4195 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
4196 LDAPMessage *msg, size_t *num_values)
4198 const char *field = "msDS-AdditionalDnsHostName";
4199 struct berval **values = NULL;
4201 size_t i, converted_size;
4204 * Windows DC implicitly adds a short name for each FQDN added to
4205 * msDS-AdditionalDnsHostName, but it comes with a strange binary
4206 * suffix "\0$" which we should ignore (see bug #14406).
4209 values = ldap_get_values_len(ads->ldap.ld, msg, field);
4210 if (values == NULL) {
4214 *num_values = ldap_count_values_len(values);
4216 ret = talloc_array(mem_ctx, char *, *num_values + 1);
4218 ldap_value_free_len(values);
4222 for (i = 0; i < *num_values; i++) {
4224 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
4226 strnlen(values[i]->bv_val,
4228 &ret[i], &converted_size)) {
4229 ldap_value_free_len(values);
4235 ldap_value_free_len(values);
4239 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
4241 const char *machine_name,
4242 char ***hostnames_array,
4243 size_t *num_hostnames)
4246 LDAPMessage *res = NULL;
4249 status = ads_find_machine_acct(ads,
4252 if (!ADS_ERR_OK(status)) {
4253 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
4258 count = ads_count_replies(ads, res);
4260 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4264 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
4265 if (*hostnames_array == NULL) {
4266 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
4268 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
4273 ads_msgfree(ads, res);
4278 /********************************************************************
4279 ********************************************************************/
4281 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4283 LDAPMessage *res = NULL;
4288 status = ads_find_machine_acct(ads, &res, machine_name);
4289 if (!ADS_ERR_OK(status)) {
4290 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
4291 lp_netbios_name()));
4295 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4296 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
4300 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
4301 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
4305 ads_msgfree(ads, res);
4310 /********************************************************************
4311 ********************************************************************/
4313 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
4315 LDAPMessage *res = NULL;
4321 status = ads_find_machine_acct(ads, &res, machine_name);
4322 if (!ADS_ERR_OK(status)) {
4323 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
4324 lp_netbios_name()));
4328 if ( (count = ads_count_replies(ads, res)) != 1 ) {
4329 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
4333 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
4334 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
4338 ads_msgfree(ads, res);
4340 ok = (strlen(name) > 0);
4348 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
4351 * Join a machine to a realm
4352 * Creates the machine account and sets the machine password
4353 * @param ads connection to ads server
4354 * @param machine name of host to add
4355 * @param org_unit Organizational unit to place machine in
4356 * @return status of join
4358 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
4359 uint32_t account_type, const char *org_unit)
4362 LDAPMessage *res = NULL;
4365 /* machine name must be lowercase */
4366 machine = SMB_STRDUP(machine_name);
4367 strlower_m(machine);
4370 status = ads_find_machine_acct(ads, (void **)&res, machine);
4371 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
4372 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
4373 status = ads_leave_realm(ads, machine);
4374 if (!ADS_ERR_OK(status)) {
4375 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
4376 machine, ads->config.realm));
4381 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
4382 if (!ADS_ERR_OK(status)) {
4383 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
4388 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
4389 if (!ADS_ERR_OK(status)) {
4390 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
4396 ads_msgfree(ads, res);
4403 * Delete a machine from the realm
4404 * @param ads connection to ads server
4405 * @param hostname Machine to remove
4406 * @return status of delete
4408 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4413 char *hostnameDN, *host;
4415 LDAPControl ldap_control;
4416 LDAPControl * pldap_control[2] = {NULL, NULL};
4418 pldap_control[0] = &ldap_control;
4419 memset(&ldap_control, 0, sizeof(LDAPControl));
4420 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4422 /* hostname must be lowercase */
4423 host = SMB_STRDUP(hostname);
4424 if (!strlower_m(host)) {
4426 return ADS_ERROR_SYSTEM(EINVAL);
4429 status = ads_find_machine_acct(ads, &res, host);
4430 if (!ADS_ERR_OK(status)) {
4431 DEBUG(0, ("Host account for %s does not exist.\n", host));
4436 msg = ads_first_entry(ads, res);
4439 return ADS_ERROR_SYSTEM(ENOENT);
4442 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4443 if (hostnameDN == NULL) {
4445 return ADS_ERROR_SYSTEM(ENOENT);
4448 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4450 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4452 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4455 if (rc != LDAP_SUCCESS) {
4456 const char *attrs[] = { "cn", NULL };
4457 LDAPMessage *msg_sub;
4459 /* we only search with scope ONE, we do not expect any further
4460 * objects to be created deeper */
4462 status = ads_do_search_retry(ads, hostnameDN,
4463 LDAP_SCOPE_ONELEVEL,
4464 "(objectclass=*)", attrs, &res);
4466 if (!ADS_ERR_OK(status)) {
4468 TALLOC_FREE(hostnameDN);
4472 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4473 msg_sub = ads_next_entry(ads, msg_sub)) {
4477 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4479 TALLOC_FREE(hostnameDN);
4480 return ADS_ERROR(LDAP_NO_MEMORY);
4483 status = ads_del_dn(ads, dn);
4484 if (!ADS_ERR_OK(status)) {
4485 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4488 TALLOC_FREE(hostnameDN);
4495 /* there should be no subordinate objects anymore */
4496 status = ads_do_search_retry(ads, hostnameDN,
4497 LDAP_SCOPE_ONELEVEL,
4498 "(objectclass=*)", attrs, &res);
4500 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4502 TALLOC_FREE(hostnameDN);
4506 /* delete hostnameDN now */
4507 status = ads_del_dn(ads, hostnameDN);
4508 if (!ADS_ERR_OK(status)) {
4510 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4511 TALLOC_FREE(hostnameDN);
4516 TALLOC_FREE(hostnameDN);
4518 status = ads_find_machine_acct(ads, &res, host);
4519 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4520 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4521 DEBUG(3, ("Failed to remove host account.\n"));
4531 * pull all token-sids from an LDAP dn
4532 * @param ads connection to ads server
4533 * @param mem_ctx TALLOC_CTX for allocating sid array
4534 * @param dn of LDAP object
4535 * @param user_sid pointer to struct dom_sid (objectSid)
4536 * @param primary_group_sid pointer to struct dom_sid (self composed)
4537 * @param sids pointer to sid array to allocate
4538 * @param num_sids counter of SIDs pulled
4539 * @return status of token query
4541 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4542 TALLOC_CTX *mem_ctx,
4544 struct dom_sid *user_sid,
4545 struct dom_sid *primary_group_sid,
4546 struct dom_sid **sids,
4550 LDAPMessage *res = NULL;
4552 size_t tmp_num_sids;
4553 struct dom_sid *tmp_sids;
4554 struct dom_sid tmp_user_sid;
4555 struct dom_sid tmp_primary_group_sid;
4557 const char *attrs[] = {
4564 status = ads_search_retry_dn(ads, &res, dn, attrs);
4565 if (!ADS_ERR_OK(status)) {
4569 count = ads_count_replies(ads, res);
4571 ads_msgfree(ads, res);
4572 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4575 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4576 ads_msgfree(ads, res);
4577 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4580 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4581 ads_msgfree(ads, res);
4582 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4586 /* hack to compose the primary group sid without knowing the
4589 struct dom_sid domsid;
4591 sid_copy(&domsid, &tmp_user_sid);
4593 if (!sid_split_rid(&domsid, NULL)) {
4594 ads_msgfree(ads, res);
4595 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4598 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4599 ads_msgfree(ads, res);
4600 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4604 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4606 if (tmp_num_sids == 0 || !tmp_sids) {
4607 ads_msgfree(ads, res);
4608 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4612 *num_sids = tmp_num_sids;
4620 *user_sid = tmp_user_sid;
4623 if (primary_group_sid) {
4624 *primary_group_sid = tmp_primary_group_sid;
4627 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4629 ads_msgfree(ads, res);
4630 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4634 * Find a sAMAccountName in LDAP
4635 * @param ads connection to ads server
4636 * @param mem_ctx TALLOC_CTX for allocating sid array
4637 * @param samaccountname to search
4638 * @param uac_ret uint32_t pointer userAccountControl attribute value
4639 * @param dn_ret pointer to dn
4640 * @return status of token query
4642 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4643 TALLOC_CTX *mem_ctx,
4644 const char *samaccountname,
4646 const char **dn_ret)
4649 const char *attrs[] = { "userAccountControl", NULL };
4651 LDAPMessage *res = NULL;
4655 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4657 if (filter == NULL) {
4658 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4662 status = ads_do_search_all(ads, ads->config.bind_path,
4664 filter, attrs, &res);
4666 if (!ADS_ERR_OK(status)) {
4670 if (ads_count_replies(ads, res) != 1) {
4671 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4675 dn = ads_get_dn(ads, talloc_tos(), res);
4677 status = ADS_ERROR(LDAP_NO_MEMORY);
4681 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4682 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4691 *dn_ret = talloc_strdup(mem_ctx, dn);
4693 status = ADS_ERROR(LDAP_NO_MEMORY);
4699 ads_msgfree(ads, res);
4705 * find our configuration path
4706 * @param ads connection to ads server
4707 * @param mem_ctx Pointer to talloc context
4708 * @param config_path Pointer to the config path
4709 * @return status of search
4711 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4712 TALLOC_CTX *mem_ctx,
4716 LDAPMessage *res = NULL;
4717 const char *config_context = NULL;
4718 const char *attrs[] = { "configurationNamingContext", NULL };
4720 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4721 "(objectclass=*)", attrs, &res);
4722 if (!ADS_ERR_OK(status)) {
4726 config_context = ads_pull_string(ads, mem_ctx, res,
4727 "configurationNamingContext");
4728 ads_msgfree(ads, res);
4729 if (!config_context) {
4730 return ADS_ERROR(LDAP_NO_MEMORY);
4734 *config_path = talloc_strdup(mem_ctx, config_context);
4735 if (!*config_path) {
4736 return ADS_ERROR(LDAP_NO_MEMORY);
4740 return ADS_ERROR(LDAP_SUCCESS);
4744 * find the displayName of an extended right
4745 * @param ads connection to ads server
4746 * @param config_path The config path
4747 * @param mem_ctx Pointer to talloc context
4748 * @param GUID struct of the rightsGUID
4749 * @return status of search
4751 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4752 const char *config_path,
4753 TALLOC_CTX *mem_ctx,
4754 const struct GUID *rights_guid)
4757 LDAPMessage *res = NULL;
4759 const char *attrs[] = { "displayName", NULL };
4760 const char *result = NULL;
4763 if (!ads || !mem_ctx || !rights_guid) {
4767 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4768 GUID_string(mem_ctx, rights_guid));
4773 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4778 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4780 if (!ADS_ERR_OK(rc)) {
4784 if (ads_count_replies(ads, res) != 1) {
4788 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4791 ads_msgfree(ads, res);
4796 * verify or build and verify an account ou
4797 * @param mem_ctx Pointer to talloc context
4798 * @param ads connection to ads server
4800 * @return status of search
4803 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4805 const char **account_ou)
4811 if (account_ou == NULL) {
4812 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4815 if (*account_ou != NULL) {
4816 exploded_dn = ldap_explode_dn(*account_ou, 0);
4818 ldap_value_free(exploded_dn);
4823 ou_string = ads_ou_string(ads, *account_ou);
4825 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4828 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4829 ads->config.bind_path);
4830 SAFE_FREE(ou_string);
4833 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4836 exploded_dn = ldap_explode_dn(name, 0);
4838 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4840 ldap_value_free(exploded_dn);