s4:libcli/smb2: setup a smbXcli_tcon for each smb2_tree
[Samba/gebeck_regimport.git] / source3 / libads / ldap.c
blob640a020a8bdbbe12cd638acfed73fdff79706e72
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "ads.h"
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/addns/dnsquery.h"
29 #include "../libds/common/flags.h"
30 #include "smbldap.h"
31 #include "../libcli/security/security.h"
32 #include "lib/param/loadparm.h"
34 #ifdef HAVE_LDAP
36 /**
37 * @file ldap.c
38 * @brief basic ldap client-side routines for ads server communications
40 * The routines contained here should do the necessary ldap calls for
41 * ads setups.
43 * Important note: attribute names passed into ads_ routines must
44 * already be in UTF-8 format. We do not convert them because in almost
45 * all cases, they are just ascii (which is represented with the same
46 * codepoints in UTF-8). This may have to change at some point
47 **/
50 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
52 static SIG_ATOMIC_T gotalarm;
54 /***************************************************************
55 Signal function to tell us we timed out.
56 ****************************************************************/
58 static void gotalarm_sig(int signum)
60 gotalarm = 1;
63 LDAP *ldap_open_with_timeout(const char *server,
64 struct sockaddr_storage *ss,
65 int port, unsigned int to)
67 LDAP *ldp = NULL;
69 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
70 "%u seconds\n", server, port, to));
72 #if defined(HAVE_LDAP_INIT_FD) && defined(SOCKET_WRAPPER)
73 /* Only use this private LDAP function if we are in make test,
74 * as this is the best way to get the emulated TCP socket into
75 * OpenLDAP */
76 if (socket_wrapper_dir() != NULL) {
77 int fd, ldap_err;
78 NTSTATUS status;
79 char *uri;
81 status = open_socket_out(ss, port, to, &fd);
83 if (!NT_STATUS_IS_OK(status)) {
84 return NULL;
87 #ifndef LDAP_PROTO_TCP
88 #define LDAP_PROTO_TCP 1
89 #endif
90 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
91 if (uri == NULL) {
92 return NULL;
94 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
95 talloc_free(uri);
97 if (ldap_err != LDAP_SUCCESS) {
98 return NULL;
100 return ldp;
102 #endif
104 if (to) {
105 /* Setup timeout */
106 gotalarm = 0;
107 CatchSignal(SIGALRM, gotalarm_sig);
108 alarm(to);
109 /* End setup timeout. */
112 ldp = ldap_open(server, port);
114 if (ldp == NULL) {
115 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
116 server, port, strerror(errno)));
117 } else {
118 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
121 if (to) {
122 /* Teardown timeout. */
123 alarm(0);
124 CatchSignal(SIGALRM, SIG_IGN);
127 return ldp;
130 static int ldap_search_with_timeout(LDAP *ld,
131 LDAP_CONST char *base,
132 int scope,
133 LDAP_CONST char *filter,
134 char **attrs,
135 int attrsonly,
136 LDAPControl **sctrls,
137 LDAPControl **cctrls,
138 int sizelimit,
139 LDAPMessage **res )
141 int to = lp_ldap_timeout();
142 struct timeval timeout;
143 struct timeval *timeout_ptr = NULL;
144 int result;
146 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
147 gotalarm = 0;
149 if (to) {
150 timeout.tv_sec = to;
151 timeout.tv_usec = 0;
152 timeout_ptr = &timeout;
154 /* Setup alarm timeout. */
155 CatchSignal(SIGALRM, gotalarm_sig);
156 /* Make the alarm time one second beyond
157 the timout we're setting for the
158 remote search timeout, to allow that
159 to fire in preference. */
160 alarm(to+1);
161 /* End setup timeout. */
165 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
166 attrsonly, sctrls, cctrls, timeout_ptr,
167 sizelimit, res);
169 if (to) {
170 /* Teardown alarm timeout. */
171 CatchSignal(SIGALRM, SIG_IGN);
172 alarm(0);
175 if (gotalarm != 0)
176 return LDAP_TIMELIMIT_EXCEEDED;
179 * A bug in OpenLDAP means ldap_search_ext_s can return
180 * LDAP_SUCCESS but with a NULL res pointer. Cope with
181 * this. See bug #6279 for details. JRA.
184 if (*res == NULL) {
185 return LDAP_TIMELIMIT_EXCEEDED;
188 return result;
191 /**********************************************
192 Do client and server sitename match ?
193 **********************************************/
195 bool ads_sitename_match(ADS_STRUCT *ads)
197 if (ads->config.server_site_name == NULL &&
198 ads->config.client_site_name == NULL ) {
199 DEBUG(10,("ads_sitename_match: both null\n"));
200 return True;
202 if (ads->config.server_site_name &&
203 ads->config.client_site_name &&
204 strequal(ads->config.server_site_name,
205 ads->config.client_site_name)) {
206 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
207 return True;
209 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
210 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
211 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
212 return False;
215 /**********************************************
216 Is this the closest DC ?
217 **********************************************/
219 bool ads_closest_dc(ADS_STRUCT *ads)
221 if (ads->config.flags & NBT_SERVER_CLOSEST) {
222 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
223 return True;
226 /* not sure if this can ever happen */
227 if (ads_sitename_match(ads)) {
228 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
229 return True;
232 if (ads->config.client_site_name == NULL) {
233 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
234 return True;
237 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
238 ads->config.ldap_server_name));
240 return False;
245 try a connection to a given ldap server, returning True and setting the servers IP
246 in the ads struct if successful
248 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
250 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
251 TALLOC_CTX *frame = talloc_stackframe();
252 bool ret = false;
253 struct sockaddr_storage ss;
254 char addr[INET6_ADDRSTRLEN];
256 if (!server || !*server) {
257 TALLOC_FREE(frame);
258 return False;
261 if (!resolve_name(server, &ss, 0x20, true)) {
262 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
263 server ));
264 TALLOC_FREE(frame);
265 return false;
267 print_sockaddr(addr, sizeof(addr), &ss);
269 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
270 addr, ads->server.realm));
272 ZERO_STRUCT( cldap_reply );
274 if ( !ads_cldap_netlogon_5(frame, &ss, ads->server.realm, &cldap_reply ) ) {
275 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
276 ret = false;
277 goto out;
280 /* Check the CLDAP reply flags */
282 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
283 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
284 addr));
285 ret = false;
286 goto out;
289 /* Fill in the ads->config values */
291 SAFE_FREE(ads->config.realm);
292 SAFE_FREE(ads->config.bind_path);
293 SAFE_FREE(ads->config.ldap_server_name);
294 SAFE_FREE(ads->config.server_site_name);
295 SAFE_FREE(ads->config.client_site_name);
296 SAFE_FREE(ads->server.workgroup);
298 ads->config.flags = cldap_reply.server_type;
299 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
300 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
301 strupper_m(ads->config.realm);
302 ads->config.bind_path = ads_build_dn(ads->config.realm);
303 if (*cldap_reply.server_site) {
304 ads->config.server_site_name =
305 SMB_STRDUP(cldap_reply.server_site);
307 if (*cldap_reply.client_site) {
308 ads->config.client_site_name =
309 SMB_STRDUP(cldap_reply.client_site);
311 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
313 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
314 ads->ldap.ss = ss;
316 /* Store our site name. */
317 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
318 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
320 ret = true;
322 out:
324 TALLOC_FREE(frame);
325 return ret;
328 /**********************************************************************
329 Try to find an AD dc using our internal name resolution routines
330 Try the realm first and then then workgroup name if netbios is not
331 disabled
332 **********************************************************************/
334 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
336 const char *c_domain;
337 const char *c_realm;
338 int count, i=0;
339 struct ip_service *ip_list;
340 const char *realm;
341 const char *domain;
342 bool got_realm = False;
343 bool use_own_domain = False;
344 char *sitename;
345 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
347 /* if the realm and workgroup are both empty, assume they are ours */
349 /* realm */
350 c_realm = ads->server.realm;
352 if ( !c_realm || !*c_realm ) {
353 /* special case where no realm and no workgroup means our own */
354 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
355 use_own_domain = True;
356 c_realm = lp_realm();
360 if (c_realm && *c_realm)
361 got_realm = True;
363 /* we need to try once with the realm name and fallback to the
364 netbios domain name if we fail (if netbios has not been disabled */
366 if ( !got_realm && !lp_disable_netbios() ) {
367 c_realm = ads->server.workgroup;
368 if (!c_realm || !*c_realm) {
369 if ( use_own_domain )
370 c_realm = lp_workgroup();
374 if ( !c_realm || !*c_realm ) {
375 DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know "
376 "what to do\n"));
377 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
380 if ( use_own_domain ) {
381 c_domain = lp_workgroup();
382 } else {
383 c_domain = ads->server.workgroup;
386 realm = c_realm;
387 domain = c_domain;
390 * In case of LDAP we use get_dc_name() as that
391 * creates the custom krb5.conf file
393 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
394 fstring srv_name;
395 struct sockaddr_storage ip_out;
397 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
398 (got_realm ? "realm" : "domain"), realm));
400 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
402 * we call ads_try_connect() to fill in the
403 * ads->config details
405 if (ads_try_connect(ads, srv_name, false)) {
406 return NT_STATUS_OK;
410 return NT_STATUS_NO_LOGON_SERVERS;
413 sitename = sitename_fetch(realm);
415 again:
417 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
418 (got_realm ? "realm" : "domain"), realm));
420 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
421 if (!NT_STATUS_IS_OK(status)) {
422 /* fall back to netbios if we can */
423 if ( got_realm && !lp_disable_netbios() ) {
424 got_realm = False;
425 goto again;
428 SAFE_FREE(sitename);
429 return status;
432 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
433 for ( i=0; i<count; i++ ) {
434 char server[INET6_ADDRSTRLEN];
436 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
438 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
439 continue;
441 if (!got_realm) {
442 /* realm in this case is a workgroup name. We need
443 to ignore any IP addresses in the negative connection
444 cache that match ip addresses returned in the ad realm
445 case. It sucks that I have to reproduce the logic above... */
446 c_realm = ads->server.realm;
447 if ( !c_realm || !*c_realm ) {
448 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
449 c_realm = lp_realm();
452 if (c_realm && *c_realm &&
453 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
454 /* Ensure we add the workgroup name for this
455 IP address as negative too. */
456 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
457 continue;
461 if ( ads_try_connect(ads, server, false) ) {
462 SAFE_FREE(ip_list);
463 SAFE_FREE(sitename);
464 return NT_STATUS_OK;
467 /* keep track of failures */
468 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
471 SAFE_FREE(ip_list);
473 /* In case we failed to contact one of our closest DC on our site we
474 * need to try to find another DC, retry with a site-less SRV DNS query
475 * - Guenther */
477 if (sitename) {
478 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
479 "trying to find another DC\n", sitename));
480 SAFE_FREE(sitename);
481 namecache_delete(realm, 0x1C);
482 goto again;
485 return NT_STATUS_NO_LOGON_SERVERS;
488 /*********************************************************************
489 *********************************************************************/
491 static NTSTATUS ads_lookup_site(void)
493 ADS_STRUCT *ads = NULL;
494 ADS_STATUS ads_status;
495 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
497 ads = ads_init(lp_realm(), NULL, NULL);
498 if (!ads) {
499 return NT_STATUS_NO_MEMORY;
502 /* The NO_BIND here will find a DC and set the client site
503 but not establish the TCP connection */
505 ads->auth.flags = ADS_AUTH_NO_BIND;
506 ads_status = ads_connect(ads);
507 if (!ADS_ERR_OK(ads_status)) {
508 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
509 ads_errstr(ads_status)));
511 nt_status = ads_ntstatus(ads_status);
513 if (ads) {
514 ads_destroy(&ads);
517 return nt_status;
520 /*********************************************************************
521 *********************************************************************/
523 static const char* host_dns_domain(const char *fqdn)
525 const char *p = fqdn;
527 /* go to next char following '.' */
529 if ((p = strchr_m(fqdn, '.')) != NULL) {
530 p++;
533 return p;
538 * Connect to the Global Catalog server
539 * @param ads Pointer to an existing ADS_STRUCT
540 * @return status of connection
542 * Simple wrapper around ads_connect() that fills in the
543 * GC ldap server information
546 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
548 TALLOC_CTX *frame = talloc_stackframe();
549 struct dns_rr_srv *gcs_list;
550 int num_gcs;
551 const char *realm = ads->server.realm;
552 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
553 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
554 int i;
555 bool done = false;
556 char *sitename = NULL;
557 const char *dns_hosts_file;
559 if (!realm)
560 realm = lp_realm();
562 if ((sitename = sitename_fetch(realm)) == NULL) {
563 ads_lookup_site();
564 sitename = sitename_fetch(realm);
567 dns_hosts_file = lp_parm_const_string(-1, "resolv", "host file", NULL);
568 do {
569 /* We try once with a sitename and once without
570 (unless we don't have a sitename and then we're
571 done */
573 if (sitename == NULL)
574 done = true;
576 nt_status = ads_dns_query_gcs(frame, dns_hosts_file,
577 realm, sitename,
578 &gcs_list, &num_gcs);
580 SAFE_FREE(sitename);
582 if (!NT_STATUS_IS_OK(nt_status)) {
583 ads_status = ADS_ERROR_NT(nt_status);
584 goto done;
587 /* Loop until we get a successful connection or have gone
588 through them all. When connecting a GC server, make sure that
589 the realm is the server's DNS name and not the forest root */
591 for (i=0; i<num_gcs; i++) {
592 ads->server.gc = true;
593 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
594 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
595 ads_status = ads_connect(ads);
596 if (ADS_ERR_OK(ads_status)) {
597 /* Reset the bind_dn to "". A Global Catalog server
598 may host multiple domain trees in a forest.
599 Windows 2003 GC server will accept "" as the search
600 path to imply search all domain trees in the forest */
602 SAFE_FREE(ads->config.bind_path);
603 ads->config.bind_path = SMB_STRDUP("");
606 goto done;
608 SAFE_FREE(ads->server.ldap_server);
609 SAFE_FREE(ads->server.realm);
612 TALLOC_FREE(gcs_list);
613 num_gcs = 0;
614 } while (!done);
616 done:
617 SAFE_FREE(sitename);
618 talloc_destroy(frame);
620 return ads_status;
625 * Connect to the LDAP server
626 * @param ads Pointer to an existing ADS_STRUCT
627 * @return status of connection
629 ADS_STATUS ads_connect(ADS_STRUCT *ads)
631 int version = LDAP_VERSION3;
632 ADS_STATUS status;
633 NTSTATUS ntstatus;
634 char addr[INET6_ADDRSTRLEN];
636 ZERO_STRUCT(ads->ldap);
637 ads->ldap.last_attempt = time_mono(NULL);
638 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
640 /* try with a user specified server */
642 if (DEBUGLEVEL >= 11) {
643 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
644 DEBUG(11,("ads_connect: entering\n"));
645 DEBUGADD(11,("%s\n", s));
646 TALLOC_FREE(s);
649 if (ads->server.ldap_server)
651 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
652 goto got_connection;
655 /* The choice of which GC use is handled one level up in
656 ads_connect_gc(). If we continue on from here with
657 ads_find_dc() we will get GC searches on port 389 which
658 doesn't work. --jerry */
660 if (ads->server.gc == true) {
661 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
665 ntstatus = ads_find_dc(ads);
666 if (NT_STATUS_IS_OK(ntstatus)) {
667 goto got_connection;
670 status = ADS_ERROR_NT(ntstatus);
671 goto out;
673 got_connection:
675 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
676 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
678 if (!ads->auth.user_name) {
679 /* Must use the userPrincipalName value here or sAMAccountName
680 and not servicePrincipalName; found by Guenther Deschner */
682 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
683 DEBUG(0,("ads_connect: asprintf fail.\n"));
684 ads->auth.user_name = NULL;
688 if (!ads->auth.realm) {
689 ads->auth.realm = SMB_STRDUP(ads->config.realm);
692 if (!ads->auth.kdc_server) {
693 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
694 ads->auth.kdc_server = SMB_STRDUP(addr);
697 /* If the caller() requested no LDAP bind, then we are done */
699 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
700 status = ADS_SUCCESS;
701 goto out;
704 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
705 if (!ads->ldap.mem_ctx) {
706 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
707 goto out;
710 /* Otherwise setup the TCP LDAP session */
712 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
713 &ads->ldap.ss,
714 ads->ldap.port, lp_ldap_timeout());
715 if (ads->ldap.ld == NULL) {
716 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
717 goto out;
719 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
721 /* cache the successful connection for workgroup and realm */
722 if (ads_closest_dc(ads)) {
723 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
724 saf_store( ads->server.realm, ads->config.ldap_server_name);
727 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
729 if ( lp_ldap_ssl_ads() ) {
730 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
731 if (!ADS_ERR_OK(status)) {
732 goto out;
736 /* fill in the current time and offsets */
738 status = ads_current_time( ads );
739 if ( !ADS_ERR_OK(status) ) {
740 goto out;
743 /* Now do the bind */
745 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
746 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
747 goto out;
750 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
751 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
752 goto out;
755 status = ads_sasl_bind(ads);
757 out:
758 if (DEBUGLEVEL >= 11) {
759 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
760 DEBUG(11,("ads_connect: leaving with: %s\n",
761 ads_errstr(status)));
762 DEBUGADD(11,("%s\n", s));
763 TALLOC_FREE(s);
766 return status;
770 * Connect to the LDAP server using given credentials
771 * @param ads Pointer to an existing ADS_STRUCT
772 * @return status of connection
774 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
776 ads->auth.flags |= ADS_AUTH_USER_CREDS;
778 return ads_connect(ads);
782 * Disconnect the LDAP server
783 * @param ads Pointer to an existing ADS_STRUCT
785 void ads_disconnect(ADS_STRUCT *ads)
787 if (ads->ldap.ld) {
788 ldap_unbind(ads->ldap.ld);
789 ads->ldap.ld = NULL;
791 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
792 ads->ldap.wrap_ops->disconnect(ads);
794 if (ads->ldap.mem_ctx) {
795 talloc_free(ads->ldap.mem_ctx);
797 ZERO_STRUCT(ads->ldap);
801 Duplicate a struct berval into talloc'ed memory
803 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
805 struct berval *value;
807 if (!in_val) return NULL;
809 value = talloc_zero(ctx, struct berval);
810 if (value == NULL)
811 return NULL;
812 if (in_val->bv_len == 0) return value;
814 value->bv_len = in_val->bv_len;
815 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
816 in_val->bv_len);
817 return value;
821 Make a values list out of an array of (struct berval *)
823 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
824 const struct berval **in_vals)
826 struct berval **values;
827 int i;
829 if (!in_vals) return NULL;
830 for (i=0; in_vals[i]; i++)
831 ; /* count values */
832 values = talloc_zero_array(ctx, struct berval *, i+1);
833 if (!values) return NULL;
835 for (i=0; in_vals[i]; i++) {
836 values[i] = dup_berval(ctx, in_vals[i]);
838 return values;
842 UTF8-encode a values list out of an array of (char *)
844 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
846 char **values;
847 int i;
848 size_t size;
850 if (!in_vals) return NULL;
851 for (i=0; in_vals[i]; i++)
852 ; /* count values */
853 values = talloc_zero_array(ctx, char *, i+1);
854 if (!values) return NULL;
856 for (i=0; in_vals[i]; i++) {
857 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
858 TALLOC_FREE(values);
859 return NULL;
862 return values;
866 Pull a (char *) array out of a UTF8-encoded values list
868 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
870 char **values;
871 int i;
872 size_t converted_size;
874 if (!in_vals) return NULL;
875 for (i=0; in_vals[i]; i++)
876 ; /* count values */
877 values = talloc_zero_array(ctx, char *, i+1);
878 if (!values) return NULL;
880 for (i=0; in_vals[i]; i++) {
881 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
882 &converted_size)) {
883 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
884 "%s", strerror(errno)));
887 return values;
891 * Do a search with paged results. cookie must be null on the first
892 * call, and then returned on each subsequent call. It will be null
893 * again when the entire search is complete
894 * @param ads connection to ads server
895 * @param bind_path Base dn for the search
896 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
897 * @param expr Search expression - specified in local charset
898 * @param attrs Attributes to retrieve - specified in utf8 or ascii
899 * @param res ** which will contain results - free res* with ads_msgfree()
900 * @param count Number of entries retrieved on this page
901 * @param cookie The paged results cookie to be returned on subsequent calls
902 * @return status of search
904 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
905 const char *bind_path,
906 int scope, const char *expr,
907 const char **attrs, void *args,
908 LDAPMessage **res,
909 int *count, struct berval **cookie)
911 int rc, i, version;
912 char *utf8_expr, *utf8_path, **search_attrs = NULL;
913 size_t converted_size;
914 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
915 BerElement *cookie_be = NULL;
916 struct berval *cookie_bv= NULL;
917 BerElement *ext_be = NULL;
918 struct berval *ext_bv= NULL;
920 TALLOC_CTX *ctx;
921 ads_control *external_control = (ads_control *) args;
923 *res = NULL;
925 if (!(ctx = talloc_init("ads_do_paged_search_args")))
926 return ADS_ERROR(LDAP_NO_MEMORY);
928 /* 0 means the conversion worked but the result was empty
929 so we only fail if it's -1. In any case, it always
930 at least nulls out the dest */
931 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
932 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
934 rc = LDAP_NO_MEMORY;
935 goto done;
938 if (!attrs || !(*attrs))
939 search_attrs = NULL;
940 else {
941 /* This would be the utf8-encoded version...*/
942 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
943 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
944 rc = LDAP_NO_MEMORY;
945 goto done;
949 /* Paged results only available on ldap v3 or later */
950 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
951 if (version < LDAP_VERSION3) {
952 rc = LDAP_NOT_SUPPORTED;
953 goto done;
956 cookie_be = ber_alloc_t(LBER_USE_DER);
957 if (*cookie) {
958 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
959 ber_bvfree(*cookie); /* don't need it from last time */
960 *cookie = NULL;
961 } else {
962 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
964 ber_flatten(cookie_be, &cookie_bv);
965 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
966 PagedResults.ldctl_iscritical = (char) 1;
967 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
968 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
970 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
971 NoReferrals.ldctl_iscritical = (char) 0;
972 NoReferrals.ldctl_value.bv_len = 0;
973 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
975 if (external_control &&
976 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
977 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
979 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
980 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
982 /* win2k does not accept a ldctl_value beeing passed in */
984 if (external_control->val != 0) {
986 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
987 rc = LDAP_NO_MEMORY;
988 goto done;
991 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
992 rc = LDAP_NO_MEMORY;
993 goto done;
995 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
996 rc = LDAP_NO_MEMORY;
997 goto done;
1000 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1001 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1003 } else {
1004 ExternalCtrl.ldctl_value.bv_len = 0;
1005 ExternalCtrl.ldctl_value.bv_val = NULL;
1008 controls[0] = &NoReferrals;
1009 controls[1] = &PagedResults;
1010 controls[2] = &ExternalCtrl;
1011 controls[3] = NULL;
1013 } else {
1014 controls[0] = &NoReferrals;
1015 controls[1] = &PagedResults;
1016 controls[2] = NULL;
1019 /* we need to disable referrals as the openldap libs don't
1020 handle them and paged results at the same time. Using them
1021 together results in the result record containing the server
1022 page control being removed from the result list (tridge/jmcd)
1024 leaving this in despite the control that says don't generate
1025 referrals, in case the server doesn't support it (jmcd)
1027 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1029 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1030 search_attrs, 0, controls,
1031 NULL, LDAP_NO_LIMIT,
1032 (LDAPMessage **)res);
1034 ber_free(cookie_be, 1);
1035 ber_bvfree(cookie_bv);
1037 if (rc) {
1038 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1039 ldap_err2string(rc)));
1040 goto done;
1043 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1044 NULL, &rcontrols, 0);
1046 if (!rcontrols) {
1047 goto done;
1050 for (i=0; rcontrols[i]; i++) {
1051 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1052 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1053 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1054 &cookie_bv);
1055 /* the berval is the cookie, but must be freed when
1056 it is all done */
1057 if (cookie_bv->bv_len) /* still more to do */
1058 *cookie=ber_bvdup(cookie_bv);
1059 else
1060 *cookie=NULL;
1061 ber_bvfree(cookie_bv);
1062 ber_free(cookie_be, 1);
1063 break;
1066 ldap_controls_free(rcontrols);
1068 done:
1069 talloc_destroy(ctx);
1071 if (ext_be) {
1072 ber_free(ext_be, 1);
1075 if (ext_bv) {
1076 ber_bvfree(ext_bv);
1079 /* if/when we decide to utf8-encode attrs, take out this next line */
1080 TALLOC_FREE(search_attrs);
1082 return ADS_ERROR(rc);
1085 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1086 int scope, const char *expr,
1087 const char **attrs, LDAPMessage **res,
1088 int *count, struct berval **cookie)
1090 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1095 * Get all results for a search. This uses ads_do_paged_search() to return
1096 * all entries in a large search.
1097 * @param ads connection to ads server
1098 * @param bind_path Base dn for the search
1099 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1100 * @param expr Search expression
1101 * @param attrs Attributes to retrieve
1102 * @param res ** which will contain results - free res* with ads_msgfree()
1103 * @return status of search
1105 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1106 int scope, const char *expr,
1107 const char **attrs, void *args,
1108 LDAPMessage **res)
1110 struct berval *cookie = NULL;
1111 int count = 0;
1112 ADS_STATUS status;
1114 *res = NULL;
1115 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1116 &count, &cookie);
1118 if (!ADS_ERR_OK(status))
1119 return status;
1121 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1122 while (cookie) {
1123 LDAPMessage *res2 = NULL;
1124 ADS_STATUS status2;
1125 LDAPMessage *msg, *next;
1127 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1128 attrs, args, &res2, &count, &cookie);
1130 if (!ADS_ERR_OK(status2)) break;
1132 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1133 that this works on all ldap libs, but I have only tested with openldap */
1134 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1135 next = ads_next_message(ads, msg);
1136 ldap_add_result_entry((LDAPMessage **)res, msg);
1138 /* note that we do not free res2, as the memory is now
1139 part of the main returned list */
1141 #else
1142 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1143 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1144 #endif
1146 return status;
1149 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1150 int scope, const char *expr,
1151 const char **attrs, LDAPMessage **res)
1153 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1156 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1157 int scope, const char *expr,
1158 const char **attrs, uint32 sd_flags,
1159 LDAPMessage **res)
1161 ads_control args;
1163 args.control = ADS_SD_FLAGS_OID;
1164 args.val = sd_flags;
1165 args.critical = True;
1167 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1172 * Run a function on all results for a search. Uses ads_do_paged_search() and
1173 * runs the function as each page is returned, using ads_process_results()
1174 * @param ads connection to ads server
1175 * @param bind_path Base dn for the search
1176 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1177 * @param expr Search expression - specified in local charset
1178 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1179 * @param fn Function which takes attr name, values list, and data_area
1180 * @param data_area Pointer which is passed to function on each call
1181 * @return status of search
1183 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1184 int scope, const char *expr, const char **attrs,
1185 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1186 void *data_area)
1188 struct berval *cookie = NULL;
1189 int count = 0;
1190 ADS_STATUS status;
1191 LDAPMessage *res;
1193 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1194 &count, &cookie);
1196 if (!ADS_ERR_OK(status)) return status;
1198 ads_process_results(ads, res, fn, data_area);
1199 ads_msgfree(ads, res);
1201 while (cookie) {
1202 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1203 &res, &count, &cookie);
1205 if (!ADS_ERR_OK(status)) break;
1207 ads_process_results(ads, res, fn, data_area);
1208 ads_msgfree(ads, res);
1211 return status;
1215 * Do a search with a timeout.
1216 * @param ads connection to ads server
1217 * @param bind_path Base dn for the search
1218 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1219 * @param expr Search expression
1220 * @param attrs Attributes to retrieve
1221 * @param res ** which will contain results - free res* with ads_msgfree()
1222 * @return status of search
1224 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1225 const char *expr,
1226 const char **attrs, LDAPMessage **res)
1228 int rc;
1229 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1230 size_t converted_size;
1231 TALLOC_CTX *ctx;
1233 *res = NULL;
1234 if (!(ctx = talloc_init("ads_do_search"))) {
1235 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1236 return ADS_ERROR(LDAP_NO_MEMORY);
1239 /* 0 means the conversion worked but the result was empty
1240 so we only fail if it's negative. In any case, it always
1241 at least nulls out the dest */
1242 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1243 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1245 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1246 rc = LDAP_NO_MEMORY;
1247 goto done;
1250 if (!attrs || !(*attrs))
1251 search_attrs = NULL;
1252 else {
1253 /* This would be the utf8-encoded version...*/
1254 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1255 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1257 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1258 rc = LDAP_NO_MEMORY;
1259 goto done;
1263 /* see the note in ads_do_paged_search - we *must* disable referrals */
1264 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1266 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1267 search_attrs, 0, NULL, NULL,
1268 LDAP_NO_LIMIT,
1269 (LDAPMessage **)res);
1271 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1272 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1273 rc = 0;
1276 done:
1277 talloc_destroy(ctx);
1278 /* if/when we decide to utf8-encode attrs, take out this next line */
1279 TALLOC_FREE(search_attrs);
1280 return ADS_ERROR(rc);
1283 * Do a general ADS search
1284 * @param ads connection to ads server
1285 * @param res ** which will contain results - free res* with ads_msgfree()
1286 * @param expr Search expression
1287 * @param attrs Attributes to retrieve
1288 * @return status of search
1290 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1291 const char *expr, const char **attrs)
1293 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1294 expr, attrs, res);
1298 * Do a search on a specific DistinguishedName
1299 * @param ads connection to ads server
1300 * @param res ** which will contain results - free res* with ads_msgfree()
1301 * @param dn DistinguishName to search
1302 * @param attrs Attributes to retrieve
1303 * @return status of search
1305 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1306 const char *dn, const char **attrs)
1308 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1309 attrs, res);
1313 * Free up memory from a ads_search
1314 * @param ads connection to ads server
1315 * @param msg Search results to free
1317 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1319 if (!msg) return;
1320 ldap_msgfree(msg);
1324 * Get a dn from search results
1325 * @param ads connection to ads server
1326 * @param msg Search result
1327 * @return dn string
1329 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1331 char *utf8_dn, *unix_dn;
1332 size_t converted_size;
1334 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1336 if (!utf8_dn) {
1337 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1338 return NULL;
1341 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1342 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1343 utf8_dn ));
1344 return NULL;
1346 ldap_memfree(utf8_dn);
1347 return unix_dn;
1351 * Get the parent from a dn
1352 * @param dn the dn to return the parent from
1353 * @return parent dn string
1355 char *ads_parent_dn(const char *dn)
1357 char *p;
1359 if (dn == NULL) {
1360 return NULL;
1363 p = strchr(dn, ',');
1365 if (p == NULL) {
1366 return NULL;
1369 return p+1;
1373 * Find a machine account given a hostname
1374 * @param ads connection to ads server
1375 * @param res ** which will contain results - free res* with ads_msgfree()
1376 * @param host Hostname to search for
1377 * @return status of search
1379 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1380 const char *machine)
1382 ADS_STATUS status;
1383 char *expr;
1384 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1386 *res = NULL;
1388 /* the easiest way to find a machine account anywhere in the tree
1389 is to look for hostname$ */
1390 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1391 DEBUG(1, ("asprintf failed!\n"));
1392 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1395 status = ads_search(ads, res, expr, attrs);
1396 SAFE_FREE(expr);
1397 return status;
1401 * Initialize a list of mods to be used in a modify request
1402 * @param ctx An initialized TALLOC_CTX
1403 * @return allocated ADS_MODLIST
1405 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1407 #define ADS_MODLIST_ALLOC_SIZE 10
1408 LDAPMod **mods;
1410 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1411 /* -1 is safety to make sure we don't go over the end.
1412 need to reset it to NULL before doing ldap modify */
1413 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1415 return (ADS_MODLIST)mods;
1420 add an attribute to the list, with values list already constructed
1422 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1423 int mod_op, const char *name,
1424 const void *_invals)
1426 const void **invals = (const void **)_invals;
1427 int curmod;
1428 LDAPMod **modlist = (LDAPMod **) *mods;
1429 struct berval **ber_values = NULL;
1430 char **char_values = NULL;
1432 if (!invals) {
1433 mod_op = LDAP_MOD_DELETE;
1434 } else {
1435 if (mod_op & LDAP_MOD_BVALUES)
1436 ber_values = ads_dup_values(ctx,
1437 (const struct berval **)invals);
1438 else
1439 char_values = ads_push_strvals(ctx,
1440 (const char **) invals);
1443 /* find the first empty slot */
1444 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1445 curmod++);
1446 if (modlist[curmod] == (LDAPMod *) -1) {
1447 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1448 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1449 return ADS_ERROR(LDAP_NO_MEMORY);
1450 memset(&modlist[curmod], 0,
1451 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1452 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1453 *mods = (ADS_MODLIST)modlist;
1456 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1457 return ADS_ERROR(LDAP_NO_MEMORY);
1458 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1459 if (mod_op & LDAP_MOD_BVALUES) {
1460 modlist[curmod]->mod_bvalues = ber_values;
1461 } else if (mod_op & LDAP_MOD_DELETE) {
1462 modlist[curmod]->mod_values = NULL;
1463 } else {
1464 modlist[curmod]->mod_values = char_values;
1467 modlist[curmod]->mod_op = mod_op;
1468 return ADS_ERROR(LDAP_SUCCESS);
1472 * Add a single string value to a mod list
1473 * @param ctx An initialized TALLOC_CTX
1474 * @param mods An initialized ADS_MODLIST
1475 * @param name The attribute name to add
1476 * @param val The value to add - NULL means DELETE
1477 * @return ADS STATUS indicating success of add
1479 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1480 const char *name, const char *val)
1482 const char *values[2];
1484 values[0] = val;
1485 values[1] = NULL;
1487 if (!val)
1488 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1489 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1493 * Add an array of string values to a mod list
1494 * @param ctx An initialized TALLOC_CTX
1495 * @param mods An initialized ADS_MODLIST
1496 * @param name The attribute name to add
1497 * @param vals The array of string values to add - NULL means DELETE
1498 * @return ADS STATUS indicating success of add
1500 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1501 const char *name, const char **vals)
1503 if (!vals)
1504 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1505 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1506 name, (const void **) vals);
1509 #if 0
1511 * Add a single ber-encoded value to a mod list
1512 * @param ctx An initialized TALLOC_CTX
1513 * @param mods An initialized ADS_MODLIST
1514 * @param name The attribute name to add
1515 * @param val The value to add - NULL means DELETE
1516 * @return ADS STATUS indicating success of add
1518 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1519 const char *name, const struct berval *val)
1521 const struct berval *values[2];
1523 values[0] = val;
1524 values[1] = NULL;
1525 if (!val)
1526 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1527 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1528 name, (const void **) values);
1530 #endif
1533 * Perform an ldap modify
1534 * @param ads connection to ads server
1535 * @param mod_dn DistinguishedName to modify
1536 * @param mods list of modifications to perform
1537 * @return status of modify
1539 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1541 int ret,i;
1542 char *utf8_dn = NULL;
1543 size_t converted_size;
1545 this control is needed to modify that contains a currently
1546 non-existent attribute (but allowable for the object) to run
1548 LDAPControl PermitModify = {
1549 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1550 {0, NULL},
1551 (char) 1};
1552 LDAPControl *controls[2];
1554 controls[0] = &PermitModify;
1555 controls[1] = NULL;
1557 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
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;
1565 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1566 (LDAPMod **) mods, controls, NULL);
1567 TALLOC_FREE(utf8_dn);
1568 return ADS_ERROR(ret);
1572 * Perform an ldap add
1573 * @param ads connection to ads server
1574 * @param new_dn DistinguishedName to add
1575 * @param mods list of attributes and values for DN
1576 * @return status of add
1578 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1580 int ret, i;
1581 char *utf8_dn = NULL;
1582 size_t converted_size;
1584 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1585 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1586 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1589 /* find the end of the list, marked by NULL or -1 */
1590 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1591 /* make sure the end of the list is NULL */
1592 mods[i] = NULL;
1594 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1595 TALLOC_FREE(utf8_dn);
1596 return ADS_ERROR(ret);
1600 * Delete a DistinguishedName
1601 * @param ads connection to ads server
1602 * @param new_dn DistinguishedName to delete
1603 * @return status of delete
1605 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1607 int ret;
1608 char *utf8_dn = NULL;
1609 size_t converted_size;
1610 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1611 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1612 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1615 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1616 TALLOC_FREE(utf8_dn);
1617 return ADS_ERROR(ret);
1621 * Build an org unit string
1622 * if org unit is Computers or blank then assume a container, otherwise
1623 * assume a / separated list of organisational units.
1624 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1625 * @param ads connection to ads server
1626 * @param org_unit Organizational unit
1627 * @return org unit string - caller must free
1629 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1631 char *ret = NULL;
1633 if (!org_unit || !*org_unit) {
1635 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1637 /* samba4 might not yet respond to a wellknownobject-query */
1638 return ret ? ret : SMB_STRDUP("cn=Computers");
1641 if (strequal(org_unit, "Computers")) {
1642 return SMB_STRDUP("cn=Computers");
1645 /* jmcd: removed "\\" from the separation chars, because it is
1646 needed as an escape for chars like '#' which are valid in an
1647 OU name */
1648 return ads_build_path(org_unit, "/", "ou=", 1);
1652 * Get a org unit string for a well-known GUID
1653 * @param ads connection to ads server
1654 * @param wknguid Well known GUID
1655 * @return org unit string - caller must free
1657 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1659 ADS_STATUS status;
1660 LDAPMessage *res = NULL;
1661 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1662 **bind_dn_exp = NULL;
1663 const char *attrs[] = {"distinguishedName", NULL};
1664 int new_ln, wkn_ln, bind_ln, i;
1666 if (wknguid == NULL) {
1667 return NULL;
1670 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1671 DEBUG(1, ("asprintf failed!\n"));
1672 return NULL;
1675 status = ads_search_dn(ads, &res, base, attrs);
1676 if (!ADS_ERR_OK(status)) {
1677 DEBUG(1,("Failed while searching for: %s\n", base));
1678 goto out;
1681 if (ads_count_replies(ads, res) != 1) {
1682 goto out;
1685 /* substitute the bind-path from the well-known-guid-search result */
1686 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1687 if (!wkn_dn) {
1688 goto out;
1691 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1692 if (!wkn_dn_exp) {
1693 goto out;
1696 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1697 if (!bind_dn_exp) {
1698 goto out;
1701 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1703 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1706 new_ln = wkn_ln - bind_ln;
1708 ret = SMB_STRDUP(wkn_dn_exp[0]);
1709 if (!ret) {
1710 goto out;
1713 for (i=1; i < new_ln; i++) {
1714 char *s = NULL;
1716 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1717 SAFE_FREE(ret);
1718 goto out;
1721 SAFE_FREE(ret);
1722 ret = SMB_STRDUP(s);
1723 free(s);
1724 if (!ret) {
1725 goto out;
1729 out:
1730 SAFE_FREE(base);
1731 ads_msgfree(ads, res);
1732 TALLOC_FREE(wkn_dn);
1733 if (wkn_dn_exp) {
1734 ldap_value_free(wkn_dn_exp);
1736 if (bind_dn_exp) {
1737 ldap_value_free(bind_dn_exp);
1740 return ret;
1744 * Adds (appends) an item to an attribute array, rather then
1745 * replacing the whole list
1746 * @param ctx An initialized TALLOC_CTX
1747 * @param mods An initialized ADS_MODLIST
1748 * @param name name of the ldap attribute to append to
1749 * @param vals an array of values to add
1750 * @return status of addition
1753 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1754 const char *name, const char **vals)
1756 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1757 (const void *) vals);
1761 * Determines the an account's current KVNO via an LDAP lookup
1762 * @param ads An initialized ADS_STRUCT
1763 * @param account_name the NT samaccountname.
1764 * @return the kvno for the account, or -1 in case of a failure.
1767 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1769 LDAPMessage *res = NULL;
1770 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1771 char *filter;
1772 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1773 char *dn_string = NULL;
1774 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1776 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1777 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1778 return kvno;
1780 ret = ads_search(ads, &res, filter, attrs);
1781 SAFE_FREE(filter);
1782 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1783 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1784 ads_msgfree(ads, res);
1785 return kvno;
1788 dn_string = ads_get_dn(ads, talloc_tos(), res);
1789 if (!dn_string) {
1790 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1791 ads_msgfree(ads, res);
1792 return kvno;
1794 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1795 TALLOC_FREE(dn_string);
1797 /* ---------------------------------------------------------
1798 * 0 is returned as a default KVNO from this point on...
1799 * This is done because Windows 2000 does not support key
1800 * version numbers. Chances are that a failure in the next
1801 * step is simply due to Windows 2000 being used for a
1802 * domain controller. */
1803 kvno = 0;
1805 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1806 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1807 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1808 ads_msgfree(ads, res);
1809 return kvno;
1812 /* Success */
1813 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1814 ads_msgfree(ads, res);
1815 return kvno;
1819 * Determines the computer account's current KVNO via an LDAP lookup
1820 * @param ads An initialized ADS_STRUCT
1821 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1822 * @return the kvno for the computer account, or -1 in case of a failure.
1825 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1827 char *computer_account = NULL;
1828 uint32_t kvno = -1;
1830 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1831 return kvno;
1834 kvno = ads_get_kvno(ads, computer_account);
1835 free(computer_account);
1837 return kvno;
1841 * This clears out all registered spn's for a given hostname
1842 * @param ads An initilaized ADS_STRUCT
1843 * @param machine_name the NetBIOS name of the computer.
1844 * @return 0 upon success, non-zero otherwise.
1847 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1849 TALLOC_CTX *ctx;
1850 LDAPMessage *res = NULL;
1851 ADS_MODLIST mods;
1852 const char *servicePrincipalName[1] = {NULL};
1853 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1854 char *dn_string = NULL;
1856 ret = ads_find_machine_acct(ads, &res, machine_name);
1857 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1858 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1859 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1860 ads_msgfree(ads, res);
1861 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1864 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1865 ctx = talloc_init("ads_clear_service_principal_names");
1866 if (!ctx) {
1867 ads_msgfree(ads, res);
1868 return ADS_ERROR(LDAP_NO_MEMORY);
1871 if (!(mods = ads_init_mods(ctx))) {
1872 talloc_destroy(ctx);
1873 ads_msgfree(ads, res);
1874 return ADS_ERROR(LDAP_NO_MEMORY);
1876 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1877 if (!ADS_ERR_OK(ret)) {
1878 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1879 ads_msgfree(ads, res);
1880 talloc_destroy(ctx);
1881 return ret;
1883 dn_string = ads_get_dn(ads, talloc_tos(), res);
1884 if (!dn_string) {
1885 talloc_destroy(ctx);
1886 ads_msgfree(ads, res);
1887 return ADS_ERROR(LDAP_NO_MEMORY);
1889 ret = ads_gen_mod(ads, dn_string, mods);
1890 TALLOC_FREE(dn_string);
1891 if (!ADS_ERR_OK(ret)) {
1892 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1893 machine_name));
1894 ads_msgfree(ads, res);
1895 talloc_destroy(ctx);
1896 return ret;
1899 ads_msgfree(ads, res);
1900 talloc_destroy(ctx);
1901 return ret;
1905 * This adds a service principal name to an existing computer account
1906 * (found by hostname) in AD.
1907 * @param ads An initialized ADS_STRUCT
1908 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1909 * @param my_fqdn The fully qualified DNS name of the machine
1910 * @param spn A string of the service principal to add, i.e. 'host'
1911 * @return 0 upon sucess, or non-zero if a failure occurs
1914 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1915 const char *my_fqdn, const char *spn)
1917 ADS_STATUS ret;
1918 TALLOC_CTX *ctx;
1919 LDAPMessage *res = NULL;
1920 char *psp1, *psp2;
1921 ADS_MODLIST mods;
1922 char *dn_string = NULL;
1923 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1925 ret = ads_find_machine_acct(ads, &res, machine_name);
1926 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1927 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1928 machine_name));
1929 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1930 spn, machine_name, ads->config.realm));
1931 ads_msgfree(ads, res);
1932 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1935 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1936 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1937 ads_msgfree(ads, res);
1938 return ADS_ERROR(LDAP_NO_MEMORY);
1941 /* add short name spn */
1943 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1944 talloc_destroy(ctx);
1945 ads_msgfree(ads, res);
1946 return ADS_ERROR(LDAP_NO_MEMORY);
1948 strupper_m(psp1);
1949 strlower_m(&psp1[strlen(spn)]);
1950 servicePrincipalName[0] = psp1;
1952 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1953 psp1, machine_name));
1956 /* add fully qualified spn */
1958 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1959 ret = ADS_ERROR(LDAP_NO_MEMORY);
1960 goto out;
1962 strupper_m(psp2);
1963 strlower_m(&psp2[strlen(spn)]);
1964 servicePrincipalName[1] = psp2;
1966 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1967 psp2, machine_name));
1969 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1970 ret = ADS_ERROR(LDAP_NO_MEMORY);
1971 goto out;
1974 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1975 if (!ADS_ERR_OK(ret)) {
1976 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1977 goto out;
1980 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1981 ret = ADS_ERROR(LDAP_NO_MEMORY);
1982 goto out;
1985 ret = ads_gen_mod(ads, dn_string, mods);
1986 if (!ADS_ERR_OK(ret)) {
1987 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1988 goto out;
1991 out:
1992 TALLOC_FREE( ctx );
1993 ads_msgfree(ads, res);
1994 return ret;
1998 * adds a machine account to the ADS server
1999 * @param ads An intialized ADS_STRUCT
2000 * @param machine_name - the NetBIOS machine name of this account.
2001 * @param account_type A number indicating the type of account to create
2002 * @param org_unit The LDAP path in which to place this account
2003 * @return 0 upon success, or non-zero otherwise
2006 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2007 const char *org_unit)
2009 ADS_STATUS ret;
2010 char *samAccountName, *controlstr;
2011 TALLOC_CTX *ctx;
2012 ADS_MODLIST mods;
2013 char *machine_escaped = NULL;
2014 char *new_dn;
2015 const char *objectClass[] = {"top", "person", "organizationalPerson",
2016 "user", "computer", NULL};
2017 LDAPMessage *res = NULL;
2018 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2019 UF_DONT_EXPIRE_PASSWD |\
2020 UF_ACCOUNTDISABLE );
2022 if (!(ctx = talloc_init("ads_add_machine_acct")))
2023 return ADS_ERROR(LDAP_NO_MEMORY);
2025 ret = ADS_ERROR(LDAP_NO_MEMORY);
2027 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2028 if (!machine_escaped) {
2029 goto done;
2032 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2033 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2035 if ( !new_dn || !samAccountName ) {
2036 goto done;
2039 #ifndef ENCTYPE_ARCFOUR_HMAC
2040 acct_control |= UF_USE_DES_KEY_ONLY;
2041 #endif
2043 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2044 goto done;
2047 if (!(mods = ads_init_mods(ctx))) {
2048 goto done;
2051 ads_mod_str(ctx, &mods, "cn", machine_name);
2052 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2053 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2054 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2056 ret = ads_gen_add(ads, new_dn, mods);
2058 done:
2059 SAFE_FREE(machine_escaped);
2060 ads_msgfree(ads, res);
2061 talloc_destroy(ctx);
2063 return ret;
2067 * move a machine account to another OU on the ADS server
2068 * @param ads - An intialized ADS_STRUCT
2069 * @param machine_name - the NetBIOS machine name of this account.
2070 * @param org_unit - The LDAP path in which to place this account
2071 * @param moved - whether we moved the machine account (optional)
2072 * @return 0 upon success, or non-zero otherwise
2075 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2076 const char *org_unit, bool *moved)
2078 ADS_STATUS rc;
2079 int ldap_status;
2080 LDAPMessage *res = NULL;
2081 char *filter = NULL;
2082 char *computer_dn = NULL;
2083 char *parent_dn;
2084 char *computer_rdn = NULL;
2085 bool need_move = False;
2087 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2088 rc = ADS_ERROR(LDAP_NO_MEMORY);
2089 goto done;
2092 /* Find pre-existing machine */
2093 rc = ads_search(ads, &res, filter, NULL);
2094 if (!ADS_ERR_OK(rc)) {
2095 goto done;
2098 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2099 if (!computer_dn) {
2100 rc = ADS_ERROR(LDAP_NO_MEMORY);
2101 goto done;
2104 parent_dn = ads_parent_dn(computer_dn);
2105 if (strequal(parent_dn, org_unit)) {
2106 goto done;
2109 need_move = True;
2111 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2112 rc = ADS_ERROR(LDAP_NO_MEMORY);
2113 goto done;
2116 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2117 org_unit, 1, NULL, NULL);
2118 rc = ADS_ERROR(ldap_status);
2120 done:
2121 ads_msgfree(ads, res);
2122 SAFE_FREE(filter);
2123 TALLOC_FREE(computer_dn);
2124 SAFE_FREE(computer_rdn);
2126 if (!ADS_ERR_OK(rc)) {
2127 need_move = False;
2130 if (moved) {
2131 *moved = need_move;
2134 return rc;
2138 dump a binary result from ldap
2140 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2142 int i, j;
2143 for (i=0; values[i]; i++) {
2144 printf("%s: ", field);
2145 for (j=0; j<values[i]->bv_len; j++) {
2146 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2148 printf("\n");
2152 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2154 int i;
2155 for (i=0; values[i]; i++) {
2156 NTSTATUS status;
2157 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2158 struct GUID guid;
2160 status = GUID_from_ndr_blob(&in, &guid);
2161 if (NT_STATUS_IS_OK(status)) {
2162 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2163 } else {
2164 printf("%s: INVALID GUID\n", field);
2170 dump a sid result from ldap
2172 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2174 int i;
2175 for (i=0; values[i]; i++) {
2176 struct dom_sid sid;
2177 fstring tmp;
2178 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2179 return;
2181 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2186 dump ntSecurityDescriptor
2188 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2190 TALLOC_CTX *frame = talloc_stackframe();
2191 struct security_descriptor *psd;
2192 NTSTATUS status;
2194 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2195 values[0]->bv_len, &psd);
2196 if (!NT_STATUS_IS_OK(status)) {
2197 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2198 nt_errstr(status)));
2199 TALLOC_FREE(frame);
2200 return;
2203 if (psd) {
2204 ads_disp_sd(ads, talloc_tos(), psd);
2207 TALLOC_FREE(frame);
2211 dump a string result from ldap
2213 static void dump_string(const char *field, char **values)
2215 int i;
2216 for (i=0; values[i]; i++) {
2217 printf("%s: %s\n", field, values[i]);
2222 dump a field from LDAP on stdout
2223 used for debugging
2226 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2228 const struct {
2229 const char *name;
2230 bool string;
2231 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2232 } handlers[] = {
2233 {"objectGUID", False, dump_guid},
2234 {"netbootGUID", False, dump_guid},
2235 {"nTSecurityDescriptor", False, dump_sd},
2236 {"dnsRecord", False, dump_binary},
2237 {"objectSid", False, dump_sid},
2238 {"tokenGroups", False, dump_sid},
2239 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2240 {"tokengroupsGlobalandUniversal", False, dump_sid},
2241 {"mS-DS-CreatorSID", False, dump_sid},
2242 {"msExchMailboxGuid", False, dump_guid},
2243 {NULL, True, NULL}
2245 int i;
2247 if (!field) { /* must be end of an entry */
2248 printf("\n");
2249 return False;
2252 for (i=0; handlers[i].name; i++) {
2253 if (strcasecmp_m(handlers[i].name, field) == 0) {
2254 if (!values) /* first time, indicate string or not */
2255 return handlers[i].string;
2256 handlers[i].handler(ads, field, (struct berval **) values);
2257 break;
2260 if (!handlers[i].name) {
2261 if (!values) /* first time, indicate string conversion */
2262 return True;
2263 dump_string(field, (char **)values);
2265 return False;
2269 * Dump a result from LDAP on stdout
2270 * used for debugging
2271 * @param ads connection to ads server
2272 * @param res Results to dump
2275 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2277 ads_process_results(ads, res, ads_dump_field, NULL);
2281 * Walk through results, calling a function for each entry found.
2282 * The function receives a field name, a berval * array of values,
2283 * and a data area passed through from the start. The function is
2284 * called once with null for field and values at the end of each
2285 * entry.
2286 * @param ads connection to ads server
2287 * @param res Results to process
2288 * @param fn Function for processing each result
2289 * @param data_area user-defined area to pass to function
2291 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2292 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2293 void *data_area)
2295 LDAPMessage *msg;
2296 TALLOC_CTX *ctx;
2297 size_t converted_size;
2299 if (!(ctx = talloc_init("ads_process_results")))
2300 return;
2302 for (msg = ads_first_entry(ads, res); msg;
2303 msg = ads_next_entry(ads, msg)) {
2304 char *utf8_field;
2305 BerElement *b;
2307 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2308 (LDAPMessage *)msg,&b);
2309 utf8_field;
2310 utf8_field=ldap_next_attribute(ads->ldap.ld,
2311 (LDAPMessage *)msg,b)) {
2312 struct berval **ber_vals;
2313 char **str_vals, **utf8_vals;
2314 char *field;
2315 bool string;
2317 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2318 &converted_size))
2320 DEBUG(0,("ads_process_results: "
2321 "pull_utf8_talloc failed: %s",
2322 strerror(errno)));
2325 string = fn(ads, field, NULL, data_area);
2327 if (string) {
2328 utf8_vals = ldap_get_values(ads->ldap.ld,
2329 (LDAPMessage *)msg, field);
2330 str_vals = ads_pull_strvals(ctx,
2331 (const char **) utf8_vals);
2332 fn(ads, field, (void **) str_vals, data_area);
2333 ldap_value_free(utf8_vals);
2334 } else {
2335 ber_vals = ldap_get_values_len(ads->ldap.ld,
2336 (LDAPMessage *)msg, field);
2337 fn(ads, field, (void **) ber_vals, data_area);
2339 ldap_value_free_len(ber_vals);
2341 ldap_memfree(utf8_field);
2343 ber_free(b, 0);
2344 talloc_free_children(ctx);
2345 fn(ads, NULL, NULL, data_area); /* completed an entry */
2348 talloc_destroy(ctx);
2352 * count how many replies are in a LDAPMessage
2353 * @param ads connection to ads server
2354 * @param res Results to count
2355 * @return number of replies
2357 int ads_count_replies(ADS_STRUCT *ads, void *res)
2359 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2363 * pull the first entry from a ADS result
2364 * @param ads connection to ads server
2365 * @param res Results of search
2366 * @return first entry from result
2368 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2370 return ldap_first_entry(ads->ldap.ld, res);
2374 * pull the next entry from a ADS result
2375 * @param ads connection to ads server
2376 * @param res Results of search
2377 * @return next entry from result
2379 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2381 return ldap_next_entry(ads->ldap.ld, res);
2385 * pull the first message from a ADS result
2386 * @param ads connection to ads server
2387 * @param res Results of search
2388 * @return first message from result
2390 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2392 return ldap_first_message(ads->ldap.ld, res);
2396 * pull the next message from a ADS result
2397 * @param ads connection to ads server
2398 * @param res Results of search
2399 * @return next message from result
2401 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2403 return ldap_next_message(ads->ldap.ld, res);
2407 * pull a single string from a ADS result
2408 * @param ads connection to ads server
2409 * @param mem_ctx TALLOC_CTX to use for allocating result string
2410 * @param msg Results of search
2411 * @param field Attribute to retrieve
2412 * @return Result string in talloc context
2414 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2415 const char *field)
2417 char **values;
2418 char *ret = NULL;
2419 char *ux_string;
2420 size_t converted_size;
2422 values = ldap_get_values(ads->ldap.ld, msg, field);
2423 if (!values)
2424 return NULL;
2426 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2427 &converted_size))
2429 ret = ux_string;
2431 ldap_value_free(values);
2432 return ret;
2436 * pull an array of strings from a ADS result
2437 * @param ads connection to ads server
2438 * @param mem_ctx TALLOC_CTX to use for allocating result string
2439 * @param msg Results of search
2440 * @param field Attribute to retrieve
2441 * @return Result strings in talloc context
2443 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2444 LDAPMessage *msg, const char *field,
2445 size_t *num_values)
2447 char **values;
2448 char **ret = NULL;
2449 int i;
2450 size_t converted_size;
2452 values = ldap_get_values(ads->ldap.ld, msg, field);
2453 if (!values)
2454 return NULL;
2456 *num_values = ldap_count_values(values);
2458 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2459 if (!ret) {
2460 ldap_value_free(values);
2461 return NULL;
2464 for (i=0;i<*num_values;i++) {
2465 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2466 &converted_size))
2468 ldap_value_free(values);
2469 return NULL;
2472 ret[i] = NULL;
2474 ldap_value_free(values);
2475 return ret;
2479 * pull an array of strings from a ADS result
2480 * (handle large multivalue attributes with range retrieval)
2481 * @param ads connection to ads server
2482 * @param mem_ctx TALLOC_CTX to use for allocating result string
2483 * @param msg Results of search
2484 * @param field Attribute to retrieve
2485 * @param current_strings strings returned by a previous call to this function
2486 * @param next_attribute The next query should ask for this attribute
2487 * @param num_values How many values did we get this time?
2488 * @param more_values Are there more values to get?
2489 * @return Result strings in talloc context
2491 char **ads_pull_strings_range(ADS_STRUCT *ads,
2492 TALLOC_CTX *mem_ctx,
2493 LDAPMessage *msg, const char *field,
2494 char **current_strings,
2495 const char **next_attribute,
2496 size_t *num_strings,
2497 bool *more_strings)
2499 char *attr;
2500 char *expected_range_attrib, *range_attr;
2501 BerElement *ptr = NULL;
2502 char **strings;
2503 char **new_strings;
2504 size_t num_new_strings;
2505 unsigned long int range_start;
2506 unsigned long int range_end;
2508 /* we might have been given the whole lot anyway */
2509 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2510 *more_strings = False;
2511 return strings;
2514 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2516 /* look for Range result */
2517 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2518 attr;
2519 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2520 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2521 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2522 range_attr = attr;
2523 break;
2525 ldap_memfree(attr);
2527 if (!attr) {
2528 ber_free(ptr, 0);
2529 /* nothing here - this field is just empty */
2530 *more_strings = False;
2531 return NULL;
2534 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2535 &range_start, &range_end) == 2) {
2536 *more_strings = True;
2537 } else {
2538 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2539 &range_start) == 1) {
2540 *more_strings = False;
2541 } else {
2542 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2543 range_attr));
2544 ldap_memfree(range_attr);
2545 *more_strings = False;
2546 return NULL;
2550 if ((*num_strings) != range_start) {
2551 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2552 " - aborting range retreival\n",
2553 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2554 ldap_memfree(range_attr);
2555 *more_strings = False;
2556 return NULL;
2559 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2561 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2562 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2563 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2564 range_attr, (unsigned long int)range_end - range_start + 1,
2565 (unsigned long int)num_new_strings));
2566 ldap_memfree(range_attr);
2567 *more_strings = False;
2568 return NULL;
2571 strings = talloc_realloc(mem_ctx, current_strings, char *,
2572 *num_strings + num_new_strings);
2574 if (strings == NULL) {
2575 ldap_memfree(range_attr);
2576 *more_strings = False;
2577 return NULL;
2580 if (new_strings && num_new_strings) {
2581 memcpy(&strings[*num_strings], new_strings,
2582 sizeof(*new_strings) * num_new_strings);
2585 (*num_strings) += num_new_strings;
2587 if (*more_strings) {
2588 *next_attribute = talloc_asprintf(mem_ctx,
2589 "%s;range=%d-*",
2590 field,
2591 (int)*num_strings);
2593 if (!*next_attribute) {
2594 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2595 ldap_memfree(range_attr);
2596 *more_strings = False;
2597 return NULL;
2601 ldap_memfree(range_attr);
2603 return strings;
2607 * pull a single uint32 from a ADS result
2608 * @param ads connection to ads server
2609 * @param msg Results of search
2610 * @param field Attribute to retrieve
2611 * @param v Pointer to int to store result
2612 * @return boolean inidicating success
2614 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2615 uint32 *v)
2617 char **values;
2619 values = ldap_get_values(ads->ldap.ld, msg, field);
2620 if (!values)
2621 return False;
2622 if (!values[0]) {
2623 ldap_value_free(values);
2624 return False;
2627 *v = atoi(values[0]);
2628 ldap_value_free(values);
2629 return True;
2633 * pull a single objectGUID from an ADS result
2634 * @param ads connection to ADS server
2635 * @param msg results of search
2636 * @param guid 37-byte area to receive text guid
2637 * @return boolean indicating success
2639 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2641 DATA_BLOB blob;
2642 NTSTATUS status;
2644 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2645 &blob)) {
2646 return false;
2649 status = GUID_from_ndr_blob(&blob, guid);
2650 talloc_free(blob.data);
2651 return NT_STATUS_IS_OK(status);
2656 * pull a single struct dom_sid from a ADS result
2657 * @param ads connection to ads server
2658 * @param msg Results of search
2659 * @param field Attribute to retrieve
2660 * @param sid Pointer to sid to store result
2661 * @return boolean inidicating success
2663 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2664 struct dom_sid *sid)
2666 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2670 * pull an array of struct dom_sids from a ADS result
2671 * @param ads connection to ads server
2672 * @param mem_ctx TALLOC_CTX for allocating sid array
2673 * @param msg Results of search
2674 * @param field Attribute to retrieve
2675 * @param sids pointer to sid array to allocate
2676 * @return the count of SIDs pulled
2678 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2679 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2681 struct berval **values;
2682 bool ret;
2683 int count, i;
2685 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2687 if (!values)
2688 return 0;
2690 for (i=0; values[i]; i++)
2691 /* nop */ ;
2693 if (i) {
2694 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2695 if (!(*sids)) {
2696 ldap_value_free_len(values);
2697 return 0;
2699 } else {
2700 (*sids) = NULL;
2703 count = 0;
2704 for (i=0; values[i]; i++) {
2705 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2706 if (ret) {
2707 DEBUG(10, ("pulling SID: %s\n",
2708 sid_string_dbg(&(*sids)[count])));
2709 count++;
2713 ldap_value_free_len(values);
2714 return count;
2718 * pull a struct security_descriptor from a ADS result
2719 * @param ads connection to ads server
2720 * @param mem_ctx TALLOC_CTX for allocating sid array
2721 * @param msg Results of search
2722 * @param field Attribute to retrieve
2723 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2724 * @return boolean inidicating success
2726 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2727 LDAPMessage *msg, const char *field,
2728 struct security_descriptor **sd)
2730 struct berval **values;
2731 bool ret = true;
2733 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2735 if (!values) return false;
2737 if (values[0]) {
2738 NTSTATUS status;
2739 status = unmarshall_sec_desc(mem_ctx,
2740 (uint8 *)values[0]->bv_val,
2741 values[0]->bv_len, sd);
2742 if (!NT_STATUS_IS_OK(status)) {
2743 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2744 nt_errstr(status)));
2745 ret = false;
2749 ldap_value_free_len(values);
2750 return ret;
2754 * in order to support usernames longer than 21 characters we need to
2755 * use both the sAMAccountName and the userPrincipalName attributes
2756 * It seems that not all users have the userPrincipalName attribute set
2758 * @param ads connection to ads server
2759 * @param mem_ctx TALLOC_CTX for allocating sid array
2760 * @param msg Results of search
2761 * @return the username
2763 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2764 LDAPMessage *msg)
2766 #if 0 /* JERRY */
2767 char *ret, *p;
2769 /* lookup_name() only works on the sAMAccountName to
2770 returning the username portion of userPrincipalName
2771 breaks winbindd_getpwnam() */
2773 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2774 if (ret && (p = strchr_m(ret, '@'))) {
2775 *p = 0;
2776 return ret;
2778 #endif
2779 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2784 * find the update serial number - this is the core of the ldap cache
2785 * @param ads connection to ads server
2786 * @param ads connection to ADS server
2787 * @param usn Pointer to retrieved update serial number
2788 * @return status of search
2790 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2792 const char *attrs[] = {"highestCommittedUSN", NULL};
2793 ADS_STATUS status;
2794 LDAPMessage *res;
2796 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2797 if (!ADS_ERR_OK(status))
2798 return status;
2800 if (ads_count_replies(ads, res) != 1) {
2801 ads_msgfree(ads, res);
2802 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2805 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2806 ads_msgfree(ads, res);
2807 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2810 ads_msgfree(ads, res);
2811 return ADS_SUCCESS;
2814 /* parse a ADS timestring - typical string is
2815 '20020917091222.0Z0' which means 09:12.22 17th September
2816 2002, timezone 0 */
2817 static time_t ads_parse_time(const char *str)
2819 struct tm tm;
2821 ZERO_STRUCT(tm);
2823 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2824 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2825 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2826 return 0;
2828 tm.tm_year -= 1900;
2829 tm.tm_mon -= 1;
2831 return timegm(&tm);
2834 /********************************************************************
2835 ********************************************************************/
2837 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2839 const char *attrs[] = {"currentTime", NULL};
2840 ADS_STATUS status;
2841 LDAPMessage *res;
2842 char *timestr;
2843 TALLOC_CTX *ctx;
2844 ADS_STRUCT *ads_s = ads;
2846 if (!(ctx = talloc_init("ads_current_time"))) {
2847 return ADS_ERROR(LDAP_NO_MEMORY);
2850 /* establish a new ldap tcp session if necessary */
2852 if ( !ads->ldap.ld ) {
2853 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2854 ads->server.ldap_server )) == NULL )
2856 goto done;
2858 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2859 status = ads_connect( ads_s );
2860 if ( !ADS_ERR_OK(status))
2861 goto done;
2864 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2865 if (!ADS_ERR_OK(status)) {
2866 goto done;
2869 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2870 if (!timestr) {
2871 ads_msgfree(ads_s, res);
2872 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2873 goto done;
2876 /* but save the time and offset in the original ADS_STRUCT */
2878 ads->config.current_time = ads_parse_time(timestr);
2880 if (ads->config.current_time != 0) {
2881 ads->auth.time_offset = ads->config.current_time - time(NULL);
2882 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2885 ads_msgfree(ads, res);
2887 status = ADS_SUCCESS;
2889 done:
2890 /* free any temporary ads connections */
2891 if ( ads_s != ads ) {
2892 ads_destroy( &ads_s );
2894 talloc_destroy(ctx);
2896 return status;
2899 /********************************************************************
2900 ********************************************************************/
2902 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2904 const char *attrs[] = {"domainFunctionality", NULL};
2905 ADS_STATUS status;
2906 LDAPMessage *res;
2907 ADS_STRUCT *ads_s = ads;
2909 *val = DS_DOMAIN_FUNCTION_2000;
2911 /* establish a new ldap tcp session if necessary */
2913 if ( !ads->ldap.ld ) {
2914 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2915 ads->server.ldap_server )) == NULL )
2917 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2918 goto done;
2920 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2921 status = ads_connect( ads_s );
2922 if ( !ADS_ERR_OK(status))
2923 goto done;
2926 /* If the attribute does not exist assume it is a Windows 2000
2927 functional domain */
2929 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2930 if (!ADS_ERR_OK(status)) {
2931 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2932 status = ADS_SUCCESS;
2934 goto done;
2937 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2938 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2940 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2943 ads_msgfree(ads, res);
2945 done:
2946 /* free any temporary ads connections */
2947 if ( ads_s != ads ) {
2948 ads_destroy( &ads_s );
2951 return status;
2955 * find the domain sid for our domain
2956 * @param ads connection to ads server
2957 * @param sid Pointer to domain sid
2958 * @return status of search
2960 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2962 const char *attrs[] = {"objectSid", NULL};
2963 LDAPMessage *res;
2964 ADS_STATUS rc;
2966 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2967 attrs, &res);
2968 if (!ADS_ERR_OK(rc)) return rc;
2969 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2970 ads_msgfree(ads, res);
2971 return ADS_ERROR_SYSTEM(ENOENT);
2973 ads_msgfree(ads, res);
2975 return ADS_SUCCESS;
2979 * find our site name
2980 * @param ads connection to ads server
2981 * @param mem_ctx Pointer to talloc context
2982 * @param site_name Pointer to the sitename
2983 * @return status of search
2985 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2987 ADS_STATUS status;
2988 LDAPMessage *res;
2989 const char *dn, *service_name;
2990 const char *attrs[] = { "dsServiceName", NULL };
2992 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2993 if (!ADS_ERR_OK(status)) {
2994 return status;
2997 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2998 if (service_name == NULL) {
2999 ads_msgfree(ads, res);
3000 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3003 ads_msgfree(ads, res);
3005 /* go up three levels */
3006 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3007 if (dn == NULL) {
3008 return ADS_ERROR(LDAP_NO_MEMORY);
3011 *site_name = talloc_strdup(mem_ctx, dn);
3012 if (*site_name == NULL) {
3013 return ADS_ERROR(LDAP_NO_MEMORY);
3016 return status;
3018 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3023 * find the site dn where a machine resides
3024 * @param ads connection to ads server
3025 * @param mem_ctx Pointer to talloc context
3026 * @param computer_name name of the machine
3027 * @param site_name Pointer to the sitename
3028 * @return status of search
3030 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3032 ADS_STATUS status;
3033 LDAPMessage *res;
3034 const char *parent, *filter;
3035 char *config_context = NULL;
3036 char *dn;
3038 /* shortcut a query */
3039 if (strequal(computer_name, ads->config.ldap_server_name)) {
3040 return ads_site_dn(ads, mem_ctx, site_dn);
3043 status = ads_config_path(ads, mem_ctx, &config_context);
3044 if (!ADS_ERR_OK(status)) {
3045 return status;
3048 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3049 if (filter == NULL) {
3050 return ADS_ERROR(LDAP_NO_MEMORY);
3053 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3054 filter, NULL, &res);
3055 if (!ADS_ERR_OK(status)) {
3056 return status;
3059 if (ads_count_replies(ads, res) != 1) {
3060 ads_msgfree(ads, res);
3061 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3064 dn = ads_get_dn(ads, mem_ctx, res);
3065 if (dn == NULL) {
3066 ads_msgfree(ads, res);
3067 return ADS_ERROR(LDAP_NO_MEMORY);
3070 /* go up three levels */
3071 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3072 if (parent == NULL) {
3073 ads_msgfree(ads, res);
3074 TALLOC_FREE(dn);
3075 return ADS_ERROR(LDAP_NO_MEMORY);
3078 *site_dn = talloc_strdup(mem_ctx, parent);
3079 if (*site_dn == NULL) {
3080 ads_msgfree(ads, res);
3081 TALLOC_FREE(dn);
3082 return ADS_ERROR(LDAP_NO_MEMORY);
3085 TALLOC_FREE(dn);
3086 ads_msgfree(ads, res);
3088 return status;
3092 * get the upn suffixes for a domain
3093 * @param ads connection to ads server
3094 * @param mem_ctx Pointer to talloc context
3095 * @param suffixes Pointer to an array of suffixes
3096 * @param num_suffixes Pointer to the number of suffixes
3097 * @return status of search
3099 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3101 ADS_STATUS status;
3102 LDAPMessage *res;
3103 const char *base;
3104 char *config_context = NULL;
3105 const char *attrs[] = { "uPNSuffixes", NULL };
3107 status = ads_config_path(ads, mem_ctx, &config_context);
3108 if (!ADS_ERR_OK(status)) {
3109 return status;
3112 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3113 if (base == NULL) {
3114 return ADS_ERROR(LDAP_NO_MEMORY);
3117 status = ads_search_dn(ads, &res, base, attrs);
3118 if (!ADS_ERR_OK(status)) {
3119 return status;
3122 if (ads_count_replies(ads, res) != 1) {
3123 ads_msgfree(ads, res);
3124 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3127 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3128 if ((*suffixes) == NULL) {
3129 ads_msgfree(ads, res);
3130 return ADS_ERROR(LDAP_NO_MEMORY);
3133 ads_msgfree(ads, res);
3135 return status;
3139 * get the joinable ous for a domain
3140 * @param ads connection to ads server
3141 * @param mem_ctx Pointer to talloc context
3142 * @param ous Pointer to an array of ous
3143 * @param num_ous Pointer to the number of ous
3144 * @return status of search
3146 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3147 TALLOC_CTX *mem_ctx,
3148 char ***ous,
3149 size_t *num_ous)
3151 ADS_STATUS status;
3152 LDAPMessage *res = NULL;
3153 LDAPMessage *msg = NULL;
3154 const char *attrs[] = { "dn", NULL };
3155 int count = 0;
3157 status = ads_search(ads, &res,
3158 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3159 attrs);
3160 if (!ADS_ERR_OK(status)) {
3161 return status;
3164 count = ads_count_replies(ads, res);
3165 if (count < 1) {
3166 ads_msgfree(ads, res);
3167 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3170 for (msg = ads_first_entry(ads, res); msg;
3171 msg = ads_next_entry(ads, msg)) {
3173 char *dn = NULL;
3175 dn = ads_get_dn(ads, talloc_tos(), msg);
3176 if (!dn) {
3177 ads_msgfree(ads, res);
3178 return ADS_ERROR(LDAP_NO_MEMORY);
3181 if (!add_string_to_array(mem_ctx, dn,
3182 (const char ***)ous,
3183 (int *)num_ous)) {
3184 TALLOC_FREE(dn);
3185 ads_msgfree(ads, res);
3186 return ADS_ERROR(LDAP_NO_MEMORY);
3189 TALLOC_FREE(dn);
3192 ads_msgfree(ads, res);
3194 return status;
3199 * pull a struct dom_sid from an extended dn string
3200 * @param mem_ctx TALLOC_CTX
3201 * @param extended_dn string
3202 * @param flags string type of extended_dn
3203 * @param sid pointer to a struct dom_sid
3204 * @return NT_STATUS_OK on success,
3205 * NT_INVALID_PARAMETER on error,
3206 * NT_STATUS_NOT_FOUND if no SID present
3208 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3209 const char *extended_dn,
3210 enum ads_extended_dn_flags flags,
3211 struct dom_sid *sid)
3213 char *p, *q, *dn;
3215 if (!extended_dn) {
3216 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3219 /* otherwise extended_dn gets stripped off */
3220 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3221 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3224 * ADS_EXTENDED_DN_HEX_STRING:
3225 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3227 * ADS_EXTENDED_DN_STRING (only with w2k3):
3228 * <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
3230 * Object with no SID, such as an Exchange Public Folder
3231 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3234 p = strchr(dn, ';');
3235 if (!p) {
3236 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3239 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3240 DEBUG(5,("No SID present in extended dn\n"));
3241 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3244 p += strlen(";<SID=");
3246 q = strchr(p, '>');
3247 if (!q) {
3248 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3251 *q = '\0';
3253 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3255 switch (flags) {
3257 case ADS_EXTENDED_DN_STRING:
3258 if (!string_to_sid(sid, p)) {
3259 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3261 break;
3262 case ADS_EXTENDED_DN_HEX_STRING: {
3263 fstring buf;
3264 size_t buf_len;
3266 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3267 if (buf_len == 0) {
3268 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3271 if (!sid_parse(buf, buf_len, sid)) {
3272 DEBUG(10,("failed to parse sid\n"));
3273 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3275 break;
3277 default:
3278 DEBUG(10,("unknown extended dn format\n"));
3279 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3282 return ADS_ERROR_NT(NT_STATUS_OK);
3285 /********************************************************************
3286 ********************************************************************/
3288 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3290 LDAPMessage *res = NULL;
3291 ADS_STATUS status;
3292 int count = 0;
3293 char *name = NULL;
3295 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3296 if (!ADS_ERR_OK(status)) {
3297 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3298 lp_netbios_name()));
3299 goto out;
3302 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3303 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3304 goto out;
3307 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3308 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3311 out:
3312 ads_msgfree(ads, res);
3314 return name;
3317 /********************************************************************
3318 ********************************************************************/
3320 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3322 LDAPMessage *res = NULL;
3323 ADS_STATUS status;
3324 int count = 0;
3325 char *name = NULL;
3327 status = ads_find_machine_acct(ads, &res, machine_name);
3328 if (!ADS_ERR_OK(status)) {
3329 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3330 lp_netbios_name()));
3331 goto out;
3334 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3335 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3336 goto out;
3339 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3340 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3343 out:
3344 ads_msgfree(ads, res);
3346 return name;
3349 /********************************************************************
3350 ********************************************************************/
3352 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3354 LDAPMessage *res = NULL;
3355 ADS_STATUS status;
3356 int count = 0;
3357 char *name = NULL;
3359 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3360 if (!ADS_ERR_OK(status)) {
3361 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3362 lp_netbios_name()));
3363 goto out;
3366 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3367 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3368 goto out;
3371 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3372 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3375 out:
3376 ads_msgfree(ads, res);
3378 return name;
3381 #if 0
3383 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3386 * Join a machine to a realm
3387 * Creates the machine account and sets the machine password
3388 * @param ads connection to ads server
3389 * @param machine name of host to add
3390 * @param org_unit Organizational unit to place machine in
3391 * @return status of join
3393 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3394 uint32 account_type, const char *org_unit)
3396 ADS_STATUS status;
3397 LDAPMessage *res = NULL;
3398 char *machine;
3400 /* machine name must be lowercase */
3401 machine = SMB_STRDUP(machine_name);
3402 strlower_m(machine);
3405 status = ads_find_machine_acct(ads, (void **)&res, machine);
3406 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3407 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3408 status = ads_leave_realm(ads, machine);
3409 if (!ADS_ERR_OK(status)) {
3410 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3411 machine, ads->config.realm));
3412 return status;
3416 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3417 if (!ADS_ERR_OK(status)) {
3418 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3419 SAFE_FREE(machine);
3420 return status;
3423 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3424 if (!ADS_ERR_OK(status)) {
3425 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3426 SAFE_FREE(machine);
3427 return status;
3430 SAFE_FREE(machine);
3431 ads_msgfree(ads, res);
3433 return status;
3435 #endif
3438 * Delete a machine from the realm
3439 * @param ads connection to ads server
3440 * @param hostname Machine to remove
3441 * @return status of delete
3443 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3445 ADS_STATUS status;
3446 void *msg;
3447 LDAPMessage *res;
3448 char *hostnameDN, *host;
3449 int rc;
3450 LDAPControl ldap_control;
3451 LDAPControl * pldap_control[2] = {NULL, NULL};
3453 pldap_control[0] = &ldap_control;
3454 memset(&ldap_control, 0, sizeof(LDAPControl));
3455 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3457 /* hostname must be lowercase */
3458 host = SMB_STRDUP(hostname);
3459 strlower_m(host);
3461 status = ads_find_machine_acct(ads, &res, host);
3462 if (!ADS_ERR_OK(status)) {
3463 DEBUG(0, ("Host account for %s does not exist.\n", host));
3464 SAFE_FREE(host);
3465 return status;
3468 msg = ads_first_entry(ads, res);
3469 if (!msg) {
3470 SAFE_FREE(host);
3471 return ADS_ERROR_SYSTEM(ENOENT);
3474 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3475 if (hostnameDN == NULL) {
3476 SAFE_FREE(host);
3477 return ADS_ERROR_SYSTEM(ENOENT);
3480 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3481 if (rc) {
3482 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3483 }else {
3484 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3487 if (rc != LDAP_SUCCESS) {
3488 const char *attrs[] = { "cn", NULL };
3489 LDAPMessage *msg_sub;
3491 /* we only search with scope ONE, we do not expect any further
3492 * objects to be created deeper */
3494 status = ads_do_search_retry(ads, hostnameDN,
3495 LDAP_SCOPE_ONELEVEL,
3496 "(objectclass=*)", attrs, &res);
3498 if (!ADS_ERR_OK(status)) {
3499 SAFE_FREE(host);
3500 TALLOC_FREE(hostnameDN);
3501 return status;
3504 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3505 msg_sub = ads_next_entry(ads, msg_sub)) {
3507 char *dn = NULL;
3509 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3510 SAFE_FREE(host);
3511 TALLOC_FREE(hostnameDN);
3512 return ADS_ERROR(LDAP_NO_MEMORY);
3515 status = ads_del_dn(ads, dn);
3516 if (!ADS_ERR_OK(status)) {
3517 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3518 SAFE_FREE(host);
3519 TALLOC_FREE(dn);
3520 TALLOC_FREE(hostnameDN);
3521 return status;
3524 TALLOC_FREE(dn);
3527 /* there should be no subordinate objects anymore */
3528 status = ads_do_search_retry(ads, hostnameDN,
3529 LDAP_SCOPE_ONELEVEL,
3530 "(objectclass=*)", attrs, &res);
3532 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3533 SAFE_FREE(host);
3534 TALLOC_FREE(hostnameDN);
3535 return status;
3538 /* delete hostnameDN now */
3539 status = ads_del_dn(ads, hostnameDN);
3540 if (!ADS_ERR_OK(status)) {
3541 SAFE_FREE(host);
3542 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3543 TALLOC_FREE(hostnameDN);
3544 return status;
3548 TALLOC_FREE(hostnameDN);
3550 status = ads_find_machine_acct(ads, &res, host);
3551 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3552 DEBUG(3, ("Failed to remove host account.\n"));
3553 SAFE_FREE(host);
3554 return status;
3557 SAFE_FREE(host);
3558 return status;
3562 * pull all token-sids from an LDAP dn
3563 * @param ads connection to ads server
3564 * @param mem_ctx TALLOC_CTX for allocating sid array
3565 * @param dn of LDAP object
3566 * @param user_sid pointer to struct dom_sid (objectSid)
3567 * @param primary_group_sid pointer to struct dom_sid (self composed)
3568 * @param sids pointer to sid array to allocate
3569 * @param num_sids counter of SIDs pulled
3570 * @return status of token query
3572 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3573 TALLOC_CTX *mem_ctx,
3574 const char *dn,
3575 struct dom_sid *user_sid,
3576 struct dom_sid *primary_group_sid,
3577 struct dom_sid **sids,
3578 size_t *num_sids)
3580 ADS_STATUS status;
3581 LDAPMessage *res = NULL;
3582 int count = 0;
3583 size_t tmp_num_sids;
3584 struct dom_sid *tmp_sids;
3585 struct dom_sid tmp_user_sid;
3586 struct dom_sid tmp_primary_group_sid;
3587 uint32 pgid;
3588 const char *attrs[] = {
3589 "objectSid",
3590 "tokenGroups",
3591 "primaryGroupID",
3592 NULL
3595 status = ads_search_retry_dn(ads, &res, dn, attrs);
3596 if (!ADS_ERR_OK(status)) {
3597 return status;
3600 count = ads_count_replies(ads, res);
3601 if (count != 1) {
3602 ads_msgfree(ads, res);
3603 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3606 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3607 ads_msgfree(ads, res);
3608 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3611 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3612 ads_msgfree(ads, res);
3613 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3617 /* hack to compose the primary group sid without knowing the
3618 * domsid */
3620 struct dom_sid domsid;
3622 sid_copy(&domsid, &tmp_user_sid);
3624 if (!sid_split_rid(&domsid, NULL)) {
3625 ads_msgfree(ads, res);
3626 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3629 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3630 ads_msgfree(ads, res);
3631 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3635 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3637 if (tmp_num_sids == 0 || !tmp_sids) {
3638 ads_msgfree(ads, res);
3639 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3642 if (num_sids) {
3643 *num_sids = tmp_num_sids;
3646 if (sids) {
3647 *sids = tmp_sids;
3650 if (user_sid) {
3651 *user_sid = tmp_user_sid;
3654 if (primary_group_sid) {
3655 *primary_group_sid = tmp_primary_group_sid;
3658 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3660 ads_msgfree(ads, res);
3661 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3665 * Find a sAMAccoutName in LDAP
3666 * @param ads connection to ads server
3667 * @param mem_ctx TALLOC_CTX for allocating sid array
3668 * @param samaccountname to search
3669 * @param uac_ret uint32 pointer userAccountControl attribute value
3670 * @param dn_ret pointer to dn
3671 * @return status of token query
3673 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3674 TALLOC_CTX *mem_ctx,
3675 const char *samaccountname,
3676 uint32 *uac_ret,
3677 const char **dn_ret)
3679 ADS_STATUS status;
3680 const char *attrs[] = { "userAccountControl", NULL };
3681 const char *filter;
3682 LDAPMessage *res = NULL;
3683 char *dn = NULL;
3684 uint32 uac = 0;
3686 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3687 samaccountname);
3688 if (filter == NULL) {
3689 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3690 goto out;
3693 status = ads_do_search_all(ads, ads->config.bind_path,
3694 LDAP_SCOPE_SUBTREE,
3695 filter, attrs, &res);
3697 if (!ADS_ERR_OK(status)) {
3698 goto out;
3701 if (ads_count_replies(ads, res) != 1) {
3702 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3703 goto out;
3706 dn = ads_get_dn(ads, talloc_tos(), res);
3707 if (dn == NULL) {
3708 status = ADS_ERROR(LDAP_NO_MEMORY);
3709 goto out;
3712 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3713 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3714 goto out;
3717 if (uac_ret) {
3718 *uac_ret = uac;
3721 if (dn_ret) {
3722 *dn_ret = talloc_strdup(mem_ctx, dn);
3723 if (!*dn_ret) {
3724 status = ADS_ERROR(LDAP_NO_MEMORY);
3725 goto out;
3728 out:
3729 TALLOC_FREE(dn);
3730 ads_msgfree(ads, res);
3732 return status;
3736 * find our configuration path
3737 * @param ads connection to ads server
3738 * @param mem_ctx Pointer to talloc context
3739 * @param config_path Pointer to the config path
3740 * @return status of search
3742 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3743 TALLOC_CTX *mem_ctx,
3744 char **config_path)
3746 ADS_STATUS status;
3747 LDAPMessage *res = NULL;
3748 const char *config_context = NULL;
3749 const char *attrs[] = { "configurationNamingContext", NULL };
3751 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3752 "(objectclass=*)", attrs, &res);
3753 if (!ADS_ERR_OK(status)) {
3754 return status;
3757 config_context = ads_pull_string(ads, mem_ctx, res,
3758 "configurationNamingContext");
3759 ads_msgfree(ads, res);
3760 if (!config_context) {
3761 return ADS_ERROR(LDAP_NO_MEMORY);
3764 if (config_path) {
3765 *config_path = talloc_strdup(mem_ctx, config_context);
3766 if (!*config_path) {
3767 return ADS_ERROR(LDAP_NO_MEMORY);
3771 return ADS_ERROR(LDAP_SUCCESS);
3775 * find the displayName of an extended right
3776 * @param ads connection to ads server
3777 * @param config_path The config path
3778 * @param mem_ctx Pointer to talloc context
3779 * @param GUID struct of the rightsGUID
3780 * @return status of search
3782 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3783 const char *config_path,
3784 TALLOC_CTX *mem_ctx,
3785 const struct GUID *rights_guid)
3787 ADS_STATUS rc;
3788 LDAPMessage *res = NULL;
3789 char *expr = NULL;
3790 const char *attrs[] = { "displayName", NULL };
3791 const char *result = NULL;
3792 const char *path;
3794 if (!ads || !mem_ctx || !rights_guid) {
3795 goto done;
3798 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3799 GUID_string(mem_ctx, rights_guid));
3800 if (!expr) {
3801 goto done;
3804 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3805 if (!path) {
3806 goto done;
3809 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3810 expr, attrs, &res);
3811 if (!ADS_ERR_OK(rc)) {
3812 goto done;
3815 if (ads_count_replies(ads, res) != 1) {
3816 goto done;
3819 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3821 done:
3822 ads_msgfree(ads, res);
3823 return result;
3827 * verify or build and verify an account ou
3828 * @param mem_ctx Pointer to talloc context
3829 * @param ads connection to ads server
3830 * @param account_ou
3831 * @return status of search
3834 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3835 ADS_STRUCT *ads,
3836 const char **account_ou)
3838 char **exploded_dn;
3839 const char *name;
3840 char *ou_string;
3842 exploded_dn = ldap_explode_dn(*account_ou, 0);
3843 if (exploded_dn) {
3844 ldap_value_free(exploded_dn);
3845 return ADS_SUCCESS;
3848 ou_string = ads_ou_string(ads, *account_ou);
3849 if (!ou_string) {
3850 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3853 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3854 ads->config.bind_path);
3855 SAFE_FREE(ou_string);
3857 if (!name) {
3858 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3861 exploded_dn = ldap_explode_dn(name, 0);
3862 if (!exploded_dn) {
3863 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3865 ldap_value_free(exploded_dn);
3867 *account_ou = name;
3868 return ADS_SUCCESS;
3871 #endif