s3-libads: Print the debug string of a failed call with LDAP_OTHER.
[Samba/bjacke.git] / source3 / libads / ldap.c
blobc8ef5b5774d72a4fefadbaaf1c449a54c851c2f9
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 ADS_STATUS status2;
1147 LDAPMessage *msg, *next;
1149 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1150 attrs, args, &res2, &count, &cookie);
1152 if (!ADS_ERR_OK(status2)) break;
1154 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1155 that this works on all ldap libs, but I have only tested with openldap */
1156 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1157 next = ads_next_message(ads, msg);
1158 ldap_add_result_entry((LDAPMessage **)res, msg);
1160 /* note that we do not free res2, as the memory is now
1161 part of the main returned list */
1163 #else
1164 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1165 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1166 #endif
1168 return status;
1171 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1172 int scope, const char *expr,
1173 const char **attrs, LDAPMessage **res)
1175 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1178 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1179 int scope, const char *expr,
1180 const char **attrs, uint32 sd_flags,
1181 LDAPMessage **res)
1183 ads_control args;
1185 args.control = ADS_SD_FLAGS_OID;
1186 args.val = sd_flags;
1187 args.critical = True;
1189 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1194 * Run a function on all results for a search. Uses ads_do_paged_search() and
1195 * runs the function as each page is returned, using ads_process_results()
1196 * @param ads connection to ads server
1197 * @param bind_path Base dn for the search
1198 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1199 * @param expr Search expression - specified in local charset
1200 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1201 * @param fn Function which takes attr name, values list, and data_area
1202 * @param data_area Pointer which is passed to function on each call
1203 * @return status of search
1205 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1206 int scope, const char *expr, const char **attrs,
1207 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1208 void *data_area)
1210 struct berval *cookie = NULL;
1211 int count = 0;
1212 ADS_STATUS status;
1213 LDAPMessage *res;
1215 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1216 &count, &cookie);
1218 if (!ADS_ERR_OK(status)) return status;
1220 ads_process_results(ads, res, fn, data_area);
1221 ads_msgfree(ads, res);
1223 while (cookie) {
1224 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1225 &res, &count, &cookie);
1227 if (!ADS_ERR_OK(status)) break;
1229 ads_process_results(ads, res, fn, data_area);
1230 ads_msgfree(ads, res);
1233 return status;
1237 * Do a search with a timeout.
1238 * @param ads connection to ads server
1239 * @param bind_path Base dn for the search
1240 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1241 * @param expr Search expression
1242 * @param attrs Attributes to retrieve
1243 * @param res ** which will contain results - free res* with ads_msgfree()
1244 * @return status of search
1246 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1247 const char *expr,
1248 const char **attrs, LDAPMessage **res)
1250 int rc;
1251 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1252 size_t converted_size;
1253 TALLOC_CTX *ctx;
1255 *res = NULL;
1256 if (!(ctx = talloc_init("ads_do_search"))) {
1257 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1258 return ADS_ERROR(LDAP_NO_MEMORY);
1261 /* 0 means the conversion worked but the result was empty
1262 so we only fail if it's negative. In any case, it always
1263 at least nulls out the dest */
1264 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1265 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1267 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1268 rc = LDAP_NO_MEMORY;
1269 goto done;
1272 if (!attrs || !(*attrs))
1273 search_attrs = NULL;
1274 else {
1275 /* This would be the utf8-encoded version...*/
1276 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1277 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1279 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1280 rc = LDAP_NO_MEMORY;
1281 goto done;
1285 /* see the note in ads_do_paged_search - we *must* disable referrals */
1286 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1288 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1289 search_attrs, 0, NULL, NULL,
1290 LDAP_NO_LIMIT,
1291 (LDAPMessage **)res);
1293 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1294 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1295 rc = 0;
1298 done:
1299 talloc_destroy(ctx);
1300 /* if/when we decide to utf8-encode attrs, take out this next line */
1301 TALLOC_FREE(search_attrs);
1302 return ADS_ERROR(rc);
1305 * Do a general ADS search
1306 * @param ads connection to ads server
1307 * @param res ** which will contain results - free res* with ads_msgfree()
1308 * @param expr Search expression
1309 * @param attrs Attributes to retrieve
1310 * @return status of search
1312 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1313 const char *expr, const char **attrs)
1315 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1316 expr, attrs, res);
1320 * Do a search on a specific DistinguishedName
1321 * @param ads connection to ads server
1322 * @param res ** which will contain results - free res* with ads_msgfree()
1323 * @param dn DistinguishName to search
1324 * @param attrs Attributes to retrieve
1325 * @return status of search
1327 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1328 const char *dn, const char **attrs)
1330 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1331 attrs, res);
1335 * Free up memory from a ads_search
1336 * @param ads connection to ads server
1337 * @param msg Search results to free
1339 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1341 if (!msg) return;
1342 ldap_msgfree(msg);
1346 * Get a dn from search results
1347 * @param ads connection to ads server
1348 * @param msg Search result
1349 * @return dn string
1351 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1353 char *utf8_dn, *unix_dn;
1354 size_t converted_size;
1356 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1358 if (!utf8_dn) {
1359 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1360 return NULL;
1363 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1364 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1365 utf8_dn ));
1366 return NULL;
1368 ldap_memfree(utf8_dn);
1369 return unix_dn;
1373 * Get the parent from a dn
1374 * @param dn the dn to return the parent from
1375 * @return parent dn string
1377 char *ads_parent_dn(const char *dn)
1379 char *p;
1381 if (dn == NULL) {
1382 return NULL;
1385 p = strchr(dn, ',');
1387 if (p == NULL) {
1388 return NULL;
1391 return p+1;
1395 * Find a machine account given a hostname
1396 * @param ads connection to ads server
1397 * @param res ** which will contain results - free res* with ads_msgfree()
1398 * @param host Hostname to search for
1399 * @return status of search
1401 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1402 const char *machine)
1404 ADS_STATUS status;
1405 char *expr;
1406 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1408 *res = NULL;
1410 /* the easiest way to find a machine account anywhere in the tree
1411 is to look for hostname$ */
1412 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1413 DEBUG(1, ("asprintf failed!\n"));
1414 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1417 status = ads_search(ads, res, expr, attrs);
1418 SAFE_FREE(expr);
1419 return status;
1423 * Initialize a list of mods to be used in a modify request
1424 * @param ctx An initialized TALLOC_CTX
1425 * @return allocated ADS_MODLIST
1427 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1429 #define ADS_MODLIST_ALLOC_SIZE 10
1430 LDAPMod **mods;
1432 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1433 /* -1 is safety to make sure we don't go over the end.
1434 need to reset it to NULL before doing ldap modify */
1435 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1437 return (ADS_MODLIST)mods;
1442 add an attribute to the list, with values list already constructed
1444 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1445 int mod_op, const char *name,
1446 const void *_invals)
1448 const void **invals = (const void **)_invals;
1449 int curmod;
1450 LDAPMod **modlist = (LDAPMod **) *mods;
1451 struct berval **ber_values = NULL;
1452 char **char_values = NULL;
1454 if (!invals) {
1455 mod_op = LDAP_MOD_DELETE;
1456 } else {
1457 if (mod_op & LDAP_MOD_BVALUES)
1458 ber_values = ads_dup_values(ctx,
1459 (const struct berval **)invals);
1460 else
1461 char_values = ads_push_strvals(ctx,
1462 (const char **) invals);
1465 /* find the first empty slot */
1466 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1467 curmod++);
1468 if (modlist[curmod] == (LDAPMod *) -1) {
1469 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1470 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1471 return ADS_ERROR(LDAP_NO_MEMORY);
1472 memset(&modlist[curmod], 0,
1473 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1474 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1475 *mods = (ADS_MODLIST)modlist;
1478 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1479 return ADS_ERROR(LDAP_NO_MEMORY);
1480 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1481 if (mod_op & LDAP_MOD_BVALUES) {
1482 modlist[curmod]->mod_bvalues = ber_values;
1483 } else if (mod_op & LDAP_MOD_DELETE) {
1484 modlist[curmod]->mod_values = NULL;
1485 } else {
1486 modlist[curmod]->mod_values = char_values;
1489 modlist[curmod]->mod_op = mod_op;
1490 return ADS_ERROR(LDAP_SUCCESS);
1494 * Add a single string value to a mod list
1495 * @param ctx An initialized TALLOC_CTX
1496 * @param mods An initialized ADS_MODLIST
1497 * @param name The attribute name to add
1498 * @param val The value to add - NULL means DELETE
1499 * @return ADS STATUS indicating success of add
1501 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1502 const char *name, const char *val)
1504 const char *values[2];
1506 values[0] = val;
1507 values[1] = NULL;
1509 if (!val)
1510 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1511 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1515 * Add an array of string values to a mod list
1516 * @param ctx An initialized TALLOC_CTX
1517 * @param mods An initialized ADS_MODLIST
1518 * @param name The attribute name to add
1519 * @param vals The array of string values to add - NULL means DELETE
1520 * @return ADS STATUS indicating success of add
1522 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1523 const char *name, const char **vals)
1525 if (!vals)
1526 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1527 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1528 name, (const void **) vals);
1531 #if 0
1533 * Add a single ber-encoded value to a mod list
1534 * @param ctx An initialized TALLOC_CTX
1535 * @param mods An initialized ADS_MODLIST
1536 * @param name The attribute name to add
1537 * @param val The value to add - NULL means DELETE
1538 * @return ADS STATUS indicating success of add
1540 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1541 const char *name, const struct berval *val)
1543 const struct berval *values[2];
1545 values[0] = val;
1546 values[1] = NULL;
1547 if (!val)
1548 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1549 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1550 name, (const void **) values);
1552 #endif
1555 * Perform an ldap modify
1556 * @param ads connection to ads server
1557 * @param mod_dn DistinguishedName to modify
1558 * @param mods list of modifications to perform
1559 * @return status of modify
1561 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1563 int ret,i;
1564 char *utf8_dn = NULL;
1565 size_t converted_size;
1567 this control is needed to modify that contains a currently
1568 non-existent attribute (but allowable for the object) to run
1570 LDAPControl PermitModify = {
1571 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1572 {0, NULL},
1573 (char) 1};
1574 LDAPControl *controls[2];
1576 controls[0] = &PermitModify;
1577 controls[1] = NULL;
1579 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1580 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1583 /* find the end of the list, marked by NULL or -1 */
1584 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1585 /* make sure the end of the list is NULL */
1586 mods[i] = NULL;
1587 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1588 (LDAPMod **) mods, controls, NULL);
1589 TALLOC_FREE(utf8_dn);
1590 return ADS_ERROR(ret);
1594 * Perform an ldap add
1595 * @param ads connection to ads server
1596 * @param new_dn DistinguishedName to add
1597 * @param mods list of attributes and values for DN
1598 * @return status of add
1600 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1602 int ret, i;
1603 char *utf8_dn = NULL;
1604 size_t converted_size;
1606 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1607 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1608 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1611 /* find the end of the list, marked by NULL or -1 */
1612 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1613 /* make sure the end of the list is NULL */
1614 mods[i] = NULL;
1616 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1617 TALLOC_FREE(utf8_dn);
1618 return ADS_ERROR(ret);
1622 * Delete a DistinguishedName
1623 * @param ads connection to ads server
1624 * @param new_dn DistinguishedName to delete
1625 * @return status of delete
1627 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1629 int ret;
1630 char *utf8_dn = NULL;
1631 size_t converted_size;
1632 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1633 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1634 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1637 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1638 TALLOC_FREE(utf8_dn);
1639 return ADS_ERROR(ret);
1643 * Build an org unit string
1644 * if org unit is Computers or blank then assume a container, otherwise
1645 * assume a / separated list of organisational units.
1646 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1647 * @param ads connection to ads server
1648 * @param org_unit Organizational unit
1649 * @return org unit string - caller must free
1651 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1653 char *ret = NULL;
1655 if (!org_unit || !*org_unit) {
1657 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1659 /* samba4 might not yet respond to a wellknownobject-query */
1660 return ret ? ret : SMB_STRDUP("cn=Computers");
1663 if (strequal(org_unit, "Computers")) {
1664 return SMB_STRDUP("cn=Computers");
1667 /* jmcd: removed "\\" from the separation chars, because it is
1668 needed as an escape for chars like '#' which are valid in an
1669 OU name */
1670 return ads_build_path(org_unit, "/", "ou=", 1);
1674 * Get a org unit string for a well-known GUID
1675 * @param ads connection to ads server
1676 * @param wknguid Well known GUID
1677 * @return org unit string - caller must free
1679 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1681 ADS_STATUS status;
1682 LDAPMessage *res = NULL;
1683 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1684 **bind_dn_exp = NULL;
1685 const char *attrs[] = {"distinguishedName", NULL};
1686 int new_ln, wkn_ln, bind_ln, i;
1688 if (wknguid == NULL) {
1689 return NULL;
1692 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1693 DEBUG(1, ("asprintf failed!\n"));
1694 return NULL;
1697 status = ads_search_dn(ads, &res, base, attrs);
1698 if (!ADS_ERR_OK(status)) {
1699 DEBUG(1,("Failed while searching for: %s\n", base));
1700 goto out;
1703 if (ads_count_replies(ads, res) != 1) {
1704 goto out;
1707 /* substitute the bind-path from the well-known-guid-search result */
1708 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1709 if (!wkn_dn) {
1710 goto out;
1713 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1714 if (!wkn_dn_exp) {
1715 goto out;
1718 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1719 if (!bind_dn_exp) {
1720 goto out;
1723 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1725 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1728 new_ln = wkn_ln - bind_ln;
1730 ret = SMB_STRDUP(wkn_dn_exp[0]);
1731 if (!ret) {
1732 goto out;
1735 for (i=1; i < new_ln; i++) {
1736 char *s = NULL;
1738 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1739 SAFE_FREE(ret);
1740 goto out;
1743 SAFE_FREE(ret);
1744 ret = SMB_STRDUP(s);
1745 free(s);
1746 if (!ret) {
1747 goto out;
1751 out:
1752 SAFE_FREE(base);
1753 ads_msgfree(ads, res);
1754 TALLOC_FREE(wkn_dn);
1755 if (wkn_dn_exp) {
1756 ldap_value_free(wkn_dn_exp);
1758 if (bind_dn_exp) {
1759 ldap_value_free(bind_dn_exp);
1762 return ret;
1766 * Adds (appends) an item to an attribute array, rather then
1767 * replacing the whole list
1768 * @param ctx An initialized TALLOC_CTX
1769 * @param mods An initialized ADS_MODLIST
1770 * @param name name of the ldap attribute to append to
1771 * @param vals an array of values to add
1772 * @return status of addition
1775 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1776 const char *name, const char **vals)
1778 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1779 (const void *) vals);
1783 * Determines the an account's current KVNO via an LDAP lookup
1784 * @param ads An initialized ADS_STRUCT
1785 * @param account_name the NT samaccountname.
1786 * @return the kvno for the account, or -1 in case of a failure.
1789 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1791 LDAPMessage *res = NULL;
1792 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1793 char *filter;
1794 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1795 char *dn_string = NULL;
1796 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1798 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1799 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1800 return kvno;
1802 ret = ads_search(ads, &res, filter, attrs);
1803 SAFE_FREE(filter);
1804 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1805 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1806 ads_msgfree(ads, res);
1807 return kvno;
1810 dn_string = ads_get_dn(ads, talloc_tos(), res);
1811 if (!dn_string) {
1812 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1813 ads_msgfree(ads, res);
1814 return kvno;
1816 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1817 TALLOC_FREE(dn_string);
1819 /* ---------------------------------------------------------
1820 * 0 is returned as a default KVNO from this point on...
1821 * This is done because Windows 2000 does not support key
1822 * version numbers. Chances are that a failure in the next
1823 * step is simply due to Windows 2000 being used for a
1824 * domain controller. */
1825 kvno = 0;
1827 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1828 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1829 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1830 ads_msgfree(ads, res);
1831 return kvno;
1834 /* Success */
1835 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1836 ads_msgfree(ads, res);
1837 return kvno;
1841 * Determines the computer account's current KVNO via an LDAP lookup
1842 * @param ads An initialized ADS_STRUCT
1843 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1844 * @return the kvno for the computer account, or -1 in case of a failure.
1847 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1849 char *computer_account = NULL;
1850 uint32_t kvno = -1;
1852 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1853 return kvno;
1856 kvno = ads_get_kvno(ads, computer_account);
1857 free(computer_account);
1859 return kvno;
1863 * This clears out all registered spn's for a given hostname
1864 * @param ads An initilaized ADS_STRUCT
1865 * @param machine_name the NetBIOS name of the computer.
1866 * @return 0 upon success, non-zero otherwise.
1869 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1871 TALLOC_CTX *ctx;
1872 LDAPMessage *res = NULL;
1873 ADS_MODLIST mods;
1874 const char *servicePrincipalName[1] = {NULL};
1875 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1876 char *dn_string = NULL;
1878 ret = ads_find_machine_acct(ads, &res, machine_name);
1879 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1880 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1881 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1882 ads_msgfree(ads, res);
1883 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1886 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1887 ctx = talloc_init("ads_clear_service_principal_names");
1888 if (!ctx) {
1889 ads_msgfree(ads, res);
1890 return ADS_ERROR(LDAP_NO_MEMORY);
1893 if (!(mods = ads_init_mods(ctx))) {
1894 talloc_destroy(ctx);
1895 ads_msgfree(ads, res);
1896 return ADS_ERROR(LDAP_NO_MEMORY);
1898 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1899 if (!ADS_ERR_OK(ret)) {
1900 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1901 ads_msgfree(ads, res);
1902 talloc_destroy(ctx);
1903 return ret;
1905 dn_string = ads_get_dn(ads, talloc_tos(), res);
1906 if (!dn_string) {
1907 talloc_destroy(ctx);
1908 ads_msgfree(ads, res);
1909 return ADS_ERROR(LDAP_NO_MEMORY);
1911 ret = ads_gen_mod(ads, dn_string, mods);
1912 TALLOC_FREE(dn_string);
1913 if (!ADS_ERR_OK(ret)) {
1914 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1915 machine_name));
1916 ads_msgfree(ads, res);
1917 talloc_destroy(ctx);
1918 return ret;
1921 ads_msgfree(ads, res);
1922 talloc_destroy(ctx);
1923 return ret;
1927 * This adds a service principal name to an existing computer account
1928 * (found by hostname) in AD.
1929 * @param ads An initialized ADS_STRUCT
1930 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1931 * @param my_fqdn The fully qualified DNS name of the machine
1932 * @param spn A string of the service principal to add, i.e. 'host'
1933 * @return 0 upon sucess, or non-zero if a failure occurs
1936 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1937 const char *my_fqdn, const char *spn)
1939 ADS_STATUS ret;
1940 TALLOC_CTX *ctx;
1941 LDAPMessage *res = NULL;
1942 char *psp1, *psp2;
1943 ADS_MODLIST mods;
1944 char *dn_string = NULL;
1945 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1947 ret = ads_find_machine_acct(ads, &res, machine_name);
1948 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1949 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1950 machine_name));
1951 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1952 spn, machine_name, ads->config.realm));
1953 ads_msgfree(ads, res);
1954 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1957 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1958 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1959 ads_msgfree(ads, res);
1960 return ADS_ERROR(LDAP_NO_MEMORY);
1963 /* add short name spn */
1965 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1966 talloc_destroy(ctx);
1967 ads_msgfree(ads, res);
1968 return ADS_ERROR(LDAP_NO_MEMORY);
1970 if (!strlower_m(&psp1[strlen(spn) + 1])) {
1971 ret = ADS_ERROR(LDAP_NO_MEMORY);
1972 goto out;
1974 servicePrincipalName[0] = psp1;
1976 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1977 psp1, machine_name));
1980 /* add fully qualified spn */
1982 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1983 ret = ADS_ERROR(LDAP_NO_MEMORY);
1984 goto out;
1986 if (!strlower_m(&psp2[strlen(spn) + 1])) {
1987 ret = ADS_ERROR(LDAP_NO_MEMORY);
1988 goto out;
1990 servicePrincipalName[1] = psp2;
1992 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1993 psp2, machine_name));
1995 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1996 ret = ADS_ERROR(LDAP_NO_MEMORY);
1997 goto out;
2000 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2001 if (!ADS_ERR_OK(ret)) {
2002 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2003 goto out;
2006 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2007 ret = ADS_ERROR(LDAP_NO_MEMORY);
2008 goto out;
2011 ret = ads_gen_mod(ads, dn_string, mods);
2012 if (!ADS_ERR_OK(ret)) {
2013 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2014 goto out;
2017 out:
2018 TALLOC_FREE( ctx );
2019 ads_msgfree(ads, res);
2020 return ret;
2024 * adds a machine account to the ADS server
2025 * @param ads An intialized ADS_STRUCT
2026 * @param machine_name - the NetBIOS machine name of this account.
2027 * @param account_type A number indicating the type of account to create
2028 * @param org_unit The LDAP path in which to place this account
2029 * @return 0 upon success, or non-zero otherwise
2032 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2033 const char *org_unit)
2035 ADS_STATUS ret;
2036 char *samAccountName, *controlstr;
2037 TALLOC_CTX *ctx;
2038 ADS_MODLIST mods;
2039 char *machine_escaped = NULL;
2040 char *new_dn;
2041 const char *objectClass[] = {"top", "person", "organizationalPerson",
2042 "user", "computer", NULL};
2043 LDAPMessage *res = NULL;
2044 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2045 UF_DONT_EXPIRE_PASSWD |\
2046 UF_ACCOUNTDISABLE );
2048 if (!(ctx = talloc_init("ads_add_machine_acct")))
2049 return ADS_ERROR(LDAP_NO_MEMORY);
2051 ret = ADS_ERROR(LDAP_NO_MEMORY);
2053 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2054 if (!machine_escaped) {
2055 goto done;
2058 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2059 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2061 if ( !new_dn || !samAccountName ) {
2062 goto done;
2065 #ifndef ENCTYPE_ARCFOUR_HMAC
2066 acct_control |= UF_USE_DES_KEY_ONLY;
2067 #endif
2069 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2070 goto done;
2073 if (!(mods = ads_init_mods(ctx))) {
2074 goto done;
2077 ads_mod_str(ctx, &mods, "cn", machine_name);
2078 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2079 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2080 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2082 ret = ads_gen_add(ads, new_dn, mods);
2084 done:
2085 SAFE_FREE(machine_escaped);
2086 ads_msgfree(ads, res);
2087 talloc_destroy(ctx);
2089 return ret;
2093 * move a machine account to another OU on the ADS server
2094 * @param ads - An intialized ADS_STRUCT
2095 * @param machine_name - the NetBIOS machine name of this account.
2096 * @param org_unit - The LDAP path in which to place this account
2097 * @param moved - whether we moved the machine account (optional)
2098 * @return 0 upon success, or non-zero otherwise
2101 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2102 const char *org_unit, bool *moved)
2104 ADS_STATUS rc;
2105 int ldap_status;
2106 LDAPMessage *res = NULL;
2107 char *filter = NULL;
2108 char *computer_dn = NULL;
2109 char *parent_dn;
2110 char *computer_rdn = NULL;
2111 bool need_move = False;
2113 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2114 rc = ADS_ERROR(LDAP_NO_MEMORY);
2115 goto done;
2118 /* Find pre-existing machine */
2119 rc = ads_search(ads, &res, filter, NULL);
2120 if (!ADS_ERR_OK(rc)) {
2121 goto done;
2124 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2125 if (!computer_dn) {
2126 rc = ADS_ERROR(LDAP_NO_MEMORY);
2127 goto done;
2130 parent_dn = ads_parent_dn(computer_dn);
2131 if (strequal(parent_dn, org_unit)) {
2132 goto done;
2135 need_move = True;
2137 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2138 rc = ADS_ERROR(LDAP_NO_MEMORY);
2139 goto done;
2142 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2143 org_unit, 1, NULL, NULL);
2144 rc = ADS_ERROR(ldap_status);
2146 done:
2147 ads_msgfree(ads, res);
2148 SAFE_FREE(filter);
2149 TALLOC_FREE(computer_dn);
2150 SAFE_FREE(computer_rdn);
2152 if (!ADS_ERR_OK(rc)) {
2153 need_move = False;
2156 if (moved) {
2157 *moved = need_move;
2160 return rc;
2164 dump a binary result from ldap
2166 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2168 int i, j;
2169 for (i=0; values[i]; i++) {
2170 printf("%s: ", field);
2171 for (j=0; j<values[i]->bv_len; j++) {
2172 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2174 printf("\n");
2178 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2180 int i;
2181 for (i=0; values[i]; i++) {
2182 NTSTATUS status;
2183 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2184 struct GUID guid;
2186 status = GUID_from_ndr_blob(&in, &guid);
2187 if (NT_STATUS_IS_OK(status)) {
2188 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2189 } else {
2190 printf("%s: INVALID GUID\n", field);
2196 dump a sid result from ldap
2198 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2200 int i;
2201 for (i=0; values[i]; i++) {
2202 struct dom_sid sid;
2203 fstring tmp;
2204 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2205 return;
2207 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2212 dump ntSecurityDescriptor
2214 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2216 TALLOC_CTX *frame = talloc_stackframe();
2217 struct security_descriptor *psd;
2218 NTSTATUS status;
2220 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2221 values[0]->bv_len, &psd);
2222 if (!NT_STATUS_IS_OK(status)) {
2223 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2224 nt_errstr(status)));
2225 TALLOC_FREE(frame);
2226 return;
2229 if (psd) {
2230 ads_disp_sd(ads, talloc_tos(), psd);
2233 TALLOC_FREE(frame);
2237 dump a string result from ldap
2239 static void dump_string(const char *field, char **values)
2241 int i;
2242 for (i=0; values[i]; i++) {
2243 printf("%s: %s\n", field, values[i]);
2248 dump a field from LDAP on stdout
2249 used for debugging
2252 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2254 const struct {
2255 const char *name;
2256 bool string;
2257 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2258 } handlers[] = {
2259 {"objectGUID", False, dump_guid},
2260 {"netbootGUID", False, dump_guid},
2261 {"nTSecurityDescriptor", False, dump_sd},
2262 {"dnsRecord", False, dump_binary},
2263 {"objectSid", False, dump_sid},
2264 {"tokenGroups", False, dump_sid},
2265 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2266 {"tokengroupsGlobalandUniversal", False, dump_sid},
2267 {"mS-DS-CreatorSID", False, dump_sid},
2268 {"msExchMailboxGuid", False, dump_guid},
2269 {NULL, True, NULL}
2271 int i;
2273 if (!field) { /* must be end of an entry */
2274 printf("\n");
2275 return False;
2278 for (i=0; handlers[i].name; i++) {
2279 if (strcasecmp_m(handlers[i].name, field) == 0) {
2280 if (!values) /* first time, indicate string or not */
2281 return handlers[i].string;
2282 handlers[i].handler(ads, field, (struct berval **) values);
2283 break;
2286 if (!handlers[i].name) {
2287 if (!values) /* first time, indicate string conversion */
2288 return True;
2289 dump_string(field, (char **)values);
2291 return False;
2295 * Dump a result from LDAP on stdout
2296 * used for debugging
2297 * @param ads connection to ads server
2298 * @param res Results to dump
2301 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2303 ads_process_results(ads, res, ads_dump_field, NULL);
2307 * Walk through results, calling a function for each entry found.
2308 * The function receives a field name, a berval * array of values,
2309 * and a data area passed through from the start. The function is
2310 * called once with null for field and values at the end of each
2311 * entry.
2312 * @param ads connection to ads server
2313 * @param res Results to process
2314 * @param fn Function for processing each result
2315 * @param data_area user-defined area to pass to function
2317 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2318 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2319 void *data_area)
2321 LDAPMessage *msg;
2322 TALLOC_CTX *ctx;
2323 size_t converted_size;
2325 if (!(ctx = talloc_init("ads_process_results")))
2326 return;
2328 for (msg = ads_first_entry(ads, res); msg;
2329 msg = ads_next_entry(ads, msg)) {
2330 char *utf8_field;
2331 BerElement *b;
2333 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2334 (LDAPMessage *)msg,&b);
2335 utf8_field;
2336 utf8_field=ldap_next_attribute(ads->ldap.ld,
2337 (LDAPMessage *)msg,b)) {
2338 struct berval **ber_vals;
2339 char **str_vals, **utf8_vals;
2340 char *field;
2341 bool string;
2343 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2344 &converted_size))
2346 DEBUG(0,("ads_process_results: "
2347 "pull_utf8_talloc failed: %s",
2348 strerror(errno)));
2351 string = fn(ads, field, NULL, data_area);
2353 if (string) {
2354 utf8_vals = ldap_get_values(ads->ldap.ld,
2355 (LDAPMessage *)msg, field);
2356 str_vals = ads_pull_strvals(ctx,
2357 (const char **) utf8_vals);
2358 fn(ads, field, (void **) str_vals, data_area);
2359 ldap_value_free(utf8_vals);
2360 } else {
2361 ber_vals = ldap_get_values_len(ads->ldap.ld,
2362 (LDAPMessage *)msg, field);
2363 fn(ads, field, (void **) ber_vals, data_area);
2365 ldap_value_free_len(ber_vals);
2367 ldap_memfree(utf8_field);
2369 ber_free(b, 0);
2370 talloc_free_children(ctx);
2371 fn(ads, NULL, NULL, data_area); /* completed an entry */
2374 talloc_destroy(ctx);
2378 * count how many replies are in a LDAPMessage
2379 * @param ads connection to ads server
2380 * @param res Results to count
2381 * @return number of replies
2383 int ads_count_replies(ADS_STRUCT *ads, void *res)
2385 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2389 * pull the first entry from a ADS result
2390 * @param ads connection to ads server
2391 * @param res Results of search
2392 * @return first entry from result
2394 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2396 return ldap_first_entry(ads->ldap.ld, res);
2400 * pull the next entry from a ADS result
2401 * @param ads connection to ads server
2402 * @param res Results of search
2403 * @return next entry from result
2405 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2407 return ldap_next_entry(ads->ldap.ld, res);
2411 * pull the first message from a ADS result
2412 * @param ads connection to ads server
2413 * @param res Results of search
2414 * @return first message from result
2416 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2418 return ldap_first_message(ads->ldap.ld, res);
2422 * pull the next message from a ADS result
2423 * @param ads connection to ads server
2424 * @param res Results of search
2425 * @return next message from result
2427 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2429 return ldap_next_message(ads->ldap.ld, res);
2433 * pull a single string from a ADS result
2434 * @param ads connection to ads server
2435 * @param mem_ctx TALLOC_CTX to use for allocating result string
2436 * @param msg Results of search
2437 * @param field Attribute to retrieve
2438 * @return Result string in talloc context
2440 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2441 const char *field)
2443 char **values;
2444 char *ret = NULL;
2445 char *ux_string;
2446 size_t converted_size;
2448 values = ldap_get_values(ads->ldap.ld, msg, field);
2449 if (!values)
2450 return NULL;
2452 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2453 &converted_size))
2455 ret = ux_string;
2457 ldap_value_free(values);
2458 return ret;
2462 * pull an array of strings from a ADS result
2463 * @param ads connection to ads server
2464 * @param mem_ctx TALLOC_CTX to use for allocating result string
2465 * @param msg Results of search
2466 * @param field Attribute to retrieve
2467 * @return Result strings in talloc context
2469 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2470 LDAPMessage *msg, const char *field,
2471 size_t *num_values)
2473 char **values;
2474 char **ret = NULL;
2475 int i;
2476 size_t converted_size;
2478 values = ldap_get_values(ads->ldap.ld, msg, field);
2479 if (!values)
2480 return NULL;
2482 *num_values = ldap_count_values(values);
2484 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2485 if (!ret) {
2486 ldap_value_free(values);
2487 return NULL;
2490 for (i=0;i<*num_values;i++) {
2491 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2492 &converted_size))
2494 ldap_value_free(values);
2495 return NULL;
2498 ret[i] = NULL;
2500 ldap_value_free(values);
2501 return ret;
2505 * pull an array of strings from a ADS result
2506 * (handle large multivalue attributes with range retrieval)
2507 * @param ads connection to ads server
2508 * @param mem_ctx TALLOC_CTX to use for allocating result string
2509 * @param msg Results of search
2510 * @param field Attribute to retrieve
2511 * @param current_strings strings returned by a previous call to this function
2512 * @param next_attribute The next query should ask for this attribute
2513 * @param num_values How many values did we get this time?
2514 * @param more_values Are there more values to get?
2515 * @return Result strings in talloc context
2517 char **ads_pull_strings_range(ADS_STRUCT *ads,
2518 TALLOC_CTX *mem_ctx,
2519 LDAPMessage *msg, const char *field,
2520 char **current_strings,
2521 const char **next_attribute,
2522 size_t *num_strings,
2523 bool *more_strings)
2525 char *attr;
2526 char *expected_range_attrib, *range_attr;
2527 BerElement *ptr = NULL;
2528 char **strings;
2529 char **new_strings;
2530 size_t num_new_strings;
2531 unsigned long int range_start;
2532 unsigned long int range_end;
2534 /* we might have been given the whole lot anyway */
2535 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2536 *more_strings = False;
2537 return strings;
2540 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2542 /* look for Range result */
2543 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2544 attr;
2545 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2546 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2547 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2548 range_attr = attr;
2549 break;
2551 ldap_memfree(attr);
2553 if (!attr) {
2554 ber_free(ptr, 0);
2555 /* nothing here - this field is just empty */
2556 *more_strings = False;
2557 return NULL;
2560 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2561 &range_start, &range_end) == 2) {
2562 *more_strings = True;
2563 } else {
2564 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2565 &range_start) == 1) {
2566 *more_strings = False;
2567 } else {
2568 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2569 range_attr));
2570 ldap_memfree(range_attr);
2571 *more_strings = False;
2572 return NULL;
2576 if ((*num_strings) != range_start) {
2577 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2578 " - aborting range retreival\n",
2579 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2580 ldap_memfree(range_attr);
2581 *more_strings = False;
2582 return NULL;
2585 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2587 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2588 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2589 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2590 range_attr, (unsigned long int)range_end - range_start + 1,
2591 (unsigned long int)num_new_strings));
2592 ldap_memfree(range_attr);
2593 *more_strings = False;
2594 return NULL;
2597 strings = talloc_realloc(mem_ctx, current_strings, char *,
2598 *num_strings + num_new_strings);
2600 if (strings == NULL) {
2601 ldap_memfree(range_attr);
2602 *more_strings = False;
2603 return NULL;
2606 if (new_strings && num_new_strings) {
2607 memcpy(&strings[*num_strings], new_strings,
2608 sizeof(*new_strings) * num_new_strings);
2611 (*num_strings) += num_new_strings;
2613 if (*more_strings) {
2614 *next_attribute = talloc_asprintf(mem_ctx,
2615 "%s;range=%d-*",
2616 field,
2617 (int)*num_strings);
2619 if (!*next_attribute) {
2620 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2621 ldap_memfree(range_attr);
2622 *more_strings = False;
2623 return NULL;
2627 ldap_memfree(range_attr);
2629 return strings;
2633 * pull a single uint32 from a ADS result
2634 * @param ads connection to ads server
2635 * @param msg Results of search
2636 * @param field Attribute to retrieve
2637 * @param v Pointer to int to store result
2638 * @return boolean inidicating success
2640 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2641 uint32 *v)
2643 char **values;
2645 values = ldap_get_values(ads->ldap.ld, msg, field);
2646 if (!values)
2647 return False;
2648 if (!values[0]) {
2649 ldap_value_free(values);
2650 return False;
2653 *v = atoi(values[0]);
2654 ldap_value_free(values);
2655 return True;
2659 * pull a single objectGUID from an ADS result
2660 * @param ads connection to ADS server
2661 * @param msg results of search
2662 * @param guid 37-byte area to receive text guid
2663 * @return boolean indicating success
2665 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2667 DATA_BLOB blob;
2668 NTSTATUS status;
2670 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2671 &blob)) {
2672 return false;
2675 status = GUID_from_ndr_blob(&blob, guid);
2676 talloc_free(blob.data);
2677 return NT_STATUS_IS_OK(status);
2682 * pull a single struct dom_sid from a ADS result
2683 * @param ads connection to ads server
2684 * @param msg Results of search
2685 * @param field Attribute to retrieve
2686 * @param sid Pointer to sid to store result
2687 * @return boolean inidicating success
2689 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2690 struct dom_sid *sid)
2692 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2696 * pull an array of struct dom_sids from a ADS result
2697 * @param ads connection to ads server
2698 * @param mem_ctx TALLOC_CTX for allocating sid array
2699 * @param msg Results of search
2700 * @param field Attribute to retrieve
2701 * @param sids pointer to sid array to allocate
2702 * @return the count of SIDs pulled
2704 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2705 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2707 struct berval **values;
2708 bool ret;
2709 int count, i;
2711 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2713 if (!values)
2714 return 0;
2716 for (i=0; values[i]; i++)
2717 /* nop */ ;
2719 if (i) {
2720 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2721 if (!(*sids)) {
2722 ldap_value_free_len(values);
2723 return 0;
2725 } else {
2726 (*sids) = NULL;
2729 count = 0;
2730 for (i=0; values[i]; i++) {
2731 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2732 if (ret) {
2733 DEBUG(10, ("pulling SID: %s\n",
2734 sid_string_dbg(&(*sids)[count])));
2735 count++;
2739 ldap_value_free_len(values);
2740 return count;
2744 * pull a struct security_descriptor from a ADS result
2745 * @param ads connection to ads server
2746 * @param mem_ctx TALLOC_CTX for allocating sid array
2747 * @param msg Results of search
2748 * @param field Attribute to retrieve
2749 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2750 * @return boolean inidicating success
2752 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2753 LDAPMessage *msg, const char *field,
2754 struct security_descriptor **sd)
2756 struct berval **values;
2757 bool ret = true;
2759 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2761 if (!values) return false;
2763 if (values[0]) {
2764 NTSTATUS status;
2765 status = unmarshall_sec_desc(mem_ctx,
2766 (uint8 *)values[0]->bv_val,
2767 values[0]->bv_len, sd);
2768 if (!NT_STATUS_IS_OK(status)) {
2769 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2770 nt_errstr(status)));
2771 ret = false;
2775 ldap_value_free_len(values);
2776 return ret;
2780 * in order to support usernames longer than 21 characters we need to
2781 * use both the sAMAccountName and the userPrincipalName attributes
2782 * It seems that not all users have the userPrincipalName attribute set
2784 * @param ads connection to ads server
2785 * @param mem_ctx TALLOC_CTX for allocating sid array
2786 * @param msg Results of search
2787 * @return the username
2789 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2790 LDAPMessage *msg)
2792 #if 0 /* JERRY */
2793 char *ret, *p;
2795 /* lookup_name() only works on the sAMAccountName to
2796 returning the username portion of userPrincipalName
2797 breaks winbindd_getpwnam() */
2799 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2800 if (ret && (p = strchr_m(ret, '@'))) {
2801 *p = 0;
2802 return ret;
2804 #endif
2805 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2810 * find the update serial number - this is the core of the ldap cache
2811 * @param ads connection to ads server
2812 * @param ads connection to ADS server
2813 * @param usn Pointer to retrieved update serial number
2814 * @return status of search
2816 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2818 const char *attrs[] = {"highestCommittedUSN", NULL};
2819 ADS_STATUS status;
2820 LDAPMessage *res;
2822 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2823 if (!ADS_ERR_OK(status))
2824 return status;
2826 if (ads_count_replies(ads, res) != 1) {
2827 ads_msgfree(ads, res);
2828 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2831 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2832 ads_msgfree(ads, res);
2833 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2836 ads_msgfree(ads, res);
2837 return ADS_SUCCESS;
2840 /* parse a ADS timestring - typical string is
2841 '20020917091222.0Z0' which means 09:12.22 17th September
2842 2002, timezone 0 */
2843 static time_t ads_parse_time(const char *str)
2845 struct tm tm;
2847 ZERO_STRUCT(tm);
2849 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2850 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2851 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2852 return 0;
2854 tm.tm_year -= 1900;
2855 tm.tm_mon -= 1;
2857 return timegm(&tm);
2860 /********************************************************************
2861 ********************************************************************/
2863 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2865 const char *attrs[] = {"currentTime", NULL};
2866 ADS_STATUS status;
2867 LDAPMessage *res;
2868 char *timestr;
2869 TALLOC_CTX *ctx;
2870 ADS_STRUCT *ads_s = ads;
2872 if (!(ctx = talloc_init("ads_current_time"))) {
2873 return ADS_ERROR(LDAP_NO_MEMORY);
2876 /* establish a new ldap tcp session if necessary */
2878 if ( !ads->ldap.ld ) {
2879 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2880 ads->server.ldap_server )) == NULL )
2882 goto done;
2884 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2885 status = ads_connect( ads_s );
2886 if ( !ADS_ERR_OK(status))
2887 goto done;
2890 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2891 if (!ADS_ERR_OK(status)) {
2892 goto done;
2895 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2896 if (!timestr) {
2897 ads_msgfree(ads_s, res);
2898 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2899 goto done;
2902 /* but save the time and offset in the original ADS_STRUCT */
2904 ads->config.current_time = ads_parse_time(timestr);
2906 if (ads->config.current_time != 0) {
2907 ads->auth.time_offset = ads->config.current_time - time(NULL);
2908 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2911 ads_msgfree(ads, res);
2913 status = ADS_SUCCESS;
2915 done:
2916 /* free any temporary ads connections */
2917 if ( ads_s != ads ) {
2918 ads_destroy( &ads_s );
2920 talloc_destroy(ctx);
2922 return status;
2925 /********************************************************************
2926 ********************************************************************/
2928 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2930 const char *attrs[] = {"domainFunctionality", NULL};
2931 ADS_STATUS status;
2932 LDAPMessage *res;
2933 ADS_STRUCT *ads_s = ads;
2935 *val = DS_DOMAIN_FUNCTION_2000;
2937 /* establish a new ldap tcp session if necessary */
2939 if ( !ads->ldap.ld ) {
2940 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2941 ads->server.ldap_server )) == NULL )
2943 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2944 goto done;
2946 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2947 status = ads_connect( ads_s );
2948 if ( !ADS_ERR_OK(status))
2949 goto done;
2952 /* If the attribute does not exist assume it is a Windows 2000
2953 functional domain */
2955 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2956 if (!ADS_ERR_OK(status)) {
2957 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2958 status = ADS_SUCCESS;
2960 goto done;
2963 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2964 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2966 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2969 ads_msgfree(ads, res);
2971 done:
2972 /* free any temporary ads connections */
2973 if ( ads_s != ads ) {
2974 ads_destroy( &ads_s );
2977 return status;
2981 * find the domain sid for our domain
2982 * @param ads connection to ads server
2983 * @param sid Pointer to domain sid
2984 * @return status of search
2986 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2988 const char *attrs[] = {"objectSid", NULL};
2989 LDAPMessage *res;
2990 ADS_STATUS rc;
2992 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2993 attrs, &res);
2994 if (!ADS_ERR_OK(rc)) return rc;
2995 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2996 ads_msgfree(ads, res);
2997 return ADS_ERROR_SYSTEM(ENOENT);
2999 ads_msgfree(ads, res);
3001 return ADS_SUCCESS;
3005 * find our site name
3006 * @param ads connection to ads server
3007 * @param mem_ctx Pointer to talloc context
3008 * @param site_name Pointer to the sitename
3009 * @return status of search
3011 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3013 ADS_STATUS status;
3014 LDAPMessage *res;
3015 const char *dn, *service_name;
3016 const char *attrs[] = { "dsServiceName", NULL };
3018 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3019 if (!ADS_ERR_OK(status)) {
3020 return status;
3023 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3024 if (service_name == NULL) {
3025 ads_msgfree(ads, res);
3026 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3029 ads_msgfree(ads, res);
3031 /* go up three levels */
3032 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3033 if (dn == NULL) {
3034 return ADS_ERROR(LDAP_NO_MEMORY);
3037 *site_name = talloc_strdup(mem_ctx, dn);
3038 if (*site_name == NULL) {
3039 return ADS_ERROR(LDAP_NO_MEMORY);
3042 return status;
3044 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3049 * find the site dn where a machine resides
3050 * @param ads connection to ads server
3051 * @param mem_ctx Pointer to talloc context
3052 * @param computer_name name of the machine
3053 * @param site_name Pointer to the sitename
3054 * @return status of search
3056 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3058 ADS_STATUS status;
3059 LDAPMessage *res;
3060 const char *parent, *filter;
3061 char *config_context = NULL;
3062 char *dn;
3064 /* shortcut a query */
3065 if (strequal(computer_name, ads->config.ldap_server_name)) {
3066 return ads_site_dn(ads, mem_ctx, site_dn);
3069 status = ads_config_path(ads, mem_ctx, &config_context);
3070 if (!ADS_ERR_OK(status)) {
3071 return status;
3074 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3075 if (filter == NULL) {
3076 return ADS_ERROR(LDAP_NO_MEMORY);
3079 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3080 filter, NULL, &res);
3081 if (!ADS_ERR_OK(status)) {
3082 return status;
3085 if (ads_count_replies(ads, res) != 1) {
3086 ads_msgfree(ads, res);
3087 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3090 dn = ads_get_dn(ads, mem_ctx, res);
3091 if (dn == NULL) {
3092 ads_msgfree(ads, res);
3093 return ADS_ERROR(LDAP_NO_MEMORY);
3096 /* go up three levels */
3097 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3098 if (parent == NULL) {
3099 ads_msgfree(ads, res);
3100 TALLOC_FREE(dn);
3101 return ADS_ERROR(LDAP_NO_MEMORY);
3104 *site_dn = talloc_strdup(mem_ctx, parent);
3105 if (*site_dn == NULL) {
3106 ads_msgfree(ads, res);
3107 TALLOC_FREE(dn);
3108 return ADS_ERROR(LDAP_NO_MEMORY);
3111 TALLOC_FREE(dn);
3112 ads_msgfree(ads, res);
3114 return status;
3118 * get the upn suffixes for a domain
3119 * @param ads connection to ads server
3120 * @param mem_ctx Pointer to talloc context
3121 * @param suffixes Pointer to an array of suffixes
3122 * @param num_suffixes Pointer to the number of suffixes
3123 * @return status of search
3125 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3127 ADS_STATUS status;
3128 LDAPMessage *res;
3129 const char *base;
3130 char *config_context = NULL;
3131 const char *attrs[] = { "uPNSuffixes", NULL };
3133 status = ads_config_path(ads, mem_ctx, &config_context);
3134 if (!ADS_ERR_OK(status)) {
3135 return status;
3138 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3139 if (base == NULL) {
3140 return ADS_ERROR(LDAP_NO_MEMORY);
3143 status = ads_search_dn(ads, &res, base, attrs);
3144 if (!ADS_ERR_OK(status)) {
3145 return status;
3148 if (ads_count_replies(ads, res) != 1) {
3149 ads_msgfree(ads, res);
3150 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3153 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3154 if ((*suffixes) == NULL) {
3155 ads_msgfree(ads, res);
3156 return ADS_ERROR(LDAP_NO_MEMORY);
3159 ads_msgfree(ads, res);
3161 return status;
3165 * get the joinable ous for a domain
3166 * @param ads connection to ads server
3167 * @param mem_ctx Pointer to talloc context
3168 * @param ous Pointer to an array of ous
3169 * @param num_ous Pointer to the number of ous
3170 * @return status of search
3172 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3173 TALLOC_CTX *mem_ctx,
3174 char ***ous,
3175 size_t *num_ous)
3177 ADS_STATUS status;
3178 LDAPMessage *res = NULL;
3179 LDAPMessage *msg = NULL;
3180 const char *attrs[] = { "dn", NULL };
3181 int count = 0;
3183 status = ads_search(ads, &res,
3184 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3185 attrs);
3186 if (!ADS_ERR_OK(status)) {
3187 return status;
3190 count = ads_count_replies(ads, res);
3191 if (count < 1) {
3192 ads_msgfree(ads, res);
3193 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3196 for (msg = ads_first_entry(ads, res); msg;
3197 msg = ads_next_entry(ads, msg)) {
3199 char *dn = NULL;
3201 dn = ads_get_dn(ads, talloc_tos(), msg);
3202 if (!dn) {
3203 ads_msgfree(ads, res);
3204 return ADS_ERROR(LDAP_NO_MEMORY);
3207 if (!add_string_to_array(mem_ctx, dn,
3208 (const char ***)ous,
3209 (int *)num_ous)) {
3210 TALLOC_FREE(dn);
3211 ads_msgfree(ads, res);
3212 return ADS_ERROR(LDAP_NO_MEMORY);
3215 TALLOC_FREE(dn);
3218 ads_msgfree(ads, res);
3220 return status;
3225 * pull a struct dom_sid from an extended dn string
3226 * @param mem_ctx TALLOC_CTX
3227 * @param extended_dn string
3228 * @param flags string type of extended_dn
3229 * @param sid pointer to a struct dom_sid
3230 * @return NT_STATUS_OK on success,
3231 * NT_INVALID_PARAMETER on error,
3232 * NT_STATUS_NOT_FOUND if no SID present
3234 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3235 const char *extended_dn,
3236 enum ads_extended_dn_flags flags,
3237 struct dom_sid *sid)
3239 char *p, *q, *dn;
3241 if (!extended_dn) {
3242 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3245 /* otherwise extended_dn gets stripped off */
3246 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3247 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3250 * ADS_EXTENDED_DN_HEX_STRING:
3251 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3253 * ADS_EXTENDED_DN_STRING (only with w2k3):
3254 * <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
3256 * Object with no SID, such as an Exchange Public Folder
3257 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3260 p = strchr(dn, ';');
3261 if (!p) {
3262 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3265 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3266 DEBUG(5,("No SID present in extended dn\n"));
3267 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3270 p += strlen(";<SID=");
3272 q = strchr(p, '>');
3273 if (!q) {
3274 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3277 *q = '\0';
3279 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3281 switch (flags) {
3283 case ADS_EXTENDED_DN_STRING:
3284 if (!string_to_sid(sid, p)) {
3285 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3287 break;
3288 case ADS_EXTENDED_DN_HEX_STRING: {
3289 fstring buf;
3290 size_t buf_len;
3292 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3293 if (buf_len == 0) {
3294 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3297 if (!sid_parse(buf, buf_len, sid)) {
3298 DEBUG(10,("failed to parse sid\n"));
3299 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3301 break;
3303 default:
3304 DEBUG(10,("unknown extended dn format\n"));
3305 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3308 return ADS_ERROR_NT(NT_STATUS_OK);
3311 /********************************************************************
3312 ********************************************************************/
3314 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3316 LDAPMessage *res = NULL;
3317 ADS_STATUS status;
3318 int count = 0;
3319 char *name = NULL;
3321 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3322 if (!ADS_ERR_OK(status)) {
3323 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3324 lp_netbios_name()));
3325 goto out;
3328 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3329 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3330 goto out;
3333 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3334 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3337 out:
3338 ads_msgfree(ads, res);
3340 return name;
3343 /********************************************************************
3344 ********************************************************************/
3346 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3348 LDAPMessage *res = NULL;
3349 ADS_STATUS status;
3350 int count = 0;
3351 char *name = NULL;
3353 status = ads_find_machine_acct(ads, &res, machine_name);
3354 if (!ADS_ERR_OK(status)) {
3355 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3356 lp_netbios_name()));
3357 goto out;
3360 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3361 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3362 goto out;
3365 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3366 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3369 out:
3370 ads_msgfree(ads, res);
3372 return name;
3375 /********************************************************************
3376 ********************************************************************/
3378 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3380 LDAPMessage *res = NULL;
3381 ADS_STATUS status;
3382 int count = 0;
3383 char *name = NULL;
3385 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3386 if (!ADS_ERR_OK(status)) {
3387 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3388 lp_netbios_name()));
3389 goto out;
3392 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3393 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3394 goto out;
3397 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3398 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3401 out:
3402 ads_msgfree(ads, res);
3404 return name;
3407 #if 0
3409 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3412 * Join a machine to a realm
3413 * Creates the machine account and sets the machine password
3414 * @param ads connection to ads server
3415 * @param machine name of host to add
3416 * @param org_unit Organizational unit to place machine in
3417 * @return status of join
3419 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3420 uint32 account_type, const char *org_unit)
3422 ADS_STATUS status;
3423 LDAPMessage *res = NULL;
3424 char *machine;
3426 /* machine name must be lowercase */
3427 machine = SMB_STRDUP(machine_name);
3428 strlower_m(machine);
3431 status = ads_find_machine_acct(ads, (void **)&res, machine);
3432 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3433 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3434 status = ads_leave_realm(ads, machine);
3435 if (!ADS_ERR_OK(status)) {
3436 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3437 machine, ads->config.realm));
3438 return status;
3442 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3443 if (!ADS_ERR_OK(status)) {
3444 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3445 SAFE_FREE(machine);
3446 return status;
3449 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3450 if (!ADS_ERR_OK(status)) {
3451 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3452 SAFE_FREE(machine);
3453 return status;
3456 SAFE_FREE(machine);
3457 ads_msgfree(ads, res);
3459 return status;
3461 #endif
3464 * Delete a machine from the realm
3465 * @param ads connection to ads server
3466 * @param hostname Machine to remove
3467 * @return status of delete
3469 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3471 ADS_STATUS status;
3472 void *msg;
3473 LDAPMessage *res;
3474 char *hostnameDN, *host;
3475 int rc;
3476 LDAPControl ldap_control;
3477 LDAPControl * pldap_control[2] = {NULL, NULL};
3479 pldap_control[0] = &ldap_control;
3480 memset(&ldap_control, 0, sizeof(LDAPControl));
3481 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3483 /* hostname must be lowercase */
3484 host = SMB_STRDUP(hostname);
3485 if (!strlower_m(host)) {
3486 SAFE_FREE(host);
3487 return ADS_ERROR_SYSTEM(EINVAL);
3490 status = ads_find_machine_acct(ads, &res, host);
3491 if (!ADS_ERR_OK(status)) {
3492 DEBUG(0, ("Host account for %s does not exist.\n", host));
3493 SAFE_FREE(host);
3494 return status;
3497 msg = ads_first_entry(ads, res);
3498 if (!msg) {
3499 SAFE_FREE(host);
3500 return ADS_ERROR_SYSTEM(ENOENT);
3503 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3504 if (hostnameDN == NULL) {
3505 SAFE_FREE(host);
3506 return ADS_ERROR_SYSTEM(ENOENT);
3509 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3510 if (rc) {
3511 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3512 }else {
3513 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3516 if (rc != LDAP_SUCCESS) {
3517 const char *attrs[] = { "cn", NULL };
3518 LDAPMessage *msg_sub;
3520 /* we only search with scope ONE, we do not expect any further
3521 * objects to be created deeper */
3523 status = ads_do_search_retry(ads, hostnameDN,
3524 LDAP_SCOPE_ONELEVEL,
3525 "(objectclass=*)", attrs, &res);
3527 if (!ADS_ERR_OK(status)) {
3528 SAFE_FREE(host);
3529 TALLOC_FREE(hostnameDN);
3530 return status;
3533 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3534 msg_sub = ads_next_entry(ads, msg_sub)) {
3536 char *dn = NULL;
3538 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3539 SAFE_FREE(host);
3540 TALLOC_FREE(hostnameDN);
3541 return ADS_ERROR(LDAP_NO_MEMORY);
3544 status = ads_del_dn(ads, dn);
3545 if (!ADS_ERR_OK(status)) {
3546 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3547 SAFE_FREE(host);
3548 TALLOC_FREE(dn);
3549 TALLOC_FREE(hostnameDN);
3550 return status;
3553 TALLOC_FREE(dn);
3556 /* there should be no subordinate objects anymore */
3557 status = ads_do_search_retry(ads, hostnameDN,
3558 LDAP_SCOPE_ONELEVEL,
3559 "(objectclass=*)", attrs, &res);
3561 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3562 SAFE_FREE(host);
3563 TALLOC_FREE(hostnameDN);
3564 return status;
3567 /* delete hostnameDN now */
3568 status = ads_del_dn(ads, hostnameDN);
3569 if (!ADS_ERR_OK(status)) {
3570 SAFE_FREE(host);
3571 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3572 TALLOC_FREE(hostnameDN);
3573 return status;
3577 TALLOC_FREE(hostnameDN);
3579 status = ads_find_machine_acct(ads, &res, host);
3580 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3581 DEBUG(3, ("Failed to remove host account.\n"));
3582 SAFE_FREE(host);
3583 return status;
3586 SAFE_FREE(host);
3587 return status;
3591 * pull all token-sids from an LDAP dn
3592 * @param ads connection to ads server
3593 * @param mem_ctx TALLOC_CTX for allocating sid array
3594 * @param dn of LDAP object
3595 * @param user_sid pointer to struct dom_sid (objectSid)
3596 * @param primary_group_sid pointer to struct dom_sid (self composed)
3597 * @param sids pointer to sid array to allocate
3598 * @param num_sids counter of SIDs pulled
3599 * @return status of token query
3601 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3602 TALLOC_CTX *mem_ctx,
3603 const char *dn,
3604 struct dom_sid *user_sid,
3605 struct dom_sid *primary_group_sid,
3606 struct dom_sid **sids,
3607 size_t *num_sids)
3609 ADS_STATUS status;
3610 LDAPMessage *res = NULL;
3611 int count = 0;
3612 size_t tmp_num_sids;
3613 struct dom_sid *tmp_sids;
3614 struct dom_sid tmp_user_sid;
3615 struct dom_sid tmp_primary_group_sid;
3616 uint32 pgid;
3617 const char *attrs[] = {
3618 "objectSid",
3619 "tokenGroups",
3620 "primaryGroupID",
3621 NULL
3624 status = ads_search_retry_dn(ads, &res, dn, attrs);
3625 if (!ADS_ERR_OK(status)) {
3626 return status;
3629 count = ads_count_replies(ads, res);
3630 if (count != 1) {
3631 ads_msgfree(ads, res);
3632 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3635 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3636 ads_msgfree(ads, res);
3637 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3640 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3641 ads_msgfree(ads, res);
3642 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3646 /* hack to compose the primary group sid without knowing the
3647 * domsid */
3649 struct dom_sid domsid;
3651 sid_copy(&domsid, &tmp_user_sid);
3653 if (!sid_split_rid(&domsid, NULL)) {
3654 ads_msgfree(ads, res);
3655 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3658 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3659 ads_msgfree(ads, res);
3660 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3664 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3666 if (tmp_num_sids == 0 || !tmp_sids) {
3667 ads_msgfree(ads, res);
3668 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3671 if (num_sids) {
3672 *num_sids = tmp_num_sids;
3675 if (sids) {
3676 *sids = tmp_sids;
3679 if (user_sid) {
3680 *user_sid = tmp_user_sid;
3683 if (primary_group_sid) {
3684 *primary_group_sid = tmp_primary_group_sid;
3687 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3689 ads_msgfree(ads, res);
3690 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3694 * Find a sAMAccoutName in LDAP
3695 * @param ads connection to ads server
3696 * @param mem_ctx TALLOC_CTX for allocating sid array
3697 * @param samaccountname to search
3698 * @param uac_ret uint32 pointer userAccountControl attribute value
3699 * @param dn_ret pointer to dn
3700 * @return status of token query
3702 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3703 TALLOC_CTX *mem_ctx,
3704 const char *samaccountname,
3705 uint32 *uac_ret,
3706 const char **dn_ret)
3708 ADS_STATUS status;
3709 const char *attrs[] = { "userAccountControl", NULL };
3710 const char *filter;
3711 LDAPMessage *res = NULL;
3712 char *dn = NULL;
3713 uint32 uac = 0;
3715 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3716 samaccountname);
3717 if (filter == NULL) {
3718 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3719 goto out;
3722 status = ads_do_search_all(ads, ads->config.bind_path,
3723 LDAP_SCOPE_SUBTREE,
3724 filter, attrs, &res);
3726 if (!ADS_ERR_OK(status)) {
3727 goto out;
3730 if (ads_count_replies(ads, res) != 1) {
3731 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3732 goto out;
3735 dn = ads_get_dn(ads, talloc_tos(), res);
3736 if (dn == NULL) {
3737 status = ADS_ERROR(LDAP_NO_MEMORY);
3738 goto out;
3741 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3742 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3743 goto out;
3746 if (uac_ret) {
3747 *uac_ret = uac;
3750 if (dn_ret) {
3751 *dn_ret = talloc_strdup(mem_ctx, dn);
3752 if (!*dn_ret) {
3753 status = ADS_ERROR(LDAP_NO_MEMORY);
3754 goto out;
3757 out:
3758 TALLOC_FREE(dn);
3759 ads_msgfree(ads, res);
3761 return status;
3765 * find our configuration path
3766 * @param ads connection to ads server
3767 * @param mem_ctx Pointer to talloc context
3768 * @param config_path Pointer to the config path
3769 * @return status of search
3771 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3772 TALLOC_CTX *mem_ctx,
3773 char **config_path)
3775 ADS_STATUS status;
3776 LDAPMessage *res = NULL;
3777 const char *config_context = NULL;
3778 const char *attrs[] = { "configurationNamingContext", NULL };
3780 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3781 "(objectclass=*)", attrs, &res);
3782 if (!ADS_ERR_OK(status)) {
3783 return status;
3786 config_context = ads_pull_string(ads, mem_ctx, res,
3787 "configurationNamingContext");
3788 ads_msgfree(ads, res);
3789 if (!config_context) {
3790 return ADS_ERROR(LDAP_NO_MEMORY);
3793 if (config_path) {
3794 *config_path = talloc_strdup(mem_ctx, config_context);
3795 if (!*config_path) {
3796 return ADS_ERROR(LDAP_NO_MEMORY);
3800 return ADS_ERROR(LDAP_SUCCESS);
3804 * find the displayName of an extended right
3805 * @param ads connection to ads server
3806 * @param config_path The config path
3807 * @param mem_ctx Pointer to talloc context
3808 * @param GUID struct of the rightsGUID
3809 * @return status of search
3811 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3812 const char *config_path,
3813 TALLOC_CTX *mem_ctx,
3814 const struct GUID *rights_guid)
3816 ADS_STATUS rc;
3817 LDAPMessage *res = NULL;
3818 char *expr = NULL;
3819 const char *attrs[] = { "displayName", NULL };
3820 const char *result = NULL;
3821 const char *path;
3823 if (!ads || !mem_ctx || !rights_guid) {
3824 goto done;
3827 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3828 GUID_string(mem_ctx, rights_guid));
3829 if (!expr) {
3830 goto done;
3833 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3834 if (!path) {
3835 goto done;
3838 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3839 expr, attrs, &res);
3840 if (!ADS_ERR_OK(rc)) {
3841 goto done;
3844 if (ads_count_replies(ads, res) != 1) {
3845 goto done;
3848 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3850 done:
3851 ads_msgfree(ads, res);
3852 return result;
3856 * verify or build and verify an account ou
3857 * @param mem_ctx Pointer to talloc context
3858 * @param ads connection to ads server
3859 * @param account_ou
3860 * @return status of search
3863 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3864 ADS_STRUCT *ads,
3865 const char **account_ou)
3867 char **exploded_dn;
3868 const char *name;
3869 char *ou_string;
3871 exploded_dn = ldap_explode_dn(*account_ou, 0);
3872 if (exploded_dn) {
3873 ldap_value_free(exploded_dn);
3874 return ADS_SUCCESS;
3877 ou_string = ads_ou_string(ads, *account_ou);
3878 if (!ou_string) {
3879 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3882 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3883 ads->config.bind_path);
3884 SAFE_FREE(ou_string);
3886 if (!name) {
3887 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3890 exploded_dn = ldap_explode_dn(name, 0);
3891 if (!exploded_dn) {
3892 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3894 ldap_value_free(exploded_dn);
3896 *account_ou = name;
3897 return ADS_SUCCESS;
3900 #endif