s3-libads: Fix memory leaks in ads_build_path().
[Samba.git] / source3 / libads / ldap.c
blob12aacd4662069e262971aedce41a146f2d986ccb
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(talloc_tos(), 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 TALLOC_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 TALLOC_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 TALLOC_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(frame, realm)) == NULL) {
567 ads_lookup_site();
568 sitename = sitename_fetch(frame, 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 if (!NT_STATUS_IS_OK(nt_status)) {
585 ads_status = ADS_ERROR_NT(nt_status);
586 goto done;
589 /* Loop until we get a successful connection or have gone
590 through them all. When connecting a GC server, make sure that
591 the realm is the server's DNS name and not the forest root */
593 for (i=0; i<num_gcs; i++) {
594 ads->server.gc = true;
595 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
596 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
597 ads_status = ads_connect(ads);
598 if (ADS_ERR_OK(ads_status)) {
599 /* Reset the bind_dn to "". A Global Catalog server
600 may host multiple domain trees in a forest.
601 Windows 2003 GC server will accept "" as the search
602 path to imply search all domain trees in the forest */
604 SAFE_FREE(ads->config.bind_path);
605 ads->config.bind_path = SMB_STRDUP("");
608 goto done;
610 SAFE_FREE(ads->server.ldap_server);
611 SAFE_FREE(ads->server.realm);
614 TALLOC_FREE(gcs_list);
615 num_gcs = 0;
616 } while (!done);
618 done:
619 talloc_destroy(frame);
621 return ads_status;
626 * Connect to the LDAP server
627 * @param ads Pointer to an existing ADS_STRUCT
628 * @return status of connection
630 ADS_STATUS ads_connect(ADS_STRUCT *ads)
632 int version = LDAP_VERSION3;
633 ADS_STATUS status;
634 NTSTATUS ntstatus;
635 char addr[INET6_ADDRSTRLEN];
637 ZERO_STRUCT(ads->ldap);
638 ads->ldap.last_attempt = time_mono(NULL);
639 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
641 /* try with a user specified server */
643 if (DEBUGLEVEL >= 11) {
644 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
645 DEBUG(11,("ads_connect: entering\n"));
646 DEBUGADD(11,("%s\n", s));
647 TALLOC_FREE(s);
650 if (ads->server.ldap_server)
652 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
653 goto got_connection;
656 /* The choice of which GC use is handled one level up in
657 ads_connect_gc(). If we continue on from here with
658 ads_find_dc() we will get GC searches on port 389 which
659 doesn't work. --jerry */
661 if (ads->server.gc == true) {
662 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
666 ntstatus = ads_find_dc(ads);
667 if (NT_STATUS_IS_OK(ntstatus)) {
668 goto got_connection;
671 status = ADS_ERROR_NT(ntstatus);
672 goto out;
674 got_connection:
676 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
677 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
679 if (!ads->auth.user_name) {
680 /* Must use the userPrincipalName value here or sAMAccountName
681 and not servicePrincipalName; found by Guenther Deschner */
683 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
684 DEBUG(0,("ads_connect: asprintf fail.\n"));
685 ads->auth.user_name = NULL;
689 if (!ads->auth.realm) {
690 ads->auth.realm = SMB_STRDUP(ads->config.realm);
693 if (!ads->auth.kdc_server) {
694 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
695 ads->auth.kdc_server = SMB_STRDUP(addr);
698 /* If the caller() requested no LDAP bind, then we are done */
700 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
701 status = ADS_SUCCESS;
702 goto out;
705 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
706 if (!ads->ldap.mem_ctx) {
707 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
708 goto out;
711 /* Otherwise setup the TCP LDAP session */
713 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
714 &ads->ldap.ss,
715 ads->ldap.port, lp_ldap_timeout());
716 if (ads->ldap.ld == NULL) {
717 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
718 goto out;
720 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
722 /* cache the successful connection for workgroup and realm */
723 if (ads_closest_dc(ads)) {
724 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
725 saf_store( ads->server.realm, ads->config.ldap_server_name);
728 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
730 if ( lp_ldap_ssl_ads() ) {
731 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
732 if (!ADS_ERR_OK(status)) {
733 goto out;
737 /* fill in the current time and offsets */
739 status = ads_current_time( ads );
740 if ( !ADS_ERR_OK(status) ) {
741 goto out;
744 /* Now do the bind */
746 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
747 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
748 goto out;
751 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
752 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
753 goto out;
756 status = ads_sasl_bind(ads);
758 out:
759 if (DEBUGLEVEL >= 11) {
760 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
761 DEBUG(11,("ads_connect: leaving with: %s\n",
762 ads_errstr(status)));
763 DEBUGADD(11,("%s\n", s));
764 TALLOC_FREE(s);
767 return status;
771 * Connect to the LDAP server using given credentials
772 * @param ads Pointer to an existing ADS_STRUCT
773 * @return status of connection
775 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
777 ads->auth.flags |= ADS_AUTH_USER_CREDS;
779 return ads_connect(ads);
783 * Disconnect the LDAP server
784 * @param ads Pointer to an existing ADS_STRUCT
786 void ads_disconnect(ADS_STRUCT *ads)
788 if (ads->ldap.ld) {
789 ldap_unbind(ads->ldap.ld);
790 ads->ldap.ld = NULL;
792 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
793 ads->ldap.wrap_ops->disconnect(ads);
795 if (ads->ldap.mem_ctx) {
796 talloc_free(ads->ldap.mem_ctx);
798 ZERO_STRUCT(ads->ldap);
802 Duplicate a struct berval into talloc'ed memory
804 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
806 struct berval *value;
808 if (!in_val) return NULL;
810 value = talloc_zero(ctx, struct berval);
811 if (value == NULL)
812 return NULL;
813 if (in_val->bv_len == 0) return value;
815 value->bv_len = in_val->bv_len;
816 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
817 in_val->bv_len);
818 return value;
822 Make a values list out of an array of (struct berval *)
824 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
825 const struct berval **in_vals)
827 struct berval **values;
828 int i;
830 if (!in_vals) return NULL;
831 for (i=0; in_vals[i]; i++)
832 ; /* count values */
833 values = talloc_zero_array(ctx, struct berval *, i+1);
834 if (!values) return NULL;
836 for (i=0; in_vals[i]; i++) {
837 values[i] = dup_berval(ctx, in_vals[i]);
839 return values;
843 UTF8-encode a values list out of an array of (char *)
845 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
847 char **values;
848 int i;
849 size_t size;
851 if (!in_vals) return NULL;
852 for (i=0; in_vals[i]; i++)
853 ; /* count values */
854 values = talloc_zero_array(ctx, char *, i+1);
855 if (!values) return NULL;
857 for (i=0; in_vals[i]; i++) {
858 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
859 TALLOC_FREE(values);
860 return NULL;
863 return values;
867 Pull a (char *) array out of a UTF8-encoded values list
869 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
871 char **values;
872 int i;
873 size_t converted_size;
875 if (!in_vals) return NULL;
876 for (i=0; in_vals[i]; i++)
877 ; /* count values */
878 values = talloc_zero_array(ctx, char *, i+1);
879 if (!values) return NULL;
881 for (i=0; in_vals[i]; i++) {
882 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
883 &converted_size)) {
884 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
885 "%s", strerror(errno)));
888 return values;
892 * Do a search with paged results. cookie must be null on the first
893 * call, and then returned on each subsequent call. It will be null
894 * again when the entire search is complete
895 * @param ads connection to ads server
896 * @param bind_path Base dn for the search
897 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
898 * @param expr Search expression - specified in local charset
899 * @param attrs Attributes to retrieve - specified in utf8 or ascii
900 * @param res ** which will contain results - free res* with ads_msgfree()
901 * @param count Number of entries retrieved on this page
902 * @param cookie The paged results cookie to be returned on subsequent calls
903 * @return status of search
905 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
906 const char *bind_path,
907 int scope, const char *expr,
908 const char **attrs, void *args,
909 LDAPMessage **res,
910 int *count, struct berval **cookie)
912 int rc, i, version;
913 char *utf8_expr, *utf8_path, **search_attrs = NULL;
914 size_t converted_size;
915 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
916 BerElement *cookie_be = NULL;
917 struct berval *cookie_bv= NULL;
918 BerElement *ext_be = NULL;
919 struct berval *ext_bv= NULL;
921 TALLOC_CTX *ctx;
922 ads_control *external_control = (ads_control *) args;
924 *res = NULL;
926 if (!(ctx = talloc_init("ads_do_paged_search_args")))
927 return ADS_ERROR(LDAP_NO_MEMORY);
929 /* 0 means the conversion worked but the result was empty
930 so we only fail if it's -1. In any case, it always
931 at least nulls out the dest */
932 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
933 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
935 rc = LDAP_NO_MEMORY;
936 goto done;
939 if (!attrs || !(*attrs))
940 search_attrs = NULL;
941 else {
942 /* This would be the utf8-encoded version...*/
943 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
944 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
945 rc = LDAP_NO_MEMORY;
946 goto done;
950 /* Paged results only available on ldap v3 or later */
951 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
952 if (version < LDAP_VERSION3) {
953 rc = LDAP_NOT_SUPPORTED;
954 goto done;
957 cookie_be = ber_alloc_t(LBER_USE_DER);
958 if (*cookie) {
959 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
960 ber_bvfree(*cookie); /* don't need it from last time */
961 *cookie = NULL;
962 } else {
963 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
965 ber_flatten(cookie_be, &cookie_bv);
966 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
967 PagedResults.ldctl_iscritical = (char) 1;
968 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
969 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
971 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
972 NoReferrals.ldctl_iscritical = (char) 0;
973 NoReferrals.ldctl_value.bv_len = 0;
974 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
976 if (external_control &&
977 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
978 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
980 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
981 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
983 /* win2k does not accept a ldctl_value beeing passed in */
985 if (external_control->val != 0) {
987 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
988 rc = LDAP_NO_MEMORY;
989 goto done;
992 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
993 rc = LDAP_NO_MEMORY;
994 goto done;
996 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
997 rc = LDAP_NO_MEMORY;
998 goto done;
1001 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1002 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1004 } else {
1005 ExternalCtrl.ldctl_value.bv_len = 0;
1006 ExternalCtrl.ldctl_value.bv_val = NULL;
1009 controls[0] = &NoReferrals;
1010 controls[1] = &PagedResults;
1011 controls[2] = &ExternalCtrl;
1012 controls[3] = NULL;
1014 } else {
1015 controls[0] = &NoReferrals;
1016 controls[1] = &PagedResults;
1017 controls[2] = NULL;
1020 /* we need to disable referrals as the openldap libs don't
1021 handle them and paged results at the same time. Using them
1022 together results in the result record containing the server
1023 page control being removed from the result list (tridge/jmcd)
1025 leaving this in despite the control that says don't generate
1026 referrals, in case the server doesn't support it (jmcd)
1028 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1030 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1031 search_attrs, 0, controls,
1032 NULL, LDAP_NO_LIMIT,
1033 (LDAPMessage **)res);
1035 ber_free(cookie_be, 1);
1036 ber_bvfree(cookie_bv);
1038 if (rc) {
1039 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1040 ldap_err2string(rc)));
1041 if (rc == LDAP_OTHER) {
1042 char *ldap_errmsg;
1043 int ret;
1045 ret = ldap_parse_result(ads->ldap.ld,
1046 *res,
1047 NULL,
1048 NULL,
1049 &ldap_errmsg,
1050 NULL,
1051 NULL,
1053 if (ret == LDAP_SUCCESS) {
1054 DEBUG(3, ("ldap_search_with_timeout(%s) "
1055 "error: %s\n", expr, ldap_errmsg));
1056 ldap_memfree(ldap_errmsg);
1059 goto done;
1062 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1063 NULL, &rcontrols, 0);
1065 if (!rcontrols) {
1066 goto done;
1069 for (i=0; rcontrols[i]; i++) {
1070 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1071 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1072 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1073 &cookie_bv);
1074 /* the berval is the cookie, but must be freed when
1075 it is all done */
1076 if (cookie_bv->bv_len) /* still more to do */
1077 *cookie=ber_bvdup(cookie_bv);
1078 else
1079 *cookie=NULL;
1080 ber_bvfree(cookie_bv);
1081 ber_free(cookie_be, 1);
1082 break;
1085 ldap_controls_free(rcontrols);
1087 done:
1088 talloc_destroy(ctx);
1090 if (ext_be) {
1091 ber_free(ext_be, 1);
1094 if (ext_bv) {
1095 ber_bvfree(ext_bv);
1098 /* if/when we decide to utf8-encode attrs, take out this next line */
1099 TALLOC_FREE(search_attrs);
1101 return ADS_ERROR(rc);
1104 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1105 int scope, const char *expr,
1106 const char **attrs, LDAPMessage **res,
1107 int *count, struct berval **cookie)
1109 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1114 * Get all results for a search. This uses ads_do_paged_search() to return
1115 * all entries in a large search.
1116 * @param ads connection to ads server
1117 * @param bind_path Base dn for the search
1118 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1119 * @param expr Search expression
1120 * @param attrs Attributes to retrieve
1121 * @param res ** which will contain results - free res* with ads_msgfree()
1122 * @return status of search
1124 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1125 int scope, const char *expr,
1126 const char **attrs, void *args,
1127 LDAPMessage **res)
1129 struct berval *cookie = NULL;
1130 int count = 0;
1131 ADS_STATUS status;
1133 *res = NULL;
1134 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1135 &count, &cookie);
1137 if (!ADS_ERR_OK(status))
1138 return status;
1140 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1141 while (cookie) {
1142 LDAPMessage *res2 = NULL;
1143 ADS_STATUS status2;
1144 LDAPMessage *msg, *next;
1146 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1147 attrs, args, &res2, &count, &cookie);
1149 if (!ADS_ERR_OK(status2)) break;
1151 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1152 that this works on all ldap libs, but I have only tested with openldap */
1153 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1154 next = ads_next_message(ads, msg);
1155 ldap_add_result_entry((LDAPMessage **)res, msg);
1157 /* note that we do not free res2, as the memory is now
1158 part of the main returned list */
1160 #else
1161 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1162 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1163 #endif
1165 return status;
1168 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1169 int scope, const char *expr,
1170 const char **attrs, LDAPMessage **res)
1172 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1175 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1176 int scope, const char *expr,
1177 const char **attrs, uint32 sd_flags,
1178 LDAPMessage **res)
1180 ads_control args;
1182 args.control = ADS_SD_FLAGS_OID;
1183 args.val = sd_flags;
1184 args.critical = True;
1186 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1191 * Run a function on all results for a search. Uses ads_do_paged_search() and
1192 * runs the function as each page is returned, using ads_process_results()
1193 * @param ads connection to ads server
1194 * @param bind_path Base dn for the search
1195 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1196 * @param expr Search expression - specified in local charset
1197 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1198 * @param fn Function which takes attr name, values list, and data_area
1199 * @param data_area Pointer which is passed to function on each call
1200 * @return status of search
1202 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1203 int scope, const char *expr, const char **attrs,
1204 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1205 void *data_area)
1207 struct berval *cookie = NULL;
1208 int count = 0;
1209 ADS_STATUS status;
1210 LDAPMessage *res;
1212 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1213 &count, &cookie);
1215 if (!ADS_ERR_OK(status)) return status;
1217 ads_process_results(ads, res, fn, data_area);
1218 ads_msgfree(ads, res);
1220 while (cookie) {
1221 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1222 &res, &count, &cookie);
1224 if (!ADS_ERR_OK(status)) break;
1226 ads_process_results(ads, res, fn, data_area);
1227 ads_msgfree(ads, res);
1230 return status;
1234 * Do a search with a timeout.
1235 * @param ads connection to ads server
1236 * @param bind_path Base dn for the search
1237 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1238 * @param expr Search expression
1239 * @param attrs Attributes to retrieve
1240 * @param res ** which will contain results - free res* with ads_msgfree()
1241 * @return status of search
1243 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1244 const char *expr,
1245 const char **attrs, LDAPMessage **res)
1247 int rc;
1248 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1249 size_t converted_size;
1250 TALLOC_CTX *ctx;
1252 *res = NULL;
1253 if (!(ctx = talloc_init("ads_do_search"))) {
1254 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1255 return ADS_ERROR(LDAP_NO_MEMORY);
1258 /* 0 means the conversion worked but the result was empty
1259 so we only fail if it's negative. In any case, it always
1260 at least nulls out the dest */
1261 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1262 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1264 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1265 rc = LDAP_NO_MEMORY;
1266 goto done;
1269 if (!attrs || !(*attrs))
1270 search_attrs = NULL;
1271 else {
1272 /* This would be the utf8-encoded version...*/
1273 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1274 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1276 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1277 rc = LDAP_NO_MEMORY;
1278 goto done;
1282 /* see the note in ads_do_paged_search - we *must* disable referrals */
1283 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1285 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1286 search_attrs, 0, NULL, NULL,
1287 LDAP_NO_LIMIT,
1288 (LDAPMessage **)res);
1290 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1291 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1292 rc = 0;
1295 done:
1296 talloc_destroy(ctx);
1297 /* if/when we decide to utf8-encode attrs, take out this next line */
1298 TALLOC_FREE(search_attrs);
1299 return ADS_ERROR(rc);
1302 * Do a general ADS search
1303 * @param ads connection to ads server
1304 * @param res ** which will contain results - free res* with ads_msgfree()
1305 * @param expr Search expression
1306 * @param attrs Attributes to retrieve
1307 * @return status of search
1309 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1310 const char *expr, const char **attrs)
1312 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1313 expr, attrs, res);
1317 * Do a search on a specific DistinguishedName
1318 * @param ads connection to ads server
1319 * @param res ** which will contain results - free res* with ads_msgfree()
1320 * @param dn DistinguishName to search
1321 * @param attrs Attributes to retrieve
1322 * @return status of search
1324 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1325 const char *dn, const char **attrs)
1327 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1328 attrs, res);
1332 * Free up memory from a ads_search
1333 * @param ads connection to ads server
1334 * @param msg Search results to free
1336 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1338 if (!msg) return;
1339 ldap_msgfree(msg);
1343 * Get a dn from search results
1344 * @param ads connection to ads server
1345 * @param msg Search result
1346 * @return dn string
1348 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1350 char *utf8_dn, *unix_dn;
1351 size_t converted_size;
1353 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1355 if (!utf8_dn) {
1356 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1357 return NULL;
1360 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1361 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1362 utf8_dn ));
1363 return NULL;
1365 ldap_memfree(utf8_dn);
1366 return unix_dn;
1370 * Get the parent from a dn
1371 * @param dn the dn to return the parent from
1372 * @return parent dn string
1374 char *ads_parent_dn(const char *dn)
1376 char *p;
1378 if (dn == NULL) {
1379 return NULL;
1382 p = strchr(dn, ',');
1384 if (p == NULL) {
1385 return NULL;
1388 return p+1;
1392 * Find a machine account given a hostname
1393 * @param ads connection to ads server
1394 * @param res ** which will contain results - free res* with ads_msgfree()
1395 * @param host Hostname to search for
1396 * @return status of search
1398 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1399 const char *machine)
1401 ADS_STATUS status;
1402 char *expr;
1403 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1405 *res = NULL;
1407 /* the easiest way to find a machine account anywhere in the tree
1408 is to look for hostname$ */
1409 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1410 DEBUG(1, ("asprintf failed!\n"));
1411 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1414 status = ads_search(ads, res, expr, attrs);
1415 SAFE_FREE(expr);
1416 return status;
1420 * Initialize a list of mods to be used in a modify request
1421 * @param ctx An initialized TALLOC_CTX
1422 * @return allocated ADS_MODLIST
1424 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1426 #define ADS_MODLIST_ALLOC_SIZE 10
1427 LDAPMod **mods;
1429 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1430 /* -1 is safety to make sure we don't go over the end.
1431 need to reset it to NULL before doing ldap modify */
1432 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1434 return (ADS_MODLIST)mods;
1439 add an attribute to the list, with values list already constructed
1441 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1442 int mod_op, const char *name,
1443 const void *_invals)
1445 const void **invals = (const void **)_invals;
1446 int curmod;
1447 LDAPMod **modlist = (LDAPMod **) *mods;
1448 struct berval **ber_values = NULL;
1449 char **char_values = NULL;
1451 if (!invals) {
1452 mod_op = LDAP_MOD_DELETE;
1453 } else {
1454 if (mod_op & LDAP_MOD_BVALUES)
1455 ber_values = ads_dup_values(ctx,
1456 (const struct berval **)invals);
1457 else
1458 char_values = ads_push_strvals(ctx,
1459 (const char **) invals);
1462 /* find the first empty slot */
1463 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1464 curmod++);
1465 if (modlist[curmod] == (LDAPMod *) -1) {
1466 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1467 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1468 return ADS_ERROR(LDAP_NO_MEMORY);
1469 memset(&modlist[curmod], 0,
1470 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1471 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1472 *mods = (ADS_MODLIST)modlist;
1475 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1476 return ADS_ERROR(LDAP_NO_MEMORY);
1477 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1478 if (mod_op & LDAP_MOD_BVALUES) {
1479 modlist[curmod]->mod_bvalues = ber_values;
1480 } else if (mod_op & LDAP_MOD_DELETE) {
1481 modlist[curmod]->mod_values = NULL;
1482 } else {
1483 modlist[curmod]->mod_values = char_values;
1486 modlist[curmod]->mod_op = mod_op;
1487 return ADS_ERROR(LDAP_SUCCESS);
1491 * Add a single string value to a mod list
1492 * @param ctx An initialized TALLOC_CTX
1493 * @param mods An initialized ADS_MODLIST
1494 * @param name The attribute name to add
1495 * @param val The value to add - NULL means DELETE
1496 * @return ADS STATUS indicating success of add
1498 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1499 const char *name, const char *val)
1501 const char *values[2];
1503 values[0] = val;
1504 values[1] = NULL;
1506 if (!val)
1507 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1508 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1512 * Add an array of string values to a mod list
1513 * @param ctx An initialized TALLOC_CTX
1514 * @param mods An initialized ADS_MODLIST
1515 * @param name The attribute name to add
1516 * @param vals The array of string values to add - NULL means DELETE
1517 * @return ADS STATUS indicating success of add
1519 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1520 const char *name, const char **vals)
1522 if (!vals)
1523 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1524 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1525 name, (const void **) vals);
1528 #if 0
1530 * Add a single ber-encoded value to a mod list
1531 * @param ctx An initialized TALLOC_CTX
1532 * @param mods An initialized ADS_MODLIST
1533 * @param name The attribute name to add
1534 * @param val The value to add - NULL means DELETE
1535 * @return ADS STATUS indicating success of add
1537 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1538 const char *name, const struct berval *val)
1540 const struct berval *values[2];
1542 values[0] = val;
1543 values[1] = NULL;
1544 if (!val)
1545 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1546 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1547 name, (const void **) values);
1549 #endif
1552 * Perform an ldap modify
1553 * @param ads connection to ads server
1554 * @param mod_dn DistinguishedName to modify
1555 * @param mods list of modifications to perform
1556 * @return status of modify
1558 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1560 int ret,i;
1561 char *utf8_dn = NULL;
1562 size_t converted_size;
1564 this control is needed to modify that contains a currently
1565 non-existent attribute (but allowable for the object) to run
1567 LDAPControl PermitModify = {
1568 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1569 {0, NULL},
1570 (char) 1};
1571 LDAPControl *controls[2];
1573 controls[0] = &PermitModify;
1574 controls[1] = NULL;
1576 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1577 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1580 /* find the end of the list, marked by NULL or -1 */
1581 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1582 /* make sure the end of the list is NULL */
1583 mods[i] = NULL;
1584 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1585 (LDAPMod **) mods, controls, NULL);
1586 TALLOC_FREE(utf8_dn);
1587 return ADS_ERROR(ret);
1591 * Perform an ldap add
1592 * @param ads connection to ads server
1593 * @param new_dn DistinguishedName to add
1594 * @param mods list of attributes and values for DN
1595 * @return status of add
1597 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1599 int ret, i;
1600 char *utf8_dn = NULL;
1601 size_t converted_size;
1603 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1604 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1605 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1608 /* find the end of the list, marked by NULL or -1 */
1609 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1610 /* make sure the end of the list is NULL */
1611 mods[i] = NULL;
1613 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1614 TALLOC_FREE(utf8_dn);
1615 return ADS_ERROR(ret);
1619 * Delete a DistinguishedName
1620 * @param ads connection to ads server
1621 * @param new_dn DistinguishedName to delete
1622 * @return status of delete
1624 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1626 int ret;
1627 char *utf8_dn = NULL;
1628 size_t converted_size;
1629 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1630 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1631 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1634 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1635 TALLOC_FREE(utf8_dn);
1636 return ADS_ERROR(ret);
1640 * Build an org unit string
1641 * if org unit is Computers or blank then assume a container, otherwise
1642 * assume a / separated list of organisational units.
1643 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1644 * @param ads connection to ads server
1645 * @param org_unit Organizational unit
1646 * @return org unit string - caller must free
1648 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1650 char *ret = NULL;
1652 if (!org_unit || !*org_unit) {
1654 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1656 /* samba4 might not yet respond to a wellknownobject-query */
1657 return ret ? ret : SMB_STRDUP("cn=Computers");
1660 if (strequal(org_unit, "Computers")) {
1661 return SMB_STRDUP("cn=Computers");
1664 /* jmcd: removed "\\" from the separation chars, because it is
1665 needed as an escape for chars like '#' which are valid in an
1666 OU name */
1667 return ads_build_path(org_unit, "/", "ou=", 1);
1671 * Get a org unit string for a well-known GUID
1672 * @param ads connection to ads server
1673 * @param wknguid Well known GUID
1674 * @return org unit string - caller must free
1676 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1678 ADS_STATUS status;
1679 LDAPMessage *res = NULL;
1680 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1681 **bind_dn_exp = NULL;
1682 const char *attrs[] = {"distinguishedName", NULL};
1683 int new_ln, wkn_ln, bind_ln, i;
1685 if (wknguid == NULL) {
1686 return NULL;
1689 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1690 DEBUG(1, ("asprintf failed!\n"));
1691 return NULL;
1694 status = ads_search_dn(ads, &res, base, attrs);
1695 if (!ADS_ERR_OK(status)) {
1696 DEBUG(1,("Failed while searching for: %s\n", base));
1697 goto out;
1700 if (ads_count_replies(ads, res) != 1) {
1701 goto out;
1704 /* substitute the bind-path from the well-known-guid-search result */
1705 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1706 if (!wkn_dn) {
1707 goto out;
1710 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1711 if (!wkn_dn_exp) {
1712 goto out;
1715 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1716 if (!bind_dn_exp) {
1717 goto out;
1720 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1722 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1725 new_ln = wkn_ln - bind_ln;
1727 ret = SMB_STRDUP(wkn_dn_exp[0]);
1728 if (!ret) {
1729 goto out;
1732 for (i=1; i < new_ln; i++) {
1733 char *s = NULL;
1735 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1736 SAFE_FREE(ret);
1737 goto out;
1740 SAFE_FREE(ret);
1741 ret = SMB_STRDUP(s);
1742 free(s);
1743 if (!ret) {
1744 goto out;
1748 out:
1749 SAFE_FREE(base);
1750 ads_msgfree(ads, res);
1751 TALLOC_FREE(wkn_dn);
1752 if (wkn_dn_exp) {
1753 ldap_value_free(wkn_dn_exp);
1755 if (bind_dn_exp) {
1756 ldap_value_free(bind_dn_exp);
1759 return ret;
1763 * Adds (appends) an item to an attribute array, rather then
1764 * replacing the whole list
1765 * @param ctx An initialized TALLOC_CTX
1766 * @param mods An initialized ADS_MODLIST
1767 * @param name name of the ldap attribute to append to
1768 * @param vals an array of values to add
1769 * @return status of addition
1772 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1773 const char *name, const char **vals)
1775 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1776 (const void *) vals);
1780 * Determines the an account's current KVNO via an LDAP lookup
1781 * @param ads An initialized ADS_STRUCT
1782 * @param account_name the NT samaccountname.
1783 * @return the kvno for the account, or -1 in case of a failure.
1786 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1788 LDAPMessage *res = NULL;
1789 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1790 char *filter;
1791 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1792 char *dn_string = NULL;
1793 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1795 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1796 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1797 return kvno;
1799 ret = ads_search(ads, &res, filter, attrs);
1800 SAFE_FREE(filter);
1801 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1802 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1803 ads_msgfree(ads, res);
1804 return kvno;
1807 dn_string = ads_get_dn(ads, talloc_tos(), res);
1808 if (!dn_string) {
1809 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1810 ads_msgfree(ads, res);
1811 return kvno;
1813 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1814 TALLOC_FREE(dn_string);
1816 /* ---------------------------------------------------------
1817 * 0 is returned as a default KVNO from this point on...
1818 * This is done because Windows 2000 does not support key
1819 * version numbers. Chances are that a failure in the next
1820 * step is simply due to Windows 2000 being used for a
1821 * domain controller. */
1822 kvno = 0;
1824 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1825 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1826 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1827 ads_msgfree(ads, res);
1828 return kvno;
1831 /* Success */
1832 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1833 ads_msgfree(ads, res);
1834 return kvno;
1838 * Determines the computer account's current KVNO via an LDAP lookup
1839 * @param ads An initialized ADS_STRUCT
1840 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1841 * @return the kvno for the computer account, or -1 in case of a failure.
1844 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1846 char *computer_account = NULL;
1847 uint32_t kvno = -1;
1849 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1850 return kvno;
1853 kvno = ads_get_kvno(ads, computer_account);
1854 free(computer_account);
1856 return kvno;
1860 * This clears out all registered spn's for a given hostname
1861 * @param ads An initilaized ADS_STRUCT
1862 * @param machine_name the NetBIOS name of the computer.
1863 * @return 0 upon success, non-zero otherwise.
1866 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1868 TALLOC_CTX *ctx;
1869 LDAPMessage *res = NULL;
1870 ADS_MODLIST mods;
1871 const char *servicePrincipalName[1] = {NULL};
1872 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1873 char *dn_string = NULL;
1875 ret = ads_find_machine_acct(ads, &res, machine_name);
1876 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1877 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1878 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1879 ads_msgfree(ads, res);
1880 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1883 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1884 ctx = talloc_init("ads_clear_service_principal_names");
1885 if (!ctx) {
1886 ads_msgfree(ads, res);
1887 return ADS_ERROR(LDAP_NO_MEMORY);
1890 if (!(mods = ads_init_mods(ctx))) {
1891 talloc_destroy(ctx);
1892 ads_msgfree(ads, res);
1893 return ADS_ERROR(LDAP_NO_MEMORY);
1895 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1896 if (!ADS_ERR_OK(ret)) {
1897 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1898 ads_msgfree(ads, res);
1899 talloc_destroy(ctx);
1900 return ret;
1902 dn_string = ads_get_dn(ads, talloc_tos(), res);
1903 if (!dn_string) {
1904 talloc_destroy(ctx);
1905 ads_msgfree(ads, res);
1906 return ADS_ERROR(LDAP_NO_MEMORY);
1908 ret = ads_gen_mod(ads, dn_string, mods);
1909 TALLOC_FREE(dn_string);
1910 if (!ADS_ERR_OK(ret)) {
1911 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1912 machine_name));
1913 ads_msgfree(ads, res);
1914 talloc_destroy(ctx);
1915 return ret;
1918 ads_msgfree(ads, res);
1919 talloc_destroy(ctx);
1920 return ret;
1924 * This adds a service principal name to an existing computer account
1925 * (found by hostname) in AD.
1926 * @param ads An initialized ADS_STRUCT
1927 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1928 * @param my_fqdn The fully qualified DNS name of the machine
1929 * @param spn A string of the service principal to add, i.e. 'host'
1930 * @return 0 upon sucess, or non-zero if a failure occurs
1933 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1934 const char *my_fqdn, const char *spn)
1936 ADS_STATUS ret;
1937 TALLOC_CTX *ctx;
1938 LDAPMessage *res = NULL;
1939 char *psp1, *psp2;
1940 ADS_MODLIST mods;
1941 char *dn_string = NULL;
1942 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1944 ret = ads_find_machine_acct(ads, &res, machine_name);
1945 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1946 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1947 machine_name));
1948 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1949 spn, machine_name, ads->config.realm));
1950 ads_msgfree(ads, res);
1951 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1954 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1955 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1956 ads_msgfree(ads, res);
1957 return ADS_ERROR(LDAP_NO_MEMORY);
1960 /* add short name spn */
1962 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1963 talloc_destroy(ctx);
1964 ads_msgfree(ads, res);
1965 return ADS_ERROR(LDAP_NO_MEMORY);
1967 if (!strlower_m(&psp1[strlen(spn) + 1])) {
1968 ret = ADS_ERROR(LDAP_NO_MEMORY);
1969 goto out;
1971 servicePrincipalName[0] = psp1;
1973 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1974 psp1, machine_name));
1977 /* add fully qualified spn */
1979 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1980 ret = ADS_ERROR(LDAP_NO_MEMORY);
1981 goto out;
1983 if (!strlower_m(&psp2[strlen(spn) + 1])) {
1984 ret = ADS_ERROR(LDAP_NO_MEMORY);
1985 goto out;
1987 servicePrincipalName[1] = psp2;
1989 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1990 psp2, machine_name));
1992 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1993 ret = ADS_ERROR(LDAP_NO_MEMORY);
1994 goto out;
1997 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1998 if (!ADS_ERR_OK(ret)) {
1999 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2000 goto out;
2003 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2004 ret = ADS_ERROR(LDAP_NO_MEMORY);
2005 goto out;
2008 ret = ads_gen_mod(ads, dn_string, mods);
2009 if (!ADS_ERR_OK(ret)) {
2010 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2011 goto out;
2014 out:
2015 TALLOC_FREE( ctx );
2016 ads_msgfree(ads, res);
2017 return ret;
2021 * adds a machine account to the ADS server
2022 * @param ads An intialized ADS_STRUCT
2023 * @param machine_name - the NetBIOS machine name of this account.
2024 * @param account_type A number indicating the type of account to create
2025 * @param org_unit The LDAP path in which to place this account
2026 * @return 0 upon success, or non-zero otherwise
2029 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2030 const char *org_unit)
2032 ADS_STATUS ret;
2033 char *samAccountName, *controlstr;
2034 TALLOC_CTX *ctx;
2035 ADS_MODLIST mods;
2036 char *machine_escaped = NULL;
2037 char *new_dn;
2038 const char *objectClass[] = {"top", "person", "organizationalPerson",
2039 "user", "computer", NULL};
2040 LDAPMessage *res = NULL;
2041 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2042 UF_DONT_EXPIRE_PASSWD |\
2043 UF_ACCOUNTDISABLE );
2045 if (!(ctx = talloc_init("ads_add_machine_acct")))
2046 return ADS_ERROR(LDAP_NO_MEMORY);
2048 ret = ADS_ERROR(LDAP_NO_MEMORY);
2050 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2051 if (!machine_escaped) {
2052 goto done;
2055 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2056 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2058 if ( !new_dn || !samAccountName ) {
2059 goto done;
2062 #ifndef ENCTYPE_ARCFOUR_HMAC
2063 acct_control |= UF_USE_DES_KEY_ONLY;
2064 #endif
2066 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2067 goto done;
2070 if (!(mods = ads_init_mods(ctx))) {
2071 goto done;
2074 ads_mod_str(ctx, &mods, "cn", machine_name);
2075 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2076 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2077 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2079 ret = ads_gen_add(ads, new_dn, mods);
2081 done:
2082 SAFE_FREE(machine_escaped);
2083 ads_msgfree(ads, res);
2084 talloc_destroy(ctx);
2086 return ret;
2090 * move a machine account to another OU on the ADS server
2091 * @param ads - An intialized ADS_STRUCT
2092 * @param machine_name - the NetBIOS machine name of this account.
2093 * @param org_unit - The LDAP path in which to place this account
2094 * @param moved - whether we moved the machine account (optional)
2095 * @return 0 upon success, or non-zero otherwise
2098 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2099 const char *org_unit, bool *moved)
2101 ADS_STATUS rc;
2102 int ldap_status;
2103 LDAPMessage *res = NULL;
2104 char *filter = NULL;
2105 char *computer_dn = NULL;
2106 char *parent_dn;
2107 char *computer_rdn = NULL;
2108 bool need_move = False;
2110 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2111 rc = ADS_ERROR(LDAP_NO_MEMORY);
2112 goto done;
2115 /* Find pre-existing machine */
2116 rc = ads_search(ads, &res, filter, NULL);
2117 if (!ADS_ERR_OK(rc)) {
2118 goto done;
2121 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2122 if (!computer_dn) {
2123 rc = ADS_ERROR(LDAP_NO_MEMORY);
2124 goto done;
2127 parent_dn = ads_parent_dn(computer_dn);
2128 if (strequal(parent_dn, org_unit)) {
2129 goto done;
2132 need_move = True;
2134 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2135 rc = ADS_ERROR(LDAP_NO_MEMORY);
2136 goto done;
2139 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2140 org_unit, 1, NULL, NULL);
2141 rc = ADS_ERROR(ldap_status);
2143 done:
2144 ads_msgfree(ads, res);
2145 SAFE_FREE(filter);
2146 TALLOC_FREE(computer_dn);
2147 SAFE_FREE(computer_rdn);
2149 if (!ADS_ERR_OK(rc)) {
2150 need_move = False;
2153 if (moved) {
2154 *moved = need_move;
2157 return rc;
2161 dump a binary result from ldap
2163 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2165 int i, j;
2166 for (i=0; values[i]; i++) {
2167 printf("%s: ", field);
2168 for (j=0; j<values[i]->bv_len; j++) {
2169 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2171 printf("\n");
2175 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2177 int i;
2178 for (i=0; values[i]; i++) {
2179 NTSTATUS status;
2180 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2181 struct GUID guid;
2183 status = GUID_from_ndr_blob(&in, &guid);
2184 if (NT_STATUS_IS_OK(status)) {
2185 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2186 } else {
2187 printf("%s: INVALID GUID\n", field);
2193 dump a sid result from ldap
2195 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2197 int i;
2198 for (i=0; values[i]; i++) {
2199 struct dom_sid sid;
2200 fstring tmp;
2201 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2202 return;
2204 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2209 dump ntSecurityDescriptor
2211 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2213 TALLOC_CTX *frame = talloc_stackframe();
2214 struct security_descriptor *psd;
2215 NTSTATUS status;
2217 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2218 values[0]->bv_len, &psd);
2219 if (!NT_STATUS_IS_OK(status)) {
2220 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2221 nt_errstr(status)));
2222 TALLOC_FREE(frame);
2223 return;
2226 if (psd) {
2227 ads_disp_sd(ads, talloc_tos(), psd);
2230 TALLOC_FREE(frame);
2234 dump a string result from ldap
2236 static void dump_string(const char *field, char **values)
2238 int i;
2239 for (i=0; values[i]; i++) {
2240 printf("%s: %s\n", field, values[i]);
2245 dump a field from LDAP on stdout
2246 used for debugging
2249 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2251 const struct {
2252 const char *name;
2253 bool string;
2254 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2255 } handlers[] = {
2256 {"objectGUID", False, dump_guid},
2257 {"netbootGUID", False, dump_guid},
2258 {"nTSecurityDescriptor", False, dump_sd},
2259 {"dnsRecord", False, dump_binary},
2260 {"objectSid", False, dump_sid},
2261 {"tokenGroups", False, dump_sid},
2262 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2263 {"tokengroupsGlobalandUniversal", False, dump_sid},
2264 {"mS-DS-CreatorSID", False, dump_sid},
2265 {"msExchMailboxGuid", False, dump_guid},
2266 {NULL, True, NULL}
2268 int i;
2270 if (!field) { /* must be end of an entry */
2271 printf("\n");
2272 return False;
2275 for (i=0; handlers[i].name; i++) {
2276 if (strcasecmp_m(handlers[i].name, field) == 0) {
2277 if (!values) /* first time, indicate string or not */
2278 return handlers[i].string;
2279 handlers[i].handler(ads, field, (struct berval **) values);
2280 break;
2283 if (!handlers[i].name) {
2284 if (!values) /* first time, indicate string conversion */
2285 return True;
2286 dump_string(field, (char **)values);
2288 return False;
2292 * Dump a result from LDAP on stdout
2293 * used for debugging
2294 * @param ads connection to ads server
2295 * @param res Results to dump
2298 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2300 ads_process_results(ads, res, ads_dump_field, NULL);
2304 * Walk through results, calling a function for each entry found.
2305 * The function receives a field name, a berval * array of values,
2306 * and a data area passed through from the start. The function is
2307 * called once with null for field and values at the end of each
2308 * entry.
2309 * @param ads connection to ads server
2310 * @param res Results to process
2311 * @param fn Function for processing each result
2312 * @param data_area user-defined area to pass to function
2314 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2315 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2316 void *data_area)
2318 LDAPMessage *msg;
2319 TALLOC_CTX *ctx;
2320 size_t converted_size;
2322 if (!(ctx = talloc_init("ads_process_results")))
2323 return;
2325 for (msg = ads_first_entry(ads, res); msg;
2326 msg = ads_next_entry(ads, msg)) {
2327 char *utf8_field;
2328 BerElement *b;
2330 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2331 (LDAPMessage *)msg,&b);
2332 utf8_field;
2333 utf8_field=ldap_next_attribute(ads->ldap.ld,
2334 (LDAPMessage *)msg,b)) {
2335 struct berval **ber_vals;
2336 char **str_vals, **utf8_vals;
2337 char *field;
2338 bool string;
2340 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2341 &converted_size))
2343 DEBUG(0,("ads_process_results: "
2344 "pull_utf8_talloc failed: %s",
2345 strerror(errno)));
2348 string = fn(ads, field, NULL, data_area);
2350 if (string) {
2351 utf8_vals = ldap_get_values(ads->ldap.ld,
2352 (LDAPMessage *)msg, field);
2353 str_vals = ads_pull_strvals(ctx,
2354 (const char **) utf8_vals);
2355 fn(ads, field, (void **) str_vals, data_area);
2356 ldap_value_free(utf8_vals);
2357 } else {
2358 ber_vals = ldap_get_values_len(ads->ldap.ld,
2359 (LDAPMessage *)msg, field);
2360 fn(ads, field, (void **) ber_vals, data_area);
2362 ldap_value_free_len(ber_vals);
2364 ldap_memfree(utf8_field);
2366 ber_free(b, 0);
2367 talloc_free_children(ctx);
2368 fn(ads, NULL, NULL, data_area); /* completed an entry */
2371 talloc_destroy(ctx);
2375 * count how many replies are in a LDAPMessage
2376 * @param ads connection to ads server
2377 * @param res Results to count
2378 * @return number of replies
2380 int ads_count_replies(ADS_STRUCT *ads, void *res)
2382 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2386 * pull the first entry from a ADS result
2387 * @param ads connection to ads server
2388 * @param res Results of search
2389 * @return first entry from result
2391 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2393 return ldap_first_entry(ads->ldap.ld, res);
2397 * pull the next entry from a ADS result
2398 * @param ads connection to ads server
2399 * @param res Results of search
2400 * @return next entry from result
2402 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2404 return ldap_next_entry(ads->ldap.ld, res);
2408 * pull the first message from a ADS result
2409 * @param ads connection to ads server
2410 * @param res Results of search
2411 * @return first message from result
2413 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2415 return ldap_first_message(ads->ldap.ld, res);
2419 * pull the next message from a ADS result
2420 * @param ads connection to ads server
2421 * @param res Results of search
2422 * @return next message from result
2424 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2426 return ldap_next_message(ads->ldap.ld, res);
2430 * pull a single string from a ADS result
2431 * @param ads connection to ads server
2432 * @param mem_ctx TALLOC_CTX to use for allocating result string
2433 * @param msg Results of search
2434 * @param field Attribute to retrieve
2435 * @return Result string in talloc context
2437 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2438 const char *field)
2440 char **values;
2441 char *ret = NULL;
2442 char *ux_string;
2443 size_t converted_size;
2445 values = ldap_get_values(ads->ldap.ld, msg, field);
2446 if (!values)
2447 return NULL;
2449 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2450 &converted_size))
2452 ret = ux_string;
2454 ldap_value_free(values);
2455 return ret;
2459 * pull an array of strings from a ADS result
2460 * @param ads connection to ads server
2461 * @param mem_ctx TALLOC_CTX to use for allocating result string
2462 * @param msg Results of search
2463 * @param field Attribute to retrieve
2464 * @return Result strings in talloc context
2466 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2467 LDAPMessage *msg, const char *field,
2468 size_t *num_values)
2470 char **values;
2471 char **ret = NULL;
2472 int i;
2473 size_t converted_size;
2475 values = ldap_get_values(ads->ldap.ld, msg, field);
2476 if (!values)
2477 return NULL;
2479 *num_values = ldap_count_values(values);
2481 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2482 if (!ret) {
2483 ldap_value_free(values);
2484 return NULL;
2487 for (i=0;i<*num_values;i++) {
2488 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2489 &converted_size))
2491 ldap_value_free(values);
2492 return NULL;
2495 ret[i] = NULL;
2497 ldap_value_free(values);
2498 return ret;
2502 * pull an array of strings from a ADS result
2503 * (handle large multivalue attributes with range retrieval)
2504 * @param ads connection to ads server
2505 * @param mem_ctx TALLOC_CTX to use for allocating result string
2506 * @param msg Results of search
2507 * @param field Attribute to retrieve
2508 * @param current_strings strings returned by a previous call to this function
2509 * @param next_attribute The next query should ask for this attribute
2510 * @param num_values How many values did we get this time?
2511 * @param more_values Are there more values to get?
2512 * @return Result strings in talloc context
2514 char **ads_pull_strings_range(ADS_STRUCT *ads,
2515 TALLOC_CTX *mem_ctx,
2516 LDAPMessage *msg, const char *field,
2517 char **current_strings,
2518 const char **next_attribute,
2519 size_t *num_strings,
2520 bool *more_strings)
2522 char *attr;
2523 char *expected_range_attrib, *range_attr;
2524 BerElement *ptr = NULL;
2525 char **strings;
2526 char **new_strings;
2527 size_t num_new_strings;
2528 unsigned long int range_start;
2529 unsigned long int range_end;
2531 /* we might have been given the whole lot anyway */
2532 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2533 *more_strings = False;
2534 return strings;
2537 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2539 /* look for Range result */
2540 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2541 attr;
2542 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2543 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2544 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2545 range_attr = attr;
2546 break;
2548 ldap_memfree(attr);
2550 if (!attr) {
2551 ber_free(ptr, 0);
2552 /* nothing here - this field is just empty */
2553 *more_strings = False;
2554 return NULL;
2557 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2558 &range_start, &range_end) == 2) {
2559 *more_strings = True;
2560 } else {
2561 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2562 &range_start) == 1) {
2563 *more_strings = False;
2564 } else {
2565 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2566 range_attr));
2567 ldap_memfree(range_attr);
2568 *more_strings = False;
2569 return NULL;
2573 if ((*num_strings) != range_start) {
2574 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2575 " - aborting range retreival\n",
2576 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2577 ldap_memfree(range_attr);
2578 *more_strings = False;
2579 return NULL;
2582 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2584 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2585 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2586 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2587 range_attr, (unsigned long int)range_end - range_start + 1,
2588 (unsigned long int)num_new_strings));
2589 ldap_memfree(range_attr);
2590 *more_strings = False;
2591 return NULL;
2594 strings = talloc_realloc(mem_ctx, current_strings, char *,
2595 *num_strings + num_new_strings);
2597 if (strings == NULL) {
2598 ldap_memfree(range_attr);
2599 *more_strings = False;
2600 return NULL;
2603 if (new_strings && num_new_strings) {
2604 memcpy(&strings[*num_strings], new_strings,
2605 sizeof(*new_strings) * num_new_strings);
2608 (*num_strings) += num_new_strings;
2610 if (*more_strings) {
2611 *next_attribute = talloc_asprintf(mem_ctx,
2612 "%s;range=%d-*",
2613 field,
2614 (int)*num_strings);
2616 if (!*next_attribute) {
2617 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2618 ldap_memfree(range_attr);
2619 *more_strings = False;
2620 return NULL;
2624 ldap_memfree(range_attr);
2626 return strings;
2630 * pull a single uint32 from a ADS result
2631 * @param ads connection to ads server
2632 * @param msg Results of search
2633 * @param field Attribute to retrieve
2634 * @param v Pointer to int to store result
2635 * @return boolean inidicating success
2637 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2638 uint32 *v)
2640 char **values;
2642 values = ldap_get_values(ads->ldap.ld, msg, field);
2643 if (!values)
2644 return False;
2645 if (!values[0]) {
2646 ldap_value_free(values);
2647 return False;
2650 *v = atoi(values[0]);
2651 ldap_value_free(values);
2652 return True;
2656 * pull a single objectGUID from an ADS result
2657 * @param ads connection to ADS server
2658 * @param msg results of search
2659 * @param guid 37-byte area to receive text guid
2660 * @return boolean indicating success
2662 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2664 DATA_BLOB blob;
2665 NTSTATUS status;
2667 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2668 &blob)) {
2669 return false;
2672 status = GUID_from_ndr_blob(&blob, guid);
2673 talloc_free(blob.data);
2674 return NT_STATUS_IS_OK(status);
2679 * pull a single struct dom_sid from a ADS result
2680 * @param ads connection to ads server
2681 * @param msg Results of search
2682 * @param field Attribute to retrieve
2683 * @param sid Pointer to sid to store result
2684 * @return boolean inidicating success
2686 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2687 struct dom_sid *sid)
2689 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2693 * pull an array of struct dom_sids from a ADS result
2694 * @param ads connection to ads server
2695 * @param mem_ctx TALLOC_CTX for allocating sid array
2696 * @param msg Results of search
2697 * @param field Attribute to retrieve
2698 * @param sids pointer to sid array to allocate
2699 * @return the count of SIDs pulled
2701 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2702 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2704 struct berval **values;
2705 bool ret;
2706 int count, i;
2708 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2710 if (!values)
2711 return 0;
2713 for (i=0; values[i]; i++)
2714 /* nop */ ;
2716 if (i) {
2717 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2718 if (!(*sids)) {
2719 ldap_value_free_len(values);
2720 return 0;
2722 } else {
2723 (*sids) = NULL;
2726 count = 0;
2727 for (i=0; values[i]; i++) {
2728 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2729 if (ret) {
2730 DEBUG(10, ("pulling SID: %s\n",
2731 sid_string_dbg(&(*sids)[count])));
2732 count++;
2736 ldap_value_free_len(values);
2737 return count;
2741 * pull a struct security_descriptor from a ADS result
2742 * @param ads connection to ads server
2743 * @param mem_ctx TALLOC_CTX for allocating sid array
2744 * @param msg Results of search
2745 * @param field Attribute to retrieve
2746 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2747 * @return boolean inidicating success
2749 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2750 LDAPMessage *msg, const char *field,
2751 struct security_descriptor **sd)
2753 struct berval **values;
2754 bool ret = true;
2756 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2758 if (!values) return false;
2760 if (values[0]) {
2761 NTSTATUS status;
2762 status = unmarshall_sec_desc(mem_ctx,
2763 (uint8 *)values[0]->bv_val,
2764 values[0]->bv_len, sd);
2765 if (!NT_STATUS_IS_OK(status)) {
2766 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2767 nt_errstr(status)));
2768 ret = false;
2772 ldap_value_free_len(values);
2773 return ret;
2777 * in order to support usernames longer than 21 characters we need to
2778 * use both the sAMAccountName and the userPrincipalName attributes
2779 * It seems that not all users have the userPrincipalName attribute set
2781 * @param ads connection to ads server
2782 * @param mem_ctx TALLOC_CTX for allocating sid array
2783 * @param msg Results of search
2784 * @return the username
2786 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2787 LDAPMessage *msg)
2789 #if 0 /* JERRY */
2790 char *ret, *p;
2792 /* lookup_name() only works on the sAMAccountName to
2793 returning the username portion of userPrincipalName
2794 breaks winbindd_getpwnam() */
2796 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2797 if (ret && (p = strchr_m(ret, '@'))) {
2798 *p = 0;
2799 return ret;
2801 #endif
2802 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2807 * find the update serial number - this is the core of the ldap cache
2808 * @param ads connection to ads server
2809 * @param ads connection to ADS server
2810 * @param usn Pointer to retrieved update serial number
2811 * @return status of search
2813 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2815 const char *attrs[] = {"highestCommittedUSN", NULL};
2816 ADS_STATUS status;
2817 LDAPMessage *res;
2819 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2820 if (!ADS_ERR_OK(status))
2821 return status;
2823 if (ads_count_replies(ads, res) != 1) {
2824 ads_msgfree(ads, res);
2825 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2828 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2829 ads_msgfree(ads, res);
2830 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2833 ads_msgfree(ads, res);
2834 return ADS_SUCCESS;
2837 /* parse a ADS timestring - typical string is
2838 '20020917091222.0Z0' which means 09:12.22 17th September
2839 2002, timezone 0 */
2840 static time_t ads_parse_time(const char *str)
2842 struct tm tm;
2844 ZERO_STRUCT(tm);
2846 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2847 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2848 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2849 return 0;
2851 tm.tm_year -= 1900;
2852 tm.tm_mon -= 1;
2854 return timegm(&tm);
2857 /********************************************************************
2858 ********************************************************************/
2860 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2862 const char *attrs[] = {"currentTime", NULL};
2863 ADS_STATUS status;
2864 LDAPMessage *res;
2865 char *timestr;
2866 TALLOC_CTX *ctx;
2867 ADS_STRUCT *ads_s = ads;
2869 if (!(ctx = talloc_init("ads_current_time"))) {
2870 return ADS_ERROR(LDAP_NO_MEMORY);
2873 /* establish a new ldap tcp session if necessary */
2875 if ( !ads->ldap.ld ) {
2876 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2877 ads->server.ldap_server )) == NULL )
2879 goto done;
2881 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2882 status = ads_connect( ads_s );
2883 if ( !ADS_ERR_OK(status))
2884 goto done;
2887 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2888 if (!ADS_ERR_OK(status)) {
2889 goto done;
2892 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2893 if (!timestr) {
2894 ads_msgfree(ads_s, res);
2895 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2896 goto done;
2899 /* but save the time and offset in the original ADS_STRUCT */
2901 ads->config.current_time = ads_parse_time(timestr);
2903 if (ads->config.current_time != 0) {
2904 ads->auth.time_offset = ads->config.current_time - time(NULL);
2905 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2908 ads_msgfree(ads, res);
2910 status = ADS_SUCCESS;
2912 done:
2913 /* free any temporary ads connections */
2914 if ( ads_s != ads ) {
2915 ads_destroy( &ads_s );
2917 talloc_destroy(ctx);
2919 return status;
2922 /********************************************************************
2923 ********************************************************************/
2925 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2927 const char *attrs[] = {"domainFunctionality", NULL};
2928 ADS_STATUS status;
2929 LDAPMessage *res;
2930 ADS_STRUCT *ads_s = ads;
2932 *val = DS_DOMAIN_FUNCTION_2000;
2934 /* establish a new ldap tcp session if necessary */
2936 if ( !ads->ldap.ld ) {
2937 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2938 ads->server.ldap_server )) == NULL )
2940 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2941 goto done;
2943 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2944 status = ads_connect( ads_s );
2945 if ( !ADS_ERR_OK(status))
2946 goto done;
2949 /* If the attribute does not exist assume it is a Windows 2000
2950 functional domain */
2952 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2953 if (!ADS_ERR_OK(status)) {
2954 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2955 status = ADS_SUCCESS;
2957 goto done;
2960 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2961 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2963 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2966 ads_msgfree(ads, res);
2968 done:
2969 /* free any temporary ads connections */
2970 if ( ads_s != ads ) {
2971 ads_destroy( &ads_s );
2974 return status;
2978 * find the domain sid for our domain
2979 * @param ads connection to ads server
2980 * @param sid Pointer to domain sid
2981 * @return status of search
2983 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2985 const char *attrs[] = {"objectSid", NULL};
2986 LDAPMessage *res;
2987 ADS_STATUS rc;
2989 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2990 attrs, &res);
2991 if (!ADS_ERR_OK(rc)) return rc;
2992 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2993 ads_msgfree(ads, res);
2994 return ADS_ERROR_SYSTEM(ENOENT);
2996 ads_msgfree(ads, res);
2998 return ADS_SUCCESS;
3002 * find our site name
3003 * @param ads connection to ads server
3004 * @param mem_ctx Pointer to talloc context
3005 * @param site_name Pointer to the sitename
3006 * @return status of search
3008 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3010 ADS_STATUS status;
3011 LDAPMessage *res;
3012 const char *dn, *service_name;
3013 const char *attrs[] = { "dsServiceName", NULL };
3015 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3016 if (!ADS_ERR_OK(status)) {
3017 return status;
3020 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3021 if (service_name == NULL) {
3022 ads_msgfree(ads, res);
3023 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3026 ads_msgfree(ads, res);
3028 /* go up three levels */
3029 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3030 if (dn == NULL) {
3031 return ADS_ERROR(LDAP_NO_MEMORY);
3034 *site_name = talloc_strdup(mem_ctx, dn);
3035 if (*site_name == NULL) {
3036 return ADS_ERROR(LDAP_NO_MEMORY);
3039 return status;
3041 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3046 * find the site dn where a machine resides
3047 * @param ads connection to ads server
3048 * @param mem_ctx Pointer to talloc context
3049 * @param computer_name name of the machine
3050 * @param site_name Pointer to the sitename
3051 * @return status of search
3053 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3055 ADS_STATUS status;
3056 LDAPMessage *res;
3057 const char *parent, *filter;
3058 char *config_context = NULL;
3059 char *dn;
3061 /* shortcut a query */
3062 if (strequal(computer_name, ads->config.ldap_server_name)) {
3063 return ads_site_dn(ads, mem_ctx, site_dn);
3066 status = ads_config_path(ads, mem_ctx, &config_context);
3067 if (!ADS_ERR_OK(status)) {
3068 return status;
3071 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3072 if (filter == NULL) {
3073 return ADS_ERROR(LDAP_NO_MEMORY);
3076 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3077 filter, NULL, &res);
3078 if (!ADS_ERR_OK(status)) {
3079 return status;
3082 if (ads_count_replies(ads, res) != 1) {
3083 ads_msgfree(ads, res);
3084 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3087 dn = ads_get_dn(ads, mem_ctx, res);
3088 if (dn == NULL) {
3089 ads_msgfree(ads, res);
3090 return ADS_ERROR(LDAP_NO_MEMORY);
3093 /* go up three levels */
3094 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3095 if (parent == NULL) {
3096 ads_msgfree(ads, res);
3097 TALLOC_FREE(dn);
3098 return ADS_ERROR(LDAP_NO_MEMORY);
3101 *site_dn = talloc_strdup(mem_ctx, parent);
3102 if (*site_dn == NULL) {
3103 ads_msgfree(ads, res);
3104 TALLOC_FREE(dn);
3105 return ADS_ERROR(LDAP_NO_MEMORY);
3108 TALLOC_FREE(dn);
3109 ads_msgfree(ads, res);
3111 return status;
3115 * get the upn suffixes for a domain
3116 * @param ads connection to ads server
3117 * @param mem_ctx Pointer to talloc context
3118 * @param suffixes Pointer to an array of suffixes
3119 * @param num_suffixes Pointer to the number of suffixes
3120 * @return status of search
3122 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3124 ADS_STATUS status;
3125 LDAPMessage *res;
3126 const char *base;
3127 char *config_context = NULL;
3128 const char *attrs[] = { "uPNSuffixes", NULL };
3130 status = ads_config_path(ads, mem_ctx, &config_context);
3131 if (!ADS_ERR_OK(status)) {
3132 return status;
3135 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3136 if (base == NULL) {
3137 return ADS_ERROR(LDAP_NO_MEMORY);
3140 status = ads_search_dn(ads, &res, base, attrs);
3141 if (!ADS_ERR_OK(status)) {
3142 return status;
3145 if (ads_count_replies(ads, res) != 1) {
3146 ads_msgfree(ads, res);
3147 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3150 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3151 if ((*suffixes) == NULL) {
3152 ads_msgfree(ads, res);
3153 return ADS_ERROR(LDAP_NO_MEMORY);
3156 ads_msgfree(ads, res);
3158 return status;
3162 * get the joinable ous for a domain
3163 * @param ads connection to ads server
3164 * @param mem_ctx Pointer to talloc context
3165 * @param ous Pointer to an array of ous
3166 * @param num_ous Pointer to the number of ous
3167 * @return status of search
3169 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3170 TALLOC_CTX *mem_ctx,
3171 char ***ous,
3172 size_t *num_ous)
3174 ADS_STATUS status;
3175 LDAPMessage *res = NULL;
3176 LDAPMessage *msg = NULL;
3177 const char *attrs[] = { "dn", NULL };
3178 int count = 0;
3180 status = ads_search(ads, &res,
3181 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3182 attrs);
3183 if (!ADS_ERR_OK(status)) {
3184 return status;
3187 count = ads_count_replies(ads, res);
3188 if (count < 1) {
3189 ads_msgfree(ads, res);
3190 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3193 for (msg = ads_first_entry(ads, res); msg;
3194 msg = ads_next_entry(ads, msg)) {
3196 char *dn = NULL;
3198 dn = ads_get_dn(ads, talloc_tos(), msg);
3199 if (!dn) {
3200 ads_msgfree(ads, res);
3201 return ADS_ERROR(LDAP_NO_MEMORY);
3204 if (!add_string_to_array(mem_ctx, dn,
3205 (const char ***)ous,
3206 (int *)num_ous)) {
3207 TALLOC_FREE(dn);
3208 ads_msgfree(ads, res);
3209 return ADS_ERROR(LDAP_NO_MEMORY);
3212 TALLOC_FREE(dn);
3215 ads_msgfree(ads, res);
3217 return status;
3222 * pull a struct dom_sid from an extended dn string
3223 * @param mem_ctx TALLOC_CTX
3224 * @param extended_dn string
3225 * @param flags string type of extended_dn
3226 * @param sid pointer to a struct dom_sid
3227 * @return NT_STATUS_OK on success,
3228 * NT_INVALID_PARAMETER on error,
3229 * NT_STATUS_NOT_FOUND if no SID present
3231 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3232 const char *extended_dn,
3233 enum ads_extended_dn_flags flags,
3234 struct dom_sid *sid)
3236 char *p, *q, *dn;
3238 if (!extended_dn) {
3239 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3242 /* otherwise extended_dn gets stripped off */
3243 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3244 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3247 * ADS_EXTENDED_DN_HEX_STRING:
3248 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3250 * ADS_EXTENDED_DN_STRING (only with w2k3):
3251 * <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
3253 * Object with no SID, such as an Exchange Public Folder
3254 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3257 p = strchr(dn, ';');
3258 if (!p) {
3259 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3262 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3263 DEBUG(5,("No SID present in extended dn\n"));
3264 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3267 p += strlen(";<SID=");
3269 q = strchr(p, '>');
3270 if (!q) {
3271 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3274 *q = '\0';
3276 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3278 switch (flags) {
3280 case ADS_EXTENDED_DN_STRING:
3281 if (!string_to_sid(sid, p)) {
3282 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3284 break;
3285 case ADS_EXTENDED_DN_HEX_STRING: {
3286 fstring buf;
3287 size_t buf_len;
3289 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3290 if (buf_len == 0) {
3291 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3294 if (!sid_parse(buf, buf_len, sid)) {
3295 DEBUG(10,("failed to parse sid\n"));
3296 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3298 break;
3300 default:
3301 DEBUG(10,("unknown extended dn format\n"));
3302 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3305 return ADS_ERROR_NT(NT_STATUS_OK);
3308 /********************************************************************
3309 ********************************************************************/
3311 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3313 LDAPMessage *res = NULL;
3314 ADS_STATUS status;
3315 int count = 0;
3316 char *name = NULL;
3318 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3319 if (!ADS_ERR_OK(status)) {
3320 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3321 lp_netbios_name()));
3322 goto out;
3325 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3326 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3327 goto out;
3330 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3331 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3334 out:
3335 ads_msgfree(ads, res);
3337 return name;
3340 /********************************************************************
3341 ********************************************************************/
3343 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3345 LDAPMessage *res = NULL;
3346 ADS_STATUS status;
3347 int count = 0;
3348 char *name = NULL;
3350 status = ads_find_machine_acct(ads, &res, machine_name);
3351 if (!ADS_ERR_OK(status)) {
3352 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3353 lp_netbios_name()));
3354 goto out;
3357 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3358 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3359 goto out;
3362 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3363 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3366 out:
3367 ads_msgfree(ads, res);
3369 return name;
3372 /********************************************************************
3373 ********************************************************************/
3375 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3377 LDAPMessage *res = NULL;
3378 ADS_STATUS status;
3379 int count = 0;
3380 char *name = NULL;
3382 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3383 if (!ADS_ERR_OK(status)) {
3384 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3385 lp_netbios_name()));
3386 goto out;
3389 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3390 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3391 goto out;
3394 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3395 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3398 out:
3399 ads_msgfree(ads, res);
3401 return name;
3404 #if 0
3406 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3409 * Join a machine to a realm
3410 * Creates the machine account and sets the machine password
3411 * @param ads connection to ads server
3412 * @param machine name of host to add
3413 * @param org_unit Organizational unit to place machine in
3414 * @return status of join
3416 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3417 uint32 account_type, const char *org_unit)
3419 ADS_STATUS status;
3420 LDAPMessage *res = NULL;
3421 char *machine;
3423 /* machine name must be lowercase */
3424 machine = SMB_STRDUP(machine_name);
3425 strlower_m(machine);
3428 status = ads_find_machine_acct(ads, (void **)&res, machine);
3429 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3430 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3431 status = ads_leave_realm(ads, machine);
3432 if (!ADS_ERR_OK(status)) {
3433 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3434 machine, ads->config.realm));
3435 return status;
3439 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3440 if (!ADS_ERR_OK(status)) {
3441 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3442 SAFE_FREE(machine);
3443 return status;
3446 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3447 if (!ADS_ERR_OK(status)) {
3448 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3449 SAFE_FREE(machine);
3450 return status;
3453 SAFE_FREE(machine);
3454 ads_msgfree(ads, res);
3456 return status;
3458 #endif
3461 * Delete a machine from the realm
3462 * @param ads connection to ads server
3463 * @param hostname Machine to remove
3464 * @return status of delete
3466 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3468 ADS_STATUS status;
3469 void *msg;
3470 LDAPMessage *res;
3471 char *hostnameDN, *host;
3472 int rc;
3473 LDAPControl ldap_control;
3474 LDAPControl * pldap_control[2] = {NULL, NULL};
3476 pldap_control[0] = &ldap_control;
3477 memset(&ldap_control, 0, sizeof(LDAPControl));
3478 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3480 /* hostname must be lowercase */
3481 host = SMB_STRDUP(hostname);
3482 if (!strlower_m(host)) {
3483 SAFE_FREE(host);
3484 return ADS_ERROR_SYSTEM(EINVAL);
3487 status = ads_find_machine_acct(ads, &res, host);
3488 if (!ADS_ERR_OK(status)) {
3489 DEBUG(0, ("Host account for %s does not exist.\n", host));
3490 SAFE_FREE(host);
3491 return status;
3494 msg = ads_first_entry(ads, res);
3495 if (!msg) {
3496 SAFE_FREE(host);
3497 return ADS_ERROR_SYSTEM(ENOENT);
3500 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3501 if (hostnameDN == NULL) {
3502 SAFE_FREE(host);
3503 return ADS_ERROR_SYSTEM(ENOENT);
3506 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3507 if (rc) {
3508 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3509 }else {
3510 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3513 if (rc != LDAP_SUCCESS) {
3514 const char *attrs[] = { "cn", NULL };
3515 LDAPMessage *msg_sub;
3517 /* we only search with scope ONE, we do not expect any further
3518 * objects to be created deeper */
3520 status = ads_do_search_retry(ads, hostnameDN,
3521 LDAP_SCOPE_ONELEVEL,
3522 "(objectclass=*)", attrs, &res);
3524 if (!ADS_ERR_OK(status)) {
3525 SAFE_FREE(host);
3526 TALLOC_FREE(hostnameDN);
3527 return status;
3530 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3531 msg_sub = ads_next_entry(ads, msg_sub)) {
3533 char *dn = NULL;
3535 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3536 SAFE_FREE(host);
3537 TALLOC_FREE(hostnameDN);
3538 return ADS_ERROR(LDAP_NO_MEMORY);
3541 status = ads_del_dn(ads, dn);
3542 if (!ADS_ERR_OK(status)) {
3543 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3544 SAFE_FREE(host);
3545 TALLOC_FREE(dn);
3546 TALLOC_FREE(hostnameDN);
3547 return status;
3550 TALLOC_FREE(dn);
3553 /* there should be no subordinate objects anymore */
3554 status = ads_do_search_retry(ads, hostnameDN,
3555 LDAP_SCOPE_ONELEVEL,
3556 "(objectclass=*)", attrs, &res);
3558 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3559 SAFE_FREE(host);
3560 TALLOC_FREE(hostnameDN);
3561 return status;
3564 /* delete hostnameDN now */
3565 status = ads_del_dn(ads, hostnameDN);
3566 if (!ADS_ERR_OK(status)) {
3567 SAFE_FREE(host);
3568 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3569 TALLOC_FREE(hostnameDN);
3570 return status;
3574 TALLOC_FREE(hostnameDN);
3576 status = ads_find_machine_acct(ads, &res, host);
3577 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3578 DEBUG(3, ("Failed to remove host account.\n"));
3579 SAFE_FREE(host);
3580 return status;
3583 SAFE_FREE(host);
3584 return status;
3588 * pull all token-sids from an LDAP dn
3589 * @param ads connection to ads server
3590 * @param mem_ctx TALLOC_CTX for allocating sid array
3591 * @param dn of LDAP object
3592 * @param user_sid pointer to struct dom_sid (objectSid)
3593 * @param primary_group_sid pointer to struct dom_sid (self composed)
3594 * @param sids pointer to sid array to allocate
3595 * @param num_sids counter of SIDs pulled
3596 * @return status of token query
3598 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3599 TALLOC_CTX *mem_ctx,
3600 const char *dn,
3601 struct dom_sid *user_sid,
3602 struct dom_sid *primary_group_sid,
3603 struct dom_sid **sids,
3604 size_t *num_sids)
3606 ADS_STATUS status;
3607 LDAPMessage *res = NULL;
3608 int count = 0;
3609 size_t tmp_num_sids;
3610 struct dom_sid *tmp_sids;
3611 struct dom_sid tmp_user_sid;
3612 struct dom_sid tmp_primary_group_sid;
3613 uint32 pgid;
3614 const char *attrs[] = {
3615 "objectSid",
3616 "tokenGroups",
3617 "primaryGroupID",
3618 NULL
3621 status = ads_search_retry_dn(ads, &res, dn, attrs);
3622 if (!ADS_ERR_OK(status)) {
3623 return status;
3626 count = ads_count_replies(ads, res);
3627 if (count != 1) {
3628 ads_msgfree(ads, res);
3629 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3632 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3633 ads_msgfree(ads, res);
3634 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3637 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3638 ads_msgfree(ads, res);
3639 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3643 /* hack to compose the primary group sid without knowing the
3644 * domsid */
3646 struct dom_sid domsid;
3648 sid_copy(&domsid, &tmp_user_sid);
3650 if (!sid_split_rid(&domsid, NULL)) {
3651 ads_msgfree(ads, res);
3652 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3655 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3656 ads_msgfree(ads, res);
3657 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3661 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3663 if (tmp_num_sids == 0 || !tmp_sids) {
3664 ads_msgfree(ads, res);
3665 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3668 if (num_sids) {
3669 *num_sids = tmp_num_sids;
3672 if (sids) {
3673 *sids = tmp_sids;
3676 if (user_sid) {
3677 *user_sid = tmp_user_sid;
3680 if (primary_group_sid) {
3681 *primary_group_sid = tmp_primary_group_sid;
3684 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3686 ads_msgfree(ads, res);
3687 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3691 * Find a sAMAccoutName in LDAP
3692 * @param ads connection to ads server
3693 * @param mem_ctx TALLOC_CTX for allocating sid array
3694 * @param samaccountname to search
3695 * @param uac_ret uint32 pointer userAccountControl attribute value
3696 * @param dn_ret pointer to dn
3697 * @return status of token query
3699 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3700 TALLOC_CTX *mem_ctx,
3701 const char *samaccountname,
3702 uint32 *uac_ret,
3703 const char **dn_ret)
3705 ADS_STATUS status;
3706 const char *attrs[] = { "userAccountControl", NULL };
3707 const char *filter;
3708 LDAPMessage *res = NULL;
3709 char *dn = NULL;
3710 uint32 uac = 0;
3712 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3713 samaccountname);
3714 if (filter == NULL) {
3715 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3716 goto out;
3719 status = ads_do_search_all(ads, ads->config.bind_path,
3720 LDAP_SCOPE_SUBTREE,
3721 filter, attrs, &res);
3723 if (!ADS_ERR_OK(status)) {
3724 goto out;
3727 if (ads_count_replies(ads, res) != 1) {
3728 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3729 goto out;
3732 dn = ads_get_dn(ads, talloc_tos(), res);
3733 if (dn == NULL) {
3734 status = ADS_ERROR(LDAP_NO_MEMORY);
3735 goto out;
3738 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3739 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3740 goto out;
3743 if (uac_ret) {
3744 *uac_ret = uac;
3747 if (dn_ret) {
3748 *dn_ret = talloc_strdup(mem_ctx, dn);
3749 if (!*dn_ret) {
3750 status = ADS_ERROR(LDAP_NO_MEMORY);
3751 goto out;
3754 out:
3755 TALLOC_FREE(dn);
3756 ads_msgfree(ads, res);
3758 return status;
3762 * find our configuration path
3763 * @param ads connection to ads server
3764 * @param mem_ctx Pointer to talloc context
3765 * @param config_path Pointer to the config path
3766 * @return status of search
3768 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3769 TALLOC_CTX *mem_ctx,
3770 char **config_path)
3772 ADS_STATUS status;
3773 LDAPMessage *res = NULL;
3774 const char *config_context = NULL;
3775 const char *attrs[] = { "configurationNamingContext", NULL };
3777 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3778 "(objectclass=*)", attrs, &res);
3779 if (!ADS_ERR_OK(status)) {
3780 return status;
3783 config_context = ads_pull_string(ads, mem_ctx, res,
3784 "configurationNamingContext");
3785 ads_msgfree(ads, res);
3786 if (!config_context) {
3787 return ADS_ERROR(LDAP_NO_MEMORY);
3790 if (config_path) {
3791 *config_path = talloc_strdup(mem_ctx, config_context);
3792 if (!*config_path) {
3793 return ADS_ERROR(LDAP_NO_MEMORY);
3797 return ADS_ERROR(LDAP_SUCCESS);
3801 * find the displayName of an extended right
3802 * @param ads connection to ads server
3803 * @param config_path The config path
3804 * @param mem_ctx Pointer to talloc context
3805 * @param GUID struct of the rightsGUID
3806 * @return status of search
3808 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3809 const char *config_path,
3810 TALLOC_CTX *mem_ctx,
3811 const struct GUID *rights_guid)
3813 ADS_STATUS rc;
3814 LDAPMessage *res = NULL;
3815 char *expr = NULL;
3816 const char *attrs[] = { "displayName", NULL };
3817 const char *result = NULL;
3818 const char *path;
3820 if (!ads || !mem_ctx || !rights_guid) {
3821 goto done;
3824 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3825 GUID_string(mem_ctx, rights_guid));
3826 if (!expr) {
3827 goto done;
3830 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3831 if (!path) {
3832 goto done;
3835 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3836 expr, attrs, &res);
3837 if (!ADS_ERR_OK(rc)) {
3838 goto done;
3841 if (ads_count_replies(ads, res) != 1) {
3842 goto done;
3845 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3847 done:
3848 ads_msgfree(ads, res);
3849 return result;
3853 * verify or build and verify an account ou
3854 * @param mem_ctx Pointer to talloc context
3855 * @param ads connection to ads server
3856 * @param account_ou
3857 * @return status of search
3860 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3861 ADS_STRUCT *ads,
3862 const char **account_ou)
3864 char **exploded_dn;
3865 const char *name;
3866 char *ou_string;
3868 exploded_dn = ldap_explode_dn(*account_ou, 0);
3869 if (exploded_dn) {
3870 ldap_value_free(exploded_dn);
3871 return ADS_SUCCESS;
3874 ou_string = ads_ou_string(ads, *account_ou);
3875 if (!ou_string) {
3876 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3879 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3880 ads->config.bind_path);
3881 SAFE_FREE(ou_string);
3883 if (!name) {
3884 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3887 exploded_dn = ldap_explode_dn(name, 0);
3888 if (!exploded_dn) {
3889 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3891 ldap_value_free(exploded_dn);
3893 *account_ou = name;
3894 return ADS_SUCCESS;
3897 #endif