CVE-2020-25719 kdc: Avoid races and multiple DB lookups in s4u2self check
[Samba.git] / source3 / libads / ldap.c
blob1bc271785e2a258021f37d2175e4407b42aeb1e8
1 /*
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/>.
24 #include "includes.h"
25 #include "ads.h"
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/addns/dnsquery.h"
29 #include "../libds/common/flags.h"
30 #include "smbldap.h"
31 #include "../libcli/security/security.h"
32 #include "../librpc/gen_ndr/netlogon.h"
33 #include "lib/param/loadparm.h"
34 #include "libsmb/namequery.h"
36 #ifdef HAVE_LDAP
38 /**
39 * @file ldap.c
40 * @brief basic ldap client-side routines for ads server communications
42 * The routines contained here should do the necessary ldap calls for
43 * ads setups.
45 * Important note: attribute names passed into ads_ routines must
46 * already be in UTF-8 format. We do not convert them because in almost
47 * all cases, they are just ascii (which is represented with the same
48 * codepoints in UTF-8). This may have to change at some point
49 **/
52 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
54 static SIG_ATOMIC_T gotalarm;
56 /***************************************************************
57 Signal function to tell us we timed out.
58 ****************************************************************/
60 static void gotalarm_sig(int signum)
62 gotalarm = 1;
65 LDAP *ldap_open_with_timeout(const char *server,
66 struct sockaddr_storage *ss,
67 int port, unsigned int to)
69 LDAP *ldp = NULL;
70 int ldap_err;
71 char *uri;
73 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
74 "%u seconds\n", server, port, to));
76 if (to) {
77 /* Setup timeout */
78 gotalarm = 0;
79 CatchSignal(SIGALRM, gotalarm_sig);
80 alarm(to);
81 /* End setup timeout. */
84 if ( strchr_m(server, ':') ) {
85 /* IPv6 URI */
86 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
87 } else {
88 /* IPv4 URI */
89 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
91 if (uri == NULL) {
92 return NULL;
95 #ifdef HAVE_LDAP_INIT_FD
97 int fd = -1;
98 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
99 unsigned timeout_ms = 1000 * to;
101 status = open_socket_out(ss, port, timeout_ms, &fd);
102 if (!NT_STATUS_IS_OK(status)) {
103 DEBUG(3, ("open_socket_out: failed to open socket\n"));
104 return NULL;
107 /* define LDAP_PROTO_TCP from openldap.h if required */
108 #ifndef LDAP_PROTO_TCP
109 #define LDAP_PROTO_TCP 1
110 #endif
111 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
113 #elif defined(HAVE_LDAP_INITIALIZE)
114 ldap_err = ldap_initialize(&ldp, uri);
115 #else
116 ldp = ldap_open(server, port);
117 if (ldp != NULL) {
118 ldap_err = LDAP_SUCCESS;
119 } else {
120 ldap_err = LDAP_OTHER;
122 #endif
123 if (ldap_err != LDAP_SUCCESS) {
124 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
125 uri, ldap_err2string(ldap_err)));
126 } else {
127 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
130 if (to) {
131 /* Teardown timeout. */
132 alarm(0);
133 CatchSignal(SIGALRM, SIG_IGN);
136 return ldp;
139 static int ldap_search_with_timeout(LDAP *ld,
140 LDAP_CONST char *base,
141 int scope,
142 LDAP_CONST char *filter,
143 char **attrs,
144 int attrsonly,
145 LDAPControl **sctrls,
146 LDAPControl **cctrls,
147 int sizelimit,
148 LDAPMessage **res )
150 int to = lp_ldap_timeout();
151 struct timeval timeout;
152 struct timeval *timeout_ptr = NULL;
153 int result;
155 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
156 gotalarm = 0;
158 if (to) {
159 timeout.tv_sec = to;
160 timeout.tv_usec = 0;
161 timeout_ptr = &timeout;
163 /* Setup alarm timeout. */
164 CatchSignal(SIGALRM, gotalarm_sig);
165 /* Make the alarm time one second beyond
166 the timout we're setting for the
167 remote search timeout, to allow that
168 to fire in preference. */
169 alarm(to+1);
170 /* End setup timeout. */
174 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
175 attrsonly, sctrls, cctrls, timeout_ptr,
176 sizelimit, res);
178 if (to) {
179 /* Teardown alarm timeout. */
180 CatchSignal(SIGALRM, SIG_IGN);
181 alarm(0);
184 if (gotalarm != 0)
185 return LDAP_TIMELIMIT_EXCEEDED;
188 * A bug in OpenLDAP means ldap_search_ext_s can return
189 * LDAP_SUCCESS but with a NULL res pointer. Cope with
190 * this. See bug #6279 for details. JRA.
193 if (*res == NULL) {
194 return LDAP_TIMELIMIT_EXCEEDED;
197 return result;
200 /**********************************************
201 Do client and server sitename match ?
202 **********************************************/
204 bool ads_sitename_match(ADS_STRUCT *ads)
206 if (ads->config.server_site_name == NULL &&
207 ads->config.client_site_name == NULL ) {
208 DEBUG(10,("ads_sitename_match: both null\n"));
209 return True;
211 if (ads->config.server_site_name &&
212 ads->config.client_site_name &&
213 strequal(ads->config.server_site_name,
214 ads->config.client_site_name)) {
215 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
216 return True;
218 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
219 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
220 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
221 return False;
224 /**********************************************
225 Is this the closest DC ?
226 **********************************************/
228 bool ads_closest_dc(ADS_STRUCT *ads)
230 if (ads->config.flags & NBT_SERVER_CLOSEST) {
231 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
232 return True;
235 /* not sure if this can ever happen */
236 if (ads_sitename_match(ads)) {
237 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
238 return True;
241 if (ads->config.client_site_name == NULL) {
242 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
243 return True;
246 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
247 ads->config.ldap_server_name));
249 return False;
254 try a connection to a given ldap server, returning True and setting the servers IP
255 in the ads struct if successful
257 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
258 struct sockaddr_storage *ss)
260 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
261 TALLOC_CTX *frame = talloc_stackframe();
262 bool ret = false;
263 char addr[INET6_ADDRSTRLEN];
265 if (ss == NULL) {
266 TALLOC_FREE(frame);
267 return False;
270 print_sockaddr(addr, sizeof(addr), ss);
272 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
273 addr, ads->server.realm));
275 ZERO_STRUCT( cldap_reply );
277 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
278 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
279 ret = false;
280 goto out;
283 /* Check the CLDAP reply flags */
285 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
286 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
287 addr));
288 ret = false;
289 goto out;
292 /* Fill in the ads->config values */
294 SAFE_FREE(ads->config.realm);
295 SAFE_FREE(ads->config.bind_path);
296 SAFE_FREE(ads->config.ldap_server_name);
297 SAFE_FREE(ads->config.server_site_name);
298 SAFE_FREE(ads->config.client_site_name);
299 SAFE_FREE(ads->server.workgroup);
301 if (!check_cldap_reply_required_flags(cldap_reply.server_type,
302 ads->config.flags)) {
303 ret = false;
304 goto out;
307 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
308 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
309 if (!strupper_m(ads->config.realm)) {
310 ret = false;
311 goto out;
314 ads->config.bind_path = ads_build_dn(ads->config.realm);
315 if (*cldap_reply.server_site) {
316 ads->config.server_site_name =
317 SMB_STRDUP(cldap_reply.server_site);
319 if (*cldap_reply.client_site) {
320 ads->config.client_site_name =
321 SMB_STRDUP(cldap_reply.client_site);
323 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
325 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
326 ads->ldap.ss = *ss;
328 /* Store our site name. */
329 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
330 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
332 /* Leave this until last so that the flags are not clobbered */
333 ads->config.flags = cldap_reply.server_type;
335 ret = true;
337 out:
339 TALLOC_FREE(frame);
340 return ret;
343 /**********************************************************************
344 send a cldap ping to list of servers, one at a time, until one of
345 them answers it's an ldap server. Record success in the ADS_STRUCT.
346 Take note of and update negative connection cache.
347 **********************************************************************/
349 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,
350 const char *domain,
351 struct samba_sockaddr *sa_list,
352 size_t count)
354 size_t i;
355 bool ok;
357 for (i = 0; i < count; i++) {
358 char server[INET6_ADDRSTRLEN];
360 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
362 if (!NT_STATUS_IS_OK(
363 check_negative_conn_cache(domain, server)))
364 continue;
366 /* Returns ok only if it matches the correct server type */
367 ok = ads_try_connect(ads, false, &sa_list[i].u.ss);
369 if (ok) {
370 return NT_STATUS_OK;
373 /* keep track of failures */
374 add_failed_connection_entry(domain, server,
375 NT_STATUS_UNSUCCESSFUL);
378 return NT_STATUS_NO_LOGON_SERVERS;
381 /***************************************************************************
382 resolve a name and perform an "ldap ping" using NetBIOS and related methods
383 ****************************************************************************/
385 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
386 const char *domain, const char *realm)
388 size_t i;
389 size_t count = 0;
390 struct samba_sockaddr *sa_list = NULL;
391 NTSTATUS status;
393 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
394 domain));
396 status = get_sorted_dc_list(talloc_tos(),
397 domain,
398 NULL,
399 &sa_list,
400 &count,
401 false);
402 if (!NT_STATUS_IS_OK(status)) {
403 return status;
406 /* remove servers which are known to be dead based on
407 the corresponding DNS method */
408 if (*realm) {
409 for (i = 0; i < count; ++i) {
410 char server[INET6_ADDRSTRLEN];
412 print_sockaddr(server, sizeof(server), &sa_list[i].u.ss);
414 if(!NT_STATUS_IS_OK(
415 check_negative_conn_cache(realm, server))) {
416 /* Ensure we add the workgroup name for this
417 IP address as negative too. */
418 add_failed_connection_entry(
419 domain, server,
420 NT_STATUS_UNSUCCESSFUL);
425 status = cldap_ping_list(ads, domain, sa_list, count);
427 TALLOC_FREE(sa_list);
429 return status;
433 /**********************************************************************
434 resolve a name and perform an "ldap ping" using DNS
435 **********************************************************************/
437 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
438 const char *realm)
440 size_t count = 0;
441 struct samba_sockaddr *sa_list = NULL;
442 NTSTATUS status;
444 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
445 realm));
447 status = get_sorted_dc_list(talloc_tos(),
448 realm,
449 sitename,
450 &sa_list,
451 &count,
452 true);
453 if (!NT_STATUS_IS_OK(status)) {
454 TALLOC_FREE(sa_list);
455 return status;
458 status = cldap_ping_list(ads, realm, sa_list, count);
460 TALLOC_FREE(sa_list);
462 return status;
465 /**********************************************************************
466 Try to find an AD dc using our internal name resolution routines
467 Try the realm first and then then workgroup name if netbios is not
468 disabled
469 **********************************************************************/
471 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
473 const char *c_domain = "";
474 const char *c_realm;
475 bool use_own_domain = False;
476 char *sitename = NULL;
477 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
478 bool ok = false;
480 /* if the realm and workgroup are both empty, assume they are ours */
482 /* realm */
483 c_realm = ads->server.realm;
485 if (c_realm == NULL)
486 c_realm = "";
488 if (!*c_realm) {
489 /* special case where no realm and no workgroup means our own */
490 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
491 use_own_domain = True;
492 c_realm = lp_realm();
496 if (!lp_disable_netbios()) {
497 if (use_own_domain) {
498 c_domain = lp_workgroup();
499 } else {
500 c_domain = ads->server.workgroup;
501 if (!*c_realm && (!c_domain || !*c_domain)) {
502 c_domain = lp_workgroup();
506 if (!c_domain) {
507 c_domain = "";
511 if (!*c_realm && !*c_domain) {
512 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
513 "what to do\n"));
514 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
518 * In case of LDAP we use get_dc_name() as that
519 * creates the custom krb5.conf file
521 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
522 fstring srv_name;
523 struct sockaddr_storage ip_out;
525 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
526 " and falling back to domain '%s'\n",
527 c_realm, c_domain));
529 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
530 if (ok) {
532 * we call ads_try_connect() to fill in the
533 * ads->config details
535 ok = ads_try_connect(ads, false, &ip_out);
536 if (ok) {
537 return NT_STATUS_OK;
541 return NT_STATUS_NO_LOGON_SERVERS;
544 if (*c_realm) {
545 sitename = sitename_fetch(talloc_tos(), c_realm);
546 status = resolve_and_ping_dns(ads, sitename, c_realm);
548 if (NT_STATUS_IS_OK(status)) {
549 TALLOC_FREE(sitename);
550 return status;
553 /* In case we failed to contact one of our closest DC on our
554 * site we
555 * need to try to find another DC, retry with a site-less SRV
556 * DNS query
557 * - Guenther */
559 if (sitename) {
560 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
561 "our site (%s), Trying to find another DC "
562 "for realm '%s' (domain '%s')\n",
563 sitename, c_realm, c_domain));
564 namecache_delete(c_realm, 0x1C);
565 status =
566 resolve_and_ping_dns(ads, NULL, c_realm);
568 if (NT_STATUS_IS_OK(status)) {
569 TALLOC_FREE(sitename);
570 return status;
574 TALLOC_FREE(sitename);
577 /* try netbios as fallback - if permitted,
578 or if configuration specifically requests it */
579 if (*c_domain) {
580 if (*c_realm) {
581 DEBUG(3, ("ads_find_dc: falling back to netbios "
582 "name resolution for domain '%s' (realm '%s')\n",
583 c_domain, c_realm));
586 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
587 if (NT_STATUS_IS_OK(status)) {
588 return status;
592 DEBUG(1, ("ads_find_dc: "
593 "name resolution for realm '%s' (domain '%s') failed: %s\n",
594 c_realm, c_domain, nt_errstr(status)));
595 return status;
598 * Connect to the LDAP server
599 * @param ads Pointer to an existing ADS_STRUCT
600 * @return status of connection
602 ADS_STATUS ads_connect(ADS_STRUCT *ads)
604 int version = LDAP_VERSION3;
605 ADS_STATUS status;
606 NTSTATUS ntstatus;
607 char addr[INET6_ADDRSTRLEN];
608 struct samba_sockaddr existing_sa = {0};
611 * ads_connect can be passed in a reused ADS_STRUCT
612 * with an existing non-zero ads->ldap.ss IP address
613 * that was stored by going through ads_find_dc()
614 * if ads->server.ldap_server was NULL.
616 * If ads->server.ldap_server is still NULL but
617 * the target address isn't the zero address, then
618 * store that address off off before zeroing out
619 * ads->ldap so we don't keep doing multiple calls
620 * to ads_find_dc() in the reuse case.
622 * If a caller wants a clean ADS_STRUCT they
623 * will re-initialize by calling ads_init(), or
624 * call ads_destroy() both of which ensures
625 * ads->ldap.ss is a properly zero'ed out valid IP
626 * address.
628 if (ads->server.ldap_server == NULL && !is_zero_addr(&ads->ldap.ss)) {
629 /* Save off the address we previously found by ads_find_dc(). */
630 bool ok = sockaddr_storage_to_samba_sockaddr(&existing_sa,
631 &ads->ldap.ss);
632 if (!ok) {
633 return ADS_ERROR_NT(NT_STATUS_INVALID_ADDRESS);
637 ads_zero_ldap(ads);
638 ZERO_STRUCT(ads->ldap_wrap_data);
639 ads->ldap.last_attempt = time_mono(NULL);
640 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
642 /* try with a user specified server */
644 if (DEBUGLEVEL >= 11) {
645 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
646 DEBUG(11,("ads_connect: entering\n"));
647 DEBUGADD(11,("%s\n", s));
648 TALLOC_FREE(s);
651 if (ads->server.ldap_server) {
652 bool ok = false;
653 struct sockaddr_storage ss;
655 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
656 if (!ok) {
657 DEBUG(5,("ads_connect: unable to resolve name %s\n",
658 ads->server.ldap_server));
659 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
660 goto out;
662 ok = ads_try_connect(ads, ads->server.gc, &ss);
663 if (ok) {
664 goto got_connection;
667 /* The choice of which GC use is handled one level up in
668 ads_connect_gc(). If we continue on from here with
669 ads_find_dc() we will get GC searches on port 389 which
670 doesn't work. --jerry */
672 if (ads->server.gc == true) {
673 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
676 if (ads->server.no_fallback) {
677 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
678 goto out;
682 if (!is_zero_addr(&existing_sa.u.ss)) {
683 /* We saved off who we should talk to. */
684 bool ok = ads_try_connect(ads,
685 ads->server.gc,
686 &existing_sa.u.ss);
687 if (ok) {
688 goto got_connection;
691 * Keep trying to find a server and fall through
692 * into ads_find_dc() again.
696 ntstatus = ads_find_dc(ads);
697 if (NT_STATUS_IS_OK(ntstatus)) {
698 goto got_connection;
701 status = ADS_ERROR_NT(ntstatus);
702 goto out;
704 got_connection:
706 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
707 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
709 if (!ads->auth.user_name) {
710 /* Must use the userPrincipalName value here or sAMAccountName
711 and not servicePrincipalName; found by Guenther Deschner */
713 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
714 DEBUG(0,("ads_connect: asprintf fail.\n"));
715 ads->auth.user_name = NULL;
719 if (!ads->auth.realm) {
720 ads->auth.realm = SMB_STRDUP(ads->config.realm);
723 if (!ads->auth.kdc_server) {
724 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
725 ads->auth.kdc_server = SMB_STRDUP(addr);
728 /* If the caller() requested no LDAP bind, then we are done */
730 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
731 status = ADS_SUCCESS;
732 goto out;
735 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
736 if (!ads->ldap_wrap_data.mem_ctx) {
737 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
738 goto out;
741 /* Otherwise setup the TCP LDAP session */
743 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
744 &ads->ldap.ss,
745 ads->ldap.port, lp_ldap_timeout());
746 if (ads->ldap.ld == NULL) {
747 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
748 goto out;
750 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
752 /* cache the successful connection for workgroup and realm */
753 if (ads_closest_dc(ads)) {
754 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
755 saf_store( ads->server.realm, ads->config.ldap_server_name);
758 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
760 /* fill in the current time and offsets */
762 status = ads_current_time( ads );
763 if ( !ADS_ERR_OK(status) ) {
764 goto out;
767 /* Now do the bind */
769 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
770 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
771 goto out;
774 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
775 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
776 goto out;
779 status = ads_sasl_bind(ads);
781 out:
782 if (DEBUGLEVEL >= 11) {
783 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
784 DEBUG(11,("ads_connect: leaving with: %s\n",
785 ads_errstr(status)));
786 DEBUGADD(11,("%s\n", s));
787 TALLOC_FREE(s);
790 return status;
794 * Connect to the LDAP server using given credentials
795 * @param ads Pointer to an existing ADS_STRUCT
796 * @return status of connection
798 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
800 ads->auth.flags |= ADS_AUTH_USER_CREDS;
802 return ads_connect(ads);
806 * Zero out the internal ads->ldap struct and initialize the address to zero IP.
807 * @param ads Pointer to an existing ADS_STRUCT
809 * Sets the ads->ldap.ss to a valid
810 * zero ip address that can be detected by
811 * our is_zero_addr() function. Otherwise
812 * it is left as AF_UNSPEC (0).
814 void ads_zero_ldap(ADS_STRUCT *ads)
816 ZERO_STRUCT(ads->ldap);
818 * Initialize the sockaddr_storage so we can use
819 * sockaddr test functions against it.
821 zero_sockaddr(&ads->ldap.ss);
825 * Disconnect the LDAP server
826 * @param ads Pointer to an existing ADS_STRUCT
828 void ads_disconnect(ADS_STRUCT *ads)
830 if (ads->ldap.ld) {
831 ldap_unbind(ads->ldap.ld);
832 ads->ldap.ld = NULL;
834 if (ads->ldap_wrap_data.wrap_ops &&
835 ads->ldap_wrap_data.wrap_ops->disconnect) {
836 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
838 if (ads->ldap_wrap_data.mem_ctx) {
839 talloc_free(ads->ldap_wrap_data.mem_ctx);
841 ads_zero_ldap(ads);
842 ZERO_STRUCT(ads->ldap_wrap_data);
846 Duplicate a struct berval into talloc'ed memory
848 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
850 struct berval *value;
852 if (!in_val) return NULL;
854 value = talloc_zero(ctx, struct berval);
855 if (value == NULL)
856 return NULL;
857 if (in_val->bv_len == 0) return value;
859 value->bv_len = in_val->bv_len;
860 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
861 in_val->bv_len);
862 return value;
866 Make a values list out of an array of (struct berval *)
868 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
869 const struct berval **in_vals)
871 struct berval **values;
872 int i;
874 if (!in_vals) return NULL;
875 for (i=0; in_vals[i]; i++)
876 ; /* count values */
877 values = talloc_zero_array(ctx, struct berval *, i+1);
878 if (!values) return NULL;
880 for (i=0; in_vals[i]; i++) {
881 values[i] = dup_berval(ctx, in_vals[i]);
883 return values;
887 UTF8-encode a values list out of an array of (char *)
889 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
891 char **values;
892 int i;
893 size_t size;
895 if (!in_vals) return NULL;
896 for (i=0; in_vals[i]; i++)
897 ; /* count values */
898 values = talloc_zero_array(ctx, char *, i+1);
899 if (!values) return NULL;
901 for (i=0; in_vals[i]; i++) {
902 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
903 TALLOC_FREE(values);
904 return NULL;
907 return values;
911 Pull a (char *) array out of a UTF8-encoded values list
913 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
915 char **values;
916 int i;
917 size_t converted_size;
919 if (!in_vals) return NULL;
920 for (i=0; in_vals[i]; i++)
921 ; /* count values */
922 values = talloc_zero_array(ctx, char *, i+1);
923 if (!values) return NULL;
925 for (i=0; in_vals[i]; i++) {
926 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
927 &converted_size)) {
928 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
929 "%s", strerror(errno)));
932 return values;
936 * Do a search with paged results. cookie must be null on the first
937 * call, and then returned on each subsequent call. It will be null
938 * again when the entire search is complete
939 * @param ads connection to ads server
940 * @param bind_path Base dn for the search
941 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
942 * @param expr Search expression - specified in local charset
943 * @param attrs Attributes to retrieve - specified in utf8 or ascii
944 * @param res ** which will contain results - free res* with ads_msgfree()
945 * @param count Number of entries retrieved on this page
946 * @param cookie The paged results cookie to be returned on subsequent calls
947 * @return status of search
949 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
950 const char *bind_path,
951 int scope, const char *expr,
952 const char **attrs, void *args,
953 LDAPMessage **res,
954 int *count, struct berval **cookie)
956 int rc, i, version;
957 char *utf8_expr, *utf8_path, **search_attrs = NULL;
958 size_t converted_size;
959 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
960 BerElement *cookie_be = NULL;
961 struct berval *cookie_bv= NULL;
962 BerElement *ext_be = NULL;
963 struct berval *ext_bv= NULL;
965 TALLOC_CTX *ctx;
966 ads_control *external_control = (ads_control *) args;
968 *res = NULL;
970 if (!(ctx = talloc_init("ads_do_paged_search_args")))
971 return ADS_ERROR(LDAP_NO_MEMORY);
973 /* 0 means the conversion worked but the result was empty
974 so we only fail if it's -1. In any case, it always
975 at least nulls out the dest */
976 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
977 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
979 rc = LDAP_NO_MEMORY;
980 goto done;
983 if (!attrs || !(*attrs))
984 search_attrs = NULL;
985 else {
986 /* This would be the utf8-encoded version...*/
987 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
988 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
989 rc = LDAP_NO_MEMORY;
990 goto done;
994 /* Paged results only available on ldap v3 or later */
995 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
996 if (version < LDAP_VERSION3) {
997 rc = LDAP_NOT_SUPPORTED;
998 goto done;
1001 cookie_be = ber_alloc_t(LBER_USE_DER);
1002 if (*cookie) {
1003 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
1004 ber_bvfree(*cookie); /* don't need it from last time */
1005 *cookie = NULL;
1006 } else {
1007 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
1009 ber_flatten(cookie_be, &cookie_bv);
1010 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
1011 PagedResults.ldctl_iscritical = (char) 1;
1012 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
1013 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
1015 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
1016 NoReferrals.ldctl_iscritical = (char) 0;
1017 NoReferrals.ldctl_value.bv_len = 0;
1018 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
1020 if (external_control &&
1021 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
1022 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
1024 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
1025 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
1027 /* win2k does not accept a ldctl_value beeing passed in */
1029 if (external_control->val != 0) {
1031 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
1032 rc = LDAP_NO_MEMORY;
1033 goto done;
1036 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
1037 rc = LDAP_NO_MEMORY;
1038 goto done;
1040 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1041 rc = LDAP_NO_MEMORY;
1042 goto done;
1045 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1046 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1048 } else {
1049 ExternalCtrl.ldctl_value.bv_len = 0;
1050 ExternalCtrl.ldctl_value.bv_val = NULL;
1053 controls[0] = &NoReferrals;
1054 controls[1] = &PagedResults;
1055 controls[2] = &ExternalCtrl;
1056 controls[3] = NULL;
1058 } else {
1059 controls[0] = &NoReferrals;
1060 controls[1] = &PagedResults;
1061 controls[2] = NULL;
1064 /* we need to disable referrals as the openldap libs don't
1065 handle them and paged results at the same time. Using them
1066 together results in the result record containing the server
1067 page control being removed from the result list (tridge/jmcd)
1069 leaving this in despite the control that says don't generate
1070 referrals, in case the server doesn't support it (jmcd)
1072 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1074 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1075 search_attrs, 0, controls,
1076 NULL, LDAP_NO_LIMIT,
1077 (LDAPMessage **)res);
1079 ber_free(cookie_be, 1);
1080 ber_bvfree(cookie_bv);
1082 if (rc) {
1083 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1084 ldap_err2string(rc)));
1085 if (rc == LDAP_OTHER) {
1086 char *ldap_errmsg;
1087 int ret;
1089 ret = ldap_parse_result(ads->ldap.ld,
1090 *res,
1091 NULL,
1092 NULL,
1093 &ldap_errmsg,
1094 NULL,
1095 NULL,
1097 if (ret == LDAP_SUCCESS) {
1098 DEBUG(3, ("ldap_search_with_timeout(%s) "
1099 "error: %s\n", expr, ldap_errmsg));
1100 ldap_memfree(ldap_errmsg);
1103 goto done;
1106 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1107 NULL, &rcontrols, 0);
1109 if (!rcontrols) {
1110 goto done;
1113 for (i=0; rcontrols[i]; i++) {
1114 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1115 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1116 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1117 &cookie_bv);
1118 /* the berval is the cookie, but must be freed when
1119 it is all done */
1120 if (cookie_bv->bv_len) /* still more to do */
1121 *cookie=ber_bvdup(cookie_bv);
1122 else
1123 *cookie=NULL;
1124 ber_bvfree(cookie_bv);
1125 ber_free(cookie_be, 1);
1126 break;
1129 ldap_controls_free(rcontrols);
1131 done:
1132 talloc_destroy(ctx);
1134 if (ext_be) {
1135 ber_free(ext_be, 1);
1138 if (ext_bv) {
1139 ber_bvfree(ext_bv);
1142 if (rc != LDAP_SUCCESS && *res != NULL) {
1143 ads_msgfree(ads, *res);
1144 *res = NULL;
1147 /* if/when we decide to utf8-encode attrs, take out this next line */
1148 TALLOC_FREE(search_attrs);
1150 return ADS_ERROR(rc);
1153 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1154 int scope, const char *expr,
1155 const char **attrs, LDAPMessage **res,
1156 int *count, struct berval **cookie)
1158 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1163 * Get all results for a search. This uses ads_do_paged_search() to return
1164 * all entries in a large search.
1165 * @param ads connection to ads server
1166 * @param bind_path Base dn for the search
1167 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1168 * @param expr Search expression
1169 * @param attrs Attributes to retrieve
1170 * @param res ** which will contain results - free res* with ads_msgfree()
1171 * @return status of search
1173 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1174 int scope, const char *expr,
1175 const char **attrs, void *args,
1176 LDAPMessage **res)
1178 struct berval *cookie = NULL;
1179 int count = 0;
1180 ADS_STATUS status;
1182 *res = NULL;
1183 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1184 &count, &cookie);
1186 if (!ADS_ERR_OK(status))
1187 return status;
1189 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1190 while (cookie) {
1191 LDAPMessage *res2 = NULL;
1192 LDAPMessage *msg, *next;
1194 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1195 attrs, args, &res2, &count, &cookie);
1196 if (!ADS_ERR_OK(status)) {
1197 break;
1200 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1201 that this works on all ldap libs, but I have only tested with openldap */
1202 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1203 next = ads_next_message(ads, msg);
1204 ldap_add_result_entry((LDAPMessage **)res, msg);
1206 /* note that we do not free res2, as the memory is now
1207 part of the main returned list */
1209 #else
1210 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1211 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1212 #endif
1214 return status;
1217 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1218 int scope, const char *expr,
1219 const char **attrs, LDAPMessage **res)
1221 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1224 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1225 int scope, const char *expr,
1226 const char **attrs, uint32_t sd_flags,
1227 LDAPMessage **res)
1229 ads_control args;
1231 args.control = ADS_SD_FLAGS_OID;
1232 args.val = sd_flags;
1233 args.critical = True;
1235 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1240 * Run a function on all results for a search. Uses ads_do_paged_search() and
1241 * runs the function as each page is returned, using ads_process_results()
1242 * @param ads connection to ads server
1243 * @param bind_path Base dn for the search
1244 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1245 * @param expr Search expression - specified in local charset
1246 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1247 * @param fn Function which takes attr name, values list, and data_area
1248 * @param data_area Pointer which is passed to function on each call
1249 * @return status of search
1251 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1252 int scope, const char *expr, const char **attrs,
1253 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1254 void *data_area)
1256 struct berval *cookie = NULL;
1257 int count = 0;
1258 ADS_STATUS status;
1259 LDAPMessage *res;
1261 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1262 &count, &cookie);
1264 if (!ADS_ERR_OK(status)) return status;
1266 ads_process_results(ads, res, fn, data_area);
1267 ads_msgfree(ads, res);
1269 while (cookie) {
1270 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1271 &res, &count, &cookie);
1273 if (!ADS_ERR_OK(status)) break;
1275 ads_process_results(ads, res, fn, data_area);
1276 ads_msgfree(ads, res);
1279 return status;
1283 * Do a search with a timeout.
1284 * @param ads connection to ads server
1285 * @param bind_path Base dn for the search
1286 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1287 * @param expr Search expression
1288 * @param attrs Attributes to retrieve
1289 * @param res ** which will contain results - free res* with ads_msgfree()
1290 * @return status of search
1292 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1293 const char *expr,
1294 const char **attrs, LDAPMessage **res)
1296 int rc;
1297 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1298 size_t converted_size;
1299 TALLOC_CTX *ctx;
1301 *res = NULL;
1302 if (!(ctx = talloc_init("ads_do_search"))) {
1303 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1304 return ADS_ERROR(LDAP_NO_MEMORY);
1307 /* 0 means the conversion worked but the result was empty
1308 so we only fail if it's negative. In any case, it always
1309 at least nulls out the dest */
1310 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1311 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1313 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1314 rc = LDAP_NO_MEMORY;
1315 goto done;
1318 if (!attrs || !(*attrs))
1319 search_attrs = NULL;
1320 else {
1321 /* This would be the utf8-encoded version...*/
1322 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1323 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1325 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1326 rc = LDAP_NO_MEMORY;
1327 goto done;
1331 /* see the note in ads_do_paged_search - we *must* disable referrals */
1332 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1334 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1335 search_attrs, 0, NULL, NULL,
1336 LDAP_NO_LIMIT,
1337 (LDAPMessage **)res);
1339 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1340 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1341 rc = 0;
1344 done:
1345 talloc_destroy(ctx);
1346 /* if/when we decide to utf8-encode attrs, take out this next line */
1347 TALLOC_FREE(search_attrs);
1348 return ADS_ERROR(rc);
1351 * Do a general ADS search
1352 * @param ads connection to ads server
1353 * @param res ** which will contain results - free res* with ads_msgfree()
1354 * @param expr Search expression
1355 * @param attrs Attributes to retrieve
1356 * @return status of search
1358 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1359 const char *expr, const char **attrs)
1361 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1362 expr, attrs, res);
1366 * Do a search on a specific DistinguishedName
1367 * @param ads connection to ads server
1368 * @param res ** which will contain results - free res* with ads_msgfree()
1369 * @param dn DistinguishName to search
1370 * @param attrs Attributes to retrieve
1371 * @return status of search
1373 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1374 const char *dn, const char **attrs)
1376 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1377 attrs, res);
1381 * Free up memory from a ads_search
1382 * @param ads connection to ads server
1383 * @param msg Search results to free
1385 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1387 if (!msg) return;
1388 ldap_msgfree(msg);
1392 * Get a dn from search results
1393 * @param ads connection to ads server
1394 * @param msg Search result
1395 * @return dn string
1397 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1399 char *utf8_dn, *unix_dn;
1400 size_t converted_size;
1402 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1404 if (!utf8_dn) {
1405 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1406 return NULL;
1409 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1410 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1411 utf8_dn ));
1412 return NULL;
1414 ldap_memfree(utf8_dn);
1415 return unix_dn;
1419 * Get the parent from a dn
1420 * @param dn the dn to return the parent from
1421 * @return parent dn string
1423 char *ads_parent_dn(const char *dn)
1425 char *p;
1427 if (dn == NULL) {
1428 return NULL;
1431 p = strchr(dn, ',');
1433 if (p == NULL) {
1434 return NULL;
1437 return p+1;
1441 * Find a machine account given a hostname
1442 * @param ads connection to ads server
1443 * @param res ** which will contain results - free res* with ads_msgfree()
1444 * @param host Hostname to search for
1445 * @return status of search
1447 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1448 const char *machine)
1450 ADS_STATUS status;
1451 char *expr;
1452 const char *attrs[] = {
1453 /* This is how Windows checks for machine accounts */
1454 "objectClass",
1455 "SamAccountName",
1456 "userAccountControl",
1457 "DnsHostName",
1458 "ServicePrincipalName",
1459 "userPrincipalName",
1460 "unicodePwd",
1462 /* Additional attributes Samba checks */
1463 "msDS-AdditionalDnsHostName",
1464 "msDS-SupportedEncryptionTypes",
1465 "nTSecurityDescriptor",
1466 "objectSid",
1468 NULL
1470 TALLOC_CTX *frame = talloc_stackframe();
1472 *res = NULL;
1474 /* the easiest way to find a machine account anywhere in the tree
1475 is to look for hostname$ */
1476 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1477 if (expr == NULL) {
1478 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1479 goto done;
1482 status = ads_search(ads, res, expr, attrs);
1483 if (ADS_ERR_OK(status)) {
1484 if (ads_count_replies(ads, *res) != 1) {
1485 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1489 done:
1490 TALLOC_FREE(frame);
1491 return status;
1495 * Initialize a list of mods to be used in a modify request
1496 * @param ctx An initialized TALLOC_CTX
1497 * @return allocated ADS_MODLIST
1499 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1501 #define ADS_MODLIST_ALLOC_SIZE 10
1502 LDAPMod **mods;
1504 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1505 /* -1 is safety to make sure we don't go over the end.
1506 need to reset it to NULL before doing ldap modify */
1507 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1509 return (ADS_MODLIST)mods;
1514 add an attribute to the list, with values list already constructed
1516 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1517 int mod_op, const char *name,
1518 const void *_invals)
1520 int curmod;
1521 LDAPMod **modlist = (LDAPMod **) *mods;
1522 struct berval **ber_values = NULL;
1523 char **char_values = NULL;
1525 if (!_invals) {
1526 mod_op = LDAP_MOD_DELETE;
1527 } else {
1528 if (mod_op & LDAP_MOD_BVALUES) {
1529 const struct berval **b;
1530 b = discard_const_p(const struct berval *, _invals);
1531 ber_values = ads_dup_values(ctx, b);
1532 } else {
1533 const char **c;
1534 c = discard_const_p(const char *, _invals);
1535 char_values = ads_push_strvals(ctx, c);
1539 /* find the first empty slot */
1540 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1541 curmod++);
1542 if (modlist[curmod] == (LDAPMod *) -1) {
1543 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1544 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1545 return ADS_ERROR(LDAP_NO_MEMORY);
1546 memset(&modlist[curmod], 0,
1547 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1548 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1549 *mods = (ADS_MODLIST)modlist;
1552 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1553 return ADS_ERROR(LDAP_NO_MEMORY);
1554 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1555 if (mod_op & LDAP_MOD_BVALUES) {
1556 modlist[curmod]->mod_bvalues = ber_values;
1557 } else if (mod_op & LDAP_MOD_DELETE) {
1558 modlist[curmod]->mod_values = NULL;
1559 } else {
1560 modlist[curmod]->mod_values = char_values;
1563 modlist[curmod]->mod_op = mod_op;
1564 return ADS_ERROR(LDAP_SUCCESS);
1568 * Add a single string value to a mod list
1569 * @param ctx An initialized TALLOC_CTX
1570 * @param mods An initialized ADS_MODLIST
1571 * @param name The attribute name to add
1572 * @param val The value to add - NULL means DELETE
1573 * @return ADS STATUS indicating success of add
1575 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1576 const char *name, const char *val)
1578 const char *values[2];
1580 values[0] = val;
1581 values[1] = NULL;
1583 if (!val)
1584 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1585 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1589 * Add an array of string values to a mod list
1590 * @param ctx An initialized TALLOC_CTX
1591 * @param mods An initialized ADS_MODLIST
1592 * @param name The attribute name to add
1593 * @param vals The array of string values to add - NULL means DELETE
1594 * @return ADS STATUS indicating success of add
1596 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1597 const char *name, const char **vals)
1599 if (!vals)
1600 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1601 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1602 name, (const void **) vals);
1606 * Add a single ber-encoded value to a mod list
1607 * @param ctx An initialized TALLOC_CTX
1608 * @param mods An initialized ADS_MODLIST
1609 * @param name The attribute name to add
1610 * @param val The value to add - NULL means DELETE
1611 * @return ADS STATUS indicating success of add
1613 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1614 const char *name, const struct berval *val)
1616 const struct berval *values[2];
1618 values[0] = val;
1619 values[1] = NULL;
1620 if (!val)
1621 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1622 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1623 name, (const void **) values);
1626 static void ads_print_error(int ret, LDAP *ld)
1628 if (ret != 0) {
1629 char *ld_error = NULL;
1630 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1631 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1632 ret,
1633 ldap_err2string(ret),
1634 ld_error);
1635 SAFE_FREE(ld_error);
1640 * Perform an ldap modify
1641 * @param ads connection to ads server
1642 * @param mod_dn DistinguishedName to modify
1643 * @param mods list of modifications to perform
1644 * @return status of modify
1646 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1648 int ret,i;
1649 char *utf8_dn = NULL;
1650 size_t converted_size;
1652 this control is needed to modify that contains a currently
1653 non-existent attribute (but allowable for the object) to run
1655 LDAPControl PermitModify = {
1656 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1657 {0, NULL},
1658 (char) 1};
1659 LDAPControl *controls[2];
1661 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1663 controls[0] = &PermitModify;
1664 controls[1] = NULL;
1666 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1667 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1670 /* find the end of the list, marked by NULL or -1 */
1671 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1672 /* make sure the end of the list is NULL */
1673 mods[i] = NULL;
1674 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1675 (LDAPMod **) mods, controls, NULL);
1676 ads_print_error(ret, ads->ldap.ld);
1677 TALLOC_FREE(utf8_dn);
1678 return ADS_ERROR(ret);
1682 * Perform an ldap add
1683 * @param ads connection to ads server
1684 * @param new_dn DistinguishedName to add
1685 * @param mods list of attributes and values for DN
1686 * @return status of add
1688 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1690 int ret, i;
1691 char *utf8_dn = NULL;
1692 size_t converted_size;
1694 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1696 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1697 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1698 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1701 /* find the end of the list, marked by NULL or -1 */
1702 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1703 /* make sure the end of the list is NULL */
1704 mods[i] = NULL;
1706 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1707 ads_print_error(ret, ads->ldap.ld);
1708 TALLOC_FREE(utf8_dn);
1709 return ADS_ERROR(ret);
1713 * Delete a DistinguishedName
1714 * @param ads connection to ads server
1715 * @param new_dn DistinguishedName to delete
1716 * @return status of delete
1718 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1720 int ret;
1721 char *utf8_dn = NULL;
1722 size_t converted_size;
1723 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1724 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1725 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1728 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1730 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1731 ads_print_error(ret, ads->ldap.ld);
1732 TALLOC_FREE(utf8_dn);
1733 return ADS_ERROR(ret);
1737 * Build an org unit string
1738 * if org unit is Computers or blank then assume a container, otherwise
1739 * assume a / separated list of organisational units.
1740 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1741 * @param ads connection to ads server
1742 * @param org_unit Organizational unit
1743 * @return org unit string - caller must free
1745 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1747 char *ret = NULL;
1749 if (!org_unit || !*org_unit) {
1751 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1753 /* samba4 might not yet respond to a wellknownobject-query */
1754 return ret ? ret : SMB_STRDUP("cn=Computers");
1757 if (strequal(org_unit, "Computers")) {
1758 return SMB_STRDUP("cn=Computers");
1761 /* jmcd: removed "\\" from the separation chars, because it is
1762 needed as an escape for chars like '#' which are valid in an
1763 OU name */
1764 return ads_build_path(org_unit, "/", "ou=", 1);
1768 * Get a org unit string for a well-known GUID
1769 * @param ads connection to ads server
1770 * @param wknguid Well known GUID
1771 * @return org unit string - caller must free
1773 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1775 ADS_STATUS status;
1776 LDAPMessage *res = NULL;
1777 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1778 **bind_dn_exp = NULL;
1779 const char *attrs[] = {"distinguishedName", NULL};
1780 int new_ln, wkn_ln, bind_ln, i;
1782 if (wknguid == NULL) {
1783 return NULL;
1786 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1787 DEBUG(1, ("asprintf failed!\n"));
1788 return NULL;
1791 status = ads_search_dn(ads, &res, base, attrs);
1792 if (!ADS_ERR_OK(status)) {
1793 DEBUG(1,("Failed while searching for: %s\n", base));
1794 goto out;
1797 if (ads_count_replies(ads, res) != 1) {
1798 goto out;
1801 /* substitute the bind-path from the well-known-guid-search result */
1802 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1803 if (!wkn_dn) {
1804 goto out;
1807 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1808 if (!wkn_dn_exp) {
1809 goto out;
1812 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1813 if (!bind_dn_exp) {
1814 goto out;
1817 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1819 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1822 new_ln = wkn_ln - bind_ln;
1824 ret = SMB_STRDUP(wkn_dn_exp[0]);
1825 if (!ret) {
1826 goto out;
1829 for (i=1; i < new_ln; i++) {
1830 char *s = NULL;
1832 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1833 SAFE_FREE(ret);
1834 goto out;
1837 SAFE_FREE(ret);
1838 ret = SMB_STRDUP(s);
1839 free(s);
1840 if (!ret) {
1841 goto out;
1845 out:
1846 SAFE_FREE(base);
1847 ads_msgfree(ads, res);
1848 TALLOC_FREE(wkn_dn);
1849 if (wkn_dn_exp) {
1850 ldap_value_free(wkn_dn_exp);
1852 if (bind_dn_exp) {
1853 ldap_value_free(bind_dn_exp);
1856 return ret;
1860 * Adds (appends) an item to an attribute array, rather then
1861 * replacing the whole list
1862 * @param ctx An initialized TALLOC_CTX
1863 * @param mods An initialized ADS_MODLIST
1864 * @param name name of the ldap attribute to append to
1865 * @param vals an array of values to add
1866 * @return status of addition
1869 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1870 const char *name, const char **vals)
1872 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1873 (const void *) vals);
1877 * Determines the an account's current KVNO via an LDAP lookup
1878 * @param ads An initialized ADS_STRUCT
1879 * @param account_name the NT samaccountname.
1880 * @return the kvno for the account, or -1 in case of a failure.
1883 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1885 LDAPMessage *res = NULL;
1886 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1887 char *filter;
1888 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1889 char *dn_string = NULL;
1890 ADS_STATUS ret;
1892 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1893 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1894 return kvno;
1896 ret = ads_search(ads, &res, filter, attrs);
1897 SAFE_FREE(filter);
1898 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1899 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1900 ads_msgfree(ads, res);
1901 return kvno;
1904 dn_string = ads_get_dn(ads, talloc_tos(), res);
1905 if (!dn_string) {
1906 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1907 ads_msgfree(ads, res);
1908 return kvno;
1910 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1911 TALLOC_FREE(dn_string);
1913 /* ---------------------------------------------------------
1914 * 0 is returned as a default KVNO from this point on...
1915 * This is done because Windows 2000 does not support key
1916 * version numbers. Chances are that a failure in the next
1917 * step is simply due to Windows 2000 being used for a
1918 * domain controller. */
1919 kvno = 0;
1921 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1922 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1923 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1924 ads_msgfree(ads, res);
1925 return kvno;
1928 /* Success */
1929 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1930 ads_msgfree(ads, res);
1931 return kvno;
1935 * Determines the computer account's current KVNO via an LDAP lookup
1936 * @param ads An initialized ADS_STRUCT
1937 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1938 * @return the kvno for the computer account, or -1 in case of a failure.
1941 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1943 char *computer_account = NULL;
1944 uint32_t kvno = -1;
1946 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1947 return kvno;
1950 kvno = ads_get_kvno(ads, computer_account);
1951 free(computer_account);
1953 return kvno;
1957 * This clears out all registered spn's for a given hostname
1958 * @param ads An initilaized ADS_STRUCT
1959 * @param machine_name the NetBIOS name of the computer.
1960 * @return 0 upon success, non-zero otherwise.
1963 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1965 TALLOC_CTX *ctx;
1966 LDAPMessage *res = NULL;
1967 ADS_MODLIST mods;
1968 const char *servicePrincipalName[1] = {NULL};
1969 ADS_STATUS ret;
1970 char *dn_string = NULL;
1972 ret = ads_find_machine_acct(ads, &res, machine_name);
1973 if (!ADS_ERR_OK(ret)) {
1974 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1975 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1976 ads_msgfree(ads, res);
1977 return ret;
1980 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1981 ctx = talloc_init("ads_clear_service_principal_names");
1982 if (!ctx) {
1983 ads_msgfree(ads, res);
1984 return ADS_ERROR(LDAP_NO_MEMORY);
1987 if (!(mods = ads_init_mods(ctx))) {
1988 talloc_destroy(ctx);
1989 ads_msgfree(ads, res);
1990 return ADS_ERROR(LDAP_NO_MEMORY);
1992 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1993 if (!ADS_ERR_OK(ret)) {
1994 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1995 ads_msgfree(ads, res);
1996 talloc_destroy(ctx);
1997 return ret;
1999 dn_string = ads_get_dn(ads, talloc_tos(), res);
2000 if (!dn_string) {
2001 talloc_destroy(ctx);
2002 ads_msgfree(ads, res);
2003 return ADS_ERROR(LDAP_NO_MEMORY);
2005 ret = ads_gen_mod(ads, dn_string, mods);
2006 TALLOC_FREE(dn_string);
2007 if (!ADS_ERR_OK(ret)) {
2008 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
2009 machine_name));
2010 ads_msgfree(ads, res);
2011 talloc_destroy(ctx);
2012 return ret;
2015 ads_msgfree(ads, res);
2016 talloc_destroy(ctx);
2017 return ret;
2021 * @brief Search for an element in a string array.
2023 * @param[in] el_array The string array to search.
2025 * @param[in] num_el The number of elements in the string array.
2027 * @param[in] el The string to search.
2029 * @return True if found, false if not.
2031 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
2033 size_t i;
2035 if (el_array == NULL || num_el == 0 || el == NULL) {
2036 return false;
2039 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
2040 int cmp;
2042 cmp = strcasecmp_m(el_array[i], el);
2043 if (cmp == 0) {
2044 return true;
2048 return false;
2052 * @brief This gets the service principal names of an existing computer account.
2054 * @param[in] mem_ctx The memory context to use to allocate the spn array.
2056 * @param[in] ads The ADS context to use.
2058 * @param[in] machine_name The NetBIOS name of the computer, which is used to
2059 * identify the computer account.
2061 * @param[in] spn_array A pointer to store the array for SPNs.
2063 * @param[in] num_spns The number of principals stored in the array.
2065 * @return 0 on success, or a ADS error if a failure occurred.
2067 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
2068 ADS_STRUCT *ads,
2069 const char *machine_name,
2070 char ***spn_array,
2071 size_t *num_spns)
2073 ADS_STATUS status;
2074 LDAPMessage *res = NULL;
2075 int count;
2077 status = ads_find_machine_acct(ads,
2078 &res,
2079 machine_name);
2080 if (!ADS_ERR_OK(status)) {
2081 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2082 machine_name));
2083 return status;
2086 count = ads_count_replies(ads, res);
2087 if (count != 1) {
2088 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2089 goto done;
2092 *spn_array = ads_pull_strings(ads,
2093 mem_ctx,
2094 res,
2095 "servicePrincipalName",
2096 num_spns);
2097 if (*spn_array == NULL) {
2098 DEBUG(1, ("Host account for %s does not have service principal "
2099 "names.\n",
2100 machine_name));
2101 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2102 goto done;
2105 done:
2106 ads_msgfree(ads, res);
2108 return status;
2112 * This adds a service principal name to an existing computer account
2113 * (found by hostname) in AD.
2114 * @param ads An initialized ADS_STRUCT
2115 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2116 * @param spns An array or strings for the service principals to add,
2117 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2118 * @return 0 upon sucess, or non-zero if a failure occurs
2121 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2122 const char *machine_name,
2123 const char **spns)
2125 ADS_STATUS ret;
2126 TALLOC_CTX *ctx;
2127 LDAPMessage *res = NULL;
2128 ADS_MODLIST mods;
2129 char *dn_string = NULL;
2130 const char **servicePrincipalName = spns;
2132 ret = ads_find_machine_acct(ads, &res, machine_name);
2133 if (!ADS_ERR_OK(ret)) {
2134 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2135 machine_name));
2136 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2137 ads_msgfree(ads, res);
2138 return ret;
2141 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2142 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2143 ads_msgfree(ads, res);
2144 return ADS_ERROR(LDAP_NO_MEMORY);
2147 DEBUG(5,("ads_add_service_principal_name: INFO: "
2148 "Adding %s to host %s\n",
2149 spns[0] ? "N/A" : spns[0], machine_name));
2152 DEBUG(5,("ads_add_service_principal_name: INFO: "
2153 "Adding %s to host %s\n",
2154 spns[1] ? "N/A" : spns[1], machine_name));
2156 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2157 ret = ADS_ERROR(LDAP_NO_MEMORY);
2158 goto out;
2161 ret = ads_add_strlist(ctx,
2162 &mods,
2163 "servicePrincipalName",
2164 servicePrincipalName);
2165 if (!ADS_ERR_OK(ret)) {
2166 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2167 goto out;
2170 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2171 ret = ADS_ERROR(LDAP_NO_MEMORY);
2172 goto out;
2175 ret = ads_gen_mod(ads, dn_string, mods);
2176 if (!ADS_ERR_OK(ret)) {
2177 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2178 goto out;
2181 out:
2182 TALLOC_FREE( ctx );
2183 ads_msgfree(ads, res);
2184 return ret;
2187 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2188 LDAPMessage *msg)
2190 uint32_t acct_ctrl = 0;
2191 bool ok;
2193 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2194 if (!ok) {
2195 return 0;
2198 return acct_ctrl;
2201 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2202 LDAPMessage *msg,
2203 const struct berval *machine_pw_val)
2205 ADS_MODLIST mods;
2206 ADS_STATUS ret;
2207 TALLOC_CTX *frame = talloc_stackframe();
2208 uint32_t acct_control;
2209 char *control_str = NULL;
2210 const char *attrs[] = {
2211 "objectSid",
2212 NULL
2214 LDAPMessage *res = NULL;
2215 char *dn = NULL;
2217 dn = ads_get_dn(ads, frame, msg);
2218 if (dn == NULL) {
2219 ret = ADS_ERROR(LDAP_NO_MEMORY);
2220 goto done;
2223 acct_control = ads_get_acct_ctrl(ads, msg);
2224 if (acct_control == 0) {
2225 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2226 goto done;
2230 * Changing the password, disables the account. So we need to change the
2231 * userAccountControl flags to enable it again.
2233 mods = ads_init_mods(frame);
2234 if (mods == NULL) {
2235 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2236 goto done;
2239 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2241 ret = ads_gen_mod(ads, dn, mods);
2242 if (!ADS_ERR_OK(ret)) {
2243 goto done;
2245 TALLOC_FREE(mods);
2248 * To activate the account, we need to disable and enable it.
2250 acct_control |= UF_ACCOUNTDISABLE;
2252 control_str = talloc_asprintf(frame, "%u", acct_control);
2253 if (control_str == NULL) {
2254 ret = ADS_ERROR(LDAP_NO_MEMORY);
2255 goto done;
2258 mods = ads_init_mods(frame);
2259 if (mods == NULL) {
2260 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2261 goto done;
2264 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2266 ret = ads_gen_mod(ads, dn, mods);
2267 if (!ADS_ERR_OK(ret)) {
2268 goto done;
2270 TALLOC_FREE(mods);
2271 TALLOC_FREE(control_str);
2274 * Enable the account again.
2276 acct_control &= ~UF_ACCOUNTDISABLE;
2278 control_str = talloc_asprintf(frame, "%u", acct_control);
2279 if (control_str == NULL) {
2280 ret = ADS_ERROR(LDAP_NO_MEMORY);
2281 goto done;
2284 mods = ads_init_mods(frame);
2285 if (mods == NULL) {
2286 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2287 goto done;
2290 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2292 ret = ads_gen_mod(ads, dn, mods);
2293 if (!ADS_ERR_OK(ret)) {
2294 goto done;
2296 TALLOC_FREE(mods);
2297 TALLOC_FREE(control_str);
2299 ret = ads_search_dn(ads, &res, dn, attrs);
2300 ads_msgfree(ads, res);
2302 done:
2303 talloc_free(frame);
2305 return ret;
2309 * adds a machine account to the ADS server
2310 * @param ads An intialized ADS_STRUCT
2311 * @param machine_name - the NetBIOS machine name of this account.
2312 * @param account_type A number indicating the type of account to create
2313 * @param org_unit The LDAP path in which to place this account
2314 * @return 0 upon success, or non-zero otherwise
2317 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2318 const char *machine_name,
2319 const char *machine_password,
2320 const char *org_unit,
2321 uint32_t etype_list,
2322 const char *dns_domain_name)
2324 ADS_STATUS ret;
2325 char *samAccountName = NULL;
2326 char *controlstr = NULL;
2327 TALLOC_CTX *ctx = NULL;
2328 ADS_MODLIST mods;
2329 char *machine_escaped = NULL;
2330 char *dns_hostname = NULL;
2331 char *new_dn = NULL;
2332 char *utf8_pw = NULL;
2333 size_t utf8_pw_len = 0;
2334 char *utf16_pw = NULL;
2335 size_t utf16_pw_len = 0;
2336 struct berval machine_pw_val;
2337 bool ok;
2338 const char **spn_array = NULL;
2339 size_t num_spns = 0;
2340 const char *spn_prefix[] = {
2341 "HOST",
2342 "RestrictedKrbHost",
2344 size_t i;
2345 LDAPMessage *res = NULL;
2346 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2348 ctx = talloc_init("ads_add_machine_acct");
2349 if (ctx == NULL) {
2350 return ADS_ERROR(LDAP_NO_MEMORY);
2353 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2354 if (machine_escaped == NULL) {
2355 ret = ADS_ERROR(LDAP_NO_MEMORY);
2356 goto done;
2359 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2360 if (utf8_pw == NULL) {
2361 ret = ADS_ERROR(LDAP_NO_MEMORY);
2362 goto done;
2364 utf8_pw_len = strlen(utf8_pw);
2366 ok = convert_string_talloc(ctx,
2367 CH_UTF8, CH_UTF16MUNGED,
2368 utf8_pw, utf8_pw_len,
2369 (void *)&utf16_pw, &utf16_pw_len);
2370 if (!ok) {
2371 ret = ADS_ERROR(LDAP_NO_MEMORY);
2372 goto done;
2375 machine_pw_val = (struct berval) {
2376 .bv_val = utf16_pw,
2377 .bv_len = utf16_pw_len,
2380 /* Check if the machine account already exists. */
2381 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2382 if (ADS_ERR_OK(ret)) {
2383 /* Change the machine account password */
2384 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2385 ads_msgfree(ads, res);
2387 goto done;
2389 ads_msgfree(ads, res);
2391 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2392 if (new_dn == NULL) {
2393 ret = ADS_ERROR(LDAP_NO_MEMORY);
2394 goto done;
2397 /* Create machine account */
2399 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2400 if (samAccountName == NULL) {
2401 ret = ADS_ERROR(LDAP_NO_MEMORY);
2402 goto done;
2405 dns_hostname = talloc_asprintf(ctx,
2406 "%s.%s",
2407 machine_name,
2408 dns_domain_name);
2409 if (dns_hostname == NULL) {
2410 ret = ADS_ERROR(LDAP_NO_MEMORY);
2411 goto done;
2414 /* Add dns_hostname SPNs */
2415 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2416 char *spn = talloc_asprintf(ctx,
2417 "%s/%s",
2418 spn_prefix[i],
2419 dns_hostname);
2420 if (spn == NULL) {
2421 ret = ADS_ERROR(LDAP_NO_MEMORY);
2422 goto done;
2425 ok = add_string_to_array(spn_array,
2426 spn,
2427 &spn_array,
2428 &num_spns);
2429 if (!ok) {
2430 ret = ADS_ERROR(LDAP_NO_MEMORY);
2431 goto done;
2435 /* Add machine_name SPNs */
2436 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2437 char *spn = talloc_asprintf(ctx,
2438 "%s/%s",
2439 spn_prefix[i],
2440 machine_name);
2441 if (spn == NULL) {
2442 ret = ADS_ERROR(LDAP_NO_MEMORY);
2443 goto done;
2446 ok = add_string_to_array(spn_array,
2447 spn,
2448 &spn_array,
2449 &num_spns);
2450 if (!ok) {
2451 ret = ADS_ERROR(LDAP_NO_MEMORY);
2452 goto done;
2456 /* Make sure to NULL terminate the array */
2457 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2458 if (spn_array == NULL) {
2459 ret = ADS_ERROR(LDAP_NO_MEMORY);
2460 goto done;
2462 spn_array[num_spns] = NULL;
2464 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2465 if (controlstr == NULL) {
2466 ret = ADS_ERROR(LDAP_NO_MEMORY);
2467 goto done;
2470 mods = ads_init_mods(ctx);
2471 if (mods == NULL) {
2472 ret = ADS_ERROR(LDAP_NO_MEMORY);
2473 goto done;
2476 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2477 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2478 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2479 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2480 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2481 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2483 ret = ads_gen_add(ads, new_dn, mods);
2485 done:
2486 SAFE_FREE(machine_escaped);
2487 talloc_destroy(ctx);
2489 return ret;
2493 * move a machine account to another OU on the ADS server
2494 * @param ads - An intialized ADS_STRUCT
2495 * @param machine_name - the NetBIOS machine name of this account.
2496 * @param org_unit - The LDAP path in which to place this account
2497 * @param moved - whether we moved the machine account (optional)
2498 * @return 0 upon success, or non-zero otherwise
2501 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2502 const char *org_unit, bool *moved)
2504 ADS_STATUS rc;
2505 int ldap_status;
2506 LDAPMessage *res = NULL;
2507 char *filter = NULL;
2508 char *computer_dn = NULL;
2509 char *parent_dn;
2510 char *computer_rdn = NULL;
2511 bool need_move = False;
2513 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2514 rc = ADS_ERROR(LDAP_NO_MEMORY);
2515 goto done;
2518 /* Find pre-existing machine */
2519 rc = ads_search(ads, &res, filter, NULL);
2520 if (!ADS_ERR_OK(rc)) {
2521 goto done;
2524 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2525 if (!computer_dn) {
2526 rc = ADS_ERROR(LDAP_NO_MEMORY);
2527 goto done;
2530 parent_dn = ads_parent_dn(computer_dn);
2531 if (strequal(parent_dn, org_unit)) {
2532 goto done;
2535 need_move = True;
2537 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2538 rc = ADS_ERROR(LDAP_NO_MEMORY);
2539 goto done;
2542 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2543 org_unit, 1, NULL, NULL);
2544 rc = ADS_ERROR(ldap_status);
2546 done:
2547 ads_msgfree(ads, res);
2548 SAFE_FREE(filter);
2549 TALLOC_FREE(computer_dn);
2550 SAFE_FREE(computer_rdn);
2552 if (!ADS_ERR_OK(rc)) {
2553 need_move = False;
2556 if (moved) {
2557 *moved = need_move;
2560 return rc;
2564 dump a binary result from ldap
2566 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2568 size_t i;
2569 for (i=0; values[i]; i++) {
2570 ber_len_t j;
2571 printf("%s: ", field);
2572 for (j=0; j<values[i]->bv_len; j++) {
2573 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2575 printf("\n");
2579 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2581 int i;
2582 for (i=0; values[i]; i++) {
2583 NTSTATUS status;
2584 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2585 struct GUID guid;
2587 status = GUID_from_ndr_blob(&in, &guid);
2588 if (NT_STATUS_IS_OK(status)) {
2589 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2590 } else {
2591 printf("%s: INVALID GUID\n", field);
2597 dump a sid result from ldap
2599 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2601 int i;
2602 for (i=0; values[i]; i++) {
2603 ssize_t ret;
2604 struct dom_sid sid;
2605 struct dom_sid_buf tmp;
2606 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2607 values[i]->bv_len, &sid);
2608 if (ret == -1) {
2609 return;
2611 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2616 dump ntSecurityDescriptor
2618 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2620 TALLOC_CTX *frame = talloc_stackframe();
2621 struct security_descriptor *psd;
2622 NTSTATUS status;
2624 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2625 values[0]->bv_len, &psd);
2626 if (!NT_STATUS_IS_OK(status)) {
2627 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2628 nt_errstr(status)));
2629 TALLOC_FREE(frame);
2630 return;
2633 if (psd) {
2634 ads_disp_sd(ads, talloc_tos(), psd);
2637 TALLOC_FREE(frame);
2641 dump a string result from ldap
2643 static void dump_string(const char *field, char **values)
2645 int i;
2646 for (i=0; values[i]; i++) {
2647 printf("%s: %s\n", field, values[i]);
2652 dump a field from LDAP on stdout
2653 used for debugging
2656 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2658 const struct {
2659 const char *name;
2660 bool string;
2661 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2662 } handlers[] = {
2663 {"objectGUID", False, dump_guid},
2664 {"netbootGUID", False, dump_guid},
2665 {"nTSecurityDescriptor", False, dump_sd},
2666 {"dnsRecord", False, dump_binary},
2667 {"objectSid", False, dump_sid},
2668 {"tokenGroups", False, dump_sid},
2669 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2670 {"tokengroupsGlobalandUniversal", False, dump_sid},
2671 {"mS-DS-CreatorSID", False, dump_sid},
2672 {"msExchMailboxGuid", False, dump_guid},
2673 {NULL, True, NULL}
2675 int i;
2677 if (!field) { /* must be end of an entry */
2678 printf("\n");
2679 return False;
2682 for (i=0; handlers[i].name; i++) {
2683 if (strcasecmp_m(handlers[i].name, field) == 0) {
2684 if (!values) /* first time, indicate string or not */
2685 return handlers[i].string;
2686 handlers[i].handler(ads, field, (struct berval **) values);
2687 break;
2690 if (!handlers[i].name) {
2691 if (!values) /* first time, indicate string conversion */
2692 return True;
2693 dump_string(field, (char **)values);
2695 return False;
2699 * Dump a result from LDAP on stdout
2700 * used for debugging
2701 * @param ads connection to ads server
2702 * @param res Results to dump
2705 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2707 ads_process_results(ads, res, ads_dump_field, NULL);
2711 * Walk through results, calling a function for each entry found.
2712 * The function receives a field name, a berval * array of values,
2713 * and a data area passed through from the start. The function is
2714 * called once with null for field and values at the end of each
2715 * entry.
2716 * @param ads connection to ads server
2717 * @param res Results to process
2718 * @param fn Function for processing each result
2719 * @param data_area user-defined area to pass to function
2721 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2722 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2723 void *data_area)
2725 LDAPMessage *msg;
2726 TALLOC_CTX *ctx;
2727 size_t converted_size;
2729 if (!(ctx = talloc_init("ads_process_results")))
2730 return;
2732 for (msg = ads_first_entry(ads, res); msg;
2733 msg = ads_next_entry(ads, msg)) {
2734 char *utf8_field;
2735 BerElement *b;
2737 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2738 (LDAPMessage *)msg,&b);
2739 utf8_field;
2740 utf8_field=ldap_next_attribute(ads->ldap.ld,
2741 (LDAPMessage *)msg,b)) {
2742 struct berval **ber_vals;
2743 char **str_vals;
2744 char **utf8_vals;
2745 char *field;
2746 bool string;
2748 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2749 &converted_size))
2751 DEBUG(0,("ads_process_results: "
2752 "pull_utf8_talloc failed: %s",
2753 strerror(errno)));
2756 string = fn(ads, field, NULL, data_area);
2758 if (string) {
2759 const char **p;
2761 utf8_vals = ldap_get_values(ads->ldap.ld,
2762 (LDAPMessage *)msg, field);
2763 p = discard_const_p(const char *, utf8_vals);
2764 str_vals = ads_pull_strvals(ctx, p);
2765 fn(ads, field, (void **) str_vals, data_area);
2766 ldap_value_free(utf8_vals);
2767 } else {
2768 ber_vals = ldap_get_values_len(ads->ldap.ld,
2769 (LDAPMessage *)msg, field);
2770 fn(ads, field, (void **) ber_vals, data_area);
2772 ldap_value_free_len(ber_vals);
2774 ldap_memfree(utf8_field);
2776 ber_free(b, 0);
2777 talloc_free_children(ctx);
2778 fn(ads, NULL, NULL, data_area); /* completed an entry */
2781 talloc_destroy(ctx);
2785 * count how many replies are in a LDAPMessage
2786 * @param ads connection to ads server
2787 * @param res Results to count
2788 * @return number of replies
2790 int ads_count_replies(ADS_STRUCT *ads, void *res)
2792 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2796 * pull the first entry from a ADS result
2797 * @param ads connection to ads server
2798 * @param res Results of search
2799 * @return first entry from result
2801 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2803 return ldap_first_entry(ads->ldap.ld, res);
2807 * pull the next entry from a ADS result
2808 * @param ads connection to ads server
2809 * @param res Results of search
2810 * @return next entry from result
2812 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2814 return ldap_next_entry(ads->ldap.ld, res);
2818 * pull the first message from a ADS result
2819 * @param ads connection to ads server
2820 * @param res Results of search
2821 * @return first message from result
2823 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2825 return ldap_first_message(ads->ldap.ld, res);
2829 * pull the next message from a ADS result
2830 * @param ads connection to ads server
2831 * @param res Results of search
2832 * @return next message from result
2834 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2836 return ldap_next_message(ads->ldap.ld, res);
2840 * pull a single string from a ADS result
2841 * @param ads connection to ads server
2842 * @param mem_ctx TALLOC_CTX to use for allocating result string
2843 * @param msg Results of search
2844 * @param field Attribute to retrieve
2845 * @return Result string in talloc context
2847 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2848 const char *field)
2850 char **values;
2851 char *ret = NULL;
2852 char *ux_string;
2853 size_t converted_size;
2855 values = ldap_get_values(ads->ldap.ld, msg, field);
2856 if (!values)
2857 return NULL;
2859 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2860 &converted_size))
2862 ret = ux_string;
2864 ldap_value_free(values);
2865 return ret;
2869 * pull an array of strings from a ADS result
2870 * @param ads connection to ads server
2871 * @param mem_ctx TALLOC_CTX to use for allocating result string
2872 * @param msg Results of search
2873 * @param field Attribute to retrieve
2874 * @return Result strings in talloc context
2876 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2877 LDAPMessage *msg, const char *field,
2878 size_t *num_values)
2880 char **values;
2881 char **ret = NULL;
2882 size_t i, converted_size;
2884 values = ldap_get_values(ads->ldap.ld, msg, field);
2885 if (!values)
2886 return NULL;
2888 *num_values = ldap_count_values(values);
2890 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2891 if (!ret) {
2892 ldap_value_free(values);
2893 return NULL;
2896 for (i=0;i<*num_values;i++) {
2897 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2898 &converted_size))
2900 ldap_value_free(values);
2901 return NULL;
2904 ret[i] = NULL;
2906 ldap_value_free(values);
2907 return ret;
2911 * pull an array of strings from a ADS result
2912 * (handle large multivalue attributes with range retrieval)
2913 * @param ads connection to ads server
2914 * @param mem_ctx TALLOC_CTX to use for allocating result string
2915 * @param msg Results of search
2916 * @param field Attribute to retrieve
2917 * @param current_strings strings returned by a previous call to this function
2918 * @param next_attribute The next query should ask for this attribute
2919 * @param num_values How many values did we get this time?
2920 * @param more_values Are there more values to get?
2921 * @return Result strings in talloc context
2923 char **ads_pull_strings_range(ADS_STRUCT *ads,
2924 TALLOC_CTX *mem_ctx,
2925 LDAPMessage *msg, const char *field,
2926 char **current_strings,
2927 const char **next_attribute,
2928 size_t *num_strings,
2929 bool *more_strings)
2931 char *attr;
2932 char *expected_range_attrib, *range_attr;
2933 BerElement *ptr = NULL;
2934 char **strings;
2935 char **new_strings;
2936 size_t num_new_strings;
2937 unsigned long int range_start;
2938 unsigned long int range_end;
2940 /* we might have been given the whole lot anyway */
2941 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2942 *more_strings = False;
2943 return strings;
2946 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2948 /* look for Range result */
2949 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2950 attr;
2951 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2952 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2953 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2954 range_attr = attr;
2955 break;
2957 ldap_memfree(attr);
2959 if (!attr) {
2960 ber_free(ptr, 0);
2961 /* nothing here - this field is just empty */
2962 *more_strings = False;
2963 return NULL;
2966 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2967 &range_start, &range_end) == 2) {
2968 *more_strings = True;
2969 } else {
2970 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2971 &range_start) == 1) {
2972 *more_strings = False;
2973 } else {
2974 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2975 range_attr));
2976 ldap_memfree(range_attr);
2977 *more_strings = False;
2978 return NULL;
2982 if ((*num_strings) != range_start) {
2983 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2984 " - aborting range retreival\n",
2985 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2986 ldap_memfree(range_attr);
2987 *more_strings = False;
2988 return NULL;
2991 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2993 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2994 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2995 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2996 range_attr, (unsigned long int)range_end - range_start + 1,
2997 (unsigned long int)num_new_strings));
2998 ldap_memfree(range_attr);
2999 *more_strings = False;
3000 return NULL;
3003 strings = talloc_realloc(mem_ctx, current_strings, char *,
3004 *num_strings + num_new_strings);
3006 if (strings == NULL) {
3007 ldap_memfree(range_attr);
3008 *more_strings = False;
3009 return NULL;
3012 if (new_strings && num_new_strings) {
3013 memcpy(&strings[*num_strings], new_strings,
3014 sizeof(*new_strings) * num_new_strings);
3017 (*num_strings) += num_new_strings;
3019 if (*more_strings) {
3020 *next_attribute = talloc_asprintf(mem_ctx,
3021 "%s;range=%d-*",
3022 field,
3023 (int)*num_strings);
3025 if (!*next_attribute) {
3026 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
3027 ldap_memfree(range_attr);
3028 *more_strings = False;
3029 return NULL;
3033 ldap_memfree(range_attr);
3035 return strings;
3039 * pull a single uint32_t from a ADS result
3040 * @param ads connection to ads server
3041 * @param msg Results of search
3042 * @param field Attribute to retrieve
3043 * @param v Pointer to int to store result
3044 * @return boolean inidicating success
3046 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3047 uint32_t *v)
3049 char **values;
3051 values = ldap_get_values(ads->ldap.ld, msg, field);
3052 if (!values)
3053 return False;
3054 if (!values[0]) {
3055 ldap_value_free(values);
3056 return False;
3059 *v = atoi(values[0]);
3060 ldap_value_free(values);
3061 return True;
3065 * pull a single objectGUID from an ADS result
3066 * @param ads connection to ADS server
3067 * @param msg results of search
3068 * @param guid 37-byte area to receive text guid
3069 * @return boolean indicating success
3071 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3073 DATA_BLOB blob;
3074 NTSTATUS status;
3076 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3077 &blob)) {
3078 return false;
3081 status = GUID_from_ndr_blob(&blob, guid);
3082 talloc_free(blob.data);
3083 return NT_STATUS_IS_OK(status);
3088 * pull a single struct dom_sid from a ADS result
3089 * @param ads connection to ads server
3090 * @param msg Results of search
3091 * @param field Attribute to retrieve
3092 * @param sid Pointer to sid to store result
3093 * @return boolean inidicating success
3095 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3096 struct dom_sid *sid)
3098 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3102 * pull an array of struct dom_sids from a ADS result
3103 * @param ads connection to ads server
3104 * @param mem_ctx TALLOC_CTX for allocating sid array
3105 * @param msg Results of search
3106 * @param field Attribute to retrieve
3107 * @param sids pointer to sid array to allocate
3108 * @return the count of SIDs pulled
3110 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3111 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3113 struct berval **values;
3114 int count, i;
3116 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3118 if (!values)
3119 return 0;
3121 for (i=0; values[i]; i++)
3122 /* nop */ ;
3124 if (i) {
3125 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3126 if (!(*sids)) {
3127 ldap_value_free_len(values);
3128 return 0;
3130 } else {
3131 (*sids) = NULL;
3134 count = 0;
3135 for (i=0; values[i]; i++) {
3136 ssize_t ret;
3137 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3138 values[i]->bv_len, &(*sids)[count]);
3139 if (ret != -1) {
3140 struct dom_sid_buf buf;
3141 DBG_DEBUG("pulling SID: %s\n",
3142 dom_sid_str_buf(&(*sids)[count], &buf));
3143 count++;
3147 ldap_value_free_len(values);
3148 return count;
3152 * pull a struct security_descriptor from a ADS result
3153 * @param ads connection to ads server
3154 * @param mem_ctx TALLOC_CTX for allocating sid array
3155 * @param msg Results of search
3156 * @param field Attribute to retrieve
3157 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3158 * @return boolean inidicating success
3160 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3161 LDAPMessage *msg, const char *field,
3162 struct security_descriptor **sd)
3164 struct berval **values;
3165 bool ret = true;
3167 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3169 if (!values) return false;
3171 if (values[0]) {
3172 NTSTATUS status;
3173 status = unmarshall_sec_desc(mem_ctx,
3174 (uint8_t *)values[0]->bv_val,
3175 values[0]->bv_len, sd);
3176 if (!NT_STATUS_IS_OK(status)) {
3177 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3178 nt_errstr(status)));
3179 ret = false;
3183 ldap_value_free_len(values);
3184 return ret;
3188 * in order to support usernames longer than 21 characters we need to
3189 * use both the sAMAccountName and the userPrincipalName attributes
3190 * It seems that not all users have the userPrincipalName attribute set
3192 * @param ads connection to ads server
3193 * @param mem_ctx TALLOC_CTX for allocating sid array
3194 * @param msg Results of search
3195 * @return the username
3197 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3198 LDAPMessage *msg)
3200 #if 0 /* JERRY */
3201 char *ret, *p;
3203 /* lookup_name() only works on the sAMAccountName to
3204 returning the username portion of userPrincipalName
3205 breaks winbindd_getpwnam() */
3207 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3208 if (ret && (p = strchr_m(ret, '@'))) {
3209 *p = 0;
3210 return ret;
3212 #endif
3213 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3218 * find the update serial number - this is the core of the ldap cache
3219 * @param ads connection to ads server
3220 * @param ads connection to ADS server
3221 * @param usn Pointer to retrieved update serial number
3222 * @return status of search
3224 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3226 const char *attrs[] = {"highestCommittedUSN", NULL};
3227 ADS_STATUS status;
3228 LDAPMessage *res;
3230 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3231 if (!ADS_ERR_OK(status))
3232 return status;
3234 if (ads_count_replies(ads, res) != 1) {
3235 ads_msgfree(ads, res);
3236 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3239 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3240 ads_msgfree(ads, res);
3241 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3244 ads_msgfree(ads, res);
3245 return ADS_SUCCESS;
3248 /* parse a ADS timestring - typical string is
3249 '20020917091222.0Z0' which means 09:12.22 17th September
3250 2002, timezone 0 */
3251 static time_t ads_parse_time(const char *str)
3253 struct tm tm;
3255 ZERO_STRUCT(tm);
3257 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3258 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3259 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3260 return 0;
3262 tm.tm_year -= 1900;
3263 tm.tm_mon -= 1;
3265 return timegm(&tm);
3268 /********************************************************************
3269 ********************************************************************/
3271 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3273 const char *attrs[] = {"currentTime", NULL};
3274 ADS_STATUS status;
3275 LDAPMessage *res;
3276 char *timestr;
3277 TALLOC_CTX *ctx;
3278 ADS_STRUCT *ads_s = ads;
3280 if (!(ctx = talloc_init("ads_current_time"))) {
3281 return ADS_ERROR(LDAP_NO_MEMORY);
3284 /* establish a new ldap tcp session if necessary */
3286 if ( !ads->ldap.ld ) {
3288 * ADS_STRUCT may be being reused after a
3289 * DC lookup, so ads->ldap.ss may already have a
3290 * good address. If not, re-initialize the passed-in
3291 * ADS_STRUCT with the given server.XXXX parameters.
3293 * Note that this doesn't depend on
3294 * ads->server.ldap_server != NULL,
3295 * as the case where ads->server.ldap_server==NULL and
3296 * ads->ldap.ss != zero_address is precisely the DC
3297 * lookup case where ads->ldap.ss was found by going
3298 * through ads_find_dc() again we want to avoid repeating.
3300 if (is_zero_addr(&ads->ldap.ss)) {
3301 ads_s = ads_init(ads->server.realm,
3302 ads->server.workgroup,
3303 ads->server.ldap_server,
3304 ADS_SASL_PLAIN );
3305 if (ads_s == NULL) {
3306 status = ADS_ERROR(LDAP_NO_MEMORY);
3307 goto done;
3310 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3311 status = ads_connect( ads_s );
3312 if ( !ADS_ERR_OK(status))
3313 goto done;
3316 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3317 if (!ADS_ERR_OK(status)) {
3318 goto done;
3321 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
3322 if (!timestr) {
3323 ads_msgfree(ads_s, res);
3324 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3325 goto done;
3328 /* but save the time and offset in the original ADS_STRUCT */
3330 ads->config.current_time = ads_parse_time(timestr);
3332 if (ads->config.current_time != 0) {
3333 ads->auth.time_offset = ads->config.current_time - time(NULL);
3334 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3337 ads_msgfree(ads, res);
3339 status = ADS_SUCCESS;
3341 done:
3342 /* free any temporary ads connections */
3343 if ( ads_s != ads ) {
3344 ads_destroy( &ads_s );
3346 talloc_destroy(ctx);
3348 return status;
3351 /********************************************************************
3352 ********************************************************************/
3354 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3356 const char *attrs[] = {"domainFunctionality", NULL};
3357 ADS_STATUS status;
3358 LDAPMessage *res;
3359 ADS_STRUCT *ads_s = ads;
3361 *val = DS_DOMAIN_FUNCTION_2000;
3363 /* establish a new ldap tcp session if necessary */
3365 if ( !ads->ldap.ld ) {
3367 * ADS_STRUCT may be being reused after a
3368 * DC lookup, so ads->ldap.ss may already have a
3369 * good address. If not, re-initialize the passed-in
3370 * ADS_STRUCT with the given server.XXXX parameters.
3372 * Note that this doesn't depend on
3373 * ads->server.ldap_server != NULL,
3374 * as the case where ads->server.ldap_server==NULL and
3375 * ads->ldap.ss != zero_address is precisely the DC
3376 * lookup case where ads->ldap.ss was found by going
3377 * through ads_find_dc() again we want to avoid repeating.
3379 if (is_zero_addr(&ads->ldap.ss)) {
3380 ads_s = ads_init(ads->server.realm,
3381 ads->server.workgroup,
3382 ads->server.ldap_server,
3383 ADS_SASL_PLAIN );
3384 if (ads_s == NULL ) {
3385 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3386 goto done;
3389 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3390 status = ads_connect( ads_s );
3391 if ( !ADS_ERR_OK(status))
3392 goto done;
3395 /* If the attribute does not exist assume it is a Windows 2000
3396 functional domain */
3398 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3399 if (!ADS_ERR_OK(status)) {
3400 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3401 status = ADS_SUCCESS;
3403 goto done;
3406 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3407 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3409 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3412 ads_msgfree(ads, res);
3414 done:
3415 /* free any temporary ads connections */
3416 if ( ads_s != ads ) {
3417 ads_destroy( &ads_s );
3420 return status;
3424 * find the domain sid for our domain
3425 * @param ads connection to ads server
3426 * @param sid Pointer to domain sid
3427 * @return status of search
3429 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3431 const char *attrs[] = {"objectSid", NULL};
3432 LDAPMessage *res;
3433 ADS_STATUS rc;
3435 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3436 attrs, &res);
3437 if (!ADS_ERR_OK(rc)) return rc;
3438 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3439 ads_msgfree(ads, res);
3440 return ADS_ERROR_SYSTEM(ENOENT);
3442 ads_msgfree(ads, res);
3444 return ADS_SUCCESS;
3448 * find our site name
3449 * @param ads connection to ads server
3450 * @param mem_ctx Pointer to talloc context
3451 * @param site_name Pointer to the sitename
3452 * @return status of search
3454 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3456 ADS_STATUS status;
3457 LDAPMessage *res;
3458 const char *dn, *service_name;
3459 const char *attrs[] = { "dsServiceName", NULL };
3461 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3462 if (!ADS_ERR_OK(status)) {
3463 return status;
3466 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3467 if (service_name == NULL) {
3468 ads_msgfree(ads, res);
3469 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3472 ads_msgfree(ads, res);
3474 /* go up three levels */
3475 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3476 if (dn == NULL) {
3477 return ADS_ERROR(LDAP_NO_MEMORY);
3480 *site_name = talloc_strdup(mem_ctx, dn);
3481 if (*site_name == NULL) {
3482 return ADS_ERROR(LDAP_NO_MEMORY);
3485 return status;
3487 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3492 * find the site dn where a machine resides
3493 * @param ads connection to ads server
3494 * @param mem_ctx Pointer to talloc context
3495 * @param computer_name name of the machine
3496 * @param site_name Pointer to the sitename
3497 * @return status of search
3499 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3501 ADS_STATUS status;
3502 LDAPMessage *res;
3503 const char *parent, *filter;
3504 char *config_context = NULL;
3505 char *dn;
3507 /* shortcut a query */
3508 if (strequal(computer_name, ads->config.ldap_server_name)) {
3509 return ads_site_dn(ads, mem_ctx, site_dn);
3512 status = ads_config_path(ads, mem_ctx, &config_context);
3513 if (!ADS_ERR_OK(status)) {
3514 return status;
3517 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3518 if (filter == NULL) {
3519 return ADS_ERROR(LDAP_NO_MEMORY);
3522 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3523 filter, NULL, &res);
3524 if (!ADS_ERR_OK(status)) {
3525 return status;
3528 if (ads_count_replies(ads, res) != 1) {
3529 ads_msgfree(ads, res);
3530 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3533 dn = ads_get_dn(ads, mem_ctx, res);
3534 if (dn == NULL) {
3535 ads_msgfree(ads, res);
3536 return ADS_ERROR(LDAP_NO_MEMORY);
3539 /* go up three levels */
3540 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3541 if (parent == NULL) {
3542 ads_msgfree(ads, res);
3543 TALLOC_FREE(dn);
3544 return ADS_ERROR(LDAP_NO_MEMORY);
3547 *site_dn = talloc_strdup(mem_ctx, parent);
3548 if (*site_dn == NULL) {
3549 ads_msgfree(ads, res);
3550 TALLOC_FREE(dn);
3551 return ADS_ERROR(LDAP_NO_MEMORY);
3554 TALLOC_FREE(dn);
3555 ads_msgfree(ads, res);
3557 return status;
3561 * get the upn suffixes for a domain
3562 * @param ads connection to ads server
3563 * @param mem_ctx Pointer to talloc context
3564 * @param suffixes Pointer to an array of suffixes
3565 * @param num_suffixes Pointer to the number of suffixes
3566 * @return status of search
3568 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3570 ADS_STATUS status;
3571 LDAPMessage *res;
3572 const char *base;
3573 char *config_context = NULL;
3574 const char *attrs[] = { "uPNSuffixes", NULL };
3576 status = ads_config_path(ads, mem_ctx, &config_context);
3577 if (!ADS_ERR_OK(status)) {
3578 return status;
3581 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3582 if (base == NULL) {
3583 return ADS_ERROR(LDAP_NO_MEMORY);
3586 status = ads_search_dn(ads, &res, base, attrs);
3587 if (!ADS_ERR_OK(status)) {
3588 return status;
3591 if (ads_count_replies(ads, res) != 1) {
3592 ads_msgfree(ads, res);
3593 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3596 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3597 if ((*suffixes) == NULL) {
3598 ads_msgfree(ads, res);
3599 return ADS_ERROR(LDAP_NO_MEMORY);
3602 ads_msgfree(ads, res);
3604 return status;
3608 * get the joinable ous for a domain
3609 * @param ads connection to ads server
3610 * @param mem_ctx Pointer to talloc context
3611 * @param ous Pointer to an array of ous
3612 * @param num_ous Pointer to the number of ous
3613 * @return status of search
3615 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3616 TALLOC_CTX *mem_ctx,
3617 char ***ous,
3618 size_t *num_ous)
3620 ADS_STATUS status;
3621 LDAPMessage *res = NULL;
3622 LDAPMessage *msg = NULL;
3623 const char *attrs[] = { "dn", NULL };
3624 int count = 0;
3626 status = ads_search(ads, &res,
3627 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3628 attrs);
3629 if (!ADS_ERR_OK(status)) {
3630 return status;
3633 count = ads_count_replies(ads, res);
3634 if (count < 1) {
3635 ads_msgfree(ads, res);
3636 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3639 for (msg = ads_first_entry(ads, res); msg;
3640 msg = ads_next_entry(ads, msg)) {
3641 const char **p = discard_const_p(const char *, *ous);
3642 char *dn = NULL;
3644 dn = ads_get_dn(ads, talloc_tos(), msg);
3645 if (!dn) {
3646 ads_msgfree(ads, res);
3647 return ADS_ERROR(LDAP_NO_MEMORY);
3650 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3651 TALLOC_FREE(dn);
3652 ads_msgfree(ads, res);
3653 return ADS_ERROR(LDAP_NO_MEMORY);
3656 TALLOC_FREE(dn);
3657 *ous = discard_const_p(char *, p);
3660 ads_msgfree(ads, res);
3662 return status;
3667 * pull a struct dom_sid from an extended dn string
3668 * @param mem_ctx TALLOC_CTX
3669 * @param extended_dn string
3670 * @param flags string type of extended_dn
3671 * @param sid pointer to a struct dom_sid
3672 * @return NT_STATUS_OK on success,
3673 * NT_INVALID_PARAMETER on error,
3674 * NT_STATUS_NOT_FOUND if no SID present
3676 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3677 const char *extended_dn,
3678 enum ads_extended_dn_flags flags,
3679 struct dom_sid *sid)
3681 char *p, *q, *dn;
3683 if (!extended_dn) {
3684 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3687 /* otherwise extended_dn gets stripped off */
3688 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3689 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3692 * ADS_EXTENDED_DN_HEX_STRING:
3693 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3695 * ADS_EXTENDED_DN_STRING (only with w2k3):
3696 * <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
3698 * Object with no SID, such as an Exchange Public Folder
3699 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3702 p = strchr(dn, ';');
3703 if (!p) {
3704 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3707 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3708 DEBUG(5,("No SID present in extended dn\n"));
3709 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3712 p += strlen(";<SID=");
3714 q = strchr(p, '>');
3715 if (!q) {
3716 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3719 *q = '\0';
3721 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3723 switch (flags) {
3725 case ADS_EXTENDED_DN_STRING:
3726 if (!string_to_sid(sid, p)) {
3727 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3729 break;
3730 case ADS_EXTENDED_DN_HEX_STRING: {
3731 ssize_t ret;
3732 fstring buf;
3733 size_t buf_len;
3735 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3736 if (buf_len == 0) {
3737 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3740 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3741 if (ret == -1) {
3742 DEBUG(10,("failed to parse sid\n"));
3743 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3745 break;
3747 default:
3748 DEBUG(10,("unknown extended dn format\n"));
3749 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3752 return ADS_ERROR_NT(NT_STATUS_OK);
3755 /********************************************************************
3756 ********************************************************************/
3758 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3760 LDAPMessage *res = NULL;
3761 ADS_STATUS status;
3762 int count = 0;
3763 char *name = NULL;
3765 status = ads_find_machine_acct(ads, &res, machine_name);
3766 if (!ADS_ERR_OK(status)) {
3767 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3768 lp_netbios_name()));
3769 goto out;
3772 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3773 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3774 goto out;
3777 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3778 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3781 out:
3782 ads_msgfree(ads, res);
3784 return name;
3787 /********************************************************************
3788 ********************************************************************/
3790 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3791 LDAPMessage *msg, size_t *num_values)
3793 const char *field = "msDS-AdditionalDnsHostName";
3794 struct berval **values = NULL;
3795 char **ret = NULL;
3796 size_t i, converted_size;
3799 * Windows DC implicitly adds a short name for each FQDN added to
3800 * msDS-AdditionalDnsHostName, but it comes with a strage binary
3801 * suffix "\0$" which we should ignore (see bug #14406).
3804 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3805 if (values == NULL) {
3806 return NULL;
3809 *num_values = ldap_count_values_len(values);
3811 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3812 if (ret == NULL) {
3813 ldap_value_free_len(values);
3814 return NULL;
3817 for (i = 0; i < *num_values; i++) {
3818 ret[i] = NULL;
3819 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
3820 values[i]->bv_val,
3821 strnlen(values[i]->bv_val,
3822 values[i]->bv_len),
3823 &ret[i], &converted_size)) {
3824 ldap_value_free_len(values);
3825 return NULL;
3828 ret[i] = NULL;
3830 ldap_value_free_len(values);
3831 return ret;
3834 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
3835 ADS_STRUCT *ads,
3836 const char *machine_name,
3837 char ***hostnames_array,
3838 size_t *num_hostnames)
3840 ADS_STATUS status;
3841 LDAPMessage *res = NULL;
3842 int count;
3844 status = ads_find_machine_acct(ads,
3845 &res,
3846 machine_name);
3847 if (!ADS_ERR_OK(status)) {
3848 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
3849 machine_name));
3850 return status;
3853 count = ads_count_replies(ads, res);
3854 if (count != 1) {
3855 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3856 goto done;
3859 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
3860 if (*hostnames_array == NULL) {
3861 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
3862 machine_name));
3863 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3864 goto done;
3867 done:
3868 ads_msgfree(ads, res);
3870 return status;
3873 /********************************************************************
3874 ********************************************************************/
3876 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3878 LDAPMessage *res = NULL;
3879 ADS_STATUS status;
3880 int count = 0;
3881 char *name = NULL;
3883 status = ads_find_machine_acct(ads, &res, machine_name);
3884 if (!ADS_ERR_OK(status)) {
3885 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3886 lp_netbios_name()));
3887 goto out;
3890 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3891 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3892 goto out;
3895 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3896 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3899 out:
3900 ads_msgfree(ads, res);
3902 return name;
3905 /********************************************************************
3906 ********************************************************************/
3908 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3910 LDAPMessage *res = NULL;
3911 ADS_STATUS status;
3912 int count = 0;
3913 char *name = NULL;
3914 bool ok = false;
3916 status = ads_find_machine_acct(ads, &res, machine_name);
3917 if (!ADS_ERR_OK(status)) {
3918 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3919 lp_netbios_name()));
3920 goto out;
3923 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3924 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3925 goto out;
3928 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3929 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3932 out:
3933 ads_msgfree(ads, res);
3934 if (name != NULL) {
3935 ok = (strlen(name) > 0);
3937 TALLOC_FREE(name);
3938 return ok;
3941 #if 0
3943 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3946 * Join a machine to a realm
3947 * Creates the machine account and sets the machine password
3948 * @param ads connection to ads server
3949 * @param machine name of host to add
3950 * @param org_unit Organizational unit to place machine in
3951 * @return status of join
3953 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3954 uint32_t account_type, const char *org_unit)
3956 ADS_STATUS status;
3957 LDAPMessage *res = NULL;
3958 char *machine;
3960 /* machine name must be lowercase */
3961 machine = SMB_STRDUP(machine_name);
3962 strlower_m(machine);
3965 status = ads_find_machine_acct(ads, (void **)&res, machine);
3966 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3967 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3968 status = ads_leave_realm(ads, machine);
3969 if (!ADS_ERR_OK(status)) {
3970 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3971 machine, ads->config.realm));
3972 return status;
3976 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3977 if (!ADS_ERR_OK(status)) {
3978 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3979 SAFE_FREE(machine);
3980 return status;
3983 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3984 if (!ADS_ERR_OK(status)) {
3985 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3986 SAFE_FREE(machine);
3987 return status;
3990 SAFE_FREE(machine);
3991 ads_msgfree(ads, res);
3993 return status;
3995 #endif
3998 * Delete a machine from the realm
3999 * @param ads connection to ads server
4000 * @param hostname Machine to remove
4001 * @return status of delete
4003 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
4005 ADS_STATUS status;
4006 void *msg;
4007 LDAPMessage *res;
4008 char *hostnameDN, *host;
4009 int rc;
4010 LDAPControl ldap_control;
4011 LDAPControl * pldap_control[2] = {NULL, NULL};
4013 pldap_control[0] = &ldap_control;
4014 memset(&ldap_control, 0, sizeof(LDAPControl));
4015 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
4017 /* hostname must be lowercase */
4018 host = SMB_STRDUP(hostname);
4019 if (!strlower_m(host)) {
4020 SAFE_FREE(host);
4021 return ADS_ERROR_SYSTEM(EINVAL);
4024 status = ads_find_machine_acct(ads, &res, host);
4025 if (!ADS_ERR_OK(status)) {
4026 DEBUG(0, ("Host account for %s does not exist.\n", host));
4027 SAFE_FREE(host);
4028 return status;
4031 msg = ads_first_entry(ads, res);
4032 if (!msg) {
4033 SAFE_FREE(host);
4034 return ADS_ERROR_SYSTEM(ENOENT);
4037 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
4038 if (hostnameDN == NULL) {
4039 SAFE_FREE(host);
4040 return ADS_ERROR_SYSTEM(ENOENT);
4043 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
4044 if (rc) {
4045 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
4046 }else {
4047 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
4050 if (rc != LDAP_SUCCESS) {
4051 const char *attrs[] = { "cn", NULL };
4052 LDAPMessage *msg_sub;
4054 /* we only search with scope ONE, we do not expect any further
4055 * objects to be created deeper */
4057 status = ads_do_search_retry(ads, hostnameDN,
4058 LDAP_SCOPE_ONELEVEL,
4059 "(objectclass=*)", attrs, &res);
4061 if (!ADS_ERR_OK(status)) {
4062 SAFE_FREE(host);
4063 TALLOC_FREE(hostnameDN);
4064 return status;
4067 for (msg_sub = ads_first_entry(ads, res); msg_sub;
4068 msg_sub = ads_next_entry(ads, msg_sub)) {
4070 char *dn = NULL;
4072 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
4073 SAFE_FREE(host);
4074 TALLOC_FREE(hostnameDN);
4075 return ADS_ERROR(LDAP_NO_MEMORY);
4078 status = ads_del_dn(ads, dn);
4079 if (!ADS_ERR_OK(status)) {
4080 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
4081 SAFE_FREE(host);
4082 TALLOC_FREE(dn);
4083 TALLOC_FREE(hostnameDN);
4084 return status;
4087 TALLOC_FREE(dn);
4090 /* there should be no subordinate objects anymore */
4091 status = ads_do_search_retry(ads, hostnameDN,
4092 LDAP_SCOPE_ONELEVEL,
4093 "(objectclass=*)", attrs, &res);
4095 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
4096 SAFE_FREE(host);
4097 TALLOC_FREE(hostnameDN);
4098 return status;
4101 /* delete hostnameDN now */
4102 status = ads_del_dn(ads, hostnameDN);
4103 if (!ADS_ERR_OK(status)) {
4104 SAFE_FREE(host);
4105 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
4106 TALLOC_FREE(hostnameDN);
4107 return status;
4111 TALLOC_FREE(hostnameDN);
4113 status = ads_find_machine_acct(ads, &res, host);
4114 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4115 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4116 DEBUG(3, ("Failed to remove host account.\n"));
4117 SAFE_FREE(host);
4118 return status;
4121 SAFE_FREE(host);
4122 return ADS_SUCCESS;
4126 * pull all token-sids from an LDAP dn
4127 * @param ads connection to ads server
4128 * @param mem_ctx TALLOC_CTX for allocating sid array
4129 * @param dn of LDAP object
4130 * @param user_sid pointer to struct dom_sid (objectSid)
4131 * @param primary_group_sid pointer to struct dom_sid (self composed)
4132 * @param sids pointer to sid array to allocate
4133 * @param num_sids counter of SIDs pulled
4134 * @return status of token query
4136 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4137 TALLOC_CTX *mem_ctx,
4138 const char *dn,
4139 struct dom_sid *user_sid,
4140 struct dom_sid *primary_group_sid,
4141 struct dom_sid **sids,
4142 size_t *num_sids)
4144 ADS_STATUS status;
4145 LDAPMessage *res = NULL;
4146 int count = 0;
4147 size_t tmp_num_sids;
4148 struct dom_sid *tmp_sids;
4149 struct dom_sid tmp_user_sid;
4150 struct dom_sid tmp_primary_group_sid;
4151 uint32_t pgid;
4152 const char *attrs[] = {
4153 "objectSid",
4154 "tokenGroups",
4155 "primaryGroupID",
4156 NULL
4159 status = ads_search_retry_dn(ads, &res, dn, attrs);
4160 if (!ADS_ERR_OK(status)) {
4161 return status;
4164 count = ads_count_replies(ads, res);
4165 if (count != 1) {
4166 ads_msgfree(ads, res);
4167 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4170 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4171 ads_msgfree(ads, res);
4172 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4175 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4176 ads_msgfree(ads, res);
4177 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4181 /* hack to compose the primary group sid without knowing the
4182 * domsid */
4184 struct dom_sid domsid;
4186 sid_copy(&domsid, &tmp_user_sid);
4188 if (!sid_split_rid(&domsid, NULL)) {
4189 ads_msgfree(ads, res);
4190 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4193 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4194 ads_msgfree(ads, res);
4195 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4199 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4201 if (tmp_num_sids == 0 || !tmp_sids) {
4202 ads_msgfree(ads, res);
4203 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4206 if (num_sids) {
4207 *num_sids = tmp_num_sids;
4210 if (sids) {
4211 *sids = tmp_sids;
4214 if (user_sid) {
4215 *user_sid = tmp_user_sid;
4218 if (primary_group_sid) {
4219 *primary_group_sid = tmp_primary_group_sid;
4222 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4224 ads_msgfree(ads, res);
4225 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4229 * Find a sAMAccoutName in LDAP
4230 * @param ads connection to ads server
4231 * @param mem_ctx TALLOC_CTX for allocating sid array
4232 * @param samaccountname to search
4233 * @param uac_ret uint32_t pointer userAccountControl attribute value
4234 * @param dn_ret pointer to dn
4235 * @return status of token query
4237 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4238 TALLOC_CTX *mem_ctx,
4239 const char *samaccountname,
4240 uint32_t *uac_ret,
4241 const char **dn_ret)
4243 ADS_STATUS status;
4244 const char *attrs[] = { "userAccountControl", NULL };
4245 const char *filter;
4246 LDAPMessage *res = NULL;
4247 char *dn = NULL;
4248 uint32_t uac = 0;
4250 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4251 samaccountname);
4252 if (filter == NULL) {
4253 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4254 goto out;
4257 status = ads_do_search_all(ads, ads->config.bind_path,
4258 LDAP_SCOPE_SUBTREE,
4259 filter, attrs, &res);
4261 if (!ADS_ERR_OK(status)) {
4262 goto out;
4265 if (ads_count_replies(ads, res) != 1) {
4266 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4267 goto out;
4270 dn = ads_get_dn(ads, talloc_tos(), res);
4271 if (dn == NULL) {
4272 status = ADS_ERROR(LDAP_NO_MEMORY);
4273 goto out;
4276 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4277 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4278 goto out;
4281 if (uac_ret) {
4282 *uac_ret = uac;
4285 if (dn_ret) {
4286 *dn_ret = talloc_strdup(mem_ctx, dn);
4287 if (!*dn_ret) {
4288 status = ADS_ERROR(LDAP_NO_MEMORY);
4289 goto out;
4292 out:
4293 TALLOC_FREE(dn);
4294 ads_msgfree(ads, res);
4296 return status;
4300 * find our configuration path
4301 * @param ads connection to ads server
4302 * @param mem_ctx Pointer to talloc context
4303 * @param config_path Pointer to the config path
4304 * @return status of search
4306 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4307 TALLOC_CTX *mem_ctx,
4308 char **config_path)
4310 ADS_STATUS status;
4311 LDAPMessage *res = NULL;
4312 const char *config_context = NULL;
4313 const char *attrs[] = { "configurationNamingContext", NULL };
4315 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4316 "(objectclass=*)", attrs, &res);
4317 if (!ADS_ERR_OK(status)) {
4318 return status;
4321 config_context = ads_pull_string(ads, mem_ctx, res,
4322 "configurationNamingContext");
4323 ads_msgfree(ads, res);
4324 if (!config_context) {
4325 return ADS_ERROR(LDAP_NO_MEMORY);
4328 if (config_path) {
4329 *config_path = talloc_strdup(mem_ctx, config_context);
4330 if (!*config_path) {
4331 return ADS_ERROR(LDAP_NO_MEMORY);
4335 return ADS_ERROR(LDAP_SUCCESS);
4339 * find the displayName of an extended right
4340 * @param ads connection to ads server
4341 * @param config_path The config path
4342 * @param mem_ctx Pointer to talloc context
4343 * @param GUID struct of the rightsGUID
4344 * @return status of search
4346 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4347 const char *config_path,
4348 TALLOC_CTX *mem_ctx,
4349 const struct GUID *rights_guid)
4351 ADS_STATUS rc;
4352 LDAPMessage *res = NULL;
4353 char *expr = NULL;
4354 const char *attrs[] = { "displayName", NULL };
4355 const char *result = NULL;
4356 const char *path;
4358 if (!ads || !mem_ctx || !rights_guid) {
4359 goto done;
4362 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4363 GUID_string(mem_ctx, rights_guid));
4364 if (!expr) {
4365 goto done;
4368 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4369 if (!path) {
4370 goto done;
4373 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4374 expr, attrs, &res);
4375 if (!ADS_ERR_OK(rc)) {
4376 goto done;
4379 if (ads_count_replies(ads, res) != 1) {
4380 goto done;
4383 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4385 done:
4386 ads_msgfree(ads, res);
4387 return result;
4391 * verify or build and verify an account ou
4392 * @param mem_ctx Pointer to talloc context
4393 * @param ads connection to ads server
4394 * @param account_ou
4395 * @return status of search
4398 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4399 ADS_STRUCT *ads,
4400 const char **account_ou)
4402 char **exploded_dn;
4403 const char *name;
4404 char *ou_string;
4406 if (account_ou == NULL) {
4407 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4410 if (*account_ou != NULL) {
4411 exploded_dn = ldap_explode_dn(*account_ou, 0);
4412 if (exploded_dn) {
4413 ldap_value_free(exploded_dn);
4414 return ADS_SUCCESS;
4418 ou_string = ads_ou_string(ads, *account_ou);
4419 if (!ou_string) {
4420 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4423 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4424 ads->config.bind_path);
4425 SAFE_FREE(ou_string);
4427 if (!name) {
4428 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4431 exploded_dn = ldap_explode_dn(name, 0);
4432 if (!exploded_dn) {
4433 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4435 ldap_value_free(exploded_dn);
4437 *account_ou = name;
4438 return ADS_SUCCESS;
4441 #endif