tests/krb5: Clarify checksum type assertion message
[Samba.git] / source3 / libads / ldap.c
blobee4628a09a27ee637bf361fabfe1f254f248fc3e
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,const char *domain,
350 struct ip_service *ip_list, int count)
352 int i;
353 bool ok;
355 for (i = 0; i < count; i++) {
356 char server[INET6_ADDRSTRLEN];
358 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
360 if (!NT_STATUS_IS_OK(
361 check_negative_conn_cache(domain, server)))
362 continue;
364 /* Returns ok only if it matches the correct server type */
365 ok = ads_try_connect(ads, false, &ip_list[i].ss);
367 if (ok) {
368 return NT_STATUS_OK;
371 /* keep track of failures */
372 add_failed_connection_entry(domain, server,
373 NT_STATUS_UNSUCCESSFUL);
376 return NT_STATUS_NO_LOGON_SERVERS;
379 /***************************************************************************
380 resolve a name and perform an "ldap ping" using NetBIOS and related methods
381 ****************************************************************************/
383 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
384 const char *domain, const char *realm)
386 int count, i;
387 struct ip_service *ip_list;
388 NTSTATUS status;
390 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
391 domain));
393 status = get_sorted_dc_list(domain, NULL, &ip_list, &count,
394 false);
395 if (!NT_STATUS_IS_OK(status)) {
396 return status;
399 /* remove servers which are known to be dead based on
400 the corresponding DNS method */
401 if (*realm) {
402 for (i = 0; i < count; ++i) {
403 char server[INET6_ADDRSTRLEN];
405 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
407 if(!NT_STATUS_IS_OK(
408 check_negative_conn_cache(realm, server))) {
409 /* Ensure we add the workgroup name for this
410 IP address as negative too. */
411 add_failed_connection_entry(
412 domain, server,
413 NT_STATUS_UNSUCCESSFUL);
418 status = cldap_ping_list(ads, domain, ip_list, count);
420 SAFE_FREE(ip_list);
422 return status;
426 /**********************************************************************
427 resolve a name and perform an "ldap ping" using DNS
428 **********************************************************************/
430 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
431 const char *realm)
433 int count;
434 struct ip_service *ip_list = NULL;
435 NTSTATUS status;
437 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
438 realm));
440 status = get_sorted_dc_list(realm, sitename, &ip_list, &count,
441 true);
442 if (!NT_STATUS_IS_OK(status)) {
443 SAFE_FREE(ip_list);
444 return status;
447 status = cldap_ping_list(ads, realm, ip_list, count);
449 SAFE_FREE(ip_list);
451 return status;
454 /**********************************************************************
455 Try to find an AD dc using our internal name resolution routines
456 Try the realm first and then then workgroup name if netbios is not
457 disabled
458 **********************************************************************/
460 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
462 const char *c_domain = "";
463 const char *c_realm;
464 bool use_own_domain = False;
465 char *sitename = NULL;
466 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
467 bool ok = false;
469 /* if the realm and workgroup are both empty, assume they are ours */
471 /* realm */
472 c_realm = ads->server.realm;
474 if (c_realm == NULL)
475 c_realm = "";
477 if (!*c_realm) {
478 /* special case where no realm and no workgroup means our own */
479 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
480 use_own_domain = True;
481 c_realm = lp_realm();
485 if (!lp_disable_netbios()) {
486 if (use_own_domain) {
487 c_domain = lp_workgroup();
488 } else {
489 c_domain = ads->server.workgroup;
490 if (!*c_realm && (!c_domain || !*c_domain)) {
491 c_domain = lp_workgroup();
495 if (!c_domain) {
496 c_domain = "";
500 if (!*c_realm && !*c_domain) {
501 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
502 "what to do\n"));
503 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
507 * In case of LDAP we use get_dc_name() as that
508 * creates the custom krb5.conf file
510 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
511 fstring srv_name;
512 struct sockaddr_storage ip_out;
514 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
515 " and falling back to domain '%s'\n",
516 c_realm, c_domain));
518 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
519 if (ok) {
521 * we call ads_try_connect() to fill in the
522 * ads->config details
524 ok = ads_try_connect(ads, false, &ip_out);
525 if (ok) {
526 return NT_STATUS_OK;
530 return NT_STATUS_NO_LOGON_SERVERS;
533 if (*c_realm) {
534 sitename = sitename_fetch(talloc_tos(), c_realm);
535 status = resolve_and_ping_dns(ads, sitename, c_realm);
537 if (NT_STATUS_IS_OK(status)) {
538 TALLOC_FREE(sitename);
539 return status;
542 /* In case we failed to contact one of our closest DC on our
543 * site we
544 * need to try to find another DC, retry with a site-less SRV
545 * DNS query
546 * - Guenther */
548 if (sitename) {
549 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
550 "our site (%s), Trying to find another DC "
551 "for realm '%s' (domain '%s')\n",
552 sitename, c_realm, c_domain));
553 namecache_delete(c_realm, 0x1C);
554 status =
555 resolve_and_ping_dns(ads, NULL, c_realm);
557 if (NT_STATUS_IS_OK(status)) {
558 TALLOC_FREE(sitename);
559 return status;
563 TALLOC_FREE(sitename);
566 /* try netbios as fallback - if permitted,
567 or if configuration specifically requests it */
568 if (*c_domain) {
569 if (*c_realm) {
570 DEBUG(3, ("ads_find_dc: falling back to netbios "
571 "name resolution for domain '%s' (realm '%s')\n",
572 c_domain, c_realm));
575 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
576 if (NT_STATUS_IS_OK(status)) {
577 return status;
581 DEBUG(1, ("ads_find_dc: "
582 "name resolution for realm '%s' (domain '%s') failed: %s\n",
583 c_realm, c_domain, nt_errstr(status)));
584 return status;
587 * Connect to the LDAP server
588 * @param ads Pointer to an existing ADS_STRUCT
589 * @return status of connection
591 ADS_STATUS ads_connect(ADS_STRUCT *ads)
593 int version = LDAP_VERSION3;
594 ADS_STATUS status;
595 NTSTATUS ntstatus;
596 char addr[INET6_ADDRSTRLEN];
598 ZERO_STRUCT(ads->ldap);
599 ZERO_STRUCT(ads->ldap_wrap_data);
600 ads->ldap.last_attempt = time_mono(NULL);
601 ads->ldap_wrap_data.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
603 /* try with a user specified server */
605 if (DEBUGLEVEL >= 11) {
606 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
607 DEBUG(11,("ads_connect: entering\n"));
608 DEBUGADD(11,("%s\n", s));
609 TALLOC_FREE(s);
612 if (ads->server.ldap_server) {
613 bool ok = false;
614 struct sockaddr_storage ss;
616 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
617 if (!ok) {
618 DEBUG(5,("ads_connect: unable to resolve name %s\n",
619 ads->server.ldap_server));
620 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
621 goto out;
623 ok = ads_try_connect(ads, ads->server.gc, &ss);
624 if (ok) {
625 goto got_connection;
628 /* The choice of which GC use is handled one level up in
629 ads_connect_gc(). If we continue on from here with
630 ads_find_dc() we will get GC searches on port 389 which
631 doesn't work. --jerry */
633 if (ads->server.gc == true) {
634 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
637 if (ads->server.no_fallback) {
638 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
639 goto out;
643 ntstatus = ads_find_dc(ads);
644 if (NT_STATUS_IS_OK(ntstatus)) {
645 goto got_connection;
648 status = ADS_ERROR_NT(ntstatus);
649 goto out;
651 got_connection:
653 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
654 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
656 if (!ads->auth.user_name) {
657 /* Must use the userPrincipalName value here or sAMAccountName
658 and not servicePrincipalName; found by Guenther Deschner */
660 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
661 DEBUG(0,("ads_connect: asprintf fail.\n"));
662 ads->auth.user_name = NULL;
666 if (!ads->auth.realm) {
667 ads->auth.realm = SMB_STRDUP(ads->config.realm);
670 if (!ads->auth.kdc_server) {
671 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
672 ads->auth.kdc_server = SMB_STRDUP(addr);
675 /* If the caller() requested no LDAP bind, then we are done */
677 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
678 status = ADS_SUCCESS;
679 goto out;
682 ads->ldap_wrap_data.mem_ctx = talloc_init("ads LDAP connection memory");
683 if (!ads->ldap_wrap_data.mem_ctx) {
684 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
685 goto out;
688 /* Otherwise setup the TCP LDAP session */
690 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
691 &ads->ldap.ss,
692 ads->ldap.port, lp_ldap_timeout());
693 if (ads->ldap.ld == NULL) {
694 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
695 goto out;
697 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
699 /* cache the successful connection for workgroup and realm */
700 if (ads_closest_dc(ads)) {
701 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
702 saf_store( ads->server.realm, ads->config.ldap_server_name);
705 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
707 /* fill in the current time and offsets */
709 status = ads_current_time( ads );
710 if ( !ADS_ERR_OK(status) ) {
711 goto out;
714 /* Now do the bind */
716 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
717 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
718 goto out;
721 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
722 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
723 goto out;
726 status = ads_sasl_bind(ads);
728 out:
729 if (DEBUGLEVEL >= 11) {
730 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
731 DEBUG(11,("ads_connect: leaving with: %s\n",
732 ads_errstr(status)));
733 DEBUGADD(11,("%s\n", s));
734 TALLOC_FREE(s);
737 return status;
741 * Connect to the LDAP server using given credentials
742 * @param ads Pointer to an existing ADS_STRUCT
743 * @return status of connection
745 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
747 ads->auth.flags |= ADS_AUTH_USER_CREDS;
749 return ads_connect(ads);
753 * Disconnect the LDAP server
754 * @param ads Pointer to an existing ADS_STRUCT
756 void ads_disconnect(ADS_STRUCT *ads)
758 if (ads->ldap.ld) {
759 ldap_unbind(ads->ldap.ld);
760 ads->ldap.ld = NULL;
762 if (ads->ldap_wrap_data.wrap_ops &&
763 ads->ldap_wrap_data.wrap_ops->disconnect) {
764 ads->ldap_wrap_data.wrap_ops->disconnect(&ads->ldap_wrap_data);
766 if (ads->ldap_wrap_data.mem_ctx) {
767 talloc_free(ads->ldap_wrap_data.mem_ctx);
769 ZERO_STRUCT(ads->ldap);
770 ZERO_STRUCT(ads->ldap_wrap_data);
774 Duplicate a struct berval into talloc'ed memory
776 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
778 struct berval *value;
780 if (!in_val) return NULL;
782 value = talloc_zero(ctx, struct berval);
783 if (value == NULL)
784 return NULL;
785 if (in_val->bv_len == 0) return value;
787 value->bv_len = in_val->bv_len;
788 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
789 in_val->bv_len);
790 return value;
794 Make a values list out of an array of (struct berval *)
796 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
797 const struct berval **in_vals)
799 struct berval **values;
800 int i;
802 if (!in_vals) return NULL;
803 for (i=0; in_vals[i]; i++)
804 ; /* count values */
805 values = talloc_zero_array(ctx, struct berval *, i+1);
806 if (!values) return NULL;
808 for (i=0; in_vals[i]; i++) {
809 values[i] = dup_berval(ctx, in_vals[i]);
811 return values;
815 UTF8-encode a values list out of an array of (char *)
817 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
819 char **values;
820 int i;
821 size_t size;
823 if (!in_vals) return NULL;
824 for (i=0; in_vals[i]; i++)
825 ; /* count values */
826 values = talloc_zero_array(ctx, char *, i+1);
827 if (!values) return NULL;
829 for (i=0; in_vals[i]; i++) {
830 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
831 TALLOC_FREE(values);
832 return NULL;
835 return values;
839 Pull a (char *) array out of a UTF8-encoded values list
841 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
843 char **values;
844 int i;
845 size_t converted_size;
847 if (!in_vals) return NULL;
848 for (i=0; in_vals[i]; i++)
849 ; /* count values */
850 values = talloc_zero_array(ctx, char *, i+1);
851 if (!values) return NULL;
853 for (i=0; in_vals[i]; i++) {
854 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
855 &converted_size)) {
856 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
857 "%s", strerror(errno)));
860 return values;
864 * Do a search with paged results. cookie must be null on the first
865 * call, and then returned on each subsequent call. It will be null
866 * again when the entire search is complete
867 * @param ads connection to ads server
868 * @param bind_path Base dn for the search
869 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
870 * @param expr Search expression - specified in local charset
871 * @param attrs Attributes to retrieve - specified in utf8 or ascii
872 * @param res ** which will contain results - free res* with ads_msgfree()
873 * @param count Number of entries retrieved on this page
874 * @param cookie The paged results cookie to be returned on subsequent calls
875 * @return status of search
877 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
878 const char *bind_path,
879 int scope, const char *expr,
880 const char **attrs, void *args,
881 LDAPMessage **res,
882 int *count, struct berval **cookie)
884 int rc, i, version;
885 char *utf8_expr, *utf8_path, **search_attrs = NULL;
886 size_t converted_size;
887 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
888 BerElement *cookie_be = NULL;
889 struct berval *cookie_bv= NULL;
890 BerElement *ext_be = NULL;
891 struct berval *ext_bv= NULL;
893 TALLOC_CTX *ctx;
894 ads_control *external_control = (ads_control *) args;
896 *res = NULL;
898 if (!(ctx = talloc_init("ads_do_paged_search_args")))
899 return ADS_ERROR(LDAP_NO_MEMORY);
901 /* 0 means the conversion worked but the result was empty
902 so we only fail if it's -1. In any case, it always
903 at least nulls out the dest */
904 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
905 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
907 rc = LDAP_NO_MEMORY;
908 goto done;
911 if (!attrs || !(*attrs))
912 search_attrs = NULL;
913 else {
914 /* This would be the utf8-encoded version...*/
915 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
916 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
917 rc = LDAP_NO_MEMORY;
918 goto done;
922 /* Paged results only available on ldap v3 or later */
923 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
924 if (version < LDAP_VERSION3) {
925 rc = LDAP_NOT_SUPPORTED;
926 goto done;
929 cookie_be = ber_alloc_t(LBER_USE_DER);
930 if (*cookie) {
931 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
932 ber_bvfree(*cookie); /* don't need it from last time */
933 *cookie = NULL;
934 } else {
935 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
937 ber_flatten(cookie_be, &cookie_bv);
938 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
939 PagedResults.ldctl_iscritical = (char) 1;
940 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
941 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
943 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
944 NoReferrals.ldctl_iscritical = (char) 0;
945 NoReferrals.ldctl_value.bv_len = 0;
946 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
948 if (external_control &&
949 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
950 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
952 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
953 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
955 /* win2k does not accept a ldctl_value beeing passed in */
957 if (external_control->val != 0) {
959 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
960 rc = LDAP_NO_MEMORY;
961 goto done;
964 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
965 rc = LDAP_NO_MEMORY;
966 goto done;
968 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
969 rc = LDAP_NO_MEMORY;
970 goto done;
973 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
974 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
976 } else {
977 ExternalCtrl.ldctl_value.bv_len = 0;
978 ExternalCtrl.ldctl_value.bv_val = NULL;
981 controls[0] = &NoReferrals;
982 controls[1] = &PagedResults;
983 controls[2] = &ExternalCtrl;
984 controls[3] = NULL;
986 } else {
987 controls[0] = &NoReferrals;
988 controls[1] = &PagedResults;
989 controls[2] = NULL;
992 /* we need to disable referrals as the openldap libs don't
993 handle them and paged results at the same time. Using them
994 together results in the result record containing the server
995 page control being removed from the result list (tridge/jmcd)
997 leaving this in despite the control that says don't generate
998 referrals, in case the server doesn't support it (jmcd)
1000 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1002 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1003 search_attrs, 0, controls,
1004 NULL, LDAP_NO_LIMIT,
1005 (LDAPMessage **)res);
1007 ber_free(cookie_be, 1);
1008 ber_bvfree(cookie_bv);
1010 if (rc) {
1011 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1012 ldap_err2string(rc)));
1013 if (rc == LDAP_OTHER) {
1014 char *ldap_errmsg;
1015 int ret;
1017 ret = ldap_parse_result(ads->ldap.ld,
1018 *res,
1019 NULL,
1020 NULL,
1021 &ldap_errmsg,
1022 NULL,
1023 NULL,
1025 if (ret == LDAP_SUCCESS) {
1026 DEBUG(3, ("ldap_search_with_timeout(%s) "
1027 "error: %s\n", expr, ldap_errmsg));
1028 ldap_memfree(ldap_errmsg);
1031 goto done;
1034 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1035 NULL, &rcontrols, 0);
1037 if (!rcontrols) {
1038 goto done;
1041 for (i=0; rcontrols[i]; i++) {
1042 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1043 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1044 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1045 &cookie_bv);
1046 /* the berval is the cookie, but must be freed when
1047 it is all done */
1048 if (cookie_bv->bv_len) /* still more to do */
1049 *cookie=ber_bvdup(cookie_bv);
1050 else
1051 *cookie=NULL;
1052 ber_bvfree(cookie_bv);
1053 ber_free(cookie_be, 1);
1054 break;
1057 ldap_controls_free(rcontrols);
1059 done:
1060 talloc_destroy(ctx);
1062 if (ext_be) {
1063 ber_free(ext_be, 1);
1066 if (ext_bv) {
1067 ber_bvfree(ext_bv);
1070 if (rc != LDAP_SUCCESS && *res != NULL) {
1071 ads_msgfree(ads, *res);
1072 *res = NULL;
1075 /* if/when we decide to utf8-encode attrs, take out this next line */
1076 TALLOC_FREE(search_attrs);
1078 return ADS_ERROR(rc);
1081 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1082 int scope, const char *expr,
1083 const char **attrs, LDAPMessage **res,
1084 int *count, struct berval **cookie)
1086 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1091 * Get all results for a search. This uses ads_do_paged_search() to return
1092 * all entries in a large search.
1093 * @param ads connection to ads server
1094 * @param bind_path Base dn for the search
1095 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1096 * @param expr Search expression
1097 * @param attrs Attributes to retrieve
1098 * @param res ** which will contain results - free res* with ads_msgfree()
1099 * @return status of search
1101 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1102 int scope, const char *expr,
1103 const char **attrs, void *args,
1104 LDAPMessage **res)
1106 struct berval *cookie = NULL;
1107 int count = 0;
1108 ADS_STATUS status;
1110 *res = NULL;
1111 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1112 &count, &cookie);
1114 if (!ADS_ERR_OK(status))
1115 return status;
1117 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1118 while (cookie) {
1119 LDAPMessage *res2 = NULL;
1120 LDAPMessage *msg, *next;
1122 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1123 attrs, args, &res2, &count, &cookie);
1124 if (!ADS_ERR_OK(status)) {
1125 break;
1128 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1129 that this works on all ldap libs, but I have only tested with openldap */
1130 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1131 next = ads_next_message(ads, msg);
1132 ldap_add_result_entry((LDAPMessage **)res, msg);
1134 /* note that we do not free res2, as the memory is now
1135 part of the main returned list */
1137 #else
1138 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1139 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1140 #endif
1142 return status;
1145 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1146 int scope, const char *expr,
1147 const char **attrs, LDAPMessage **res)
1149 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1152 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1153 int scope, const char *expr,
1154 const char **attrs, uint32_t sd_flags,
1155 LDAPMessage **res)
1157 ads_control args;
1159 args.control = ADS_SD_FLAGS_OID;
1160 args.val = sd_flags;
1161 args.critical = True;
1163 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1168 * Run a function on all results for a search. Uses ads_do_paged_search() and
1169 * runs the function as each page is returned, using ads_process_results()
1170 * @param ads connection to ads server
1171 * @param bind_path Base dn for the search
1172 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1173 * @param expr Search expression - specified in local charset
1174 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1175 * @param fn Function which takes attr name, values list, and data_area
1176 * @param data_area Pointer which is passed to function on each call
1177 * @return status of search
1179 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1180 int scope, const char *expr, const char **attrs,
1181 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1182 void *data_area)
1184 struct berval *cookie = NULL;
1185 int count = 0;
1186 ADS_STATUS status;
1187 LDAPMessage *res;
1189 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1190 &count, &cookie);
1192 if (!ADS_ERR_OK(status)) return status;
1194 ads_process_results(ads, res, fn, data_area);
1195 ads_msgfree(ads, res);
1197 while (cookie) {
1198 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1199 &res, &count, &cookie);
1201 if (!ADS_ERR_OK(status)) break;
1203 ads_process_results(ads, res, fn, data_area);
1204 ads_msgfree(ads, res);
1207 return status;
1211 * Do a search with a timeout.
1212 * @param ads connection to ads server
1213 * @param bind_path Base dn for the search
1214 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1215 * @param expr Search expression
1216 * @param attrs Attributes to retrieve
1217 * @param res ** which will contain results - free res* with ads_msgfree()
1218 * @return status of search
1220 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1221 const char *expr,
1222 const char **attrs, LDAPMessage **res)
1224 int rc;
1225 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1226 size_t converted_size;
1227 TALLOC_CTX *ctx;
1229 *res = NULL;
1230 if (!(ctx = talloc_init("ads_do_search"))) {
1231 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1232 return ADS_ERROR(LDAP_NO_MEMORY);
1235 /* 0 means the conversion worked but the result was empty
1236 so we only fail if it's negative. In any case, it always
1237 at least nulls out the dest */
1238 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1239 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1241 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1242 rc = LDAP_NO_MEMORY;
1243 goto done;
1246 if (!attrs || !(*attrs))
1247 search_attrs = NULL;
1248 else {
1249 /* This would be the utf8-encoded version...*/
1250 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1251 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1253 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1254 rc = LDAP_NO_MEMORY;
1255 goto done;
1259 /* see the note in ads_do_paged_search - we *must* disable referrals */
1260 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1262 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1263 search_attrs, 0, NULL, NULL,
1264 LDAP_NO_LIMIT,
1265 (LDAPMessage **)res);
1267 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1268 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1269 rc = 0;
1272 done:
1273 talloc_destroy(ctx);
1274 /* if/when we decide to utf8-encode attrs, take out this next line */
1275 TALLOC_FREE(search_attrs);
1276 return ADS_ERROR(rc);
1279 * Do a general ADS search
1280 * @param ads connection to ads server
1281 * @param res ** which will contain results - free res* with ads_msgfree()
1282 * @param expr Search expression
1283 * @param attrs Attributes to retrieve
1284 * @return status of search
1286 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1287 const char *expr, const char **attrs)
1289 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1290 expr, attrs, res);
1294 * Do a search on a specific DistinguishedName
1295 * @param ads connection to ads server
1296 * @param res ** which will contain results - free res* with ads_msgfree()
1297 * @param dn DistinguishName to search
1298 * @param attrs Attributes to retrieve
1299 * @return status of search
1301 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1302 const char *dn, const char **attrs)
1304 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1305 attrs, res);
1309 * Free up memory from a ads_search
1310 * @param ads connection to ads server
1311 * @param msg Search results to free
1313 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1315 if (!msg) return;
1316 ldap_msgfree(msg);
1320 * Get a dn from search results
1321 * @param ads connection to ads server
1322 * @param msg Search result
1323 * @return dn string
1325 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1327 char *utf8_dn, *unix_dn;
1328 size_t converted_size;
1330 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1332 if (!utf8_dn) {
1333 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1334 return NULL;
1337 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1338 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1339 utf8_dn ));
1340 return NULL;
1342 ldap_memfree(utf8_dn);
1343 return unix_dn;
1347 * Get the parent from a dn
1348 * @param dn the dn to return the parent from
1349 * @return parent dn string
1351 char *ads_parent_dn(const char *dn)
1353 char *p;
1355 if (dn == NULL) {
1356 return NULL;
1359 p = strchr(dn, ',');
1361 if (p == NULL) {
1362 return NULL;
1365 return p+1;
1369 * Find a machine account given a hostname
1370 * @param ads connection to ads server
1371 * @param res ** which will contain results - free res* with ads_msgfree()
1372 * @param host Hostname to search for
1373 * @return status of search
1375 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1376 const char *machine)
1378 ADS_STATUS status;
1379 char *expr;
1380 const char *attrs[] = {
1381 /* This is how Windows checks for machine accounts */
1382 "objectClass",
1383 "SamAccountName",
1384 "userAccountControl",
1385 "DnsHostName",
1386 "ServicePrincipalName",
1387 "userPrincipalName",
1388 "unicodePwd",
1390 /* Additional attributes Samba checks */
1391 "msDS-AdditionalDnsHostName",
1392 "msDS-SupportedEncryptionTypes",
1393 "nTSecurityDescriptor",
1395 NULL
1397 TALLOC_CTX *frame = talloc_stackframe();
1399 *res = NULL;
1401 /* the easiest way to find a machine account anywhere in the tree
1402 is to look for hostname$ */
1403 expr = talloc_asprintf(frame, "(samAccountName=%s$)", machine);
1404 if (expr == NULL) {
1405 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1406 goto done;
1409 status = ads_search(ads, res, expr, attrs);
1410 if (ADS_ERR_OK(status)) {
1411 if (ads_count_replies(ads, *res) != 1) {
1412 status = ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
1416 done:
1417 TALLOC_FREE(frame);
1418 return status;
1422 * Initialize a list of mods to be used in a modify request
1423 * @param ctx An initialized TALLOC_CTX
1424 * @return allocated ADS_MODLIST
1426 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1428 #define ADS_MODLIST_ALLOC_SIZE 10
1429 LDAPMod **mods;
1431 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1432 /* -1 is safety to make sure we don't go over the end.
1433 need to reset it to NULL before doing ldap modify */
1434 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1436 return (ADS_MODLIST)mods;
1441 add an attribute to the list, with values list already constructed
1443 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1444 int mod_op, const char *name,
1445 const void *_invals)
1447 int curmod;
1448 LDAPMod **modlist = (LDAPMod **) *mods;
1449 struct berval **ber_values = NULL;
1450 char **char_values = NULL;
1452 if (!_invals) {
1453 mod_op = LDAP_MOD_DELETE;
1454 } else {
1455 if (mod_op & LDAP_MOD_BVALUES) {
1456 const struct berval **b;
1457 b = discard_const_p(const struct berval *, _invals);
1458 ber_values = ads_dup_values(ctx, b);
1459 } else {
1460 const char **c;
1461 c = discard_const_p(const char *, _invals);
1462 char_values = ads_push_strvals(ctx, c);
1466 /* find the first empty slot */
1467 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1468 curmod++);
1469 if (modlist[curmod] == (LDAPMod *) -1) {
1470 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1471 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1472 return ADS_ERROR(LDAP_NO_MEMORY);
1473 memset(&modlist[curmod], 0,
1474 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1475 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1476 *mods = (ADS_MODLIST)modlist;
1479 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1480 return ADS_ERROR(LDAP_NO_MEMORY);
1481 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1482 if (mod_op & LDAP_MOD_BVALUES) {
1483 modlist[curmod]->mod_bvalues = ber_values;
1484 } else if (mod_op & LDAP_MOD_DELETE) {
1485 modlist[curmod]->mod_values = NULL;
1486 } else {
1487 modlist[curmod]->mod_values = char_values;
1490 modlist[curmod]->mod_op = mod_op;
1491 return ADS_ERROR(LDAP_SUCCESS);
1495 * Add a single string value to a mod list
1496 * @param ctx An initialized TALLOC_CTX
1497 * @param mods An initialized ADS_MODLIST
1498 * @param name The attribute name to add
1499 * @param val The value to add - NULL means DELETE
1500 * @return ADS STATUS indicating success of add
1502 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1503 const char *name, const char *val)
1505 const char *values[2];
1507 values[0] = val;
1508 values[1] = NULL;
1510 if (!val)
1511 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1512 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1516 * Add an array of string values to a mod list
1517 * @param ctx An initialized TALLOC_CTX
1518 * @param mods An initialized ADS_MODLIST
1519 * @param name The attribute name to add
1520 * @param vals The array of string values to add - NULL means DELETE
1521 * @return ADS STATUS indicating success of add
1523 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1524 const char *name, const char **vals)
1526 if (!vals)
1527 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1528 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1529 name, (const void **) vals);
1533 * Add a single ber-encoded value to a mod list
1534 * @param ctx An initialized TALLOC_CTX
1535 * @param mods An initialized ADS_MODLIST
1536 * @param name The attribute name to add
1537 * @param val The value to add - NULL means DELETE
1538 * @return ADS STATUS indicating success of add
1540 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1541 const char *name, const struct berval *val)
1543 const struct berval *values[2];
1545 values[0] = val;
1546 values[1] = NULL;
1547 if (!val)
1548 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1549 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1550 name, (const void **) values);
1553 static void ads_print_error(int ret, LDAP *ld)
1555 if (ret != 0) {
1556 char *ld_error = NULL;
1557 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1558 DBG_ERR("AD LDAP ERROR: %d (%s): %s\n",
1559 ret,
1560 ldap_err2string(ret),
1561 ld_error);
1562 SAFE_FREE(ld_error);
1567 * Perform an ldap modify
1568 * @param ads connection to ads server
1569 * @param mod_dn DistinguishedName to modify
1570 * @param mods list of modifications to perform
1571 * @return status of modify
1573 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1575 int ret,i;
1576 char *utf8_dn = NULL;
1577 size_t converted_size;
1579 this control is needed to modify that contains a currently
1580 non-existent attribute (but allowable for the object) to run
1582 LDAPControl PermitModify = {
1583 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1584 {0, NULL},
1585 (char) 1};
1586 LDAPControl *controls[2];
1588 DBG_INFO("AD LDAP: Modifying %s\n", mod_dn);
1590 controls[0] = &PermitModify;
1591 controls[1] = NULL;
1593 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1594 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1597 /* find the end of the list, marked by NULL or -1 */
1598 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1599 /* make sure the end of the list is NULL */
1600 mods[i] = NULL;
1601 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1602 (LDAPMod **) mods, controls, NULL);
1603 ads_print_error(ret, ads->ldap.ld);
1604 TALLOC_FREE(utf8_dn);
1605 return ADS_ERROR(ret);
1609 * Perform an ldap add
1610 * @param ads connection to ads server
1611 * @param new_dn DistinguishedName to add
1612 * @param mods list of attributes and values for DN
1613 * @return status of add
1615 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1617 int ret, i;
1618 char *utf8_dn = NULL;
1619 size_t converted_size;
1621 DBG_INFO("AD LDAP: Adding %s\n", new_dn);
1623 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1624 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1625 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1628 /* find the end of the list, marked by NULL or -1 */
1629 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1630 /* make sure the end of the list is NULL */
1631 mods[i] = NULL;
1633 ret = ldap_add_ext_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods, NULL, NULL);
1634 ads_print_error(ret, ads->ldap.ld);
1635 TALLOC_FREE(utf8_dn);
1636 return ADS_ERROR(ret);
1640 * Delete a DistinguishedName
1641 * @param ads connection to ads server
1642 * @param new_dn DistinguishedName to delete
1643 * @return status of delete
1645 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1647 int ret;
1648 char *utf8_dn = NULL;
1649 size_t converted_size;
1650 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1651 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1652 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1655 DBG_INFO("AD LDAP: Deleting %s\n", del_dn);
1657 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1658 ads_print_error(ret, ads->ldap.ld);
1659 TALLOC_FREE(utf8_dn);
1660 return ADS_ERROR(ret);
1664 * Build an org unit string
1665 * if org unit is Computers or blank then assume a container, otherwise
1666 * assume a / separated list of organisational units.
1667 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1668 * @param ads connection to ads server
1669 * @param org_unit Organizational unit
1670 * @return org unit string - caller must free
1672 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1674 char *ret = NULL;
1676 if (!org_unit || !*org_unit) {
1678 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1680 /* samba4 might not yet respond to a wellknownobject-query */
1681 return ret ? ret : SMB_STRDUP("cn=Computers");
1684 if (strequal(org_unit, "Computers")) {
1685 return SMB_STRDUP("cn=Computers");
1688 /* jmcd: removed "\\" from the separation chars, because it is
1689 needed as an escape for chars like '#' which are valid in an
1690 OU name */
1691 return ads_build_path(org_unit, "/", "ou=", 1);
1695 * Get a org unit string for a well-known GUID
1696 * @param ads connection to ads server
1697 * @param wknguid Well known GUID
1698 * @return org unit string - caller must free
1700 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1702 ADS_STATUS status;
1703 LDAPMessage *res = NULL;
1704 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1705 **bind_dn_exp = NULL;
1706 const char *attrs[] = {"distinguishedName", NULL};
1707 int new_ln, wkn_ln, bind_ln, i;
1709 if (wknguid == NULL) {
1710 return NULL;
1713 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1714 DEBUG(1, ("asprintf failed!\n"));
1715 return NULL;
1718 status = ads_search_dn(ads, &res, base, attrs);
1719 if (!ADS_ERR_OK(status)) {
1720 DEBUG(1,("Failed while searching for: %s\n", base));
1721 goto out;
1724 if (ads_count_replies(ads, res) != 1) {
1725 goto out;
1728 /* substitute the bind-path from the well-known-guid-search result */
1729 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1730 if (!wkn_dn) {
1731 goto out;
1734 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1735 if (!wkn_dn_exp) {
1736 goto out;
1739 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1740 if (!bind_dn_exp) {
1741 goto out;
1744 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1746 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1749 new_ln = wkn_ln - bind_ln;
1751 ret = SMB_STRDUP(wkn_dn_exp[0]);
1752 if (!ret) {
1753 goto out;
1756 for (i=1; i < new_ln; i++) {
1757 char *s = NULL;
1759 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1760 SAFE_FREE(ret);
1761 goto out;
1764 SAFE_FREE(ret);
1765 ret = SMB_STRDUP(s);
1766 free(s);
1767 if (!ret) {
1768 goto out;
1772 out:
1773 SAFE_FREE(base);
1774 ads_msgfree(ads, res);
1775 TALLOC_FREE(wkn_dn);
1776 if (wkn_dn_exp) {
1777 ldap_value_free(wkn_dn_exp);
1779 if (bind_dn_exp) {
1780 ldap_value_free(bind_dn_exp);
1783 return ret;
1787 * Adds (appends) an item to an attribute array, rather then
1788 * replacing the whole list
1789 * @param ctx An initialized TALLOC_CTX
1790 * @param mods An initialized ADS_MODLIST
1791 * @param name name of the ldap attribute to append to
1792 * @param vals an array of values to add
1793 * @return status of addition
1796 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1797 const char *name, const char **vals)
1799 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1800 (const void *) vals);
1804 * Determines the an account's current KVNO via an LDAP lookup
1805 * @param ads An initialized ADS_STRUCT
1806 * @param account_name the NT samaccountname.
1807 * @return the kvno for the account, or -1 in case of a failure.
1810 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1812 LDAPMessage *res = NULL;
1813 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1814 char *filter;
1815 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1816 char *dn_string = NULL;
1817 ADS_STATUS ret;
1819 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1820 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1821 return kvno;
1823 ret = ads_search(ads, &res, filter, attrs);
1824 SAFE_FREE(filter);
1825 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1826 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1827 ads_msgfree(ads, res);
1828 return kvno;
1831 dn_string = ads_get_dn(ads, talloc_tos(), res);
1832 if (!dn_string) {
1833 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1834 ads_msgfree(ads, res);
1835 return kvno;
1837 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1838 TALLOC_FREE(dn_string);
1840 /* ---------------------------------------------------------
1841 * 0 is returned as a default KVNO from this point on...
1842 * This is done because Windows 2000 does not support key
1843 * version numbers. Chances are that a failure in the next
1844 * step is simply due to Windows 2000 being used for a
1845 * domain controller. */
1846 kvno = 0;
1848 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1849 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1850 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1851 ads_msgfree(ads, res);
1852 return kvno;
1855 /* Success */
1856 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1857 ads_msgfree(ads, res);
1858 return kvno;
1862 * Determines the computer account's current KVNO via an LDAP lookup
1863 * @param ads An initialized ADS_STRUCT
1864 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1865 * @return the kvno for the computer account, or -1 in case of a failure.
1868 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1870 char *computer_account = NULL;
1871 uint32_t kvno = -1;
1873 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1874 return kvno;
1877 kvno = ads_get_kvno(ads, computer_account);
1878 free(computer_account);
1880 return kvno;
1884 * This clears out all registered spn's for a given hostname
1885 * @param ads An initilaized ADS_STRUCT
1886 * @param machine_name the NetBIOS name of the computer.
1887 * @return 0 upon success, non-zero otherwise.
1890 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1892 TALLOC_CTX *ctx;
1893 LDAPMessage *res = NULL;
1894 ADS_MODLIST mods;
1895 const char *servicePrincipalName[1] = {NULL};
1896 ADS_STATUS ret;
1897 char *dn_string = NULL;
1899 ret = ads_find_machine_acct(ads, &res, machine_name);
1900 if (!ADS_ERR_OK(ret)) {
1901 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1902 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1903 ads_msgfree(ads, res);
1904 return ret;
1907 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1908 ctx = talloc_init("ads_clear_service_principal_names");
1909 if (!ctx) {
1910 ads_msgfree(ads, res);
1911 return ADS_ERROR(LDAP_NO_MEMORY);
1914 if (!(mods = ads_init_mods(ctx))) {
1915 talloc_destroy(ctx);
1916 ads_msgfree(ads, res);
1917 return ADS_ERROR(LDAP_NO_MEMORY);
1919 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1920 if (!ADS_ERR_OK(ret)) {
1921 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1922 ads_msgfree(ads, res);
1923 talloc_destroy(ctx);
1924 return ret;
1926 dn_string = ads_get_dn(ads, talloc_tos(), res);
1927 if (!dn_string) {
1928 talloc_destroy(ctx);
1929 ads_msgfree(ads, res);
1930 return ADS_ERROR(LDAP_NO_MEMORY);
1932 ret = ads_gen_mod(ads, dn_string, mods);
1933 TALLOC_FREE(dn_string);
1934 if (!ADS_ERR_OK(ret)) {
1935 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1936 machine_name));
1937 ads_msgfree(ads, res);
1938 talloc_destroy(ctx);
1939 return ret;
1942 ads_msgfree(ads, res);
1943 talloc_destroy(ctx);
1944 return ret;
1948 * @brief Search for an element in a string array.
1950 * @param[in] el_array The string array to search.
1952 * @param[in] num_el The number of elements in the string array.
1954 * @param[in] el The string to search.
1956 * @return True if found, false if not.
1958 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1960 size_t i;
1962 if (el_array == NULL || num_el == 0 || el == NULL) {
1963 return false;
1966 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1967 int cmp;
1969 cmp = strcasecmp_m(el_array[i], el);
1970 if (cmp == 0) {
1971 return true;
1975 return false;
1979 * @brief This gets the service principal names of an existing computer account.
1981 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1983 * @param[in] ads The ADS context to use.
1985 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1986 * identify the computer account.
1988 * @param[in] spn_array A pointer to store the array for SPNs.
1990 * @param[in] num_spns The number of principals stored in the array.
1992 * @return 0 on success, or a ADS error if a failure occurred.
1994 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1995 ADS_STRUCT *ads,
1996 const char *machine_name,
1997 char ***spn_array,
1998 size_t *num_spns)
2000 ADS_STATUS status;
2001 LDAPMessage *res = NULL;
2002 int count;
2004 status = ads_find_machine_acct(ads,
2005 &res,
2006 machine_name);
2007 if (!ADS_ERR_OK(status)) {
2008 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
2009 machine_name));
2010 return status;
2013 count = ads_count_replies(ads, res);
2014 if (count != 1) {
2015 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2016 goto done;
2019 *spn_array = ads_pull_strings(ads,
2020 mem_ctx,
2021 res,
2022 "servicePrincipalName",
2023 num_spns);
2024 if (*spn_array == NULL) {
2025 DEBUG(1, ("Host account for %s does not have service principal "
2026 "names.\n",
2027 machine_name));
2028 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2029 goto done;
2032 done:
2033 ads_msgfree(ads, res);
2035 return status;
2039 * This adds a service principal name to an existing computer account
2040 * (found by hostname) in AD.
2041 * @param ads An initialized ADS_STRUCT
2042 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2043 * @param spns An array or strings for the service principals to add,
2044 * i.e. 'cifs/machine_name', 'http/machine.full.domain.com' etc.
2045 * @return 0 upon sucess, or non-zero if a failure occurs
2048 ADS_STATUS ads_add_service_principal_names(ADS_STRUCT *ads,
2049 const char *machine_name,
2050 const char **spns)
2052 ADS_STATUS ret;
2053 TALLOC_CTX *ctx;
2054 LDAPMessage *res = NULL;
2055 ADS_MODLIST mods;
2056 char *dn_string = NULL;
2057 const char **servicePrincipalName = spns;
2059 ret = ads_find_machine_acct(ads, &res, machine_name);
2060 if (!ADS_ERR_OK(ret)) {
2061 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2062 machine_name));
2063 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principals have NOT been added.\n"));
2064 ads_msgfree(ads, res);
2065 return ret;
2068 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2069 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2070 ads_msgfree(ads, res);
2071 return ADS_ERROR(LDAP_NO_MEMORY);
2074 DEBUG(5,("ads_add_service_principal_name: INFO: "
2075 "Adding %s to host %s\n",
2076 spns[0] ? "N/A" : spns[0], machine_name));
2079 DEBUG(5,("ads_add_service_principal_name: INFO: "
2080 "Adding %s to host %s\n",
2081 spns[1] ? "N/A" : spns[1], machine_name));
2083 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2084 ret = ADS_ERROR(LDAP_NO_MEMORY);
2085 goto out;
2088 ret = ads_add_strlist(ctx,
2089 &mods,
2090 "servicePrincipalName",
2091 servicePrincipalName);
2092 if (!ADS_ERR_OK(ret)) {
2093 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2094 goto out;
2097 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2098 ret = ADS_ERROR(LDAP_NO_MEMORY);
2099 goto out;
2102 ret = ads_gen_mod(ads, dn_string, mods);
2103 if (!ADS_ERR_OK(ret)) {
2104 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2105 goto out;
2108 out:
2109 TALLOC_FREE( ctx );
2110 ads_msgfree(ads, res);
2111 return ret;
2114 static uint32_t ads_get_acct_ctrl(ADS_STRUCT *ads,
2115 LDAPMessage *msg)
2117 uint32_t acct_ctrl = 0;
2118 bool ok;
2120 ok = ads_pull_uint32(ads, msg, "userAccountControl", &acct_ctrl);
2121 if (!ok) {
2122 return 0;
2125 return acct_ctrl;
2128 static ADS_STATUS ads_change_machine_acct(ADS_STRUCT *ads,
2129 LDAPMessage *msg,
2130 const struct berval *machine_pw_val)
2132 ADS_MODLIST mods;
2133 ADS_STATUS ret;
2134 TALLOC_CTX *frame = talloc_stackframe();
2135 uint32_t acct_control;
2136 char *control_str = NULL;
2137 const char *attrs[] = {
2138 "objectSid",
2139 NULL
2141 LDAPMessage *res = NULL;
2142 char *dn = NULL;
2144 dn = ads_get_dn(ads, frame, msg);
2145 if (dn == NULL) {
2146 ret = ADS_ERROR(LDAP_NO_MEMORY);
2147 goto done;
2150 acct_control = ads_get_acct_ctrl(ads, msg);
2151 if (acct_control == 0) {
2152 ret = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2153 goto done;
2157 * Changing the password, disables the account. So we need to change the
2158 * userAccountControl flags to enable it again.
2160 mods = ads_init_mods(frame);
2161 if (mods == NULL) {
2162 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2163 goto done;
2166 ads_mod_ber(frame, &mods, "unicodePwd", machine_pw_val);
2168 ret = ads_gen_mod(ads, dn, mods);
2169 if (!ADS_ERR_OK(ret)) {
2170 goto done;
2172 TALLOC_FREE(mods);
2175 * To activate the account, we need to disable and enable it.
2177 acct_control |= UF_ACCOUNTDISABLE;
2179 control_str = talloc_asprintf(frame, "%u", acct_control);
2180 if (control_str == NULL) {
2181 ret = ADS_ERROR(LDAP_NO_MEMORY);
2182 goto done;
2185 mods = ads_init_mods(frame);
2186 if (mods == NULL) {
2187 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2188 goto done;
2191 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2193 ret = ads_gen_mod(ads, dn, mods);
2194 if (!ADS_ERR_OK(ret)) {
2195 goto done;
2197 TALLOC_FREE(mods);
2198 TALLOC_FREE(control_str);
2201 * Enable the account again.
2203 acct_control &= ~UF_ACCOUNTDISABLE;
2205 control_str = talloc_asprintf(frame, "%u", acct_control);
2206 if (control_str == NULL) {
2207 ret = ADS_ERROR(LDAP_NO_MEMORY);
2208 goto done;
2211 mods = ads_init_mods(frame);
2212 if (mods == NULL) {
2213 ret = ADS_ERROR_LDAP(LDAP_NO_MEMORY);
2214 goto done;
2217 ads_mod_str(frame, &mods, "userAccountControl", control_str);
2219 ret = ads_gen_mod(ads, dn, mods);
2220 if (!ADS_ERR_OK(ret)) {
2221 goto done;
2223 TALLOC_FREE(mods);
2224 TALLOC_FREE(control_str);
2226 ret = ads_search_dn(ads, &res, dn, attrs);
2227 ads_msgfree(ads, res);
2229 done:
2230 talloc_free(frame);
2232 return ret;
2236 * adds a machine account to the ADS server
2237 * @param ads An intialized ADS_STRUCT
2238 * @param machine_name - the NetBIOS machine name of this account.
2239 * @param account_type A number indicating the type of account to create
2240 * @param org_unit The LDAP path in which to place this account
2241 * @return 0 upon success, or non-zero otherwise
2244 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2245 const char *machine_name,
2246 const char *machine_password,
2247 const char *org_unit,
2248 uint32_t etype_list,
2249 const char *dns_domain_name)
2251 ADS_STATUS ret;
2252 char *samAccountName = NULL;
2253 char *controlstr = NULL;
2254 TALLOC_CTX *ctx = NULL;
2255 ADS_MODLIST mods;
2256 char *machine_escaped = NULL;
2257 char *dns_hostname = NULL;
2258 char *new_dn = NULL;
2259 char *utf8_pw = NULL;
2260 size_t utf8_pw_len = 0;
2261 char *utf16_pw = NULL;
2262 size_t utf16_pw_len = 0;
2263 struct berval machine_pw_val;
2264 bool ok;
2265 const char **spn_array = NULL;
2266 size_t num_spns = 0;
2267 const char *spn_prefix[] = {
2268 "HOST",
2269 "RestrictedKrbHost",
2271 size_t i;
2272 LDAPMessage *res = NULL;
2273 uint32_t acct_control = UF_WORKSTATION_TRUST_ACCOUNT;
2275 ctx = talloc_init("ads_add_machine_acct");
2276 if (ctx == NULL) {
2277 return ADS_ERROR(LDAP_NO_MEMORY);
2280 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2281 if (machine_escaped == NULL) {
2282 ret = ADS_ERROR(LDAP_NO_MEMORY);
2283 goto done;
2286 utf8_pw = talloc_asprintf(ctx, "\"%s\"", machine_password);
2287 if (utf8_pw == NULL) {
2288 ret = ADS_ERROR(LDAP_NO_MEMORY);
2289 goto done;
2291 utf8_pw_len = strlen(utf8_pw);
2293 ok = convert_string_talloc(ctx,
2294 CH_UTF8, CH_UTF16MUNGED,
2295 utf8_pw, utf8_pw_len,
2296 (void *)&utf16_pw, &utf16_pw_len);
2297 if (!ok) {
2298 ret = ADS_ERROR(LDAP_NO_MEMORY);
2299 goto done;
2302 machine_pw_val = (struct berval) {
2303 .bv_val = utf16_pw,
2304 .bv_len = utf16_pw_len,
2307 /* Check if the machine account already exists. */
2308 ret = ads_find_machine_acct(ads, &res, machine_escaped);
2309 if (ADS_ERR_OK(ret)) {
2310 /* Change the machine account password */
2311 ret = ads_change_machine_acct(ads, res, &machine_pw_val);
2312 ads_msgfree(ads, res);
2314 goto done;
2316 ads_msgfree(ads, res);
2318 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2319 if (new_dn == NULL) {
2320 ret = ADS_ERROR(LDAP_NO_MEMORY);
2321 goto done;
2324 /* Create machine account */
2326 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2327 if (samAccountName == NULL) {
2328 ret = ADS_ERROR(LDAP_NO_MEMORY);
2329 goto done;
2332 dns_hostname = talloc_asprintf(ctx,
2333 "%s.%s",
2334 machine_name,
2335 dns_domain_name);
2336 if (dns_hostname == NULL) {
2337 ret = ADS_ERROR(LDAP_NO_MEMORY);
2338 goto done;
2341 /* Add dns_hostname SPNs */
2342 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2343 char *spn = talloc_asprintf(ctx,
2344 "%s/%s",
2345 spn_prefix[i],
2346 dns_hostname);
2347 if (spn == NULL) {
2348 ret = ADS_ERROR(LDAP_NO_MEMORY);
2349 goto done;
2352 ok = add_string_to_array(spn_array,
2353 spn,
2354 &spn_array,
2355 &num_spns);
2356 if (!ok) {
2357 ret = ADS_ERROR(LDAP_NO_MEMORY);
2358 goto done;
2362 /* Add machine_name SPNs */
2363 for (i = 0; i < ARRAY_SIZE(spn_prefix); i++) {
2364 char *spn = talloc_asprintf(ctx,
2365 "%s/%s",
2366 spn_prefix[i],
2367 machine_name);
2368 if (spn == NULL) {
2369 ret = ADS_ERROR(LDAP_NO_MEMORY);
2370 goto done;
2373 ok = add_string_to_array(spn_array,
2374 spn,
2375 &spn_array,
2376 &num_spns);
2377 if (!ok) {
2378 ret = ADS_ERROR(LDAP_NO_MEMORY);
2379 goto done;
2383 /* Make sure to NULL terminate the array */
2384 spn_array = talloc_realloc(ctx, spn_array, const char *, num_spns + 1);
2385 if (spn_array == NULL) {
2386 ret = ADS_ERROR(LDAP_NO_MEMORY);
2387 goto done;
2389 spn_array[num_spns] = NULL;
2391 controlstr = talloc_asprintf(ctx, "%u", acct_control);
2392 if (controlstr == NULL) {
2393 ret = ADS_ERROR(LDAP_NO_MEMORY);
2394 goto done;
2397 mods = ads_init_mods(ctx);
2398 if (mods == NULL) {
2399 ret = ADS_ERROR(LDAP_NO_MEMORY);
2400 goto done;
2403 ads_mod_str(ctx, &mods, "objectClass", "Computer");
2404 ads_mod_str(ctx, &mods, "SamAccountName", samAccountName);
2405 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2406 ads_mod_str(ctx, &mods, "DnsHostName", dns_hostname);
2407 ads_mod_strlist(ctx, &mods, "ServicePrincipalName", spn_array);
2408 ads_mod_ber(ctx, &mods, "unicodePwd", &machine_pw_val);
2410 ret = ads_gen_add(ads, new_dn, mods);
2412 done:
2413 SAFE_FREE(machine_escaped);
2414 talloc_destroy(ctx);
2416 return ret;
2420 * move a machine account to another OU on the ADS server
2421 * @param ads - An intialized ADS_STRUCT
2422 * @param machine_name - the NetBIOS machine name of this account.
2423 * @param org_unit - The LDAP path in which to place this account
2424 * @param moved - whether we moved the machine account (optional)
2425 * @return 0 upon success, or non-zero otherwise
2428 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2429 const char *org_unit, bool *moved)
2431 ADS_STATUS rc;
2432 int ldap_status;
2433 LDAPMessage *res = NULL;
2434 char *filter = NULL;
2435 char *computer_dn = NULL;
2436 char *parent_dn;
2437 char *computer_rdn = NULL;
2438 bool need_move = False;
2440 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2441 rc = ADS_ERROR(LDAP_NO_MEMORY);
2442 goto done;
2445 /* Find pre-existing machine */
2446 rc = ads_search(ads, &res, filter, NULL);
2447 if (!ADS_ERR_OK(rc)) {
2448 goto done;
2451 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2452 if (!computer_dn) {
2453 rc = ADS_ERROR(LDAP_NO_MEMORY);
2454 goto done;
2457 parent_dn = ads_parent_dn(computer_dn);
2458 if (strequal(parent_dn, org_unit)) {
2459 goto done;
2462 need_move = True;
2464 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2465 rc = ADS_ERROR(LDAP_NO_MEMORY);
2466 goto done;
2469 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2470 org_unit, 1, NULL, NULL);
2471 rc = ADS_ERROR(ldap_status);
2473 done:
2474 ads_msgfree(ads, res);
2475 SAFE_FREE(filter);
2476 TALLOC_FREE(computer_dn);
2477 SAFE_FREE(computer_rdn);
2479 if (!ADS_ERR_OK(rc)) {
2480 need_move = False;
2483 if (moved) {
2484 *moved = need_move;
2487 return rc;
2491 dump a binary result from ldap
2493 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2495 size_t i;
2496 for (i=0; values[i]; i++) {
2497 ber_len_t j;
2498 printf("%s: ", field);
2499 for (j=0; j<values[i]->bv_len; j++) {
2500 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2502 printf("\n");
2506 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2508 int i;
2509 for (i=0; values[i]; i++) {
2510 NTSTATUS status;
2511 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2512 struct GUID guid;
2514 status = GUID_from_ndr_blob(&in, &guid);
2515 if (NT_STATUS_IS_OK(status)) {
2516 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2517 } else {
2518 printf("%s: INVALID GUID\n", field);
2524 dump a sid result from ldap
2526 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2528 int i;
2529 for (i=0; values[i]; i++) {
2530 ssize_t ret;
2531 struct dom_sid sid;
2532 struct dom_sid_buf tmp;
2533 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2534 values[i]->bv_len, &sid);
2535 if (ret == -1) {
2536 return;
2538 printf("%s: %s\n", field, dom_sid_str_buf(&sid, &tmp));
2543 dump ntSecurityDescriptor
2545 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2547 TALLOC_CTX *frame = talloc_stackframe();
2548 struct security_descriptor *psd;
2549 NTSTATUS status;
2551 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2552 values[0]->bv_len, &psd);
2553 if (!NT_STATUS_IS_OK(status)) {
2554 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2555 nt_errstr(status)));
2556 TALLOC_FREE(frame);
2557 return;
2560 if (psd) {
2561 ads_disp_sd(ads, talloc_tos(), psd);
2564 TALLOC_FREE(frame);
2568 dump a string result from ldap
2570 static void dump_string(const char *field, char **values)
2572 int i;
2573 for (i=0; values[i]; i++) {
2574 printf("%s: %s\n", field, values[i]);
2579 dump a field from LDAP on stdout
2580 used for debugging
2583 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2585 const struct {
2586 const char *name;
2587 bool string;
2588 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2589 } handlers[] = {
2590 {"objectGUID", False, dump_guid},
2591 {"netbootGUID", False, dump_guid},
2592 {"nTSecurityDescriptor", False, dump_sd},
2593 {"dnsRecord", False, dump_binary},
2594 {"objectSid", False, dump_sid},
2595 {"tokenGroups", False, dump_sid},
2596 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2597 {"tokengroupsGlobalandUniversal", False, dump_sid},
2598 {"mS-DS-CreatorSID", False, dump_sid},
2599 {"msExchMailboxGuid", False, dump_guid},
2600 {NULL, True, NULL}
2602 int i;
2604 if (!field) { /* must be end of an entry */
2605 printf("\n");
2606 return False;
2609 for (i=0; handlers[i].name; i++) {
2610 if (strcasecmp_m(handlers[i].name, field) == 0) {
2611 if (!values) /* first time, indicate string or not */
2612 return handlers[i].string;
2613 handlers[i].handler(ads, field, (struct berval **) values);
2614 break;
2617 if (!handlers[i].name) {
2618 if (!values) /* first time, indicate string conversion */
2619 return True;
2620 dump_string(field, (char **)values);
2622 return False;
2626 * Dump a result from LDAP on stdout
2627 * used for debugging
2628 * @param ads connection to ads server
2629 * @param res Results to dump
2632 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2634 ads_process_results(ads, res, ads_dump_field, NULL);
2638 * Walk through results, calling a function for each entry found.
2639 * The function receives a field name, a berval * array of values,
2640 * and a data area passed through from the start. The function is
2641 * called once with null for field and values at the end of each
2642 * entry.
2643 * @param ads connection to ads server
2644 * @param res Results to process
2645 * @param fn Function for processing each result
2646 * @param data_area user-defined area to pass to function
2648 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2649 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2650 void *data_area)
2652 LDAPMessage *msg;
2653 TALLOC_CTX *ctx;
2654 size_t converted_size;
2656 if (!(ctx = talloc_init("ads_process_results")))
2657 return;
2659 for (msg = ads_first_entry(ads, res); msg;
2660 msg = ads_next_entry(ads, msg)) {
2661 char *utf8_field;
2662 BerElement *b;
2664 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2665 (LDAPMessage *)msg,&b);
2666 utf8_field;
2667 utf8_field=ldap_next_attribute(ads->ldap.ld,
2668 (LDAPMessage *)msg,b)) {
2669 struct berval **ber_vals;
2670 char **str_vals;
2671 char **utf8_vals;
2672 char *field;
2673 bool string;
2675 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2676 &converted_size))
2678 DEBUG(0,("ads_process_results: "
2679 "pull_utf8_talloc failed: %s",
2680 strerror(errno)));
2683 string = fn(ads, field, NULL, data_area);
2685 if (string) {
2686 const char **p;
2688 utf8_vals = ldap_get_values(ads->ldap.ld,
2689 (LDAPMessage *)msg, field);
2690 p = discard_const_p(const char *, utf8_vals);
2691 str_vals = ads_pull_strvals(ctx, p);
2692 fn(ads, field, (void **) str_vals, data_area);
2693 ldap_value_free(utf8_vals);
2694 } else {
2695 ber_vals = ldap_get_values_len(ads->ldap.ld,
2696 (LDAPMessage *)msg, field);
2697 fn(ads, field, (void **) ber_vals, data_area);
2699 ldap_value_free_len(ber_vals);
2701 ldap_memfree(utf8_field);
2703 ber_free(b, 0);
2704 talloc_free_children(ctx);
2705 fn(ads, NULL, NULL, data_area); /* completed an entry */
2708 talloc_destroy(ctx);
2712 * count how many replies are in a LDAPMessage
2713 * @param ads connection to ads server
2714 * @param res Results to count
2715 * @return number of replies
2717 int ads_count_replies(ADS_STRUCT *ads, void *res)
2719 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2723 * pull the first entry from a ADS result
2724 * @param ads connection to ads server
2725 * @param res Results of search
2726 * @return first entry from result
2728 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2730 return ldap_first_entry(ads->ldap.ld, res);
2734 * pull the next entry from a ADS result
2735 * @param ads connection to ads server
2736 * @param res Results of search
2737 * @return next entry from result
2739 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2741 return ldap_next_entry(ads->ldap.ld, res);
2745 * pull the first message from a ADS result
2746 * @param ads connection to ads server
2747 * @param res Results of search
2748 * @return first message from result
2750 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2752 return ldap_first_message(ads->ldap.ld, res);
2756 * pull the next message from a ADS result
2757 * @param ads connection to ads server
2758 * @param res Results of search
2759 * @return next message from result
2761 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2763 return ldap_next_message(ads->ldap.ld, res);
2767 * pull a single string from a ADS result
2768 * @param ads connection to ads server
2769 * @param mem_ctx TALLOC_CTX to use for allocating result string
2770 * @param msg Results of search
2771 * @param field Attribute to retrieve
2772 * @return Result string in talloc context
2774 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2775 const char *field)
2777 char **values;
2778 char *ret = NULL;
2779 char *ux_string;
2780 size_t converted_size;
2782 values = ldap_get_values(ads->ldap.ld, msg, field);
2783 if (!values)
2784 return NULL;
2786 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2787 &converted_size))
2789 ret = ux_string;
2791 ldap_value_free(values);
2792 return ret;
2796 * pull an array of strings from a ADS result
2797 * @param ads connection to ads server
2798 * @param mem_ctx TALLOC_CTX to use for allocating result string
2799 * @param msg Results of search
2800 * @param field Attribute to retrieve
2801 * @return Result strings in talloc context
2803 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2804 LDAPMessage *msg, const char *field,
2805 size_t *num_values)
2807 char **values;
2808 char **ret = NULL;
2809 size_t i, converted_size;
2811 values = ldap_get_values(ads->ldap.ld, msg, field);
2812 if (!values)
2813 return NULL;
2815 *num_values = ldap_count_values(values);
2817 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2818 if (!ret) {
2819 ldap_value_free(values);
2820 return NULL;
2823 for (i=0;i<*num_values;i++) {
2824 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2825 &converted_size))
2827 ldap_value_free(values);
2828 return NULL;
2831 ret[i] = NULL;
2833 ldap_value_free(values);
2834 return ret;
2838 * pull an array of strings from a ADS result
2839 * (handle large multivalue attributes with range retrieval)
2840 * @param ads connection to ads server
2841 * @param mem_ctx TALLOC_CTX to use for allocating result string
2842 * @param msg Results of search
2843 * @param field Attribute to retrieve
2844 * @param current_strings strings returned by a previous call to this function
2845 * @param next_attribute The next query should ask for this attribute
2846 * @param num_values How many values did we get this time?
2847 * @param more_values Are there more values to get?
2848 * @return Result strings in talloc context
2850 char **ads_pull_strings_range(ADS_STRUCT *ads,
2851 TALLOC_CTX *mem_ctx,
2852 LDAPMessage *msg, const char *field,
2853 char **current_strings,
2854 const char **next_attribute,
2855 size_t *num_strings,
2856 bool *more_strings)
2858 char *attr;
2859 char *expected_range_attrib, *range_attr;
2860 BerElement *ptr = NULL;
2861 char **strings;
2862 char **new_strings;
2863 size_t num_new_strings;
2864 unsigned long int range_start;
2865 unsigned long int range_end;
2867 /* we might have been given the whole lot anyway */
2868 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2869 *more_strings = False;
2870 return strings;
2873 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2875 /* look for Range result */
2876 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2877 attr;
2878 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2879 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2880 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2881 range_attr = attr;
2882 break;
2884 ldap_memfree(attr);
2886 if (!attr) {
2887 ber_free(ptr, 0);
2888 /* nothing here - this field is just empty */
2889 *more_strings = False;
2890 return NULL;
2893 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2894 &range_start, &range_end) == 2) {
2895 *more_strings = True;
2896 } else {
2897 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2898 &range_start) == 1) {
2899 *more_strings = False;
2900 } else {
2901 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2902 range_attr));
2903 ldap_memfree(range_attr);
2904 *more_strings = False;
2905 return NULL;
2909 if ((*num_strings) != range_start) {
2910 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2911 " - aborting range retreival\n",
2912 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2913 ldap_memfree(range_attr);
2914 *more_strings = False;
2915 return NULL;
2918 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2920 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2921 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2922 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2923 range_attr, (unsigned long int)range_end - range_start + 1,
2924 (unsigned long int)num_new_strings));
2925 ldap_memfree(range_attr);
2926 *more_strings = False;
2927 return NULL;
2930 strings = talloc_realloc(mem_ctx, current_strings, char *,
2931 *num_strings + num_new_strings);
2933 if (strings == NULL) {
2934 ldap_memfree(range_attr);
2935 *more_strings = False;
2936 return NULL;
2939 if (new_strings && num_new_strings) {
2940 memcpy(&strings[*num_strings], new_strings,
2941 sizeof(*new_strings) * num_new_strings);
2944 (*num_strings) += num_new_strings;
2946 if (*more_strings) {
2947 *next_attribute = talloc_asprintf(mem_ctx,
2948 "%s;range=%d-*",
2949 field,
2950 (int)*num_strings);
2952 if (!*next_attribute) {
2953 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2954 ldap_memfree(range_attr);
2955 *more_strings = False;
2956 return NULL;
2960 ldap_memfree(range_attr);
2962 return strings;
2966 * pull a single uint32_t from a ADS result
2967 * @param ads connection to ads server
2968 * @param msg Results of search
2969 * @param field Attribute to retrieve
2970 * @param v Pointer to int to store result
2971 * @return boolean inidicating success
2973 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2974 uint32_t *v)
2976 char **values;
2978 values = ldap_get_values(ads->ldap.ld, msg, field);
2979 if (!values)
2980 return False;
2981 if (!values[0]) {
2982 ldap_value_free(values);
2983 return False;
2986 *v = atoi(values[0]);
2987 ldap_value_free(values);
2988 return True;
2992 * pull a single objectGUID from an ADS result
2993 * @param ads connection to ADS server
2994 * @param msg results of search
2995 * @param guid 37-byte area to receive text guid
2996 * @return boolean indicating success
2998 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
3000 DATA_BLOB blob;
3001 NTSTATUS status;
3003 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
3004 &blob)) {
3005 return false;
3008 status = GUID_from_ndr_blob(&blob, guid);
3009 talloc_free(blob.data);
3010 return NT_STATUS_IS_OK(status);
3015 * pull a single struct dom_sid from a ADS result
3016 * @param ads connection to ads server
3017 * @param msg Results of search
3018 * @param field Attribute to retrieve
3019 * @param sid Pointer to sid to store result
3020 * @return boolean inidicating success
3022 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
3023 struct dom_sid *sid)
3025 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
3029 * pull an array of struct dom_sids from a ADS result
3030 * @param ads connection to ads server
3031 * @param mem_ctx TALLOC_CTX for allocating sid array
3032 * @param msg Results of search
3033 * @param field Attribute to retrieve
3034 * @param sids pointer to sid array to allocate
3035 * @return the count of SIDs pulled
3037 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3038 LDAPMessage *msg, const char *field, struct dom_sid **sids)
3040 struct berval **values;
3041 int count, i;
3043 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3045 if (!values)
3046 return 0;
3048 for (i=0; values[i]; i++)
3049 /* nop */ ;
3051 if (i) {
3052 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
3053 if (!(*sids)) {
3054 ldap_value_free_len(values);
3055 return 0;
3057 } else {
3058 (*sids) = NULL;
3061 count = 0;
3062 for (i=0; values[i]; i++) {
3063 ssize_t ret;
3064 ret = sid_parse((const uint8_t *)values[i]->bv_val,
3065 values[i]->bv_len, &(*sids)[count]);
3066 if (ret != -1) {
3067 struct dom_sid_buf buf;
3068 DBG_DEBUG("pulling SID: %s\n",
3069 dom_sid_str_buf(&(*sids)[count], &buf));
3070 count++;
3074 ldap_value_free_len(values);
3075 return count;
3079 * pull a struct security_descriptor from a ADS result
3080 * @param ads connection to ads server
3081 * @param mem_ctx TALLOC_CTX for allocating sid array
3082 * @param msg Results of search
3083 * @param field Attribute to retrieve
3084 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
3085 * @return boolean inidicating success
3087 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3088 LDAPMessage *msg, const char *field,
3089 struct security_descriptor **sd)
3091 struct berval **values;
3092 bool ret = true;
3094 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3096 if (!values) return false;
3098 if (values[0]) {
3099 NTSTATUS status;
3100 status = unmarshall_sec_desc(mem_ctx,
3101 (uint8_t *)values[0]->bv_val,
3102 values[0]->bv_len, sd);
3103 if (!NT_STATUS_IS_OK(status)) {
3104 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
3105 nt_errstr(status)));
3106 ret = false;
3110 ldap_value_free_len(values);
3111 return ret;
3115 * in order to support usernames longer than 21 characters we need to
3116 * use both the sAMAccountName and the userPrincipalName attributes
3117 * It seems that not all users have the userPrincipalName attribute set
3119 * @param ads connection to ads server
3120 * @param mem_ctx TALLOC_CTX for allocating sid array
3121 * @param msg Results of search
3122 * @return the username
3124 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3125 LDAPMessage *msg)
3127 #if 0 /* JERRY */
3128 char *ret, *p;
3130 /* lookup_name() only works on the sAMAccountName to
3131 returning the username portion of userPrincipalName
3132 breaks winbindd_getpwnam() */
3134 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
3135 if (ret && (p = strchr_m(ret, '@'))) {
3136 *p = 0;
3137 return ret;
3139 #endif
3140 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
3145 * find the update serial number - this is the core of the ldap cache
3146 * @param ads connection to ads server
3147 * @param ads connection to ADS server
3148 * @param usn Pointer to retrieved update serial number
3149 * @return status of search
3151 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
3153 const char *attrs[] = {"highestCommittedUSN", NULL};
3154 ADS_STATUS status;
3155 LDAPMessage *res;
3157 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3158 if (!ADS_ERR_OK(status))
3159 return status;
3161 if (ads_count_replies(ads, res) != 1) {
3162 ads_msgfree(ads, res);
3163 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3166 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
3167 ads_msgfree(ads, res);
3168 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3171 ads_msgfree(ads, res);
3172 return ADS_SUCCESS;
3175 /* parse a ADS timestring - typical string is
3176 '20020917091222.0Z0' which means 09:12.22 17th September
3177 2002, timezone 0 */
3178 static time_t ads_parse_time(const char *str)
3180 struct tm tm;
3182 ZERO_STRUCT(tm);
3184 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
3185 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
3186 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
3187 return 0;
3189 tm.tm_year -= 1900;
3190 tm.tm_mon -= 1;
3192 return timegm(&tm);
3195 /********************************************************************
3196 ********************************************************************/
3198 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
3200 const char *attrs[] = {"currentTime", NULL};
3201 ADS_STATUS status;
3202 LDAPMessage *res;
3203 char *timestr;
3204 TALLOC_CTX *ctx;
3205 ADS_STRUCT *ads_s = ads;
3207 if (!(ctx = talloc_init("ads_current_time"))) {
3208 return ADS_ERROR(LDAP_NO_MEMORY);
3211 /* establish a new ldap tcp session if necessary */
3213 if ( !ads->ldap.ld ) {
3214 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3215 ads->server.ldap_server, ADS_SASL_PLAIN )) == NULL )
3217 status = ADS_ERROR(LDAP_NO_MEMORY);
3218 goto done;
3220 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3221 status = ads_connect( ads_s );
3222 if ( !ADS_ERR_OK(status))
3223 goto done;
3226 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3227 if (!ADS_ERR_OK(status)) {
3228 goto done;
3231 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
3232 if (!timestr) {
3233 ads_msgfree(ads_s, res);
3234 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3235 goto done;
3238 /* but save the time and offset in the original ADS_STRUCT */
3240 ads->config.current_time = ads_parse_time(timestr);
3242 if (ads->config.current_time != 0) {
3243 ads->auth.time_offset = ads->config.current_time - time(NULL);
3244 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
3247 ads_msgfree(ads, res);
3249 status = ADS_SUCCESS;
3251 done:
3252 /* free any temporary ads connections */
3253 if ( ads_s != ads ) {
3254 ads_destroy( &ads_s );
3256 talloc_destroy(ctx);
3258 return status;
3261 /********************************************************************
3262 ********************************************************************/
3264 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3266 const char *attrs[] = {"domainFunctionality", NULL};
3267 ADS_STATUS status;
3268 LDAPMessage *res;
3269 ADS_STRUCT *ads_s = ads;
3271 *val = DS_DOMAIN_FUNCTION_2000;
3273 /* establish a new ldap tcp session if necessary */
3275 if ( !ads->ldap.ld ) {
3276 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3277 ads->server.ldap_server, ADS_SASL_PLAIN )) == NULL )
3279 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3280 goto done;
3282 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3283 status = ads_connect( ads_s );
3284 if ( !ADS_ERR_OK(status))
3285 goto done;
3288 /* If the attribute does not exist assume it is a Windows 2000
3289 functional domain */
3291 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3292 if (!ADS_ERR_OK(status)) {
3293 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3294 status = ADS_SUCCESS;
3296 goto done;
3299 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3300 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3302 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3305 ads_msgfree(ads, res);
3307 done:
3308 /* free any temporary ads connections */
3309 if ( ads_s != ads ) {
3310 ads_destroy( &ads_s );
3313 return status;
3317 * find the domain sid for our domain
3318 * @param ads connection to ads server
3319 * @param sid Pointer to domain sid
3320 * @return status of search
3322 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3324 const char *attrs[] = {"objectSid", NULL};
3325 LDAPMessage *res;
3326 ADS_STATUS rc;
3328 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3329 attrs, &res);
3330 if (!ADS_ERR_OK(rc)) return rc;
3331 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3332 ads_msgfree(ads, res);
3333 return ADS_ERROR_SYSTEM(ENOENT);
3335 ads_msgfree(ads, res);
3337 return ADS_SUCCESS;
3341 * find our site name
3342 * @param ads connection to ads server
3343 * @param mem_ctx Pointer to talloc context
3344 * @param site_name Pointer to the sitename
3345 * @return status of search
3347 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3349 ADS_STATUS status;
3350 LDAPMessage *res;
3351 const char *dn, *service_name;
3352 const char *attrs[] = { "dsServiceName", NULL };
3354 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3355 if (!ADS_ERR_OK(status)) {
3356 return status;
3359 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3360 if (service_name == NULL) {
3361 ads_msgfree(ads, res);
3362 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3365 ads_msgfree(ads, res);
3367 /* go up three levels */
3368 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3369 if (dn == NULL) {
3370 return ADS_ERROR(LDAP_NO_MEMORY);
3373 *site_name = talloc_strdup(mem_ctx, dn);
3374 if (*site_name == NULL) {
3375 return ADS_ERROR(LDAP_NO_MEMORY);
3378 return status;
3380 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3385 * find the site dn where a machine resides
3386 * @param ads connection to ads server
3387 * @param mem_ctx Pointer to talloc context
3388 * @param computer_name name of the machine
3389 * @param site_name Pointer to the sitename
3390 * @return status of search
3392 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3394 ADS_STATUS status;
3395 LDAPMessage *res;
3396 const char *parent, *filter;
3397 char *config_context = NULL;
3398 char *dn;
3400 /* shortcut a query */
3401 if (strequal(computer_name, ads->config.ldap_server_name)) {
3402 return ads_site_dn(ads, mem_ctx, site_dn);
3405 status = ads_config_path(ads, mem_ctx, &config_context);
3406 if (!ADS_ERR_OK(status)) {
3407 return status;
3410 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3411 if (filter == NULL) {
3412 return ADS_ERROR(LDAP_NO_MEMORY);
3415 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3416 filter, NULL, &res);
3417 if (!ADS_ERR_OK(status)) {
3418 return status;
3421 if (ads_count_replies(ads, res) != 1) {
3422 ads_msgfree(ads, res);
3423 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3426 dn = ads_get_dn(ads, mem_ctx, res);
3427 if (dn == NULL) {
3428 ads_msgfree(ads, res);
3429 return ADS_ERROR(LDAP_NO_MEMORY);
3432 /* go up three levels */
3433 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3434 if (parent == NULL) {
3435 ads_msgfree(ads, res);
3436 TALLOC_FREE(dn);
3437 return ADS_ERROR(LDAP_NO_MEMORY);
3440 *site_dn = talloc_strdup(mem_ctx, parent);
3441 if (*site_dn == NULL) {
3442 ads_msgfree(ads, res);
3443 TALLOC_FREE(dn);
3444 return ADS_ERROR(LDAP_NO_MEMORY);
3447 TALLOC_FREE(dn);
3448 ads_msgfree(ads, res);
3450 return status;
3454 * get the upn suffixes for a domain
3455 * @param ads connection to ads server
3456 * @param mem_ctx Pointer to talloc context
3457 * @param suffixes Pointer to an array of suffixes
3458 * @param num_suffixes Pointer to the number of suffixes
3459 * @return status of search
3461 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3463 ADS_STATUS status;
3464 LDAPMessage *res;
3465 const char *base;
3466 char *config_context = NULL;
3467 const char *attrs[] = { "uPNSuffixes", NULL };
3469 status = ads_config_path(ads, mem_ctx, &config_context);
3470 if (!ADS_ERR_OK(status)) {
3471 return status;
3474 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3475 if (base == NULL) {
3476 return ADS_ERROR(LDAP_NO_MEMORY);
3479 status = ads_search_dn(ads, &res, base, attrs);
3480 if (!ADS_ERR_OK(status)) {
3481 return status;
3484 if (ads_count_replies(ads, res) != 1) {
3485 ads_msgfree(ads, res);
3486 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3489 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3490 if ((*suffixes) == NULL) {
3491 ads_msgfree(ads, res);
3492 return ADS_ERROR(LDAP_NO_MEMORY);
3495 ads_msgfree(ads, res);
3497 return status;
3501 * get the joinable ous for a domain
3502 * @param ads connection to ads server
3503 * @param mem_ctx Pointer to talloc context
3504 * @param ous Pointer to an array of ous
3505 * @param num_ous Pointer to the number of ous
3506 * @return status of search
3508 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3509 TALLOC_CTX *mem_ctx,
3510 char ***ous,
3511 size_t *num_ous)
3513 ADS_STATUS status;
3514 LDAPMessage *res = NULL;
3515 LDAPMessage *msg = NULL;
3516 const char *attrs[] = { "dn", NULL };
3517 int count = 0;
3519 status = ads_search(ads, &res,
3520 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3521 attrs);
3522 if (!ADS_ERR_OK(status)) {
3523 return status;
3526 count = ads_count_replies(ads, res);
3527 if (count < 1) {
3528 ads_msgfree(ads, res);
3529 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3532 for (msg = ads_first_entry(ads, res); msg;
3533 msg = ads_next_entry(ads, msg)) {
3534 const char **p = discard_const_p(const char *, *ous);
3535 char *dn = NULL;
3537 dn = ads_get_dn(ads, talloc_tos(), msg);
3538 if (!dn) {
3539 ads_msgfree(ads, res);
3540 return ADS_ERROR(LDAP_NO_MEMORY);
3543 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3544 TALLOC_FREE(dn);
3545 ads_msgfree(ads, res);
3546 return ADS_ERROR(LDAP_NO_MEMORY);
3549 TALLOC_FREE(dn);
3550 *ous = discard_const_p(char *, p);
3553 ads_msgfree(ads, res);
3555 return status;
3560 * pull a struct dom_sid from an extended dn string
3561 * @param mem_ctx TALLOC_CTX
3562 * @param extended_dn string
3563 * @param flags string type of extended_dn
3564 * @param sid pointer to a struct dom_sid
3565 * @return NT_STATUS_OK on success,
3566 * NT_INVALID_PARAMETER on error,
3567 * NT_STATUS_NOT_FOUND if no SID present
3569 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3570 const char *extended_dn,
3571 enum ads_extended_dn_flags flags,
3572 struct dom_sid *sid)
3574 char *p, *q, *dn;
3576 if (!extended_dn) {
3577 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3580 /* otherwise extended_dn gets stripped off */
3581 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3582 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3585 * ADS_EXTENDED_DN_HEX_STRING:
3586 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3588 * ADS_EXTENDED_DN_STRING (only with w2k3):
3589 * <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
3591 * Object with no SID, such as an Exchange Public Folder
3592 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3595 p = strchr(dn, ';');
3596 if (!p) {
3597 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3600 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3601 DEBUG(5,("No SID present in extended dn\n"));
3602 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3605 p += strlen(";<SID=");
3607 q = strchr(p, '>');
3608 if (!q) {
3609 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3612 *q = '\0';
3614 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3616 switch (flags) {
3618 case ADS_EXTENDED_DN_STRING:
3619 if (!string_to_sid(sid, p)) {
3620 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3622 break;
3623 case ADS_EXTENDED_DN_HEX_STRING: {
3624 ssize_t ret;
3625 fstring buf;
3626 size_t buf_len;
3628 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3629 if (buf_len == 0) {
3630 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3633 ret = sid_parse((const uint8_t *)buf, buf_len, sid);
3634 if (ret == -1) {
3635 DEBUG(10,("failed to parse sid\n"));
3636 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3638 break;
3640 default:
3641 DEBUG(10,("unknown extended dn format\n"));
3642 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3645 return ADS_ERROR_NT(NT_STATUS_OK);
3648 /********************************************************************
3649 ********************************************************************/
3651 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3653 LDAPMessage *res = NULL;
3654 ADS_STATUS status;
3655 int count = 0;
3656 char *name = NULL;
3658 status = ads_find_machine_acct(ads, &res, machine_name);
3659 if (!ADS_ERR_OK(status)) {
3660 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3661 lp_netbios_name()));
3662 goto out;
3665 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3666 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3667 goto out;
3670 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3671 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3674 out:
3675 ads_msgfree(ads, res);
3677 return name;
3680 /********************************************************************
3681 ********************************************************************/
3683 static char **get_addl_hosts(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
3684 LDAPMessage *msg, size_t *num_values)
3686 const char *field = "msDS-AdditionalDnsHostName";
3687 struct berval **values = NULL;
3688 char **ret = NULL;
3689 size_t i, converted_size;
3692 * Windows DC implicitly adds a short name for each FQDN added to
3693 * msDS-AdditionalDnsHostName, but it comes with a strage binary
3694 * suffix "\0$" which we should ignore (see bug #14406).
3697 values = ldap_get_values_len(ads->ldap.ld, msg, field);
3698 if (values == NULL) {
3699 return NULL;
3702 *num_values = ldap_count_values_len(values);
3704 ret = talloc_array(mem_ctx, char *, *num_values + 1);
3705 if (ret == NULL) {
3706 ldap_value_free_len(values);
3707 return NULL;
3710 for (i = 0; i < *num_values; i++) {
3711 ret[i] = NULL;
3712 if (!convert_string_talloc(mem_ctx, CH_UTF8, CH_UNIX,
3713 values[i]->bv_val,
3714 strnlen(values[i]->bv_val,
3715 values[i]->bv_len),
3716 &ret[i], &converted_size)) {
3717 ldap_value_free_len(values);
3718 return NULL;
3721 ret[i] = NULL;
3723 ldap_value_free_len(values);
3724 return ret;
3727 ADS_STATUS ads_get_additional_dns_hostnames(TALLOC_CTX *mem_ctx,
3728 ADS_STRUCT *ads,
3729 const char *machine_name,
3730 char ***hostnames_array,
3731 size_t *num_hostnames)
3733 ADS_STATUS status;
3734 LDAPMessage *res = NULL;
3735 int count;
3737 status = ads_find_machine_acct(ads,
3738 &res,
3739 machine_name);
3740 if (!ADS_ERR_OK(status)) {
3741 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
3742 machine_name));
3743 return status;
3746 count = ads_count_replies(ads, res);
3747 if (count != 1) {
3748 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3749 goto done;
3752 *hostnames_array = get_addl_hosts(ads, mem_ctx, res, num_hostnames);
3753 if (*hostnames_array == NULL) {
3754 DEBUG(1, ("Host account for %s does not have msDS-AdditionalDnsHostName.\n",
3755 machine_name));
3756 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3757 goto done;
3760 done:
3761 ads_msgfree(ads, res);
3763 return status;
3766 /********************************************************************
3767 ********************************************************************/
3769 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3771 LDAPMessage *res = NULL;
3772 ADS_STATUS status;
3773 int count = 0;
3774 char *name = NULL;
3776 status = ads_find_machine_acct(ads, &res, machine_name);
3777 if (!ADS_ERR_OK(status)) {
3778 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3779 lp_netbios_name()));
3780 goto out;
3783 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3784 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3785 goto out;
3788 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3789 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3792 out:
3793 ads_msgfree(ads, res);
3795 return name;
3798 /********************************************************************
3799 ********************************************************************/
3801 bool ads_has_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3803 LDAPMessage *res = NULL;
3804 ADS_STATUS status;
3805 int count = 0;
3806 char *name = NULL;
3807 bool ok = false;
3809 status = ads_find_machine_acct(ads, &res, machine_name);
3810 if (!ADS_ERR_OK(status)) {
3811 DEBUG(0,("ads_has_samaccountname: Failed to find account for %s\n",
3812 lp_netbios_name()));
3813 goto out;
3816 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3817 DEBUG(1,("ads_has_samaccountname: %d entries returned!\n", count));
3818 goto out;
3821 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3822 DEBUG(0,("ads_has_samaccountname: No sAMAccountName attribute!\n"));
3825 out:
3826 ads_msgfree(ads, res);
3827 if (name != NULL) {
3828 ok = (strlen(name) > 0);
3830 TALLOC_FREE(name);
3831 return ok;
3834 #if 0
3836 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3839 * Join a machine to a realm
3840 * Creates the machine account and sets the machine password
3841 * @param ads connection to ads server
3842 * @param machine name of host to add
3843 * @param org_unit Organizational unit to place machine in
3844 * @return status of join
3846 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3847 uint32_t account_type, const char *org_unit)
3849 ADS_STATUS status;
3850 LDAPMessage *res = NULL;
3851 char *machine;
3853 /* machine name must be lowercase */
3854 machine = SMB_STRDUP(machine_name);
3855 strlower_m(machine);
3858 status = ads_find_machine_acct(ads, (void **)&res, machine);
3859 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3860 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3861 status = ads_leave_realm(ads, machine);
3862 if (!ADS_ERR_OK(status)) {
3863 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3864 machine, ads->config.realm));
3865 return status;
3869 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3870 if (!ADS_ERR_OK(status)) {
3871 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3872 SAFE_FREE(machine);
3873 return status;
3876 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3877 if (!ADS_ERR_OK(status)) {
3878 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3879 SAFE_FREE(machine);
3880 return status;
3883 SAFE_FREE(machine);
3884 ads_msgfree(ads, res);
3886 return status;
3888 #endif
3891 * Delete a machine from the realm
3892 * @param ads connection to ads server
3893 * @param hostname Machine to remove
3894 * @return status of delete
3896 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3898 ADS_STATUS status;
3899 void *msg;
3900 LDAPMessage *res;
3901 char *hostnameDN, *host;
3902 int rc;
3903 LDAPControl ldap_control;
3904 LDAPControl * pldap_control[2] = {NULL, NULL};
3906 pldap_control[0] = &ldap_control;
3907 memset(&ldap_control, 0, sizeof(LDAPControl));
3908 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3910 /* hostname must be lowercase */
3911 host = SMB_STRDUP(hostname);
3912 if (!strlower_m(host)) {
3913 SAFE_FREE(host);
3914 return ADS_ERROR_SYSTEM(EINVAL);
3917 status = ads_find_machine_acct(ads, &res, host);
3918 if (!ADS_ERR_OK(status)) {
3919 DEBUG(0, ("Host account for %s does not exist.\n", host));
3920 SAFE_FREE(host);
3921 return status;
3924 msg = ads_first_entry(ads, res);
3925 if (!msg) {
3926 SAFE_FREE(host);
3927 return ADS_ERROR_SYSTEM(ENOENT);
3930 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3931 if (hostnameDN == NULL) {
3932 SAFE_FREE(host);
3933 return ADS_ERROR_SYSTEM(ENOENT);
3936 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3937 if (rc) {
3938 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3939 }else {
3940 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3943 if (rc != LDAP_SUCCESS) {
3944 const char *attrs[] = { "cn", NULL };
3945 LDAPMessage *msg_sub;
3947 /* we only search with scope ONE, we do not expect any further
3948 * objects to be created deeper */
3950 status = ads_do_search_retry(ads, hostnameDN,
3951 LDAP_SCOPE_ONELEVEL,
3952 "(objectclass=*)", attrs, &res);
3954 if (!ADS_ERR_OK(status)) {
3955 SAFE_FREE(host);
3956 TALLOC_FREE(hostnameDN);
3957 return status;
3960 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3961 msg_sub = ads_next_entry(ads, msg_sub)) {
3963 char *dn = NULL;
3965 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3966 SAFE_FREE(host);
3967 TALLOC_FREE(hostnameDN);
3968 return ADS_ERROR(LDAP_NO_MEMORY);
3971 status = ads_del_dn(ads, dn);
3972 if (!ADS_ERR_OK(status)) {
3973 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3974 SAFE_FREE(host);
3975 TALLOC_FREE(dn);
3976 TALLOC_FREE(hostnameDN);
3977 return status;
3980 TALLOC_FREE(dn);
3983 /* there should be no subordinate objects anymore */
3984 status = ads_do_search_retry(ads, hostnameDN,
3985 LDAP_SCOPE_ONELEVEL,
3986 "(objectclass=*)", attrs, &res);
3988 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3989 SAFE_FREE(host);
3990 TALLOC_FREE(hostnameDN);
3991 return status;
3994 /* delete hostnameDN now */
3995 status = ads_del_dn(ads, hostnameDN);
3996 if (!ADS_ERR_OK(status)) {
3997 SAFE_FREE(host);
3998 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3999 TALLOC_FREE(hostnameDN);
4000 return status;
4004 TALLOC_FREE(hostnameDN);
4006 status = ads_find_machine_acct(ads, &res, host);
4007 if ((status.error_type == ENUM_ADS_ERROR_LDAP) &&
4008 (status.err.rc != LDAP_NO_SUCH_OBJECT)) {
4009 DEBUG(3, ("Failed to remove host account.\n"));
4010 SAFE_FREE(host);
4011 return status;
4014 SAFE_FREE(host);
4015 return ADS_SUCCESS;
4019 * pull all token-sids from an LDAP dn
4020 * @param ads connection to ads server
4021 * @param mem_ctx TALLOC_CTX for allocating sid array
4022 * @param dn of LDAP object
4023 * @param user_sid pointer to struct dom_sid (objectSid)
4024 * @param primary_group_sid pointer to struct dom_sid (self composed)
4025 * @param sids pointer to sid array to allocate
4026 * @param num_sids counter of SIDs pulled
4027 * @return status of token query
4029 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
4030 TALLOC_CTX *mem_ctx,
4031 const char *dn,
4032 struct dom_sid *user_sid,
4033 struct dom_sid *primary_group_sid,
4034 struct dom_sid **sids,
4035 size_t *num_sids)
4037 ADS_STATUS status;
4038 LDAPMessage *res = NULL;
4039 int count = 0;
4040 size_t tmp_num_sids;
4041 struct dom_sid *tmp_sids;
4042 struct dom_sid tmp_user_sid;
4043 struct dom_sid tmp_primary_group_sid;
4044 uint32_t pgid;
4045 const char *attrs[] = {
4046 "objectSid",
4047 "tokenGroups",
4048 "primaryGroupID",
4049 NULL
4052 status = ads_search_retry_dn(ads, &res, dn, attrs);
4053 if (!ADS_ERR_OK(status)) {
4054 return status;
4057 count = ads_count_replies(ads, res);
4058 if (count != 1) {
4059 ads_msgfree(ads, res);
4060 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
4063 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
4064 ads_msgfree(ads, res);
4065 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4068 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
4069 ads_msgfree(ads, res);
4070 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4074 /* hack to compose the primary group sid without knowing the
4075 * domsid */
4077 struct dom_sid domsid;
4079 sid_copy(&domsid, &tmp_user_sid);
4081 if (!sid_split_rid(&domsid, NULL)) {
4082 ads_msgfree(ads, res);
4083 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4086 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
4087 ads_msgfree(ads, res);
4088 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4092 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
4094 if (tmp_num_sids == 0 || !tmp_sids) {
4095 ads_msgfree(ads, res);
4096 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4099 if (num_sids) {
4100 *num_sids = tmp_num_sids;
4103 if (sids) {
4104 *sids = tmp_sids;
4107 if (user_sid) {
4108 *user_sid = tmp_user_sid;
4111 if (primary_group_sid) {
4112 *primary_group_sid = tmp_primary_group_sid;
4115 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
4117 ads_msgfree(ads, res);
4118 return ADS_ERROR_LDAP(LDAP_SUCCESS);
4122 * Find a sAMAccoutName in LDAP
4123 * @param ads connection to ads server
4124 * @param mem_ctx TALLOC_CTX for allocating sid array
4125 * @param samaccountname to search
4126 * @param uac_ret uint32_t pointer userAccountControl attribute value
4127 * @param dn_ret pointer to dn
4128 * @return status of token query
4130 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
4131 TALLOC_CTX *mem_ctx,
4132 const char *samaccountname,
4133 uint32_t *uac_ret,
4134 const char **dn_ret)
4136 ADS_STATUS status;
4137 const char *attrs[] = { "userAccountControl", NULL };
4138 const char *filter;
4139 LDAPMessage *res = NULL;
4140 char *dn = NULL;
4141 uint32_t uac = 0;
4143 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
4144 samaccountname);
4145 if (filter == NULL) {
4146 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
4147 goto out;
4150 status = ads_do_search_all(ads, ads->config.bind_path,
4151 LDAP_SCOPE_SUBTREE,
4152 filter, attrs, &res);
4154 if (!ADS_ERR_OK(status)) {
4155 goto out;
4158 if (ads_count_replies(ads, res) != 1) {
4159 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
4160 goto out;
4163 dn = ads_get_dn(ads, talloc_tos(), res);
4164 if (dn == NULL) {
4165 status = ADS_ERROR(LDAP_NO_MEMORY);
4166 goto out;
4169 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
4170 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
4171 goto out;
4174 if (uac_ret) {
4175 *uac_ret = uac;
4178 if (dn_ret) {
4179 *dn_ret = talloc_strdup(mem_ctx, dn);
4180 if (!*dn_ret) {
4181 status = ADS_ERROR(LDAP_NO_MEMORY);
4182 goto out;
4185 out:
4186 TALLOC_FREE(dn);
4187 ads_msgfree(ads, res);
4189 return status;
4193 * find our configuration path
4194 * @param ads connection to ads server
4195 * @param mem_ctx Pointer to talloc context
4196 * @param config_path Pointer to the config path
4197 * @return status of search
4199 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
4200 TALLOC_CTX *mem_ctx,
4201 char **config_path)
4203 ADS_STATUS status;
4204 LDAPMessage *res = NULL;
4205 const char *config_context = NULL;
4206 const char *attrs[] = { "configurationNamingContext", NULL };
4208 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
4209 "(objectclass=*)", attrs, &res);
4210 if (!ADS_ERR_OK(status)) {
4211 return status;
4214 config_context = ads_pull_string(ads, mem_ctx, res,
4215 "configurationNamingContext");
4216 ads_msgfree(ads, res);
4217 if (!config_context) {
4218 return ADS_ERROR(LDAP_NO_MEMORY);
4221 if (config_path) {
4222 *config_path = talloc_strdup(mem_ctx, config_context);
4223 if (!*config_path) {
4224 return ADS_ERROR(LDAP_NO_MEMORY);
4228 return ADS_ERROR(LDAP_SUCCESS);
4232 * find the displayName of an extended right
4233 * @param ads connection to ads server
4234 * @param config_path The config path
4235 * @param mem_ctx Pointer to talloc context
4236 * @param GUID struct of the rightsGUID
4237 * @return status of search
4239 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
4240 const char *config_path,
4241 TALLOC_CTX *mem_ctx,
4242 const struct GUID *rights_guid)
4244 ADS_STATUS rc;
4245 LDAPMessage *res = NULL;
4246 char *expr = NULL;
4247 const char *attrs[] = { "displayName", NULL };
4248 const char *result = NULL;
4249 const char *path;
4251 if (!ads || !mem_ctx || !rights_guid) {
4252 goto done;
4255 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
4256 GUID_string(mem_ctx, rights_guid));
4257 if (!expr) {
4258 goto done;
4261 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
4262 if (!path) {
4263 goto done;
4266 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
4267 expr, attrs, &res);
4268 if (!ADS_ERR_OK(rc)) {
4269 goto done;
4272 if (ads_count_replies(ads, res) != 1) {
4273 goto done;
4276 result = ads_pull_string(ads, mem_ctx, res, "displayName");
4278 done:
4279 ads_msgfree(ads, res);
4280 return result;
4284 * verify or build and verify an account ou
4285 * @param mem_ctx Pointer to talloc context
4286 * @param ads connection to ads server
4287 * @param account_ou
4288 * @return status of search
4291 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
4292 ADS_STRUCT *ads,
4293 const char **account_ou)
4295 char **exploded_dn;
4296 const char *name;
4297 char *ou_string;
4299 if (account_ou == NULL) {
4300 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
4303 if (*account_ou != NULL) {
4304 exploded_dn = ldap_explode_dn(*account_ou, 0);
4305 if (exploded_dn) {
4306 ldap_value_free(exploded_dn);
4307 return ADS_SUCCESS;
4311 ou_string = ads_ou_string(ads, *account_ou);
4312 if (!ou_string) {
4313 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4316 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
4317 ads->config.bind_path);
4318 SAFE_FREE(ou_string);
4320 if (!name) {
4321 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
4324 exploded_dn = ldap_explode_dn(name, 0);
4325 if (!exploded_dn) {
4326 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
4328 ldap_value_free(exploded_dn);
4330 *account_ou = name;
4331 return ADS_SUCCESS;
4334 #endif