Now ACL inheritance flags are working, add test_inheritance_flags() back into raw...
[Samba/bb.git] / source3 / libads / ldap.c
blob946c1a36a72b8b9cd2f1c97287a0d9c3e0e0e100
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(smb_ldap_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 ADS_STATUS status2;
1129 LDAPMessage *msg, *next;
1131 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1132 attrs, args, &res2, &count, &cookie);
1134 if (!ADS_ERR_OK(status2)) break;
1136 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1137 that this works on all ldap libs, but I have only tested with openldap */
1138 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1139 next = ads_next_message(ads, msg);
1140 ldap_add_result_entry((LDAPMessage **)res, msg);
1142 /* note that we do not free res2, as the memory is now
1143 part of the main returned list */
1145 #else
1146 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1147 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1148 #endif
1150 return status;
1153 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1154 int scope, const char *expr,
1155 const char **attrs, LDAPMessage **res)
1157 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1160 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1161 int scope, const char *expr,
1162 const char **attrs, uint32 sd_flags,
1163 LDAPMessage **res)
1165 ads_control args;
1167 args.control = ADS_SD_FLAGS_OID;
1168 args.val = sd_flags;
1169 args.critical = True;
1171 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1176 * Run a function on all results for a search. Uses ads_do_paged_search() and
1177 * runs the function as each page is returned, using ads_process_results()
1178 * @param ads connection to ads server
1179 * @param bind_path Base dn for the search
1180 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1181 * @param expr Search expression - specified in local charset
1182 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1183 * @param fn Function which takes attr name, values list, and data_area
1184 * @param data_area Pointer which is passed to function on each call
1185 * @return status of search
1187 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1188 int scope, const char *expr, const char **attrs,
1189 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1190 void *data_area)
1192 struct berval *cookie = NULL;
1193 int count = 0;
1194 ADS_STATUS status;
1195 LDAPMessage *res;
1197 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1198 &count, &cookie);
1200 if (!ADS_ERR_OK(status)) return status;
1202 ads_process_results(ads, res, fn, data_area);
1203 ads_msgfree(ads, res);
1205 while (cookie) {
1206 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1207 &res, &count, &cookie);
1209 if (!ADS_ERR_OK(status)) break;
1211 ads_process_results(ads, res, fn, data_area);
1212 ads_msgfree(ads, res);
1215 return status;
1219 * Do a search with a timeout.
1220 * @param ads connection to ads server
1221 * @param bind_path Base dn for the search
1222 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1223 * @param expr Search expression
1224 * @param attrs Attributes to retrieve
1225 * @param res ** which will contain results - free res* with ads_msgfree()
1226 * @return status of search
1228 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1229 const char *expr,
1230 const char **attrs, LDAPMessage **res)
1232 int rc;
1233 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1234 size_t converted_size;
1235 TALLOC_CTX *ctx;
1237 *res = NULL;
1238 if (!(ctx = talloc_init("ads_do_search"))) {
1239 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1240 return ADS_ERROR(LDAP_NO_MEMORY);
1243 /* 0 means the conversion worked but the result was empty
1244 so we only fail if it's negative. In any case, it always
1245 at least nulls out the dest */
1246 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1247 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1249 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1250 rc = LDAP_NO_MEMORY;
1251 goto done;
1254 if (!attrs || !(*attrs))
1255 search_attrs = NULL;
1256 else {
1257 /* This would be the utf8-encoded version...*/
1258 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1259 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1261 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1262 rc = LDAP_NO_MEMORY;
1263 goto done;
1267 /* see the note in ads_do_paged_search - we *must* disable referrals */
1268 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1270 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1271 search_attrs, 0, NULL, NULL,
1272 LDAP_NO_LIMIT,
1273 (LDAPMessage **)res);
1275 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1276 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1277 rc = 0;
1280 done:
1281 talloc_destroy(ctx);
1282 /* if/when we decide to utf8-encode attrs, take out this next line */
1283 TALLOC_FREE(search_attrs);
1284 return ADS_ERROR(rc);
1287 * Do a general ADS search
1288 * @param ads connection to ads server
1289 * @param res ** which will contain results - free res* with ads_msgfree()
1290 * @param expr Search expression
1291 * @param attrs Attributes to retrieve
1292 * @return status of search
1294 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1295 const char *expr, const char **attrs)
1297 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1298 expr, attrs, res);
1302 * Do a search on a specific DistinguishedName
1303 * @param ads connection to ads server
1304 * @param res ** which will contain results - free res* with ads_msgfree()
1305 * @param dn DistinguishName to search
1306 * @param attrs Attributes to retrieve
1307 * @return status of search
1309 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1310 const char *dn, const char **attrs)
1312 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1313 attrs, res);
1317 * Free up memory from a ads_search
1318 * @param ads connection to ads server
1319 * @param msg Search results to free
1321 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1323 if (!msg) return;
1324 ldap_msgfree(msg);
1328 * Get a dn from search results
1329 * @param ads connection to ads server
1330 * @param msg Search result
1331 * @return dn string
1333 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1335 char *utf8_dn, *unix_dn;
1336 size_t converted_size;
1338 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1340 if (!utf8_dn) {
1341 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1342 return NULL;
1345 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1346 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1347 utf8_dn ));
1348 return NULL;
1350 ldap_memfree(utf8_dn);
1351 return unix_dn;
1355 * Get the parent from a dn
1356 * @param dn the dn to return the parent from
1357 * @return parent dn string
1359 char *ads_parent_dn(const char *dn)
1361 char *p;
1363 if (dn == NULL) {
1364 return NULL;
1367 p = strchr(dn, ',');
1369 if (p == NULL) {
1370 return NULL;
1373 return p+1;
1377 * Find a machine account given a hostname
1378 * @param ads connection to ads server
1379 * @param res ** which will contain results - free res* with ads_msgfree()
1380 * @param host Hostname to search for
1381 * @return status of search
1383 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1384 const char *machine)
1386 ADS_STATUS status;
1387 char *expr;
1388 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1390 *res = NULL;
1392 /* the easiest way to find a machine account anywhere in the tree
1393 is to look for hostname$ */
1394 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1395 DEBUG(1, ("asprintf failed!\n"));
1396 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1399 status = ads_search(ads, res, expr, attrs);
1400 SAFE_FREE(expr);
1401 return status;
1405 * Initialize a list of mods to be used in a modify request
1406 * @param ctx An initialized TALLOC_CTX
1407 * @return allocated ADS_MODLIST
1409 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1411 #define ADS_MODLIST_ALLOC_SIZE 10
1412 LDAPMod **mods;
1414 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1415 /* -1 is safety to make sure we don't go over the end.
1416 need to reset it to NULL before doing ldap modify */
1417 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1419 return (ADS_MODLIST)mods;
1424 add an attribute to the list, with values list already constructed
1426 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1427 int mod_op, const char *name,
1428 const void *_invals)
1430 const void **invals = (const void **)_invals;
1431 int curmod;
1432 LDAPMod **modlist = (LDAPMod **) *mods;
1433 struct berval **ber_values = NULL;
1434 char **char_values = NULL;
1436 if (!invals) {
1437 mod_op = LDAP_MOD_DELETE;
1438 } else {
1439 if (mod_op & LDAP_MOD_BVALUES)
1440 ber_values = ads_dup_values(ctx,
1441 (const struct berval **)invals);
1442 else
1443 char_values = ads_push_strvals(ctx,
1444 (const char **) invals);
1447 /* find the first empty slot */
1448 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1449 curmod++);
1450 if (modlist[curmod] == (LDAPMod *) -1) {
1451 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1452 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1453 return ADS_ERROR(LDAP_NO_MEMORY);
1454 memset(&modlist[curmod], 0,
1455 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1456 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1457 *mods = (ADS_MODLIST)modlist;
1460 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1461 return ADS_ERROR(LDAP_NO_MEMORY);
1462 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1463 if (mod_op & LDAP_MOD_BVALUES) {
1464 modlist[curmod]->mod_bvalues = ber_values;
1465 } else if (mod_op & LDAP_MOD_DELETE) {
1466 modlist[curmod]->mod_values = NULL;
1467 } else {
1468 modlist[curmod]->mod_values = char_values;
1471 modlist[curmod]->mod_op = mod_op;
1472 return ADS_ERROR(LDAP_SUCCESS);
1476 * Add a single string value to a mod list
1477 * @param ctx An initialized TALLOC_CTX
1478 * @param mods An initialized ADS_MODLIST
1479 * @param name The attribute name to add
1480 * @param val The value to add - NULL means DELETE
1481 * @return ADS STATUS indicating success of add
1483 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1484 const char *name, const char *val)
1486 const char *values[2];
1488 values[0] = val;
1489 values[1] = NULL;
1491 if (!val)
1492 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1493 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1497 * Add an array of string values to a mod list
1498 * @param ctx An initialized TALLOC_CTX
1499 * @param mods An initialized ADS_MODLIST
1500 * @param name The attribute name to add
1501 * @param vals The array of string values to add - NULL means DELETE
1502 * @return ADS STATUS indicating success of add
1504 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1505 const char *name, const char **vals)
1507 if (!vals)
1508 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1509 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1510 name, (const void **) vals);
1513 #if 0
1515 * Add a single ber-encoded value to a mod list
1516 * @param ctx An initialized TALLOC_CTX
1517 * @param mods An initialized ADS_MODLIST
1518 * @param name The attribute name to add
1519 * @param val The value to add - NULL means DELETE
1520 * @return ADS STATUS indicating success of add
1522 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1523 const char *name, const struct berval *val)
1525 const struct berval *values[2];
1527 values[0] = val;
1528 values[1] = NULL;
1529 if (!val)
1530 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1531 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1532 name, (const void **) values);
1534 #endif
1537 * Perform an ldap modify
1538 * @param ads connection to ads server
1539 * @param mod_dn DistinguishedName to modify
1540 * @param mods list of modifications to perform
1541 * @return status of modify
1543 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1545 int ret,i;
1546 char *utf8_dn = NULL;
1547 size_t converted_size;
1549 this control is needed to modify that contains a currently
1550 non-existent attribute (but allowable for the object) to run
1552 LDAPControl PermitModify = {
1553 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1554 {0, NULL},
1555 (char) 1};
1556 LDAPControl *controls[2];
1558 controls[0] = &PermitModify;
1559 controls[1] = NULL;
1561 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1562 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1565 /* find the end of the list, marked by NULL or -1 */
1566 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1567 /* make sure the end of the list is NULL */
1568 mods[i] = NULL;
1569 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1570 (LDAPMod **) mods, controls, NULL);
1571 TALLOC_FREE(utf8_dn);
1572 return ADS_ERROR(ret);
1576 * Perform an ldap add
1577 * @param ads connection to ads server
1578 * @param new_dn DistinguishedName to add
1579 * @param mods list of attributes and values for DN
1580 * @return status of add
1582 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1584 int ret, i;
1585 char *utf8_dn = NULL;
1586 size_t converted_size;
1588 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1589 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1590 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1593 /* find the end of the list, marked by NULL or -1 */
1594 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1595 /* make sure the end of the list is NULL */
1596 mods[i] = NULL;
1598 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1599 TALLOC_FREE(utf8_dn);
1600 return ADS_ERROR(ret);
1604 * Delete a DistinguishedName
1605 * @param ads connection to ads server
1606 * @param new_dn DistinguishedName to delete
1607 * @return status of delete
1609 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1611 int ret;
1612 char *utf8_dn = NULL;
1613 size_t converted_size;
1614 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1615 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1616 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1619 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1620 TALLOC_FREE(utf8_dn);
1621 return ADS_ERROR(ret);
1625 * Build an org unit string
1626 * if org unit is Computers or blank then assume a container, otherwise
1627 * assume a / separated list of organisational units.
1628 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1629 * @param ads connection to ads server
1630 * @param org_unit Organizational unit
1631 * @return org unit string - caller must free
1633 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1635 char *ret = NULL;
1637 if (!org_unit || !*org_unit) {
1639 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1641 /* samba4 might not yet respond to a wellknownobject-query */
1642 return ret ? ret : SMB_STRDUP("cn=Computers");
1645 if (strequal(org_unit, "Computers")) {
1646 return SMB_STRDUP("cn=Computers");
1649 /* jmcd: removed "\\" from the separation chars, because it is
1650 needed as an escape for chars like '#' which are valid in an
1651 OU name */
1652 return ads_build_path(org_unit, "/", "ou=", 1);
1656 * Get a org unit string for a well-known GUID
1657 * @param ads connection to ads server
1658 * @param wknguid Well known GUID
1659 * @return org unit string - caller must free
1661 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1663 ADS_STATUS status;
1664 LDAPMessage *res = NULL;
1665 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1666 **bind_dn_exp = NULL;
1667 const char *attrs[] = {"distinguishedName", NULL};
1668 int new_ln, wkn_ln, bind_ln, i;
1670 if (wknguid == NULL) {
1671 return NULL;
1674 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1675 DEBUG(1, ("asprintf failed!\n"));
1676 return NULL;
1679 status = ads_search_dn(ads, &res, base, attrs);
1680 if (!ADS_ERR_OK(status)) {
1681 DEBUG(1,("Failed while searching for: %s\n", base));
1682 goto out;
1685 if (ads_count_replies(ads, res) != 1) {
1686 goto out;
1689 /* substitute the bind-path from the well-known-guid-search result */
1690 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1691 if (!wkn_dn) {
1692 goto out;
1695 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1696 if (!wkn_dn_exp) {
1697 goto out;
1700 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1701 if (!bind_dn_exp) {
1702 goto out;
1705 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1707 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1710 new_ln = wkn_ln - bind_ln;
1712 ret = SMB_STRDUP(wkn_dn_exp[0]);
1713 if (!ret) {
1714 goto out;
1717 for (i=1; i < new_ln; i++) {
1718 char *s = NULL;
1720 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1721 SAFE_FREE(ret);
1722 goto out;
1725 SAFE_FREE(ret);
1726 ret = SMB_STRDUP(s);
1727 free(s);
1728 if (!ret) {
1729 goto out;
1733 out:
1734 SAFE_FREE(base);
1735 ads_msgfree(ads, res);
1736 TALLOC_FREE(wkn_dn);
1737 if (wkn_dn_exp) {
1738 ldap_value_free(wkn_dn_exp);
1740 if (bind_dn_exp) {
1741 ldap_value_free(bind_dn_exp);
1744 return ret;
1748 * Adds (appends) an item to an attribute array, rather then
1749 * replacing the whole list
1750 * @param ctx An initialized TALLOC_CTX
1751 * @param mods An initialized ADS_MODLIST
1752 * @param name name of the ldap attribute to append to
1753 * @param vals an array of values to add
1754 * @return status of addition
1757 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1758 const char *name, const char **vals)
1760 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1761 (const void *) vals);
1765 * Determines the an account's current KVNO via an LDAP lookup
1766 * @param ads An initialized ADS_STRUCT
1767 * @param account_name the NT samaccountname.
1768 * @return the kvno for the account, or -1 in case of a failure.
1771 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1773 LDAPMessage *res = NULL;
1774 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1775 char *filter;
1776 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1777 char *dn_string = NULL;
1778 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1780 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1781 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1782 return kvno;
1784 ret = ads_search(ads, &res, filter, attrs);
1785 SAFE_FREE(filter);
1786 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1787 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1788 ads_msgfree(ads, res);
1789 return kvno;
1792 dn_string = ads_get_dn(ads, talloc_tos(), res);
1793 if (!dn_string) {
1794 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1795 ads_msgfree(ads, res);
1796 return kvno;
1798 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1799 TALLOC_FREE(dn_string);
1801 /* ---------------------------------------------------------
1802 * 0 is returned as a default KVNO from this point on...
1803 * This is done because Windows 2000 does not support key
1804 * version numbers. Chances are that a failure in the next
1805 * step is simply due to Windows 2000 being used for a
1806 * domain controller. */
1807 kvno = 0;
1809 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1810 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1811 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1812 ads_msgfree(ads, res);
1813 return kvno;
1816 /* Success */
1817 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1818 ads_msgfree(ads, res);
1819 return kvno;
1823 * Determines the computer account's current KVNO via an LDAP lookup
1824 * @param ads An initialized ADS_STRUCT
1825 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1826 * @return the kvno for the computer account, or -1 in case of a failure.
1829 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1831 char *computer_account = NULL;
1832 uint32_t kvno = -1;
1834 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1835 return kvno;
1838 kvno = ads_get_kvno(ads, computer_account);
1839 free(computer_account);
1841 return kvno;
1845 * This clears out all registered spn's for a given hostname
1846 * @param ads An initilaized ADS_STRUCT
1847 * @param machine_name the NetBIOS name of the computer.
1848 * @return 0 upon success, non-zero otherwise.
1851 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1853 TALLOC_CTX *ctx;
1854 LDAPMessage *res = NULL;
1855 ADS_MODLIST mods;
1856 const char *servicePrincipalName[1] = {NULL};
1857 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1858 char *dn_string = NULL;
1860 ret = ads_find_machine_acct(ads, &res, machine_name);
1861 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1862 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1863 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1864 ads_msgfree(ads, res);
1865 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1868 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1869 ctx = talloc_init("ads_clear_service_principal_names");
1870 if (!ctx) {
1871 ads_msgfree(ads, res);
1872 return ADS_ERROR(LDAP_NO_MEMORY);
1875 if (!(mods = ads_init_mods(ctx))) {
1876 talloc_destroy(ctx);
1877 ads_msgfree(ads, res);
1878 return ADS_ERROR(LDAP_NO_MEMORY);
1880 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1881 if (!ADS_ERR_OK(ret)) {
1882 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1883 ads_msgfree(ads, res);
1884 talloc_destroy(ctx);
1885 return ret;
1887 dn_string = ads_get_dn(ads, talloc_tos(), res);
1888 if (!dn_string) {
1889 talloc_destroy(ctx);
1890 ads_msgfree(ads, res);
1891 return ADS_ERROR(LDAP_NO_MEMORY);
1893 ret = ads_gen_mod(ads, dn_string, mods);
1894 TALLOC_FREE(dn_string);
1895 if (!ADS_ERR_OK(ret)) {
1896 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1897 machine_name));
1898 ads_msgfree(ads, res);
1899 talloc_destroy(ctx);
1900 return ret;
1903 ads_msgfree(ads, res);
1904 talloc_destroy(ctx);
1905 return ret;
1909 * This adds a service principal name to an existing computer account
1910 * (found by hostname) in AD.
1911 * @param ads An initialized ADS_STRUCT
1912 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1913 * @param my_fqdn The fully qualified DNS name of the machine
1914 * @param spn A string of the service principal to add, i.e. 'host'
1915 * @return 0 upon sucess, or non-zero if a failure occurs
1918 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1919 const char *my_fqdn, const char *spn)
1921 ADS_STATUS ret;
1922 TALLOC_CTX *ctx;
1923 LDAPMessage *res = NULL;
1924 char *psp1, *psp2;
1925 ADS_MODLIST mods;
1926 char *dn_string = NULL;
1927 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1929 ret = ads_find_machine_acct(ads, &res, machine_name);
1930 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1931 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1932 machine_name));
1933 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1934 spn, machine_name, ads->config.realm));
1935 ads_msgfree(ads, res);
1936 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1939 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1940 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1941 ads_msgfree(ads, res);
1942 return ADS_ERROR(LDAP_NO_MEMORY);
1945 /* add short name spn */
1947 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1948 talloc_destroy(ctx);
1949 ads_msgfree(ads, res);
1950 return ADS_ERROR(LDAP_NO_MEMORY);
1952 if (!strupper_m(psp1)) {
1953 ret = ADS_ERROR(LDAP_NO_MEMORY);
1954 goto out;
1957 if (!strlower_m(&psp1[strlen(spn)])) {
1958 ret = ADS_ERROR(LDAP_NO_MEMORY);
1959 goto out;
1961 servicePrincipalName[0] = psp1;
1963 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1964 psp1, machine_name));
1967 /* add fully qualified spn */
1969 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1970 ret = ADS_ERROR(LDAP_NO_MEMORY);
1971 goto out;
1973 if (!strupper_m(psp2)) {
1974 ret = ADS_ERROR(LDAP_NO_MEMORY);
1975 goto out;
1978 if (!strlower_m(&psp2[strlen(spn)])) {
1979 ret = ADS_ERROR(LDAP_NO_MEMORY);
1980 goto out;
1982 servicePrincipalName[1] = psp2;
1984 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1985 psp2, machine_name));
1987 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1988 ret = ADS_ERROR(LDAP_NO_MEMORY);
1989 goto out;
1992 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1993 if (!ADS_ERR_OK(ret)) {
1994 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1995 goto out;
1998 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1999 ret = ADS_ERROR(LDAP_NO_MEMORY);
2000 goto out;
2003 ret = ads_gen_mod(ads, dn_string, mods);
2004 if (!ADS_ERR_OK(ret)) {
2005 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2006 goto out;
2009 out:
2010 TALLOC_FREE( ctx );
2011 ads_msgfree(ads, res);
2012 return ret;
2016 * adds a machine account to the ADS server
2017 * @param ads An intialized ADS_STRUCT
2018 * @param machine_name - the NetBIOS machine name of this account.
2019 * @param account_type A number indicating the type of account to create
2020 * @param org_unit The LDAP path in which to place this account
2021 * @return 0 upon success, or non-zero otherwise
2024 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2025 const char *org_unit)
2027 ADS_STATUS ret;
2028 char *samAccountName, *controlstr;
2029 TALLOC_CTX *ctx;
2030 ADS_MODLIST mods;
2031 char *machine_escaped = NULL;
2032 char *new_dn;
2033 const char *objectClass[] = {"top", "person", "organizationalPerson",
2034 "user", "computer", NULL};
2035 LDAPMessage *res = NULL;
2036 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2037 UF_DONT_EXPIRE_PASSWD |\
2038 UF_ACCOUNTDISABLE );
2040 if (!(ctx = talloc_init("ads_add_machine_acct")))
2041 return ADS_ERROR(LDAP_NO_MEMORY);
2043 ret = ADS_ERROR(LDAP_NO_MEMORY);
2045 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2046 if (!machine_escaped) {
2047 goto done;
2050 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2051 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2053 if ( !new_dn || !samAccountName ) {
2054 goto done;
2057 #ifndef ENCTYPE_ARCFOUR_HMAC
2058 acct_control |= UF_USE_DES_KEY_ONLY;
2059 #endif
2061 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2062 goto done;
2065 if (!(mods = ads_init_mods(ctx))) {
2066 goto done;
2069 ads_mod_str(ctx, &mods, "cn", machine_name);
2070 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2071 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2072 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2074 ret = ads_gen_add(ads, new_dn, mods);
2076 done:
2077 SAFE_FREE(machine_escaped);
2078 ads_msgfree(ads, res);
2079 talloc_destroy(ctx);
2081 return ret;
2085 * move a machine account to another OU on the ADS server
2086 * @param ads - An intialized ADS_STRUCT
2087 * @param machine_name - the NetBIOS machine name of this account.
2088 * @param org_unit - The LDAP path in which to place this account
2089 * @param moved - whether we moved the machine account (optional)
2090 * @return 0 upon success, or non-zero otherwise
2093 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2094 const char *org_unit, bool *moved)
2096 ADS_STATUS rc;
2097 int ldap_status;
2098 LDAPMessage *res = NULL;
2099 char *filter = NULL;
2100 char *computer_dn = NULL;
2101 char *parent_dn;
2102 char *computer_rdn = NULL;
2103 bool need_move = False;
2105 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2106 rc = ADS_ERROR(LDAP_NO_MEMORY);
2107 goto done;
2110 /* Find pre-existing machine */
2111 rc = ads_search(ads, &res, filter, NULL);
2112 if (!ADS_ERR_OK(rc)) {
2113 goto done;
2116 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2117 if (!computer_dn) {
2118 rc = ADS_ERROR(LDAP_NO_MEMORY);
2119 goto done;
2122 parent_dn = ads_parent_dn(computer_dn);
2123 if (strequal(parent_dn, org_unit)) {
2124 goto done;
2127 need_move = True;
2129 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2130 rc = ADS_ERROR(LDAP_NO_MEMORY);
2131 goto done;
2134 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2135 org_unit, 1, NULL, NULL);
2136 rc = ADS_ERROR(ldap_status);
2138 done:
2139 ads_msgfree(ads, res);
2140 SAFE_FREE(filter);
2141 TALLOC_FREE(computer_dn);
2142 SAFE_FREE(computer_rdn);
2144 if (!ADS_ERR_OK(rc)) {
2145 need_move = False;
2148 if (moved) {
2149 *moved = need_move;
2152 return rc;
2156 dump a binary result from ldap
2158 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2160 int i, j;
2161 for (i=0; values[i]; i++) {
2162 printf("%s: ", field);
2163 for (j=0; j<values[i]->bv_len; j++) {
2164 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2166 printf("\n");
2170 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2172 int i;
2173 for (i=0; values[i]; i++) {
2174 NTSTATUS status;
2175 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2176 struct GUID guid;
2178 status = GUID_from_ndr_blob(&in, &guid);
2179 if (NT_STATUS_IS_OK(status)) {
2180 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2181 } else {
2182 printf("%s: INVALID GUID\n", field);
2188 dump a sid result from ldap
2190 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2192 int i;
2193 for (i=0; values[i]; i++) {
2194 struct dom_sid sid;
2195 fstring tmp;
2196 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2197 return;
2199 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2204 dump ntSecurityDescriptor
2206 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2208 TALLOC_CTX *frame = talloc_stackframe();
2209 struct security_descriptor *psd;
2210 NTSTATUS status;
2212 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2213 values[0]->bv_len, &psd);
2214 if (!NT_STATUS_IS_OK(status)) {
2215 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2216 nt_errstr(status)));
2217 TALLOC_FREE(frame);
2218 return;
2221 if (psd) {
2222 ads_disp_sd(ads, talloc_tos(), psd);
2225 TALLOC_FREE(frame);
2229 dump a string result from ldap
2231 static void dump_string(const char *field, char **values)
2233 int i;
2234 for (i=0; values[i]; i++) {
2235 printf("%s: %s\n", field, values[i]);
2240 dump a field from LDAP on stdout
2241 used for debugging
2244 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2246 const struct {
2247 const char *name;
2248 bool string;
2249 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2250 } handlers[] = {
2251 {"objectGUID", False, dump_guid},
2252 {"netbootGUID", False, dump_guid},
2253 {"nTSecurityDescriptor", False, dump_sd},
2254 {"dnsRecord", False, dump_binary},
2255 {"objectSid", False, dump_sid},
2256 {"tokenGroups", False, dump_sid},
2257 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2258 {"tokengroupsGlobalandUniversal", False, dump_sid},
2259 {"mS-DS-CreatorSID", False, dump_sid},
2260 {"msExchMailboxGuid", False, dump_guid},
2261 {NULL, True, NULL}
2263 int i;
2265 if (!field) { /* must be end of an entry */
2266 printf("\n");
2267 return False;
2270 for (i=0; handlers[i].name; i++) {
2271 if (strcasecmp_m(handlers[i].name, field) == 0) {
2272 if (!values) /* first time, indicate string or not */
2273 return handlers[i].string;
2274 handlers[i].handler(ads, field, (struct berval **) values);
2275 break;
2278 if (!handlers[i].name) {
2279 if (!values) /* first time, indicate string conversion */
2280 return True;
2281 dump_string(field, (char **)values);
2283 return False;
2287 * Dump a result from LDAP on stdout
2288 * used for debugging
2289 * @param ads connection to ads server
2290 * @param res Results to dump
2293 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2295 ads_process_results(ads, res, ads_dump_field, NULL);
2299 * Walk through results, calling a function for each entry found.
2300 * The function receives a field name, a berval * array of values,
2301 * and a data area passed through from the start. The function is
2302 * called once with null for field and values at the end of each
2303 * entry.
2304 * @param ads connection to ads server
2305 * @param res Results to process
2306 * @param fn Function for processing each result
2307 * @param data_area user-defined area to pass to function
2309 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2310 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2311 void *data_area)
2313 LDAPMessage *msg;
2314 TALLOC_CTX *ctx;
2315 size_t converted_size;
2317 if (!(ctx = talloc_init("ads_process_results")))
2318 return;
2320 for (msg = ads_first_entry(ads, res); msg;
2321 msg = ads_next_entry(ads, msg)) {
2322 char *utf8_field;
2323 BerElement *b;
2325 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2326 (LDAPMessage *)msg,&b);
2327 utf8_field;
2328 utf8_field=ldap_next_attribute(ads->ldap.ld,
2329 (LDAPMessage *)msg,b)) {
2330 struct berval **ber_vals;
2331 char **str_vals, **utf8_vals;
2332 char *field;
2333 bool string;
2335 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2336 &converted_size))
2338 DEBUG(0,("ads_process_results: "
2339 "pull_utf8_talloc failed: %s",
2340 strerror(errno)));
2343 string = fn(ads, field, NULL, data_area);
2345 if (string) {
2346 utf8_vals = ldap_get_values(ads->ldap.ld,
2347 (LDAPMessage *)msg, field);
2348 str_vals = ads_pull_strvals(ctx,
2349 (const char **) utf8_vals);
2350 fn(ads, field, (void **) str_vals, data_area);
2351 ldap_value_free(utf8_vals);
2352 } else {
2353 ber_vals = ldap_get_values_len(ads->ldap.ld,
2354 (LDAPMessage *)msg, field);
2355 fn(ads, field, (void **) ber_vals, data_area);
2357 ldap_value_free_len(ber_vals);
2359 ldap_memfree(utf8_field);
2361 ber_free(b, 0);
2362 talloc_free_children(ctx);
2363 fn(ads, NULL, NULL, data_area); /* completed an entry */
2366 talloc_destroy(ctx);
2370 * count how many replies are in a LDAPMessage
2371 * @param ads connection to ads server
2372 * @param res Results to count
2373 * @return number of replies
2375 int ads_count_replies(ADS_STRUCT *ads, void *res)
2377 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2381 * pull the first entry from a ADS result
2382 * @param ads connection to ads server
2383 * @param res Results of search
2384 * @return first entry from result
2386 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2388 return ldap_first_entry(ads->ldap.ld, res);
2392 * pull the next entry from a ADS result
2393 * @param ads connection to ads server
2394 * @param res Results of search
2395 * @return next entry from result
2397 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2399 return ldap_next_entry(ads->ldap.ld, res);
2403 * pull the first message from a ADS result
2404 * @param ads connection to ads server
2405 * @param res Results of search
2406 * @return first message from result
2408 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2410 return ldap_first_message(ads->ldap.ld, res);
2414 * pull the next message from a ADS result
2415 * @param ads connection to ads server
2416 * @param res Results of search
2417 * @return next message from result
2419 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2421 return ldap_next_message(ads->ldap.ld, res);
2425 * pull a single string from a ADS result
2426 * @param ads connection to ads server
2427 * @param mem_ctx TALLOC_CTX to use for allocating result string
2428 * @param msg Results of search
2429 * @param field Attribute to retrieve
2430 * @return Result string in talloc context
2432 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2433 const char *field)
2435 char **values;
2436 char *ret = NULL;
2437 char *ux_string;
2438 size_t converted_size;
2440 values = ldap_get_values(ads->ldap.ld, msg, field);
2441 if (!values)
2442 return NULL;
2444 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2445 &converted_size))
2447 ret = ux_string;
2449 ldap_value_free(values);
2450 return ret;
2454 * pull an array of strings from a ADS result
2455 * @param ads connection to ads server
2456 * @param mem_ctx TALLOC_CTX to use for allocating result string
2457 * @param msg Results of search
2458 * @param field Attribute to retrieve
2459 * @return Result strings in talloc context
2461 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2462 LDAPMessage *msg, const char *field,
2463 size_t *num_values)
2465 char **values;
2466 char **ret = NULL;
2467 int i;
2468 size_t converted_size;
2470 values = ldap_get_values(ads->ldap.ld, msg, field);
2471 if (!values)
2472 return NULL;
2474 *num_values = ldap_count_values(values);
2476 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2477 if (!ret) {
2478 ldap_value_free(values);
2479 return NULL;
2482 for (i=0;i<*num_values;i++) {
2483 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2484 &converted_size))
2486 ldap_value_free(values);
2487 return NULL;
2490 ret[i] = NULL;
2492 ldap_value_free(values);
2493 return ret;
2497 * pull an array of strings from a ADS result
2498 * (handle large multivalue attributes with range retrieval)
2499 * @param ads connection to ads server
2500 * @param mem_ctx TALLOC_CTX to use for allocating result string
2501 * @param msg Results of search
2502 * @param field Attribute to retrieve
2503 * @param current_strings strings returned by a previous call to this function
2504 * @param next_attribute The next query should ask for this attribute
2505 * @param num_values How many values did we get this time?
2506 * @param more_values Are there more values to get?
2507 * @return Result strings in talloc context
2509 char **ads_pull_strings_range(ADS_STRUCT *ads,
2510 TALLOC_CTX *mem_ctx,
2511 LDAPMessage *msg, const char *field,
2512 char **current_strings,
2513 const char **next_attribute,
2514 size_t *num_strings,
2515 bool *more_strings)
2517 char *attr;
2518 char *expected_range_attrib, *range_attr;
2519 BerElement *ptr = NULL;
2520 char **strings;
2521 char **new_strings;
2522 size_t num_new_strings;
2523 unsigned long int range_start;
2524 unsigned long int range_end;
2526 /* we might have been given the whole lot anyway */
2527 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2528 *more_strings = False;
2529 return strings;
2532 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2534 /* look for Range result */
2535 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2536 attr;
2537 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2538 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2539 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2540 range_attr = attr;
2541 break;
2543 ldap_memfree(attr);
2545 if (!attr) {
2546 ber_free(ptr, 0);
2547 /* nothing here - this field is just empty */
2548 *more_strings = False;
2549 return NULL;
2552 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2553 &range_start, &range_end) == 2) {
2554 *more_strings = True;
2555 } else {
2556 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2557 &range_start) == 1) {
2558 *more_strings = False;
2559 } else {
2560 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2561 range_attr));
2562 ldap_memfree(range_attr);
2563 *more_strings = False;
2564 return NULL;
2568 if ((*num_strings) != range_start) {
2569 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2570 " - aborting range retreival\n",
2571 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2572 ldap_memfree(range_attr);
2573 *more_strings = False;
2574 return NULL;
2577 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2579 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2580 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2581 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2582 range_attr, (unsigned long int)range_end - range_start + 1,
2583 (unsigned long int)num_new_strings));
2584 ldap_memfree(range_attr);
2585 *more_strings = False;
2586 return NULL;
2589 strings = talloc_realloc(mem_ctx, current_strings, char *,
2590 *num_strings + num_new_strings);
2592 if (strings == NULL) {
2593 ldap_memfree(range_attr);
2594 *more_strings = False;
2595 return NULL;
2598 if (new_strings && num_new_strings) {
2599 memcpy(&strings[*num_strings], new_strings,
2600 sizeof(*new_strings) * num_new_strings);
2603 (*num_strings) += num_new_strings;
2605 if (*more_strings) {
2606 *next_attribute = talloc_asprintf(mem_ctx,
2607 "%s;range=%d-*",
2608 field,
2609 (int)*num_strings);
2611 if (!*next_attribute) {
2612 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2613 ldap_memfree(range_attr);
2614 *more_strings = False;
2615 return NULL;
2619 ldap_memfree(range_attr);
2621 return strings;
2625 * pull a single uint32 from a ADS result
2626 * @param ads connection to ads server
2627 * @param msg Results of search
2628 * @param field Attribute to retrieve
2629 * @param v Pointer to int to store result
2630 * @return boolean inidicating success
2632 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2633 uint32 *v)
2635 char **values;
2637 values = ldap_get_values(ads->ldap.ld, msg, field);
2638 if (!values)
2639 return False;
2640 if (!values[0]) {
2641 ldap_value_free(values);
2642 return False;
2645 *v = atoi(values[0]);
2646 ldap_value_free(values);
2647 return True;
2651 * pull a single objectGUID from an ADS result
2652 * @param ads connection to ADS server
2653 * @param msg results of search
2654 * @param guid 37-byte area to receive text guid
2655 * @return boolean indicating success
2657 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2659 DATA_BLOB blob;
2660 NTSTATUS status;
2662 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2663 &blob)) {
2664 return false;
2667 status = GUID_from_ndr_blob(&blob, guid);
2668 talloc_free(blob.data);
2669 return NT_STATUS_IS_OK(status);
2674 * pull a single struct dom_sid from a ADS result
2675 * @param ads connection to ads server
2676 * @param msg Results of search
2677 * @param field Attribute to retrieve
2678 * @param sid Pointer to sid to store result
2679 * @return boolean inidicating success
2681 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2682 struct dom_sid *sid)
2684 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2688 * pull an array of struct dom_sids from a ADS result
2689 * @param ads connection to ads server
2690 * @param mem_ctx TALLOC_CTX for allocating sid array
2691 * @param msg Results of search
2692 * @param field Attribute to retrieve
2693 * @param sids pointer to sid array to allocate
2694 * @return the count of SIDs pulled
2696 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2697 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2699 struct berval **values;
2700 bool ret;
2701 int count, i;
2703 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2705 if (!values)
2706 return 0;
2708 for (i=0; values[i]; i++)
2709 /* nop */ ;
2711 if (i) {
2712 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2713 if (!(*sids)) {
2714 ldap_value_free_len(values);
2715 return 0;
2717 } else {
2718 (*sids) = NULL;
2721 count = 0;
2722 for (i=0; values[i]; i++) {
2723 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2724 if (ret) {
2725 DEBUG(10, ("pulling SID: %s\n",
2726 sid_string_dbg(&(*sids)[count])));
2727 count++;
2731 ldap_value_free_len(values);
2732 return count;
2736 * pull a struct security_descriptor from a ADS result
2737 * @param ads connection to ads server
2738 * @param mem_ctx TALLOC_CTX for allocating sid array
2739 * @param msg Results of search
2740 * @param field Attribute to retrieve
2741 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2742 * @return boolean inidicating success
2744 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2745 LDAPMessage *msg, const char *field,
2746 struct security_descriptor **sd)
2748 struct berval **values;
2749 bool ret = true;
2751 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2753 if (!values) return false;
2755 if (values[0]) {
2756 NTSTATUS status;
2757 status = unmarshall_sec_desc(mem_ctx,
2758 (uint8 *)values[0]->bv_val,
2759 values[0]->bv_len, sd);
2760 if (!NT_STATUS_IS_OK(status)) {
2761 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2762 nt_errstr(status)));
2763 ret = false;
2767 ldap_value_free_len(values);
2768 return ret;
2772 * in order to support usernames longer than 21 characters we need to
2773 * use both the sAMAccountName and the userPrincipalName attributes
2774 * It seems that not all users have the userPrincipalName attribute set
2776 * @param ads connection to ads server
2777 * @param mem_ctx TALLOC_CTX for allocating sid array
2778 * @param msg Results of search
2779 * @return the username
2781 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2782 LDAPMessage *msg)
2784 #if 0 /* JERRY */
2785 char *ret, *p;
2787 /* lookup_name() only works on the sAMAccountName to
2788 returning the username portion of userPrincipalName
2789 breaks winbindd_getpwnam() */
2791 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2792 if (ret && (p = strchr_m(ret, '@'))) {
2793 *p = 0;
2794 return ret;
2796 #endif
2797 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2802 * find the update serial number - this is the core of the ldap cache
2803 * @param ads connection to ads server
2804 * @param ads connection to ADS server
2805 * @param usn Pointer to retrieved update serial number
2806 * @return status of search
2808 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2810 const char *attrs[] = {"highestCommittedUSN", NULL};
2811 ADS_STATUS status;
2812 LDAPMessage *res;
2814 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2815 if (!ADS_ERR_OK(status))
2816 return status;
2818 if (ads_count_replies(ads, res) != 1) {
2819 ads_msgfree(ads, res);
2820 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2823 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2824 ads_msgfree(ads, res);
2825 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2828 ads_msgfree(ads, res);
2829 return ADS_SUCCESS;
2832 /* parse a ADS timestring - typical string is
2833 '20020917091222.0Z0' which means 09:12.22 17th September
2834 2002, timezone 0 */
2835 static time_t ads_parse_time(const char *str)
2837 struct tm tm;
2839 ZERO_STRUCT(tm);
2841 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2842 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2843 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2844 return 0;
2846 tm.tm_year -= 1900;
2847 tm.tm_mon -= 1;
2849 return timegm(&tm);
2852 /********************************************************************
2853 ********************************************************************/
2855 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2857 const char *attrs[] = {"currentTime", NULL};
2858 ADS_STATUS status;
2859 LDAPMessage *res;
2860 char *timestr;
2861 TALLOC_CTX *ctx;
2862 ADS_STRUCT *ads_s = ads;
2864 if (!(ctx = talloc_init("ads_current_time"))) {
2865 return ADS_ERROR(LDAP_NO_MEMORY);
2868 /* establish a new ldap tcp session if necessary */
2870 if ( !ads->ldap.ld ) {
2871 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2872 ads->server.ldap_server )) == NULL )
2874 goto done;
2876 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2877 status = ads_connect( ads_s );
2878 if ( !ADS_ERR_OK(status))
2879 goto done;
2882 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2883 if (!ADS_ERR_OK(status)) {
2884 goto done;
2887 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2888 if (!timestr) {
2889 ads_msgfree(ads_s, res);
2890 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2891 goto done;
2894 /* but save the time and offset in the original ADS_STRUCT */
2896 ads->config.current_time = ads_parse_time(timestr);
2898 if (ads->config.current_time != 0) {
2899 ads->auth.time_offset = ads->config.current_time - time(NULL);
2900 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2903 ads_msgfree(ads, res);
2905 status = ADS_SUCCESS;
2907 done:
2908 /* free any temporary ads connections */
2909 if ( ads_s != ads ) {
2910 ads_destroy( &ads_s );
2912 talloc_destroy(ctx);
2914 return status;
2917 /********************************************************************
2918 ********************************************************************/
2920 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2922 const char *attrs[] = {"domainFunctionality", NULL};
2923 ADS_STATUS status;
2924 LDAPMessage *res;
2925 ADS_STRUCT *ads_s = ads;
2927 *val = DS_DOMAIN_FUNCTION_2000;
2929 /* establish a new ldap tcp session if necessary */
2931 if ( !ads->ldap.ld ) {
2932 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2933 ads->server.ldap_server )) == NULL )
2935 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2936 goto done;
2938 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2939 status = ads_connect( ads_s );
2940 if ( !ADS_ERR_OK(status))
2941 goto done;
2944 /* If the attribute does not exist assume it is a Windows 2000
2945 functional domain */
2947 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2948 if (!ADS_ERR_OK(status)) {
2949 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2950 status = ADS_SUCCESS;
2952 goto done;
2955 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2956 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2958 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2961 ads_msgfree(ads, res);
2963 done:
2964 /* free any temporary ads connections */
2965 if ( ads_s != ads ) {
2966 ads_destroy( &ads_s );
2969 return status;
2973 * find the domain sid for our domain
2974 * @param ads connection to ads server
2975 * @param sid Pointer to domain sid
2976 * @return status of search
2978 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2980 const char *attrs[] = {"objectSid", NULL};
2981 LDAPMessage *res;
2982 ADS_STATUS rc;
2984 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2985 attrs, &res);
2986 if (!ADS_ERR_OK(rc)) return rc;
2987 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2988 ads_msgfree(ads, res);
2989 return ADS_ERROR_SYSTEM(ENOENT);
2991 ads_msgfree(ads, res);
2993 return ADS_SUCCESS;
2997 * find our site name
2998 * @param ads connection to ads server
2999 * @param mem_ctx Pointer to talloc context
3000 * @param site_name Pointer to the sitename
3001 * @return status of search
3003 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3005 ADS_STATUS status;
3006 LDAPMessage *res;
3007 const char *dn, *service_name;
3008 const char *attrs[] = { "dsServiceName", NULL };
3010 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3011 if (!ADS_ERR_OK(status)) {
3012 return status;
3015 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3016 if (service_name == NULL) {
3017 ads_msgfree(ads, res);
3018 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3021 ads_msgfree(ads, res);
3023 /* go up three levels */
3024 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3025 if (dn == NULL) {
3026 return ADS_ERROR(LDAP_NO_MEMORY);
3029 *site_name = talloc_strdup(mem_ctx, dn);
3030 if (*site_name == NULL) {
3031 return ADS_ERROR(LDAP_NO_MEMORY);
3034 return status;
3036 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3041 * find the site dn where a machine resides
3042 * @param ads connection to ads server
3043 * @param mem_ctx Pointer to talloc context
3044 * @param computer_name name of the machine
3045 * @param site_name Pointer to the sitename
3046 * @return status of search
3048 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3050 ADS_STATUS status;
3051 LDAPMessage *res;
3052 const char *parent, *filter;
3053 char *config_context = NULL;
3054 char *dn;
3056 /* shortcut a query */
3057 if (strequal(computer_name, ads->config.ldap_server_name)) {
3058 return ads_site_dn(ads, mem_ctx, site_dn);
3061 status = ads_config_path(ads, mem_ctx, &config_context);
3062 if (!ADS_ERR_OK(status)) {
3063 return status;
3066 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3067 if (filter == NULL) {
3068 return ADS_ERROR(LDAP_NO_MEMORY);
3071 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3072 filter, NULL, &res);
3073 if (!ADS_ERR_OK(status)) {
3074 return status;
3077 if (ads_count_replies(ads, res) != 1) {
3078 ads_msgfree(ads, res);
3079 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3082 dn = ads_get_dn(ads, mem_ctx, res);
3083 if (dn == NULL) {
3084 ads_msgfree(ads, res);
3085 return ADS_ERROR(LDAP_NO_MEMORY);
3088 /* go up three levels */
3089 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3090 if (parent == NULL) {
3091 ads_msgfree(ads, res);
3092 TALLOC_FREE(dn);
3093 return ADS_ERROR(LDAP_NO_MEMORY);
3096 *site_dn = talloc_strdup(mem_ctx, parent);
3097 if (*site_dn == NULL) {
3098 ads_msgfree(ads, res);
3099 TALLOC_FREE(dn);
3100 return ADS_ERROR(LDAP_NO_MEMORY);
3103 TALLOC_FREE(dn);
3104 ads_msgfree(ads, res);
3106 return status;
3110 * get the upn suffixes for a domain
3111 * @param ads connection to ads server
3112 * @param mem_ctx Pointer to talloc context
3113 * @param suffixes Pointer to an array of suffixes
3114 * @param num_suffixes Pointer to the number of suffixes
3115 * @return status of search
3117 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3119 ADS_STATUS status;
3120 LDAPMessage *res;
3121 const char *base;
3122 char *config_context = NULL;
3123 const char *attrs[] = { "uPNSuffixes", NULL };
3125 status = ads_config_path(ads, mem_ctx, &config_context);
3126 if (!ADS_ERR_OK(status)) {
3127 return status;
3130 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3131 if (base == NULL) {
3132 return ADS_ERROR(LDAP_NO_MEMORY);
3135 status = ads_search_dn(ads, &res, base, attrs);
3136 if (!ADS_ERR_OK(status)) {
3137 return status;
3140 if (ads_count_replies(ads, res) != 1) {
3141 ads_msgfree(ads, res);
3142 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3145 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3146 if ((*suffixes) == NULL) {
3147 ads_msgfree(ads, res);
3148 return ADS_ERROR(LDAP_NO_MEMORY);
3151 ads_msgfree(ads, res);
3153 return status;
3157 * get the joinable ous for a domain
3158 * @param ads connection to ads server
3159 * @param mem_ctx Pointer to talloc context
3160 * @param ous Pointer to an array of ous
3161 * @param num_ous Pointer to the number of ous
3162 * @return status of search
3164 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3165 TALLOC_CTX *mem_ctx,
3166 char ***ous,
3167 size_t *num_ous)
3169 ADS_STATUS status;
3170 LDAPMessage *res = NULL;
3171 LDAPMessage *msg = NULL;
3172 const char *attrs[] = { "dn", NULL };
3173 int count = 0;
3175 status = ads_search(ads, &res,
3176 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3177 attrs);
3178 if (!ADS_ERR_OK(status)) {
3179 return status;
3182 count = ads_count_replies(ads, res);
3183 if (count < 1) {
3184 ads_msgfree(ads, res);
3185 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3188 for (msg = ads_first_entry(ads, res); msg;
3189 msg = ads_next_entry(ads, msg)) {
3191 char *dn = NULL;
3193 dn = ads_get_dn(ads, talloc_tos(), msg);
3194 if (!dn) {
3195 ads_msgfree(ads, res);
3196 return ADS_ERROR(LDAP_NO_MEMORY);
3199 if (!add_string_to_array(mem_ctx, dn,
3200 (const char ***)ous,
3201 (int *)num_ous)) {
3202 TALLOC_FREE(dn);
3203 ads_msgfree(ads, res);
3204 return ADS_ERROR(LDAP_NO_MEMORY);
3207 TALLOC_FREE(dn);
3210 ads_msgfree(ads, res);
3212 return status;
3217 * pull a struct dom_sid from an extended dn string
3218 * @param mem_ctx TALLOC_CTX
3219 * @param extended_dn string
3220 * @param flags string type of extended_dn
3221 * @param sid pointer to a struct dom_sid
3222 * @return NT_STATUS_OK on success,
3223 * NT_INVALID_PARAMETER on error,
3224 * NT_STATUS_NOT_FOUND if no SID present
3226 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3227 const char *extended_dn,
3228 enum ads_extended_dn_flags flags,
3229 struct dom_sid *sid)
3231 char *p, *q, *dn;
3233 if (!extended_dn) {
3234 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3237 /* otherwise extended_dn gets stripped off */
3238 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3239 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3242 * ADS_EXTENDED_DN_HEX_STRING:
3243 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3245 * ADS_EXTENDED_DN_STRING (only with w2k3):
3246 * <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
3248 * Object with no SID, such as an Exchange Public Folder
3249 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3252 p = strchr(dn, ';');
3253 if (!p) {
3254 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3257 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3258 DEBUG(5,("No SID present in extended dn\n"));
3259 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3262 p += strlen(";<SID=");
3264 q = strchr(p, '>');
3265 if (!q) {
3266 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3269 *q = '\0';
3271 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3273 switch (flags) {
3275 case ADS_EXTENDED_DN_STRING:
3276 if (!string_to_sid(sid, p)) {
3277 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3279 break;
3280 case ADS_EXTENDED_DN_HEX_STRING: {
3281 fstring buf;
3282 size_t buf_len;
3284 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3285 if (buf_len == 0) {
3286 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3289 if (!sid_parse(buf, buf_len, sid)) {
3290 DEBUG(10,("failed to parse sid\n"));
3291 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3293 break;
3295 default:
3296 DEBUG(10,("unknown extended dn format\n"));
3297 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3300 return ADS_ERROR_NT(NT_STATUS_OK);
3303 /********************************************************************
3304 ********************************************************************/
3306 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3308 LDAPMessage *res = NULL;
3309 ADS_STATUS status;
3310 int count = 0;
3311 char *name = NULL;
3313 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3314 if (!ADS_ERR_OK(status)) {
3315 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3316 lp_netbios_name()));
3317 goto out;
3320 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3321 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3322 goto out;
3325 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3326 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3329 out:
3330 ads_msgfree(ads, res);
3332 return name;
3335 /********************************************************************
3336 ********************************************************************/
3338 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3340 LDAPMessage *res = NULL;
3341 ADS_STATUS status;
3342 int count = 0;
3343 char *name = NULL;
3345 status = ads_find_machine_acct(ads, &res, machine_name);
3346 if (!ADS_ERR_OK(status)) {
3347 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3348 lp_netbios_name()));
3349 goto out;
3352 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3353 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3354 goto out;
3357 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3358 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3361 out:
3362 ads_msgfree(ads, res);
3364 return name;
3367 /********************************************************************
3368 ********************************************************************/
3370 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3372 LDAPMessage *res = NULL;
3373 ADS_STATUS status;
3374 int count = 0;
3375 char *name = NULL;
3377 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3378 if (!ADS_ERR_OK(status)) {
3379 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3380 lp_netbios_name()));
3381 goto out;
3384 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3385 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3386 goto out;
3389 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3390 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3393 out:
3394 ads_msgfree(ads, res);
3396 return name;
3399 #if 0
3401 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3404 * Join a machine to a realm
3405 * Creates the machine account and sets the machine password
3406 * @param ads connection to ads server
3407 * @param machine name of host to add
3408 * @param org_unit Organizational unit to place machine in
3409 * @return status of join
3411 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3412 uint32 account_type, const char *org_unit)
3414 ADS_STATUS status;
3415 LDAPMessage *res = NULL;
3416 char *machine;
3418 /* machine name must be lowercase */
3419 machine = SMB_STRDUP(machine_name);
3420 strlower_m(machine);
3423 status = ads_find_machine_acct(ads, (void **)&res, machine);
3424 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3425 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3426 status = ads_leave_realm(ads, machine);
3427 if (!ADS_ERR_OK(status)) {
3428 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3429 machine, ads->config.realm));
3430 return status;
3434 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3435 if (!ADS_ERR_OK(status)) {
3436 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3437 SAFE_FREE(machine);
3438 return status;
3441 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3442 if (!ADS_ERR_OK(status)) {
3443 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3444 SAFE_FREE(machine);
3445 return status;
3448 SAFE_FREE(machine);
3449 ads_msgfree(ads, res);
3451 return status;
3453 #endif
3456 * Delete a machine from the realm
3457 * @param ads connection to ads server
3458 * @param hostname Machine to remove
3459 * @return status of delete
3461 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3463 ADS_STATUS status;
3464 void *msg;
3465 LDAPMessage *res;
3466 char *hostnameDN, *host;
3467 int rc;
3468 LDAPControl ldap_control;
3469 LDAPControl * pldap_control[2] = {NULL, NULL};
3471 pldap_control[0] = &ldap_control;
3472 memset(&ldap_control, 0, sizeof(LDAPControl));
3473 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3475 /* hostname must be lowercase */
3476 host = SMB_STRDUP(hostname);
3477 if (!strlower_m(host)) {
3478 SAFE_FREE(host);
3479 return ADS_ERROR_SYSTEM(EINVAL);
3482 status = ads_find_machine_acct(ads, &res, host);
3483 if (!ADS_ERR_OK(status)) {
3484 DEBUG(0, ("Host account for %s does not exist.\n", host));
3485 SAFE_FREE(host);
3486 return status;
3489 msg = ads_first_entry(ads, res);
3490 if (!msg) {
3491 SAFE_FREE(host);
3492 return ADS_ERROR_SYSTEM(ENOENT);
3495 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3496 if (hostnameDN == NULL) {
3497 SAFE_FREE(host);
3498 return ADS_ERROR_SYSTEM(ENOENT);
3501 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3502 if (rc) {
3503 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3504 }else {
3505 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3508 if (rc != LDAP_SUCCESS) {
3509 const char *attrs[] = { "cn", NULL };
3510 LDAPMessage *msg_sub;
3512 /* we only search with scope ONE, we do not expect any further
3513 * objects to be created deeper */
3515 status = ads_do_search_retry(ads, hostnameDN,
3516 LDAP_SCOPE_ONELEVEL,
3517 "(objectclass=*)", attrs, &res);
3519 if (!ADS_ERR_OK(status)) {
3520 SAFE_FREE(host);
3521 TALLOC_FREE(hostnameDN);
3522 return status;
3525 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3526 msg_sub = ads_next_entry(ads, msg_sub)) {
3528 char *dn = NULL;
3530 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3531 SAFE_FREE(host);
3532 TALLOC_FREE(hostnameDN);
3533 return ADS_ERROR(LDAP_NO_MEMORY);
3536 status = ads_del_dn(ads, dn);
3537 if (!ADS_ERR_OK(status)) {
3538 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3539 SAFE_FREE(host);
3540 TALLOC_FREE(dn);
3541 TALLOC_FREE(hostnameDN);
3542 return status;
3545 TALLOC_FREE(dn);
3548 /* there should be no subordinate objects anymore */
3549 status = ads_do_search_retry(ads, hostnameDN,
3550 LDAP_SCOPE_ONELEVEL,
3551 "(objectclass=*)", attrs, &res);
3553 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3554 SAFE_FREE(host);
3555 TALLOC_FREE(hostnameDN);
3556 return status;
3559 /* delete hostnameDN now */
3560 status = ads_del_dn(ads, hostnameDN);
3561 if (!ADS_ERR_OK(status)) {
3562 SAFE_FREE(host);
3563 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3564 TALLOC_FREE(hostnameDN);
3565 return status;
3569 TALLOC_FREE(hostnameDN);
3571 status = ads_find_machine_acct(ads, &res, host);
3572 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3573 DEBUG(3, ("Failed to remove host account.\n"));
3574 SAFE_FREE(host);
3575 return status;
3578 SAFE_FREE(host);
3579 return status;
3583 * pull all token-sids from an LDAP dn
3584 * @param ads connection to ads server
3585 * @param mem_ctx TALLOC_CTX for allocating sid array
3586 * @param dn of LDAP object
3587 * @param user_sid pointer to struct dom_sid (objectSid)
3588 * @param primary_group_sid pointer to struct dom_sid (self composed)
3589 * @param sids pointer to sid array to allocate
3590 * @param num_sids counter of SIDs pulled
3591 * @return status of token query
3593 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3594 TALLOC_CTX *mem_ctx,
3595 const char *dn,
3596 struct dom_sid *user_sid,
3597 struct dom_sid *primary_group_sid,
3598 struct dom_sid **sids,
3599 size_t *num_sids)
3601 ADS_STATUS status;
3602 LDAPMessage *res = NULL;
3603 int count = 0;
3604 size_t tmp_num_sids;
3605 struct dom_sid *tmp_sids;
3606 struct dom_sid tmp_user_sid;
3607 struct dom_sid tmp_primary_group_sid;
3608 uint32 pgid;
3609 const char *attrs[] = {
3610 "objectSid",
3611 "tokenGroups",
3612 "primaryGroupID",
3613 NULL
3616 status = ads_search_retry_dn(ads, &res, dn, attrs);
3617 if (!ADS_ERR_OK(status)) {
3618 return status;
3621 count = ads_count_replies(ads, res);
3622 if (count != 1) {
3623 ads_msgfree(ads, res);
3624 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3627 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3628 ads_msgfree(ads, res);
3629 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3632 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3633 ads_msgfree(ads, res);
3634 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3638 /* hack to compose the primary group sid without knowing the
3639 * domsid */
3641 struct dom_sid domsid;
3643 sid_copy(&domsid, &tmp_user_sid);
3645 if (!sid_split_rid(&domsid, NULL)) {
3646 ads_msgfree(ads, res);
3647 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3650 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3651 ads_msgfree(ads, res);
3652 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3656 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3658 if (tmp_num_sids == 0 || !tmp_sids) {
3659 ads_msgfree(ads, res);
3660 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3663 if (num_sids) {
3664 *num_sids = tmp_num_sids;
3667 if (sids) {
3668 *sids = tmp_sids;
3671 if (user_sid) {
3672 *user_sid = tmp_user_sid;
3675 if (primary_group_sid) {
3676 *primary_group_sid = tmp_primary_group_sid;
3679 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3681 ads_msgfree(ads, res);
3682 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3686 * Find a sAMAccoutName in LDAP
3687 * @param ads connection to ads server
3688 * @param mem_ctx TALLOC_CTX for allocating sid array
3689 * @param samaccountname to search
3690 * @param uac_ret uint32 pointer userAccountControl attribute value
3691 * @param dn_ret pointer to dn
3692 * @return status of token query
3694 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3695 TALLOC_CTX *mem_ctx,
3696 const char *samaccountname,
3697 uint32 *uac_ret,
3698 const char **dn_ret)
3700 ADS_STATUS status;
3701 const char *attrs[] = { "userAccountControl", NULL };
3702 const char *filter;
3703 LDAPMessage *res = NULL;
3704 char *dn = NULL;
3705 uint32 uac = 0;
3707 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3708 samaccountname);
3709 if (filter == NULL) {
3710 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3711 goto out;
3714 status = ads_do_search_all(ads, ads->config.bind_path,
3715 LDAP_SCOPE_SUBTREE,
3716 filter, attrs, &res);
3718 if (!ADS_ERR_OK(status)) {
3719 goto out;
3722 if (ads_count_replies(ads, res) != 1) {
3723 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3724 goto out;
3727 dn = ads_get_dn(ads, talloc_tos(), res);
3728 if (dn == NULL) {
3729 status = ADS_ERROR(LDAP_NO_MEMORY);
3730 goto out;
3733 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3734 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3735 goto out;
3738 if (uac_ret) {
3739 *uac_ret = uac;
3742 if (dn_ret) {
3743 *dn_ret = talloc_strdup(mem_ctx, dn);
3744 if (!*dn_ret) {
3745 status = ADS_ERROR(LDAP_NO_MEMORY);
3746 goto out;
3749 out:
3750 TALLOC_FREE(dn);
3751 ads_msgfree(ads, res);
3753 return status;
3757 * find our configuration path
3758 * @param ads connection to ads server
3759 * @param mem_ctx Pointer to talloc context
3760 * @param config_path Pointer to the config path
3761 * @return status of search
3763 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3764 TALLOC_CTX *mem_ctx,
3765 char **config_path)
3767 ADS_STATUS status;
3768 LDAPMessage *res = NULL;
3769 const char *config_context = NULL;
3770 const char *attrs[] = { "configurationNamingContext", NULL };
3772 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3773 "(objectclass=*)", attrs, &res);
3774 if (!ADS_ERR_OK(status)) {
3775 return status;
3778 config_context = ads_pull_string(ads, mem_ctx, res,
3779 "configurationNamingContext");
3780 ads_msgfree(ads, res);
3781 if (!config_context) {
3782 return ADS_ERROR(LDAP_NO_MEMORY);
3785 if (config_path) {
3786 *config_path = talloc_strdup(mem_ctx, config_context);
3787 if (!*config_path) {
3788 return ADS_ERROR(LDAP_NO_MEMORY);
3792 return ADS_ERROR(LDAP_SUCCESS);
3796 * find the displayName of an extended right
3797 * @param ads connection to ads server
3798 * @param config_path The config path
3799 * @param mem_ctx Pointer to talloc context
3800 * @param GUID struct of the rightsGUID
3801 * @return status of search
3803 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3804 const char *config_path,
3805 TALLOC_CTX *mem_ctx,
3806 const struct GUID *rights_guid)
3808 ADS_STATUS rc;
3809 LDAPMessage *res = NULL;
3810 char *expr = NULL;
3811 const char *attrs[] = { "displayName", NULL };
3812 const char *result = NULL;
3813 const char *path;
3815 if (!ads || !mem_ctx || !rights_guid) {
3816 goto done;
3819 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3820 GUID_string(mem_ctx, rights_guid));
3821 if (!expr) {
3822 goto done;
3825 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3826 if (!path) {
3827 goto done;
3830 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3831 expr, attrs, &res);
3832 if (!ADS_ERR_OK(rc)) {
3833 goto done;
3836 if (ads_count_replies(ads, res) != 1) {
3837 goto done;
3840 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3842 done:
3843 ads_msgfree(ads, res);
3844 return result;
3848 * verify or build and verify an account ou
3849 * @param mem_ctx Pointer to talloc context
3850 * @param ads connection to ads server
3851 * @param account_ou
3852 * @return status of search
3855 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3856 ADS_STRUCT *ads,
3857 const char **account_ou)
3859 char **exploded_dn;
3860 const char *name;
3861 char *ou_string;
3863 exploded_dn = ldap_explode_dn(*account_ou, 0);
3864 if (exploded_dn) {
3865 ldap_value_free(exploded_dn);
3866 return ADS_SUCCESS;
3869 ou_string = ads_ou_string(ads, *account_ou);
3870 if (!ou_string) {
3871 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3874 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3875 ads->config.bind_path);
3876 SAFE_FREE(ou_string);
3878 if (!name) {
3879 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3882 exploded_dn = ldap_explode_dn(name, 0);
3883 if (!exploded_dn) {
3884 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3886 ldap_value_free(exploded_dn);
3888 *account_ou = name;
3889 return ADS_SUCCESS;
3892 #endif