s4:torture/remote_pac: verify the order of PAC elements
[Samba.git] / source3 / libads / ldap.c
blob9e2ed98805f46896c4b26656e23413ea2b5bddce
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/when we decide to utf8-encode attrs, take out this next line */
1040 TALLOC_FREE(search_attrs);
1042 return ADS_ERROR(rc);
1045 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1046 int scope, const char *expr,
1047 const char **attrs, LDAPMessage **res,
1048 int *count, struct berval **cookie)
1050 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1055 * Get all results for a search. This uses ads_do_paged_search() to return
1056 * all entries in a large search.
1057 * @param ads connection to ads server
1058 * @param bind_path Base dn for the search
1059 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1060 * @param expr Search expression
1061 * @param attrs Attributes to retrieve
1062 * @param res ** which will contain results - free res* with ads_msgfree()
1063 * @return status of search
1065 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1066 int scope, const char *expr,
1067 const char **attrs, void *args,
1068 LDAPMessage **res)
1070 struct berval *cookie = NULL;
1071 int count = 0;
1072 ADS_STATUS status;
1074 *res = NULL;
1075 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1076 &count, &cookie);
1078 if (!ADS_ERR_OK(status))
1079 return status;
1081 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1082 while (cookie) {
1083 LDAPMessage *res2 = NULL;
1084 LDAPMessage *msg, *next;
1086 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1087 attrs, args, &res2, &count, &cookie);
1088 if (!ADS_ERR_OK(status)) {
1089 /* Ensure we free all collected results */
1090 ads_msgfree(ads, *res);
1091 *res = NULL;
1092 break;
1095 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1096 that this works on all ldap libs, but I have only tested with openldap */
1097 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1098 next = ads_next_message(ads, msg);
1099 ldap_add_result_entry((LDAPMessage **)res, msg);
1101 /* note that we do not free res2, as the memory is now
1102 part of the main returned list */
1104 #else
1105 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1106 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1107 #endif
1109 return status;
1112 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1113 int scope, const char *expr,
1114 const char **attrs, LDAPMessage **res)
1116 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1119 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1120 int scope, const char *expr,
1121 const char **attrs, uint32_t sd_flags,
1122 LDAPMessage **res)
1124 ads_control args;
1126 args.control = ADS_SD_FLAGS_OID;
1127 args.val = sd_flags;
1128 args.critical = True;
1130 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1135 * Run a function on all results for a search. Uses ads_do_paged_search() and
1136 * runs the function as each page is returned, using ads_process_results()
1137 * @param ads connection to ads server
1138 * @param bind_path Base dn for the search
1139 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1140 * @param expr Search expression - specified in local charset
1141 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1142 * @param fn Function which takes attr name, values list, and data_area
1143 * @param data_area Pointer which is passed to function on each call
1144 * @return status of search
1146 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1147 int scope, const char *expr, const char **attrs,
1148 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1149 void *data_area)
1151 struct berval *cookie = NULL;
1152 int count = 0;
1153 ADS_STATUS status;
1154 LDAPMessage *res;
1156 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1157 &count, &cookie);
1159 if (!ADS_ERR_OK(status)) return status;
1161 ads_process_results(ads, res, fn, data_area);
1162 ads_msgfree(ads, res);
1164 while (cookie) {
1165 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1166 &res, &count, &cookie);
1168 if (!ADS_ERR_OK(status)) break;
1170 ads_process_results(ads, res, fn, data_area);
1171 ads_msgfree(ads, res);
1174 return status;
1178 * Do a search with a timeout.
1179 * @param ads connection to ads server
1180 * @param bind_path Base dn for the search
1181 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1182 * @param expr Search expression
1183 * @param attrs Attributes to retrieve
1184 * @param res ** which will contain results - free res* with ads_msgfree()
1185 * @return status of search
1187 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1188 const char *expr,
1189 const char **attrs, LDAPMessage **res)
1191 int rc;
1192 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1193 size_t converted_size;
1194 TALLOC_CTX *ctx;
1196 *res = NULL;
1197 if (!(ctx = talloc_init("ads_do_search"))) {
1198 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1199 return ADS_ERROR(LDAP_NO_MEMORY);
1202 /* 0 means the conversion worked but the result was empty
1203 so we only fail if it's negative. In any case, it always
1204 at least nulls out the dest */
1205 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1206 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1208 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1209 rc = LDAP_NO_MEMORY;
1210 goto done;
1213 if (!attrs || !(*attrs))
1214 search_attrs = NULL;
1215 else {
1216 /* This would be the utf8-encoded version...*/
1217 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1218 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1220 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1221 rc = LDAP_NO_MEMORY;
1222 goto done;
1226 /* see the note in ads_do_paged_search - we *must* disable referrals */
1227 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1229 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1230 search_attrs, 0, NULL, NULL,
1231 LDAP_NO_LIMIT,
1232 (LDAPMessage **)res);
1234 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1235 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1236 rc = 0;
1239 done:
1240 talloc_destroy(ctx);
1241 /* if/when we decide to utf8-encode attrs, take out this next line */
1242 TALLOC_FREE(search_attrs);
1243 return ADS_ERROR(rc);
1246 * Do a general ADS search
1247 * @param ads connection to ads server
1248 * @param res ** which will contain results - free res* with ads_msgfree()
1249 * @param expr Search expression
1250 * @param attrs Attributes to retrieve
1251 * @return status of search
1253 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1254 const char *expr, const char **attrs)
1256 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1257 expr, attrs, res);
1261 * Do a search on a specific DistinguishedName
1262 * @param ads connection to ads server
1263 * @param res ** which will contain results - free res* with ads_msgfree()
1264 * @param dn DistinguishName to search
1265 * @param attrs Attributes to retrieve
1266 * @return status of search
1268 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1269 const char *dn, const char **attrs)
1271 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1272 attrs, res);
1276 * Free up memory from a ads_search
1277 * @param ads connection to ads server
1278 * @param msg Search results to free
1280 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1282 if (!msg) return;
1283 ldap_msgfree(msg);
1287 * Get a dn from search results
1288 * @param ads connection to ads server
1289 * @param msg Search result
1290 * @return dn string
1292 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1294 char *utf8_dn, *unix_dn;
1295 size_t converted_size;
1297 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1299 if (!utf8_dn) {
1300 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1301 return NULL;
1304 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1305 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1306 utf8_dn ));
1307 return NULL;
1309 ldap_memfree(utf8_dn);
1310 return unix_dn;
1314 * Get the parent from a dn
1315 * @param dn the dn to return the parent from
1316 * @return parent dn string
1318 char *ads_parent_dn(const char *dn)
1320 char *p;
1322 if (dn == NULL) {
1323 return NULL;
1326 p = strchr(dn, ',');
1328 if (p == NULL) {
1329 return NULL;
1332 return p+1;
1336 * Find a machine account given a hostname
1337 * @param ads connection to ads server
1338 * @param res ** which will contain results - free res* with ads_msgfree()
1339 * @param host Hostname to search for
1340 * @return status of search
1342 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1343 const char *machine)
1345 ADS_STATUS status;
1346 char *expr;
1347 const char *attrs[] = {"*", "msDS-SupportedEncryptionTypes", "nTSecurityDescriptor", NULL};
1349 *res = NULL;
1351 /* the easiest way to find a machine account anywhere in the tree
1352 is to look for hostname$ */
1353 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1354 DEBUG(1, ("asprintf failed!\n"));
1355 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1358 status = ads_search(ads, res, expr, attrs);
1359 SAFE_FREE(expr);
1360 return status;
1364 * Initialize a list of mods to be used in a modify request
1365 * @param ctx An initialized TALLOC_CTX
1366 * @return allocated ADS_MODLIST
1368 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1370 #define ADS_MODLIST_ALLOC_SIZE 10
1371 LDAPMod **mods;
1373 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1374 /* -1 is safety to make sure we don't go over the end.
1375 need to reset it to NULL before doing ldap modify */
1376 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1378 return (ADS_MODLIST)mods;
1383 add an attribute to the list, with values list already constructed
1385 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1386 int mod_op, const char *name,
1387 const void *_invals)
1389 int curmod;
1390 LDAPMod **modlist = (LDAPMod **) *mods;
1391 struct berval **ber_values = NULL;
1392 char **char_values = NULL;
1394 if (!_invals) {
1395 mod_op = LDAP_MOD_DELETE;
1396 } else {
1397 if (mod_op & LDAP_MOD_BVALUES) {
1398 const struct berval **b;
1399 b = discard_const_p(const struct berval *, _invals);
1400 ber_values = ads_dup_values(ctx, b);
1401 } else {
1402 const char **c;
1403 c = discard_const_p(const char *, _invals);
1404 char_values = ads_push_strvals(ctx, c);
1408 /* find the first empty slot */
1409 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1410 curmod++);
1411 if (modlist[curmod] == (LDAPMod *) -1) {
1412 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1413 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1414 return ADS_ERROR(LDAP_NO_MEMORY);
1415 memset(&modlist[curmod], 0,
1416 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1417 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1418 *mods = (ADS_MODLIST)modlist;
1421 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1422 return ADS_ERROR(LDAP_NO_MEMORY);
1423 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1424 if (mod_op & LDAP_MOD_BVALUES) {
1425 modlist[curmod]->mod_bvalues = ber_values;
1426 } else if (mod_op & LDAP_MOD_DELETE) {
1427 modlist[curmod]->mod_values = NULL;
1428 } else {
1429 modlist[curmod]->mod_values = char_values;
1432 modlist[curmod]->mod_op = mod_op;
1433 return ADS_ERROR(LDAP_SUCCESS);
1437 * Add a single string value to a mod list
1438 * @param ctx An initialized TALLOC_CTX
1439 * @param mods An initialized ADS_MODLIST
1440 * @param name The attribute name to add
1441 * @param val The value to add - NULL means DELETE
1442 * @return ADS STATUS indicating success of add
1444 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1445 const char *name, const char *val)
1447 const char *values[2];
1449 values[0] = val;
1450 values[1] = NULL;
1452 if (!val)
1453 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1454 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1458 * Add an array of string values to a mod list
1459 * @param ctx An initialized TALLOC_CTX
1460 * @param mods An initialized ADS_MODLIST
1461 * @param name The attribute name to add
1462 * @param vals The array of string values to add - NULL means DELETE
1463 * @return ADS STATUS indicating success of add
1465 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1466 const char *name, const char **vals)
1468 if (!vals)
1469 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1470 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1471 name, (const void **) vals);
1474 #if 0
1476 * Add a single ber-encoded value to a mod list
1477 * @param ctx An initialized TALLOC_CTX
1478 * @param mods An initialized ADS_MODLIST
1479 * @param name The attribute name to add
1480 * @param val The value to add - NULL means DELETE
1481 * @return ADS STATUS indicating success of add
1483 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1484 const char *name, const struct berval *val)
1486 const struct berval *values[2];
1488 values[0] = val;
1489 values[1] = NULL;
1490 if (!val)
1491 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1492 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1493 name, (const void **) values);
1495 #endif
1497 static void ads_print_error(int ret, LDAP *ld)
1499 if (ret != 0) {
1500 char *ld_error = NULL;
1501 ldap_get_option(ld, LDAP_OPT_ERROR_STRING, &ld_error);
1502 DEBUG(10,("AD LDAP failure %d (%s):\n%s\n", ret,
1503 ldap_err2string(ret), ld_error));
1504 SAFE_FREE(ld_error);
1509 * Perform an ldap modify
1510 * @param ads connection to ads server
1511 * @param mod_dn DistinguishedName to modify
1512 * @param mods list of modifications to perform
1513 * @return status of modify
1515 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1517 int ret,i;
1518 char *utf8_dn = NULL;
1519 size_t converted_size;
1521 this control is needed to modify that contains a currently
1522 non-existent attribute (but allowable for the object) to run
1524 LDAPControl PermitModify = {
1525 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1526 {0, NULL},
1527 (char) 1};
1528 LDAPControl *controls[2];
1530 controls[0] = &PermitModify;
1531 controls[1] = NULL;
1533 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1534 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1537 /* find the end of the list, marked by NULL or -1 */
1538 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1539 /* make sure the end of the list is NULL */
1540 mods[i] = NULL;
1541 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1542 (LDAPMod **) mods, controls, NULL);
1543 ads_print_error(ret, ads->ldap.ld);
1544 TALLOC_FREE(utf8_dn);
1545 return ADS_ERROR(ret);
1549 * Perform an ldap add
1550 * @param ads connection to ads server
1551 * @param new_dn DistinguishedName to add
1552 * @param mods list of attributes and values for DN
1553 * @return status of add
1555 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1557 int ret, i;
1558 char *utf8_dn = NULL;
1559 size_t converted_size;
1561 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1562 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1563 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1566 /* find the end of the list, marked by NULL or -1 */
1567 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1568 /* make sure the end of the list is NULL */
1569 mods[i] = NULL;
1571 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1572 ads_print_error(ret, ads->ldap.ld);
1573 TALLOC_FREE(utf8_dn);
1574 return ADS_ERROR(ret);
1578 * Delete a DistinguishedName
1579 * @param ads connection to ads server
1580 * @param new_dn DistinguishedName to delete
1581 * @return status of delete
1583 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1585 int ret;
1586 char *utf8_dn = NULL;
1587 size_t converted_size;
1588 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1589 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1590 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1593 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1594 ads_print_error(ret, ads->ldap.ld);
1595 TALLOC_FREE(utf8_dn);
1596 return ADS_ERROR(ret);
1600 * Build an org unit string
1601 * if org unit is Computers or blank then assume a container, otherwise
1602 * assume a / separated list of organisational units.
1603 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1604 * @param ads connection to ads server
1605 * @param org_unit Organizational unit
1606 * @return org unit string - caller must free
1608 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1610 char *ret = NULL;
1612 if (!org_unit || !*org_unit) {
1614 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1616 /* samba4 might not yet respond to a wellknownobject-query */
1617 return ret ? ret : SMB_STRDUP("cn=Computers");
1620 if (strequal(org_unit, "Computers")) {
1621 return SMB_STRDUP("cn=Computers");
1624 /* jmcd: removed "\\" from the separation chars, because it is
1625 needed as an escape for chars like '#' which are valid in an
1626 OU name */
1627 return ads_build_path(org_unit, "/", "ou=", 1);
1631 * Get a org unit string for a well-known GUID
1632 * @param ads connection to ads server
1633 * @param wknguid Well known GUID
1634 * @return org unit string - caller must free
1636 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1638 ADS_STATUS status;
1639 LDAPMessage *res = NULL;
1640 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1641 **bind_dn_exp = NULL;
1642 const char *attrs[] = {"distinguishedName", NULL};
1643 int new_ln, wkn_ln, bind_ln, i;
1645 if (wknguid == NULL) {
1646 return NULL;
1649 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1650 DEBUG(1, ("asprintf failed!\n"));
1651 return NULL;
1654 status = ads_search_dn(ads, &res, base, attrs);
1655 if (!ADS_ERR_OK(status)) {
1656 DEBUG(1,("Failed while searching for: %s\n", base));
1657 goto out;
1660 if (ads_count_replies(ads, res) != 1) {
1661 goto out;
1664 /* substitute the bind-path from the well-known-guid-search result */
1665 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1666 if (!wkn_dn) {
1667 goto out;
1670 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1671 if (!wkn_dn_exp) {
1672 goto out;
1675 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1676 if (!bind_dn_exp) {
1677 goto out;
1680 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1682 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1685 new_ln = wkn_ln - bind_ln;
1687 ret = SMB_STRDUP(wkn_dn_exp[0]);
1688 if (!ret) {
1689 goto out;
1692 for (i=1; i < new_ln; i++) {
1693 char *s = NULL;
1695 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1696 SAFE_FREE(ret);
1697 goto out;
1700 SAFE_FREE(ret);
1701 ret = SMB_STRDUP(s);
1702 free(s);
1703 if (!ret) {
1704 goto out;
1708 out:
1709 SAFE_FREE(base);
1710 ads_msgfree(ads, res);
1711 TALLOC_FREE(wkn_dn);
1712 if (wkn_dn_exp) {
1713 ldap_value_free(wkn_dn_exp);
1715 if (bind_dn_exp) {
1716 ldap_value_free(bind_dn_exp);
1719 return ret;
1723 * Adds (appends) an item to an attribute array, rather then
1724 * replacing the whole list
1725 * @param ctx An initialized TALLOC_CTX
1726 * @param mods An initialized ADS_MODLIST
1727 * @param name name of the ldap attribute to append to
1728 * @param vals an array of values to add
1729 * @return status of addition
1732 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1733 const char *name, const char **vals)
1735 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1736 (const void *) vals);
1740 * Determines the an account's current KVNO via an LDAP lookup
1741 * @param ads An initialized ADS_STRUCT
1742 * @param account_name the NT samaccountname.
1743 * @return the kvno for the account, or -1 in case of a failure.
1746 uint32_t ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1748 LDAPMessage *res = NULL;
1749 uint32_t kvno = (uint32_t)-1; /* -1 indicates a failure */
1750 char *filter;
1751 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1752 char *dn_string = NULL;
1753 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1755 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1756 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1757 return kvno;
1759 ret = ads_search(ads, &res, filter, attrs);
1760 SAFE_FREE(filter);
1761 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1762 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1763 ads_msgfree(ads, res);
1764 return kvno;
1767 dn_string = ads_get_dn(ads, talloc_tos(), res);
1768 if (!dn_string) {
1769 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1770 ads_msgfree(ads, res);
1771 return kvno;
1773 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1774 TALLOC_FREE(dn_string);
1776 /* ---------------------------------------------------------
1777 * 0 is returned as a default KVNO from this point on...
1778 * This is done because Windows 2000 does not support key
1779 * version numbers. Chances are that a failure in the next
1780 * step is simply due to Windows 2000 being used for a
1781 * domain controller. */
1782 kvno = 0;
1784 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1785 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1786 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1787 ads_msgfree(ads, res);
1788 return kvno;
1791 /* Success */
1792 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1793 ads_msgfree(ads, res);
1794 return kvno;
1798 * Determines the computer account's current KVNO via an LDAP lookup
1799 * @param ads An initialized ADS_STRUCT
1800 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1801 * @return the kvno for the computer account, or -1 in case of a failure.
1804 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1806 char *computer_account = NULL;
1807 uint32_t kvno = -1;
1809 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1810 return kvno;
1813 kvno = ads_get_kvno(ads, computer_account);
1814 free(computer_account);
1816 return kvno;
1820 * This clears out all registered spn's for a given hostname
1821 * @param ads An initilaized ADS_STRUCT
1822 * @param machine_name the NetBIOS name of the computer.
1823 * @return 0 upon success, non-zero otherwise.
1826 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1828 TALLOC_CTX *ctx;
1829 LDAPMessage *res = NULL;
1830 ADS_MODLIST mods;
1831 const char *servicePrincipalName[1] = {NULL};
1832 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1833 char *dn_string = NULL;
1835 ret = ads_find_machine_acct(ads, &res, machine_name);
1836 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1837 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1838 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1839 ads_msgfree(ads, res);
1840 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1843 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1844 ctx = talloc_init("ads_clear_service_principal_names");
1845 if (!ctx) {
1846 ads_msgfree(ads, res);
1847 return ADS_ERROR(LDAP_NO_MEMORY);
1850 if (!(mods = ads_init_mods(ctx))) {
1851 talloc_destroy(ctx);
1852 ads_msgfree(ads, res);
1853 return ADS_ERROR(LDAP_NO_MEMORY);
1855 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1856 if (!ADS_ERR_OK(ret)) {
1857 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1858 ads_msgfree(ads, res);
1859 talloc_destroy(ctx);
1860 return ret;
1862 dn_string = ads_get_dn(ads, talloc_tos(), res);
1863 if (!dn_string) {
1864 talloc_destroy(ctx);
1865 ads_msgfree(ads, res);
1866 return ADS_ERROR(LDAP_NO_MEMORY);
1868 ret = ads_gen_mod(ads, dn_string, mods);
1869 TALLOC_FREE(dn_string);
1870 if (!ADS_ERR_OK(ret)) {
1871 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1872 machine_name));
1873 ads_msgfree(ads, res);
1874 talloc_destroy(ctx);
1875 return ret;
1878 ads_msgfree(ads, res);
1879 talloc_destroy(ctx);
1880 return ret;
1884 * @brief Search for an element in a string array.
1886 * @param[in] el_array The string array to search.
1888 * @param[in] num_el The number of elements in the string array.
1890 * @param[in] el The string to search.
1892 * @return True if found, false if not.
1894 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1896 size_t i;
1898 if (el_array == NULL || num_el == 0 || el == NULL) {
1899 return false;
1902 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1903 int cmp;
1905 cmp = strcasecmp_m(el_array[i], el);
1906 if (cmp == 0) {
1907 return true;
1911 return false;
1915 * @brief This gets the service principal names of an existing computer account.
1917 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1919 * @param[in] ads The ADS context to use.
1921 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1922 * identify the computer account.
1924 * @param[in] spn_array A pointer to store the array for SPNs.
1926 * @param[in] num_spns The number of principals stored in the array.
1928 * @return 0 on success, or a ADS error if a failure occured.
1930 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1931 ADS_STRUCT *ads,
1932 const char *machine_name,
1933 char ***spn_array,
1934 size_t *num_spns)
1936 ADS_STATUS status;
1937 LDAPMessage *res = NULL;
1938 int count;
1940 status = ads_find_machine_acct(ads,
1941 &res,
1942 machine_name);
1943 if (!ADS_ERR_OK(status)) {
1944 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1945 machine_name));
1946 return status;
1949 count = ads_count_replies(ads, res);
1950 if (count != 1) {
1951 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1952 goto done;
1955 *spn_array = ads_pull_strings(ads,
1956 mem_ctx,
1957 res,
1958 "servicePrincipalName",
1959 num_spns);
1960 if (*spn_array == NULL) {
1961 DEBUG(1, ("Host account for %s does not have service principal "
1962 "names.\n",
1963 machine_name));
1964 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1965 goto done;
1968 done:
1969 ads_msgfree(ads, res);
1971 return status;
1975 * This adds a service principal name to an existing computer account
1976 * (found by hostname) in AD.
1977 * @param ads An initialized ADS_STRUCT
1978 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1979 * @param my_fqdn The fully qualified DNS name of the machine
1980 * @param spn A string of the service principal to add, i.e. 'host'
1981 * @return 0 upon sucess, or non-zero if a failure occurs
1984 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1985 const char *my_fqdn, const char *spn)
1987 ADS_STATUS ret;
1988 TALLOC_CTX *ctx;
1989 LDAPMessage *res = NULL;
1990 char *psp1, *psp2;
1991 ADS_MODLIST mods;
1992 char *dn_string = NULL;
1993 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1995 ret = ads_find_machine_acct(ads, &res, machine_name);
1996 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1997 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1998 machine_name));
1999 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2000 spn, machine_name, ads->config.realm));
2001 ads_msgfree(ads, res);
2002 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2005 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2006 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2007 ads_msgfree(ads, res);
2008 return ADS_ERROR(LDAP_NO_MEMORY);
2011 /* add short name spn */
2013 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2014 talloc_destroy(ctx);
2015 ads_msgfree(ads, res);
2016 return ADS_ERROR(LDAP_NO_MEMORY);
2018 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2019 ret = ADS_ERROR(LDAP_NO_MEMORY);
2020 goto out;
2022 servicePrincipalName[0] = psp1;
2024 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2025 psp1, machine_name));
2028 /* add fully qualified spn */
2030 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2031 ret = ADS_ERROR(LDAP_NO_MEMORY);
2032 goto out;
2034 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2035 ret = ADS_ERROR(LDAP_NO_MEMORY);
2036 goto out;
2038 servicePrincipalName[1] = psp2;
2040 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2041 psp2, machine_name));
2043 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2044 ret = ADS_ERROR(LDAP_NO_MEMORY);
2045 goto out;
2048 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2049 if (!ADS_ERR_OK(ret)) {
2050 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2051 goto out;
2054 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2055 ret = ADS_ERROR(LDAP_NO_MEMORY);
2056 goto out;
2059 ret = ads_gen_mod(ads, dn_string, mods);
2060 if (!ADS_ERR_OK(ret)) {
2061 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2062 goto out;
2065 out:
2066 TALLOC_FREE( ctx );
2067 ads_msgfree(ads, res);
2068 return ret;
2072 * adds a machine account to the ADS server
2073 * @param ads An intialized ADS_STRUCT
2074 * @param machine_name - the NetBIOS machine name of this account.
2075 * @param account_type A number indicating the type of account to create
2076 * @param org_unit The LDAP path in which to place this account
2077 * @return 0 upon success, or non-zero otherwise
2080 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads,
2081 const char *machine_name,
2082 const char *org_unit,
2083 uint32_t etype_list)
2085 ADS_STATUS ret;
2086 char *samAccountName, *controlstr;
2087 TALLOC_CTX *ctx;
2088 ADS_MODLIST mods;
2089 char *machine_escaped = NULL;
2090 char *new_dn;
2091 const char *objectClass[] = {"top", "person", "organizationalPerson",
2092 "user", "computer", NULL};
2093 LDAPMessage *res = NULL;
2094 uint32_t acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2095 UF_DONT_EXPIRE_PASSWD |\
2096 UF_ACCOUNTDISABLE );
2097 uint32_t func_level = 0;
2099 ret = ads_domain_func_level(ads, &func_level);
2100 if (!ADS_ERR_OK(ret)) {
2101 return ret;
2104 if (!(ctx = talloc_init("ads_add_machine_acct")))
2105 return ADS_ERROR(LDAP_NO_MEMORY);
2107 ret = ADS_ERROR(LDAP_NO_MEMORY);
2109 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2110 if (!machine_escaped) {
2111 goto done;
2114 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2115 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2117 if ( !new_dn || !samAccountName ) {
2118 goto done;
2121 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2122 goto done;
2125 if (!(mods = ads_init_mods(ctx))) {
2126 goto done;
2129 ads_mod_str(ctx, &mods, "cn", machine_name);
2130 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2131 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2132 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2134 if (func_level >= DS_DOMAIN_FUNCTION_2008) {
2135 const char *etype_list_str;
2137 etype_list_str = talloc_asprintf(ctx, "%d", (int)etype_list);
2138 if (etype_list_str == NULL) {
2139 goto done;
2141 ads_mod_str(ctx, &mods, "msDS-SupportedEncryptionTypes",
2142 etype_list_str);
2145 ret = ads_gen_add(ads, new_dn, mods);
2147 done:
2148 SAFE_FREE(machine_escaped);
2149 ads_msgfree(ads, res);
2150 talloc_destroy(ctx);
2152 return ret;
2156 * move a machine account to another OU on the ADS server
2157 * @param ads - An intialized ADS_STRUCT
2158 * @param machine_name - the NetBIOS machine name of this account.
2159 * @param org_unit - The LDAP path in which to place this account
2160 * @param moved - whether we moved the machine account (optional)
2161 * @return 0 upon success, or non-zero otherwise
2164 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2165 const char *org_unit, bool *moved)
2167 ADS_STATUS rc;
2168 int ldap_status;
2169 LDAPMessage *res = NULL;
2170 char *filter = NULL;
2171 char *computer_dn = NULL;
2172 char *parent_dn;
2173 char *computer_rdn = NULL;
2174 bool need_move = False;
2176 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2177 rc = ADS_ERROR(LDAP_NO_MEMORY);
2178 goto done;
2181 /* Find pre-existing machine */
2182 rc = ads_search(ads, &res, filter, NULL);
2183 if (!ADS_ERR_OK(rc)) {
2184 goto done;
2187 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2188 if (!computer_dn) {
2189 rc = ADS_ERROR(LDAP_NO_MEMORY);
2190 goto done;
2193 parent_dn = ads_parent_dn(computer_dn);
2194 if (strequal(parent_dn, org_unit)) {
2195 goto done;
2198 need_move = True;
2200 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2201 rc = ADS_ERROR(LDAP_NO_MEMORY);
2202 goto done;
2205 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2206 org_unit, 1, NULL, NULL);
2207 rc = ADS_ERROR(ldap_status);
2209 done:
2210 ads_msgfree(ads, res);
2211 SAFE_FREE(filter);
2212 TALLOC_FREE(computer_dn);
2213 SAFE_FREE(computer_rdn);
2215 if (!ADS_ERR_OK(rc)) {
2216 need_move = False;
2219 if (moved) {
2220 *moved = need_move;
2223 return rc;
2227 dump a binary result from ldap
2229 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2231 int i, j;
2232 for (i=0; values[i]; i++) {
2233 printf("%s: ", field);
2234 for (j=0; j<values[i]->bv_len; j++) {
2235 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2237 printf("\n");
2241 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2243 int i;
2244 for (i=0; values[i]; i++) {
2245 NTSTATUS status;
2246 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2247 struct GUID guid;
2249 status = GUID_from_ndr_blob(&in, &guid);
2250 if (NT_STATUS_IS_OK(status)) {
2251 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2252 } else {
2253 printf("%s: INVALID GUID\n", field);
2259 dump a sid result from ldap
2261 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2263 int i;
2264 for (i=0; values[i]; i++) {
2265 struct dom_sid sid;
2266 fstring tmp;
2267 if (!sid_parse((const uint8_t *)values[i]->bv_val,
2268 values[i]->bv_len, &sid)) {
2269 return;
2271 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2276 dump ntSecurityDescriptor
2278 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2280 TALLOC_CTX *frame = talloc_stackframe();
2281 struct security_descriptor *psd;
2282 NTSTATUS status;
2284 status = unmarshall_sec_desc(talloc_tos(), (uint8_t *)values[0]->bv_val,
2285 values[0]->bv_len, &psd);
2286 if (!NT_STATUS_IS_OK(status)) {
2287 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2288 nt_errstr(status)));
2289 TALLOC_FREE(frame);
2290 return;
2293 if (psd) {
2294 ads_disp_sd(ads, talloc_tos(), psd);
2297 TALLOC_FREE(frame);
2301 dump a string result from ldap
2303 static void dump_string(const char *field, char **values)
2305 int i;
2306 for (i=0; values[i]; i++) {
2307 printf("%s: %s\n", field, values[i]);
2312 dump a field from LDAP on stdout
2313 used for debugging
2316 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2318 const struct {
2319 const char *name;
2320 bool string;
2321 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2322 } handlers[] = {
2323 {"objectGUID", False, dump_guid},
2324 {"netbootGUID", False, dump_guid},
2325 {"nTSecurityDescriptor", False, dump_sd},
2326 {"dnsRecord", False, dump_binary},
2327 {"objectSid", False, dump_sid},
2328 {"tokenGroups", False, dump_sid},
2329 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2330 {"tokengroupsGlobalandUniversal", False, dump_sid},
2331 {"mS-DS-CreatorSID", False, dump_sid},
2332 {"msExchMailboxGuid", False, dump_guid},
2333 {NULL, True, NULL}
2335 int i;
2337 if (!field) { /* must be end of an entry */
2338 printf("\n");
2339 return False;
2342 for (i=0; handlers[i].name; i++) {
2343 if (strcasecmp_m(handlers[i].name, field) == 0) {
2344 if (!values) /* first time, indicate string or not */
2345 return handlers[i].string;
2346 handlers[i].handler(ads, field, (struct berval **) values);
2347 break;
2350 if (!handlers[i].name) {
2351 if (!values) /* first time, indicate string conversion */
2352 return True;
2353 dump_string(field, (char **)values);
2355 return False;
2359 * Dump a result from LDAP on stdout
2360 * used for debugging
2361 * @param ads connection to ads server
2362 * @param res Results to dump
2365 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2367 ads_process_results(ads, res, ads_dump_field, NULL);
2371 * Walk through results, calling a function for each entry found.
2372 * The function receives a field name, a berval * array of values,
2373 * and a data area passed through from the start. The function is
2374 * called once with null for field and values at the end of each
2375 * entry.
2376 * @param ads connection to ads server
2377 * @param res Results to process
2378 * @param fn Function for processing each result
2379 * @param data_area user-defined area to pass to function
2381 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2382 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2383 void *data_area)
2385 LDAPMessage *msg;
2386 TALLOC_CTX *ctx;
2387 size_t converted_size;
2389 if (!(ctx = talloc_init("ads_process_results")))
2390 return;
2392 for (msg = ads_first_entry(ads, res); msg;
2393 msg = ads_next_entry(ads, msg)) {
2394 char *utf8_field;
2395 BerElement *b;
2397 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2398 (LDAPMessage *)msg,&b);
2399 utf8_field;
2400 utf8_field=ldap_next_attribute(ads->ldap.ld,
2401 (LDAPMessage *)msg,b)) {
2402 struct berval **ber_vals;
2403 char **str_vals;
2404 char **utf8_vals;
2405 char *field;
2406 bool string;
2408 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2409 &converted_size))
2411 DEBUG(0,("ads_process_results: "
2412 "pull_utf8_talloc failed: %s",
2413 strerror(errno)));
2416 string = fn(ads, field, NULL, data_area);
2418 if (string) {
2419 const char **p;
2421 utf8_vals = ldap_get_values(ads->ldap.ld,
2422 (LDAPMessage *)msg, field);
2423 p = discard_const_p(const char *, utf8_vals);
2424 str_vals = ads_pull_strvals(ctx, p);
2425 fn(ads, field, (void **) str_vals, data_area);
2426 ldap_value_free(utf8_vals);
2427 } else {
2428 ber_vals = ldap_get_values_len(ads->ldap.ld,
2429 (LDAPMessage *)msg, field);
2430 fn(ads, field, (void **) ber_vals, data_area);
2432 ldap_value_free_len(ber_vals);
2434 ldap_memfree(utf8_field);
2436 ber_free(b, 0);
2437 talloc_free_children(ctx);
2438 fn(ads, NULL, NULL, data_area); /* completed an entry */
2441 talloc_destroy(ctx);
2445 * count how many replies are in a LDAPMessage
2446 * @param ads connection to ads server
2447 * @param res Results to count
2448 * @return number of replies
2450 int ads_count_replies(ADS_STRUCT *ads, void *res)
2452 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2456 * pull the first entry from a ADS result
2457 * @param ads connection to ads server
2458 * @param res Results of search
2459 * @return first entry from result
2461 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2463 return ldap_first_entry(ads->ldap.ld, res);
2467 * pull the next entry from a ADS result
2468 * @param ads connection to ads server
2469 * @param res Results of search
2470 * @return next entry from result
2472 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2474 return ldap_next_entry(ads->ldap.ld, res);
2478 * pull the first message from a ADS result
2479 * @param ads connection to ads server
2480 * @param res Results of search
2481 * @return first message from result
2483 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2485 return ldap_first_message(ads->ldap.ld, res);
2489 * pull the next message from a ADS result
2490 * @param ads connection to ads server
2491 * @param res Results of search
2492 * @return next message from result
2494 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2496 return ldap_next_message(ads->ldap.ld, res);
2500 * pull a single string from a ADS result
2501 * @param ads connection to ads server
2502 * @param mem_ctx TALLOC_CTX to use for allocating result string
2503 * @param msg Results of search
2504 * @param field Attribute to retrieve
2505 * @return Result string in talloc context
2507 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2508 const char *field)
2510 char **values;
2511 char *ret = NULL;
2512 char *ux_string;
2513 size_t converted_size;
2515 values = ldap_get_values(ads->ldap.ld, msg, field);
2516 if (!values)
2517 return NULL;
2519 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2520 &converted_size))
2522 ret = ux_string;
2524 ldap_value_free(values);
2525 return ret;
2529 * pull an array of strings from a ADS result
2530 * @param ads connection to ads server
2531 * @param mem_ctx TALLOC_CTX to use for allocating result string
2532 * @param msg Results of search
2533 * @param field Attribute to retrieve
2534 * @return Result strings in talloc context
2536 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2537 LDAPMessage *msg, const char *field,
2538 size_t *num_values)
2540 char **values;
2541 char **ret = NULL;
2542 int i;
2543 size_t converted_size;
2545 values = ldap_get_values(ads->ldap.ld, msg, field);
2546 if (!values)
2547 return NULL;
2549 *num_values = ldap_count_values(values);
2551 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2552 if (!ret) {
2553 ldap_value_free(values);
2554 return NULL;
2557 for (i=0;i<*num_values;i++) {
2558 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2559 &converted_size))
2561 ldap_value_free(values);
2562 return NULL;
2565 ret[i] = NULL;
2567 ldap_value_free(values);
2568 return ret;
2572 * pull an array of strings from a ADS result
2573 * (handle large multivalue attributes with range retrieval)
2574 * @param ads connection to ads server
2575 * @param mem_ctx TALLOC_CTX to use for allocating result string
2576 * @param msg Results of search
2577 * @param field Attribute to retrieve
2578 * @param current_strings strings returned by a previous call to this function
2579 * @param next_attribute The next query should ask for this attribute
2580 * @param num_values How many values did we get this time?
2581 * @param more_values Are there more values to get?
2582 * @return Result strings in talloc context
2584 char **ads_pull_strings_range(ADS_STRUCT *ads,
2585 TALLOC_CTX *mem_ctx,
2586 LDAPMessage *msg, const char *field,
2587 char **current_strings,
2588 const char **next_attribute,
2589 size_t *num_strings,
2590 bool *more_strings)
2592 char *attr;
2593 char *expected_range_attrib, *range_attr;
2594 BerElement *ptr = NULL;
2595 char **strings;
2596 char **new_strings;
2597 size_t num_new_strings;
2598 unsigned long int range_start;
2599 unsigned long int range_end;
2601 /* we might have been given the whole lot anyway */
2602 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2603 *more_strings = False;
2604 return strings;
2607 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2609 /* look for Range result */
2610 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2611 attr;
2612 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2613 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2614 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2615 range_attr = attr;
2616 break;
2618 ldap_memfree(attr);
2620 if (!attr) {
2621 ber_free(ptr, 0);
2622 /* nothing here - this field is just empty */
2623 *more_strings = False;
2624 return NULL;
2627 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2628 &range_start, &range_end) == 2) {
2629 *more_strings = True;
2630 } else {
2631 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2632 &range_start) == 1) {
2633 *more_strings = False;
2634 } else {
2635 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2636 range_attr));
2637 ldap_memfree(range_attr);
2638 *more_strings = False;
2639 return NULL;
2643 if ((*num_strings) != range_start) {
2644 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2645 " - aborting range retreival\n",
2646 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2647 ldap_memfree(range_attr);
2648 *more_strings = False;
2649 return NULL;
2652 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2654 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2655 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2656 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2657 range_attr, (unsigned long int)range_end - range_start + 1,
2658 (unsigned long int)num_new_strings));
2659 ldap_memfree(range_attr);
2660 *more_strings = False;
2661 return NULL;
2664 strings = talloc_realloc(mem_ctx, current_strings, char *,
2665 *num_strings + num_new_strings);
2667 if (strings == NULL) {
2668 ldap_memfree(range_attr);
2669 *more_strings = False;
2670 return NULL;
2673 if (new_strings && num_new_strings) {
2674 memcpy(&strings[*num_strings], new_strings,
2675 sizeof(*new_strings) * num_new_strings);
2678 (*num_strings) += num_new_strings;
2680 if (*more_strings) {
2681 *next_attribute = talloc_asprintf(mem_ctx,
2682 "%s;range=%d-*",
2683 field,
2684 (int)*num_strings);
2686 if (!*next_attribute) {
2687 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2688 ldap_memfree(range_attr);
2689 *more_strings = False;
2690 return NULL;
2694 ldap_memfree(range_attr);
2696 return strings;
2700 * pull a single uint32_t from a ADS result
2701 * @param ads connection to ads server
2702 * @param msg Results of search
2703 * @param field Attribute to retrieve
2704 * @param v Pointer to int to store result
2705 * @return boolean inidicating success
2707 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2708 uint32_t *v)
2710 char **values;
2712 values = ldap_get_values(ads->ldap.ld, msg, field);
2713 if (!values)
2714 return False;
2715 if (!values[0]) {
2716 ldap_value_free(values);
2717 return False;
2720 *v = atoi(values[0]);
2721 ldap_value_free(values);
2722 return True;
2726 * pull a single objectGUID from an ADS result
2727 * @param ads connection to ADS server
2728 * @param msg results of search
2729 * @param guid 37-byte area to receive text guid
2730 * @return boolean indicating success
2732 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2734 DATA_BLOB blob;
2735 NTSTATUS status;
2737 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2738 &blob)) {
2739 return false;
2742 status = GUID_from_ndr_blob(&blob, guid);
2743 talloc_free(blob.data);
2744 return NT_STATUS_IS_OK(status);
2749 * pull a single struct dom_sid from a ADS result
2750 * @param ads connection to ads server
2751 * @param msg Results of search
2752 * @param field Attribute to retrieve
2753 * @param sid Pointer to sid to store result
2754 * @return boolean inidicating success
2756 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2757 struct dom_sid *sid)
2759 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2763 * pull an array of struct dom_sids from a ADS result
2764 * @param ads connection to ads server
2765 * @param mem_ctx TALLOC_CTX for allocating sid array
2766 * @param msg Results of search
2767 * @param field Attribute to retrieve
2768 * @param sids pointer to sid array to allocate
2769 * @return the count of SIDs pulled
2771 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2772 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2774 struct berval **values;
2775 bool ret;
2776 int count, i;
2778 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2780 if (!values)
2781 return 0;
2783 for (i=0; values[i]; i++)
2784 /* nop */ ;
2786 if (i) {
2787 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2788 if (!(*sids)) {
2789 ldap_value_free_len(values);
2790 return 0;
2792 } else {
2793 (*sids) = NULL;
2796 count = 0;
2797 for (i=0; values[i]; i++) {
2798 ret = sid_parse((const uint8_t *)values[i]->bv_val,
2799 values[i]->bv_len, &(*sids)[count]);
2800 if (ret) {
2801 DEBUG(10, ("pulling SID: %s\n",
2802 sid_string_dbg(&(*sids)[count])));
2803 count++;
2807 ldap_value_free_len(values);
2808 return count;
2812 * pull a struct security_descriptor from a ADS result
2813 * @param ads connection to ads server
2814 * @param mem_ctx TALLOC_CTX for allocating sid array
2815 * @param msg Results of search
2816 * @param field Attribute to retrieve
2817 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2818 * @return boolean inidicating success
2820 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2821 LDAPMessage *msg, const char *field,
2822 struct security_descriptor **sd)
2824 struct berval **values;
2825 bool ret = true;
2827 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2829 if (!values) return false;
2831 if (values[0]) {
2832 NTSTATUS status;
2833 status = unmarshall_sec_desc(mem_ctx,
2834 (uint8_t *)values[0]->bv_val,
2835 values[0]->bv_len, sd);
2836 if (!NT_STATUS_IS_OK(status)) {
2837 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2838 nt_errstr(status)));
2839 ret = false;
2843 ldap_value_free_len(values);
2844 return ret;
2848 * in order to support usernames longer than 21 characters we need to
2849 * use both the sAMAccountName and the userPrincipalName attributes
2850 * It seems that not all users have the userPrincipalName attribute set
2852 * @param ads connection to ads server
2853 * @param mem_ctx TALLOC_CTX for allocating sid array
2854 * @param msg Results of search
2855 * @return the username
2857 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2858 LDAPMessage *msg)
2860 #if 0 /* JERRY */
2861 char *ret, *p;
2863 /* lookup_name() only works on the sAMAccountName to
2864 returning the username portion of userPrincipalName
2865 breaks winbindd_getpwnam() */
2867 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2868 if (ret && (p = strchr_m(ret, '@'))) {
2869 *p = 0;
2870 return ret;
2872 #endif
2873 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2878 * find the update serial number - this is the core of the ldap cache
2879 * @param ads connection to ads server
2880 * @param ads connection to ADS server
2881 * @param usn Pointer to retrieved update serial number
2882 * @return status of search
2884 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32_t *usn)
2886 const char *attrs[] = {"highestCommittedUSN", NULL};
2887 ADS_STATUS status;
2888 LDAPMessage *res;
2890 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2891 if (!ADS_ERR_OK(status))
2892 return status;
2894 if (ads_count_replies(ads, res) != 1) {
2895 ads_msgfree(ads, res);
2896 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2899 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2900 ads_msgfree(ads, res);
2901 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2904 ads_msgfree(ads, res);
2905 return ADS_SUCCESS;
2908 /* parse a ADS timestring - typical string is
2909 '20020917091222.0Z0' which means 09:12.22 17th September
2910 2002, timezone 0 */
2911 static time_t ads_parse_time(const char *str)
2913 struct tm tm;
2915 ZERO_STRUCT(tm);
2917 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2918 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2919 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2920 return 0;
2922 tm.tm_year -= 1900;
2923 tm.tm_mon -= 1;
2925 return timegm(&tm);
2928 /********************************************************************
2929 ********************************************************************/
2931 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2933 const char *attrs[] = {"currentTime", NULL};
2934 ADS_STATUS status;
2935 LDAPMessage *res;
2936 char *timestr;
2937 TALLOC_CTX *ctx;
2938 ADS_STRUCT *ads_s = ads;
2940 if (!(ctx = talloc_init("ads_current_time"))) {
2941 return ADS_ERROR(LDAP_NO_MEMORY);
2944 /* establish a new ldap tcp session if necessary */
2946 if ( !ads->ldap.ld ) {
2947 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2948 ads->server.ldap_server )) == NULL )
2950 goto done;
2952 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2953 status = ads_connect( ads_s );
2954 if ( !ADS_ERR_OK(status))
2955 goto done;
2958 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2959 if (!ADS_ERR_OK(status)) {
2960 goto done;
2963 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2964 if (!timestr) {
2965 ads_msgfree(ads_s, res);
2966 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2967 goto done;
2970 /* but save the time and offset in the original ADS_STRUCT */
2972 ads->config.current_time = ads_parse_time(timestr);
2974 if (ads->config.current_time != 0) {
2975 ads->auth.time_offset = ads->config.current_time - time(NULL);
2976 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2979 ads_msgfree(ads, res);
2981 status = ADS_SUCCESS;
2983 done:
2984 /* free any temporary ads connections */
2985 if ( ads_s != ads ) {
2986 ads_destroy( &ads_s );
2988 talloc_destroy(ctx);
2990 return status;
2993 /********************************************************************
2994 ********************************************************************/
2996 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32_t *val)
2998 const char *attrs[] = {"domainFunctionality", NULL};
2999 ADS_STATUS status;
3000 LDAPMessage *res;
3001 ADS_STRUCT *ads_s = ads;
3003 *val = DS_DOMAIN_FUNCTION_2000;
3005 /* establish a new ldap tcp session if necessary */
3007 if ( !ads->ldap.ld ) {
3008 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3009 ads->server.ldap_server )) == NULL )
3011 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3012 goto done;
3014 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3015 status = ads_connect( ads_s );
3016 if ( !ADS_ERR_OK(status))
3017 goto done;
3020 /* If the attribute does not exist assume it is a Windows 2000
3021 functional domain */
3023 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3024 if (!ADS_ERR_OK(status)) {
3025 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3026 status = ADS_SUCCESS;
3028 goto done;
3031 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3032 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3034 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3037 ads_msgfree(ads, res);
3039 done:
3040 /* free any temporary ads connections */
3041 if ( ads_s != ads ) {
3042 ads_destroy( &ads_s );
3045 return status;
3049 * find the domain sid for our domain
3050 * @param ads connection to ads server
3051 * @param sid Pointer to domain sid
3052 * @return status of search
3054 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3056 const char *attrs[] = {"objectSid", NULL};
3057 LDAPMessage *res;
3058 ADS_STATUS rc;
3060 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3061 attrs, &res);
3062 if (!ADS_ERR_OK(rc)) return rc;
3063 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3064 ads_msgfree(ads, res);
3065 return ADS_ERROR_SYSTEM(ENOENT);
3067 ads_msgfree(ads, res);
3069 return ADS_SUCCESS;
3073 * find our site name
3074 * @param ads connection to ads server
3075 * @param mem_ctx Pointer to talloc context
3076 * @param site_name Pointer to the sitename
3077 * @return status of search
3079 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3081 ADS_STATUS status;
3082 LDAPMessage *res;
3083 const char *dn, *service_name;
3084 const char *attrs[] = { "dsServiceName", NULL };
3086 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3087 if (!ADS_ERR_OK(status)) {
3088 return status;
3091 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3092 if (service_name == NULL) {
3093 ads_msgfree(ads, res);
3094 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3097 ads_msgfree(ads, res);
3099 /* go up three levels */
3100 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3101 if (dn == NULL) {
3102 return ADS_ERROR(LDAP_NO_MEMORY);
3105 *site_name = talloc_strdup(mem_ctx, dn);
3106 if (*site_name == NULL) {
3107 return ADS_ERROR(LDAP_NO_MEMORY);
3110 return status;
3112 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3117 * find the site dn where a machine resides
3118 * @param ads connection to ads server
3119 * @param mem_ctx Pointer to talloc context
3120 * @param computer_name name of the machine
3121 * @param site_name Pointer to the sitename
3122 * @return status of search
3124 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3126 ADS_STATUS status;
3127 LDAPMessage *res;
3128 const char *parent, *filter;
3129 char *config_context = NULL;
3130 char *dn;
3132 /* shortcut a query */
3133 if (strequal(computer_name, ads->config.ldap_server_name)) {
3134 return ads_site_dn(ads, mem_ctx, site_dn);
3137 status = ads_config_path(ads, mem_ctx, &config_context);
3138 if (!ADS_ERR_OK(status)) {
3139 return status;
3142 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3143 if (filter == NULL) {
3144 return ADS_ERROR(LDAP_NO_MEMORY);
3147 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3148 filter, NULL, &res);
3149 if (!ADS_ERR_OK(status)) {
3150 return status;
3153 if (ads_count_replies(ads, res) != 1) {
3154 ads_msgfree(ads, res);
3155 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3158 dn = ads_get_dn(ads, mem_ctx, res);
3159 if (dn == NULL) {
3160 ads_msgfree(ads, res);
3161 return ADS_ERROR(LDAP_NO_MEMORY);
3164 /* go up three levels */
3165 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3166 if (parent == NULL) {
3167 ads_msgfree(ads, res);
3168 TALLOC_FREE(dn);
3169 return ADS_ERROR(LDAP_NO_MEMORY);
3172 *site_dn = talloc_strdup(mem_ctx, parent);
3173 if (*site_dn == NULL) {
3174 ads_msgfree(ads, res);
3175 TALLOC_FREE(dn);
3176 return ADS_ERROR(LDAP_NO_MEMORY);
3179 TALLOC_FREE(dn);
3180 ads_msgfree(ads, res);
3182 return status;
3186 * get the upn suffixes for a domain
3187 * @param ads connection to ads server
3188 * @param mem_ctx Pointer to talloc context
3189 * @param suffixes Pointer to an array of suffixes
3190 * @param num_suffixes Pointer to the number of suffixes
3191 * @return status of search
3193 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3195 ADS_STATUS status;
3196 LDAPMessage *res;
3197 const char *base;
3198 char *config_context = NULL;
3199 const char *attrs[] = { "uPNSuffixes", NULL };
3201 status = ads_config_path(ads, mem_ctx, &config_context);
3202 if (!ADS_ERR_OK(status)) {
3203 return status;
3206 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3207 if (base == NULL) {
3208 return ADS_ERROR(LDAP_NO_MEMORY);
3211 status = ads_search_dn(ads, &res, base, attrs);
3212 if (!ADS_ERR_OK(status)) {
3213 return status;
3216 if (ads_count_replies(ads, res) != 1) {
3217 ads_msgfree(ads, res);
3218 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3221 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3222 if ((*suffixes) == NULL) {
3223 ads_msgfree(ads, res);
3224 return ADS_ERROR(LDAP_NO_MEMORY);
3227 ads_msgfree(ads, res);
3229 return status;
3233 * get the joinable ous for a domain
3234 * @param ads connection to ads server
3235 * @param mem_ctx Pointer to talloc context
3236 * @param ous Pointer to an array of ous
3237 * @param num_ous Pointer to the number of ous
3238 * @return status of search
3240 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3241 TALLOC_CTX *mem_ctx,
3242 char ***ous,
3243 size_t *num_ous)
3245 ADS_STATUS status;
3246 LDAPMessage *res = NULL;
3247 LDAPMessage *msg = NULL;
3248 const char *attrs[] = { "dn", NULL };
3249 int count = 0;
3251 status = ads_search(ads, &res,
3252 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3253 attrs);
3254 if (!ADS_ERR_OK(status)) {
3255 return status;
3258 count = ads_count_replies(ads, res);
3259 if (count < 1) {
3260 ads_msgfree(ads, res);
3261 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3264 for (msg = ads_first_entry(ads, res); msg;
3265 msg = ads_next_entry(ads, msg)) {
3266 const char **p = discard_const_p(const char *, *ous);
3267 char *dn = NULL;
3269 dn = ads_get_dn(ads, talloc_tos(), msg);
3270 if (!dn) {
3271 ads_msgfree(ads, res);
3272 return ADS_ERROR(LDAP_NO_MEMORY);
3275 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3276 TALLOC_FREE(dn);
3277 ads_msgfree(ads, res);
3278 return ADS_ERROR(LDAP_NO_MEMORY);
3281 TALLOC_FREE(dn);
3282 *ous = discard_const_p(char *, p);
3285 ads_msgfree(ads, res);
3287 return status;
3292 * pull a struct dom_sid from an extended dn string
3293 * @param mem_ctx TALLOC_CTX
3294 * @param extended_dn string
3295 * @param flags string type of extended_dn
3296 * @param sid pointer to a struct dom_sid
3297 * @return NT_STATUS_OK on success,
3298 * NT_INVALID_PARAMETER on error,
3299 * NT_STATUS_NOT_FOUND if no SID present
3301 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3302 const char *extended_dn,
3303 enum ads_extended_dn_flags flags,
3304 struct dom_sid *sid)
3306 char *p, *q, *dn;
3308 if (!extended_dn) {
3309 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3312 /* otherwise extended_dn gets stripped off */
3313 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3314 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3317 * ADS_EXTENDED_DN_HEX_STRING:
3318 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3320 * ADS_EXTENDED_DN_STRING (only with w2k3):
3321 * <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
3323 * Object with no SID, such as an Exchange Public Folder
3324 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3327 p = strchr(dn, ';');
3328 if (!p) {
3329 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3332 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3333 DEBUG(5,("No SID present in extended dn\n"));
3334 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3337 p += strlen(";<SID=");
3339 q = strchr(p, '>');
3340 if (!q) {
3341 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3344 *q = '\0';
3346 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3348 switch (flags) {
3350 case ADS_EXTENDED_DN_STRING:
3351 if (!string_to_sid(sid, p)) {
3352 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3354 break;
3355 case ADS_EXTENDED_DN_HEX_STRING: {
3356 fstring buf;
3357 size_t buf_len;
3359 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3360 if (buf_len == 0) {
3361 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3364 if (!sid_parse((const uint8_t *)buf, buf_len, sid)) {
3365 DEBUG(10,("failed to parse sid\n"));
3366 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3368 break;
3370 default:
3371 DEBUG(10,("unknown extended dn format\n"));
3372 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3375 return ADS_ERROR_NT(NT_STATUS_OK);
3378 /********************************************************************
3379 ********************************************************************/
3381 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3383 LDAPMessage *res = NULL;
3384 ADS_STATUS status;
3385 int count = 0;
3386 char *name = NULL;
3388 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3389 if (!ADS_ERR_OK(status)) {
3390 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3391 lp_netbios_name()));
3392 goto out;
3395 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3396 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3397 goto out;
3400 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3401 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3404 out:
3405 ads_msgfree(ads, res);
3407 return name;
3410 /********************************************************************
3411 ********************************************************************/
3413 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3415 LDAPMessage *res = NULL;
3416 ADS_STATUS status;
3417 int count = 0;
3418 char *name = NULL;
3420 status = ads_find_machine_acct(ads, &res, machine_name);
3421 if (!ADS_ERR_OK(status)) {
3422 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3423 lp_netbios_name()));
3424 goto out;
3427 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3428 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3429 goto out;
3432 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3433 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3436 out:
3437 ads_msgfree(ads, res);
3439 return name;
3442 /********************************************************************
3443 ********************************************************************/
3445 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3447 LDAPMessage *res = NULL;
3448 ADS_STATUS status;
3449 int count = 0;
3450 char *name = NULL;
3452 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3453 if (!ADS_ERR_OK(status)) {
3454 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3455 lp_netbios_name()));
3456 goto out;
3459 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3460 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3461 goto out;
3464 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3465 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3468 out:
3469 ads_msgfree(ads, res);
3471 return name;
3474 #if 0
3476 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3479 * Join a machine to a realm
3480 * Creates the machine account and sets the machine password
3481 * @param ads connection to ads server
3482 * @param machine name of host to add
3483 * @param org_unit Organizational unit to place machine in
3484 * @return status of join
3486 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3487 uint32_t account_type, const char *org_unit)
3489 ADS_STATUS status;
3490 LDAPMessage *res = NULL;
3491 char *machine;
3493 /* machine name must be lowercase */
3494 machine = SMB_STRDUP(machine_name);
3495 strlower_m(machine);
3498 status = ads_find_machine_acct(ads, (void **)&res, machine);
3499 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3500 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3501 status = ads_leave_realm(ads, machine);
3502 if (!ADS_ERR_OK(status)) {
3503 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3504 machine, ads->config.realm));
3505 return status;
3509 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3510 if (!ADS_ERR_OK(status)) {
3511 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3512 SAFE_FREE(machine);
3513 return status;
3516 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3517 if (!ADS_ERR_OK(status)) {
3518 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3519 SAFE_FREE(machine);
3520 return status;
3523 SAFE_FREE(machine);
3524 ads_msgfree(ads, res);
3526 return status;
3528 #endif
3531 * Delete a machine from the realm
3532 * @param ads connection to ads server
3533 * @param hostname Machine to remove
3534 * @return status of delete
3536 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3538 ADS_STATUS status;
3539 void *msg;
3540 LDAPMessage *res;
3541 char *hostnameDN, *host;
3542 int rc;
3543 LDAPControl ldap_control;
3544 LDAPControl * pldap_control[2] = {NULL, NULL};
3546 pldap_control[0] = &ldap_control;
3547 memset(&ldap_control, 0, sizeof(LDAPControl));
3548 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3550 /* hostname must be lowercase */
3551 host = SMB_STRDUP(hostname);
3552 if (!strlower_m(host)) {
3553 SAFE_FREE(host);
3554 return ADS_ERROR_SYSTEM(EINVAL);
3557 status = ads_find_machine_acct(ads, &res, host);
3558 if (!ADS_ERR_OK(status)) {
3559 DEBUG(0, ("Host account for %s does not exist.\n", host));
3560 SAFE_FREE(host);
3561 return status;
3564 msg = ads_first_entry(ads, res);
3565 if (!msg) {
3566 SAFE_FREE(host);
3567 return ADS_ERROR_SYSTEM(ENOENT);
3570 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3571 if (hostnameDN == NULL) {
3572 SAFE_FREE(host);
3573 return ADS_ERROR_SYSTEM(ENOENT);
3576 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3577 if (rc) {
3578 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3579 }else {
3580 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3583 if (rc != LDAP_SUCCESS) {
3584 const char *attrs[] = { "cn", NULL };
3585 LDAPMessage *msg_sub;
3587 /* we only search with scope ONE, we do not expect any further
3588 * objects to be created deeper */
3590 status = ads_do_search_retry(ads, hostnameDN,
3591 LDAP_SCOPE_ONELEVEL,
3592 "(objectclass=*)", attrs, &res);
3594 if (!ADS_ERR_OK(status)) {
3595 SAFE_FREE(host);
3596 TALLOC_FREE(hostnameDN);
3597 return status;
3600 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3601 msg_sub = ads_next_entry(ads, msg_sub)) {
3603 char *dn = NULL;
3605 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3606 SAFE_FREE(host);
3607 TALLOC_FREE(hostnameDN);
3608 return ADS_ERROR(LDAP_NO_MEMORY);
3611 status = ads_del_dn(ads, dn);
3612 if (!ADS_ERR_OK(status)) {
3613 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3614 SAFE_FREE(host);
3615 TALLOC_FREE(dn);
3616 TALLOC_FREE(hostnameDN);
3617 return status;
3620 TALLOC_FREE(dn);
3623 /* there should be no subordinate objects anymore */
3624 status = ads_do_search_retry(ads, hostnameDN,
3625 LDAP_SCOPE_ONELEVEL,
3626 "(objectclass=*)", attrs, &res);
3628 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3629 SAFE_FREE(host);
3630 TALLOC_FREE(hostnameDN);
3631 return status;
3634 /* delete hostnameDN now */
3635 status = ads_del_dn(ads, hostnameDN);
3636 if (!ADS_ERR_OK(status)) {
3637 SAFE_FREE(host);
3638 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3639 TALLOC_FREE(hostnameDN);
3640 return status;
3644 TALLOC_FREE(hostnameDN);
3646 status = ads_find_machine_acct(ads, &res, host);
3647 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3648 DEBUG(3, ("Failed to remove host account.\n"));
3649 SAFE_FREE(host);
3650 return status;
3653 SAFE_FREE(host);
3654 return status;
3658 * pull all token-sids from an LDAP dn
3659 * @param ads connection to ads server
3660 * @param mem_ctx TALLOC_CTX for allocating sid array
3661 * @param dn of LDAP object
3662 * @param user_sid pointer to struct dom_sid (objectSid)
3663 * @param primary_group_sid pointer to struct dom_sid (self composed)
3664 * @param sids pointer to sid array to allocate
3665 * @param num_sids counter of SIDs pulled
3666 * @return status of token query
3668 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3669 TALLOC_CTX *mem_ctx,
3670 const char *dn,
3671 struct dom_sid *user_sid,
3672 struct dom_sid *primary_group_sid,
3673 struct dom_sid **sids,
3674 size_t *num_sids)
3676 ADS_STATUS status;
3677 LDAPMessage *res = NULL;
3678 int count = 0;
3679 size_t tmp_num_sids;
3680 struct dom_sid *tmp_sids;
3681 struct dom_sid tmp_user_sid;
3682 struct dom_sid tmp_primary_group_sid;
3683 uint32_t pgid;
3684 const char *attrs[] = {
3685 "objectSid",
3686 "tokenGroups",
3687 "primaryGroupID",
3688 NULL
3691 status = ads_search_retry_dn(ads, &res, dn, attrs);
3692 if (!ADS_ERR_OK(status)) {
3693 return status;
3696 count = ads_count_replies(ads, res);
3697 if (count != 1) {
3698 ads_msgfree(ads, res);
3699 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3702 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3703 ads_msgfree(ads, res);
3704 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3707 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3708 ads_msgfree(ads, res);
3709 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3713 /* hack to compose the primary group sid without knowing the
3714 * domsid */
3716 struct dom_sid domsid;
3718 sid_copy(&domsid, &tmp_user_sid);
3720 if (!sid_split_rid(&domsid, NULL)) {
3721 ads_msgfree(ads, res);
3722 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3725 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3726 ads_msgfree(ads, res);
3727 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3731 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3733 if (tmp_num_sids == 0 || !tmp_sids) {
3734 ads_msgfree(ads, res);
3735 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3738 if (num_sids) {
3739 *num_sids = tmp_num_sids;
3742 if (sids) {
3743 *sids = tmp_sids;
3746 if (user_sid) {
3747 *user_sid = tmp_user_sid;
3750 if (primary_group_sid) {
3751 *primary_group_sid = tmp_primary_group_sid;
3754 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3756 ads_msgfree(ads, res);
3757 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3761 * Find a sAMAccoutName in LDAP
3762 * @param ads connection to ads server
3763 * @param mem_ctx TALLOC_CTX for allocating sid array
3764 * @param samaccountname to search
3765 * @param uac_ret uint32_t pointer userAccountControl attribute value
3766 * @param dn_ret pointer to dn
3767 * @return status of token query
3769 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3770 TALLOC_CTX *mem_ctx,
3771 const char *samaccountname,
3772 uint32_t *uac_ret,
3773 const char **dn_ret)
3775 ADS_STATUS status;
3776 const char *attrs[] = { "userAccountControl", NULL };
3777 const char *filter;
3778 LDAPMessage *res = NULL;
3779 char *dn = NULL;
3780 uint32_t uac = 0;
3782 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3783 samaccountname);
3784 if (filter == NULL) {
3785 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3786 goto out;
3789 status = ads_do_search_all(ads, ads->config.bind_path,
3790 LDAP_SCOPE_SUBTREE,
3791 filter, attrs, &res);
3793 if (!ADS_ERR_OK(status)) {
3794 goto out;
3797 if (ads_count_replies(ads, res) != 1) {
3798 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3799 goto out;
3802 dn = ads_get_dn(ads, talloc_tos(), res);
3803 if (dn == NULL) {
3804 status = ADS_ERROR(LDAP_NO_MEMORY);
3805 goto out;
3808 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3809 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3810 goto out;
3813 if (uac_ret) {
3814 *uac_ret = uac;
3817 if (dn_ret) {
3818 *dn_ret = talloc_strdup(mem_ctx, dn);
3819 if (!*dn_ret) {
3820 status = ADS_ERROR(LDAP_NO_MEMORY);
3821 goto out;
3824 out:
3825 TALLOC_FREE(dn);
3826 ads_msgfree(ads, res);
3828 return status;
3832 * find our configuration path
3833 * @param ads connection to ads server
3834 * @param mem_ctx Pointer to talloc context
3835 * @param config_path Pointer to the config path
3836 * @return status of search
3838 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3839 TALLOC_CTX *mem_ctx,
3840 char **config_path)
3842 ADS_STATUS status;
3843 LDAPMessage *res = NULL;
3844 const char *config_context = NULL;
3845 const char *attrs[] = { "configurationNamingContext", NULL };
3847 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3848 "(objectclass=*)", attrs, &res);
3849 if (!ADS_ERR_OK(status)) {
3850 return status;
3853 config_context = ads_pull_string(ads, mem_ctx, res,
3854 "configurationNamingContext");
3855 ads_msgfree(ads, res);
3856 if (!config_context) {
3857 return ADS_ERROR(LDAP_NO_MEMORY);
3860 if (config_path) {
3861 *config_path = talloc_strdup(mem_ctx, config_context);
3862 if (!*config_path) {
3863 return ADS_ERROR(LDAP_NO_MEMORY);
3867 return ADS_ERROR(LDAP_SUCCESS);
3871 * find the displayName of an extended right
3872 * @param ads connection to ads server
3873 * @param config_path The config path
3874 * @param mem_ctx Pointer to talloc context
3875 * @param GUID struct of the rightsGUID
3876 * @return status of search
3878 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3879 const char *config_path,
3880 TALLOC_CTX *mem_ctx,
3881 const struct GUID *rights_guid)
3883 ADS_STATUS rc;
3884 LDAPMessage *res = NULL;
3885 char *expr = NULL;
3886 const char *attrs[] = { "displayName", NULL };
3887 const char *result = NULL;
3888 const char *path;
3890 if (!ads || !mem_ctx || !rights_guid) {
3891 goto done;
3894 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3895 GUID_string(mem_ctx, rights_guid));
3896 if (!expr) {
3897 goto done;
3900 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3901 if (!path) {
3902 goto done;
3905 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3906 expr, attrs, &res);
3907 if (!ADS_ERR_OK(rc)) {
3908 goto done;
3911 if (ads_count_replies(ads, res) != 1) {
3912 goto done;
3915 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3917 done:
3918 ads_msgfree(ads, res);
3919 return result;
3923 * verify or build and verify an account ou
3924 * @param mem_ctx Pointer to talloc context
3925 * @param ads connection to ads server
3926 * @param account_ou
3927 * @return status of search
3930 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3931 ADS_STRUCT *ads,
3932 const char **account_ou)
3934 char **exploded_dn;
3935 const char *name;
3936 char *ou_string;
3938 if (account_ou == NULL) {
3939 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3942 if (*account_ou != NULL) {
3943 exploded_dn = ldap_explode_dn(*account_ou, 0);
3944 if (exploded_dn) {
3945 ldap_value_free(exploded_dn);
3946 return ADS_SUCCESS;
3950 ou_string = ads_ou_string(ads, *account_ou);
3951 if (!ou_string) {
3952 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3955 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3956 ads->config.bind_path);
3957 SAFE_FREE(ou_string);
3959 if (!name) {
3960 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3963 exploded_dn = ldap_explode_dn(name, 0);
3964 if (!exploded_dn) {
3965 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3967 ldap_value_free(exploded_dn);
3969 *account_ou = name;
3970 return ADS_SUCCESS;
3973 #endif