WHATSNEW: Update changes.
[Samba/gebeck_regimport.git] / source3 / libads / ldap.c
blob5adbd6d039b646137ca8f6925ffdd094541e0f02
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 "libads/sitename_cache.h"
26 #include "libads/cldap.h"
27 #include "libads/dns.h"
28 #include "../libds/common/flags.h"
30 #ifdef HAVE_LDAP
32 /**
33 * @file ldap.c
34 * @brief basic ldap client-side routines for ads server communications
36 * The routines contained here should do the necessary ldap calls for
37 * ads setups.
39 * Important note: attribute names passed into ads_ routines must
40 * already be in UTF-8 format. We do not convert them because in almost
41 * all cases, they are just ascii (which is represented with the same
42 * codepoints in UTF-8). This may have to change at some point
43 **/
46 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
48 static SIG_ATOMIC_T gotalarm;
50 /***************************************************************
51 Signal function to tell us we timed out.
52 ****************************************************************/
54 static void gotalarm_sig(int signum)
56 gotalarm = 1;
59 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
61 LDAP *ldp = NULL;
64 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
65 "%u seconds\n", server, port, to));
67 /* Setup timeout */
68 gotalarm = 0;
69 CatchSignal(SIGALRM, gotalarm_sig);
70 alarm(to);
71 /* End setup timeout. */
73 ldp = ldap_open(server, port);
75 if (ldp == NULL) {
76 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
77 server, port, strerror(errno)));
78 } else {
79 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
82 /* Teardown timeout. */
83 CatchSignal(SIGALRM, SIG_IGN);
84 alarm(0);
86 return ldp;
89 static int ldap_search_with_timeout(LDAP *ld,
90 LDAP_CONST char *base,
91 int scope,
92 LDAP_CONST char *filter,
93 char **attrs,
94 int attrsonly,
95 LDAPControl **sctrls,
96 LDAPControl **cctrls,
97 int sizelimit,
98 LDAPMessage **res )
100 struct timeval timeout;
101 int result;
103 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
104 timeout.tv_sec = lp_ldap_timeout();
105 timeout.tv_usec = 0;
107 /* Setup alarm timeout.... Do we need both of these ? JRA. */
108 gotalarm = 0;
109 CatchSignal(SIGALRM, gotalarm_sig);
110 alarm(lp_ldap_timeout());
111 /* End setup timeout. */
113 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
114 attrsonly, sctrls, cctrls, &timeout,
115 sizelimit, res);
117 /* Teardown timeout. */
118 CatchSignal(SIGALRM, SIG_IGN);
119 alarm(0);
121 if (gotalarm != 0)
122 return LDAP_TIMELIMIT_EXCEEDED;
125 * A bug in OpenLDAP means ldap_search_ext_s can return
126 * LDAP_SUCCESS but with a NULL res pointer. Cope with
127 * this. See bug #6279 for details. JRA.
130 if (*res == NULL) {
131 return LDAP_TIMELIMIT_EXCEEDED;
134 return result;
137 /**********************************************
138 Do client and server sitename match ?
139 **********************************************/
141 bool ads_sitename_match(ADS_STRUCT *ads)
143 if (ads->config.server_site_name == NULL &&
144 ads->config.client_site_name == NULL ) {
145 DEBUG(10,("ads_sitename_match: both null\n"));
146 return True;
148 if (ads->config.server_site_name &&
149 ads->config.client_site_name &&
150 strequal(ads->config.server_site_name,
151 ads->config.client_site_name)) {
152 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
153 return True;
155 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
156 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
157 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
158 return False;
161 /**********************************************
162 Is this the closest DC ?
163 **********************************************/
165 bool ads_closest_dc(ADS_STRUCT *ads)
167 if (ads->config.flags & NBT_SERVER_CLOSEST) {
168 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
169 return True;
172 /* not sure if this can ever happen */
173 if (ads_sitename_match(ads)) {
174 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
175 return True;
178 if (ads->config.client_site_name == NULL) {
179 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
180 return True;
183 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
184 ads->config.ldap_server_name));
186 return False;
191 try a connection to a given ldap server, returning True and setting the servers IP
192 in the ads struct if successful
194 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
196 char *srv;
197 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
198 TALLOC_CTX *frame = talloc_stackframe();
199 bool ret = false;
201 if (!server || !*server) {
202 TALLOC_FREE(frame);
203 return False;
206 if (!is_ipaddress(server)) {
207 struct sockaddr_storage ss;
208 char addr[INET6_ADDRSTRLEN];
210 if (!resolve_name(server, &ss, 0x20, true)) {
211 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
212 server ));
213 TALLOC_FREE(frame);
214 return false;
216 print_sockaddr(addr, sizeof(addr), &ss);
217 srv = talloc_strdup(frame, addr);
218 } else {
219 /* this copes with inet_ntoa brokenness */
220 srv = talloc_strdup(frame, server);
223 if (!srv) {
224 TALLOC_FREE(frame);
225 return false;
228 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
229 srv, ads->server.realm));
231 ZERO_STRUCT( cldap_reply );
233 if ( !ads_cldap_netlogon_5(frame, srv, ads->server.realm, &cldap_reply ) ) {
234 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
235 ret = false;
236 goto out;
239 /* Check the CLDAP reply flags */
241 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
242 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
243 srv));
244 ret = false;
245 goto out;
248 /* Fill in the ads->config values */
250 SAFE_FREE(ads->config.realm);
251 SAFE_FREE(ads->config.bind_path);
252 SAFE_FREE(ads->config.ldap_server_name);
253 SAFE_FREE(ads->config.server_site_name);
254 SAFE_FREE(ads->config.client_site_name);
255 SAFE_FREE(ads->server.workgroup);
257 ads->config.flags = cldap_reply.server_type;
258 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
259 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
260 strupper_m(ads->config.realm);
261 ads->config.bind_path = ads_build_dn(ads->config.realm);
262 if (*cldap_reply.server_site) {
263 ads->config.server_site_name =
264 SMB_STRDUP(cldap_reply.server_site);
266 if (*cldap_reply.client_site) {
267 ads->config.client_site_name =
268 SMB_STRDUP(cldap_reply.client_site);
270 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
272 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
273 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
274 DEBUG(1,("ads_try_connect: unable to convert %s "
275 "to an address\n",
276 srv));
277 ret = false;
278 goto out;
281 /* Store our site name. */
282 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
283 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
285 ret = true;
287 out:
289 TALLOC_FREE(frame);
290 return ret;
293 /**********************************************************************
294 Try to find an AD dc using our internal name resolution routines
295 Try the realm first and then then workgroup name if netbios is not
296 disabled
297 **********************************************************************/
299 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
301 const char *c_domain;
302 const char *c_realm;
303 int count, i=0;
304 struct ip_service *ip_list;
305 const char *realm;
306 const char *domain;
307 bool got_realm = False;
308 bool use_own_domain = False;
309 char *sitename;
310 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
312 /* if the realm and workgroup are both empty, assume they are ours */
314 /* realm */
315 c_realm = ads->server.realm;
317 if ( !c_realm || !*c_realm ) {
318 /* special case where no realm and no workgroup means our own */
319 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
320 use_own_domain = True;
321 c_realm = lp_realm();
325 if (c_realm && *c_realm)
326 got_realm = True;
328 /* we need to try once with the realm name and fallback to the
329 netbios domain name if we fail (if netbios has not been disabled */
331 if ( !got_realm && !lp_disable_netbios() ) {
332 c_realm = ads->server.workgroup;
333 if (!c_realm || !*c_realm) {
334 if ( use_own_domain )
335 c_realm = lp_workgroup();
339 if ( !c_realm || !*c_realm ) {
340 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
341 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
344 if ( use_own_domain ) {
345 c_domain = lp_workgroup();
346 } else {
347 c_domain = ads->server.workgroup;
350 realm = c_realm;
351 domain = c_domain;
354 * In case of LDAP we use get_dc_name() as that
355 * creates the custom krb5.conf file
357 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
358 fstring srv_name;
359 struct sockaddr_storage ip_out;
361 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
362 (got_realm ? "realm" : "domain"), realm));
364 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
366 * we call ads_try_connect() to fill in the
367 * ads->config details
369 if (ads_try_connect(ads, srv_name, false)) {
370 return NT_STATUS_OK;
374 return NT_STATUS_NO_LOGON_SERVERS;
377 sitename = sitename_fetch(realm);
379 again:
381 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
382 (got_realm ? "realm" : "domain"), realm));
384 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
385 if (!NT_STATUS_IS_OK(status)) {
386 /* fall back to netbios if we can */
387 if ( got_realm && !lp_disable_netbios() ) {
388 got_realm = False;
389 goto again;
392 SAFE_FREE(sitename);
393 return status;
396 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
397 for ( i=0; i<count; i++ ) {
398 char server[INET6_ADDRSTRLEN];
400 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
402 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
403 continue;
405 if (!got_realm) {
406 /* realm in this case is a workgroup name. We need
407 to ignore any IP addresses in the negative connection
408 cache that match ip addresses returned in the ad realm
409 case. It sucks that I have to reproduce the logic above... */
410 c_realm = ads->server.realm;
411 if ( !c_realm || !*c_realm ) {
412 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
413 c_realm = lp_realm();
416 if (c_realm && *c_realm &&
417 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
418 /* Ensure we add the workgroup name for this
419 IP address as negative too. */
420 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
421 continue;
425 if ( ads_try_connect(ads, server, false) ) {
426 SAFE_FREE(ip_list);
427 SAFE_FREE(sitename);
428 return NT_STATUS_OK;
431 /* keep track of failures */
432 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
435 SAFE_FREE(ip_list);
437 /* In case we failed to contact one of our closest DC on our site we
438 * need to try to find another DC, retry with a site-less SRV DNS query
439 * - Guenther */
441 if (sitename) {
442 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
443 "trying to find another DC\n", sitename));
444 SAFE_FREE(sitename);
445 namecache_delete(realm, 0x1C);
446 goto again;
449 return NT_STATUS_NO_LOGON_SERVERS;
452 /*********************************************************************
453 *********************************************************************/
455 static NTSTATUS ads_lookup_site(void)
457 ADS_STRUCT *ads = NULL;
458 ADS_STATUS ads_status;
459 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
461 ads = ads_init(lp_realm(), NULL, NULL);
462 if (!ads) {
463 return NT_STATUS_NO_MEMORY;
466 /* The NO_BIND here will find a DC and set the client site
467 but not establish the TCP connection */
469 ads->auth.flags = ADS_AUTH_NO_BIND;
470 ads_status = ads_connect(ads);
471 if (!ADS_ERR_OK(ads_status)) {
472 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
473 ads_errstr(ads_status)));
475 nt_status = ads_ntstatus(ads_status);
477 if (ads) {
478 ads_destroy(&ads);
481 return nt_status;
484 /*********************************************************************
485 *********************************************************************/
487 static const char* host_dns_domain(const char *fqdn)
489 const char *p = fqdn;
491 /* go to next char following '.' */
493 if ((p = strchr_m(fqdn, '.')) != NULL) {
494 p++;
497 return p;
502 * Connect to the Global Catalog server
503 * @param ads Pointer to an existing ADS_STRUCT
504 * @return status of connection
506 * Simple wrapper around ads_connect() that fills in the
507 * GC ldap server information
510 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
512 TALLOC_CTX *frame = talloc_stackframe();
513 struct dns_rr_srv *gcs_list;
514 int num_gcs;
515 char *realm = ads->server.realm;
516 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
517 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
518 int i;
519 bool done = false;
520 char *sitename = NULL;
522 if (!realm)
523 realm = lp_realm();
525 if ((sitename = sitename_fetch(realm)) == NULL) {
526 ads_lookup_site();
527 sitename = sitename_fetch(realm);
530 do {
531 /* We try once with a sitename and once without
532 (unless we don't have a sitename and then we're
533 done */
535 if (sitename == NULL)
536 done = true;
538 nt_status = ads_dns_query_gcs(frame, realm, sitename,
539 &gcs_list, &num_gcs);
541 SAFE_FREE(sitename);
543 if (!NT_STATUS_IS_OK(nt_status)) {
544 ads_status = ADS_ERROR_NT(nt_status);
545 goto done;
548 /* Loop until we get a successful connection or have gone
549 through them all. When connecting a GC server, make sure that
550 the realm is the server's DNS name and not the forest root */
552 for (i=0; i<num_gcs; i++) {
553 ads->server.gc = true;
554 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
555 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
556 ads_status = ads_connect(ads);
557 if (ADS_ERR_OK(ads_status)) {
558 /* Reset the bind_dn to "". A Global Catalog server
559 may host multiple domain trees in a forest.
560 Windows 2003 GC server will accept "" as the search
561 path to imply search all domain trees in the forest */
563 SAFE_FREE(ads->config.bind_path);
564 ads->config.bind_path = SMB_STRDUP("");
567 goto done;
569 SAFE_FREE(ads->server.ldap_server);
570 SAFE_FREE(ads->server.realm);
573 TALLOC_FREE(gcs_list);
574 num_gcs = 0;
575 } while (!done);
577 done:
578 SAFE_FREE(sitename);
579 talloc_destroy(frame);
581 return ads_status;
586 * Connect to the LDAP server
587 * @param ads Pointer to an existing ADS_STRUCT
588 * @return status of connection
590 ADS_STATUS ads_connect(ADS_STRUCT *ads)
592 int version = LDAP_VERSION3;
593 ADS_STATUS status;
594 NTSTATUS ntstatus;
595 char addr[INET6_ADDRSTRLEN];
597 ZERO_STRUCT(ads->ldap);
598 ads->ldap.last_attempt = time(NULL);
599 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
601 /* try with a user specified server */
603 if (DEBUGLEVEL >= 11) {
604 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
605 DEBUG(11,("ads_connect: entering\n"));
606 DEBUGADD(11,("%s\n", s));
607 TALLOC_FREE(s);
610 if (ads->server.ldap_server)
612 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
613 goto got_connection;
616 /* The choice of which GC use is handled one level up in
617 ads_connect_gc(). If we continue on from here with
618 ads_find_dc() we will get GC searches on port 389 which
619 doesn't work. --jerry */
621 if (ads->server.gc == true) {
622 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
626 ntstatus = ads_find_dc(ads);
627 if (NT_STATUS_IS_OK(ntstatus)) {
628 goto got_connection;
631 status = ADS_ERROR_NT(ntstatus);
632 goto out;
634 got_connection:
636 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
637 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
639 if (!ads->auth.user_name) {
640 /* Must use the userPrincipalName value here or sAMAccountName
641 and not servicePrincipalName; found by Guenther Deschner */
643 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
644 DEBUG(0,("ads_connect: asprintf fail.\n"));
645 ads->auth.user_name = NULL;
649 if (!ads->auth.realm) {
650 ads->auth.realm = SMB_STRDUP(ads->config.realm);
653 if (!ads->auth.kdc_server) {
654 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
655 ads->auth.kdc_server = SMB_STRDUP(addr);
658 #if KRB5_DNS_HACK
659 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
660 to MIT kerberos to work (tridge) */
662 char *env = NULL;
663 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
664 setenv(env, ads->auth.kdc_server, 1);
665 free(env);
668 #endif
670 /* If the caller() requested no LDAP bind, then we are done */
672 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
673 status = ADS_SUCCESS;
674 goto out;
677 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
678 if (!ads->ldap.mem_ctx) {
679 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
680 goto out;
683 /* Otherwise setup the TCP LDAP session */
685 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
686 ads->ldap.port, lp_ldap_timeout());
687 if (ads->ldap.ld == NULL) {
688 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
689 goto out;
691 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
693 /* cache the successful connection for workgroup and realm */
694 if (ads_closest_dc(ads)) {
695 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
696 saf_store( ads->server.realm, ads->config.ldap_server_name);
699 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
701 if ( lp_ldap_ssl_ads() ) {
702 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
703 if (!ADS_ERR_OK(status)) {
704 goto out;
708 /* fill in the current time and offsets */
710 status = ads_current_time( ads );
711 if ( !ADS_ERR_OK(status) ) {
712 goto out;
715 /* Now do the bind */
717 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
718 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
719 goto out;
722 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
723 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
724 goto out;
727 status = ads_sasl_bind(ads);
729 out:
730 if (DEBUGLEVEL >= 11) {
731 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
732 DEBUG(11,("ads_connect: leaving with: %s\n",
733 ads_errstr(status)));
734 DEBUGADD(11,("%s\n", s));
735 TALLOC_FREE(s);
738 return status;
742 * Connect to the LDAP server using given credentials
743 * @param ads Pointer to an existing ADS_STRUCT
744 * @return status of connection
746 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
748 ads->auth.flags |= ADS_AUTH_USER_CREDS;
750 return ads_connect(ads);
754 * Disconnect the LDAP server
755 * @param ads Pointer to an existing ADS_STRUCT
757 void ads_disconnect(ADS_STRUCT *ads)
759 if (ads->ldap.ld) {
760 ldap_unbind(ads->ldap.ld);
761 ads->ldap.ld = NULL;
763 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
764 ads->ldap.wrap_ops->disconnect(ads);
766 if (ads->ldap.mem_ctx) {
767 talloc_free(ads->ldap.mem_ctx);
769 ZERO_STRUCT(ads->ldap);
773 Duplicate a struct berval into talloc'ed memory
775 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
777 struct berval *value;
779 if (!in_val) return NULL;
781 value = TALLOC_ZERO_P(ctx, struct berval);
782 if (value == NULL)
783 return NULL;
784 if (in_val->bv_len == 0) return value;
786 value->bv_len = in_val->bv_len;
787 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
788 in_val->bv_len);
789 return value;
793 Make a values list out of an array of (struct berval *)
795 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
796 const struct berval **in_vals)
798 struct berval **values;
799 int i;
801 if (!in_vals) return NULL;
802 for (i=0; in_vals[i]; i++)
803 ; /* count values */
804 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
805 if (!values) return NULL;
807 for (i=0; in_vals[i]; i++) {
808 values[i] = dup_berval(ctx, in_vals[i]);
810 return values;
814 UTF8-encode a values list out of an array of (char *)
816 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
818 char **values;
819 int i;
820 size_t size;
822 if (!in_vals) return NULL;
823 for (i=0; in_vals[i]; i++)
824 ; /* count values */
825 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
826 if (!values) return NULL;
828 for (i=0; in_vals[i]; i++) {
829 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
830 TALLOC_FREE(values);
831 return NULL;
834 return values;
838 Pull a (char *) array out of a UTF8-encoded values list
840 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
842 char **values;
843 int i;
844 size_t converted_size;
846 if (!in_vals) return NULL;
847 for (i=0; in_vals[i]; i++)
848 ; /* count values */
849 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
850 if (!values) return NULL;
852 for (i=0; in_vals[i]; i++) {
853 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
854 &converted_size)) {
855 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
856 "%s", strerror(errno)));
859 return values;
863 * Do a search with paged results. cookie must be null on the first
864 * call, and then returned on each subsequent call. It will be null
865 * again when the entire search is complete
866 * @param ads connection to ads server
867 * @param bind_path Base dn for the search
868 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
869 * @param expr Search expression - specified in local charset
870 * @param attrs Attributes to retrieve - specified in utf8 or ascii
871 * @param res ** which will contain results - free res* with ads_msgfree()
872 * @param count Number of entries retrieved on this page
873 * @param cookie The paged results cookie to be returned on subsequent calls
874 * @return status of search
876 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
877 const char *bind_path,
878 int scope, const char *expr,
879 const char **attrs, void *args,
880 LDAPMessage **res,
881 int *count, struct berval **cookie)
883 int rc, i, version;
884 char *utf8_expr, *utf8_path, **search_attrs = NULL;
885 size_t converted_size;
886 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
887 BerElement *cookie_be = NULL;
888 struct berval *cookie_bv= NULL;
889 BerElement *ext_be = NULL;
890 struct berval *ext_bv= NULL;
892 TALLOC_CTX *ctx;
893 ads_control *external_control = (ads_control *) args;
895 *res = NULL;
897 if (!(ctx = talloc_init("ads_do_paged_search_args")))
898 return ADS_ERROR(LDAP_NO_MEMORY);
900 /* 0 means the conversion worked but the result was empty
901 so we only fail if it's -1. In any case, it always
902 at least nulls out the dest */
903 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
904 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
906 rc = LDAP_NO_MEMORY;
907 goto done;
910 if (!attrs || !(*attrs))
911 search_attrs = NULL;
912 else {
913 /* This would be the utf8-encoded version...*/
914 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
915 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
916 rc = LDAP_NO_MEMORY;
917 goto done;
921 /* Paged results only available on ldap v3 or later */
922 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
923 if (version < LDAP_VERSION3) {
924 rc = LDAP_NOT_SUPPORTED;
925 goto done;
928 cookie_be = ber_alloc_t(LBER_USE_DER);
929 if (*cookie) {
930 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
931 ber_bvfree(*cookie); /* don't need it from last time */
932 *cookie = NULL;
933 } else {
934 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
936 ber_flatten(cookie_be, &cookie_bv);
937 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
938 PagedResults.ldctl_iscritical = (char) 1;
939 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
940 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
942 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
943 NoReferrals.ldctl_iscritical = (char) 0;
944 NoReferrals.ldctl_value.bv_len = 0;
945 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
947 if (external_control &&
948 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
949 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
951 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
952 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
954 /* win2k does not accept a ldctl_value beeing passed in */
956 if (external_control->val != 0) {
958 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
959 rc = LDAP_NO_MEMORY;
960 goto done;
963 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
964 rc = LDAP_NO_MEMORY;
965 goto done;
967 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
968 rc = LDAP_NO_MEMORY;
969 goto done;
972 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
973 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
975 } else {
976 ExternalCtrl.ldctl_value.bv_len = 0;
977 ExternalCtrl.ldctl_value.bv_val = NULL;
980 controls[0] = &NoReferrals;
981 controls[1] = &PagedResults;
982 controls[2] = &ExternalCtrl;
983 controls[3] = NULL;
985 } else {
986 controls[0] = &NoReferrals;
987 controls[1] = &PagedResults;
988 controls[2] = NULL;
991 /* we need to disable referrals as the openldap libs don't
992 handle them and paged results at the same time. Using them
993 together results in the result record containing the server
994 page control being removed from the result list (tridge/jmcd)
996 leaving this in despite the control that says don't generate
997 referrals, in case the server doesn't support it (jmcd)
999 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1001 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1002 search_attrs, 0, controls,
1003 NULL, LDAP_NO_LIMIT,
1004 (LDAPMessage **)res);
1006 ber_free(cookie_be, 1);
1007 ber_bvfree(cookie_bv);
1009 if (rc) {
1010 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1011 ldap_err2string(rc)));
1012 goto done;
1015 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1016 NULL, &rcontrols, 0);
1018 if (!rcontrols) {
1019 goto done;
1022 for (i=0; rcontrols[i]; i++) {
1023 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1024 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1025 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1026 &cookie_bv);
1027 /* the berval is the cookie, but must be freed when
1028 it is all done */
1029 if (cookie_bv->bv_len) /* still more to do */
1030 *cookie=ber_bvdup(cookie_bv);
1031 else
1032 *cookie=NULL;
1033 ber_bvfree(cookie_bv);
1034 ber_free(cookie_be, 1);
1035 break;
1038 ldap_controls_free(rcontrols);
1040 done:
1041 talloc_destroy(ctx);
1043 if (ext_be) {
1044 ber_free(ext_be, 1);
1047 if (ext_bv) {
1048 ber_bvfree(ext_bv);
1051 /* if/when we decide to utf8-encode attrs, take out this next line */
1052 TALLOC_FREE(search_attrs);
1054 return ADS_ERROR(rc);
1057 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1058 int scope, const char *expr,
1059 const char **attrs, LDAPMessage **res,
1060 int *count, struct berval **cookie)
1062 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1067 * Get all results for a search. This uses ads_do_paged_search() to return
1068 * all entries in a large search.
1069 * @param ads connection to ads server
1070 * @param bind_path Base dn for the search
1071 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1072 * @param expr Search expression
1073 * @param attrs Attributes to retrieve
1074 * @param res ** which will contain results - free res* with ads_msgfree()
1075 * @return status of search
1077 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1078 int scope, const char *expr,
1079 const char **attrs, void *args,
1080 LDAPMessage **res)
1082 struct berval *cookie = NULL;
1083 int count = 0;
1084 ADS_STATUS status;
1086 *res = NULL;
1087 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1088 &count, &cookie);
1090 if (!ADS_ERR_OK(status))
1091 return status;
1093 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1094 while (cookie) {
1095 LDAPMessage *res2 = NULL;
1096 ADS_STATUS status2;
1097 LDAPMessage *msg, *next;
1099 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1100 attrs, args, &res2, &count, &cookie);
1102 if (!ADS_ERR_OK(status2)) break;
1104 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1105 that this works on all ldap libs, but I have only tested with openldap */
1106 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1107 next = ads_next_message(ads, msg);
1108 ldap_add_result_entry((LDAPMessage **)res, msg);
1110 /* note that we do not free res2, as the memory is now
1111 part of the main returned list */
1113 #else
1114 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1115 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1116 #endif
1118 return status;
1121 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1122 int scope, const char *expr,
1123 const char **attrs, LDAPMessage **res)
1125 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1128 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1129 int scope, const char *expr,
1130 const char **attrs, uint32 sd_flags,
1131 LDAPMessage **res)
1133 ads_control args;
1135 args.control = ADS_SD_FLAGS_OID;
1136 args.val = sd_flags;
1137 args.critical = True;
1139 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1144 * Run a function on all results for a search. Uses ads_do_paged_search() and
1145 * runs the function as each page is returned, using ads_process_results()
1146 * @param ads connection to ads server
1147 * @param bind_path Base dn for the search
1148 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1149 * @param expr Search expression - specified in local charset
1150 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1151 * @param fn Function which takes attr name, values list, and data_area
1152 * @param data_area Pointer which is passed to function on each call
1153 * @return status of search
1155 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1156 int scope, const char *expr, const char **attrs,
1157 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1158 void *data_area)
1160 struct berval *cookie = NULL;
1161 int count = 0;
1162 ADS_STATUS status;
1163 LDAPMessage *res;
1165 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1166 &count, &cookie);
1168 if (!ADS_ERR_OK(status)) return status;
1170 ads_process_results(ads, res, fn, data_area);
1171 ads_msgfree(ads, res);
1173 while (cookie) {
1174 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1175 &res, &count, &cookie);
1177 if (!ADS_ERR_OK(status)) break;
1179 ads_process_results(ads, res, fn, data_area);
1180 ads_msgfree(ads, res);
1183 return status;
1187 * Do a search with a timeout.
1188 * @param ads connection to ads server
1189 * @param bind_path Base dn for the search
1190 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1191 * @param expr Search expression
1192 * @param attrs Attributes to retrieve
1193 * @param res ** which will contain results - free res* with ads_msgfree()
1194 * @return status of search
1196 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1197 const char *expr,
1198 const char **attrs, LDAPMessage **res)
1200 int rc;
1201 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1202 size_t converted_size;
1203 TALLOC_CTX *ctx;
1205 *res = NULL;
1206 if (!(ctx = talloc_init("ads_do_search"))) {
1207 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1208 return ADS_ERROR(LDAP_NO_MEMORY);
1211 /* 0 means the conversion worked but the result was empty
1212 so we only fail if it's negative. In any case, it always
1213 at least nulls out the dest */
1214 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1215 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1217 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1218 rc = LDAP_NO_MEMORY;
1219 goto done;
1222 if (!attrs || !(*attrs))
1223 search_attrs = NULL;
1224 else {
1225 /* This would be the utf8-encoded version...*/
1226 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1227 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1229 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1230 rc = LDAP_NO_MEMORY;
1231 goto done;
1235 /* see the note in ads_do_paged_search - we *must* disable referrals */
1236 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1238 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1239 search_attrs, 0, NULL, NULL,
1240 LDAP_NO_LIMIT,
1241 (LDAPMessage **)res);
1243 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1244 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1245 rc = 0;
1248 done:
1249 talloc_destroy(ctx);
1250 /* if/when we decide to utf8-encode attrs, take out this next line */
1251 TALLOC_FREE(search_attrs);
1252 return ADS_ERROR(rc);
1255 * Do a general ADS search
1256 * @param ads connection to ads server
1257 * @param res ** which will contain results - free res* with ads_msgfree()
1258 * @param expr Search expression
1259 * @param attrs Attributes to retrieve
1260 * @return status of search
1262 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1263 const char *expr, const char **attrs)
1265 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1266 expr, attrs, res);
1270 * Do a search on a specific DistinguishedName
1271 * @param ads connection to ads server
1272 * @param res ** which will contain results - free res* with ads_msgfree()
1273 * @param dn DistinguishName to search
1274 * @param attrs Attributes to retrieve
1275 * @return status of search
1277 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1278 const char *dn, const char **attrs)
1280 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1281 attrs, res);
1285 * Free up memory from a ads_search
1286 * @param ads connection to ads server
1287 * @param msg Search results to free
1289 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1291 if (!msg) return;
1292 ldap_msgfree(msg);
1296 * Get a dn from search results
1297 * @param ads connection to ads server
1298 * @param msg Search result
1299 * @return dn string
1301 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1303 char *utf8_dn, *unix_dn;
1304 size_t converted_size;
1306 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1308 if (!utf8_dn) {
1309 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1310 return NULL;
1313 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1314 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1315 utf8_dn ));
1316 return NULL;
1318 ldap_memfree(utf8_dn);
1319 return unix_dn;
1323 * Get the parent from a dn
1324 * @param dn the dn to return the parent from
1325 * @return parent dn string
1327 char *ads_parent_dn(const char *dn)
1329 char *p;
1331 if (dn == NULL) {
1332 return NULL;
1335 p = strchr(dn, ',');
1337 if (p == NULL) {
1338 return NULL;
1341 return p+1;
1345 * Find a machine account given a hostname
1346 * @param ads connection to ads server
1347 * @param res ** which will contain results - free res* with ads_msgfree()
1348 * @param host Hostname to search for
1349 * @return status of search
1351 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1352 const char *machine)
1354 ADS_STATUS status;
1355 char *expr;
1356 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1358 *res = NULL;
1360 /* the easiest way to find a machine account anywhere in the tree
1361 is to look for hostname$ */
1362 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1363 DEBUG(1, ("asprintf failed!\n"));
1364 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1367 status = ads_search(ads, res, expr, attrs);
1368 SAFE_FREE(expr);
1369 return status;
1373 * Initialize a list of mods to be used in a modify request
1374 * @param ctx An initialized TALLOC_CTX
1375 * @return allocated ADS_MODLIST
1377 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1379 #define ADS_MODLIST_ALLOC_SIZE 10
1380 LDAPMod **mods;
1382 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1383 /* -1 is safety to make sure we don't go over the end.
1384 need to reset it to NULL before doing ldap modify */
1385 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1387 return (ADS_MODLIST)mods;
1392 add an attribute to the list, with values list already constructed
1394 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1395 int mod_op, const char *name,
1396 const void *_invals)
1398 const void **invals = (const void **)_invals;
1399 int curmod;
1400 LDAPMod **modlist = (LDAPMod **) *mods;
1401 struct berval **ber_values = NULL;
1402 char **char_values = NULL;
1404 if (!invals) {
1405 mod_op = LDAP_MOD_DELETE;
1406 } else {
1407 if (mod_op & LDAP_MOD_BVALUES)
1408 ber_values = ads_dup_values(ctx,
1409 (const struct berval **)invals);
1410 else
1411 char_values = ads_push_strvals(ctx,
1412 (const char **) invals);
1415 /* find the first empty slot */
1416 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1417 curmod++);
1418 if (modlist[curmod] == (LDAPMod *) -1) {
1419 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1420 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1421 return ADS_ERROR(LDAP_NO_MEMORY);
1422 memset(&modlist[curmod], 0,
1423 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1424 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1425 *mods = (ADS_MODLIST)modlist;
1428 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1429 return ADS_ERROR(LDAP_NO_MEMORY);
1430 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1431 if (mod_op & LDAP_MOD_BVALUES) {
1432 modlist[curmod]->mod_bvalues = ber_values;
1433 } else if (mod_op & LDAP_MOD_DELETE) {
1434 modlist[curmod]->mod_values = NULL;
1435 } else {
1436 modlist[curmod]->mod_values = char_values;
1439 modlist[curmod]->mod_op = mod_op;
1440 return ADS_ERROR(LDAP_SUCCESS);
1444 * Add a single string value to a mod list
1445 * @param ctx An initialized TALLOC_CTX
1446 * @param mods An initialized ADS_MODLIST
1447 * @param name The attribute name to add
1448 * @param val The value to add - NULL means DELETE
1449 * @return ADS STATUS indicating success of add
1451 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1452 const char *name, const char *val)
1454 const char *values[2];
1456 values[0] = val;
1457 values[1] = NULL;
1459 if (!val)
1460 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1461 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1465 * Add an array of string values to a mod list
1466 * @param ctx An initialized TALLOC_CTX
1467 * @param mods An initialized ADS_MODLIST
1468 * @param name The attribute name to add
1469 * @param vals The array of string values to add - NULL means DELETE
1470 * @return ADS STATUS indicating success of add
1472 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1473 const char *name, const char **vals)
1475 if (!vals)
1476 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1477 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1478 name, (const void **) vals);
1481 #if 0
1483 * Add a single ber-encoded value to a mod list
1484 * @param ctx An initialized TALLOC_CTX
1485 * @param mods An initialized ADS_MODLIST
1486 * @param name The attribute name to add
1487 * @param val The value to add - NULL means DELETE
1488 * @return ADS STATUS indicating success of add
1490 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1491 const char *name, const struct berval *val)
1493 const struct berval *values[2];
1495 values[0] = val;
1496 values[1] = NULL;
1497 if (!val)
1498 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1499 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1500 name, (const void **) values);
1502 #endif
1505 * Perform an ldap modify
1506 * @param ads connection to ads server
1507 * @param mod_dn DistinguishedName to modify
1508 * @param mods list of modifications to perform
1509 * @return status of modify
1511 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1513 int ret,i;
1514 char *utf8_dn = NULL;
1515 size_t converted_size;
1517 this control is needed to modify that contains a currently
1518 non-existent attribute (but allowable for the object) to run
1520 LDAPControl PermitModify = {
1521 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1522 {0, NULL},
1523 (char) 1};
1524 LDAPControl *controls[2];
1526 controls[0] = &PermitModify;
1527 controls[1] = NULL;
1529 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1530 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1533 /* find the end of the list, marked by NULL or -1 */
1534 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1535 /* make sure the end of the list is NULL */
1536 mods[i] = NULL;
1537 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1538 (LDAPMod **) mods, controls, NULL);
1539 TALLOC_FREE(utf8_dn);
1540 return ADS_ERROR(ret);
1544 * Perform an ldap add
1545 * @param ads connection to ads server
1546 * @param new_dn DistinguishedName to add
1547 * @param mods list of attributes and values for DN
1548 * @return status of add
1550 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1552 int ret, i;
1553 char *utf8_dn = NULL;
1554 size_t converted_size;
1556 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1557 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1558 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1561 /* find the end of the list, marked by NULL or -1 */
1562 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1563 /* make sure the end of the list is NULL */
1564 mods[i] = NULL;
1566 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1567 TALLOC_FREE(utf8_dn);
1568 return ADS_ERROR(ret);
1572 * Delete a DistinguishedName
1573 * @param ads connection to ads server
1574 * @param new_dn DistinguishedName to delete
1575 * @return status of delete
1577 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1579 int ret;
1580 char *utf8_dn = NULL;
1581 size_t converted_size;
1582 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1583 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1584 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1587 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1588 TALLOC_FREE(utf8_dn);
1589 return ADS_ERROR(ret);
1593 * Build an org unit string
1594 * if org unit is Computers or blank then assume a container, otherwise
1595 * assume a / separated list of organisational units.
1596 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1597 * @param ads connection to ads server
1598 * @param org_unit Organizational unit
1599 * @return org unit string - caller must free
1601 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1603 char *ret = NULL;
1605 if (!org_unit || !*org_unit) {
1607 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1609 /* samba4 might not yet respond to a wellknownobject-query */
1610 return ret ? ret : SMB_STRDUP("cn=Computers");
1613 if (strequal(org_unit, "Computers")) {
1614 return SMB_STRDUP("cn=Computers");
1617 /* jmcd: removed "\\" from the separation chars, because it is
1618 needed as an escape for chars like '#' which are valid in an
1619 OU name */
1620 return ads_build_path(org_unit, "/", "ou=", 1);
1624 * Get a org unit string for a well-known GUID
1625 * @param ads connection to ads server
1626 * @param wknguid Well known GUID
1627 * @return org unit string - caller must free
1629 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1631 ADS_STATUS status;
1632 LDAPMessage *res = NULL;
1633 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1634 **bind_dn_exp = NULL;
1635 const char *attrs[] = {"distinguishedName", NULL};
1636 int new_ln, wkn_ln, bind_ln, i;
1638 if (wknguid == NULL) {
1639 return NULL;
1642 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1643 DEBUG(1, ("asprintf failed!\n"));
1644 return NULL;
1647 status = ads_search_dn(ads, &res, base, attrs);
1648 if (!ADS_ERR_OK(status)) {
1649 DEBUG(1,("Failed while searching for: %s\n", base));
1650 goto out;
1653 if (ads_count_replies(ads, res) != 1) {
1654 goto out;
1657 /* substitute the bind-path from the well-known-guid-search result */
1658 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1659 if (!wkn_dn) {
1660 goto out;
1663 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1664 if (!wkn_dn_exp) {
1665 goto out;
1668 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1669 if (!bind_dn_exp) {
1670 goto out;
1673 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1675 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1678 new_ln = wkn_ln - bind_ln;
1680 ret = SMB_STRDUP(wkn_dn_exp[0]);
1681 if (!ret) {
1682 goto out;
1685 for (i=1; i < new_ln; i++) {
1686 char *s = NULL;
1688 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1689 SAFE_FREE(ret);
1690 goto out;
1693 SAFE_FREE(ret);
1694 ret = SMB_STRDUP(s);
1695 free(s);
1696 if (!ret) {
1697 goto out;
1701 out:
1702 SAFE_FREE(base);
1703 ads_msgfree(ads, res);
1704 TALLOC_FREE(wkn_dn);
1705 if (wkn_dn_exp) {
1706 ldap_value_free(wkn_dn_exp);
1708 if (bind_dn_exp) {
1709 ldap_value_free(bind_dn_exp);
1712 return ret;
1716 * Adds (appends) an item to an attribute array, rather then
1717 * replacing the whole list
1718 * @param ctx An initialized TALLOC_CTX
1719 * @param mods An initialized ADS_MODLIST
1720 * @param name name of the ldap attribute to append to
1721 * @param vals an array of values to add
1722 * @return status of addition
1725 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1726 const char *name, const char **vals)
1728 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1729 (const void *) vals);
1733 * Determines the an account's current KVNO via an LDAP lookup
1734 * @param ads An initialized ADS_STRUCT
1735 * @param account_name the NT samaccountname.
1736 * @return the kvno for the account, or -1 in case of a failure.
1739 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1741 LDAPMessage *res = NULL;
1742 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1743 char *filter;
1744 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1745 char *dn_string = NULL;
1746 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1748 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1749 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1750 return kvno;
1752 ret = ads_search(ads, &res, filter, attrs);
1753 SAFE_FREE(filter);
1754 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1755 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1756 ads_msgfree(ads, res);
1757 return kvno;
1760 dn_string = ads_get_dn(ads, talloc_tos(), res);
1761 if (!dn_string) {
1762 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1763 ads_msgfree(ads, res);
1764 return kvno;
1766 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1767 TALLOC_FREE(dn_string);
1769 /* ---------------------------------------------------------
1770 * 0 is returned as a default KVNO from this point on...
1771 * This is done because Windows 2000 does not support key
1772 * version numbers. Chances are that a failure in the next
1773 * step is simply due to Windows 2000 being used for a
1774 * domain controller. */
1775 kvno = 0;
1777 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1778 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1779 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1780 ads_msgfree(ads, res);
1781 return kvno;
1784 /* Success */
1785 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1786 ads_msgfree(ads, res);
1787 return kvno;
1791 * Determines the computer account's current KVNO via an LDAP lookup
1792 * @param ads An initialized ADS_STRUCT
1793 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1794 * @return the kvno for the computer account, or -1 in case of a failure.
1797 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1799 char *computer_account = NULL;
1800 uint32_t kvno = -1;
1802 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1803 return kvno;
1806 kvno = ads_get_kvno(ads, computer_account);
1807 free(computer_account);
1809 return kvno;
1813 * This clears out all registered spn's for a given hostname
1814 * @param ads An initilaized ADS_STRUCT
1815 * @param machine_name the NetBIOS name of the computer.
1816 * @return 0 upon success, non-zero otherwise.
1819 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1821 TALLOC_CTX *ctx;
1822 LDAPMessage *res = NULL;
1823 ADS_MODLIST mods;
1824 const char *servicePrincipalName[1] = {NULL};
1825 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1826 char *dn_string = NULL;
1828 ret = ads_find_machine_acct(ads, &res, machine_name);
1829 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1830 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1831 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1832 ads_msgfree(ads, res);
1833 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1836 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1837 ctx = talloc_init("ads_clear_service_principal_names");
1838 if (!ctx) {
1839 ads_msgfree(ads, res);
1840 return ADS_ERROR(LDAP_NO_MEMORY);
1843 if (!(mods = ads_init_mods(ctx))) {
1844 talloc_destroy(ctx);
1845 ads_msgfree(ads, res);
1846 return ADS_ERROR(LDAP_NO_MEMORY);
1848 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1849 if (!ADS_ERR_OK(ret)) {
1850 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1851 ads_msgfree(ads, res);
1852 talloc_destroy(ctx);
1853 return ret;
1855 dn_string = ads_get_dn(ads, talloc_tos(), res);
1856 if (!dn_string) {
1857 talloc_destroy(ctx);
1858 ads_msgfree(ads, res);
1859 return ADS_ERROR(LDAP_NO_MEMORY);
1861 ret = ads_gen_mod(ads, dn_string, mods);
1862 TALLOC_FREE(dn_string);
1863 if (!ADS_ERR_OK(ret)) {
1864 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1865 machine_name));
1866 ads_msgfree(ads, res);
1867 talloc_destroy(ctx);
1868 return ret;
1871 ads_msgfree(ads, res);
1872 talloc_destroy(ctx);
1873 return ret;
1877 * This adds a service principal name to an existing computer account
1878 * (found by hostname) in AD.
1879 * @param ads An initialized ADS_STRUCT
1880 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1881 * @param my_fqdn The fully qualified DNS name of the machine
1882 * @param spn A string of the service principal to add, i.e. 'host'
1883 * @return 0 upon sucess, or non-zero if a failure occurs
1886 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1887 const char *my_fqdn, const char *spn)
1889 ADS_STATUS ret;
1890 TALLOC_CTX *ctx;
1891 LDAPMessage *res = NULL;
1892 char *psp1, *psp2;
1893 ADS_MODLIST mods;
1894 char *dn_string = NULL;
1895 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1897 ret = ads_find_machine_acct(ads, &res, machine_name);
1898 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1899 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1900 machine_name));
1901 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1902 spn, machine_name, ads->config.realm));
1903 ads_msgfree(ads, res);
1904 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1907 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1908 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1909 ads_msgfree(ads, res);
1910 return ADS_ERROR(LDAP_NO_MEMORY);
1913 /* add short name spn */
1915 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1916 talloc_destroy(ctx);
1917 ads_msgfree(ads, res);
1918 return ADS_ERROR(LDAP_NO_MEMORY);
1920 strupper_m(psp1);
1921 strlower_m(&psp1[strlen(spn)]);
1922 servicePrincipalName[0] = psp1;
1924 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1925 psp1, machine_name));
1928 /* add fully qualified spn */
1930 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1931 ret = ADS_ERROR(LDAP_NO_MEMORY);
1932 goto out;
1934 strupper_m(psp2);
1935 strlower_m(&psp2[strlen(spn)]);
1936 servicePrincipalName[1] = psp2;
1938 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1939 psp2, machine_name));
1941 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1942 ret = ADS_ERROR(LDAP_NO_MEMORY);
1943 goto out;
1946 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1947 if (!ADS_ERR_OK(ret)) {
1948 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1949 goto out;
1952 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1953 ret = ADS_ERROR(LDAP_NO_MEMORY);
1954 goto out;
1957 ret = ads_gen_mod(ads, dn_string, mods);
1958 if (!ADS_ERR_OK(ret)) {
1959 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1960 goto out;
1963 out:
1964 TALLOC_FREE( ctx );
1965 ads_msgfree(ads, res);
1966 return ret;
1970 * adds a machine account to the ADS server
1971 * @param ads An intialized ADS_STRUCT
1972 * @param machine_name - the NetBIOS machine name of this account.
1973 * @param account_type A number indicating the type of account to create
1974 * @param org_unit The LDAP path in which to place this account
1975 * @return 0 upon success, or non-zero otherwise
1978 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1979 const char *org_unit)
1981 ADS_STATUS ret;
1982 char *samAccountName, *controlstr;
1983 TALLOC_CTX *ctx;
1984 ADS_MODLIST mods;
1985 char *machine_escaped = NULL;
1986 char *new_dn;
1987 const char *objectClass[] = {"top", "person", "organizationalPerson",
1988 "user", "computer", NULL};
1989 LDAPMessage *res = NULL;
1990 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1991 UF_DONT_EXPIRE_PASSWD |\
1992 UF_ACCOUNTDISABLE );
1994 if (!(ctx = talloc_init("ads_add_machine_acct")))
1995 return ADS_ERROR(LDAP_NO_MEMORY);
1997 ret = ADS_ERROR(LDAP_NO_MEMORY);
1999 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2000 if (!machine_escaped) {
2001 goto done;
2004 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2005 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2007 if ( !new_dn || !samAccountName ) {
2008 goto done;
2011 #ifndef ENCTYPE_ARCFOUR_HMAC
2012 acct_control |= UF_USE_DES_KEY_ONLY;
2013 #endif
2015 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2016 goto done;
2019 if (!(mods = ads_init_mods(ctx))) {
2020 goto done;
2023 ads_mod_str(ctx, &mods, "cn", machine_name);
2024 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2025 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2026 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2028 ret = ads_gen_add(ads, new_dn, mods);
2030 done:
2031 SAFE_FREE(machine_escaped);
2032 ads_msgfree(ads, res);
2033 talloc_destroy(ctx);
2035 return ret;
2039 * move a machine account to another OU on the ADS server
2040 * @param ads - An intialized ADS_STRUCT
2041 * @param machine_name - the NetBIOS machine name of this account.
2042 * @param org_unit - The LDAP path in which to place this account
2043 * @param moved - whether we moved the machine account (optional)
2044 * @return 0 upon success, or non-zero otherwise
2047 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2048 const char *org_unit, bool *moved)
2050 ADS_STATUS rc;
2051 int ldap_status;
2052 LDAPMessage *res = NULL;
2053 char *filter = NULL;
2054 char *computer_dn = NULL;
2055 char *parent_dn;
2056 char *computer_rdn = NULL;
2057 bool need_move = False;
2059 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2060 rc = ADS_ERROR(LDAP_NO_MEMORY);
2061 goto done;
2064 /* Find pre-existing machine */
2065 rc = ads_search(ads, &res, filter, NULL);
2066 if (!ADS_ERR_OK(rc)) {
2067 goto done;
2070 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2071 if (!computer_dn) {
2072 rc = ADS_ERROR(LDAP_NO_MEMORY);
2073 goto done;
2076 parent_dn = ads_parent_dn(computer_dn);
2077 if (strequal(parent_dn, org_unit)) {
2078 goto done;
2081 need_move = True;
2083 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2084 rc = ADS_ERROR(LDAP_NO_MEMORY);
2085 goto done;
2088 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2089 org_unit, 1, NULL, NULL);
2090 rc = ADS_ERROR(ldap_status);
2092 done:
2093 ads_msgfree(ads, res);
2094 SAFE_FREE(filter);
2095 TALLOC_FREE(computer_dn);
2096 SAFE_FREE(computer_rdn);
2098 if (!ADS_ERR_OK(rc)) {
2099 need_move = False;
2102 if (moved) {
2103 *moved = need_move;
2106 return rc;
2110 dump a binary result from ldap
2112 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2114 int i, j;
2115 for (i=0; values[i]; i++) {
2116 printf("%s: ", field);
2117 for (j=0; j<values[i]->bv_len; j++) {
2118 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2120 printf("\n");
2124 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2126 int i;
2127 for (i=0; values[i]; i++) {
2129 UUID_FLAT guid;
2130 struct GUID tmp;
2132 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2133 smb_uuid_unpack(guid, &tmp);
2134 printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2139 dump a sid result from ldap
2141 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2143 int i;
2144 for (i=0; values[i]; i++) {
2145 struct dom_sid sid;
2146 fstring tmp;
2147 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2148 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2153 dump ntSecurityDescriptor
2155 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2157 TALLOC_CTX *frame = talloc_stackframe();
2158 struct security_descriptor *psd;
2159 NTSTATUS status;
2161 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2162 values[0]->bv_len, &psd);
2163 if (!NT_STATUS_IS_OK(status)) {
2164 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2165 nt_errstr(status)));
2166 TALLOC_FREE(frame);
2167 return;
2170 if (psd) {
2171 ads_disp_sd(ads, talloc_tos(), psd);
2174 TALLOC_FREE(frame);
2178 dump a string result from ldap
2180 static void dump_string(const char *field, char **values)
2182 int i;
2183 for (i=0; values[i]; i++) {
2184 printf("%s: %s\n", field, values[i]);
2189 dump a field from LDAP on stdout
2190 used for debugging
2193 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2195 const struct {
2196 const char *name;
2197 bool string;
2198 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2199 } handlers[] = {
2200 {"objectGUID", False, dump_guid},
2201 {"netbootGUID", False, dump_guid},
2202 {"nTSecurityDescriptor", False, dump_sd},
2203 {"dnsRecord", False, dump_binary},
2204 {"objectSid", False, dump_sid},
2205 {"tokenGroups", False, dump_sid},
2206 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2207 {"tokengroupsGlobalandUniversal", False, dump_sid},
2208 {"mS-DS-CreatorSID", False, dump_sid},
2209 {"msExchMailboxGuid", False, dump_guid},
2210 {NULL, True, NULL}
2212 int i;
2214 if (!field) { /* must be end of an entry */
2215 printf("\n");
2216 return False;
2219 for (i=0; handlers[i].name; i++) {
2220 if (StrCaseCmp(handlers[i].name, field) == 0) {
2221 if (!values) /* first time, indicate string or not */
2222 return handlers[i].string;
2223 handlers[i].handler(ads, field, (struct berval **) values);
2224 break;
2227 if (!handlers[i].name) {
2228 if (!values) /* first time, indicate string conversion */
2229 return True;
2230 dump_string(field, (char **)values);
2232 return False;
2236 * Dump a result from LDAP on stdout
2237 * used for debugging
2238 * @param ads connection to ads server
2239 * @param res Results to dump
2242 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2244 ads_process_results(ads, res, ads_dump_field, NULL);
2248 * Walk through results, calling a function for each entry found.
2249 * The function receives a field name, a berval * array of values,
2250 * and a data area passed through from the start. The function is
2251 * called once with null for field and values at the end of each
2252 * entry.
2253 * @param ads connection to ads server
2254 * @param res Results to process
2255 * @param fn Function for processing each result
2256 * @param data_area user-defined area to pass to function
2258 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2259 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2260 void *data_area)
2262 LDAPMessage *msg;
2263 TALLOC_CTX *ctx;
2264 size_t converted_size;
2266 if (!(ctx = talloc_init("ads_process_results")))
2267 return;
2269 for (msg = ads_first_entry(ads, res); msg;
2270 msg = ads_next_entry(ads, msg)) {
2271 char *utf8_field;
2272 BerElement *b;
2274 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2275 (LDAPMessage *)msg,&b);
2276 utf8_field;
2277 utf8_field=ldap_next_attribute(ads->ldap.ld,
2278 (LDAPMessage *)msg,b)) {
2279 struct berval **ber_vals;
2280 char **str_vals, **utf8_vals;
2281 char *field;
2282 bool string;
2284 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2285 &converted_size))
2287 DEBUG(0,("ads_process_results: "
2288 "pull_utf8_talloc failed: %s",
2289 strerror(errno)));
2292 string = fn(ads, field, NULL, data_area);
2294 if (string) {
2295 utf8_vals = ldap_get_values(ads->ldap.ld,
2296 (LDAPMessage *)msg, field);
2297 str_vals = ads_pull_strvals(ctx,
2298 (const char **) utf8_vals);
2299 fn(ads, field, (void **) str_vals, data_area);
2300 ldap_value_free(utf8_vals);
2301 } else {
2302 ber_vals = ldap_get_values_len(ads->ldap.ld,
2303 (LDAPMessage *)msg, field);
2304 fn(ads, field, (void **) ber_vals, data_area);
2306 ldap_value_free_len(ber_vals);
2308 ldap_memfree(utf8_field);
2310 ber_free(b, 0);
2311 talloc_free_children(ctx);
2312 fn(ads, NULL, NULL, data_area); /* completed an entry */
2315 talloc_destroy(ctx);
2319 * count how many replies are in a LDAPMessage
2320 * @param ads connection to ads server
2321 * @param res Results to count
2322 * @return number of replies
2324 int ads_count_replies(ADS_STRUCT *ads, void *res)
2326 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2330 * pull the first entry from a ADS result
2331 * @param ads connection to ads server
2332 * @param res Results of search
2333 * @return first entry from result
2335 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2337 return ldap_first_entry(ads->ldap.ld, res);
2341 * pull the next entry from a ADS result
2342 * @param ads connection to ads server
2343 * @param res Results of search
2344 * @return next entry from result
2346 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2348 return ldap_next_entry(ads->ldap.ld, res);
2352 * pull the first message from a ADS result
2353 * @param ads connection to ads server
2354 * @param res Results of search
2355 * @return first message from result
2357 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2359 return ldap_first_message(ads->ldap.ld, res);
2363 * pull the next message from a ADS result
2364 * @param ads connection to ads server
2365 * @param res Results of search
2366 * @return next message from result
2368 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2370 return ldap_next_message(ads->ldap.ld, res);
2374 * pull a single string from a ADS result
2375 * @param ads connection to ads server
2376 * @param mem_ctx TALLOC_CTX to use for allocating result string
2377 * @param msg Results of search
2378 * @param field Attribute to retrieve
2379 * @return Result string in talloc context
2381 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2382 const char *field)
2384 char **values;
2385 char *ret = NULL;
2386 char *ux_string;
2387 size_t converted_size;
2389 values = ldap_get_values(ads->ldap.ld, msg, field);
2390 if (!values)
2391 return NULL;
2393 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2394 &converted_size))
2396 ret = ux_string;
2398 ldap_value_free(values);
2399 return ret;
2403 * pull an array of strings from a ADS result
2404 * @param ads connection to ads server
2405 * @param mem_ctx TALLOC_CTX to use for allocating result string
2406 * @param msg Results of search
2407 * @param field Attribute to retrieve
2408 * @return Result strings in talloc context
2410 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2411 LDAPMessage *msg, const char *field,
2412 size_t *num_values)
2414 char **values;
2415 char **ret = NULL;
2416 int i;
2417 size_t converted_size;
2419 values = ldap_get_values(ads->ldap.ld, msg, field);
2420 if (!values)
2421 return NULL;
2423 *num_values = ldap_count_values(values);
2425 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2426 if (!ret) {
2427 ldap_value_free(values);
2428 return NULL;
2431 for (i=0;i<*num_values;i++) {
2432 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2433 &converted_size))
2435 ldap_value_free(values);
2436 return NULL;
2439 ret[i] = NULL;
2441 ldap_value_free(values);
2442 return ret;
2446 * pull an array of strings from a ADS result
2447 * (handle large multivalue attributes with range retrieval)
2448 * @param ads connection to ads server
2449 * @param mem_ctx TALLOC_CTX to use for allocating result string
2450 * @param msg Results of search
2451 * @param field Attribute to retrieve
2452 * @param current_strings strings returned by a previous call to this function
2453 * @param next_attribute The next query should ask for this attribute
2454 * @param num_values How many values did we get this time?
2455 * @param more_values Are there more values to get?
2456 * @return Result strings in talloc context
2458 char **ads_pull_strings_range(ADS_STRUCT *ads,
2459 TALLOC_CTX *mem_ctx,
2460 LDAPMessage *msg, const char *field,
2461 char **current_strings,
2462 const char **next_attribute,
2463 size_t *num_strings,
2464 bool *more_strings)
2466 char *attr;
2467 char *expected_range_attrib, *range_attr;
2468 BerElement *ptr = NULL;
2469 char **strings;
2470 char **new_strings;
2471 size_t num_new_strings;
2472 unsigned long int range_start;
2473 unsigned long int range_end;
2475 /* we might have been given the whole lot anyway */
2476 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2477 *more_strings = False;
2478 return strings;
2481 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2483 /* look for Range result */
2484 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2485 attr;
2486 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2487 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2488 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2489 range_attr = attr;
2490 break;
2492 ldap_memfree(attr);
2494 if (!attr) {
2495 ber_free(ptr, 0);
2496 /* nothing here - this field is just empty */
2497 *more_strings = False;
2498 return NULL;
2501 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2502 &range_start, &range_end) == 2) {
2503 *more_strings = True;
2504 } else {
2505 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2506 &range_start) == 1) {
2507 *more_strings = False;
2508 } else {
2509 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2510 range_attr));
2511 ldap_memfree(range_attr);
2512 *more_strings = False;
2513 return NULL;
2517 if ((*num_strings) != range_start) {
2518 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2519 " - aborting range retreival\n",
2520 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2521 ldap_memfree(range_attr);
2522 *more_strings = False;
2523 return NULL;
2526 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2528 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2529 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2530 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2531 range_attr, (unsigned long int)range_end - range_start + 1,
2532 (unsigned long int)num_new_strings));
2533 ldap_memfree(range_attr);
2534 *more_strings = False;
2535 return NULL;
2538 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2539 *num_strings + num_new_strings);
2541 if (strings == NULL) {
2542 ldap_memfree(range_attr);
2543 *more_strings = False;
2544 return NULL;
2547 if (new_strings && num_new_strings) {
2548 memcpy(&strings[*num_strings], new_strings,
2549 sizeof(*new_strings) * num_new_strings);
2552 (*num_strings) += num_new_strings;
2554 if (*more_strings) {
2555 *next_attribute = talloc_asprintf(mem_ctx,
2556 "%s;range=%d-*",
2557 field,
2558 (int)*num_strings);
2560 if (!*next_attribute) {
2561 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2562 ldap_memfree(range_attr);
2563 *more_strings = False;
2564 return NULL;
2568 ldap_memfree(range_attr);
2570 return strings;
2574 * pull a single uint32 from a ADS result
2575 * @param ads connection to ads server
2576 * @param msg Results of search
2577 * @param field Attribute to retrieve
2578 * @param v Pointer to int to store result
2579 * @return boolean inidicating success
2581 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2582 uint32 *v)
2584 char **values;
2586 values = ldap_get_values(ads->ldap.ld, msg, field);
2587 if (!values)
2588 return False;
2589 if (!values[0]) {
2590 ldap_value_free(values);
2591 return False;
2594 *v = atoi(values[0]);
2595 ldap_value_free(values);
2596 return True;
2600 * pull a single objectGUID from an ADS result
2601 * @param ads connection to ADS server
2602 * @param msg results of search
2603 * @param guid 37-byte area to receive text guid
2604 * @return boolean indicating success
2606 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2608 char **values;
2609 UUID_FLAT flat_guid;
2611 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2612 if (!values)
2613 return False;
2615 if (values[0]) {
2616 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2617 smb_uuid_unpack(flat_guid, guid);
2618 ldap_value_free(values);
2619 return True;
2621 ldap_value_free(values);
2622 return False;
2628 * pull a single struct dom_sid from a ADS result
2629 * @param ads connection to ads server
2630 * @param msg Results of search
2631 * @param field Attribute to retrieve
2632 * @param sid Pointer to sid to store result
2633 * @return boolean inidicating success
2635 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2636 struct dom_sid *sid)
2638 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2642 * pull an array of struct dom_sids from a ADS result
2643 * @param ads connection to ads server
2644 * @param mem_ctx TALLOC_CTX for allocating sid array
2645 * @param msg Results of search
2646 * @param field Attribute to retrieve
2647 * @param sids pointer to sid array to allocate
2648 * @return the count of SIDs pulled
2650 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2651 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2653 struct berval **values;
2654 bool ret;
2655 int count, i;
2657 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2659 if (!values)
2660 return 0;
2662 for (i=0; values[i]; i++)
2663 /* nop */ ;
2665 if (i) {
2666 (*sids) = TALLOC_ARRAY(mem_ctx, struct dom_sid, i);
2667 if (!(*sids)) {
2668 ldap_value_free_len(values);
2669 return 0;
2671 } else {
2672 (*sids) = NULL;
2675 count = 0;
2676 for (i=0; values[i]; i++) {
2677 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2678 if (ret) {
2679 DEBUG(10, ("pulling SID: %s\n",
2680 sid_string_dbg(&(*sids)[count])));
2681 count++;
2685 ldap_value_free_len(values);
2686 return count;
2690 * pull a struct security_descriptor from a ADS result
2691 * @param ads connection to ads server
2692 * @param mem_ctx TALLOC_CTX for allocating sid array
2693 * @param msg Results of search
2694 * @param field Attribute to retrieve
2695 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2696 * @return boolean inidicating success
2698 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2699 LDAPMessage *msg, const char *field,
2700 struct security_descriptor **sd)
2702 struct berval **values;
2703 bool ret = true;
2705 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2707 if (!values) return false;
2709 if (values[0]) {
2710 NTSTATUS status;
2711 status = unmarshall_sec_desc(mem_ctx,
2712 (uint8 *)values[0]->bv_val,
2713 values[0]->bv_len, sd);
2714 if (!NT_STATUS_IS_OK(status)) {
2715 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2716 nt_errstr(status)));
2717 ret = false;
2721 ldap_value_free_len(values);
2722 return ret;
2726 * in order to support usernames longer than 21 characters we need to
2727 * use both the sAMAccountName and the userPrincipalName attributes
2728 * It seems that not all users have the userPrincipalName attribute set
2730 * @param ads connection to ads server
2731 * @param mem_ctx TALLOC_CTX for allocating sid array
2732 * @param msg Results of search
2733 * @return the username
2735 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2736 LDAPMessage *msg)
2738 #if 0 /* JERRY */
2739 char *ret, *p;
2741 /* lookup_name() only works on the sAMAccountName to
2742 returning the username portion of userPrincipalName
2743 breaks winbindd_getpwnam() */
2745 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2746 if (ret && (p = strchr_m(ret, '@'))) {
2747 *p = 0;
2748 return ret;
2750 #endif
2751 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2756 * find the update serial number - this is the core of the ldap cache
2757 * @param ads connection to ads server
2758 * @param ads connection to ADS server
2759 * @param usn Pointer to retrieved update serial number
2760 * @return status of search
2762 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2764 const char *attrs[] = {"highestCommittedUSN", NULL};
2765 ADS_STATUS status;
2766 LDAPMessage *res;
2768 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2769 if (!ADS_ERR_OK(status))
2770 return status;
2772 if (ads_count_replies(ads, res) != 1) {
2773 ads_msgfree(ads, res);
2774 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2777 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2778 ads_msgfree(ads, res);
2779 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2782 ads_msgfree(ads, res);
2783 return ADS_SUCCESS;
2786 /* parse a ADS timestring - typical string is
2787 '20020917091222.0Z0' which means 09:12.22 17th September
2788 2002, timezone 0 */
2789 static time_t ads_parse_time(const char *str)
2791 struct tm tm;
2793 ZERO_STRUCT(tm);
2795 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2796 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2797 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2798 return 0;
2800 tm.tm_year -= 1900;
2801 tm.tm_mon -= 1;
2803 return timegm(&tm);
2806 /********************************************************************
2807 ********************************************************************/
2809 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2811 const char *attrs[] = {"currentTime", NULL};
2812 ADS_STATUS status;
2813 LDAPMessage *res;
2814 char *timestr;
2815 TALLOC_CTX *ctx;
2816 ADS_STRUCT *ads_s = ads;
2818 if (!(ctx = talloc_init("ads_current_time"))) {
2819 return ADS_ERROR(LDAP_NO_MEMORY);
2822 /* establish a new ldap tcp session if necessary */
2824 if ( !ads->ldap.ld ) {
2825 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2826 ads->server.ldap_server )) == NULL )
2828 goto done;
2830 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2831 status = ads_connect( ads_s );
2832 if ( !ADS_ERR_OK(status))
2833 goto done;
2836 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2837 if (!ADS_ERR_OK(status)) {
2838 goto done;
2841 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2842 if (!timestr) {
2843 ads_msgfree(ads_s, res);
2844 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2845 goto done;
2848 /* but save the time and offset in the original ADS_STRUCT */
2850 ads->config.current_time = ads_parse_time(timestr);
2852 if (ads->config.current_time != 0) {
2853 ads->auth.time_offset = ads->config.current_time - time(NULL);
2854 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2857 ads_msgfree(ads, res);
2859 status = ADS_SUCCESS;
2861 done:
2862 /* free any temporary ads connections */
2863 if ( ads_s != ads ) {
2864 ads_destroy( &ads_s );
2866 talloc_destroy(ctx);
2868 return status;
2871 /********************************************************************
2872 ********************************************************************/
2874 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2876 const char *attrs[] = {"domainFunctionality", NULL};
2877 ADS_STATUS status;
2878 LDAPMessage *res;
2879 ADS_STRUCT *ads_s = ads;
2881 *val = DS_DOMAIN_FUNCTION_2000;
2883 /* establish a new ldap tcp session if necessary */
2885 if ( !ads->ldap.ld ) {
2886 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2887 ads->server.ldap_server )) == NULL )
2889 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2890 goto done;
2892 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2893 status = ads_connect( ads_s );
2894 if ( !ADS_ERR_OK(status))
2895 goto done;
2898 /* If the attribute does not exist assume it is a Windows 2000
2899 functional domain */
2901 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2902 if (!ADS_ERR_OK(status)) {
2903 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2904 status = ADS_SUCCESS;
2906 goto done;
2909 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2910 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2912 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2915 ads_msgfree(ads, res);
2917 done:
2918 /* free any temporary ads connections */
2919 if ( ads_s != ads ) {
2920 ads_destroy( &ads_s );
2923 return status;
2927 * find the domain sid for our domain
2928 * @param ads connection to ads server
2929 * @param sid Pointer to domain sid
2930 * @return status of search
2932 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2934 const char *attrs[] = {"objectSid", NULL};
2935 LDAPMessage *res;
2936 ADS_STATUS rc;
2938 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2939 attrs, &res);
2940 if (!ADS_ERR_OK(rc)) return rc;
2941 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2942 ads_msgfree(ads, res);
2943 return ADS_ERROR_SYSTEM(ENOENT);
2945 ads_msgfree(ads, res);
2947 return ADS_SUCCESS;
2951 * find our site name
2952 * @param ads connection to ads server
2953 * @param mem_ctx Pointer to talloc context
2954 * @param site_name Pointer to the sitename
2955 * @return status of search
2957 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2959 ADS_STATUS status;
2960 LDAPMessage *res;
2961 const char *dn, *service_name;
2962 const char *attrs[] = { "dsServiceName", NULL };
2964 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2965 if (!ADS_ERR_OK(status)) {
2966 return status;
2969 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2970 if (service_name == NULL) {
2971 ads_msgfree(ads, res);
2972 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2975 ads_msgfree(ads, res);
2977 /* go up three levels */
2978 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2979 if (dn == NULL) {
2980 return ADS_ERROR(LDAP_NO_MEMORY);
2983 *site_name = talloc_strdup(mem_ctx, dn);
2984 if (*site_name == NULL) {
2985 return ADS_ERROR(LDAP_NO_MEMORY);
2988 return status;
2990 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2995 * find the site dn where a machine resides
2996 * @param ads connection to ads server
2997 * @param mem_ctx Pointer to talloc context
2998 * @param computer_name name of the machine
2999 * @param site_name Pointer to the sitename
3000 * @return status of search
3002 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3004 ADS_STATUS status;
3005 LDAPMessage *res;
3006 const char *parent, *filter;
3007 char *config_context = NULL;
3008 char *dn;
3010 /* shortcut a query */
3011 if (strequal(computer_name, ads->config.ldap_server_name)) {
3012 return ads_site_dn(ads, mem_ctx, site_dn);
3015 status = ads_config_path(ads, mem_ctx, &config_context);
3016 if (!ADS_ERR_OK(status)) {
3017 return status;
3020 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3021 if (filter == NULL) {
3022 return ADS_ERROR(LDAP_NO_MEMORY);
3025 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3026 filter, NULL, &res);
3027 if (!ADS_ERR_OK(status)) {
3028 return status;
3031 if (ads_count_replies(ads, res) != 1) {
3032 ads_msgfree(ads, res);
3033 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3036 dn = ads_get_dn(ads, mem_ctx, res);
3037 if (dn == NULL) {
3038 ads_msgfree(ads, res);
3039 return ADS_ERROR(LDAP_NO_MEMORY);
3042 /* go up three levels */
3043 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3044 if (parent == NULL) {
3045 ads_msgfree(ads, res);
3046 TALLOC_FREE(dn);
3047 return ADS_ERROR(LDAP_NO_MEMORY);
3050 *site_dn = talloc_strdup(mem_ctx, parent);
3051 if (*site_dn == NULL) {
3052 ads_msgfree(ads, res);
3053 TALLOC_FREE(dn);
3054 return ADS_ERROR(LDAP_NO_MEMORY);
3057 TALLOC_FREE(dn);
3058 ads_msgfree(ads, res);
3060 return status;
3064 * get the upn suffixes for a domain
3065 * @param ads connection to ads server
3066 * @param mem_ctx Pointer to talloc context
3067 * @param suffixes Pointer to an array of suffixes
3068 * @param num_suffixes Pointer to the number of suffixes
3069 * @return status of search
3071 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3073 ADS_STATUS status;
3074 LDAPMessage *res;
3075 const char *base;
3076 char *config_context = NULL;
3077 const char *attrs[] = { "uPNSuffixes", NULL };
3079 status = ads_config_path(ads, mem_ctx, &config_context);
3080 if (!ADS_ERR_OK(status)) {
3081 return status;
3084 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3085 if (base == NULL) {
3086 return ADS_ERROR(LDAP_NO_MEMORY);
3089 status = ads_search_dn(ads, &res, base, attrs);
3090 if (!ADS_ERR_OK(status)) {
3091 return status;
3094 if (ads_count_replies(ads, res) != 1) {
3095 ads_msgfree(ads, res);
3096 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3099 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3100 if ((*suffixes) == NULL) {
3101 ads_msgfree(ads, res);
3102 return ADS_ERROR(LDAP_NO_MEMORY);
3105 ads_msgfree(ads, res);
3107 return status;
3111 * get the joinable ous for a domain
3112 * @param ads connection to ads server
3113 * @param mem_ctx Pointer to talloc context
3114 * @param ous Pointer to an array of ous
3115 * @param num_ous Pointer to the number of ous
3116 * @return status of search
3118 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3119 TALLOC_CTX *mem_ctx,
3120 char ***ous,
3121 size_t *num_ous)
3123 ADS_STATUS status;
3124 LDAPMessage *res = NULL;
3125 LDAPMessage *msg = NULL;
3126 const char *attrs[] = { "dn", NULL };
3127 int count = 0;
3129 status = ads_search(ads, &res,
3130 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3131 attrs);
3132 if (!ADS_ERR_OK(status)) {
3133 return status;
3136 count = ads_count_replies(ads, res);
3137 if (count < 1) {
3138 ads_msgfree(ads, res);
3139 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3142 for (msg = ads_first_entry(ads, res); msg;
3143 msg = ads_next_entry(ads, msg)) {
3145 char *dn = NULL;
3147 dn = ads_get_dn(ads, talloc_tos(), msg);
3148 if (!dn) {
3149 ads_msgfree(ads, res);
3150 return ADS_ERROR(LDAP_NO_MEMORY);
3153 if (!add_string_to_array(mem_ctx, dn,
3154 (const char ***)ous,
3155 (int *)num_ous)) {
3156 TALLOC_FREE(dn);
3157 ads_msgfree(ads, res);
3158 return ADS_ERROR(LDAP_NO_MEMORY);
3161 TALLOC_FREE(dn);
3164 ads_msgfree(ads, res);
3166 return status;
3171 * pull a struct dom_sid from an extended dn string
3172 * @param mem_ctx TALLOC_CTX
3173 * @param extended_dn string
3174 * @param flags string type of extended_dn
3175 * @param sid pointer to a struct dom_sid
3176 * @return NT_STATUS_OK on success,
3177 * NT_INVALID_PARAMETER on error,
3178 * NT_STATUS_NOT_FOUND if no SID present
3180 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3181 const char *extended_dn,
3182 enum ads_extended_dn_flags flags,
3183 struct dom_sid *sid)
3185 char *p, *q, *dn;
3187 if (!extended_dn) {
3188 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3191 /* otherwise extended_dn gets stripped off */
3192 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3193 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3196 * ADS_EXTENDED_DN_HEX_STRING:
3197 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3199 * ADS_EXTENDED_DN_STRING (only with w2k3):
3200 * <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
3202 * Object with no SID, such as an Exchange Public Folder
3203 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3206 p = strchr(dn, ';');
3207 if (!p) {
3208 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3211 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3212 DEBUG(5,("No SID present in extended dn\n"));
3213 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3216 p += strlen(";<SID=");
3218 q = strchr(p, '>');
3219 if (!q) {
3220 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3223 *q = '\0';
3225 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3227 switch (flags) {
3229 case ADS_EXTENDED_DN_STRING:
3230 if (!string_to_sid(sid, p)) {
3231 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3233 break;
3234 case ADS_EXTENDED_DN_HEX_STRING: {
3235 fstring buf;
3236 size_t buf_len;
3238 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3239 if (buf_len == 0) {
3240 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3243 if (!sid_parse(buf, buf_len, sid)) {
3244 DEBUG(10,("failed to parse sid\n"));
3245 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3247 break;
3249 default:
3250 DEBUG(10,("unknown extended dn format\n"));
3251 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3254 return ADS_ERROR_NT(NT_STATUS_OK);
3258 * pull an array of struct dom_sids from a ADS result
3259 * @param ads connection to ads server
3260 * @param mem_ctx TALLOC_CTX for allocating sid array
3261 * @param msg Results of search
3262 * @param field Attribute to retrieve
3263 * @param flags string type of extended_dn
3264 * @param sids pointer to sid array to allocate
3265 * @return the count of SIDs pulled
3267 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3268 TALLOC_CTX *mem_ctx,
3269 LDAPMessage *msg,
3270 const char *field,
3271 enum ads_extended_dn_flags flags,
3272 struct dom_sid **sids)
3274 int i;
3275 ADS_STATUS rc;
3276 size_t dn_count, ret_count = 0;
3277 char **dn_strings;
3279 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3280 &dn_count)) == NULL) {
3281 return 0;
3284 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, dn_count + 1);
3285 if (!(*sids)) {
3286 TALLOC_FREE(dn_strings);
3287 return 0;
3290 for (i=0; i<dn_count; i++) {
3291 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3292 flags, &(*sids)[i]);
3293 if (!ADS_ERR_OK(rc)) {
3294 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3295 NT_STATUS_NOT_FOUND)) {
3296 continue;
3298 else {
3299 TALLOC_FREE(*sids);
3300 TALLOC_FREE(dn_strings);
3301 return 0;
3304 ret_count++;
3307 TALLOC_FREE(dn_strings);
3309 return ret_count;
3312 /********************************************************************
3313 ********************************************************************/
3315 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3317 LDAPMessage *res = NULL;
3318 ADS_STATUS status;
3319 int count = 0;
3320 char *name = NULL;
3322 status = ads_find_machine_acct(ads, &res, global_myname());
3323 if (!ADS_ERR_OK(status)) {
3324 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3325 global_myname()));
3326 goto out;
3329 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3330 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3331 goto out;
3334 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3335 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3338 out:
3339 ads_msgfree(ads, res);
3341 return name;
3344 /********************************************************************
3345 ********************************************************************/
3347 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3349 LDAPMessage *res = NULL;
3350 ADS_STATUS status;
3351 int count = 0;
3352 char *name = NULL;
3354 status = ads_find_machine_acct(ads, &res, machine_name);
3355 if (!ADS_ERR_OK(status)) {
3356 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3357 global_myname()));
3358 goto out;
3361 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3362 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3363 goto out;
3366 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3367 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3370 out:
3371 ads_msgfree(ads, res);
3373 return name;
3376 /********************************************************************
3377 ********************************************************************/
3379 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3381 LDAPMessage *res = NULL;
3382 ADS_STATUS status;
3383 int count = 0;
3384 char *name = NULL;
3386 status = ads_find_machine_acct(ads, &res, global_myname());
3387 if (!ADS_ERR_OK(status)) {
3388 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3389 global_myname()));
3390 goto out;
3393 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3394 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3395 goto out;
3398 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3399 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3402 out:
3403 ads_msgfree(ads, res);
3405 return name;
3408 #if 0
3410 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3413 * Join a machine to a realm
3414 * Creates the machine account and sets the machine password
3415 * @param ads connection to ads server
3416 * @param machine name of host to add
3417 * @param org_unit Organizational unit to place machine in
3418 * @return status of join
3420 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3421 uint32 account_type, const char *org_unit)
3423 ADS_STATUS status;
3424 LDAPMessage *res = NULL;
3425 char *machine;
3427 /* machine name must be lowercase */
3428 machine = SMB_STRDUP(machine_name);
3429 strlower_m(machine);
3432 status = ads_find_machine_acct(ads, (void **)&res, machine);
3433 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3434 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3435 status = ads_leave_realm(ads, machine);
3436 if (!ADS_ERR_OK(status)) {
3437 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3438 machine, ads->config.realm));
3439 return status;
3443 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3444 if (!ADS_ERR_OK(status)) {
3445 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3446 SAFE_FREE(machine);
3447 return status;
3450 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3451 if (!ADS_ERR_OK(status)) {
3452 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3453 SAFE_FREE(machine);
3454 return status;
3457 SAFE_FREE(machine);
3458 ads_msgfree(ads, res);
3460 return status;
3462 #endif
3465 * Delete a machine from the realm
3466 * @param ads connection to ads server
3467 * @param hostname Machine to remove
3468 * @return status of delete
3470 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3472 ADS_STATUS status;
3473 void *msg;
3474 LDAPMessage *res;
3475 char *hostnameDN, *host;
3476 int rc;
3477 LDAPControl ldap_control;
3478 LDAPControl * pldap_control[2] = {NULL, NULL};
3480 pldap_control[0] = &ldap_control;
3481 memset(&ldap_control, 0, sizeof(LDAPControl));
3482 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3484 /* hostname must be lowercase */
3485 host = SMB_STRDUP(hostname);
3486 strlower_m(host);
3488 status = ads_find_machine_acct(ads, &res, host);
3489 if (!ADS_ERR_OK(status)) {
3490 DEBUG(0, ("Host account for %s does not exist.\n", host));
3491 SAFE_FREE(host);
3492 return status;
3495 msg = ads_first_entry(ads, res);
3496 if (!msg) {
3497 SAFE_FREE(host);
3498 return ADS_ERROR_SYSTEM(ENOENT);
3501 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3503 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3504 if (rc) {
3505 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3506 }else {
3507 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3510 if (rc != LDAP_SUCCESS) {
3511 const char *attrs[] = { "cn", NULL };
3512 LDAPMessage *msg_sub;
3514 /* we only search with scope ONE, we do not expect any further
3515 * objects to be created deeper */
3517 status = ads_do_search_retry(ads, hostnameDN,
3518 LDAP_SCOPE_ONELEVEL,
3519 "(objectclass=*)", attrs, &res);
3521 if (!ADS_ERR_OK(status)) {
3522 SAFE_FREE(host);
3523 TALLOC_FREE(hostnameDN);
3524 return status;
3527 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3528 msg_sub = ads_next_entry(ads, msg_sub)) {
3530 char *dn = NULL;
3532 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3533 SAFE_FREE(host);
3534 TALLOC_FREE(hostnameDN);
3535 return ADS_ERROR(LDAP_NO_MEMORY);
3538 status = ads_del_dn(ads, dn);
3539 if (!ADS_ERR_OK(status)) {
3540 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3541 SAFE_FREE(host);
3542 TALLOC_FREE(dn);
3543 TALLOC_FREE(hostnameDN);
3544 return status;
3547 TALLOC_FREE(dn);
3550 /* there should be no subordinate objects anymore */
3551 status = ads_do_search_retry(ads, hostnameDN,
3552 LDAP_SCOPE_ONELEVEL,
3553 "(objectclass=*)", attrs, &res);
3555 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3556 SAFE_FREE(host);
3557 TALLOC_FREE(hostnameDN);
3558 return status;
3561 /* delete hostnameDN now */
3562 status = ads_del_dn(ads, hostnameDN);
3563 if (!ADS_ERR_OK(status)) {
3564 SAFE_FREE(host);
3565 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3566 TALLOC_FREE(hostnameDN);
3567 return status;
3571 TALLOC_FREE(hostnameDN);
3573 status = ads_find_machine_acct(ads, &res, host);
3574 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3575 DEBUG(3, ("Failed to remove host account.\n"));
3576 SAFE_FREE(host);
3577 return status;
3580 SAFE_FREE(host);
3581 return status;
3585 * pull all token-sids from an LDAP dn
3586 * @param ads connection to ads server
3587 * @param mem_ctx TALLOC_CTX for allocating sid array
3588 * @param dn of LDAP object
3589 * @param user_sid pointer to struct dom_sid (objectSid)
3590 * @param primary_group_sid pointer to struct dom_sid (self composed)
3591 * @param sids pointer to sid array to allocate
3592 * @param num_sids counter of SIDs pulled
3593 * @return status of token query
3595 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3596 TALLOC_CTX *mem_ctx,
3597 const char *dn,
3598 struct dom_sid *user_sid,
3599 struct dom_sid *primary_group_sid,
3600 struct dom_sid **sids,
3601 size_t *num_sids)
3603 ADS_STATUS status;
3604 LDAPMessage *res = NULL;
3605 int count = 0;
3606 size_t tmp_num_sids;
3607 struct dom_sid *tmp_sids;
3608 struct dom_sid tmp_user_sid;
3609 struct dom_sid tmp_primary_group_sid;
3610 uint32 pgid;
3611 const char *attrs[] = {
3612 "objectSid",
3613 "tokenGroups",
3614 "primaryGroupID",
3615 NULL
3618 status = ads_search_retry_dn(ads, &res, dn, attrs);
3619 if (!ADS_ERR_OK(status)) {
3620 return status;
3623 count = ads_count_replies(ads, res);
3624 if (count != 1) {
3625 ads_msgfree(ads, res);
3626 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3629 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3630 ads_msgfree(ads, res);
3631 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3634 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3635 ads_msgfree(ads, res);
3636 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3640 /* hack to compose the primary group sid without knowing the
3641 * domsid */
3643 struct dom_sid domsid;
3644 uint32 dummy_rid;
3646 sid_copy(&domsid, &tmp_user_sid);
3648 if (!sid_split_rid(&domsid, &dummy_rid)) {
3649 ads_msgfree(ads, res);
3650 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3653 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3654 ads_msgfree(ads, res);
3655 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3659 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3661 if (tmp_num_sids == 0 || !tmp_sids) {
3662 ads_msgfree(ads, res);
3663 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3666 if (num_sids) {
3667 *num_sids = tmp_num_sids;
3670 if (sids) {
3671 *sids = tmp_sids;
3674 if (user_sid) {
3675 *user_sid = tmp_user_sid;
3678 if (primary_group_sid) {
3679 *primary_group_sid = tmp_primary_group_sid;
3682 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3684 ads_msgfree(ads, res);
3685 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3689 * Find a sAMAccoutName in LDAP
3690 * @param ads connection to ads server
3691 * @param mem_ctx TALLOC_CTX for allocating sid array
3692 * @param samaccountname to search
3693 * @param uac_ret uint32 pointer userAccountControl attribute value
3694 * @param dn_ret pointer to dn
3695 * @return status of token query
3697 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3698 TALLOC_CTX *mem_ctx,
3699 const char *samaccountname,
3700 uint32 *uac_ret,
3701 const char **dn_ret)
3703 ADS_STATUS status;
3704 const char *attrs[] = { "userAccountControl", NULL };
3705 const char *filter;
3706 LDAPMessage *res = NULL;
3707 char *dn = NULL;
3708 uint32 uac = 0;
3710 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3711 samaccountname);
3712 if (filter == NULL) {
3713 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3714 goto out;
3717 status = ads_do_search_all(ads, ads->config.bind_path,
3718 LDAP_SCOPE_SUBTREE,
3719 filter, attrs, &res);
3721 if (!ADS_ERR_OK(status)) {
3722 goto out;
3725 if (ads_count_replies(ads, res) != 1) {
3726 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3727 goto out;
3730 dn = ads_get_dn(ads, talloc_tos(), res);
3731 if (dn == NULL) {
3732 status = ADS_ERROR(LDAP_NO_MEMORY);
3733 goto out;
3736 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3737 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3738 goto out;
3741 if (uac_ret) {
3742 *uac_ret = uac;
3745 if (dn_ret) {
3746 *dn_ret = talloc_strdup(mem_ctx, dn);
3747 if (!*dn_ret) {
3748 status = ADS_ERROR(LDAP_NO_MEMORY);
3749 goto out;
3752 out:
3753 TALLOC_FREE(dn);
3754 ads_msgfree(ads, res);
3756 return status;
3760 * find our configuration path
3761 * @param ads connection to ads server
3762 * @param mem_ctx Pointer to talloc context
3763 * @param config_path Pointer to the config path
3764 * @return status of search
3766 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3767 TALLOC_CTX *mem_ctx,
3768 char **config_path)
3770 ADS_STATUS status;
3771 LDAPMessage *res = NULL;
3772 const char *config_context = NULL;
3773 const char *attrs[] = { "configurationNamingContext", NULL };
3775 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3776 "(objectclass=*)", attrs, &res);
3777 if (!ADS_ERR_OK(status)) {
3778 return status;
3781 config_context = ads_pull_string(ads, mem_ctx, res,
3782 "configurationNamingContext");
3783 ads_msgfree(ads, res);
3784 if (!config_context) {
3785 return ADS_ERROR(LDAP_NO_MEMORY);
3788 if (config_path) {
3789 *config_path = talloc_strdup(mem_ctx, config_context);
3790 if (!*config_path) {
3791 return ADS_ERROR(LDAP_NO_MEMORY);
3795 return ADS_ERROR(LDAP_SUCCESS);
3799 * find the displayName of an extended right
3800 * @param ads connection to ads server
3801 * @param config_path The config path
3802 * @param mem_ctx Pointer to talloc context
3803 * @param GUID struct of the rightsGUID
3804 * @return status of search
3806 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3807 const char *config_path,
3808 TALLOC_CTX *mem_ctx,
3809 const struct GUID *rights_guid)
3811 ADS_STATUS rc;
3812 LDAPMessage *res = NULL;
3813 char *expr = NULL;
3814 const char *attrs[] = { "displayName", NULL };
3815 const char *result = NULL;
3816 const char *path;
3818 if (!ads || !mem_ctx || !rights_guid) {
3819 goto done;
3822 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3823 GUID_string(mem_ctx, rights_guid));
3824 if (!expr) {
3825 goto done;
3828 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3829 if (!path) {
3830 goto done;
3833 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3834 expr, attrs, &res);
3835 if (!ADS_ERR_OK(rc)) {
3836 goto done;
3839 if (ads_count_replies(ads, res) != 1) {
3840 goto done;
3843 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3845 done:
3846 ads_msgfree(ads, res);
3847 return result;
3851 * verify or build and verify an account ou
3852 * @param mem_ctx Pointer to talloc context
3853 * @param ads connection to ads server
3854 * @param account_ou
3855 * @return status of search
3858 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3859 ADS_STRUCT *ads,
3860 const char **account_ou)
3862 char **exploded_dn;
3863 const char *name;
3864 char *ou_string;
3866 exploded_dn = ldap_explode_dn(*account_ou, 0);
3867 if (exploded_dn) {
3868 ldap_value_free(exploded_dn);
3869 return ADS_SUCCESS;
3872 ou_string = ads_ou_string(ads, *account_ou);
3873 if (!ou_string) {
3874 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3877 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3878 ads->config.bind_path);
3879 SAFE_FREE(ou_string);
3881 if (!name) {
3882 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3885 exploded_dn = ldap_explode_dn(name, 0);
3886 if (!exploded_dn) {
3887 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3889 ldap_value_free(exploded_dn);
3891 *account_ou = name;
3892 return ADS_SUCCESS;
3895 #endif