auth log: Add tests for anonymous bind and SamLogon
[Samba.git] / source3 / libads / ldap.c
blobc70cdeb5163a129b5cddb9f756472b2514fca1e6
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"
35 #ifdef HAVE_LDAP
37 /**
38 * @file ldap.c
39 * @brief basic ldap client-side routines for ads server communications
41 * The routines contained here should do the necessary ldap calls for
42 * ads setups.
44 * Important note: attribute names passed into ads_ routines must
45 * already be in UTF-8 format. We do not convert them because in almost
46 * all cases, they are just ascii (which is represented with the same
47 * codepoints in UTF-8). This may have to change at some point
48 **/
51 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
53 static SIG_ATOMIC_T gotalarm;
55 /***************************************************************
56 Signal function to tell us we timed out.
57 ****************************************************************/
59 static void gotalarm_sig(int signum)
61 gotalarm = 1;
64 LDAP *ldap_open_with_timeout(const char *server,
65 struct sockaddr_storage *ss,
66 int port, unsigned int to)
68 LDAP *ldp = NULL;
69 int ldap_err;
70 char *uri;
72 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
73 "%u seconds\n", server, port, to));
75 if (to) {
76 /* Setup timeout */
77 gotalarm = 0;
78 CatchSignal(SIGALRM, gotalarm_sig);
79 alarm(to);
80 /* End setup timeout. */
83 if ( strchr_m(server, ':') ) {
84 /* IPv6 URI */
85 uri = talloc_asprintf(talloc_tos(), "ldap://[%s]:%u", server, port);
86 } else {
87 /* IPv4 URI */
88 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
90 if (uri == NULL) {
91 return NULL;
94 #ifdef HAVE_LDAP_INITIALIZE
95 ldap_err = ldap_initialize(&ldp, uri);
96 #else
97 ldp = ldap_open(server, port);
98 if (ldp != NULL) {
99 ldap_err = LDAP_SUCCESS;
100 } else {
101 ldap_err = LDAP_OTHER;
103 #endif
104 if (ldap_err != LDAP_SUCCESS) {
105 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
106 uri, ldap_err2string(ldap_err)));
107 } else {
108 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
111 if (to) {
112 /* Teardown timeout. */
113 alarm(0);
114 CatchSignal(SIGALRM, SIG_IGN);
117 return ldp;
120 static int ldap_search_with_timeout(LDAP *ld,
121 LDAP_CONST char *base,
122 int scope,
123 LDAP_CONST char *filter,
124 char **attrs,
125 int attrsonly,
126 LDAPControl **sctrls,
127 LDAPControl **cctrls,
128 int sizelimit,
129 LDAPMessage **res )
131 int to = lp_ldap_timeout();
132 struct timeval timeout;
133 struct timeval *timeout_ptr = NULL;
134 int result;
136 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
137 gotalarm = 0;
139 if (to) {
140 timeout.tv_sec = to;
141 timeout.tv_usec = 0;
142 timeout_ptr = &timeout;
144 /* Setup alarm timeout. */
145 CatchSignal(SIGALRM, gotalarm_sig);
146 /* Make the alarm time one second beyond
147 the timout we're setting for the
148 remote search timeout, to allow that
149 to fire in preference. */
150 alarm(to+1);
151 /* End setup timeout. */
155 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
156 attrsonly, sctrls, cctrls, timeout_ptr,
157 sizelimit, res);
159 if (to) {
160 /* Teardown alarm timeout. */
161 CatchSignal(SIGALRM, SIG_IGN);
162 alarm(0);
165 if (gotalarm != 0)
166 return LDAP_TIMELIMIT_EXCEEDED;
169 * A bug in OpenLDAP means ldap_search_ext_s can return
170 * LDAP_SUCCESS but with a NULL res pointer. Cope with
171 * this. See bug #6279 for details. JRA.
174 if (*res == NULL) {
175 return LDAP_TIMELIMIT_EXCEEDED;
178 return result;
181 /**********************************************
182 Do client and server sitename match ?
183 **********************************************/
185 bool ads_sitename_match(ADS_STRUCT *ads)
187 if (ads->config.server_site_name == NULL &&
188 ads->config.client_site_name == NULL ) {
189 DEBUG(10,("ads_sitename_match: both null\n"));
190 return True;
192 if (ads->config.server_site_name &&
193 ads->config.client_site_name &&
194 strequal(ads->config.server_site_name,
195 ads->config.client_site_name)) {
196 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
197 return True;
199 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
200 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
201 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
202 return False;
205 /**********************************************
206 Is this the closest DC ?
207 **********************************************/
209 bool ads_closest_dc(ADS_STRUCT *ads)
211 if (ads->config.flags & NBT_SERVER_CLOSEST) {
212 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
213 return True;
216 /* not sure if this can ever happen */
217 if (ads_sitename_match(ads)) {
218 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
219 return True;
222 if (ads->config.client_site_name == NULL) {
223 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
224 return True;
227 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
228 ads->config.ldap_server_name));
230 return False;
235 try a connection to a given ldap server, returning True and setting the servers IP
236 in the ads struct if successful
238 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
239 struct sockaddr_storage *ss)
241 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
242 TALLOC_CTX *frame = talloc_stackframe();
243 bool ret = false;
244 char addr[INET6_ADDRSTRLEN];
246 if (ss == NULL) {
247 TALLOC_FREE(frame);
248 return False;
251 print_sockaddr(addr, sizeof(addr), ss);
253 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
254 addr, ads->server.realm));
256 ZERO_STRUCT( cldap_reply );
258 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
259 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
260 ret = false;
261 goto out;
264 /* Check the CLDAP reply flags */
266 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
267 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
268 addr));
269 ret = false;
270 goto out;
273 /* Fill in the ads->config values */
275 SAFE_FREE(ads->config.realm);
276 SAFE_FREE(ads->config.bind_path);
277 SAFE_FREE(ads->config.ldap_server_name);
278 SAFE_FREE(ads->config.server_site_name);
279 SAFE_FREE(ads->config.client_site_name);
280 SAFE_FREE(ads->server.workgroup);
282 ads->config.flags = cldap_reply.server_type;
283 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
284 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
285 if (!strupper_m(ads->config.realm)) {
286 ret = false;
287 goto out;
290 ads->config.bind_path = ads_build_dn(ads->config.realm);
291 if (*cldap_reply.server_site) {
292 ads->config.server_site_name =
293 SMB_STRDUP(cldap_reply.server_site);
295 if (*cldap_reply.client_site) {
296 ads->config.client_site_name =
297 SMB_STRDUP(cldap_reply.client_site);
299 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
301 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
302 ads->ldap.ss = *ss;
304 /* Store our site name. */
305 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
306 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
308 ret = true;
310 out:
312 TALLOC_FREE(frame);
313 return ret;
316 /**********************************************************************
317 send a cldap ping to list of servers, one at a time, until one of
318 them answers it's an ldap server. Record success in the ADS_STRUCT.
319 Take note of and update negative connection cache.
320 **********************************************************************/
322 static NTSTATUS cldap_ping_list(ADS_STRUCT *ads,const char *domain,
323 struct ip_service *ip_list, int count)
325 int i;
326 bool ok;
328 for (i = 0; i < count; i++) {
329 char server[INET6_ADDRSTRLEN];
331 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
333 if (!NT_STATUS_IS_OK(
334 check_negative_conn_cache(domain, server)))
335 continue;
337 ok = ads_try_connect(ads, false, &ip_list[i].ss);
338 if (ok) {
339 return NT_STATUS_OK;
342 /* keep track of failures */
343 add_failed_connection_entry(domain, server,
344 NT_STATUS_UNSUCCESSFUL);
347 return NT_STATUS_NO_LOGON_SERVERS;
350 /***************************************************************************
351 resolve a name and perform an "ldap ping" using NetBIOS and related methods
352 ****************************************************************************/
354 static NTSTATUS resolve_and_ping_netbios(ADS_STRUCT *ads,
355 const char *domain, const char *realm)
357 int count, i;
358 struct ip_service *ip_list;
359 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
361 DEBUG(6, ("resolve_and_ping_netbios: (cldap) looking for domain '%s'\n",
362 domain));
364 status = get_sorted_dc_list(domain, NULL, &ip_list, &count,
365 false);
366 if (!NT_STATUS_IS_OK(status)) {
367 return status;
370 /* remove servers which are known to be dead based on
371 the corresponding DNS method */
372 if (*realm) {
373 for (i = 0; i < count; ++i) {
374 char server[INET6_ADDRSTRLEN];
376 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
378 if(!NT_STATUS_IS_OK(
379 check_negative_conn_cache(realm, server))) {
380 /* Ensure we add the workgroup name for this
381 IP address as negative too. */
382 add_failed_connection_entry(
383 domain, server,
384 NT_STATUS_UNSUCCESSFUL);
389 status = cldap_ping_list(ads, domain, ip_list, count);
391 SAFE_FREE(ip_list);
393 return status;
397 /**********************************************************************
398 resolve a name and perform an "ldap ping" using DNS
399 **********************************************************************/
401 static NTSTATUS resolve_and_ping_dns(ADS_STRUCT *ads, const char *sitename,
402 const char *realm)
404 int count;
405 struct ip_service *ip_list;
406 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
408 DEBUG(6, ("resolve_and_ping_dns: (cldap) looking for realm '%s'\n",
409 realm));
411 status = get_sorted_dc_list(realm, sitename, &ip_list, &count,
412 true);
413 if (!NT_STATUS_IS_OK(status)) {
414 return status;
417 status = cldap_ping_list(ads, realm, ip_list, count);
419 SAFE_FREE(ip_list);
421 return status;
424 /**********************************************************************
425 Try to find an AD dc using our internal name resolution routines
426 Try the realm first and then then workgroup name if netbios is not
427 disabled
428 **********************************************************************/
430 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
432 const char *c_domain = "";
433 const char *c_realm;
434 bool use_own_domain = False;
435 char *sitename = NULL;
436 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
437 bool ok = false;
439 /* if the realm and workgroup are both empty, assume they are ours */
441 /* realm */
442 c_realm = ads->server.realm;
444 if (c_realm == NULL)
445 c_realm = "";
447 if (!*c_realm) {
448 /* special case where no realm and no workgroup means our own */
449 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
450 use_own_domain = True;
451 c_realm = lp_realm();
455 if (!lp_disable_netbios()) {
456 if (use_own_domain) {
457 c_domain = lp_workgroup();
458 } else {
459 c_domain = ads->server.workgroup;
460 if (!*c_realm && (!c_domain || !*c_domain)) {
461 c_domain = lp_workgroup();
465 if (!c_domain) {
466 c_domain = "";
470 if (!*c_realm && !*c_domain) {
471 DEBUG(0, ("ads_find_dc: no realm or workgroup! Don't know "
472 "what to do\n"));
473 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
477 * In case of LDAP we use get_dc_name() as that
478 * creates the custom krb5.conf file
480 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
481 fstring srv_name;
482 struct sockaddr_storage ip_out;
484 DEBUG(6, ("ads_find_dc: (ldap) looking for realm '%s'"
485 " and falling back to domain '%s'\n",
486 c_realm, c_domain));
488 ok = get_dc_name(c_domain, c_realm, srv_name, &ip_out);
489 if (ok) {
491 * we call ads_try_connect() to fill in the
492 * ads->config details
494 ok = ads_try_connect(ads, false, &ip_out);
495 if (ok) {
496 return NT_STATUS_OK;
500 return NT_STATUS_NO_LOGON_SERVERS;
503 if (*c_realm) {
504 sitename = sitename_fetch(talloc_tos(), c_realm);
505 status = resolve_and_ping_dns(ads, sitename, c_realm);
507 if (NT_STATUS_IS_OK(status)) {
508 TALLOC_FREE(sitename);
509 return status;
512 /* In case we failed to contact one of our closest DC on our
513 * site we
514 * need to try to find another DC, retry with a site-less SRV
515 * DNS query
516 * - Guenther */
518 if (sitename) {
519 DEBUG(3, ("ads_find_dc: failed to find a valid DC on "
520 "our site (%s), Trying to find another DC "
521 "for realm '%s' (domain '%s')\n",
522 sitename, c_realm, c_domain));
523 namecache_delete(c_realm, 0x1C);
524 status =
525 resolve_and_ping_dns(ads, NULL, c_realm);
527 if (NT_STATUS_IS_OK(status)) {
528 TALLOC_FREE(sitename);
529 return status;
533 TALLOC_FREE(sitename);
536 /* try netbios as fallback - if permitted,
537 or if configuration specifically requests it */
538 if (*c_domain) {
539 if (*c_realm) {
540 DEBUG(3, ("ads_find_dc: falling back to netbios "
541 "name resolution for domain '%s' (realm '%s')\n",
542 c_domain, c_realm));
545 status = resolve_and_ping_netbios(ads, c_domain, c_realm);
546 if (NT_STATUS_IS_OK(status)) {
547 return status;
551 DEBUG(1, ("ads_find_dc: "
552 "name resolution for realm '%s' (domain '%s') failed: %s\n",
553 c_realm, c_domain, nt_errstr(status)));
554 return status;
557 * Connect to the LDAP server
558 * @param ads Pointer to an existing ADS_STRUCT
559 * @return status of connection
561 ADS_STATUS ads_connect(ADS_STRUCT *ads)
563 int version = LDAP_VERSION3;
564 ADS_STATUS status;
565 NTSTATUS ntstatus;
566 char addr[INET6_ADDRSTRLEN];
568 ZERO_STRUCT(ads->ldap);
569 ads->ldap.last_attempt = time_mono(NULL);
570 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
572 /* try with a user specified server */
574 if (DEBUGLEVEL >= 11) {
575 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
576 DEBUG(11,("ads_connect: entering\n"));
577 DEBUGADD(11,("%s\n", s));
578 TALLOC_FREE(s);
581 if (ads->server.ldap_server) {
582 bool ok = false;
583 struct sockaddr_storage ss;
585 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
586 if (!ok) {
587 DEBUG(5,("ads_connect: unable to resolve name %s\n",
588 ads->server.ldap_server));
589 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
590 goto out;
592 ok = ads_try_connect(ads, ads->server.gc, &ss);
593 if (ok) {
594 goto got_connection;
597 /* The choice of which GC use is handled one level up in
598 ads_connect_gc(). If we continue on from here with
599 ads_find_dc() we will get GC searches on port 389 which
600 doesn't work. --jerry */
602 if (ads->server.gc == true) {
603 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
607 ntstatus = ads_find_dc(ads);
608 if (NT_STATUS_IS_OK(ntstatus)) {
609 goto got_connection;
612 status = ADS_ERROR_NT(ntstatus);
613 goto out;
615 got_connection:
617 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
618 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
620 if (!ads->auth.user_name) {
621 /* Must use the userPrincipalName value here or sAMAccountName
622 and not servicePrincipalName; found by Guenther Deschner */
624 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
625 DEBUG(0,("ads_connect: asprintf fail.\n"));
626 ads->auth.user_name = NULL;
630 if (!ads->auth.realm) {
631 ads->auth.realm = SMB_STRDUP(ads->config.realm);
634 if (!ads->auth.kdc_server) {
635 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
636 ads->auth.kdc_server = SMB_STRDUP(addr);
639 /* If the caller() requested no LDAP bind, then we are done */
641 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
642 status = ADS_SUCCESS;
643 goto out;
646 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
647 if (!ads->ldap.mem_ctx) {
648 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
649 goto out;
652 /* Otherwise setup the TCP LDAP session */
654 ads->ldap.ld = ldap_open_with_timeout(addr,
655 &ads->ldap.ss,
656 ads->ldap.port, lp_ldap_timeout());
657 if (ads->ldap.ld == NULL) {
658 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
659 goto out;
661 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
663 /* cache the successful connection for workgroup and realm */
664 if (ads_closest_dc(ads)) {
665 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
666 saf_store( ads->server.realm, ads->config.ldap_server_name);
669 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
671 if ( lp_ldap_ssl_ads() ) {
672 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
673 if (!ADS_ERR_OK(status)) {
674 goto out;
678 /* fill in the current time and offsets */
680 status = ads_current_time( ads );
681 if ( !ADS_ERR_OK(status) ) {
682 goto out;
685 /* Now do the bind */
687 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
688 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
689 goto out;
692 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
693 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
694 goto out;
697 status = ads_sasl_bind(ads);
699 out:
700 if (DEBUGLEVEL >= 11) {
701 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
702 DEBUG(11,("ads_connect: leaving with: %s\n",
703 ads_errstr(status)));
704 DEBUGADD(11,("%s\n", s));
705 TALLOC_FREE(s);
708 return status;
712 * Connect to the LDAP server using given credentials
713 * @param ads Pointer to an existing ADS_STRUCT
714 * @return status of connection
716 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
718 ads->auth.flags |= ADS_AUTH_USER_CREDS;
720 return ads_connect(ads);
724 * Disconnect the LDAP server
725 * @param ads Pointer to an existing ADS_STRUCT
727 void ads_disconnect(ADS_STRUCT *ads)
729 if (ads->ldap.ld) {
730 ldap_unbind(ads->ldap.ld);
731 ads->ldap.ld = NULL;
733 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
734 ads->ldap.wrap_ops->disconnect(ads);
736 if (ads->ldap.mem_ctx) {
737 talloc_free(ads->ldap.mem_ctx);
739 ZERO_STRUCT(ads->ldap);
743 Duplicate a struct berval into talloc'ed memory
745 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
747 struct berval *value;
749 if (!in_val) return NULL;
751 value = talloc_zero(ctx, struct berval);
752 if (value == NULL)
753 return NULL;
754 if (in_val->bv_len == 0) return value;
756 value->bv_len = in_val->bv_len;
757 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
758 in_val->bv_len);
759 return value;
763 Make a values list out of an array of (struct berval *)
765 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
766 const struct berval **in_vals)
768 struct berval **values;
769 int i;
771 if (!in_vals) return NULL;
772 for (i=0; in_vals[i]; i++)
773 ; /* count values */
774 values = talloc_zero_array(ctx, struct berval *, i+1);
775 if (!values) return NULL;
777 for (i=0; in_vals[i]; i++) {
778 values[i] = dup_berval(ctx, in_vals[i]);
780 return values;
784 UTF8-encode a values list out of an array of (char *)
786 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
788 char **values;
789 int i;
790 size_t size;
792 if (!in_vals) return NULL;
793 for (i=0; in_vals[i]; i++)
794 ; /* count values */
795 values = talloc_zero_array(ctx, char *, i+1);
796 if (!values) return NULL;
798 for (i=0; in_vals[i]; i++) {
799 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
800 TALLOC_FREE(values);
801 return NULL;
804 return values;
808 Pull a (char *) array out of a UTF8-encoded values list
810 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
812 char **values;
813 int i;
814 size_t converted_size;
816 if (!in_vals) return NULL;
817 for (i=0; in_vals[i]; i++)
818 ; /* count values */
819 values = talloc_zero_array(ctx, char *, i+1);
820 if (!values) return NULL;
822 for (i=0; in_vals[i]; i++) {
823 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
824 &converted_size)) {
825 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
826 "%s", strerror(errno)));
829 return values;
833 * Do a search with paged results. cookie must be null on the first
834 * call, and then returned on each subsequent call. It will be null
835 * again when the entire search is complete
836 * @param ads connection to ads server
837 * @param bind_path Base dn for the search
838 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
839 * @param expr Search expression - specified in local charset
840 * @param attrs Attributes to retrieve - specified in utf8 or ascii
841 * @param res ** which will contain results - free res* with ads_msgfree()
842 * @param count Number of entries retrieved on this page
843 * @param cookie The paged results cookie to be returned on subsequent calls
844 * @return status of search
846 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
847 const char *bind_path,
848 int scope, const char *expr,
849 const char **attrs, void *args,
850 LDAPMessage **res,
851 int *count, struct berval **cookie)
853 int rc, i, version;
854 char *utf8_expr, *utf8_path, **search_attrs = NULL;
855 size_t converted_size;
856 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
857 BerElement *cookie_be = NULL;
858 struct berval *cookie_bv= NULL;
859 BerElement *ext_be = NULL;
860 struct berval *ext_bv= NULL;
862 TALLOC_CTX *ctx;
863 ads_control *external_control = (ads_control *) args;
865 *res = NULL;
867 if (!(ctx = talloc_init("ads_do_paged_search_args")))
868 return ADS_ERROR(LDAP_NO_MEMORY);
870 /* 0 means the conversion worked but the result was empty
871 so we only fail if it's -1. In any case, it always
872 at least nulls out the dest */
873 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
874 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
876 rc = LDAP_NO_MEMORY;
877 goto done;
880 if (!attrs || !(*attrs))
881 search_attrs = NULL;
882 else {
883 /* This would be the utf8-encoded version...*/
884 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
885 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
886 rc = LDAP_NO_MEMORY;
887 goto done;
891 /* Paged results only available on ldap v3 or later */
892 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
893 if (version < LDAP_VERSION3) {
894 rc = LDAP_NOT_SUPPORTED;
895 goto done;
898 cookie_be = ber_alloc_t(LBER_USE_DER);
899 if (*cookie) {
900 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
901 ber_bvfree(*cookie); /* don't need it from last time */
902 *cookie = NULL;
903 } else {
904 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
906 ber_flatten(cookie_be, &cookie_bv);
907 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
908 PagedResults.ldctl_iscritical = (char) 1;
909 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
910 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
912 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
913 NoReferrals.ldctl_iscritical = (char) 0;
914 NoReferrals.ldctl_value.bv_len = 0;
915 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
917 if (external_control &&
918 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
919 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
921 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
922 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
924 /* win2k does not accept a ldctl_value beeing passed in */
926 if (external_control->val != 0) {
928 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
929 rc = LDAP_NO_MEMORY;
930 goto done;
933 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
934 rc = LDAP_NO_MEMORY;
935 goto done;
937 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
938 rc = LDAP_NO_MEMORY;
939 goto done;
942 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
943 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
945 } else {
946 ExternalCtrl.ldctl_value.bv_len = 0;
947 ExternalCtrl.ldctl_value.bv_val = NULL;
950 controls[0] = &NoReferrals;
951 controls[1] = &PagedResults;
952 controls[2] = &ExternalCtrl;
953 controls[3] = NULL;
955 } else {
956 controls[0] = &NoReferrals;
957 controls[1] = &PagedResults;
958 controls[2] = NULL;
961 /* we need to disable referrals as the openldap libs don't
962 handle them and paged results at the same time. Using them
963 together results in the result record containing the server
964 page control being removed from the result list (tridge/jmcd)
966 leaving this in despite the control that says don't generate
967 referrals, in case the server doesn't support it (jmcd)
969 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
971 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
972 search_attrs, 0, controls,
973 NULL, LDAP_NO_LIMIT,
974 (LDAPMessage **)res);
976 ber_free(cookie_be, 1);
977 ber_bvfree(cookie_bv);
979 if (rc) {
980 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
981 ldap_err2string(rc)));
982 if (rc == LDAP_OTHER) {
983 char *ldap_errmsg;
984 int ret;
986 ret = ldap_parse_result(ads->ldap.ld,
987 *res,
988 NULL,
989 NULL,
990 &ldap_errmsg,
991 NULL,
992 NULL,
994 if (ret == LDAP_SUCCESS) {
995 DEBUG(3, ("ldap_search_with_timeout(%s) "
996 "error: %s\n", expr, ldap_errmsg));
997 ldap_memfree(ldap_errmsg);
1000 goto done;
1003 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1004 NULL, &rcontrols, 0);
1006 if (!rcontrols) {
1007 goto done;
1010 for (i=0; rcontrols[i]; i++) {
1011 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1012 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1013 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1014 &cookie_bv);
1015 /* the berval is the cookie, but must be freed when
1016 it is all done */
1017 if (cookie_bv->bv_len) /* still more to do */
1018 *cookie=ber_bvdup(cookie_bv);
1019 else
1020 *cookie=NULL;
1021 ber_bvfree(cookie_bv);
1022 ber_free(cookie_be, 1);
1023 break;
1026 ldap_controls_free(rcontrols);
1028 done:
1029 talloc_destroy(ctx);
1031 if (ext_be) {
1032 ber_free(ext_be, 1);
1035 if (ext_bv) {
1036 ber_bvfree(ext_bv);
1039 if (rc != LDAP_SUCCESS && *res != NULL) {
1040 ads_msgfree(ads, *res);
1041 *res = NULL;
1044 /* if/when we decide to utf8-encode attrs, take out this next line */
1045 TALLOC_FREE(search_attrs);
1047 return ADS_ERROR(rc);
1050 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1051 int scope, const char *expr,
1052 const char **attrs, LDAPMessage **res,
1053 int *count, struct berval **cookie)
1055 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1060 * Get all results for a search. This uses ads_do_paged_search() to return
1061 * all entries in a large search.
1062 * @param ads connection to ads server
1063 * @param bind_path Base dn for the search
1064 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1065 * @param expr Search expression
1066 * @param attrs Attributes to retrieve
1067 * @param res ** which will contain results - free res* with ads_msgfree()
1068 * @return status of search
1070 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1071 int scope, const char *expr,
1072 const char **attrs, void *args,
1073 LDAPMessage **res)
1075 struct berval *cookie = NULL;
1076 int count = 0;
1077 ADS_STATUS status;
1079 *res = NULL;
1080 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1081 &count, &cookie);
1083 if (!ADS_ERR_OK(status))
1084 return status;
1086 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1087 while (cookie) {
1088 LDAPMessage *res2 = NULL;
1089 LDAPMessage *msg, *next;
1091 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1092 attrs, args, &res2, &count, &cookie);
1093 if (!ADS_ERR_OK(status)) {
1094 break;
1097 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1098 that this works on all ldap libs, but I have only tested with openldap */
1099 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1100 next = ads_next_message(ads, msg);
1101 ldap_add_result_entry((LDAPMessage **)res, msg);
1103 /* note that we do not free res2, as the memory is now
1104 part of the main returned list */
1106 #else
1107 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1108 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1109 #endif
1111 return status;
1114 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1115 int scope, const char *expr,
1116 const char **attrs, LDAPMessage **res)
1118 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1121 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1122 int scope, const char *expr,
1123 const char **attrs, uint32_t sd_flags,
1124 LDAPMessage **res)
1126 ads_control args;
1128 args.control = ADS_SD_FLAGS_OID;
1129 args.val = sd_flags;
1130 args.critical = True;
1132 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1137 * Run a function on all results for a search. Uses ads_do_paged_search() and
1138 * runs the function as each page is returned, using ads_process_results()
1139 * @param ads connection to ads server
1140 * @param bind_path Base dn for the search
1141 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1142 * @param expr Search expression - specified in local charset
1143 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1144 * @param fn Function which takes attr name, values list, and data_area
1145 * @param data_area Pointer which is passed to function on each call
1146 * @return status of search
1148 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1149 int scope, const char *expr, const char **attrs,
1150 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1151 void *data_area)
1153 struct berval *cookie = NULL;
1154 int count = 0;
1155 ADS_STATUS status;
1156 LDAPMessage *res;
1158 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1159 &count, &cookie);
1161 if (!ADS_ERR_OK(status)) return status;
1163 ads_process_results(ads, res, fn, data_area);
1164 ads_msgfree(ads, res);
1166 while (cookie) {
1167 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1168 &res, &count, &cookie);
1170 if (!ADS_ERR_OK(status)) break;
1172 ads_process_results(ads, res, fn, data_area);
1173 ads_msgfree(ads, res);
1176 return status;
1180 * Do a search with a timeout.
1181 * @param ads connection to ads server
1182 * @param bind_path Base dn for the search
1183 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1184 * @param expr Search expression
1185 * @param attrs Attributes to retrieve
1186 * @param res ** which will contain results - free res* with ads_msgfree()
1187 * @return status of search
1189 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1190 const char *expr,
1191 const char **attrs, LDAPMessage **res)
1193 int rc;
1194 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1195 size_t converted_size;
1196 TALLOC_CTX *ctx;
1198 *res = NULL;
1199 if (!(ctx = talloc_init("ads_do_search"))) {
1200 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1201 return ADS_ERROR(LDAP_NO_MEMORY);
1204 /* 0 means the conversion worked but the result was empty
1205 so we only fail if it's negative. In any case, it always
1206 at least nulls out the dest */
1207 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1208 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1210 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1211 rc = LDAP_NO_MEMORY;
1212 goto done;
1215 if (!attrs || !(*attrs))
1216 search_attrs = NULL;
1217 else {
1218 /* This would be the utf8-encoded version...*/
1219 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1220 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1222 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1223 rc = LDAP_NO_MEMORY;
1224 goto done;
1228 /* see the note in ads_do_paged_search - we *must* disable referrals */
1229 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1231 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1232 search_attrs, 0, NULL, NULL,
1233 LDAP_NO_LIMIT,
1234 (LDAPMessage **)res);
1236 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1237 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1238 rc = 0;
1241 done:
1242 talloc_destroy(ctx);
1243 /* if/when we decide to utf8-encode attrs, take out this next line */
1244 TALLOC_FREE(search_attrs);
1245 return ADS_ERROR(rc);
1248 * Do a general ADS search
1249 * @param ads connection to ads server
1250 * @param res ** which will contain results - free res* with ads_msgfree()
1251 * @param expr Search expression
1252 * @param attrs Attributes to retrieve
1253 * @return status of search
1255 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1256 const char *expr, const char **attrs)
1258 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1259 expr, attrs, res);
1263 * Do a search on a specific DistinguishedName
1264 * @param ads connection to ads server
1265 * @param res ** which will contain results - free res* with ads_msgfree()
1266 * @param dn DistinguishName to search
1267 * @param attrs Attributes to retrieve
1268 * @return status of search
1270 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1271 const char *dn, const char **attrs)
1273 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1274 attrs, res);
1278 * Free up memory from a ads_search
1279 * @param ads connection to ads server
1280 * @param msg Search results to free
1282 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1284 if (!msg) return;
1285 ldap_msgfree(msg);
1289 * Get a dn from search results
1290 * @param ads connection to ads server
1291 * @param msg Search result
1292 * @return dn string
1294 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1296 char *utf8_dn, *unix_dn;
1297 size_t converted_size;
1299 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1301 if (!utf8_dn) {
1302 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1303 return NULL;
1306 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1307 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1308 utf8_dn ));
1309 return NULL;
1311 ldap_memfree(utf8_dn);
1312 return unix_dn;
1316 * Get the parent from a dn
1317 * @param dn the dn to return the parent from
1318 * @return parent dn string
1320 char *ads_parent_dn(const char *dn)
1322 char *p;
1324 if (dn == NULL) {
1325 return NULL;
1328 p = strchr(dn, ',');
1330 if (p == NULL) {
1331 return NULL;
1334 return p+1;
1338 * Find a machine account given a hostname
1339 * @param ads connection to ads server
1340 * @param res ** which will contain results - free res* with ads_msgfree()
1341 * @param host Hostname to search for
1342 * @return status of search
1344 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1345 const char *machine)
1347 ADS_STATUS status;
1348 char *expr;
1349 const char *attrs[] = {"*", "msDS-SupportedEncryptionTypes", "nTSecurityDescriptor", NULL};
1351 *res = NULL;
1353 /* the easiest way to find a machine account anywhere in the tree
1354 is to look for hostname$ */
1355 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1356 DEBUG(1, ("asprintf failed!\n"));
1357 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1360 status = ads_search(ads, res, expr, attrs);
1361 SAFE_FREE(expr);
1362 return status;
1366 * Initialize a list of mods to be used in a modify request
1367 * @param ctx An initialized TALLOC_CTX
1368 * @return allocated ADS_MODLIST
1370 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1372 #define ADS_MODLIST_ALLOC_SIZE 10
1373 LDAPMod **mods;
1375 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1376 /* -1 is safety to make sure we don't go over the end.
1377 need to reset it to NULL before doing ldap modify */
1378 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1380 return (ADS_MODLIST)mods;
1385 add an attribute to the list, with values list already constructed
1387 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1388 int mod_op, const char *name,
1389 const void *_invals)
1391 int curmod;
1392 LDAPMod **modlist = (LDAPMod **) *mods;
1393 struct berval **ber_values = NULL;
1394 char **char_values = NULL;
1396 if (!_invals) {
1397 mod_op = LDAP_MOD_DELETE;
1398 } else {
1399 if (mod_op & LDAP_MOD_BVALUES) {
1400 const struct berval **b;
1401 b = discard_const_p(const struct berval *, _invals);
1402 ber_values = ads_dup_values(ctx, b);
1403 } else {
1404 const char **c;
1405 c = discard_const_p(const char *, _invals);
1406 char_values = ads_push_strvals(ctx, c);
1410 /* find the first empty slot */
1411 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1412 curmod++);
1413 if (modlist[curmod] == (LDAPMod *) -1) {
1414 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1415 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1416 return ADS_ERROR(LDAP_NO_MEMORY);
1417 memset(&modlist[curmod], 0,
1418 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1419 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1420 *mods = (ADS_MODLIST)modlist;
1423 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1424 return ADS_ERROR(LDAP_NO_MEMORY);
1425 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1426 if (mod_op & LDAP_MOD_BVALUES) {
1427 modlist[curmod]->mod_bvalues = ber_values;
1428 } else if (mod_op & LDAP_MOD_DELETE) {
1429 modlist[curmod]->mod_values = NULL;
1430 } else {
1431 modlist[curmod]->mod_values = char_values;
1434 modlist[curmod]->mod_op = mod_op;
1435 return ADS_ERROR(LDAP_SUCCESS);
1439 * Add a single string value to a mod list
1440 * @param ctx An initialized TALLOC_CTX
1441 * @param mods An initialized ADS_MODLIST
1442 * @param name The attribute name to add
1443 * @param val The value to add - NULL means DELETE
1444 * @return ADS STATUS indicating success of add
1446 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1447 const char *name, const char *val)
1449 const char *values[2];
1451 values[0] = val;
1452 values[1] = NULL;
1454 if (!val)
1455 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1456 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1460 * Add an array of string values to a mod list
1461 * @param ctx An initialized TALLOC_CTX
1462 * @param mods An initialized ADS_MODLIST
1463 * @param name The attribute name to add
1464 * @param vals The array of string values to add - NULL means DELETE
1465 * @return ADS STATUS indicating success of add
1467 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1468 const char *name, const char **vals)
1470 if (!vals)
1471 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1472 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1473 name, (const void **) vals);
1476 #if 0
1478 * Add a single ber-encoded value to a mod list
1479 * @param ctx An initialized TALLOC_CTX
1480 * @param mods An initialized ADS_MODLIST
1481 * @param name The attribute name to add
1482 * @param val The value to add - NULL means DELETE
1483 * @return ADS STATUS indicating success of add
1485 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1486 const char *name, const struct berval *val)
1488 const struct berval *values[2];
1490 values[0] = val;
1491 values[1] = NULL;
1492 if (!val)
1493 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1494 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1495 name, (const void **) values);
1497 #endif
1499 static void ads_print_error(int ret, LDAP *ld)
1501 if (ret != 0) {
1502 char *ld_error = NULL;
1503 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1504 DEBUG(10,("AD LDAP failure %d (%s):\n%s\n", ret,
1505 ldap_err2string(ret), ld_error));
1506 SAFE_FREE(ld_error);
1511 * Perform an ldap modify
1512 * @param ads connection to ads server
1513 * @param mod_dn DistinguishedName to modify
1514 * @param mods list of modifications to perform
1515 * @return status of modify
1517 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1519 int ret,i;
1520 char *utf8_dn = NULL;
1521 size_t converted_size;
1523 this control is needed to modify that contains a currently
1524 non-existent attribute (but allowable for the object) to run
1526 LDAPControl PermitModify = {
1527 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1528 {0, NULL},
1529 (char) 1};
1530 LDAPControl *controls[2];
1532 controls[0] = &PermitModify;
1533 controls[1] = NULL;
1535 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1536 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1539 /* find the end of the list, marked by NULL or -1 */
1540 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1541 /* make sure the end of the list is NULL */
1542 mods[i] = NULL;
1543 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1544 (LDAPMod **) mods, controls, NULL);
1545 ads_print_error(ret, ads->ldap.ld);
1546 TALLOC_FREE(utf8_dn);
1547 return ADS_ERROR(ret);
1551 * Perform an ldap add
1552 * @param ads connection to ads server
1553 * @param new_dn DistinguishedName to add
1554 * @param mods list of attributes and values for DN
1555 * @return status of add
1557 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1559 int ret, i;
1560 char *utf8_dn = NULL;
1561 size_t converted_size;
1563 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1564 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1565 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1568 /* find the end of the list, marked by NULL or -1 */
1569 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1570 /* make sure the end of the list is NULL */
1571 mods[i] = NULL;
1573 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1574 ads_print_error(ret, ads->ldap.ld);
1575 TALLOC_FREE(utf8_dn);
1576 return ADS_ERROR(ret);
1580 * Delete a DistinguishedName
1581 * @param ads connection to ads server
1582 * @param new_dn DistinguishedName to delete
1583 * @return status of delete
1585 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1587 int ret;
1588 char *utf8_dn = NULL;
1589 size_t converted_size;
1590 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1591 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1592 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1595 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1596 ads_print_error(ret, ads->ldap.ld);
1597 TALLOC_FREE(utf8_dn);
1598 return ADS_ERROR(ret);
1602 * Build an org unit string
1603 * if org unit is Computers or blank then assume a container, otherwise
1604 * assume a / separated list of organisational units.
1605 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1606 * @param ads connection to ads server
1607 * @param org_unit Organizational unit
1608 * @return org unit string - caller must free
1610 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1612 char *ret = NULL;
1614 if (!org_unit || !*org_unit) {
1616 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1618 /* samba4 might not yet respond to a wellknownobject-query */
1619 return ret ? ret : SMB_STRDUP("cn=Computers");
1622 if (strequal(org_unit, "Computers")) {
1623 return SMB_STRDUP("cn=Computers");
1626 /* jmcd: removed "\\" from the separation chars, because it is
1627 needed as an escape for chars like '#' which are valid in an
1628 OU name */
1629 return ads_build_path(org_unit, "/", "ou=", 1);
1633 * Get a org unit string for a well-known GUID
1634 * @param ads connection to ads server
1635 * @param wknguid Well known GUID
1636 * @return org unit string - caller must free
1638 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1640 ADS_STATUS status;
1641 LDAPMessage *res = NULL;
1642 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1643 **bind_dn_exp = NULL;
1644 const char *attrs[] = {"distinguishedName", NULL};
1645 int new_ln, wkn_ln, bind_ln, i;
1647 if (wknguid == NULL) {
1648 return NULL;
1651 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1652 DEBUG(1, ("asprintf failed!\n"));
1653 return NULL;
1656 status = ads_search_dn(ads, &res, base, attrs);
1657 if (!ADS_ERR_OK(status)) {
1658 DEBUG(1,("Failed while searching for: %s\n", base));
1659 goto out;
1662 if (ads_count_replies(ads, res) != 1) {
1663 goto out;
1666 /* substitute the bind-path from the well-known-guid-search result */
1667 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1668 if (!wkn_dn) {
1669 goto out;
1672 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1673 if (!wkn_dn_exp) {
1674 goto out;
1677 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1678 if (!bind_dn_exp) {
1679 goto out;
1682 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1684 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1687 new_ln = wkn_ln - bind_ln;
1689 ret = SMB_STRDUP(wkn_dn_exp[0]);
1690 if (!ret) {
1691 goto out;
1694 for (i=1; i < new_ln; i++) {
1695 char *s = NULL;
1697 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1698 SAFE_FREE(ret);
1699 goto out;
1702 SAFE_FREE(ret);
1703 ret = SMB_STRDUP(s);
1704 free(s);
1705 if (!ret) {
1706 goto out;
1710 out:
1711 SAFE_FREE(base);
1712 ads_msgfree(ads, res);
1713 TALLOC_FREE(wkn_dn);
1714 if (wkn_dn_exp) {
1715 ldap_value_free(wkn_dn_exp);
1717 if (bind_dn_exp) {
1718 ldap_value_free(bind_dn_exp);
1721 return ret;
1725 * Adds (appends) an item to an attribute array, rather then
1726 * replacing the whole list
1727 * @param ctx An initialized TALLOC_CTX
1728 * @param mods An initialized ADS_MODLIST
1729 * @param name name of the ldap attribute to append to
1730 * @param vals an array of values to add
1731 * @return status of addition
1734 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1735 const char *name, const char **vals)
1737 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1738 (const void *) vals);
1742 * Determines the an account's current KVNO via an LDAP lookup
1743 * @param ads An initialized ADS_STRUCT
1744 * @param account_name the NT samaccountname.
1745 * @return the kvno for the account, or -1 in case of a failure.
1748 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1750 LDAPMessage *res = NULL;
1751 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1752 char *filter;
1753 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1754 char *dn_string = NULL;
1755 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1757 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1758 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1759 return kvno;
1761 ret = ads_search(ads, &res, filter, attrs);
1762 SAFE_FREE(filter);
1763 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1764 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1765 ads_msgfree(ads, res);
1766 return kvno;
1769 dn_string = ads_get_dn(ads, talloc_tos(), res);
1770 if (!dn_string) {
1771 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1772 ads_msgfree(ads, res);
1773 return kvno;
1775 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1776 TALLOC_FREE(dn_string);
1778 /* ---------------------------------------------------------
1779 * 0 is returned as a default KVNO from this point on...
1780 * This is done because Windows 2000 does not support key
1781 * version numbers. Chances are that a failure in the next
1782 * step is simply due to Windows 2000 being used for a
1783 * domain controller. */
1784 kvno = 0;
1786 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1787 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1788 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1789 ads_msgfree(ads, res);
1790 return kvno;
1793 /* Success */
1794 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1795 ads_msgfree(ads, res);
1796 return kvno;
1800 * Determines the computer account's current KVNO via an LDAP lookup
1801 * @param ads An initialized ADS_STRUCT
1802 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1803 * @return the kvno for the computer account, or -1 in case of a failure.
1806 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1808 char *computer_account = NULL;
1809 uint32_t kvno = -1;
1811 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1812 return kvno;
1815 kvno = ads_get_kvno(ads, computer_account);
1816 free(computer_account);
1818 return kvno;
1822 * This clears out all registered spn's for a given hostname
1823 * @param ads An initilaized ADS_STRUCT
1824 * @param machine_name the NetBIOS name of the computer.
1825 * @return 0 upon success, non-zero otherwise.
1828 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1830 TALLOC_CTX *ctx;
1831 LDAPMessage *res = NULL;
1832 ADS_MODLIST mods;
1833 const char *servicePrincipalName[1] = {NULL};
1834 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1835 char *dn_string = NULL;
1837 ret = ads_find_machine_acct(ads, &res, machine_name);
1838 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1839 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1840 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1841 ads_msgfree(ads, res);
1842 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1845 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1846 ctx = talloc_init("ads_clear_service_principal_names");
1847 if (!ctx) {
1848 ads_msgfree(ads, res);
1849 return ADS_ERROR(LDAP_NO_MEMORY);
1852 if (!(mods = ads_init_mods(ctx))) {
1853 talloc_destroy(ctx);
1854 ads_msgfree(ads, res);
1855 return ADS_ERROR(LDAP_NO_MEMORY);
1857 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1858 if (!ADS_ERR_OK(ret)) {
1859 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1860 ads_msgfree(ads, res);
1861 talloc_destroy(ctx);
1862 return ret;
1864 dn_string = ads_get_dn(ads, talloc_tos(), res);
1865 if (!dn_string) {
1866 talloc_destroy(ctx);
1867 ads_msgfree(ads, res);
1868 return ADS_ERROR(LDAP_NO_MEMORY);
1870 ret = ads_gen_mod(ads, dn_string, mods);
1871 TALLOC_FREE(dn_string);
1872 if (!ADS_ERR_OK(ret)) {
1873 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1874 machine_name));
1875 ads_msgfree(ads, res);
1876 talloc_destroy(ctx);
1877 return ret;
1880 ads_msgfree(ads, res);
1881 talloc_destroy(ctx);
1882 return ret;
1886 * @brief Search for an element in a string array.
1888 * @param[in] el_array The string array to search.
1890 * @param[in] num_el The number of elements in the string array.
1892 * @param[in] el The string to search.
1894 * @return True if found, false if not.
1896 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1898 size_t i;
1900 if (el_array == NULL || num_el == 0 || el == NULL) {
1901 return false;
1904 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1905 int cmp;
1907 cmp = strcasecmp_m(el_array[i], el);
1908 if (cmp == 0) {
1909 return true;
1913 return false;
1917 * @brief This gets the service principal names of an existing computer account.
1919 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1921 * @param[in] ads The ADS context to use.
1923 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1924 * identify the computer account.
1926 * @param[in] spn_array A pointer to store the array for SPNs.
1928 * @param[in] num_spns The number of principals stored in the array.
1930 * @return 0 on success, or a ADS error if a failure occurred.
1932 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1933 ADS_STRUCT *ads,
1934 const char *machine_name,
1935 char ***spn_array,
1936 size_t *num_spns)
1938 ADS_STATUS status;
1939 LDAPMessage *res = NULL;
1940 int count;
1942 status = ads_find_machine_acct(ads,
1943 &res,
1944 machine_name);
1945 if (!ADS_ERR_OK(status)) {
1946 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1947 machine_name));
1948 return status;
1951 count = ads_count_replies(ads, res);
1952 if (count != 1) {
1953 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1954 goto done;
1957 *spn_array = ads_pull_strings(ads,
1958 mem_ctx,
1959 res,
1960 "servicePrincipalName",
1961 num_spns);
1962 if (*spn_array == NULL) {
1963 DEBUG(1, ("Host account for %s does not have service principal "
1964 "names.\n",
1965 machine_name));
1966 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1967 goto done;
1970 done:
1971 ads_msgfree(ads, res);
1973 return status;
1977 * This adds a service principal name to an existing computer account
1978 * (found by hostname) in AD.
1979 * @param ads An initialized ADS_STRUCT
1980 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1981 * @param my_fqdn The fully qualified DNS name of the machine
1982 * @param spn A string of the service principal to add, i.e. 'host'
1983 * @return 0 upon sucess, or non-zero if a failure occurs
1986 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1987 const char *my_fqdn, const char *spn)
1989 ADS_STATUS ret;
1990 TALLOC_CTX *ctx;
1991 LDAPMessage *res = NULL;
1992 char *psp1, *psp2;
1993 ADS_MODLIST mods;
1994 char *dn_string = NULL;
1995 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1997 ret = ads_find_machine_acct(ads, &res, machine_name);
1998 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1999 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2000 machine_name));
2001 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2002 spn, machine_name, ads->config.realm));
2003 ads_msgfree(ads, res);
2004 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2007 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2008 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2009 ads_msgfree(ads, res);
2010 return ADS_ERROR(LDAP_NO_MEMORY);
2013 /* add short name spn */
2015 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2016 talloc_destroy(ctx);
2017 ads_msgfree(ads, res);
2018 return ADS_ERROR(LDAP_NO_MEMORY);
2020 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2021 ret = ADS_ERROR(LDAP_NO_MEMORY);
2022 goto out;
2024 servicePrincipalName[0] = psp1;
2026 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2027 psp1, machine_name));
2030 /* add fully qualified spn */
2032 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2033 ret = ADS_ERROR(LDAP_NO_MEMORY);
2034 goto out;
2036 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2037 ret = ADS_ERROR(LDAP_NO_MEMORY);
2038 goto out;
2040 servicePrincipalName[1] = psp2;
2042 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2043 psp2, machine_name));
2045 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2046 ret = ADS_ERROR(LDAP_NO_MEMORY);
2047 goto out;
2050 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2051 if (!ADS_ERR_OK(ret)) {
2052 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2053 goto out;
2056 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2057 ret = ADS_ERROR(LDAP_NO_MEMORY);
2058 goto out;
2061 ret = ads_gen_mod(ads, dn_string, mods);
2062 if (!ADS_ERR_OK(ret)) {
2063 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2064 goto out;
2067 out:
2068 TALLOC_FREE( ctx );
2069 ads_msgfree(ads, res);
2070 return ret;
2074 * adds a machine account to the ADS server
2075 * @param ads An intialized ADS_STRUCT
2076 * @param machine_name - the NetBIOS machine name of this account.
2077 * @param account_type A number indicating the type of account to create
2078 * @param org_unit The LDAP path in which to place this account
2079 * @return 0 upon success, or non-zero otherwise
2082 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2083 const char *machine_name,
2084 const char *org_unit,
2085 uint32_t etype_list)
2087 ADS_STATUS ret;
2088 char *samAccountName, *controlstr;
2089 TALLOC_CTX *ctx;
2090 ADS_MODLIST mods;
2091 char *machine_escaped = NULL;
2092 char *new_dn;
2093 const char *objectClass[] = {"top", "person", "organizationalPerson",
2094 "user", "computer", NULL};
2095 LDAPMessage *res = NULL;
2096 uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2097 UF_DONT_EXPIRE_PASSWD |\
2098 UF_ACCOUNTDISABLE );
2099 uint32_t func_level = 0;
2101 ret = ads_domain_func_level(ads, &func_level);
2102 if (!ADS_ERR_OK(ret)) {
2103 return ret;
2106 if (!(ctx = talloc_init("ads_add_machine_acct")))
2107 return ADS_ERROR(LDAP_NO_MEMORY);
2109 ret = ADS_ERROR(LDAP_NO_MEMORY);
2111 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2112 if (!machine_escaped) {
2113 goto done;
2116 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2117 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2119 if ( !new_dn || !samAccountName ) {
2120 goto done;
2123 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2124 goto done;
2127 if (!(mods = ads_init_mods(ctx))) {
2128 goto done;
2131 ads_mod_str(ctx, &mods, "cn", machine_name);
2132 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2133 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2134 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2136 if (func_level >= DS_DOMAIN_FUNCTION_2008) {
2137 const char *etype_list_str;
2139 etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list);
2140 if (etype_list_str == NULL) {
2141 goto done;
2143 ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes",
2144 etype_list_str);
2147 ret = ads_gen_add(ads, new_dn, mods);
2149 done:
2150 SAFE_FREE(machine_escaped);
2151 ads_msgfree(ads, res);
2152 talloc_destroy(ctx);
2154 return ret;
2158 * move a machine account to another OU on the ADS server
2159 * @param ads - An intialized ADS_STRUCT
2160 * @param machine_name - the NetBIOS machine name of this account.
2161 * @param org_unit - The LDAP path in which to place this account
2162 * @param moved - whether we moved the machine account (optional)
2163 * @return 0 upon success, or non-zero otherwise
2166 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2167 const char *org_unit, bool *moved)
2169 ADS_STATUS rc;
2170 int ldap_status;
2171 LDAPMessage *res = NULL;
2172 char *filter = NULL;
2173 char *computer_dn = NULL;
2174 char *parent_dn;
2175 char *computer_rdn = NULL;
2176 bool need_move = False;
2178 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2179 rc = ADS_ERROR(LDAP_NO_MEMORY);
2180 goto done;
2183 /* Find pre-existing machine */
2184 rc = ads_search(ads, &res, filter, NULL);
2185 if (!ADS_ERR_OK(rc)) {
2186 goto done;
2189 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2190 if (!computer_dn) {
2191 rc = ADS_ERROR(LDAP_NO_MEMORY);
2192 goto done;
2195 parent_dn = ads_parent_dn(computer_dn);
2196 if (strequal(parent_dn, org_unit)) {
2197 goto done;
2200 need_move = True;
2202 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2203 rc = ADS_ERROR(LDAP_NO_MEMORY);
2204 goto done;
2207 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2208 org_unit, 1, NULL, NULL);
2209 rc = ADS_ERROR(ldap_status);
2211 done:
2212 ads_msgfree(ads, res);
2213 SAFE_FREE(filter);
2214 TALLOC_FREE(computer_dn);
2215 SAFE_FREE(computer_rdn);
2217 if (!ADS_ERR_OK(rc)) {
2218 need_move = False;
2221 if (moved) {
2222 *moved = need_move;
2225 return rc;
2229 dump a binary result from ldap
2231 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2233 int i, j;
2234 for (i=0; values[i]; i++) {
2235 printf("%s: ", field);
2236 for (j=0; j<values[i]->bv_len; j++) {
2237 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2239 printf("\n");
2243 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2245 int i;
2246 for (i=0; values[i]; i++) {
2247 NTSTATUS status;
2248 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2249 struct GUID guid;
2251 status = GUID_from_ndr_blob(&in, &guid);
2252 if (NT_STATUS_IS_OK(status)) {
2253 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2254 } else {
2255 printf("%s: INVALID GUID\n", field);
2261 dump a sid result from ldap
2263 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2265 int i;
2266 for (i=0; values[i]; i++) {
2267 struct dom_sid sid;
2268 fstring tmp;
2269 if (!sid_parse((const uint8_t *)values[i]->bv_val,
2270 values[i]->bv_len, &sid)) {
2271 return;
2273 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2278 dump ntSecurityDescriptor
2280 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2282 TALLOC_CTX *frame = talloc_stackframe();
2283 struct security_descriptor *psd;
2284 NTSTATUS status;
2286 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2287 values[0]->bv_len, &psd);
2288 if (!NT_STATUS_IS_OK(status)) {
2289 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2290 nt_errstr(status)));
2291 TALLOC_FREE(frame);
2292 return;
2295 if (psd) {
2296 ads_disp_sd(ads, talloc_tos(), psd);
2299 TALLOC_FREE(frame);
2303 dump a string result from ldap
2305 static void dump_string(const char *field, char **values)
2307 int i;
2308 for (i=0; values[i]; i++) {
2309 printf("%s: %s\n", field, values[i]);
2314 dump a field from LDAP on stdout
2315 used for debugging
2318 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2320 const struct {
2321 const char *name;
2322 bool string;
2323 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2324 } handlers[] = {
2325 {"objectGUID", False, dump_guid},
2326 {"netbootGUID", False, dump_guid},
2327 {"nTSecurityDescriptor", False, dump_sd},
2328 {"dnsRecord", False, dump_binary},
2329 {"objectSid", False, dump_sid},
2330 {"tokenGroups", False, dump_sid},
2331 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2332 {"tokengroupsGlobalandUniversal", False, dump_sid},
2333 {"mS-DS-CreatorSID", False, dump_sid},
2334 {"msExchMailboxGuid", False, dump_guid},
2335 {NULL, True, NULL}
2337 int i;
2339 if (!field) { /* must be end of an entry */
2340 printf("\n");
2341 return False;
2344 for (i=0; handlers[i].name; i++) {
2345 if (strcasecmp_m(handlers[i].name, field) == 0) {
2346 if (!values) /* first time, indicate string or not */
2347 return handlers[i].string;
2348 handlers[i].handler(ads, field, (struct berval **) values);
2349 break;
2352 if (!handlers[i].name) {
2353 if (!values) /* first time, indicate string conversion */
2354 return True;
2355 dump_string(field, (char **)values);
2357 return False;
2361 * Dump a result from LDAP on stdout
2362 * used for debugging
2363 * @param ads connection to ads server
2364 * @param res Results to dump
2367 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2369 ads_process_results(ads, res, ads_dump_field, NULL);
2373 * Walk through results, calling a function for each entry found.
2374 * The function receives a field name, a berval * array of values,
2375 * and a data area passed through from the start. The function is
2376 * called once with null for field and values at the end of each
2377 * entry.
2378 * @param ads connection to ads server
2379 * @param res Results to process
2380 * @param fn Function for processing each result
2381 * @param data_area user-defined area to pass to function
2383 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2384 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2385 void *data_area)
2387 LDAPMessage *msg;
2388 TALLOC_CTX *ctx;
2389 size_t converted_size;
2391 if (!(ctx = talloc_init("ads_process_results")))
2392 return;
2394 for (msg = ads_first_entry(ads, res); msg;
2395 msg = ads_next_entry(ads, msg)) {
2396 char *utf8_field;
2397 BerElement *b;
2399 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2400 (LDAPMessage *)msg,&b);
2401 utf8_field;
2402 utf8_field=ldap_next_attribute(ads->ldap.ld,
2403 (LDAPMessage *)msg,b)) {
2404 struct berval **ber_vals;
2405 char **str_vals;
2406 char **utf8_vals;
2407 char *field;
2408 bool string;
2410 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2411 &converted_size))
2413 DEBUG(0,("ads_process_results: "
2414 "pull_utf8_talloc failed: %s",
2415 strerror(errno)));
2418 string = fn(ads, field, NULL, data_area);
2420 if (string) {
2421 const char **p;
2423 utf8_vals = ldap_get_values(ads->ldap.ld,
2424 (LDAPMessage *)msg, field);
2425 p = discard_const_p(const char *, utf8_vals);
2426 str_vals = ads_pull_strvals(ctx, p);
2427 fn(ads, field, (void **) str_vals, data_area);
2428 ldap_value_free(utf8_vals);
2429 } else {
2430 ber_vals = ldap_get_values_len(ads->ldap.ld,
2431 (LDAPMessage *)msg, field);
2432 fn(ads, field, (void **) ber_vals, data_area);
2434 ldap_value_free_len(ber_vals);
2436 ldap_memfree(utf8_field);
2438 ber_free(b, 0);
2439 talloc_free_children(ctx);
2440 fn(ads, NULL, NULL, data_area); /* completed an entry */
2443 talloc_destroy(ctx);
2447 * count how many replies are in a LDAPMessage
2448 * @param ads connection to ads server
2449 * @param res Results to count
2450 * @return number of replies
2452 int ads_count_replies(ADS_STRUCT *ads, void *res)
2454 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2458 * pull the first entry from a ADS result
2459 * @param ads connection to ads server
2460 * @param res Results of search
2461 * @return first entry from result
2463 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2465 return ldap_first_entry(ads->ldap.ld, res);
2469 * pull the next entry from a ADS result
2470 * @param ads connection to ads server
2471 * @param res Results of search
2472 * @return next entry from result
2474 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2476 return ldap_next_entry(ads->ldap.ld, res);
2480 * pull the first message from a ADS result
2481 * @param ads connection to ads server
2482 * @param res Results of search
2483 * @return first message from result
2485 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2487 return ldap_first_message(ads->ldap.ld, res);
2491 * pull the next message from a ADS result
2492 * @param ads connection to ads server
2493 * @param res Results of search
2494 * @return next message from result
2496 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2498 return ldap_next_message(ads->ldap.ld, res);
2502 * pull a single string from a ADS result
2503 * @param ads connection to ads server
2504 * @param mem_ctx TALLOC_CTX to use for allocating result string
2505 * @param msg Results of search
2506 * @param field Attribute to retrieve
2507 * @return Result string in talloc context
2509 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2510 const char *field)
2512 char **values;
2513 char *ret = NULL;
2514 char *ux_string;
2515 size_t converted_size;
2517 values = ldap_get_values(ads->ldap.ld, msg, field);
2518 if (!values)
2519 return NULL;
2521 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2522 &converted_size))
2524 ret = ux_string;
2526 ldap_value_free(values);
2527 return ret;
2531 * pull an array of strings from a ADS result
2532 * @param ads connection to ads server
2533 * @param mem_ctx TALLOC_CTX to use for allocating result string
2534 * @param msg Results of search
2535 * @param field Attribute to retrieve
2536 * @return Result strings in talloc context
2538 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2539 LDAPMessage *msg, const char *field,
2540 size_t *num_values)
2542 char **values;
2543 char **ret = NULL;
2544 int i;
2545 size_t converted_size;
2547 values = ldap_get_values(ads->ldap.ld, msg, field);
2548 if (!values)
2549 return NULL;
2551 *num_values = ldap_count_values(values);
2553 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2554 if (!ret) {
2555 ldap_value_free(values);
2556 return NULL;
2559 for (i=0;i<*num_values;i++) {
2560 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2561 &converted_size))
2563 ldap_value_free(values);
2564 return NULL;
2567 ret[i] = NULL;
2569 ldap_value_free(values);
2570 return ret;
2574 * pull an array of strings from a ADS result
2575 * (handle large multivalue attributes with range retrieval)
2576 * @param ads connection to ads server
2577 * @param mem_ctx TALLOC_CTX to use for allocating result string
2578 * @param msg Results of search
2579 * @param field Attribute to retrieve
2580 * @param current_strings strings returned by a previous call to this function
2581 * @param next_attribute The next query should ask for this attribute
2582 * @param num_values How many values did we get this time?
2583 * @param more_values Are there more values to get?
2584 * @return Result strings in talloc context
2586 char **ads_pull_strings_range(ADS_STRUCT *ads,
2587 TALLOC_CTX *mem_ctx,
2588 LDAPMessage *msg, const char *field,
2589 char **current_strings,
2590 const char **next_attribute,
2591 size_t *num_strings,
2592 bool *more_strings)
2594 char *attr;
2595 char *expected_range_attrib, *range_attr;
2596 BerElement *ptr = NULL;
2597 char **strings;
2598 char **new_strings;
2599 size_t num_new_strings;
2600 unsigned long int range_start;
2601 unsigned long int range_end;
2603 /* we might have been given the whole lot anyway */
2604 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2605 *more_strings = False;
2606 return strings;
2609 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2611 /* look for Range result */
2612 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2613 attr;
2614 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2615 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2616 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2617 range_attr = attr;
2618 break;
2620 ldap_memfree(attr);
2622 if (!attr) {
2623 ber_free(ptr, 0);
2624 /* nothing here - this field is just empty */
2625 *more_strings = False;
2626 return NULL;
2629 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2630 &range_start, &range_end) == 2) {
2631 *more_strings = True;
2632 } else {
2633 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2634 &range_start) == 1) {
2635 *more_strings = False;
2636 } else {
2637 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2638 range_attr));
2639 ldap_memfree(range_attr);
2640 *more_strings = False;
2641 return NULL;
2645 if ((*num_strings) != range_start) {
2646 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2647 " - aborting range retreival\n",
2648 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2649 ldap_memfree(range_attr);
2650 *more_strings = False;
2651 return NULL;
2654 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2656 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2657 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2658 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2659 range_attr, (unsigned long int)range_end - range_start + 1,
2660 (unsigned long int)num_new_strings));
2661 ldap_memfree(range_attr);
2662 *more_strings = False;
2663 return NULL;
2666 strings = talloc_realloc(mem_ctx, current_strings, char *,
2667 *num_strings + num_new_strings);
2669 if (strings == NULL) {
2670 ldap_memfree(range_attr);
2671 *more_strings = False;
2672 return NULL;
2675 if (new_strings && num_new_strings) {
2676 memcpy(&strings[*num_strings], new_strings,
2677 sizeof(*new_strings) * num_new_strings);
2680 (*num_strings) += num_new_strings;
2682 if (*more_strings) {
2683 *next_attribute = talloc_asprintf(mem_ctx,
2684 "%s;range=%d-*",
2685 field,
2686 (int)*num_strings);
2688 if (!*next_attribute) {
2689 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2690 ldap_memfree(range_attr);
2691 *more_strings = False;
2692 return NULL;
2696 ldap_memfree(range_attr);
2698 return strings;
2702 * pull a single uint32_t from a ADS result
2703 * @param ads connection to ads server
2704 * @param msg Results of search
2705 * @param field Attribute to retrieve
2706 * @param v Pointer to int to store result
2707 * @return boolean inidicating success
2709 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2710 uint32_t *v)
2712 char **values;
2714 values = ldap_get_values(ads->ldap.ld, msg, field);
2715 if (!values)
2716 return False;
2717 if (!values[0]) {
2718 ldap_value_free(values);
2719 return False;
2722 *v = atoi(values[0]);
2723 ldap_value_free(values);
2724 return True;
2728 * pull a single objectGUID from an ADS result
2729 * @param ads connection to ADS server
2730 * @param msg results of search
2731 * @param guid 37-byte area to receive text guid
2732 * @return boolean indicating success
2734 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2736 DATA_BLOB blob;
2737 NTSTATUS status;
2739 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2740 &blob)) {
2741 return false;
2744 status = GUID_from_ndr_blob(&blob, guid);
2745 talloc_free(blob.data);
2746 return NT_STATUS_IS_OK(status);
2751 * pull a single struct dom_sid from a ADS result
2752 * @param ads connection to ads server
2753 * @param msg Results of search
2754 * @param field Attribute to retrieve
2755 * @param sid Pointer to sid to store result
2756 * @return boolean inidicating success
2758 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2759 struct dom_sid *sid)
2761 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2765 * pull an array of struct dom_sids from a ADS result
2766 * @param ads connection to ads server
2767 * @param mem_ctx TALLOC_CTX for allocating sid array
2768 * @param msg Results of search
2769 * @param field Attribute to retrieve
2770 * @param sids pointer to sid array to allocate
2771 * @return the count of SIDs pulled
2773 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2774 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2776 struct berval **values;
2777 bool ret;
2778 int count, i;
2780 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2782 if (!values)
2783 return 0;
2785 for (i=0; values[i]; i++)
2786 /* nop */ ;
2788 if (i) {
2789 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2790 if (!(*sids)) {
2791 ldap_value_free_len(values);
2792 return 0;
2794 } else {
2795 (*sids) = NULL;
2798 count = 0;
2799 for (i=0; values[i]; i++) {
2800 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2801 values[i]->bv_len, &(*sids)[count]);
2802 if (ret) {
2803 DEBUG(10, ("pulling SID: %s\n",
2804 sid_string_dbg(&(*sids)[count])));
2805 count++;
2809 ldap_value_free_len(values);
2810 return count;
2814 * pull a struct security_descriptor from a ADS result
2815 * @param ads connection to ads server
2816 * @param mem_ctx TALLOC_CTX for allocating sid array
2817 * @param msg Results of search
2818 * @param field Attribute to retrieve
2819 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2820 * @return boolean inidicating success
2822 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2823 LDAPMessage *msg, const char *field,
2824 struct security_descriptor **sd)
2826 struct berval **values;
2827 bool ret = true;
2829 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2831 if (!values) return false;
2833 if (values[0]) {
2834 NTSTATUS status;
2835 status = unmarshall_sec_desc(mem_ctx,
2836 (uint8_t *)values[0]->bv_val,
2837 values[0]->bv_len, sd);
2838 if (!NT_STATUS_IS_OK(status)) {
2839 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2840 nt_errstr(status)));
2841 ret = false;
2845 ldap_value_free_len(values);
2846 return ret;
2850 * in order to support usernames longer than 21 characters we need to
2851 * use both the sAMAccountName and the userPrincipalName attributes
2852 * It seems that not all users have the userPrincipalName attribute set
2854 * @param ads connection to ads server
2855 * @param mem_ctx TALLOC_CTX for allocating sid array
2856 * @param msg Results of search
2857 * @return the username
2859 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2860 LDAPMessage *msg)
2862 #if 0 /* JERRY */
2863 char *ret, *p;
2865 /* lookup_name() only works on the sAMAccountName to
2866 returning the username portion of userPrincipalName
2867 breaks winbindd_getpwnam() */
2869 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2870 if (ret && (p = strchr_m(ret, '@'))) {
2871 *p = 0;
2872 return ret;
2874 #endif
2875 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2880 * find the update serial number - this is the core of the ldap cache
2881 * @param ads connection to ads server
2882 * @param ads connection to ADS server
2883 * @param usn Pointer to retrieved update serial number
2884 * @return status of search
2886 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2888 const char *attrs[] = {"highestCommittedUSN", NULL};
2889 ADS_STATUS status;
2890 LDAPMessage *res;
2892 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2893 if (!ADS_ERR_OK(status))
2894 return status;
2896 if (ads_count_replies(ads, res) != 1) {
2897 ads_msgfree(ads, res);
2898 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2901 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2902 ads_msgfree(ads, res);
2903 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2906 ads_msgfree(ads, res);
2907 return ADS_SUCCESS;
2910 /* parse a ADS timestring - typical string is
2911 '20020917091222.0Z0' which means 09:12.22 17th September
2912 2002, timezone 0 */
2913 static time_t ads_parse_time(const char *str)
2915 struct tm tm;
2917 ZERO_STRUCT(tm);
2919 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2920 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2921 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2922 return 0;
2924 tm.tm_year -= 1900;
2925 tm.tm_mon -= 1;
2927 return timegm(&tm);
2930 /********************************************************************
2931 ********************************************************************/
2933 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2935 const char *attrs[] = {"currentTime", NULL};
2936 ADS_STATUS status;
2937 LDAPMessage *res;
2938 char *timestr;
2939 TALLOC_CTX *ctx;
2940 ADS_STRUCT *ads_s = ads;
2942 if (!(ctx = talloc_init("ads_current_time"))) {
2943 return ADS_ERROR(LDAP_NO_MEMORY);
2946 /* establish a new ldap tcp session if necessary */
2948 if ( !ads->ldap.ld ) {
2949 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2950 ads->server.ldap_server )) == NULL )
2952 status = ADS_ERROR(LDAP_NO_MEMORY);
2953 goto done;
2955 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2956 status = ads_connect( ads_s );
2957 if ( !ADS_ERR_OK(status))
2958 goto done;
2961 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2962 if (!ADS_ERR_OK(status)) {
2963 goto done;
2966 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2967 if (!timestr) {
2968 ads_msgfree(ads_s, res);
2969 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2970 goto done;
2973 /* but save the time and offset in the original ADS_STRUCT */
2975 ads->config.current_time = ads_parse_time(timestr);
2977 if (ads->config.current_time != 0) {
2978 ads->auth.time_offset = ads->config.current_time - time(NULL);
2979 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2982 ads_msgfree(ads, res);
2984 status = ADS_SUCCESS;
2986 done:
2987 /* free any temporary ads connections */
2988 if ( ads_s != ads ) {
2989 ads_destroy( &ads_s );
2991 talloc_destroy(ctx);
2993 return status;
2996 /********************************************************************
2997 ********************************************************************/
2999 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
3001 const char *attrs[] = {"domainFunctionality", NULL};
3002 ADS_STATUS status;
3003 LDAPMessage *res;
3004 ADS_STRUCT *ads_s = ads;
3006 *val = DS_DOMAIN_FUNCTION_2000;
3008 /* establish a new ldap tcp session if necessary */
3010 if ( !ads->ldap.ld ) {
3011 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3012 ads->server.ldap_server )) == NULL )
3014 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3015 goto done;
3017 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3018 status = ads_connect( ads_s );
3019 if ( !ADS_ERR_OK(status))
3020 goto done;
3023 /* If the attribute does not exist assume it is a Windows 2000
3024 functional domain */
3026 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3027 if (!ADS_ERR_OK(status)) {
3028 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3029 status = ADS_SUCCESS;
3031 goto done;
3034 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3035 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3037 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3040 ads_msgfree(ads, res);
3042 done:
3043 /* free any temporary ads connections */
3044 if ( ads_s != ads ) {
3045 ads_destroy( &ads_s );
3048 return status;
3052 * find the domain sid for our domain
3053 * @param ads connection to ads server
3054 * @param sid Pointer to domain sid
3055 * @return status of search
3057 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3059 const char *attrs[] = {"objectSid", NULL};
3060 LDAPMessage *res;
3061 ADS_STATUS rc;
3063 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3064 attrs, &res);
3065 if (!ADS_ERR_OK(rc)) return rc;
3066 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3067 ads_msgfree(ads, res);
3068 return ADS_ERROR_SYSTEM(ENOENT);
3070 ads_msgfree(ads, res);
3072 return ADS_SUCCESS;
3076 * find our site name
3077 * @param ads connection to ads server
3078 * @param mem_ctx Pointer to talloc context
3079 * @param site_name Pointer to the sitename
3080 * @return status of search
3082 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3084 ADS_STATUS status;
3085 LDAPMessage *res;
3086 const char *dn, *service_name;
3087 const char *attrs[] = { "dsServiceName", NULL };
3089 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3090 if (!ADS_ERR_OK(status)) {
3091 return status;
3094 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3095 if (service_name == NULL) {
3096 ads_msgfree(ads, res);
3097 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3100 ads_msgfree(ads, res);
3102 /* go up three levels */
3103 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3104 if (dn == NULL) {
3105 return ADS_ERROR(LDAP_NO_MEMORY);
3108 *site_name = talloc_strdup(mem_ctx, dn);
3109 if (*site_name == NULL) {
3110 return ADS_ERROR(LDAP_NO_MEMORY);
3113 return status;
3115 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3120 * find the site dn where a machine resides
3121 * @param ads connection to ads server
3122 * @param mem_ctx Pointer to talloc context
3123 * @param computer_name name of the machine
3124 * @param site_name Pointer to the sitename
3125 * @return status of search
3127 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3129 ADS_STATUS status;
3130 LDAPMessage *res;
3131 const char *parent, *filter;
3132 char *config_context = NULL;
3133 char *dn;
3135 /* shortcut a query */
3136 if (strequal(computer_name, ads->config.ldap_server_name)) {
3137 return ads_site_dn(ads, mem_ctx, site_dn);
3140 status = ads_config_path(ads, mem_ctx, &config_context);
3141 if (!ADS_ERR_OK(status)) {
3142 return status;
3145 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3146 if (filter == NULL) {
3147 return ADS_ERROR(LDAP_NO_MEMORY);
3150 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3151 filter, NULL, &res);
3152 if (!ADS_ERR_OK(status)) {
3153 return status;
3156 if (ads_count_replies(ads, res) != 1) {
3157 ads_msgfree(ads, res);
3158 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3161 dn = ads_get_dn(ads, mem_ctx, res);
3162 if (dn == NULL) {
3163 ads_msgfree(ads, res);
3164 return ADS_ERROR(LDAP_NO_MEMORY);
3167 /* go up three levels */
3168 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3169 if (parent == NULL) {
3170 ads_msgfree(ads, res);
3171 TALLOC_FREE(dn);
3172 return ADS_ERROR(LDAP_NO_MEMORY);
3175 *site_dn = talloc_strdup(mem_ctx, parent);
3176 if (*site_dn == NULL) {
3177 ads_msgfree(ads, res);
3178 TALLOC_FREE(dn);
3179 return ADS_ERROR(LDAP_NO_MEMORY);
3182 TALLOC_FREE(dn);
3183 ads_msgfree(ads, res);
3185 return status;
3189 * get the upn suffixes for a domain
3190 * @param ads connection to ads server
3191 * @param mem_ctx Pointer to talloc context
3192 * @param suffixes Pointer to an array of suffixes
3193 * @param num_suffixes Pointer to the number of suffixes
3194 * @return status of search
3196 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3198 ADS_STATUS status;
3199 LDAPMessage *res;
3200 const char *base;
3201 char *config_context = NULL;
3202 const char *attrs[] = { "uPNSuffixes", NULL };
3204 status = ads_config_path(ads, mem_ctx, &config_context);
3205 if (!ADS_ERR_OK(status)) {
3206 return status;
3209 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3210 if (base == NULL) {
3211 return ADS_ERROR(LDAP_NO_MEMORY);
3214 status = ads_search_dn(ads, &res, base, attrs);
3215 if (!ADS_ERR_OK(status)) {
3216 return status;
3219 if (ads_count_replies(ads, res) != 1) {
3220 ads_msgfree(ads, res);
3221 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3224 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3225 if ((*suffixes) == NULL) {
3226 ads_msgfree(ads, res);
3227 return ADS_ERROR(LDAP_NO_MEMORY);
3230 ads_msgfree(ads, res);
3232 return status;
3236 * get the joinable ous for a domain
3237 * @param ads connection to ads server
3238 * @param mem_ctx Pointer to talloc context
3239 * @param ous Pointer to an array of ous
3240 * @param num_ous Pointer to the number of ous
3241 * @return status of search
3243 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3244 TALLOC_CTX *mem_ctx,
3245 char ***ous,
3246 size_t *num_ous)
3248 ADS_STATUS status;
3249 LDAPMessage *res = NULL;
3250 LDAPMessage *msg = NULL;
3251 const char *attrs[] = { "dn", NULL };
3252 int count = 0;
3254 status = ads_search(ads, &res,
3255 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3256 attrs);
3257 if (!ADS_ERR_OK(status)) {
3258 return status;
3261 count = ads_count_replies(ads, res);
3262 if (count < 1) {
3263 ads_msgfree(ads, res);
3264 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3267 for (msg = ads_first_entry(ads, res); msg;
3268 msg = ads_next_entry(ads, msg)) {
3269 const char **p = discard_const_p(const char *, *ous);
3270 char *dn = NULL;
3272 dn = ads_get_dn(ads, talloc_tos(), msg);
3273 if (!dn) {
3274 ads_msgfree(ads, res);
3275 return ADS_ERROR(LDAP_NO_MEMORY);
3278 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3279 TALLOC_FREE(dn);
3280 ads_msgfree(ads, res);
3281 return ADS_ERROR(LDAP_NO_MEMORY);
3284 TALLOC_FREE(dn);
3285 *ous = discard_const_p(char *, p);
3288 ads_msgfree(ads, res);
3290 return status;
3295 * pull a struct dom_sid from an extended dn string
3296 * @param mem_ctx TALLOC_CTX
3297 * @param extended_dn string
3298 * @param flags string type of extended_dn
3299 * @param sid pointer to a struct dom_sid
3300 * @return NT_STATUS_OK on success,
3301 * NT_INVALID_PARAMETER on error,
3302 * NT_STATUS_NOT_FOUND if no SID present
3304 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3305 const char *extended_dn,
3306 enum ads_extended_dn_flags flags,
3307 struct dom_sid *sid)
3309 char *p, *q, *dn;
3311 if (!extended_dn) {
3312 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3315 /* otherwise extended_dn gets stripped off */
3316 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3317 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3320 * ADS_EXTENDED_DN_HEX_STRING:
3321 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3323 * ADS_EXTENDED_DN_STRING (only with w2k3):
3324 * <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
3326 * Object with no SID, such as an Exchange Public Folder
3327 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3330 p = strchr(dn, ';');
3331 if (!p) {
3332 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3335 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3336 DEBUG(5,("No SID present in extended dn\n"));
3337 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3340 p += strlen(";<SID=");
3342 q = strchr(p, '>');
3343 if (!q) {
3344 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3347 *q = '\0';
3349 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3351 switch (flags) {
3353 case ADS_EXTENDED_DN_STRING:
3354 if (!string_to_sid(sid, p)) {
3355 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3357 break;
3358 case ADS_EXTENDED_DN_HEX_STRING: {
3359 fstring buf;
3360 size_t buf_len;
3362 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3363 if (buf_len == 0) {
3364 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3367 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3368 DEBUG(10,("failed to parse sid\n"));
3369 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3371 break;
3373 default:
3374 DEBUG(10,("unknown extended dn format\n"));
3375 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3378 return ADS_ERROR_NT(NT_STATUS_OK);
3381 /********************************************************************
3382 ********************************************************************/
3384 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3386 LDAPMessage *res = NULL;
3387 ADS_STATUS status;
3388 int count = 0;
3389 char *name = NULL;
3391 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3392 if (!ADS_ERR_OK(status)) {
3393 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3394 lp_netbios_name()));
3395 goto out;
3398 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3399 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3400 goto out;
3403 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3404 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3407 out:
3408 ads_msgfree(ads, res);
3410 return name;
3413 /********************************************************************
3414 ********************************************************************/
3416 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3418 LDAPMessage *res = NULL;
3419 ADS_STATUS status;
3420 int count = 0;
3421 char *name = NULL;
3423 status = ads_find_machine_acct(ads, &res, machine_name);
3424 if (!ADS_ERR_OK(status)) {
3425 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3426 lp_netbios_name()));
3427 goto out;
3430 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3431 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3432 goto out;
3435 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3436 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3439 out:
3440 ads_msgfree(ads, res);
3442 return name;
3445 /********************************************************************
3446 ********************************************************************/
3448 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3450 LDAPMessage *res = NULL;
3451 ADS_STATUS status;
3452 int count = 0;
3453 char *name = NULL;
3455 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3456 if (!ADS_ERR_OK(status)) {
3457 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3458 lp_netbios_name()));
3459 goto out;
3462 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3463 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3464 goto out;
3467 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3468 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3471 out:
3472 ads_msgfree(ads, res);
3474 return name;
3477 #if 0
3479 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3482 * Join a machine to a realm
3483 * Creates the machine account and sets the machine password
3484 * @param ads connection to ads server
3485 * @param machine name of host to add
3486 * @param org_unit Organizational unit to place machine in
3487 * @return status of join
3489 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3490 uint32_t account_type, const char *org_unit)
3492 ADS_STATUS status;
3493 LDAPMessage *res = NULL;
3494 char *machine;
3496 /* machine name must be lowercase */
3497 machine = SMB_STRDUP(machine_name);
3498 strlower_m(machine);
3501 status = ads_find_machine_acct(ads, (void **)&res, machine);
3502 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3503 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3504 status = ads_leave_realm(ads, machine);
3505 if (!ADS_ERR_OK(status)) {
3506 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3507 machine, ads->config.realm));
3508 return status;
3512 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3513 if (!ADS_ERR_OK(status)) {
3514 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3515 SAFE_FREE(machine);
3516 return status;
3519 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3520 if (!ADS_ERR_OK(status)) {
3521 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3522 SAFE_FREE(machine);
3523 return status;
3526 SAFE_FREE(machine);
3527 ads_msgfree(ads, res);
3529 return status;
3531 #endif
3534 * Delete a machine from the realm
3535 * @param ads connection to ads server
3536 * @param hostname Machine to remove
3537 * @return status of delete
3539 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3541 ADS_STATUS status;
3542 void *msg;
3543 LDAPMessage *res;
3544 char *hostnameDN, *host;
3545 int rc;
3546 LDAPControl ldap_control;
3547 LDAPControl * pldap_control[2] = {NULL, NULL};
3549 pldap_control[0] = &ldap_control;
3550 memset(&ldap_control, 0, sizeof(LDAPControl));
3551 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3553 /* hostname must be lowercase */
3554 host = SMB_STRDUP(hostname);
3555 if (!strlower_m(host)) {
3556 SAFE_FREE(host);
3557 return ADS_ERROR_SYSTEM(EINVAL);
3560 status = ads_find_machine_acct(ads, &res, host);
3561 if (!ADS_ERR_OK(status)) {
3562 DEBUG(0, ("Host account for %s does not exist.\n", host));
3563 SAFE_FREE(host);
3564 return status;
3567 msg = ads_first_entry(ads, res);
3568 if (!msg) {
3569 SAFE_FREE(host);
3570 return ADS_ERROR_SYSTEM(ENOENT);
3573 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3574 if (hostnameDN == NULL) {
3575 SAFE_FREE(host);
3576 return ADS_ERROR_SYSTEM(ENOENT);
3579 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3580 if (rc) {
3581 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3582 }else {
3583 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3586 if (rc != LDAP_SUCCESS) {
3587 const char *attrs[] = { "cn", NULL };
3588 LDAPMessage *msg_sub;
3590 /* we only search with scope ONE, we do not expect any further
3591 * objects to be created deeper */
3593 status = ads_do_search_retry(ads, hostnameDN,
3594 LDAP_SCOPE_ONELEVEL,
3595 "(objectclass=*)", attrs, &res);
3597 if (!ADS_ERR_OK(status)) {
3598 SAFE_FREE(host);
3599 TALLOC_FREE(hostnameDN);
3600 return status;
3603 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3604 msg_sub = ads_next_entry(ads, msg_sub)) {
3606 char *dn = NULL;
3608 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3609 SAFE_FREE(host);
3610 TALLOC_FREE(hostnameDN);
3611 return ADS_ERROR(LDAP_NO_MEMORY);
3614 status = ads_del_dn(ads, dn);
3615 if (!ADS_ERR_OK(status)) {
3616 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3617 SAFE_FREE(host);
3618 TALLOC_FREE(dn);
3619 TALLOC_FREE(hostnameDN);
3620 return status;
3623 TALLOC_FREE(dn);
3626 /* there should be no subordinate objects anymore */
3627 status = ads_do_search_retry(ads, hostnameDN,
3628 LDAP_SCOPE_ONELEVEL,
3629 "(objectclass=*)", attrs, &res);
3631 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3632 SAFE_FREE(host);
3633 TALLOC_FREE(hostnameDN);
3634 return status;
3637 /* delete hostnameDN now */
3638 status = ads_del_dn(ads, hostnameDN);
3639 if (!ADS_ERR_OK(status)) {
3640 SAFE_FREE(host);
3641 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3642 TALLOC_FREE(hostnameDN);
3643 return status;
3647 TALLOC_FREE(hostnameDN);
3649 status = ads_find_machine_acct(ads, &res, host);
3650 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3651 DEBUG(3, ("Failed to remove host account.\n"));
3652 SAFE_FREE(host);
3653 return status;
3656 SAFE_FREE(host);
3657 return status;
3661 * pull all token-sids from an LDAP dn
3662 * @param ads connection to ads server
3663 * @param mem_ctx TALLOC_CTX for allocating sid array
3664 * @param dn of LDAP object
3665 * @param user_sid pointer to struct dom_sid (objectSid)
3666 * @param primary_group_sid pointer to struct dom_sid (self composed)
3667 * @param sids pointer to sid array to allocate
3668 * @param num_sids counter of SIDs pulled
3669 * @return status of token query
3671 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3672 TALLOC_CTX *mem_ctx,
3673 const char *dn,
3674 struct dom_sid *user_sid,
3675 struct dom_sid *primary_group_sid,
3676 struct dom_sid **sids,
3677 size_t *num_sids)
3679 ADS_STATUS status;
3680 LDAPMessage *res = NULL;
3681 int count = 0;
3682 size_t tmp_num_sids;
3683 struct dom_sid *tmp_sids;
3684 struct dom_sid tmp_user_sid;
3685 struct dom_sid tmp_primary_group_sid;
3686 uint32_t pgid;
3687 const char *attrs[] = {
3688 "objectSid",
3689 "tokenGroups",
3690 "primaryGroupID",
3691 NULL
3694 status = ads_search_retry_dn(ads, &res, dn, attrs);
3695 if (!ADS_ERR_OK(status)) {
3696 return status;
3699 count = ads_count_replies(ads, res);
3700 if (count != 1) {
3701 ads_msgfree(ads, res);
3702 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3705 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3706 ads_msgfree(ads, res);
3707 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3710 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3711 ads_msgfree(ads, res);
3712 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3716 /* hack to compose the primary group sid without knowing the
3717 * domsid */
3719 struct dom_sid domsid;
3721 sid_copy(&domsid, &tmp_user_sid);
3723 if (!sid_split_rid(&domsid, NULL)) {
3724 ads_msgfree(ads, res);
3725 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3728 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3729 ads_msgfree(ads, res);
3730 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3734 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3736 if (tmp_num_sids == 0 || !tmp_sids) {
3737 ads_msgfree(ads, res);
3738 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3741 if (num_sids) {
3742 *num_sids = tmp_num_sids;
3745 if (sids) {
3746 *sids = tmp_sids;
3749 if (user_sid) {
3750 *user_sid = tmp_user_sid;
3753 if (primary_group_sid) {
3754 *primary_group_sid = tmp_primary_group_sid;
3757 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3759 ads_msgfree(ads, res);
3760 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3764 * Find a sAMAccoutName in LDAP
3765 * @param ads connection to ads server
3766 * @param mem_ctx TALLOC_CTX for allocating sid array
3767 * @param samaccountname to search
3768 * @param uac_ret uint32_t pointer userAccountControl attribute value
3769 * @param dn_ret pointer to dn
3770 * @return status of token query
3772 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3773 TALLOC_CTX *mem_ctx,
3774 const char *samaccountname,
3775 uint32_t *uac_ret,
3776 const char **dn_ret)
3778 ADS_STATUS status;
3779 const char *attrs[] = { "userAccountControl", NULL };
3780 const char *filter;
3781 LDAPMessage *res = NULL;
3782 char *dn = NULL;
3783 uint32_t uac = 0;
3785 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3786 samaccountname);
3787 if (filter == NULL) {
3788 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3789 goto out;
3792 status = ads_do_search_all(ads, ads->config.bind_path,
3793 LDAP_SCOPE_SUBTREE,
3794 filter, attrs, &res);
3796 if (!ADS_ERR_OK(status)) {
3797 goto out;
3800 if (ads_count_replies(ads, res) != 1) {
3801 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3802 goto out;
3805 dn = ads_get_dn(ads, talloc_tos(), res);
3806 if (dn == NULL) {
3807 status = ADS_ERROR(LDAP_NO_MEMORY);
3808 goto out;
3811 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3812 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3813 goto out;
3816 if (uac_ret) {
3817 *uac_ret = uac;
3820 if (dn_ret) {
3821 *dn_ret = talloc_strdup(mem_ctx, dn);
3822 if (!*dn_ret) {
3823 status = ADS_ERROR(LDAP_NO_MEMORY);
3824 goto out;
3827 out:
3828 TALLOC_FREE(dn);
3829 ads_msgfree(ads, res);
3831 return status;
3835 * find our configuration path
3836 * @param ads connection to ads server
3837 * @param mem_ctx Pointer to talloc context
3838 * @param config_path Pointer to the config path
3839 * @return status of search
3841 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3842 TALLOC_CTX *mem_ctx,
3843 char **config_path)
3845 ADS_STATUS status;
3846 LDAPMessage *res = NULL;
3847 const char *config_context = NULL;
3848 const char *attrs[] = { "configurationNamingContext", NULL };
3850 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3851 "(objectclass=*)", attrs, &res);
3852 if (!ADS_ERR_OK(status)) {
3853 return status;
3856 config_context = ads_pull_string(ads, mem_ctx, res,
3857 "configurationNamingContext");
3858 ads_msgfree(ads, res);
3859 if (!config_context) {
3860 return ADS_ERROR(LDAP_NO_MEMORY);
3863 if (config_path) {
3864 *config_path = talloc_strdup(mem_ctx, config_context);
3865 if (!*config_path) {
3866 return ADS_ERROR(LDAP_NO_MEMORY);
3870 return ADS_ERROR(LDAP_SUCCESS);
3874 * find the displayName of an extended right
3875 * @param ads connection to ads server
3876 * @param config_path The config path
3877 * @param mem_ctx Pointer to talloc context
3878 * @param GUID struct of the rightsGUID
3879 * @return status of search
3881 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3882 const char *config_path,
3883 TALLOC_CTX *mem_ctx,
3884 const struct GUID *rights_guid)
3886 ADS_STATUS rc;
3887 LDAPMessage *res = NULL;
3888 char *expr = NULL;
3889 const char *attrs[] = { "displayName", NULL };
3890 const char *result = NULL;
3891 const char *path;
3893 if (!ads || !mem_ctx || !rights_guid) {
3894 goto done;
3897 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3898 GUID_string(mem_ctx, rights_guid));
3899 if (!expr) {
3900 goto done;
3903 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3904 if (!path) {
3905 goto done;
3908 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3909 expr, attrs, &res);
3910 if (!ADS_ERR_OK(rc)) {
3911 goto done;
3914 if (ads_count_replies(ads, res) != 1) {
3915 goto done;
3918 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3920 done:
3921 ads_msgfree(ads, res);
3922 return result;
3926 * verify or build and verify an account ou
3927 * @param mem_ctx Pointer to talloc context
3928 * @param ads connection to ads server
3929 * @param account_ou
3930 * @return status of search
3933 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3934 ADS_STRUCT *ads,
3935 const char **account_ou)
3937 char **exploded_dn;
3938 const char *name;
3939 char *ou_string;
3941 if (account_ou == NULL) {
3942 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3945 if (*account_ou != NULL) {
3946 exploded_dn = ldap_explode_dn(*account_ou, 0);
3947 if (exploded_dn) {
3948 ldap_value_free(exploded_dn);
3949 return ADS_SUCCESS;
3953 ou_string = ads_ou_string(ads, *account_ou);
3954 if (!ou_string) {
3955 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3958 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3959 ads->config.bind_path);
3960 SAFE_FREE(ou_string);
3962 if (!name) {
3963 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3966 exploded_dn = ldap_explode_dn(name, 0);
3967 if (!exploded_dn) {
3968 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3970 ldap_value_free(exploded_dn);
3972 *account_ou = name;
3973 return ADS_SUCCESS;
3976 #endif