WHATSNEW: Add release notes for Samba 4.0.18.
[Samba.git] / source3 / libads / ldap.c
blob20c2e319f5954c7acc1324efdad36106ff1c1963
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 goto done;
1047 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1048 NULL, &rcontrols, 0);
1050 if (!rcontrols) {
1051 goto done;
1054 for (i=0; rcontrols[i]; i++) {
1055 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1056 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1057 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1058 &cookie_bv);
1059 /* the berval is the cookie, but must be freed when
1060 it is all done */
1061 if (cookie_bv->bv_len) /* still more to do */
1062 *cookie=ber_bvdup(cookie_bv);
1063 else
1064 *cookie=NULL;
1065 ber_bvfree(cookie_bv);
1066 ber_free(cookie_be, 1);
1067 break;
1070 ldap_controls_free(rcontrols);
1072 done:
1073 talloc_destroy(ctx);
1075 if (ext_be) {
1076 ber_free(ext_be, 1);
1079 if (ext_bv) {
1080 ber_bvfree(ext_bv);
1083 /* if/when we decide to utf8-encode attrs, take out this next line */
1084 TALLOC_FREE(search_attrs);
1086 return ADS_ERROR(rc);
1089 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1090 int scope, const char *expr,
1091 const char **attrs, LDAPMessage **res,
1092 int *count, struct berval **cookie)
1094 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1099 * Get all results for a search. This uses ads_do_paged_search() to return
1100 * all entries in a large search.
1101 * @param ads connection to ads server
1102 * @param bind_path Base dn for the search
1103 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1104 * @param expr Search expression
1105 * @param attrs Attributes to retrieve
1106 * @param res ** which will contain results - free res* with ads_msgfree()
1107 * @return status of search
1109 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1110 int scope, const char *expr,
1111 const char **attrs, void *args,
1112 LDAPMessage **res)
1114 struct berval *cookie = NULL;
1115 int count = 0;
1116 ADS_STATUS status;
1118 *res = NULL;
1119 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1120 &count, &cookie);
1122 if (!ADS_ERR_OK(status))
1123 return status;
1125 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1126 while (cookie) {
1127 LDAPMessage *res2 = NULL;
1128 LDAPMessage *msg, *next;
1130 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1131 attrs, args, &res2, &count, &cookie);
1132 if (!ADS_ERR_OK(status)) {
1133 /* Ensure we free all collected results */
1134 ads_msgfree(ads, *res);
1135 *res = NULL;
1136 break;
1139 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1140 that this works on all ldap libs, but I have only tested with openldap */
1141 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1142 next = ads_next_message(ads, msg);
1143 ldap_add_result_entry((LDAPMessage **)res, msg);
1145 /* note that we do not free res2, as the memory is now
1146 part of the main returned list */
1148 #else
1149 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1150 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1151 #endif
1153 return status;
1156 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1157 int scope, const char *expr,
1158 const char **attrs, LDAPMessage **res)
1160 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1163 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1164 int scope, const char *expr,
1165 const char **attrs, uint32 sd_flags,
1166 LDAPMessage **res)
1168 ads_control args;
1170 args.control = ADS_SD_FLAGS_OID;
1171 args.val = sd_flags;
1172 args.critical = True;
1174 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1179 * Run a function on all results for a search. Uses ads_do_paged_search() and
1180 * runs the function as each page is returned, using ads_process_results()
1181 * @param ads connection to ads server
1182 * @param bind_path Base dn for the search
1183 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1184 * @param expr Search expression - specified in local charset
1185 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1186 * @param fn Function which takes attr name, values list, and data_area
1187 * @param data_area Pointer which is passed to function on each call
1188 * @return status of search
1190 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1191 int scope, const char *expr, const char **attrs,
1192 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1193 void *data_area)
1195 struct berval *cookie = NULL;
1196 int count = 0;
1197 ADS_STATUS status;
1198 LDAPMessage *res;
1200 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1201 &count, &cookie);
1203 if (!ADS_ERR_OK(status)) return status;
1205 ads_process_results(ads, res, fn, data_area);
1206 ads_msgfree(ads, res);
1208 while (cookie) {
1209 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1210 &res, &count, &cookie);
1212 if (!ADS_ERR_OK(status)) break;
1214 ads_process_results(ads, res, fn, data_area);
1215 ads_msgfree(ads, res);
1218 return status;
1222 * Do a search with a timeout.
1223 * @param ads connection to ads server
1224 * @param bind_path Base dn for the search
1225 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1226 * @param expr Search expression
1227 * @param attrs Attributes to retrieve
1228 * @param res ** which will contain results - free res* with ads_msgfree()
1229 * @return status of search
1231 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1232 const char *expr,
1233 const char **attrs, LDAPMessage **res)
1235 int rc;
1236 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1237 size_t converted_size;
1238 TALLOC_CTX *ctx;
1240 *res = NULL;
1241 if (!(ctx = talloc_init("ads_do_search"))) {
1242 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1243 return ADS_ERROR(LDAP_NO_MEMORY);
1246 /* 0 means the conversion worked but the result was empty
1247 so we only fail if it's negative. In any case, it always
1248 at least nulls out the dest */
1249 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1250 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1252 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1253 rc = LDAP_NO_MEMORY;
1254 goto done;
1257 if (!attrs || !(*attrs))
1258 search_attrs = NULL;
1259 else {
1260 /* This would be the utf8-encoded version...*/
1261 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1262 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1264 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1265 rc = LDAP_NO_MEMORY;
1266 goto done;
1270 /* see the note in ads_do_paged_search - we *must* disable referrals */
1271 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1273 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1274 search_attrs, 0, NULL, NULL,
1275 LDAP_NO_LIMIT,
1276 (LDAPMessage **)res);
1278 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1279 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1280 rc = 0;
1283 done:
1284 talloc_destroy(ctx);
1285 /* if/when we decide to utf8-encode attrs, take out this next line */
1286 TALLOC_FREE(search_attrs);
1287 return ADS_ERROR(rc);
1290 * Do a general ADS search
1291 * @param ads connection to ads server
1292 * @param res ** which will contain results - free res* with ads_msgfree()
1293 * @param expr Search expression
1294 * @param attrs Attributes to retrieve
1295 * @return status of search
1297 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1298 const char *expr, const char **attrs)
1300 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1301 expr, attrs, res);
1305 * Do a search on a specific DistinguishedName
1306 * @param ads connection to ads server
1307 * @param res ** which will contain results - free res* with ads_msgfree()
1308 * @param dn DistinguishName to search
1309 * @param attrs Attributes to retrieve
1310 * @return status of search
1312 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1313 const char *dn, const char **attrs)
1315 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1316 attrs, res);
1320 * Free up memory from a ads_search
1321 * @param ads connection to ads server
1322 * @param msg Search results to free
1324 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1326 if (!msg) return;
1327 ldap_msgfree(msg);
1331 * Get a dn from search results
1332 * @param ads connection to ads server
1333 * @param msg Search result
1334 * @return dn string
1336 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1338 char *utf8_dn, *unix_dn;
1339 size_t converted_size;
1341 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1343 if (!utf8_dn) {
1344 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1345 return NULL;
1348 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1349 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1350 utf8_dn ));
1351 return NULL;
1353 ldap_memfree(utf8_dn);
1354 return unix_dn;
1358 * Get the parent from a dn
1359 * @param dn the dn to return the parent from
1360 * @return parent dn string
1362 char *ads_parent_dn(const char *dn)
1364 char *p;
1366 if (dn == NULL) {
1367 return NULL;
1370 p = strchr(dn, ',');
1372 if (p == NULL) {
1373 return NULL;
1376 return p+1;
1380 * Find a machine account given a hostname
1381 * @param ads connection to ads server
1382 * @param res ** which will contain results - free res* with ads_msgfree()
1383 * @param host Hostname to search for
1384 * @return status of search
1386 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1387 const char *machine)
1389 ADS_STATUS status;
1390 char *expr;
1391 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1393 *res = NULL;
1395 /* the easiest way to find a machine account anywhere in the tree
1396 is to look for hostname$ */
1397 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1398 DEBUG(1, ("asprintf failed!\n"));
1399 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1402 status = ads_search(ads, res, expr, attrs);
1403 SAFE_FREE(expr);
1404 return status;
1408 * Initialize a list of mods to be used in a modify request
1409 * @param ctx An initialized TALLOC_CTX
1410 * @return allocated ADS_MODLIST
1412 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1414 #define ADS_MODLIST_ALLOC_SIZE 10
1415 LDAPMod **mods;
1417 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1418 /* -1 is safety to make sure we don't go over the end.
1419 need to reset it to NULL before doing ldap modify */
1420 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1422 return (ADS_MODLIST)mods;
1427 add an attribute to the list, with values list already constructed
1429 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1430 int mod_op, const char *name,
1431 const void *_invals)
1433 const void **invals = (const void **)_invals;
1434 int curmod;
1435 LDAPMod **modlist = (LDAPMod **) *mods;
1436 struct berval **ber_values = NULL;
1437 char **char_values = NULL;
1439 if (!invals) {
1440 mod_op = LDAP_MOD_DELETE;
1441 } else {
1442 if (mod_op & LDAP_MOD_BVALUES)
1443 ber_values = ads_dup_values(ctx,
1444 (const struct berval **)invals);
1445 else
1446 char_values = ads_push_strvals(ctx,
1447 (const char **) invals);
1450 /* find the first empty slot */
1451 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1452 curmod++);
1453 if (modlist[curmod] == (LDAPMod *) -1) {
1454 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1455 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1456 return ADS_ERROR(LDAP_NO_MEMORY);
1457 memset(&modlist[curmod], 0,
1458 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1459 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1460 *mods = (ADS_MODLIST)modlist;
1463 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1464 return ADS_ERROR(LDAP_NO_MEMORY);
1465 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1466 if (mod_op & LDAP_MOD_BVALUES) {
1467 modlist[curmod]->mod_bvalues = ber_values;
1468 } else if (mod_op & LDAP_MOD_DELETE) {
1469 modlist[curmod]->mod_values = NULL;
1470 } else {
1471 modlist[curmod]->mod_values = char_values;
1474 modlist[curmod]->mod_op = mod_op;
1475 return ADS_ERROR(LDAP_SUCCESS);
1479 * Add a single string value to a mod list
1480 * @param ctx An initialized TALLOC_CTX
1481 * @param mods An initialized ADS_MODLIST
1482 * @param name The attribute name to add
1483 * @param val The value to add - NULL means DELETE
1484 * @return ADS STATUS indicating success of add
1486 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1487 const char *name, const char *val)
1489 const char *values[2];
1491 values[0] = val;
1492 values[1] = NULL;
1494 if (!val)
1495 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1496 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1500 * Add an array of string values to a mod list
1501 * @param ctx An initialized TALLOC_CTX
1502 * @param mods An initialized ADS_MODLIST
1503 * @param name The attribute name to add
1504 * @param vals The array of string values to add - NULL means DELETE
1505 * @return ADS STATUS indicating success of add
1507 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1508 const char *name, const char **vals)
1510 if (!vals)
1511 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1512 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1513 name, (const void **) vals);
1516 #if 0
1518 * Add a single ber-encoded value to a mod list
1519 * @param ctx An initialized TALLOC_CTX
1520 * @param mods An initialized ADS_MODLIST
1521 * @param name The attribute name to add
1522 * @param val The value to add - NULL means DELETE
1523 * @return ADS STATUS indicating success of add
1525 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1526 const char *name, const struct berval *val)
1528 const struct berval *values[2];
1530 values[0] = val;
1531 values[1] = NULL;
1532 if (!val)
1533 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1534 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1535 name, (const void **) values);
1537 #endif
1540 * Perform an ldap modify
1541 * @param ads connection to ads server
1542 * @param mod_dn DistinguishedName to modify
1543 * @param mods list of modifications to perform
1544 * @return status of modify
1546 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1548 int ret,i;
1549 char *utf8_dn = NULL;
1550 size_t converted_size;
1552 this control is needed to modify that contains a currently
1553 non-existent attribute (but allowable for the object) to run
1555 LDAPControl PermitModify = {
1556 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1557 {0, NULL},
1558 (char) 1};
1559 LDAPControl *controls[2];
1561 controls[0] = &PermitModify;
1562 controls[1] = NULL;
1564 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1565 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1568 /* find the end of the list, marked by NULL or -1 */
1569 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1570 /* make sure the end of the list is NULL */
1571 mods[i] = NULL;
1572 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1573 (LDAPMod **) mods, controls, NULL);
1574 TALLOC_FREE(utf8_dn);
1575 return ADS_ERROR(ret);
1579 * Perform an ldap add
1580 * @param ads connection to ads server
1581 * @param new_dn DistinguishedName to add
1582 * @param mods list of attributes and values for DN
1583 * @return status of add
1585 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1587 int ret, i;
1588 char *utf8_dn = NULL;
1589 size_t converted_size;
1591 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1592 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1593 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1596 /* find the end of the list, marked by NULL or -1 */
1597 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1598 /* make sure the end of the list is NULL */
1599 mods[i] = NULL;
1601 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1602 TALLOC_FREE(utf8_dn);
1603 return ADS_ERROR(ret);
1607 * Delete a DistinguishedName
1608 * @param ads connection to ads server
1609 * @param new_dn DistinguishedName to delete
1610 * @return status of delete
1612 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1614 int ret;
1615 char *utf8_dn = NULL;
1616 size_t converted_size;
1617 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1618 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1619 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1622 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1623 TALLOC_FREE(utf8_dn);
1624 return ADS_ERROR(ret);
1628 * Build an org unit string
1629 * if org unit is Computers or blank then assume a container, otherwise
1630 * assume a / separated list of organisational units.
1631 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1632 * @param ads connection to ads server
1633 * @param org_unit Organizational unit
1634 * @return org unit string - caller must free
1636 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1638 char *ret = NULL;
1640 if (!org_unit || !*org_unit) {
1642 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1644 /* samba4 might not yet respond to a wellknownobject-query */
1645 return ret ? ret : SMB_STRDUP("cn=Computers");
1648 if (strequal(org_unit, "Computers")) {
1649 return SMB_STRDUP("cn=Computers");
1652 /* jmcd: removed "\\" from the separation chars, because it is
1653 needed as an escape for chars like '#' which are valid in an
1654 OU name */
1655 return ads_build_path(org_unit, "/", "ou=", 1);
1659 * Get a org unit string for a well-known GUID
1660 * @param ads connection to ads server
1661 * @param wknguid Well known GUID
1662 * @return org unit string - caller must free
1664 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1666 ADS_STATUS status;
1667 LDAPMessage *res = NULL;
1668 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1669 **bind_dn_exp = NULL;
1670 const char *attrs[] = {"distinguishedName", NULL};
1671 int new_ln, wkn_ln, bind_ln, i;
1673 if (wknguid == NULL) {
1674 return NULL;
1677 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1678 DEBUG(1, ("asprintf failed!\n"));
1679 return NULL;
1682 status = ads_search_dn(ads, &res, base, attrs);
1683 if (!ADS_ERR_OK(status)) {
1684 DEBUG(1,("Failed while searching for: %s\n", base));
1685 goto out;
1688 if (ads_count_replies(ads, res) != 1) {
1689 goto out;
1692 /* substitute the bind-path from the well-known-guid-search result */
1693 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1694 if (!wkn_dn) {
1695 goto out;
1698 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1699 if (!wkn_dn_exp) {
1700 goto out;
1703 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1704 if (!bind_dn_exp) {
1705 goto out;
1708 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1710 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1713 new_ln = wkn_ln - bind_ln;
1715 ret = SMB_STRDUP(wkn_dn_exp[0]);
1716 if (!ret) {
1717 goto out;
1720 for (i=1; i < new_ln; i++) {
1721 char *s = NULL;
1723 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1724 SAFE_FREE(ret);
1725 goto out;
1728 SAFE_FREE(ret);
1729 ret = SMB_STRDUP(s);
1730 free(s);
1731 if (!ret) {
1732 goto out;
1736 out:
1737 SAFE_FREE(base);
1738 ads_msgfree(ads, res);
1739 TALLOC_FREE(wkn_dn);
1740 if (wkn_dn_exp) {
1741 ldap_value_free(wkn_dn_exp);
1743 if (bind_dn_exp) {
1744 ldap_value_free(bind_dn_exp);
1747 return ret;
1751 * Adds (appends) an item to an attribute array, rather then
1752 * replacing the whole list
1753 * @param ctx An initialized TALLOC_CTX
1754 * @param mods An initialized ADS_MODLIST
1755 * @param name name of the ldap attribute to append to
1756 * @param vals an array of values to add
1757 * @return status of addition
1760 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1761 const char *name, const char **vals)
1763 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1764 (const void *) vals);
1768 * Determines the an account's current KVNO via an LDAP lookup
1769 * @param ads An initialized ADS_STRUCT
1770 * @param account_name the NT samaccountname.
1771 * @return the kvno for the account, or -1 in case of a failure.
1774 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1776 LDAPMessage *res = NULL;
1777 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1778 char *filter;
1779 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1780 char *dn_string = NULL;
1781 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1783 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1784 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1785 return kvno;
1787 ret = ads_search(ads, &res, filter, attrs);
1788 SAFE_FREE(filter);
1789 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1790 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1791 ads_msgfree(ads, res);
1792 return kvno;
1795 dn_string = ads_get_dn(ads, talloc_tos(), res);
1796 if (!dn_string) {
1797 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1798 ads_msgfree(ads, res);
1799 return kvno;
1801 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1802 TALLOC_FREE(dn_string);
1804 /* ---------------------------------------------------------
1805 * 0 is returned as a default KVNO from this point on...
1806 * This is done because Windows 2000 does not support key
1807 * version numbers. Chances are that a failure in the next
1808 * step is simply due to Windows 2000 being used for a
1809 * domain controller. */
1810 kvno = 0;
1812 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1813 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1814 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1815 ads_msgfree(ads, res);
1816 return kvno;
1819 /* Success */
1820 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1821 ads_msgfree(ads, res);
1822 return kvno;
1826 * Determines the computer account's current KVNO via an LDAP lookup
1827 * @param ads An initialized ADS_STRUCT
1828 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1829 * @return the kvno for the computer account, or -1 in case of a failure.
1832 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1834 char *computer_account = NULL;
1835 uint32_t kvno = -1;
1837 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1838 return kvno;
1841 kvno = ads_get_kvno(ads, computer_account);
1842 free(computer_account);
1844 return kvno;
1848 * This clears out all registered spn's for a given hostname
1849 * @param ads An initilaized ADS_STRUCT
1850 * @param machine_name the NetBIOS name of the computer.
1851 * @return 0 upon success, non-zero otherwise.
1854 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1856 TALLOC_CTX *ctx;
1857 LDAPMessage *res = NULL;
1858 ADS_MODLIST mods;
1859 const char *servicePrincipalName[1] = {NULL};
1860 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1861 char *dn_string = NULL;
1863 ret = ads_find_machine_acct(ads, &res, machine_name);
1864 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1865 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1866 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1867 ads_msgfree(ads, res);
1868 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1871 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1872 ctx = talloc_init("ads_clear_service_principal_names");
1873 if (!ctx) {
1874 ads_msgfree(ads, res);
1875 return ADS_ERROR(LDAP_NO_MEMORY);
1878 if (!(mods = ads_init_mods(ctx))) {
1879 talloc_destroy(ctx);
1880 ads_msgfree(ads, res);
1881 return ADS_ERROR(LDAP_NO_MEMORY);
1883 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1884 if (!ADS_ERR_OK(ret)) {
1885 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1886 ads_msgfree(ads, res);
1887 talloc_destroy(ctx);
1888 return ret;
1890 dn_string = ads_get_dn(ads, talloc_tos(), res);
1891 if (!dn_string) {
1892 talloc_destroy(ctx);
1893 ads_msgfree(ads, res);
1894 return ADS_ERROR(LDAP_NO_MEMORY);
1896 ret = ads_gen_mod(ads, dn_string, mods);
1897 TALLOC_FREE(dn_string);
1898 if (!ADS_ERR_OK(ret)) {
1899 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1900 machine_name));
1901 ads_msgfree(ads, res);
1902 talloc_destroy(ctx);
1903 return ret;
1906 ads_msgfree(ads, res);
1907 talloc_destroy(ctx);
1908 return ret;
1912 * This adds a service principal name to an existing computer account
1913 * (found by hostname) in AD.
1914 * @param ads An initialized ADS_STRUCT
1915 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1916 * @param my_fqdn The fully qualified DNS name of the machine
1917 * @param spn A string of the service principal to add, i.e. 'host'
1918 * @return 0 upon sucess, or non-zero if a failure occurs
1921 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1922 const char *my_fqdn, const char *spn)
1924 ADS_STATUS ret;
1925 TALLOC_CTX *ctx;
1926 LDAPMessage *res = NULL;
1927 char *psp1, *psp2;
1928 ADS_MODLIST mods;
1929 char *dn_string = NULL;
1930 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1932 ret = ads_find_machine_acct(ads, &res, machine_name);
1933 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1934 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1935 machine_name));
1936 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1937 spn, machine_name, ads->config.realm));
1938 ads_msgfree(ads, res);
1939 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1942 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1943 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1944 ads_msgfree(ads, res);
1945 return ADS_ERROR(LDAP_NO_MEMORY);
1948 /* add short name spn */
1950 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1951 talloc_destroy(ctx);
1952 ads_msgfree(ads, res);
1953 return ADS_ERROR(LDAP_NO_MEMORY);
1955 if (!strlower_m(&psp1[strlen(spn) + 1])) {
1956 ret = ADS_ERROR(LDAP_NO_MEMORY);
1957 goto out;
1959 servicePrincipalName[0] = psp1;
1961 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1962 psp1, machine_name));
1965 /* add fully qualified spn */
1967 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1968 ret = ADS_ERROR(LDAP_NO_MEMORY);
1969 goto out;
1971 if (!strlower_m(&psp2[strlen(spn) + 1])) {
1972 ret = ADS_ERROR(LDAP_NO_MEMORY);
1973 goto out;
1975 servicePrincipalName[1] = psp2;
1977 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1978 psp2, machine_name));
1980 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1981 ret = ADS_ERROR(LDAP_NO_MEMORY);
1982 goto out;
1985 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1986 if (!ADS_ERR_OK(ret)) {
1987 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1988 goto out;
1991 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1992 ret = ADS_ERROR(LDAP_NO_MEMORY);
1993 goto out;
1996 ret = ads_gen_mod(ads, dn_string, mods);
1997 if (!ADS_ERR_OK(ret)) {
1998 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1999 goto out;
2002 out:
2003 TALLOC_FREE( ctx );
2004 ads_msgfree(ads, res);
2005 return ret;
2009 * adds a machine account to the ADS server
2010 * @param ads An intialized ADS_STRUCT
2011 * @param machine_name - the NetBIOS machine name of this account.
2012 * @param account_type A number indicating the type of account to create
2013 * @param org_unit The LDAP path in which to place this account
2014 * @return 0 upon success, or non-zero otherwise
2017 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2018 const char *org_unit)
2020 ADS_STATUS ret;
2021 char *samAccountName, *controlstr;
2022 TALLOC_CTX *ctx;
2023 ADS_MODLIST mods;
2024 char *machine_escaped = NULL;
2025 char *new_dn;
2026 const char *objectClass[] = {"top", "person", "organizationalPerson",
2027 "user", "computer", NULL};
2028 LDAPMessage *res = NULL;
2029 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2030 UF_DONT_EXPIRE_PASSWD |\
2031 UF_ACCOUNTDISABLE );
2033 if (!(ctx = talloc_init("ads_add_machine_acct")))
2034 return ADS_ERROR(LDAP_NO_MEMORY);
2036 ret = ADS_ERROR(LDAP_NO_MEMORY);
2038 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2039 if (!machine_escaped) {
2040 goto done;
2043 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2044 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2046 if ( !new_dn || !samAccountName ) {
2047 goto done;
2050 #ifndef ENCTYPE_ARCFOUR_HMAC
2051 acct_control |= UF_USE_DES_KEY_ONLY;
2052 #endif
2054 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2055 goto done;
2058 if (!(mods = ads_init_mods(ctx))) {
2059 goto done;
2062 ads_mod_str(ctx, &mods, "cn", machine_name);
2063 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2064 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2065 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2067 ret = ads_gen_add(ads, new_dn, mods);
2069 done:
2070 SAFE_FREE(machine_escaped);
2071 ads_msgfree(ads, res);
2072 talloc_destroy(ctx);
2074 return ret;
2078 * move a machine account to another OU on the ADS server
2079 * @param ads - An intialized ADS_STRUCT
2080 * @param machine_name - the NetBIOS machine name of this account.
2081 * @param org_unit - The LDAP path in which to place this account
2082 * @param moved - whether we moved the machine account (optional)
2083 * @return 0 upon success, or non-zero otherwise
2086 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2087 const char *org_unit, bool *moved)
2089 ADS_STATUS rc;
2090 int ldap_status;
2091 LDAPMessage *res = NULL;
2092 char *filter = NULL;
2093 char *computer_dn = NULL;
2094 char *parent_dn;
2095 char *computer_rdn = NULL;
2096 bool need_move = False;
2098 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2099 rc = ADS_ERROR(LDAP_NO_MEMORY);
2100 goto done;
2103 /* Find pre-existing machine */
2104 rc = ads_search(ads, &res, filter, NULL);
2105 if (!ADS_ERR_OK(rc)) {
2106 goto done;
2109 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2110 if (!computer_dn) {
2111 rc = ADS_ERROR(LDAP_NO_MEMORY);
2112 goto done;
2115 parent_dn = ads_parent_dn(computer_dn);
2116 if (strequal(parent_dn, org_unit)) {
2117 goto done;
2120 need_move = True;
2122 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2123 rc = ADS_ERROR(LDAP_NO_MEMORY);
2124 goto done;
2127 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2128 org_unit, 1, NULL, NULL);
2129 rc = ADS_ERROR(ldap_status);
2131 done:
2132 ads_msgfree(ads, res);
2133 SAFE_FREE(filter);
2134 TALLOC_FREE(computer_dn);
2135 SAFE_FREE(computer_rdn);
2137 if (!ADS_ERR_OK(rc)) {
2138 need_move = False;
2141 if (moved) {
2142 *moved = need_move;
2145 return rc;
2149 dump a binary result from ldap
2151 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2153 int i, j;
2154 for (i=0; values[i]; i++) {
2155 printf("%s: ", field);
2156 for (j=0; j<values[i]->bv_len; j++) {
2157 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2159 printf("\n");
2163 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2165 int i;
2166 for (i=0; values[i]; i++) {
2167 NTSTATUS status;
2168 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2169 struct GUID guid;
2171 status = GUID_from_ndr_blob(&in, &guid);
2172 if (NT_STATUS_IS_OK(status)) {
2173 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2174 } else {
2175 printf("%s: INVALID GUID\n", field);
2181 dump a sid result from ldap
2183 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2185 int i;
2186 for (i=0; values[i]; i++) {
2187 struct dom_sid sid;
2188 fstring tmp;
2189 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2190 return;
2192 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2197 dump ntSecurityDescriptor
2199 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2201 TALLOC_CTX *frame = talloc_stackframe();
2202 struct security_descriptor *psd;
2203 NTSTATUS status;
2205 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2206 values[0]->bv_len, &psd);
2207 if (!NT_STATUS_IS_OK(status)) {
2208 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2209 nt_errstr(status)));
2210 TALLOC_FREE(frame);
2211 return;
2214 if (psd) {
2215 ads_disp_sd(ads, talloc_tos(), psd);
2218 TALLOC_FREE(frame);
2222 dump a string result from ldap
2224 static void dump_string(const char *field, char **values)
2226 int i;
2227 for (i=0; values[i]; i++) {
2228 printf("%s: %s\n", field, values[i]);
2233 dump a field from LDAP on stdout
2234 used for debugging
2237 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2239 const struct {
2240 const char *name;
2241 bool string;
2242 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2243 } handlers[] = {
2244 {"objectGUID", False, dump_guid},
2245 {"netbootGUID", False, dump_guid},
2246 {"nTSecurityDescriptor", False, dump_sd},
2247 {"dnsRecord", False, dump_binary},
2248 {"objectSid", False, dump_sid},
2249 {"tokenGroups", False, dump_sid},
2250 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2251 {"tokengroupsGlobalandUniversal", False, dump_sid},
2252 {"mS-DS-CreatorSID", False, dump_sid},
2253 {"msExchMailboxGuid", False, dump_guid},
2254 {NULL, True, NULL}
2256 int i;
2258 if (!field) { /* must be end of an entry */
2259 printf("\n");
2260 return False;
2263 for (i=0; handlers[i].name; i++) {
2264 if (strcasecmp_m(handlers[i].name, field) == 0) {
2265 if (!values) /* first time, indicate string or not */
2266 return handlers[i].string;
2267 handlers[i].handler(ads, field, (struct berval **) values);
2268 break;
2271 if (!handlers[i].name) {
2272 if (!values) /* first time, indicate string conversion */
2273 return True;
2274 dump_string(field, (char **)values);
2276 return False;
2280 * Dump a result from LDAP on stdout
2281 * used for debugging
2282 * @param ads connection to ads server
2283 * @param res Results to dump
2286 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2288 ads_process_results(ads, res, ads_dump_field, NULL);
2292 * Walk through results, calling a function for each entry found.
2293 * The function receives a field name, a berval * array of values,
2294 * and a data area passed through from the start. The function is
2295 * called once with null for field and values at the end of each
2296 * entry.
2297 * @param ads connection to ads server
2298 * @param res Results to process
2299 * @param fn Function for processing each result
2300 * @param data_area user-defined area to pass to function
2302 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2303 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2304 void *data_area)
2306 LDAPMessage *msg;
2307 TALLOC_CTX *ctx;
2308 size_t converted_size;
2310 if (!(ctx = talloc_init("ads_process_results")))
2311 return;
2313 for (msg = ads_first_entry(ads, res); msg;
2314 msg = ads_next_entry(ads, msg)) {
2315 char *utf8_field;
2316 BerElement *b;
2318 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2319 (LDAPMessage *)msg,&b);
2320 utf8_field;
2321 utf8_field=ldap_next_attribute(ads->ldap.ld,
2322 (LDAPMessage *)msg,b)) {
2323 struct berval **ber_vals;
2324 char **str_vals, **utf8_vals;
2325 char *field;
2326 bool string;
2328 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2329 &converted_size))
2331 DEBUG(0,("ads_process_results: "
2332 "pull_utf8_talloc failed: %s",
2333 strerror(errno)));
2336 string = fn(ads, field, NULL, data_area);
2338 if (string) {
2339 utf8_vals = ldap_get_values(ads->ldap.ld,
2340 (LDAPMessage *)msg, field);
2341 str_vals = ads_pull_strvals(ctx,
2342 (const char **) utf8_vals);
2343 fn(ads, field, (void **) str_vals, data_area);
2344 ldap_value_free(utf8_vals);
2345 } else {
2346 ber_vals = ldap_get_values_len(ads->ldap.ld,
2347 (LDAPMessage *)msg, field);
2348 fn(ads, field, (void **) ber_vals, data_area);
2350 ldap_value_free_len(ber_vals);
2352 ldap_memfree(utf8_field);
2354 ber_free(b, 0);
2355 talloc_free_children(ctx);
2356 fn(ads, NULL, NULL, data_area); /* completed an entry */
2359 talloc_destroy(ctx);
2363 * count how many replies are in a LDAPMessage
2364 * @param ads connection to ads server
2365 * @param res Results to count
2366 * @return number of replies
2368 int ads_count_replies(ADS_STRUCT *ads, void *res)
2370 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2374 * pull the first entry from a ADS result
2375 * @param ads connection to ads server
2376 * @param res Results of search
2377 * @return first entry from result
2379 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2381 return ldap_first_entry(ads->ldap.ld, res);
2385 * pull the next entry from a ADS result
2386 * @param ads connection to ads server
2387 * @param res Results of search
2388 * @return next entry from result
2390 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2392 return ldap_next_entry(ads->ldap.ld, res);
2396 * pull the first message from a ADS result
2397 * @param ads connection to ads server
2398 * @param res Results of search
2399 * @return first message from result
2401 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2403 return ldap_first_message(ads->ldap.ld, res);
2407 * pull the next message from a ADS result
2408 * @param ads connection to ads server
2409 * @param res Results of search
2410 * @return next message from result
2412 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2414 return ldap_next_message(ads->ldap.ld, res);
2418 * pull a single string from a ADS result
2419 * @param ads connection to ads server
2420 * @param mem_ctx TALLOC_CTX to use for allocating result string
2421 * @param msg Results of search
2422 * @param field Attribute to retrieve
2423 * @return Result string in talloc context
2425 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2426 const char *field)
2428 char **values;
2429 char *ret = NULL;
2430 char *ux_string;
2431 size_t converted_size;
2433 values = ldap_get_values(ads->ldap.ld, msg, field);
2434 if (!values)
2435 return NULL;
2437 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2438 &converted_size))
2440 ret = ux_string;
2442 ldap_value_free(values);
2443 return ret;
2447 * pull an array of strings from a ADS result
2448 * @param ads connection to ads server
2449 * @param mem_ctx TALLOC_CTX to use for allocating result string
2450 * @param msg Results of search
2451 * @param field Attribute to retrieve
2452 * @return Result strings in talloc context
2454 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2455 LDAPMessage *msg, const char *field,
2456 size_t *num_values)
2458 char **values;
2459 char **ret = NULL;
2460 int i;
2461 size_t converted_size;
2463 values = ldap_get_values(ads->ldap.ld, msg, field);
2464 if (!values)
2465 return NULL;
2467 *num_values = ldap_count_values(values);
2469 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2470 if (!ret) {
2471 ldap_value_free(values);
2472 return NULL;
2475 for (i=0;i<*num_values;i++) {
2476 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2477 &converted_size))
2479 ldap_value_free(values);
2480 return NULL;
2483 ret[i] = NULL;
2485 ldap_value_free(values);
2486 return ret;
2490 * pull an array of strings from a ADS result
2491 * (handle large multivalue attributes with range retrieval)
2492 * @param ads connection to ads server
2493 * @param mem_ctx TALLOC_CTX to use for allocating result string
2494 * @param msg Results of search
2495 * @param field Attribute to retrieve
2496 * @param current_strings strings returned by a previous call to this function
2497 * @param next_attribute The next query should ask for this attribute
2498 * @param num_values How many values did we get this time?
2499 * @param more_values Are there more values to get?
2500 * @return Result strings in talloc context
2502 char **ads_pull_strings_range(ADS_STRUCT *ads,
2503 TALLOC_CTX *mem_ctx,
2504 LDAPMessage *msg, const char *field,
2505 char **current_strings,
2506 const char **next_attribute,
2507 size_t *num_strings,
2508 bool *more_strings)
2510 char *attr;
2511 char *expected_range_attrib, *range_attr;
2512 BerElement *ptr = NULL;
2513 char **strings;
2514 char **new_strings;
2515 size_t num_new_strings;
2516 unsigned long int range_start;
2517 unsigned long int range_end;
2519 /* we might have been given the whole lot anyway */
2520 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2521 *more_strings = False;
2522 return strings;
2525 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2527 /* look for Range result */
2528 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2529 attr;
2530 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2531 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2532 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2533 range_attr = attr;
2534 break;
2536 ldap_memfree(attr);
2538 if (!attr) {
2539 ber_free(ptr, 0);
2540 /* nothing here - this field is just empty */
2541 *more_strings = False;
2542 return NULL;
2545 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2546 &range_start, &range_end) == 2) {
2547 *more_strings = True;
2548 } else {
2549 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2550 &range_start) == 1) {
2551 *more_strings = False;
2552 } else {
2553 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2554 range_attr));
2555 ldap_memfree(range_attr);
2556 *more_strings = False;
2557 return NULL;
2561 if ((*num_strings) != range_start) {
2562 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2563 " - aborting range retreival\n",
2564 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2565 ldap_memfree(range_attr);
2566 *more_strings = False;
2567 return NULL;
2570 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2572 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2573 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2574 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2575 range_attr, (unsigned long int)range_end - range_start + 1,
2576 (unsigned long int)num_new_strings));
2577 ldap_memfree(range_attr);
2578 *more_strings = False;
2579 return NULL;
2582 strings = talloc_realloc(mem_ctx, current_strings, char *,
2583 *num_strings + num_new_strings);
2585 if (strings == NULL) {
2586 ldap_memfree(range_attr);
2587 *more_strings = False;
2588 return NULL;
2591 if (new_strings && num_new_strings) {
2592 memcpy(&strings[*num_strings], new_strings,
2593 sizeof(*new_strings) * num_new_strings);
2596 (*num_strings) += num_new_strings;
2598 if (*more_strings) {
2599 *next_attribute = talloc_asprintf(mem_ctx,
2600 "%s;range=%d-*",
2601 field,
2602 (int)*num_strings);
2604 if (!*next_attribute) {
2605 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2606 ldap_memfree(range_attr);
2607 *more_strings = False;
2608 return NULL;
2612 ldap_memfree(range_attr);
2614 return strings;
2618 * pull a single uint32 from a ADS result
2619 * @param ads connection to ads server
2620 * @param msg Results of search
2621 * @param field Attribute to retrieve
2622 * @param v Pointer to int to store result
2623 * @return boolean inidicating success
2625 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2626 uint32 *v)
2628 char **values;
2630 values = ldap_get_values(ads->ldap.ld, msg, field);
2631 if (!values)
2632 return False;
2633 if (!values[0]) {
2634 ldap_value_free(values);
2635 return False;
2638 *v = atoi(values[0]);
2639 ldap_value_free(values);
2640 return True;
2644 * pull a single objectGUID from an ADS result
2645 * @param ads connection to ADS server
2646 * @param msg results of search
2647 * @param guid 37-byte area to receive text guid
2648 * @return boolean indicating success
2650 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2652 DATA_BLOB blob;
2653 NTSTATUS status;
2655 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2656 &blob)) {
2657 return false;
2660 status = GUID_from_ndr_blob(&blob, guid);
2661 talloc_free(blob.data);
2662 return NT_STATUS_IS_OK(status);
2667 * pull a single struct dom_sid from a ADS result
2668 * @param ads connection to ads server
2669 * @param msg Results of search
2670 * @param field Attribute to retrieve
2671 * @param sid Pointer to sid to store result
2672 * @return boolean inidicating success
2674 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2675 struct dom_sid *sid)
2677 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2681 * pull an array of struct dom_sids from a ADS result
2682 * @param ads connection to ads server
2683 * @param mem_ctx TALLOC_CTX for allocating sid array
2684 * @param msg Results of search
2685 * @param field Attribute to retrieve
2686 * @param sids pointer to sid array to allocate
2687 * @return the count of SIDs pulled
2689 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2690 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2692 struct berval **values;
2693 bool ret;
2694 int count, i;
2696 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2698 if (!values)
2699 return 0;
2701 for (i=0; values[i]; i++)
2702 /* nop */ ;
2704 if (i) {
2705 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2706 if (!(*sids)) {
2707 ldap_value_free_len(values);
2708 return 0;
2710 } else {
2711 (*sids) = NULL;
2714 count = 0;
2715 for (i=0; values[i]; i++) {
2716 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2717 if (ret) {
2718 DEBUG(10, ("pulling SID: %s\n",
2719 sid_string_dbg(&(*sids)[count])));
2720 count++;
2724 ldap_value_free_len(values);
2725 return count;
2729 * pull a struct security_descriptor from a ADS result
2730 * @param ads connection to ads server
2731 * @param mem_ctx TALLOC_CTX for allocating sid array
2732 * @param msg Results of search
2733 * @param field Attribute to retrieve
2734 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2735 * @return boolean inidicating success
2737 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2738 LDAPMessage *msg, const char *field,
2739 struct security_descriptor **sd)
2741 struct berval **values;
2742 bool ret = true;
2744 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2746 if (!values) return false;
2748 if (values[0]) {
2749 NTSTATUS status;
2750 status = unmarshall_sec_desc(mem_ctx,
2751 (uint8 *)values[0]->bv_val,
2752 values[0]->bv_len, sd);
2753 if (!NT_STATUS_IS_OK(status)) {
2754 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2755 nt_errstr(status)));
2756 ret = false;
2760 ldap_value_free_len(values);
2761 return ret;
2765 * in order to support usernames longer than 21 characters we need to
2766 * use both the sAMAccountName and the userPrincipalName attributes
2767 * It seems that not all users have the userPrincipalName attribute set
2769 * @param ads connection to ads server
2770 * @param mem_ctx TALLOC_CTX for allocating sid array
2771 * @param msg Results of search
2772 * @return the username
2774 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2775 LDAPMessage *msg)
2777 #if 0 /* JERRY */
2778 char *ret, *p;
2780 /* lookup_name() only works on the sAMAccountName to
2781 returning the username portion of userPrincipalName
2782 breaks winbindd_getpwnam() */
2784 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2785 if (ret && (p = strchr_m(ret, '@'))) {
2786 *p = 0;
2787 return ret;
2789 #endif
2790 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2795 * find the update serial number - this is the core of the ldap cache
2796 * @param ads connection to ads server
2797 * @param ads connection to ADS server
2798 * @param usn Pointer to retrieved update serial number
2799 * @return status of search
2801 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2803 const char *attrs[] = {"highestCommittedUSN", NULL};
2804 ADS_STATUS status;
2805 LDAPMessage *res;
2807 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2808 if (!ADS_ERR_OK(status))
2809 return status;
2811 if (ads_count_replies(ads, res) != 1) {
2812 ads_msgfree(ads, res);
2813 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2816 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2817 ads_msgfree(ads, res);
2818 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2821 ads_msgfree(ads, res);
2822 return ADS_SUCCESS;
2825 /* parse a ADS timestring - typical string is
2826 '20020917091222.0Z0' which means 09:12.22 17th September
2827 2002, timezone 0 */
2828 static time_t ads_parse_time(const char *str)
2830 struct tm tm;
2832 ZERO_STRUCT(tm);
2834 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2835 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2836 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2837 return 0;
2839 tm.tm_year -= 1900;
2840 tm.tm_mon -= 1;
2842 return timegm(&tm);
2845 /********************************************************************
2846 ********************************************************************/
2848 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2850 const char *attrs[] = {"currentTime", NULL};
2851 ADS_STATUS status;
2852 LDAPMessage *res;
2853 char *timestr;
2854 TALLOC_CTX *ctx;
2855 ADS_STRUCT *ads_s = ads;
2857 if (!(ctx = talloc_init("ads_current_time"))) {
2858 return ADS_ERROR(LDAP_NO_MEMORY);
2861 /* establish a new ldap tcp session if necessary */
2863 if ( !ads->ldap.ld ) {
2864 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2865 ads->server.ldap_server )) == NULL )
2867 goto done;
2869 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2870 status = ads_connect( ads_s );
2871 if ( !ADS_ERR_OK(status))
2872 goto done;
2875 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2876 if (!ADS_ERR_OK(status)) {
2877 goto done;
2880 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2881 if (!timestr) {
2882 ads_msgfree(ads_s, res);
2883 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2884 goto done;
2887 /* but save the time and offset in the original ADS_STRUCT */
2889 ads->config.current_time = ads_parse_time(timestr);
2891 if (ads->config.current_time != 0) {
2892 ads->auth.time_offset = ads->config.current_time - time(NULL);
2893 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2896 ads_msgfree(ads, res);
2898 status = ADS_SUCCESS;
2900 done:
2901 /* free any temporary ads connections */
2902 if ( ads_s != ads ) {
2903 ads_destroy( &ads_s );
2905 talloc_destroy(ctx);
2907 return status;
2910 /********************************************************************
2911 ********************************************************************/
2913 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2915 const char *attrs[] = {"domainFunctionality", NULL};
2916 ADS_STATUS status;
2917 LDAPMessage *res;
2918 ADS_STRUCT *ads_s = ads;
2920 *val = DS_DOMAIN_FUNCTION_2000;
2922 /* establish a new ldap tcp session if necessary */
2924 if ( !ads->ldap.ld ) {
2925 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2926 ads->server.ldap_server )) == NULL )
2928 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2929 goto done;
2931 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2932 status = ads_connect( ads_s );
2933 if ( !ADS_ERR_OK(status))
2934 goto done;
2937 /* If the attribute does not exist assume it is a Windows 2000
2938 functional domain */
2940 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2941 if (!ADS_ERR_OK(status)) {
2942 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2943 status = ADS_SUCCESS;
2945 goto done;
2948 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2949 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2951 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2954 ads_msgfree(ads, res);
2956 done:
2957 /* free any temporary ads connections */
2958 if ( ads_s != ads ) {
2959 ads_destroy( &ads_s );
2962 return status;
2966 * find the domain sid for our domain
2967 * @param ads connection to ads server
2968 * @param sid Pointer to domain sid
2969 * @return status of search
2971 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2973 const char *attrs[] = {"objectSid", NULL};
2974 LDAPMessage *res;
2975 ADS_STATUS rc;
2977 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2978 attrs, &res);
2979 if (!ADS_ERR_OK(rc)) return rc;
2980 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2981 ads_msgfree(ads, res);
2982 return ADS_ERROR_SYSTEM(ENOENT);
2984 ads_msgfree(ads, res);
2986 return ADS_SUCCESS;
2990 * find our site name
2991 * @param ads connection to ads server
2992 * @param mem_ctx Pointer to talloc context
2993 * @param site_name Pointer to the sitename
2994 * @return status of search
2996 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2998 ADS_STATUS status;
2999 LDAPMessage *res;
3000 const char *dn, *service_name;
3001 const char *attrs[] = { "dsServiceName", NULL };
3003 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3004 if (!ADS_ERR_OK(status)) {
3005 return status;
3008 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3009 if (service_name == NULL) {
3010 ads_msgfree(ads, res);
3011 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3014 ads_msgfree(ads, res);
3016 /* go up three levels */
3017 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3018 if (dn == NULL) {
3019 return ADS_ERROR(LDAP_NO_MEMORY);
3022 *site_name = talloc_strdup(mem_ctx, dn);
3023 if (*site_name == NULL) {
3024 return ADS_ERROR(LDAP_NO_MEMORY);
3027 return status;
3029 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3034 * find the site dn where a machine resides
3035 * @param ads connection to ads server
3036 * @param mem_ctx Pointer to talloc context
3037 * @param computer_name name of the machine
3038 * @param site_name Pointer to the sitename
3039 * @return status of search
3041 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3043 ADS_STATUS status;
3044 LDAPMessage *res;
3045 const char *parent, *filter;
3046 char *config_context = NULL;
3047 char *dn;
3049 /* shortcut a query */
3050 if (strequal(computer_name, ads->config.ldap_server_name)) {
3051 return ads_site_dn(ads, mem_ctx, site_dn);
3054 status = ads_config_path(ads, mem_ctx, &config_context);
3055 if (!ADS_ERR_OK(status)) {
3056 return status;
3059 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3060 if (filter == NULL) {
3061 return ADS_ERROR(LDAP_NO_MEMORY);
3064 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3065 filter, NULL, &res);
3066 if (!ADS_ERR_OK(status)) {
3067 return status;
3070 if (ads_count_replies(ads, res) != 1) {
3071 ads_msgfree(ads, res);
3072 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3075 dn = ads_get_dn(ads, mem_ctx, res);
3076 if (dn == NULL) {
3077 ads_msgfree(ads, res);
3078 return ADS_ERROR(LDAP_NO_MEMORY);
3081 /* go up three levels */
3082 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3083 if (parent == NULL) {
3084 ads_msgfree(ads, res);
3085 TALLOC_FREE(dn);
3086 return ADS_ERROR(LDAP_NO_MEMORY);
3089 *site_dn = talloc_strdup(mem_ctx, parent);
3090 if (*site_dn == NULL) {
3091 ads_msgfree(ads, res);
3092 TALLOC_FREE(dn);
3093 return ADS_ERROR(LDAP_NO_MEMORY);
3096 TALLOC_FREE(dn);
3097 ads_msgfree(ads, res);
3099 return status;
3103 * get the upn suffixes for a domain
3104 * @param ads connection to ads server
3105 * @param mem_ctx Pointer to talloc context
3106 * @param suffixes Pointer to an array of suffixes
3107 * @param num_suffixes Pointer to the number of suffixes
3108 * @return status of search
3110 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3112 ADS_STATUS status;
3113 LDAPMessage *res;
3114 const char *base;
3115 char *config_context = NULL;
3116 const char *attrs[] = { "uPNSuffixes", NULL };
3118 status = ads_config_path(ads, mem_ctx, &config_context);
3119 if (!ADS_ERR_OK(status)) {
3120 return status;
3123 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3124 if (base == NULL) {
3125 return ADS_ERROR(LDAP_NO_MEMORY);
3128 status = ads_search_dn(ads, &res, base, attrs);
3129 if (!ADS_ERR_OK(status)) {
3130 return status;
3133 if (ads_count_replies(ads, res) != 1) {
3134 ads_msgfree(ads, res);
3135 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3138 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3139 if ((*suffixes) == NULL) {
3140 ads_msgfree(ads, res);
3141 return ADS_ERROR(LDAP_NO_MEMORY);
3144 ads_msgfree(ads, res);
3146 return status;
3150 * get the joinable ous for a domain
3151 * @param ads connection to ads server
3152 * @param mem_ctx Pointer to talloc context
3153 * @param ous Pointer to an array of ous
3154 * @param num_ous Pointer to the number of ous
3155 * @return status of search
3157 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3158 TALLOC_CTX *mem_ctx,
3159 char ***ous,
3160 size_t *num_ous)
3162 ADS_STATUS status;
3163 LDAPMessage *res = NULL;
3164 LDAPMessage *msg = NULL;
3165 const char *attrs[] = { "dn", NULL };
3166 int count = 0;
3168 status = ads_search(ads, &res,
3169 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3170 attrs);
3171 if (!ADS_ERR_OK(status)) {
3172 return status;
3175 count = ads_count_replies(ads, res);
3176 if (count < 1) {
3177 ads_msgfree(ads, res);
3178 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3181 for (msg = ads_first_entry(ads, res); msg;
3182 msg = ads_next_entry(ads, msg)) {
3184 char *dn = NULL;
3186 dn = ads_get_dn(ads, talloc_tos(), msg);
3187 if (!dn) {
3188 ads_msgfree(ads, res);
3189 return ADS_ERROR(LDAP_NO_MEMORY);
3192 if (!add_string_to_array(mem_ctx, dn,
3193 (const char ***)ous,
3194 (int *)num_ous)) {
3195 TALLOC_FREE(dn);
3196 ads_msgfree(ads, res);
3197 return ADS_ERROR(LDAP_NO_MEMORY);
3200 TALLOC_FREE(dn);
3203 ads_msgfree(ads, res);
3205 return status;
3210 * pull a struct dom_sid from an extended dn string
3211 * @param mem_ctx TALLOC_CTX
3212 * @param extended_dn string
3213 * @param flags string type of extended_dn
3214 * @param sid pointer to a struct dom_sid
3215 * @return NT_STATUS_OK on success,
3216 * NT_INVALID_PARAMETER on error,
3217 * NT_STATUS_NOT_FOUND if no SID present
3219 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3220 const char *extended_dn,
3221 enum ads_extended_dn_flags flags,
3222 struct dom_sid *sid)
3224 char *p, *q, *dn;
3226 if (!extended_dn) {
3227 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3230 /* otherwise extended_dn gets stripped off */
3231 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3232 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3235 * ADS_EXTENDED_DN_HEX_STRING:
3236 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3238 * ADS_EXTENDED_DN_STRING (only with w2k3):
3239 * <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
3241 * Object with no SID, such as an Exchange Public Folder
3242 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3245 p = strchr(dn, ';');
3246 if (!p) {
3247 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3250 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3251 DEBUG(5,("No SID present in extended dn\n"));
3252 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3255 p += strlen(";<SID=");
3257 q = strchr(p, '>');
3258 if (!q) {
3259 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3262 *q = '\0';
3264 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3266 switch (flags) {
3268 case ADS_EXTENDED_DN_STRING:
3269 if (!string_to_sid(sid, p)) {
3270 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3272 break;
3273 case ADS_EXTENDED_DN_HEX_STRING: {
3274 fstring buf;
3275 size_t buf_len;
3277 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3278 if (buf_len == 0) {
3279 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3282 if (!sid_parse(buf, buf_len, sid)) {
3283 DEBUG(10,("failed to parse sid\n"));
3284 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3286 break;
3288 default:
3289 DEBUG(10,("unknown extended dn format\n"));
3290 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3293 return ADS_ERROR_NT(NT_STATUS_OK);
3296 /********************************************************************
3297 ********************************************************************/
3299 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3301 LDAPMessage *res = NULL;
3302 ADS_STATUS status;
3303 int count = 0;
3304 char *name = NULL;
3306 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3307 if (!ADS_ERR_OK(status)) {
3308 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3309 lp_netbios_name()));
3310 goto out;
3313 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3314 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3315 goto out;
3318 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3319 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3322 out:
3323 ads_msgfree(ads, res);
3325 return name;
3328 /********************************************************************
3329 ********************************************************************/
3331 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3333 LDAPMessage *res = NULL;
3334 ADS_STATUS status;
3335 int count = 0;
3336 char *name = NULL;
3338 status = ads_find_machine_acct(ads, &res, machine_name);
3339 if (!ADS_ERR_OK(status)) {
3340 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3341 lp_netbios_name()));
3342 goto out;
3345 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3346 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3347 goto out;
3350 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3351 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3354 out:
3355 ads_msgfree(ads, res);
3357 return name;
3360 /********************************************************************
3361 ********************************************************************/
3363 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3365 LDAPMessage *res = NULL;
3366 ADS_STATUS status;
3367 int count = 0;
3368 char *name = NULL;
3370 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3371 if (!ADS_ERR_OK(status)) {
3372 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3373 lp_netbios_name()));
3374 goto out;
3377 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3378 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3379 goto out;
3382 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3383 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3386 out:
3387 ads_msgfree(ads, res);
3389 return name;
3392 #if 0
3394 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3397 * Join a machine to a realm
3398 * Creates the machine account and sets the machine password
3399 * @param ads connection to ads server
3400 * @param machine name of host to add
3401 * @param org_unit Organizational unit to place machine in
3402 * @return status of join
3404 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3405 uint32 account_type, const char *org_unit)
3407 ADS_STATUS status;
3408 LDAPMessage *res = NULL;
3409 char *machine;
3411 /* machine name must be lowercase */
3412 machine = SMB_STRDUP(machine_name);
3413 strlower_m(machine);
3416 status = ads_find_machine_acct(ads, (void **)&res, machine);
3417 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3418 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3419 status = ads_leave_realm(ads, machine);
3420 if (!ADS_ERR_OK(status)) {
3421 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3422 machine, ads->config.realm));
3423 return status;
3427 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3428 if (!ADS_ERR_OK(status)) {
3429 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3430 SAFE_FREE(machine);
3431 return status;
3434 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3435 if (!ADS_ERR_OK(status)) {
3436 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3437 SAFE_FREE(machine);
3438 return status;
3441 SAFE_FREE(machine);
3442 ads_msgfree(ads, res);
3444 return status;
3446 #endif
3449 * Delete a machine from the realm
3450 * @param ads connection to ads server
3451 * @param hostname Machine to remove
3452 * @return status of delete
3454 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3456 ADS_STATUS status;
3457 void *msg;
3458 LDAPMessage *res;
3459 char *hostnameDN, *host;
3460 int rc;
3461 LDAPControl ldap_control;
3462 LDAPControl * pldap_control[2] = {NULL, NULL};
3464 pldap_control[0] = &ldap_control;
3465 memset(&ldap_control, 0, sizeof(LDAPControl));
3466 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3468 /* hostname must be lowercase */
3469 host = SMB_STRDUP(hostname);
3470 if (!strlower_m(host)) {
3471 SAFE_FREE(host);
3472 return ADS_ERROR_SYSTEM(EINVAL);
3475 status = ads_find_machine_acct(ads, &res, host);
3476 if (!ADS_ERR_OK(status)) {
3477 DEBUG(0, ("Host account for %s does not exist.\n", host));
3478 SAFE_FREE(host);
3479 return status;
3482 msg = ads_first_entry(ads, res);
3483 if (!msg) {
3484 SAFE_FREE(host);
3485 return ADS_ERROR_SYSTEM(ENOENT);
3488 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3489 if (hostnameDN == NULL) {
3490 SAFE_FREE(host);
3491 return ADS_ERROR_SYSTEM(ENOENT);
3494 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3495 if (rc) {
3496 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3497 }else {
3498 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3501 if (rc != LDAP_SUCCESS) {
3502 const char *attrs[] = { "cn", NULL };
3503 LDAPMessage *msg_sub;
3505 /* we only search with scope ONE, we do not expect any further
3506 * objects to be created deeper */
3508 status = ads_do_search_retry(ads, hostnameDN,
3509 LDAP_SCOPE_ONELEVEL,
3510 "(objectclass=*)", attrs, &res);
3512 if (!ADS_ERR_OK(status)) {
3513 SAFE_FREE(host);
3514 TALLOC_FREE(hostnameDN);
3515 return status;
3518 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3519 msg_sub = ads_next_entry(ads, msg_sub)) {
3521 char *dn = NULL;
3523 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3524 SAFE_FREE(host);
3525 TALLOC_FREE(hostnameDN);
3526 return ADS_ERROR(LDAP_NO_MEMORY);
3529 status = ads_del_dn(ads, dn);
3530 if (!ADS_ERR_OK(status)) {
3531 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3532 SAFE_FREE(host);
3533 TALLOC_FREE(dn);
3534 TALLOC_FREE(hostnameDN);
3535 return status;
3538 TALLOC_FREE(dn);
3541 /* there should be no subordinate objects anymore */
3542 status = ads_do_search_retry(ads, hostnameDN,
3543 LDAP_SCOPE_ONELEVEL,
3544 "(objectclass=*)", attrs, &res);
3546 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3547 SAFE_FREE(host);
3548 TALLOC_FREE(hostnameDN);
3549 return status;
3552 /* delete hostnameDN now */
3553 status = ads_del_dn(ads, hostnameDN);
3554 if (!ADS_ERR_OK(status)) {
3555 SAFE_FREE(host);
3556 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3557 TALLOC_FREE(hostnameDN);
3558 return status;
3562 TALLOC_FREE(hostnameDN);
3564 status = ads_find_machine_acct(ads, &res, host);
3565 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3566 DEBUG(3, ("Failed to remove host account.\n"));
3567 SAFE_FREE(host);
3568 return status;
3571 SAFE_FREE(host);
3572 return status;
3576 * pull all token-sids from an LDAP dn
3577 * @param ads connection to ads server
3578 * @param mem_ctx TALLOC_CTX for allocating sid array
3579 * @param dn of LDAP object
3580 * @param user_sid pointer to struct dom_sid (objectSid)
3581 * @param primary_group_sid pointer to struct dom_sid (self composed)
3582 * @param sids pointer to sid array to allocate
3583 * @param num_sids counter of SIDs pulled
3584 * @return status of token query
3586 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3587 TALLOC_CTX *mem_ctx,
3588 const char *dn,
3589 struct dom_sid *user_sid,
3590 struct dom_sid *primary_group_sid,
3591 struct dom_sid **sids,
3592 size_t *num_sids)
3594 ADS_STATUS status;
3595 LDAPMessage *res = NULL;
3596 int count = 0;
3597 size_t tmp_num_sids;
3598 struct dom_sid *tmp_sids;
3599 struct dom_sid tmp_user_sid;
3600 struct dom_sid tmp_primary_group_sid;
3601 uint32 pgid;
3602 const char *attrs[] = {
3603 "objectSid",
3604 "tokenGroups",
3605 "primaryGroupID",
3606 NULL
3609 status = ads_search_retry_dn(ads, &res, dn, attrs);
3610 if (!ADS_ERR_OK(status)) {
3611 return status;
3614 count = ads_count_replies(ads, res);
3615 if (count != 1) {
3616 ads_msgfree(ads, res);
3617 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3620 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3621 ads_msgfree(ads, res);
3622 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3625 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3626 ads_msgfree(ads, res);
3627 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3631 /* hack to compose the primary group sid without knowing the
3632 * domsid */
3634 struct dom_sid domsid;
3636 sid_copy(&domsid, &tmp_user_sid);
3638 if (!sid_split_rid(&domsid, NULL)) {
3639 ads_msgfree(ads, res);
3640 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3643 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3644 ads_msgfree(ads, res);
3645 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3649 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3651 if (tmp_num_sids == 0 || !tmp_sids) {
3652 ads_msgfree(ads, res);
3653 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3656 if (num_sids) {
3657 *num_sids = tmp_num_sids;
3660 if (sids) {
3661 *sids = tmp_sids;
3664 if (user_sid) {
3665 *user_sid = tmp_user_sid;
3668 if (primary_group_sid) {
3669 *primary_group_sid = tmp_primary_group_sid;
3672 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3674 ads_msgfree(ads, res);
3675 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3679 * Find a sAMAccoutName in LDAP
3680 * @param ads connection to ads server
3681 * @param mem_ctx TALLOC_CTX for allocating sid array
3682 * @param samaccountname to search
3683 * @param uac_ret uint32 pointer userAccountControl attribute value
3684 * @param dn_ret pointer to dn
3685 * @return status of token query
3687 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3688 TALLOC_CTX *mem_ctx,
3689 const char *samaccountname,
3690 uint32 *uac_ret,
3691 const char **dn_ret)
3693 ADS_STATUS status;
3694 const char *attrs[] = { "userAccountControl", NULL };
3695 const char *filter;
3696 LDAPMessage *res = NULL;
3697 char *dn = NULL;
3698 uint32 uac = 0;
3700 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3701 samaccountname);
3702 if (filter == NULL) {
3703 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3704 goto out;
3707 status = ads_do_search_all(ads, ads->config.bind_path,
3708 LDAP_SCOPE_SUBTREE,
3709 filter, attrs, &res);
3711 if (!ADS_ERR_OK(status)) {
3712 goto out;
3715 if (ads_count_replies(ads, res) != 1) {
3716 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3717 goto out;
3720 dn = ads_get_dn(ads, talloc_tos(), res);
3721 if (dn == NULL) {
3722 status = ADS_ERROR(LDAP_NO_MEMORY);
3723 goto out;
3726 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3727 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3728 goto out;
3731 if (uac_ret) {
3732 *uac_ret = uac;
3735 if (dn_ret) {
3736 *dn_ret = talloc_strdup(mem_ctx, dn);
3737 if (!*dn_ret) {
3738 status = ADS_ERROR(LDAP_NO_MEMORY);
3739 goto out;
3742 out:
3743 TALLOC_FREE(dn);
3744 ads_msgfree(ads, res);
3746 return status;
3750 * find our configuration path
3751 * @param ads connection to ads server
3752 * @param mem_ctx Pointer to talloc context
3753 * @param config_path Pointer to the config path
3754 * @return status of search
3756 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3757 TALLOC_CTX *mem_ctx,
3758 char **config_path)
3760 ADS_STATUS status;
3761 LDAPMessage *res = NULL;
3762 const char *config_context = NULL;
3763 const char *attrs[] = { "configurationNamingContext", NULL };
3765 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3766 "(objectclass=*)", attrs, &res);
3767 if (!ADS_ERR_OK(status)) {
3768 return status;
3771 config_context = ads_pull_string(ads, mem_ctx, res,
3772 "configurationNamingContext");
3773 ads_msgfree(ads, res);
3774 if (!config_context) {
3775 return ADS_ERROR(LDAP_NO_MEMORY);
3778 if (config_path) {
3779 *config_path = talloc_strdup(mem_ctx, config_context);
3780 if (!*config_path) {
3781 return ADS_ERROR(LDAP_NO_MEMORY);
3785 return ADS_ERROR(LDAP_SUCCESS);
3789 * find the displayName of an extended right
3790 * @param ads connection to ads server
3791 * @param config_path The config path
3792 * @param mem_ctx Pointer to talloc context
3793 * @param GUID struct of the rightsGUID
3794 * @return status of search
3796 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3797 const char *config_path,
3798 TALLOC_CTX *mem_ctx,
3799 const struct GUID *rights_guid)
3801 ADS_STATUS rc;
3802 LDAPMessage *res = NULL;
3803 char *expr = NULL;
3804 const char *attrs[] = { "displayName", NULL };
3805 const char *result = NULL;
3806 const char *path;
3808 if (!ads || !mem_ctx || !rights_guid) {
3809 goto done;
3812 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3813 GUID_string(mem_ctx, rights_guid));
3814 if (!expr) {
3815 goto done;
3818 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3819 if (!path) {
3820 goto done;
3823 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3824 expr, attrs, &res);
3825 if (!ADS_ERR_OK(rc)) {
3826 goto done;
3829 if (ads_count_replies(ads, res) != 1) {
3830 goto done;
3833 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3835 done:
3836 ads_msgfree(ads, res);
3837 return result;
3841 * verify or build and verify an account ou
3842 * @param mem_ctx Pointer to talloc context
3843 * @param ads connection to ads server
3844 * @param account_ou
3845 * @return status of search
3848 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3849 ADS_STRUCT *ads,
3850 const char **account_ou)
3852 char **exploded_dn;
3853 const char *name;
3854 char *ou_string;
3856 exploded_dn = ldap_explode_dn(*account_ou, 0);
3857 if (exploded_dn) {
3858 ldap_value_free(exploded_dn);
3859 return ADS_SUCCESS;
3862 ou_string = ads_ou_string(ads, *account_ou);
3863 if (!ou_string) {
3864 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3867 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3868 ads->config.bind_path);
3869 SAFE_FREE(ou_string);
3871 if (!name) {
3872 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3875 exploded_dn = ldap_explode_dn(name, 0);
3876 if (!exploded_dn) {
3877 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3879 ldap_value_free(exploded_dn);
3881 *account_ou = name;
3882 return ADS_SUCCESS;
3885 #endif