script/autobuild: make use of --with-perl-{arch,lib}-install-dir
[Samba.git] / source3 / libads / ldap.c
blobfb99132e57ef9a89cc54b88601a1e4ea7362d683
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 if (!strupper_m(ads->config.realm)) {
302 ret = false;
303 goto out;
306 ads->config.bind_path = ads_build_dn(ads->config.realm);
307 if (*cldap_reply.server_site) {
308 ads->config.server_site_name =
309 SMB_STRDUP(cldap_reply.server_site);
311 if (*cldap_reply.client_site) {
312 ads->config.client_site_name =
313 SMB_STRDUP(cldap_reply.client_site);
315 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
317 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
318 ads->ldap.ss = ss;
320 /* Store our site name. */
321 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
322 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
324 ret = true;
326 out:
328 TALLOC_FREE(frame);
329 return ret;
332 /**********************************************************************
333 Try to find an AD dc using our internal name resolution routines
334 Try the realm first and then then workgroup name if netbios is not
335 disabled
336 **********************************************************************/
338 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
340 const char *c_domain;
341 const char *c_realm;
342 int count, i=0;
343 struct ip_service *ip_list;
344 const char *realm;
345 const char *domain;
346 bool got_realm = False;
347 bool use_own_domain = False;
348 char *sitename;
349 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
351 /* if the realm and workgroup are both empty, assume they are ours */
353 /* realm */
354 c_realm = ads->server.realm;
356 if ( !c_realm || !*c_realm ) {
357 /* special case where no realm and no workgroup means our own */
358 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
359 use_own_domain = True;
360 c_realm = lp_realm();
364 if (c_realm && *c_realm)
365 got_realm = True;
367 /* we need to try once with the realm name and fallback to the
368 netbios domain name if we fail (if netbios has not been disabled */
370 if ( !got_realm && !lp_disable_netbios() ) {
371 c_realm = ads->server.workgroup;
372 if (!c_realm || !*c_realm) {
373 if ( use_own_domain )
374 c_realm = lp_workgroup();
378 if ( !c_realm || !*c_realm ) {
379 DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know "
380 "what to do\n"));
381 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
384 if ( use_own_domain ) {
385 c_domain = lp_workgroup();
386 } else {
387 c_domain = ads->server.workgroup;
390 realm = c_realm;
391 domain = c_domain;
394 * In case of LDAP we use get_dc_name() as that
395 * creates the custom krb5.conf file
397 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
398 fstring srv_name;
399 struct sockaddr_storage ip_out;
401 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
402 (got_realm ? "realm" : "domain"), realm));
404 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
406 * we call ads_try_connect() to fill in the
407 * ads->config details
409 if (ads_try_connect(ads, srv_name, false)) {
410 return NT_STATUS_OK;
414 return NT_STATUS_NO_LOGON_SERVERS;
417 sitename = sitename_fetch(realm);
419 again:
421 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
422 (got_realm ? "realm" : "domain"), realm));
424 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
425 if (!NT_STATUS_IS_OK(status)) {
426 /* fall back to netbios if we can */
427 if ( got_realm && !lp_disable_netbios() ) {
428 got_realm = False;
429 goto again;
432 SAFE_FREE(sitename);
433 return status;
436 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
437 for ( i=0; i<count; i++ ) {
438 char server[INET6_ADDRSTRLEN];
440 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
442 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
443 continue;
445 if (!got_realm) {
446 /* realm in this case is a workgroup name. We need
447 to ignore any IP addresses in the negative connection
448 cache that match ip addresses returned in the ad realm
449 case. It sucks that I have to reproduce the logic above... */
450 c_realm = ads->server.realm;
451 if ( !c_realm || !*c_realm ) {
452 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
453 c_realm = lp_realm();
456 if (c_realm && *c_realm &&
457 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
458 /* Ensure we add the workgroup name for this
459 IP address as negative too. */
460 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
461 continue;
465 if ( ads_try_connect(ads, server, false) ) {
466 SAFE_FREE(ip_list);
467 SAFE_FREE(sitename);
468 return NT_STATUS_OK;
471 /* keep track of failures */
472 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
475 SAFE_FREE(ip_list);
477 /* In case we failed to contact one of our closest DC on our site we
478 * need to try to find another DC, retry with a site-less SRV DNS query
479 * - Guenther */
481 if (sitename) {
482 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
483 "trying to find another DC\n", sitename));
484 SAFE_FREE(sitename);
485 namecache_delete(realm, 0x1C);
486 goto again;
489 return NT_STATUS_NO_LOGON_SERVERS;
492 /*********************************************************************
493 *********************************************************************/
495 static NTSTATUS ads_lookup_site(void)
497 ADS_STRUCT *ads = NULL;
498 ADS_STATUS ads_status;
499 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
501 ads = ads_init(lp_realm(), NULL, NULL);
502 if (!ads) {
503 return NT_STATUS_NO_MEMORY;
506 /* The NO_BIND here will find a DC and set the client site
507 but not establish the TCP connection */
509 ads->auth.flags = ADS_AUTH_NO_BIND;
510 ads_status = ads_connect(ads);
511 if (!ADS_ERR_OK(ads_status)) {
512 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
513 ads_errstr(ads_status)));
515 nt_status = ads_ntstatus(ads_status);
517 if (ads) {
518 ads_destroy(&ads);
521 return nt_status;
524 /*********************************************************************
525 *********************************************************************/
527 static const char* host_dns_domain(const char *fqdn)
529 const char *p = fqdn;
531 /* go to next char following '.' */
533 if ((p = strchr_m(fqdn, '.')) != NULL) {
534 p++;
537 return p;
542 * Connect to the Global Catalog server
543 * @param ads Pointer to an existing ADS_STRUCT
544 * @return status of connection
546 * Simple wrapper around ads_connect() that fills in the
547 * GC ldap server information
550 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
552 TALLOC_CTX *frame = talloc_stackframe();
553 struct dns_rr_srv *gcs_list;
554 int num_gcs;
555 const char *realm = ads->server.realm;
556 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
557 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
558 int i;
559 bool done = false;
560 char *sitename = NULL;
561 const char *dns_hosts_file;
563 if (!realm)
564 realm = lp_realm();
566 if ((sitename = sitename_fetch(realm)) == NULL) {
567 ads_lookup_site();
568 sitename = sitename_fetch(realm);
571 dns_hosts_file = lp_parm_const_string(-1, "resolv", "host file", NULL);
572 do {
573 /* We try once with a sitename and once without
574 (unless we don't have a sitename and then we're
575 done */
577 if (sitename == NULL)
578 done = true;
580 nt_status = ads_dns_query_gcs(frame, dns_hosts_file,
581 realm, sitename,
582 &gcs_list, &num_gcs);
584 SAFE_FREE(sitename);
586 if (!NT_STATUS_IS_OK(nt_status)) {
587 ads_status = ADS_ERROR_NT(nt_status);
588 goto done;
591 /* Loop until we get a successful connection or have gone
592 through them all. When connecting a GC server, make sure that
593 the realm is the server's DNS name and not the forest root */
595 for (i=0; i<num_gcs; i++) {
596 ads->server.gc = true;
597 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
598 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
599 ads_status = ads_connect(ads);
600 if (ADS_ERR_OK(ads_status)) {
601 /* Reset the bind_dn to "". A Global Catalog server
602 may host multiple domain trees in a forest.
603 Windows 2003 GC server will accept "" as the search
604 path to imply search all domain trees in the forest */
606 SAFE_FREE(ads->config.bind_path);
607 ads->config.bind_path = SMB_STRDUP("");
610 goto done;
612 SAFE_FREE(ads->server.ldap_server);
613 SAFE_FREE(ads->server.realm);
616 TALLOC_FREE(gcs_list);
617 num_gcs = 0;
618 } while (!done);
620 done:
621 SAFE_FREE(sitename);
622 talloc_destroy(frame);
624 return ads_status;
629 * Connect to the LDAP server
630 * @param ads Pointer to an existing ADS_STRUCT
631 * @return status of connection
633 ADS_STATUS ads_connect(ADS_STRUCT *ads)
635 int version = LDAP_VERSION3;
636 ADS_STATUS status;
637 NTSTATUS ntstatus;
638 char addr[INET6_ADDRSTRLEN];
640 ZERO_STRUCT(ads->ldap);
641 ads->ldap.last_attempt = time_mono(NULL);
642 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
644 /* try with a user specified server */
646 if (DEBUGLEVEL >= 11) {
647 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
648 DEBUG(11,("ads_connect: entering\n"));
649 DEBUGADD(11,("%s\n", s));
650 TALLOC_FREE(s);
653 if (ads->server.ldap_server)
655 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
656 goto got_connection;
659 /* The choice of which GC use is handled one level up in
660 ads_connect_gc(). If we continue on from here with
661 ads_find_dc() we will get GC searches on port 389 which
662 doesn't work. --jerry */
664 if (ads->server.gc == true) {
665 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
669 ntstatus = ads_find_dc(ads);
670 if (NT_STATUS_IS_OK(ntstatus)) {
671 goto got_connection;
674 status = ADS_ERROR_NT(ntstatus);
675 goto out;
677 got_connection:
679 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
680 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
682 if (!ads->auth.user_name) {
683 /* Must use the userPrincipalName value here or sAMAccountName
684 and not servicePrincipalName; found by Guenther Deschner */
686 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
687 DEBUG(0,("ads_connect: asprintf fail.\n"));
688 ads->auth.user_name = NULL;
692 if (!ads->auth.realm) {
693 ads->auth.realm = SMB_STRDUP(ads->config.realm);
696 if (!ads->auth.kdc_server) {
697 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
698 ads->auth.kdc_server = SMB_STRDUP(addr);
701 /* If the caller() requested no LDAP bind, then we are done */
703 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
704 status = ADS_SUCCESS;
705 goto out;
708 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
709 if (!ads->ldap.mem_ctx) {
710 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
711 goto out;
714 /* Otherwise setup the TCP LDAP session */
716 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
717 &ads->ldap.ss,
718 ads->ldap.port, lp_ldap_timeout());
719 if (ads->ldap.ld == NULL) {
720 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
721 goto out;
723 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
725 /* cache the successful connection for workgroup and realm */
726 if (ads_closest_dc(ads)) {
727 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
728 saf_store( ads->server.realm, ads->config.ldap_server_name);
731 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
733 if ( lp_ldap_ssl_ads() ) {
734 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
735 if (!ADS_ERR_OK(status)) {
736 goto out;
740 /* fill in the current time and offsets */
742 status = ads_current_time( ads );
743 if ( !ADS_ERR_OK(status) ) {
744 goto out;
747 /* Now do the bind */
749 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
750 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
751 goto out;
754 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
755 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
756 goto out;
759 status = ads_sasl_bind(ads);
761 out:
762 if (DEBUGLEVEL >= 11) {
763 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
764 DEBUG(11,("ads_connect: leaving with: %s\n",
765 ads_errstr(status)));
766 DEBUGADD(11,("%s\n", s));
767 TALLOC_FREE(s);
770 return status;
774 * Connect to the LDAP server using given credentials
775 * @param ads Pointer to an existing ADS_STRUCT
776 * @return status of connection
778 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
780 ads->auth.flags |= ADS_AUTH_USER_CREDS;
782 return ads_connect(ads);
786 * Disconnect the LDAP server
787 * @param ads Pointer to an existing ADS_STRUCT
789 void ads_disconnect(ADS_STRUCT *ads)
791 if (ads->ldap.ld) {
792 ldap_unbind(ads->ldap.ld);
793 ads->ldap.ld = NULL;
795 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
796 ads->ldap.wrap_ops->disconnect(ads);
798 if (ads->ldap.mem_ctx) {
799 talloc_free(ads->ldap.mem_ctx);
801 ZERO_STRUCT(ads->ldap);
805 Duplicate a struct berval into talloc'ed memory
807 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
809 struct berval *value;
811 if (!in_val) return NULL;
813 value = talloc_zero(ctx, struct berval);
814 if (value == NULL)
815 return NULL;
816 if (in_val->bv_len == 0) return value;
818 value->bv_len = in_val->bv_len;
819 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
820 in_val->bv_len);
821 return value;
825 Make a values list out of an array of (struct berval *)
827 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
828 const struct berval **in_vals)
830 struct berval **values;
831 int i;
833 if (!in_vals) return NULL;
834 for (i=0; in_vals[i]; i++)
835 ; /* count values */
836 values = talloc_zero_array(ctx, struct berval *, i+1);
837 if (!values) return NULL;
839 for (i=0; in_vals[i]; i++) {
840 values[i] = dup_berval(ctx, in_vals[i]);
842 return values;
846 UTF8-encode a values list out of an array of (char *)
848 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
850 char **values;
851 int i;
852 size_t size;
854 if (!in_vals) return NULL;
855 for (i=0; in_vals[i]; i++)
856 ; /* count values */
857 values = talloc_zero_array(ctx, char *, i+1);
858 if (!values) return NULL;
860 for (i=0; in_vals[i]; i++) {
861 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
862 TALLOC_FREE(values);
863 return NULL;
866 return values;
870 Pull a (char *) array out of a UTF8-encoded values list
872 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
874 char **values;
875 int i;
876 size_t converted_size;
878 if (!in_vals) return NULL;
879 for (i=0; in_vals[i]; i++)
880 ; /* count values */
881 values = talloc_zero_array(ctx, char *, i+1);
882 if (!values) return NULL;
884 for (i=0; in_vals[i]; i++) {
885 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
886 &converted_size)) {
887 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
888 "%s", strerror(errno)));
891 return values;
895 * Do a search with paged results. cookie must be null on the first
896 * call, and then returned on each subsequent call. It will be null
897 * again when the entire search is complete
898 * @param ads connection to ads server
899 * @param bind_path Base dn for the search
900 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
901 * @param expr Search expression - specified in local charset
902 * @param attrs Attributes to retrieve - specified in utf8 or ascii
903 * @param res ** which will contain results - free res* with ads_msgfree()
904 * @param count Number of entries retrieved on this page
905 * @param cookie The paged results cookie to be returned on subsequent calls
906 * @return status of search
908 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
909 const char *bind_path,
910 int scope, const char *expr,
911 const char **attrs, void *args,
912 LDAPMessage **res,
913 int *count, struct berval **cookie)
915 int rc, i, version;
916 char *utf8_expr, *utf8_path, **search_attrs = NULL;
917 size_t converted_size;
918 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
919 BerElement *cookie_be = NULL;
920 struct berval *cookie_bv= NULL;
921 BerElement *ext_be = NULL;
922 struct berval *ext_bv= NULL;
924 TALLOC_CTX *ctx;
925 ads_control *external_control = (ads_control *) args;
927 *res = NULL;
929 if (!(ctx = talloc_init("ads_do_paged_search_args")))
930 return ADS_ERROR(LDAP_NO_MEMORY);
932 /* 0 means the conversion worked but the result was empty
933 so we only fail if it's -1. In any case, it always
934 at least nulls out the dest */
935 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
936 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
938 rc = LDAP_NO_MEMORY;
939 goto done;
942 if (!attrs || !(*attrs))
943 search_attrs = NULL;
944 else {
945 /* This would be the utf8-encoded version...*/
946 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
947 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
948 rc = LDAP_NO_MEMORY;
949 goto done;
953 /* Paged results only available on ldap v3 or later */
954 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
955 if (version < LDAP_VERSION3) {
956 rc = LDAP_NOT_SUPPORTED;
957 goto done;
960 cookie_be = ber_alloc_t(LBER_USE_DER);
961 if (*cookie) {
962 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
963 ber_bvfree(*cookie); /* don't need it from last time */
964 *cookie = NULL;
965 } else {
966 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
968 ber_flatten(cookie_be, &cookie_bv);
969 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
970 PagedResults.ldctl_iscritical = (char) 1;
971 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
972 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
974 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
975 NoReferrals.ldctl_iscritical = (char) 0;
976 NoReferrals.ldctl_value.bv_len = 0;
977 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
979 if (external_control &&
980 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
981 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
983 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
984 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
986 /* win2k does not accept a ldctl_value beeing passed in */
988 if (external_control->val != 0) {
990 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
991 rc = LDAP_NO_MEMORY;
992 goto done;
995 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
996 rc = LDAP_NO_MEMORY;
997 goto done;
999 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
1000 rc = LDAP_NO_MEMORY;
1001 goto done;
1004 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1005 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1007 } else {
1008 ExternalCtrl.ldctl_value.bv_len = 0;
1009 ExternalCtrl.ldctl_value.bv_val = NULL;
1012 controls[0] = &NoReferrals;
1013 controls[1] = &PagedResults;
1014 controls[2] = &ExternalCtrl;
1015 controls[3] = NULL;
1017 } else {
1018 controls[0] = &NoReferrals;
1019 controls[1] = &PagedResults;
1020 controls[2] = NULL;
1023 /* we need to disable referrals as the openldap libs don't
1024 handle them and paged results at the same time. Using them
1025 together results in the result record containing the server
1026 page control being removed from the result list (tridge/jmcd)
1028 leaving this in despite the control that says don't generate
1029 referrals, in case the server doesn't support it (jmcd)
1031 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1033 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1034 search_attrs, 0, controls,
1035 NULL, LDAP_NO_LIMIT,
1036 (LDAPMessage **)res);
1038 ber_free(cookie_be, 1);
1039 ber_bvfree(cookie_bv);
1041 if (rc) {
1042 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1043 ldap_err2string(rc)));
1044 if (rc == LDAP_OTHER) {
1045 char *ldap_errmsg;
1046 int ret;
1048 ret = ldap_parse_result(ads->ldap.ld,
1049 *res,
1050 NULL,
1051 NULL,
1052 &ldap_errmsg,
1053 NULL,
1054 NULL,
1056 if (ret == LDAP_SUCCESS) {
1057 DEBUG(3, ("ldap_search_with_timeout(%s) "
1058 "error: %s\n", expr, ldap_errmsg));
1059 ldap_memfree(ldap_errmsg);
1062 goto done;
1065 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1066 NULL, &rcontrols, 0);
1068 if (!rcontrols) {
1069 goto done;
1072 for (i=0; rcontrols[i]; i++) {
1073 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1074 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1075 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1076 &cookie_bv);
1077 /* the berval is the cookie, but must be freed when
1078 it is all done */
1079 if (cookie_bv->bv_len) /* still more to do */
1080 *cookie=ber_bvdup(cookie_bv);
1081 else
1082 *cookie=NULL;
1083 ber_bvfree(cookie_bv);
1084 ber_free(cookie_be, 1);
1085 break;
1088 ldap_controls_free(rcontrols);
1090 done:
1091 talloc_destroy(ctx);
1093 if (ext_be) {
1094 ber_free(ext_be, 1);
1097 if (ext_bv) {
1098 ber_bvfree(ext_bv);
1101 /* if/when we decide to utf8-encode attrs, take out this next line */
1102 TALLOC_FREE(search_attrs);
1104 return ADS_ERROR(rc);
1107 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1108 int scope, const char *expr,
1109 const char **attrs, LDAPMessage **res,
1110 int *count, struct berval **cookie)
1112 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1117 * Get all results for a search. This uses ads_do_paged_search() to return
1118 * all entries in a large search.
1119 * @param ads connection to ads server
1120 * @param bind_path Base dn for the search
1121 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1122 * @param expr Search expression
1123 * @param attrs Attributes to retrieve
1124 * @param res ** which will contain results - free res* with ads_msgfree()
1125 * @return status of search
1127 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1128 int scope, const char *expr,
1129 const char **attrs, void *args,
1130 LDAPMessage **res)
1132 struct berval *cookie = NULL;
1133 int count = 0;
1134 ADS_STATUS status;
1136 *res = NULL;
1137 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1138 &count, &cookie);
1140 if (!ADS_ERR_OK(status))
1141 return status;
1143 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1144 while (cookie) {
1145 LDAPMessage *res2 = NULL;
1146 LDAPMessage *msg, *next;
1148 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1149 attrs, args, &res2, &count, &cookie);
1150 if (!ADS_ERR_OK(status)) {
1151 /* Ensure we free all collected results */
1152 ads_msgfree(ads, *res);
1153 *res = NULL;
1154 break;
1157 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1158 that this works on all ldap libs, but I have only tested with openldap */
1159 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1160 next = ads_next_message(ads, msg);
1161 ldap_add_result_entry((LDAPMessage **)res, msg);
1163 /* note that we do not free res2, as the memory is now
1164 part of the main returned list */
1166 #else
1167 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1168 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1169 #endif
1171 return status;
1174 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1175 int scope, const char *expr,
1176 const char **attrs, LDAPMessage **res)
1178 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1181 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1182 int scope, const char *expr,
1183 const char **attrs, uint32 sd_flags,
1184 LDAPMessage **res)
1186 ads_control args;
1188 args.control = ADS_SD_FLAGS_OID;
1189 args.val = sd_flags;
1190 args.critical = True;
1192 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1197 * Run a function on all results for a search. Uses ads_do_paged_search() and
1198 * runs the function as each page is returned, using ads_process_results()
1199 * @param ads connection to ads server
1200 * @param bind_path Base dn for the search
1201 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1202 * @param expr Search expression - specified in local charset
1203 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1204 * @param fn Function which takes attr name, values list, and data_area
1205 * @param data_area Pointer which is passed to function on each call
1206 * @return status of search
1208 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1209 int scope, const char *expr, const char **attrs,
1210 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1211 void *data_area)
1213 struct berval *cookie = NULL;
1214 int count = 0;
1215 ADS_STATUS status;
1216 LDAPMessage *res;
1218 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1219 &count, &cookie);
1221 if (!ADS_ERR_OK(status)) return status;
1223 ads_process_results(ads, res, fn, data_area);
1224 ads_msgfree(ads, res);
1226 while (cookie) {
1227 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1228 &res, &count, &cookie);
1230 if (!ADS_ERR_OK(status)) break;
1232 ads_process_results(ads, res, fn, data_area);
1233 ads_msgfree(ads, res);
1236 return status;
1240 * Do a search with a timeout.
1241 * @param ads connection to ads server
1242 * @param bind_path Base dn for the search
1243 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1244 * @param expr Search expression
1245 * @param attrs Attributes to retrieve
1246 * @param res ** which will contain results - free res* with ads_msgfree()
1247 * @return status of search
1249 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1250 const char *expr,
1251 const char **attrs, LDAPMessage **res)
1253 int rc;
1254 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1255 size_t converted_size;
1256 TALLOC_CTX *ctx;
1258 *res = NULL;
1259 if (!(ctx = talloc_init("ads_do_search"))) {
1260 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1261 return ADS_ERROR(LDAP_NO_MEMORY);
1264 /* 0 means the conversion worked but the result was empty
1265 so we only fail if it's negative. In any case, it always
1266 at least nulls out the dest */
1267 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1268 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1270 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1271 rc = LDAP_NO_MEMORY;
1272 goto done;
1275 if (!attrs || !(*attrs))
1276 search_attrs = NULL;
1277 else {
1278 /* This would be the utf8-encoded version...*/
1279 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1280 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1282 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1283 rc = LDAP_NO_MEMORY;
1284 goto done;
1288 /* see the note in ads_do_paged_search - we *must* disable referrals */
1289 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1291 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1292 search_attrs, 0, NULL, NULL,
1293 LDAP_NO_LIMIT,
1294 (LDAPMessage **)res);
1296 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1297 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1298 rc = 0;
1301 done:
1302 talloc_destroy(ctx);
1303 /* if/when we decide to utf8-encode attrs, take out this next line */
1304 TALLOC_FREE(search_attrs);
1305 return ADS_ERROR(rc);
1308 * Do a general ADS search
1309 * @param ads connection to ads server
1310 * @param res ** which will contain results - free res* with ads_msgfree()
1311 * @param expr Search expression
1312 * @param attrs Attributes to retrieve
1313 * @return status of search
1315 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1316 const char *expr, const char **attrs)
1318 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1319 expr, attrs, res);
1323 * Do a search on a specific DistinguishedName
1324 * @param ads connection to ads server
1325 * @param res ** which will contain results - free res* with ads_msgfree()
1326 * @param dn DistinguishName to search
1327 * @param attrs Attributes to retrieve
1328 * @return status of search
1330 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1331 const char *dn, const char **attrs)
1333 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1334 attrs, res);
1338 * Free up memory from a ads_search
1339 * @param ads connection to ads server
1340 * @param msg Search results to free
1342 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1344 if (!msg) return;
1345 ldap_msgfree(msg);
1349 * Get a dn from search results
1350 * @param ads connection to ads server
1351 * @param msg Search result
1352 * @return dn string
1354 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1356 char *utf8_dn, *unix_dn;
1357 size_t converted_size;
1359 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1361 if (!utf8_dn) {
1362 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1363 return NULL;
1366 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1367 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1368 utf8_dn ));
1369 return NULL;
1371 ldap_memfree(utf8_dn);
1372 return unix_dn;
1376 * Get the parent from a dn
1377 * @param dn the dn to return the parent from
1378 * @return parent dn string
1380 char *ads_parent_dn(const char *dn)
1382 char *p;
1384 if (dn == NULL) {
1385 return NULL;
1388 p = strchr(dn, ',');
1390 if (p == NULL) {
1391 return NULL;
1394 return p+1;
1398 * Find a machine account given a hostname
1399 * @param ads connection to ads server
1400 * @param res ** which will contain results - free res* with ads_msgfree()
1401 * @param host Hostname to search for
1402 * @return status of search
1404 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1405 const char *machine)
1407 ADS_STATUS status;
1408 char *expr;
1409 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1411 *res = NULL;
1413 /* the easiest way to find a machine account anywhere in the tree
1414 is to look for hostname$ */
1415 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1416 DEBUG(1, ("asprintf failed!\n"));
1417 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1420 status = ads_search(ads, res, expr, attrs);
1421 SAFE_FREE(expr);
1422 return status;
1426 * Initialize a list of mods to be used in a modify request
1427 * @param ctx An initialized TALLOC_CTX
1428 * @return allocated ADS_MODLIST
1430 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1432 #define ADS_MODLIST_ALLOC_SIZE 10
1433 LDAPMod **mods;
1435 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1436 /* -1 is safety to make sure we don't go over the end.
1437 need to reset it to NULL before doing ldap modify */
1438 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1440 return (ADS_MODLIST)mods;
1445 add an attribute to the list, with values list already constructed
1447 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1448 int mod_op, const char *name,
1449 const void *_invals)
1451 const void **invals = (const void **)_invals;
1452 int curmod;
1453 LDAPMod **modlist = (LDAPMod **) *mods;
1454 struct berval **ber_values = NULL;
1455 char **char_values = NULL;
1457 if (!invals) {
1458 mod_op = LDAP_MOD_DELETE;
1459 } else {
1460 if (mod_op & LDAP_MOD_BVALUES)
1461 ber_values = ads_dup_values(ctx,
1462 (const struct berval **)invals);
1463 else
1464 char_values = ads_push_strvals(ctx,
1465 (const char **) invals);
1468 /* find the first empty slot */
1469 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1470 curmod++);
1471 if (modlist[curmod] == (LDAPMod *) -1) {
1472 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1473 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1474 return ADS_ERROR(LDAP_NO_MEMORY);
1475 memset(&modlist[curmod], 0,
1476 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1477 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1478 *mods = (ADS_MODLIST)modlist;
1481 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1482 return ADS_ERROR(LDAP_NO_MEMORY);
1483 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1484 if (mod_op & LDAP_MOD_BVALUES) {
1485 modlist[curmod]->mod_bvalues = ber_values;
1486 } else if (mod_op & LDAP_MOD_DELETE) {
1487 modlist[curmod]->mod_values = NULL;
1488 } else {
1489 modlist[curmod]->mod_values = char_values;
1492 modlist[curmod]->mod_op = mod_op;
1493 return ADS_ERROR(LDAP_SUCCESS);
1497 * Add a single string value to a mod list
1498 * @param ctx An initialized TALLOC_CTX
1499 * @param mods An initialized ADS_MODLIST
1500 * @param name The attribute name to add
1501 * @param val The value to add - NULL means DELETE
1502 * @return ADS STATUS indicating success of add
1504 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1505 const char *name, const char *val)
1507 const char *values[2];
1509 values[0] = val;
1510 values[1] = NULL;
1512 if (!val)
1513 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1514 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1518 * Add an array of string values to a mod list
1519 * @param ctx An initialized TALLOC_CTX
1520 * @param mods An initialized ADS_MODLIST
1521 * @param name The attribute name to add
1522 * @param vals The array of string values to add - NULL means DELETE
1523 * @return ADS STATUS indicating success of add
1525 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1526 const char *name, const char **vals)
1528 if (!vals)
1529 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1530 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1531 name, (const void **) vals);
1534 #if 0
1536 * Add a single ber-encoded value to a mod list
1537 * @param ctx An initialized TALLOC_CTX
1538 * @param mods An initialized ADS_MODLIST
1539 * @param name The attribute name to add
1540 * @param val The value to add - NULL means DELETE
1541 * @return ADS STATUS indicating success of add
1543 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1544 const char *name, const struct berval *val)
1546 const struct berval *values[2];
1548 values[0] = val;
1549 values[1] = NULL;
1550 if (!val)
1551 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1552 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1553 name, (const void **) values);
1555 #endif
1558 * Perform an ldap modify
1559 * @param ads connection to ads server
1560 * @param mod_dn DistinguishedName to modify
1561 * @param mods list of modifications to perform
1562 * @return status of modify
1564 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1566 int ret,i;
1567 char *utf8_dn = NULL;
1568 size_t converted_size;
1570 this control is needed to modify that contains a currently
1571 non-existent attribute (but allowable for the object) to run
1573 LDAPControl PermitModify = {
1574 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1575 {0, NULL},
1576 (char) 1};
1577 LDAPControl *controls[2];
1579 controls[0] = &PermitModify;
1580 controls[1] = NULL;
1582 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1583 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1586 /* find the end of the list, marked by NULL or -1 */
1587 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1588 /* make sure the end of the list is NULL */
1589 mods[i] = NULL;
1590 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1591 (LDAPMod **) mods, controls, NULL);
1592 TALLOC_FREE(utf8_dn);
1593 return ADS_ERROR(ret);
1597 * Perform an ldap add
1598 * @param ads connection to ads server
1599 * @param new_dn DistinguishedName to add
1600 * @param mods list of attributes and values for DN
1601 * @return status of add
1603 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1605 int ret, i;
1606 char *utf8_dn = NULL;
1607 size_t converted_size;
1609 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1610 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1611 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1614 /* find the end of the list, marked by NULL or -1 */
1615 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1616 /* make sure the end of the list is NULL */
1617 mods[i] = NULL;
1619 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1620 TALLOC_FREE(utf8_dn);
1621 return ADS_ERROR(ret);
1625 * Delete a DistinguishedName
1626 * @param ads connection to ads server
1627 * @param new_dn DistinguishedName to delete
1628 * @return status of delete
1630 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1632 int ret;
1633 char *utf8_dn = NULL;
1634 size_t converted_size;
1635 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1636 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1637 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1640 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1641 TALLOC_FREE(utf8_dn);
1642 return ADS_ERROR(ret);
1646 * Build an org unit string
1647 * if org unit is Computers or blank then assume a container, otherwise
1648 * assume a / separated list of organisational units.
1649 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1650 * @param ads connection to ads server
1651 * @param org_unit Organizational unit
1652 * @return org unit string - caller must free
1654 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1656 char *ret = NULL;
1658 if (!org_unit || !*org_unit) {
1660 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1662 /* samba4 might not yet respond to a wellknownobject-query */
1663 return ret ? ret : SMB_STRDUP("cn=Computers");
1666 if (strequal(org_unit, "Computers")) {
1667 return SMB_STRDUP("cn=Computers");
1670 /* jmcd: removed "\\" from the separation chars, because it is
1671 needed as an escape for chars like '#' which are valid in an
1672 OU name */
1673 return ads_build_path(org_unit, "/", "ou=", 1);
1677 * Get a org unit string for a well-known GUID
1678 * @param ads connection to ads server
1679 * @param wknguid Well known GUID
1680 * @return org unit string - caller must free
1682 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1684 ADS_STATUS status;
1685 LDAPMessage *res = NULL;
1686 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1687 **bind_dn_exp = NULL;
1688 const char *attrs[] = {"distinguishedName", NULL};
1689 int new_ln, wkn_ln, bind_ln, i;
1691 if (wknguid == NULL) {
1692 return NULL;
1695 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1696 DEBUG(1, ("asprintf failed!\n"));
1697 return NULL;
1700 status = ads_search_dn(ads, &res, base, attrs);
1701 if (!ADS_ERR_OK(status)) {
1702 DEBUG(1,("Failed while searching for: %s\n", base));
1703 goto out;
1706 if (ads_count_replies(ads, res) != 1) {
1707 goto out;
1710 /* substitute the bind-path from the well-known-guid-search result */
1711 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1712 if (!wkn_dn) {
1713 goto out;
1716 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1717 if (!wkn_dn_exp) {
1718 goto out;
1721 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1722 if (!bind_dn_exp) {
1723 goto out;
1726 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1728 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1731 new_ln = wkn_ln - bind_ln;
1733 ret = SMB_STRDUP(wkn_dn_exp[0]);
1734 if (!ret) {
1735 goto out;
1738 for (i=1; i < new_ln; i++) {
1739 char *s = NULL;
1741 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1742 SAFE_FREE(ret);
1743 goto out;
1746 SAFE_FREE(ret);
1747 ret = SMB_STRDUP(s);
1748 free(s);
1749 if (!ret) {
1750 goto out;
1754 out:
1755 SAFE_FREE(base);
1756 ads_msgfree(ads, res);
1757 TALLOC_FREE(wkn_dn);
1758 if (wkn_dn_exp) {
1759 ldap_value_free(wkn_dn_exp);
1761 if (bind_dn_exp) {
1762 ldap_value_free(bind_dn_exp);
1765 return ret;
1769 * Adds (appends) an item to an attribute array, rather then
1770 * replacing the whole list
1771 * @param ctx An initialized TALLOC_CTX
1772 * @param mods An initialized ADS_MODLIST
1773 * @param name name of the ldap attribute to append to
1774 * @param vals an array of values to add
1775 * @return status of addition
1778 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1779 const char *name, const char **vals)
1781 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1782 (const void *) vals);
1786 * Determines the an account's current KVNO via an LDAP lookup
1787 * @param ads An initialized ADS_STRUCT
1788 * @param account_name the NT samaccountname.
1789 * @return the kvno for the account, or -1 in case of a failure.
1792 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1794 LDAPMessage *res = NULL;
1795 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1796 char *filter;
1797 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1798 char *dn_string = NULL;
1799 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1801 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1802 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1803 return kvno;
1805 ret = ads_search(ads, &res, filter, attrs);
1806 SAFE_FREE(filter);
1807 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1808 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1809 ads_msgfree(ads, res);
1810 return kvno;
1813 dn_string = ads_get_dn(ads, talloc_tos(), res);
1814 if (!dn_string) {
1815 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1816 ads_msgfree(ads, res);
1817 return kvno;
1819 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1820 TALLOC_FREE(dn_string);
1822 /* ---------------------------------------------------------
1823 * 0 is returned as a default KVNO from this point on...
1824 * This is done because Windows 2000 does not support key
1825 * version numbers. Chances are that a failure in the next
1826 * step is simply due to Windows 2000 being used for a
1827 * domain controller. */
1828 kvno = 0;
1830 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1831 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1832 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1833 ads_msgfree(ads, res);
1834 return kvno;
1837 /* Success */
1838 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1839 ads_msgfree(ads, res);
1840 return kvno;
1844 * Determines the computer account's current KVNO via an LDAP lookup
1845 * @param ads An initialized ADS_STRUCT
1846 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1847 * @return the kvno for the computer account, or -1 in case of a failure.
1850 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1852 char *computer_account = NULL;
1853 uint32_t kvno = -1;
1855 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1856 return kvno;
1859 kvno = ads_get_kvno(ads, computer_account);
1860 free(computer_account);
1862 return kvno;
1866 * This clears out all registered spn's for a given hostname
1867 * @param ads An initilaized ADS_STRUCT
1868 * @param machine_name the NetBIOS name of the computer.
1869 * @return 0 upon success, non-zero otherwise.
1872 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1874 TALLOC_CTX *ctx;
1875 LDAPMessage *res = NULL;
1876 ADS_MODLIST mods;
1877 const char *servicePrincipalName[1] = {NULL};
1878 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1879 char *dn_string = NULL;
1881 ret = ads_find_machine_acct(ads, &res, machine_name);
1882 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1883 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1884 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1885 ads_msgfree(ads, res);
1886 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1889 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1890 ctx = talloc_init("ads_clear_service_principal_names");
1891 if (!ctx) {
1892 ads_msgfree(ads, res);
1893 return ADS_ERROR(LDAP_NO_MEMORY);
1896 if (!(mods = ads_init_mods(ctx))) {
1897 talloc_destroy(ctx);
1898 ads_msgfree(ads, res);
1899 return ADS_ERROR(LDAP_NO_MEMORY);
1901 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1902 if (!ADS_ERR_OK(ret)) {
1903 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1904 ads_msgfree(ads, res);
1905 talloc_destroy(ctx);
1906 return ret;
1908 dn_string = ads_get_dn(ads, talloc_tos(), res);
1909 if (!dn_string) {
1910 talloc_destroy(ctx);
1911 ads_msgfree(ads, res);
1912 return ADS_ERROR(LDAP_NO_MEMORY);
1914 ret = ads_gen_mod(ads, dn_string, mods);
1915 TALLOC_FREE(dn_string);
1916 if (!ADS_ERR_OK(ret)) {
1917 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1918 machine_name));
1919 ads_msgfree(ads, res);
1920 talloc_destroy(ctx);
1921 return ret;
1924 ads_msgfree(ads, res);
1925 talloc_destroy(ctx);
1926 return ret;
1930 * This adds a service principal name to an existing computer account
1931 * (found by hostname) in AD.
1932 * @param ads An initialized ADS_STRUCT
1933 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1934 * @param my_fqdn The fully qualified DNS name of the machine
1935 * @param spn A string of the service principal to add, i.e. 'host'
1936 * @return 0 upon sucess, or non-zero if a failure occurs
1939 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1940 const char *my_fqdn, const char *spn)
1942 ADS_STATUS ret;
1943 TALLOC_CTX *ctx;
1944 LDAPMessage *res = NULL;
1945 char *psp1, *psp2;
1946 ADS_MODLIST mods;
1947 char *dn_string = NULL;
1948 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1950 ret = ads_find_machine_acct(ads, &res, machine_name);
1951 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1952 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1953 machine_name));
1954 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1955 spn, machine_name, ads->config.realm));
1956 ads_msgfree(ads, res);
1957 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1960 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1961 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1962 ads_msgfree(ads, res);
1963 return ADS_ERROR(LDAP_NO_MEMORY);
1966 /* add short name spn */
1968 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1969 talloc_destroy(ctx);
1970 ads_msgfree(ads, res);
1971 return ADS_ERROR(LDAP_NO_MEMORY);
1973 if (!strlower_m(&psp1[strlen(spn) + 1])) {
1974 ret = ADS_ERROR(LDAP_NO_MEMORY);
1975 goto out;
1977 servicePrincipalName[0] = psp1;
1979 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1980 psp1, machine_name));
1983 /* add fully qualified spn */
1985 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1986 ret = ADS_ERROR(LDAP_NO_MEMORY);
1987 goto out;
1989 if (!strlower_m(&psp2[strlen(spn) + 1])) {
1990 ret = ADS_ERROR(LDAP_NO_MEMORY);
1991 goto out;
1993 servicePrincipalName[1] = psp2;
1995 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1996 psp2, machine_name));
1998 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1999 ret = ADS_ERROR(LDAP_NO_MEMORY);
2000 goto out;
2003 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2004 if (!ADS_ERR_OK(ret)) {
2005 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2006 goto out;
2009 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2010 ret = ADS_ERROR(LDAP_NO_MEMORY);
2011 goto out;
2014 ret = ads_gen_mod(ads, dn_string, mods);
2015 if (!ADS_ERR_OK(ret)) {
2016 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2017 goto out;
2020 out:
2021 TALLOC_FREE( ctx );
2022 ads_msgfree(ads, res);
2023 return ret;
2027 * adds a machine account to the ADS server
2028 * @param ads An intialized ADS_STRUCT
2029 * @param machine_name - the NetBIOS machine name of this account.
2030 * @param account_type A number indicating the type of account to create
2031 * @param org_unit The LDAP path in which to place this account
2032 * @return 0 upon success, or non-zero otherwise
2035 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2036 const char *org_unit)
2038 ADS_STATUS ret;
2039 char *samAccountName, *controlstr;
2040 TALLOC_CTX *ctx;
2041 ADS_MODLIST mods;
2042 char *machine_escaped = NULL;
2043 char *new_dn;
2044 const char *objectClass[] = {"top", "person", "organizationalPerson",
2045 "user", "computer", NULL};
2046 LDAPMessage *res = NULL;
2047 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2048 UF_DONT_EXPIRE_PASSWD |\
2049 UF_ACCOUNTDISABLE );
2051 if (!(ctx = talloc_init("ads_add_machine_acct")))
2052 return ADS_ERROR(LDAP_NO_MEMORY);
2054 ret = ADS_ERROR(LDAP_NO_MEMORY);
2056 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2057 if (!machine_escaped) {
2058 goto done;
2061 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2062 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2064 if ( !new_dn || !samAccountName ) {
2065 goto done;
2068 #ifndef ENCTYPE_ARCFOUR_HMAC
2069 acct_control |= UF_USE_DES_KEY_ONLY;
2070 #endif
2072 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2073 goto done;
2076 if (!(mods = ads_init_mods(ctx))) {
2077 goto done;
2080 ads_mod_str(ctx, &mods, "cn", machine_name);
2081 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2082 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2083 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2085 ret = ads_gen_add(ads, new_dn, mods);
2087 done:
2088 SAFE_FREE(machine_escaped);
2089 ads_msgfree(ads, res);
2090 talloc_destroy(ctx);
2092 return ret;
2096 * move a machine account to another OU on the ADS server
2097 * @param ads - An intialized ADS_STRUCT
2098 * @param machine_name - the NetBIOS machine name of this account.
2099 * @param org_unit - The LDAP path in which to place this account
2100 * @param moved - whether we moved the machine account (optional)
2101 * @return 0 upon success, or non-zero otherwise
2104 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2105 const char *org_unit, bool *moved)
2107 ADS_STATUS rc;
2108 int ldap_status;
2109 LDAPMessage *res = NULL;
2110 char *filter = NULL;
2111 char *computer_dn = NULL;
2112 char *parent_dn;
2113 char *computer_rdn = NULL;
2114 bool need_move = False;
2116 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2117 rc = ADS_ERROR(LDAP_NO_MEMORY);
2118 goto done;
2121 /* Find pre-existing machine */
2122 rc = ads_search(ads, &res, filter, NULL);
2123 if (!ADS_ERR_OK(rc)) {
2124 goto done;
2127 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2128 if (!computer_dn) {
2129 rc = ADS_ERROR(LDAP_NO_MEMORY);
2130 goto done;
2133 parent_dn = ads_parent_dn(computer_dn);
2134 if (strequal(parent_dn, org_unit)) {
2135 goto done;
2138 need_move = True;
2140 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2141 rc = ADS_ERROR(LDAP_NO_MEMORY);
2142 goto done;
2145 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2146 org_unit, 1, NULL, NULL);
2147 rc = ADS_ERROR(ldap_status);
2149 done:
2150 ads_msgfree(ads, res);
2151 SAFE_FREE(filter);
2152 TALLOC_FREE(computer_dn);
2153 SAFE_FREE(computer_rdn);
2155 if (!ADS_ERR_OK(rc)) {
2156 need_move = False;
2159 if (moved) {
2160 *moved = need_move;
2163 return rc;
2167 dump a binary result from ldap
2169 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2171 int i, j;
2172 for (i=0; values[i]; i++) {
2173 printf("%s: ", field);
2174 for (j=0; j<values[i]->bv_len; j++) {
2175 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2177 printf("\n");
2181 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2183 int i;
2184 for (i=0; values[i]; i++) {
2185 NTSTATUS status;
2186 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2187 struct GUID guid;
2189 status = GUID_from_ndr_blob(&in, &guid);
2190 if (NT_STATUS_IS_OK(status)) {
2191 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2192 } else {
2193 printf("%s: INVALID GUID\n", field);
2199 dump a sid result from ldap
2201 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2203 int i;
2204 for (i=0; values[i]; i++) {
2205 struct dom_sid sid;
2206 fstring tmp;
2207 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2208 return;
2210 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2215 dump ntSecurityDescriptor
2217 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2219 TALLOC_CTX *frame = talloc_stackframe();
2220 struct security_descriptor *psd;
2221 NTSTATUS status;
2223 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2224 values[0]->bv_len, &psd);
2225 if (!NT_STATUS_IS_OK(status)) {
2226 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2227 nt_errstr(status)));
2228 TALLOC_FREE(frame);
2229 return;
2232 if (psd) {
2233 ads_disp_sd(ads, talloc_tos(), psd);
2236 TALLOC_FREE(frame);
2240 dump a string result from ldap
2242 static void dump_string(const char *field, char **values)
2244 int i;
2245 for (i=0; values[i]; i++) {
2246 printf("%s: %s\n", field, values[i]);
2251 dump a field from LDAP on stdout
2252 used for debugging
2255 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2257 const struct {
2258 const char *name;
2259 bool string;
2260 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2261 } handlers[] = {
2262 {"objectGUID", False, dump_guid},
2263 {"netbootGUID", False, dump_guid},
2264 {"nTSecurityDescriptor", False, dump_sd},
2265 {"dnsRecord", False, dump_binary},
2266 {"objectSid", False, dump_sid},
2267 {"tokenGroups", False, dump_sid},
2268 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2269 {"tokengroupsGlobalandUniversal", False, dump_sid},
2270 {"mS-DS-CreatorSID", False, dump_sid},
2271 {"msExchMailboxGuid", False, dump_guid},
2272 {NULL, True, NULL}
2274 int i;
2276 if (!field) { /* must be end of an entry */
2277 printf("\n");
2278 return False;
2281 for (i=0; handlers[i].name; i++) {
2282 if (strcasecmp_m(handlers[i].name, field) == 0) {
2283 if (!values) /* first time, indicate string or not */
2284 return handlers[i].string;
2285 handlers[i].handler(ads, field, (struct berval **) values);
2286 break;
2289 if (!handlers[i].name) {
2290 if (!values) /* first time, indicate string conversion */
2291 return True;
2292 dump_string(field, (char **)values);
2294 return False;
2298 * Dump a result from LDAP on stdout
2299 * used for debugging
2300 * @param ads connection to ads server
2301 * @param res Results to dump
2304 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2306 ads_process_results(ads, res, ads_dump_field, NULL);
2310 * Walk through results, calling a function for each entry found.
2311 * The function receives a field name, a berval * array of values,
2312 * and a data area passed through from the start. The function is
2313 * called once with null for field and values at the end of each
2314 * entry.
2315 * @param ads connection to ads server
2316 * @param res Results to process
2317 * @param fn Function for processing each result
2318 * @param data_area user-defined area to pass to function
2320 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2321 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2322 void *data_area)
2324 LDAPMessage *msg;
2325 TALLOC_CTX *ctx;
2326 size_t converted_size;
2328 if (!(ctx = talloc_init("ads_process_results")))
2329 return;
2331 for (msg = ads_first_entry(ads, res); msg;
2332 msg = ads_next_entry(ads, msg)) {
2333 char *utf8_field;
2334 BerElement *b;
2336 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2337 (LDAPMessage *)msg,&b);
2338 utf8_field;
2339 utf8_field=ldap_next_attribute(ads->ldap.ld,
2340 (LDAPMessage *)msg,b)) {
2341 struct berval **ber_vals;
2342 char **str_vals, **utf8_vals;
2343 char *field;
2344 bool string;
2346 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2347 &converted_size))
2349 DEBUG(0,("ads_process_results: "
2350 "pull_utf8_talloc failed: %s",
2351 strerror(errno)));
2354 string = fn(ads, field, NULL, data_area);
2356 if (string) {
2357 utf8_vals = ldap_get_values(ads->ldap.ld,
2358 (LDAPMessage *)msg, field);
2359 str_vals = ads_pull_strvals(ctx,
2360 (const char **) utf8_vals);
2361 fn(ads, field, (void **) str_vals, data_area);
2362 ldap_value_free(utf8_vals);
2363 } else {
2364 ber_vals = ldap_get_values_len(ads->ldap.ld,
2365 (LDAPMessage *)msg, field);
2366 fn(ads, field, (void **) ber_vals, data_area);
2368 ldap_value_free_len(ber_vals);
2370 ldap_memfree(utf8_field);
2372 ber_free(b, 0);
2373 talloc_free_children(ctx);
2374 fn(ads, NULL, NULL, data_area); /* completed an entry */
2377 talloc_destroy(ctx);
2381 * count how many replies are in a LDAPMessage
2382 * @param ads connection to ads server
2383 * @param res Results to count
2384 * @return number of replies
2386 int ads_count_replies(ADS_STRUCT *ads, void *res)
2388 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2392 * pull the first entry from a ADS result
2393 * @param ads connection to ads server
2394 * @param res Results of search
2395 * @return first entry from result
2397 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2399 return ldap_first_entry(ads->ldap.ld, res);
2403 * pull the next entry from a ADS result
2404 * @param ads connection to ads server
2405 * @param res Results of search
2406 * @return next entry from result
2408 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2410 return ldap_next_entry(ads->ldap.ld, res);
2414 * pull the first message from a ADS result
2415 * @param ads connection to ads server
2416 * @param res Results of search
2417 * @return first message from result
2419 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2421 return ldap_first_message(ads->ldap.ld, res);
2425 * pull the next message from a ADS result
2426 * @param ads connection to ads server
2427 * @param res Results of search
2428 * @return next message from result
2430 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2432 return ldap_next_message(ads->ldap.ld, res);
2436 * pull a single string 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 string in talloc context
2443 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2444 const char *field)
2446 char **values;
2447 char *ret = NULL;
2448 char *ux_string;
2449 size_t converted_size;
2451 values = ldap_get_values(ads->ldap.ld, msg, field);
2452 if (!values)
2453 return NULL;
2455 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2456 &converted_size))
2458 ret = ux_string;
2460 ldap_value_free(values);
2461 return ret;
2465 * pull an array of strings from a ADS result
2466 * @param ads connection to ads server
2467 * @param mem_ctx TALLOC_CTX to use for allocating result string
2468 * @param msg Results of search
2469 * @param field Attribute to retrieve
2470 * @return Result strings in talloc context
2472 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2473 LDAPMessage *msg, const char *field,
2474 size_t *num_values)
2476 char **values;
2477 char **ret = NULL;
2478 int i;
2479 size_t converted_size;
2481 values = ldap_get_values(ads->ldap.ld, msg, field);
2482 if (!values)
2483 return NULL;
2485 *num_values = ldap_count_values(values);
2487 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2488 if (!ret) {
2489 ldap_value_free(values);
2490 return NULL;
2493 for (i=0;i<*num_values;i++) {
2494 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2495 &converted_size))
2497 ldap_value_free(values);
2498 return NULL;
2501 ret[i] = NULL;
2503 ldap_value_free(values);
2504 return ret;
2508 * pull an array of strings from a ADS result
2509 * (handle large multivalue attributes with range retrieval)
2510 * @param ads connection to ads server
2511 * @param mem_ctx TALLOC_CTX to use for allocating result string
2512 * @param msg Results of search
2513 * @param field Attribute to retrieve
2514 * @param current_strings strings returned by a previous call to this function
2515 * @param next_attribute The next query should ask for this attribute
2516 * @param num_values How many values did we get this time?
2517 * @param more_values Are there more values to get?
2518 * @return Result strings in talloc context
2520 char **ads_pull_strings_range(ADS_STRUCT *ads,
2521 TALLOC_CTX *mem_ctx,
2522 LDAPMessage *msg, const char *field,
2523 char **current_strings,
2524 const char **next_attribute,
2525 size_t *num_strings,
2526 bool *more_strings)
2528 char *attr;
2529 char *expected_range_attrib, *range_attr;
2530 BerElement *ptr = NULL;
2531 char **strings;
2532 char **new_strings;
2533 size_t num_new_strings;
2534 unsigned long int range_start;
2535 unsigned long int range_end;
2537 /* we might have been given the whole lot anyway */
2538 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2539 *more_strings = False;
2540 return strings;
2543 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2545 /* look for Range result */
2546 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2547 attr;
2548 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2549 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2550 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2551 range_attr = attr;
2552 break;
2554 ldap_memfree(attr);
2556 if (!attr) {
2557 ber_free(ptr, 0);
2558 /* nothing here - this field is just empty */
2559 *more_strings = False;
2560 return NULL;
2563 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2564 &range_start, &range_end) == 2) {
2565 *more_strings = True;
2566 } else {
2567 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2568 &range_start) == 1) {
2569 *more_strings = False;
2570 } else {
2571 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2572 range_attr));
2573 ldap_memfree(range_attr);
2574 *more_strings = False;
2575 return NULL;
2579 if ((*num_strings) != range_start) {
2580 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2581 " - aborting range retreival\n",
2582 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2583 ldap_memfree(range_attr);
2584 *more_strings = False;
2585 return NULL;
2588 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2590 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2591 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2592 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2593 range_attr, (unsigned long int)range_end - range_start + 1,
2594 (unsigned long int)num_new_strings));
2595 ldap_memfree(range_attr);
2596 *more_strings = False;
2597 return NULL;
2600 strings = talloc_realloc(mem_ctx, current_strings, char *,
2601 *num_strings + num_new_strings);
2603 if (strings == NULL) {
2604 ldap_memfree(range_attr);
2605 *more_strings = False;
2606 return NULL;
2609 if (new_strings && num_new_strings) {
2610 memcpy(&strings[*num_strings], new_strings,
2611 sizeof(*new_strings) * num_new_strings);
2614 (*num_strings) += num_new_strings;
2616 if (*more_strings) {
2617 *next_attribute = talloc_asprintf(mem_ctx,
2618 "%s;range=%d-*",
2619 field,
2620 (int)*num_strings);
2622 if (!*next_attribute) {
2623 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2624 ldap_memfree(range_attr);
2625 *more_strings = False;
2626 return NULL;
2630 ldap_memfree(range_attr);
2632 return strings;
2636 * pull a single uint32 from a ADS result
2637 * @param ads connection to ads server
2638 * @param msg Results of search
2639 * @param field Attribute to retrieve
2640 * @param v Pointer to int to store result
2641 * @return boolean inidicating success
2643 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2644 uint32 *v)
2646 char **values;
2648 values = ldap_get_values(ads->ldap.ld, msg, field);
2649 if (!values)
2650 return False;
2651 if (!values[0]) {
2652 ldap_value_free(values);
2653 return False;
2656 *v = atoi(values[0]);
2657 ldap_value_free(values);
2658 return True;
2662 * pull a single objectGUID from an ADS result
2663 * @param ads connection to ADS server
2664 * @param msg results of search
2665 * @param guid 37-byte area to receive text guid
2666 * @return boolean indicating success
2668 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2670 DATA_BLOB blob;
2671 NTSTATUS status;
2673 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2674 &blob)) {
2675 return false;
2678 status = GUID_from_ndr_blob(&blob, guid);
2679 talloc_free(blob.data);
2680 return NT_STATUS_IS_OK(status);
2685 * pull a single struct dom_sid from a ADS result
2686 * @param ads connection to ads server
2687 * @param msg Results of search
2688 * @param field Attribute to retrieve
2689 * @param sid Pointer to sid to store result
2690 * @return boolean inidicating success
2692 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2693 struct dom_sid *sid)
2695 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2699 * pull an array of struct dom_sids from a ADS result
2700 * @param ads connection to ads server
2701 * @param mem_ctx TALLOC_CTX for allocating sid array
2702 * @param msg Results of search
2703 * @param field Attribute to retrieve
2704 * @param sids pointer to sid array to allocate
2705 * @return the count of SIDs pulled
2707 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2708 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2710 struct berval **values;
2711 bool ret;
2712 int count, i;
2714 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2716 if (!values)
2717 return 0;
2719 for (i=0; values[i]; i++)
2720 /* nop */ ;
2722 if (i) {
2723 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2724 if (!(*sids)) {
2725 ldap_value_free_len(values);
2726 return 0;
2728 } else {
2729 (*sids) = NULL;
2732 count = 0;
2733 for (i=0; values[i]; i++) {
2734 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2735 if (ret) {
2736 DEBUG(10, ("pulling SID: %s\n",
2737 sid_string_dbg(&(*sids)[count])));
2738 count++;
2742 ldap_value_free_len(values);
2743 return count;
2747 * pull a struct security_descriptor from a ADS result
2748 * @param ads connection to ads server
2749 * @param mem_ctx TALLOC_CTX for allocating sid array
2750 * @param msg Results of search
2751 * @param field Attribute to retrieve
2752 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2753 * @return boolean inidicating success
2755 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2756 LDAPMessage *msg, const char *field,
2757 struct security_descriptor **sd)
2759 struct berval **values;
2760 bool ret = true;
2762 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2764 if (!values) return false;
2766 if (values[0]) {
2767 NTSTATUS status;
2768 status = unmarshall_sec_desc(mem_ctx,
2769 (uint8 *)values[0]->bv_val,
2770 values[0]->bv_len, sd);
2771 if (!NT_STATUS_IS_OK(status)) {
2772 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2773 nt_errstr(status)));
2774 ret = false;
2778 ldap_value_free_len(values);
2779 return ret;
2783 * in order to support usernames longer than 21 characters we need to
2784 * use both the sAMAccountName and the userPrincipalName attributes
2785 * It seems that not all users have the userPrincipalName attribute set
2787 * @param ads connection to ads server
2788 * @param mem_ctx TALLOC_CTX for allocating sid array
2789 * @param msg Results of search
2790 * @return the username
2792 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2793 LDAPMessage *msg)
2795 #if 0 /* JERRY */
2796 char *ret, *p;
2798 /* lookup_name() only works on the sAMAccountName to
2799 returning the username portion of userPrincipalName
2800 breaks winbindd_getpwnam() */
2802 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2803 if (ret && (p = strchr_m(ret, '@'))) {
2804 *p = 0;
2805 return ret;
2807 #endif
2808 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2813 * find the update serial number - this is the core of the ldap cache
2814 * @param ads connection to ads server
2815 * @param ads connection to ADS server
2816 * @param usn Pointer to retrieved update serial number
2817 * @return status of search
2819 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2821 const char *attrs[] = {"highestCommittedUSN", NULL};
2822 ADS_STATUS status;
2823 LDAPMessage *res;
2825 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2826 if (!ADS_ERR_OK(status))
2827 return status;
2829 if (ads_count_replies(ads, res) != 1) {
2830 ads_msgfree(ads, res);
2831 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2834 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2835 ads_msgfree(ads, res);
2836 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2839 ads_msgfree(ads, res);
2840 return ADS_SUCCESS;
2843 /* parse a ADS timestring - typical string is
2844 '20020917091222.0Z0' which means 09:12.22 17th September
2845 2002, timezone 0 */
2846 static time_t ads_parse_time(const char *str)
2848 struct tm tm;
2850 ZERO_STRUCT(tm);
2852 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2853 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2854 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2855 return 0;
2857 tm.tm_year -= 1900;
2858 tm.tm_mon -= 1;
2860 return timegm(&tm);
2863 /********************************************************************
2864 ********************************************************************/
2866 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2868 const char *attrs[] = {"currentTime", NULL};
2869 ADS_STATUS status;
2870 LDAPMessage *res;
2871 char *timestr;
2872 TALLOC_CTX *ctx;
2873 ADS_STRUCT *ads_s = ads;
2875 if (!(ctx = talloc_init("ads_current_time"))) {
2876 return ADS_ERROR(LDAP_NO_MEMORY);
2879 /* establish a new ldap tcp session if necessary */
2881 if ( !ads->ldap.ld ) {
2882 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2883 ads->server.ldap_server )) == NULL )
2885 goto done;
2887 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2888 status = ads_connect( ads_s );
2889 if ( !ADS_ERR_OK(status))
2890 goto done;
2893 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2894 if (!ADS_ERR_OK(status)) {
2895 goto done;
2898 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2899 if (!timestr) {
2900 ads_msgfree(ads_s, res);
2901 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2902 goto done;
2905 /* but save the time and offset in the original ADS_STRUCT */
2907 ads->config.current_time = ads_parse_time(timestr);
2909 if (ads->config.current_time != 0) {
2910 ads->auth.time_offset = ads->config.current_time - time(NULL);
2911 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2914 ads_msgfree(ads, res);
2916 status = ADS_SUCCESS;
2918 done:
2919 /* free any temporary ads connections */
2920 if ( ads_s != ads ) {
2921 ads_destroy( &ads_s );
2923 talloc_destroy(ctx);
2925 return status;
2928 /********************************************************************
2929 ********************************************************************/
2931 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2933 const char *attrs[] = {"domainFunctionality", NULL};
2934 ADS_STATUS status;
2935 LDAPMessage *res;
2936 ADS_STRUCT *ads_s = ads;
2938 *val = DS_DOMAIN_FUNCTION_2000;
2940 /* establish a new ldap tcp session if necessary */
2942 if ( !ads->ldap.ld ) {
2943 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2944 ads->server.ldap_server )) == NULL )
2946 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2947 goto done;
2949 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2950 status = ads_connect( ads_s );
2951 if ( !ADS_ERR_OK(status))
2952 goto done;
2955 /* If the attribute does not exist assume it is a Windows 2000
2956 functional domain */
2958 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2959 if (!ADS_ERR_OK(status)) {
2960 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2961 status = ADS_SUCCESS;
2963 goto done;
2966 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2967 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2969 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2972 ads_msgfree(ads, res);
2974 done:
2975 /* free any temporary ads connections */
2976 if ( ads_s != ads ) {
2977 ads_destroy( &ads_s );
2980 return status;
2984 * find the domain sid for our domain
2985 * @param ads connection to ads server
2986 * @param sid Pointer to domain sid
2987 * @return status of search
2989 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2991 const char *attrs[] = {"objectSid", NULL};
2992 LDAPMessage *res;
2993 ADS_STATUS rc;
2995 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2996 attrs, &res);
2997 if (!ADS_ERR_OK(rc)) return rc;
2998 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2999 ads_msgfree(ads, res);
3000 return ADS_ERROR_SYSTEM(ENOENT);
3002 ads_msgfree(ads, res);
3004 return ADS_SUCCESS;
3008 * find our site name
3009 * @param ads connection to ads server
3010 * @param mem_ctx Pointer to talloc context
3011 * @param site_name Pointer to the sitename
3012 * @return status of search
3014 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3016 ADS_STATUS status;
3017 LDAPMessage *res;
3018 const char *dn, *service_name;
3019 const char *attrs[] = { "dsServiceName", NULL };
3021 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3022 if (!ADS_ERR_OK(status)) {
3023 return status;
3026 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3027 if (service_name == NULL) {
3028 ads_msgfree(ads, res);
3029 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3032 ads_msgfree(ads, res);
3034 /* go up three levels */
3035 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3036 if (dn == NULL) {
3037 return ADS_ERROR(LDAP_NO_MEMORY);
3040 *site_name = talloc_strdup(mem_ctx, dn);
3041 if (*site_name == NULL) {
3042 return ADS_ERROR(LDAP_NO_MEMORY);
3045 return status;
3047 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3052 * find the site dn where a machine resides
3053 * @param ads connection to ads server
3054 * @param mem_ctx Pointer to talloc context
3055 * @param computer_name name of the machine
3056 * @param site_name Pointer to the sitename
3057 * @return status of search
3059 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3061 ADS_STATUS status;
3062 LDAPMessage *res;
3063 const char *parent, *filter;
3064 char *config_context = NULL;
3065 char *dn;
3067 /* shortcut a query */
3068 if (strequal(computer_name, ads->config.ldap_server_name)) {
3069 return ads_site_dn(ads, mem_ctx, site_dn);
3072 status = ads_config_path(ads, mem_ctx, &config_context);
3073 if (!ADS_ERR_OK(status)) {
3074 return status;
3077 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3078 if (filter == NULL) {
3079 return ADS_ERROR(LDAP_NO_MEMORY);
3082 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3083 filter, NULL, &res);
3084 if (!ADS_ERR_OK(status)) {
3085 return status;
3088 if (ads_count_replies(ads, res) != 1) {
3089 ads_msgfree(ads, res);
3090 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3093 dn = ads_get_dn(ads, mem_ctx, res);
3094 if (dn == NULL) {
3095 ads_msgfree(ads, res);
3096 return ADS_ERROR(LDAP_NO_MEMORY);
3099 /* go up three levels */
3100 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3101 if (parent == NULL) {
3102 ads_msgfree(ads, res);
3103 TALLOC_FREE(dn);
3104 return ADS_ERROR(LDAP_NO_MEMORY);
3107 *site_dn = talloc_strdup(mem_ctx, parent);
3108 if (*site_dn == NULL) {
3109 ads_msgfree(ads, res);
3110 TALLOC_FREE(dn);
3111 return ADS_ERROR(LDAP_NO_MEMORY);
3114 TALLOC_FREE(dn);
3115 ads_msgfree(ads, res);
3117 return status;
3121 * get the upn suffixes for a domain
3122 * @param ads connection to ads server
3123 * @param mem_ctx Pointer to talloc context
3124 * @param suffixes Pointer to an array of suffixes
3125 * @param num_suffixes Pointer to the number of suffixes
3126 * @return status of search
3128 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3130 ADS_STATUS status;
3131 LDAPMessage *res;
3132 const char *base;
3133 char *config_context = NULL;
3134 const char *attrs[] = { "uPNSuffixes", NULL };
3136 status = ads_config_path(ads, mem_ctx, &config_context);
3137 if (!ADS_ERR_OK(status)) {
3138 return status;
3141 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3142 if (base == NULL) {
3143 return ADS_ERROR(LDAP_NO_MEMORY);
3146 status = ads_search_dn(ads, &res, base, attrs);
3147 if (!ADS_ERR_OK(status)) {
3148 return status;
3151 if (ads_count_replies(ads, res) != 1) {
3152 ads_msgfree(ads, res);
3153 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3156 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3157 if ((*suffixes) == NULL) {
3158 ads_msgfree(ads, res);
3159 return ADS_ERROR(LDAP_NO_MEMORY);
3162 ads_msgfree(ads, res);
3164 return status;
3168 * get the joinable ous for a domain
3169 * @param ads connection to ads server
3170 * @param mem_ctx Pointer to talloc context
3171 * @param ous Pointer to an array of ous
3172 * @param num_ous Pointer to the number of ous
3173 * @return status of search
3175 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3176 TALLOC_CTX *mem_ctx,
3177 char ***ous,
3178 size_t *num_ous)
3180 ADS_STATUS status;
3181 LDAPMessage *res = NULL;
3182 LDAPMessage *msg = NULL;
3183 const char *attrs[] = { "dn", NULL };
3184 int count = 0;
3186 status = ads_search(ads, &res,
3187 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3188 attrs);
3189 if (!ADS_ERR_OK(status)) {
3190 return status;
3193 count = ads_count_replies(ads, res);
3194 if (count < 1) {
3195 ads_msgfree(ads, res);
3196 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3199 for (msg = ads_first_entry(ads, res); msg;
3200 msg = ads_next_entry(ads, msg)) {
3202 char *dn = NULL;
3204 dn = ads_get_dn(ads, talloc_tos(), msg);
3205 if (!dn) {
3206 ads_msgfree(ads, res);
3207 return ADS_ERROR(LDAP_NO_MEMORY);
3210 if (!add_string_to_array(mem_ctx, dn,
3211 (const char ***)ous,
3212 (int *)num_ous)) {
3213 TALLOC_FREE(dn);
3214 ads_msgfree(ads, res);
3215 return ADS_ERROR(LDAP_NO_MEMORY);
3218 TALLOC_FREE(dn);
3221 ads_msgfree(ads, res);
3223 return status;
3228 * pull a struct dom_sid from an extended dn string
3229 * @param mem_ctx TALLOC_CTX
3230 * @param extended_dn string
3231 * @param flags string type of extended_dn
3232 * @param sid pointer to a struct dom_sid
3233 * @return NT_STATUS_OK on success,
3234 * NT_INVALID_PARAMETER on error,
3235 * NT_STATUS_NOT_FOUND if no SID present
3237 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3238 const char *extended_dn,
3239 enum ads_extended_dn_flags flags,
3240 struct dom_sid *sid)
3242 char *p, *q, *dn;
3244 if (!extended_dn) {
3245 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3248 /* otherwise extended_dn gets stripped off */
3249 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3250 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3253 * ADS_EXTENDED_DN_HEX_STRING:
3254 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3256 * ADS_EXTENDED_DN_STRING (only with w2k3):
3257 * <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
3259 * Object with no SID, such as an Exchange Public Folder
3260 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3263 p = strchr(dn, ';');
3264 if (!p) {
3265 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3268 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3269 DEBUG(5,("No SID present in extended dn\n"));
3270 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3273 p += strlen(";<SID=");
3275 q = strchr(p, '>');
3276 if (!q) {
3277 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3280 *q = '\0';
3282 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3284 switch (flags) {
3286 case ADS_EXTENDED_DN_STRING:
3287 if (!string_to_sid(sid, p)) {
3288 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3290 break;
3291 case ADS_EXTENDED_DN_HEX_STRING: {
3292 fstring buf;
3293 size_t buf_len;
3295 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3296 if (buf_len == 0) {
3297 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3300 if (!sid_parse(buf, buf_len, sid)) {
3301 DEBUG(10,("failed to parse sid\n"));
3302 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3304 break;
3306 default:
3307 DEBUG(10,("unknown extended dn format\n"));
3308 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3311 return ADS_ERROR_NT(NT_STATUS_OK);
3314 /********************************************************************
3315 ********************************************************************/
3317 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3319 LDAPMessage *res = NULL;
3320 ADS_STATUS status;
3321 int count = 0;
3322 char *name = NULL;
3324 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3325 if (!ADS_ERR_OK(status)) {
3326 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3327 lp_netbios_name()));
3328 goto out;
3331 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3332 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3333 goto out;
3336 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3337 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3340 out:
3341 ads_msgfree(ads, res);
3343 return name;
3346 /********************************************************************
3347 ********************************************************************/
3349 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3351 LDAPMessage *res = NULL;
3352 ADS_STATUS status;
3353 int count = 0;
3354 char *name = NULL;
3356 status = ads_find_machine_acct(ads, &res, machine_name);
3357 if (!ADS_ERR_OK(status)) {
3358 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3359 lp_netbios_name()));
3360 goto out;
3363 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3364 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3365 goto out;
3368 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3369 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3372 out:
3373 ads_msgfree(ads, res);
3375 return name;
3378 /********************************************************************
3379 ********************************************************************/
3381 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3383 LDAPMessage *res = NULL;
3384 ADS_STATUS status;
3385 int count = 0;
3386 char *name = NULL;
3388 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3389 if (!ADS_ERR_OK(status)) {
3390 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3391 lp_netbios_name()));
3392 goto out;
3395 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3396 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3397 goto out;
3400 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3401 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3404 out:
3405 ads_msgfree(ads, res);
3407 return name;
3410 #if 0
3412 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3415 * Join a machine to a realm
3416 * Creates the machine account and sets the machine password
3417 * @param ads connection to ads server
3418 * @param machine name of host to add
3419 * @param org_unit Organizational unit to place machine in
3420 * @return status of join
3422 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3423 uint32 account_type, const char *org_unit)
3425 ADS_STATUS status;
3426 LDAPMessage *res = NULL;
3427 char *machine;
3429 /* machine name must be lowercase */
3430 machine = SMB_STRDUP(machine_name);
3431 strlower_m(machine);
3434 status = ads_find_machine_acct(ads, (void **)&res, machine);
3435 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3436 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3437 status = ads_leave_realm(ads, machine);
3438 if (!ADS_ERR_OK(status)) {
3439 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3440 machine, ads->config.realm));
3441 return status;
3445 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3446 if (!ADS_ERR_OK(status)) {
3447 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3448 SAFE_FREE(machine);
3449 return status;
3452 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3453 if (!ADS_ERR_OK(status)) {
3454 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3455 SAFE_FREE(machine);
3456 return status;
3459 SAFE_FREE(machine);
3460 ads_msgfree(ads, res);
3462 return status;
3464 #endif
3467 * Delete a machine from the realm
3468 * @param ads connection to ads server
3469 * @param hostname Machine to remove
3470 * @return status of delete
3472 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3474 ADS_STATUS status;
3475 void *msg;
3476 LDAPMessage *res;
3477 char *hostnameDN, *host;
3478 int rc;
3479 LDAPControl ldap_control;
3480 LDAPControl * pldap_control[2] = {NULL, NULL};
3482 pldap_control[0] = &ldap_control;
3483 memset(&ldap_control, 0, sizeof(LDAPControl));
3484 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3486 /* hostname must be lowercase */
3487 host = SMB_STRDUP(hostname);
3488 if (!strlower_m(host)) {
3489 SAFE_FREE(host);
3490 return ADS_ERROR_SYSTEM(EINVAL);
3493 status = ads_find_machine_acct(ads, &res, host);
3494 if (!ADS_ERR_OK(status)) {
3495 DEBUG(0, ("Host account for %s does not exist.\n", host));
3496 SAFE_FREE(host);
3497 return status;
3500 msg = ads_first_entry(ads, res);
3501 if (!msg) {
3502 SAFE_FREE(host);
3503 return ADS_ERROR_SYSTEM(ENOENT);
3506 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3507 if (hostnameDN == NULL) {
3508 SAFE_FREE(host);
3509 return ADS_ERROR_SYSTEM(ENOENT);
3512 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3513 if (rc) {
3514 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3515 }else {
3516 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3519 if (rc != LDAP_SUCCESS) {
3520 const char *attrs[] = { "cn", NULL };
3521 LDAPMessage *msg_sub;
3523 /* we only search with scope ONE, we do not expect any further
3524 * objects to be created deeper */
3526 status = ads_do_search_retry(ads, hostnameDN,
3527 LDAP_SCOPE_ONELEVEL,
3528 "(objectclass=*)", attrs, &res);
3530 if (!ADS_ERR_OK(status)) {
3531 SAFE_FREE(host);
3532 TALLOC_FREE(hostnameDN);
3533 return status;
3536 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3537 msg_sub = ads_next_entry(ads, msg_sub)) {
3539 char *dn = NULL;
3541 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3542 SAFE_FREE(host);
3543 TALLOC_FREE(hostnameDN);
3544 return ADS_ERROR(LDAP_NO_MEMORY);
3547 status = ads_del_dn(ads, dn);
3548 if (!ADS_ERR_OK(status)) {
3549 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3550 SAFE_FREE(host);
3551 TALLOC_FREE(dn);
3552 TALLOC_FREE(hostnameDN);
3553 return status;
3556 TALLOC_FREE(dn);
3559 /* there should be no subordinate objects anymore */
3560 status = ads_do_search_retry(ads, hostnameDN,
3561 LDAP_SCOPE_ONELEVEL,
3562 "(objectclass=*)", attrs, &res);
3564 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3565 SAFE_FREE(host);
3566 TALLOC_FREE(hostnameDN);
3567 return status;
3570 /* delete hostnameDN now */
3571 status = ads_del_dn(ads, hostnameDN);
3572 if (!ADS_ERR_OK(status)) {
3573 SAFE_FREE(host);
3574 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3575 TALLOC_FREE(hostnameDN);
3576 return status;
3580 TALLOC_FREE(hostnameDN);
3582 status = ads_find_machine_acct(ads, &res, host);
3583 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3584 DEBUG(3, ("Failed to remove host account.\n"));
3585 SAFE_FREE(host);
3586 return status;
3589 SAFE_FREE(host);
3590 return status;
3594 * pull all token-sids from an LDAP dn
3595 * @param ads connection to ads server
3596 * @param mem_ctx TALLOC_CTX for allocating sid array
3597 * @param dn of LDAP object
3598 * @param user_sid pointer to struct dom_sid (objectSid)
3599 * @param primary_group_sid pointer to struct dom_sid (self composed)
3600 * @param sids pointer to sid array to allocate
3601 * @param num_sids counter of SIDs pulled
3602 * @return status of token query
3604 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3605 TALLOC_CTX *mem_ctx,
3606 const char *dn,
3607 struct dom_sid *user_sid,
3608 struct dom_sid *primary_group_sid,
3609 struct dom_sid **sids,
3610 size_t *num_sids)
3612 ADS_STATUS status;
3613 LDAPMessage *res = NULL;
3614 int count = 0;
3615 size_t tmp_num_sids;
3616 struct dom_sid *tmp_sids;
3617 struct dom_sid tmp_user_sid;
3618 struct dom_sid tmp_primary_group_sid;
3619 uint32 pgid;
3620 const char *attrs[] = {
3621 "objectSid",
3622 "tokenGroups",
3623 "primaryGroupID",
3624 NULL
3627 status = ads_search_retry_dn(ads, &res, dn, attrs);
3628 if (!ADS_ERR_OK(status)) {
3629 return status;
3632 count = ads_count_replies(ads, res);
3633 if (count != 1) {
3634 ads_msgfree(ads, res);
3635 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3638 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3639 ads_msgfree(ads, res);
3640 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3643 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3644 ads_msgfree(ads, res);
3645 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3649 /* hack to compose the primary group sid without knowing the
3650 * domsid */
3652 struct dom_sid domsid;
3654 sid_copy(&domsid, &tmp_user_sid);
3656 if (!sid_split_rid(&domsid, NULL)) {
3657 ads_msgfree(ads, res);
3658 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3661 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3662 ads_msgfree(ads, res);
3663 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3667 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3669 if (tmp_num_sids == 0 || !tmp_sids) {
3670 ads_msgfree(ads, res);
3671 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3674 if (num_sids) {
3675 *num_sids = tmp_num_sids;
3678 if (sids) {
3679 *sids = tmp_sids;
3682 if (user_sid) {
3683 *user_sid = tmp_user_sid;
3686 if (primary_group_sid) {
3687 *primary_group_sid = tmp_primary_group_sid;
3690 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3692 ads_msgfree(ads, res);
3693 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3697 * Find a sAMAccoutName in LDAP
3698 * @param ads connection to ads server
3699 * @param mem_ctx TALLOC_CTX for allocating sid array
3700 * @param samaccountname to search
3701 * @param uac_ret uint32 pointer userAccountControl attribute value
3702 * @param dn_ret pointer to dn
3703 * @return status of token query
3705 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3706 TALLOC_CTX *mem_ctx,
3707 const char *samaccountname,
3708 uint32 *uac_ret,
3709 const char **dn_ret)
3711 ADS_STATUS status;
3712 const char *attrs[] = { "userAccountControl", NULL };
3713 const char *filter;
3714 LDAPMessage *res = NULL;
3715 char *dn = NULL;
3716 uint32 uac = 0;
3718 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3719 samaccountname);
3720 if (filter == NULL) {
3721 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3722 goto out;
3725 status = ads_do_search_all(ads, ads->config.bind_path,
3726 LDAP_SCOPE_SUBTREE,
3727 filter, attrs, &res);
3729 if (!ADS_ERR_OK(status)) {
3730 goto out;
3733 if (ads_count_replies(ads, res) != 1) {
3734 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3735 goto out;
3738 dn = ads_get_dn(ads, talloc_tos(), res);
3739 if (dn == NULL) {
3740 status = ADS_ERROR(LDAP_NO_MEMORY);
3741 goto out;
3744 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3745 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3746 goto out;
3749 if (uac_ret) {
3750 *uac_ret = uac;
3753 if (dn_ret) {
3754 *dn_ret = talloc_strdup(mem_ctx, dn);
3755 if (!*dn_ret) {
3756 status = ADS_ERROR(LDAP_NO_MEMORY);
3757 goto out;
3760 out:
3761 TALLOC_FREE(dn);
3762 ads_msgfree(ads, res);
3764 return status;
3768 * find our configuration path
3769 * @param ads connection to ads server
3770 * @param mem_ctx Pointer to talloc context
3771 * @param config_path Pointer to the config path
3772 * @return status of search
3774 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3775 TALLOC_CTX *mem_ctx,
3776 char **config_path)
3778 ADS_STATUS status;
3779 LDAPMessage *res = NULL;
3780 const char *config_context = NULL;
3781 const char *attrs[] = { "configurationNamingContext", NULL };
3783 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3784 "(objectclass=*)", attrs, &res);
3785 if (!ADS_ERR_OK(status)) {
3786 return status;
3789 config_context = ads_pull_string(ads, mem_ctx, res,
3790 "configurationNamingContext");
3791 ads_msgfree(ads, res);
3792 if (!config_context) {
3793 return ADS_ERROR(LDAP_NO_MEMORY);
3796 if (config_path) {
3797 *config_path = talloc_strdup(mem_ctx, config_context);
3798 if (!*config_path) {
3799 return ADS_ERROR(LDAP_NO_MEMORY);
3803 return ADS_ERROR(LDAP_SUCCESS);
3807 * find the displayName of an extended right
3808 * @param ads connection to ads server
3809 * @param config_path The config path
3810 * @param mem_ctx Pointer to talloc context
3811 * @param GUID struct of the rightsGUID
3812 * @return status of search
3814 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3815 const char *config_path,
3816 TALLOC_CTX *mem_ctx,
3817 const struct GUID *rights_guid)
3819 ADS_STATUS rc;
3820 LDAPMessage *res = NULL;
3821 char *expr = NULL;
3822 const char *attrs[] = { "displayName", NULL };
3823 const char *result = NULL;
3824 const char *path;
3826 if (!ads || !mem_ctx || !rights_guid) {
3827 goto done;
3830 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3831 GUID_string(mem_ctx, rights_guid));
3832 if (!expr) {
3833 goto done;
3836 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3837 if (!path) {
3838 goto done;
3841 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3842 expr, attrs, &res);
3843 if (!ADS_ERR_OK(rc)) {
3844 goto done;
3847 if (ads_count_replies(ads, res) != 1) {
3848 goto done;
3851 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3853 done:
3854 ads_msgfree(ads, res);
3855 return result;
3859 * verify or build and verify an account ou
3860 * @param mem_ctx Pointer to talloc context
3861 * @param ads connection to ads server
3862 * @param account_ou
3863 * @return status of search
3866 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3867 ADS_STRUCT *ads,
3868 const char **account_ou)
3870 char **exploded_dn;
3871 const char *name;
3872 char *ou_string;
3874 exploded_dn = ldap_explode_dn(*account_ou, 0);
3875 if (exploded_dn) {
3876 ldap_value_free(exploded_dn);
3877 return ADS_SUCCESS;
3880 ou_string = ads_ou_string(ads, *account_ou);
3881 if (!ou_string) {
3882 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3885 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3886 ads->config.bind_path);
3887 SAFE_FREE(ou_string);
3889 if (!name) {
3890 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3893 exploded_dn = ldap_explode_dn(name, 0);
3894 if (!exploded_dn) {
3895 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3897 ldap_value_free(exploded_dn);
3899 *account_ou = name;
3900 return ADS_SUCCESS;
3903 #endif