s3:smbd: change blocking.c to use fsp_fnum_dbg() for fsp->fnum logging.
[Samba/gebeck_regimport.git] / source3 / libads / ldap.c
blob5c77df1ade3f765ea028a59add86cc0b1ddb09ef
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"
33 #ifdef HAVE_LDAP
35 /**
36 * @file ldap.c
37 * @brief basic ldap client-side routines for ads server communications
39 * The routines contained here should do the necessary ldap calls for
40 * ads setups.
42 * Important note: attribute names passed into ads_ routines must
43 * already be in UTF-8 format. We do not convert them because in almost
44 * all cases, they are just ascii (which is represented with the same
45 * codepoints in UTF-8). This may have to change at some point
46 **/
49 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
51 static SIG_ATOMIC_T gotalarm;
53 /***************************************************************
54 Signal function to tell us we timed out.
55 ****************************************************************/
57 static void gotalarm_sig(int signum)
59 gotalarm = 1;
62 LDAP *ldap_open_with_timeout(const char *server,
63 struct sockaddr_storage *ss,
64 int port, unsigned int to)
66 LDAP *ldp = NULL;
68 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
69 "%u seconds\n", server, port, to));
71 #if defined(HAVE_LDAP_INIT_FD) && defined(SOCKET_WRAPPER)
72 /* Only use this private LDAP function if we are in make test,
73 * as this is the best way to get the emulated TCP socket into
74 * OpenLDAP */
75 if (socket_wrapper_dir() != NULL) {
76 int fd, ldap_err;
77 NTSTATUS status;
78 char *uri;
80 status = open_socket_out(ss, port, to, &fd);
82 if (!NT_STATUS_IS_OK(status)) {
83 return NULL;
86 #ifndef LDAP_PROTO_TCP
87 #define LDAP_PROTO_TCP 1
88 #endif
89 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
90 if (uri == NULL) {
91 return NULL;
93 ldap_err = ldap_init_fd(fd, LDAP_PROTO_TCP, uri, &ldp);
94 talloc_free(uri);
96 if (ldap_err != LDAP_SUCCESS) {
97 return NULL;
99 return ldp;
101 #endif
103 if (to) {
104 /* Setup timeout */
105 gotalarm = 0;
106 CatchSignal(SIGALRM, gotalarm_sig);
107 alarm(to);
108 /* End setup timeout. */
111 ldp = ldap_open(server, port);
113 if (ldp == NULL) {
114 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
115 server, port, strerror(errno)));
116 } else {
117 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
120 if (to) {
121 /* Teardown timeout. */
122 alarm(0);
123 CatchSignal(SIGALRM, SIG_IGN);
126 return ldp;
129 static int ldap_search_with_timeout(LDAP *ld,
130 LDAP_CONST char *base,
131 int scope,
132 LDAP_CONST char *filter,
133 char **attrs,
134 int attrsonly,
135 LDAPControl **sctrls,
136 LDAPControl **cctrls,
137 int sizelimit,
138 LDAPMessage **res )
140 int to = lp_ldap_timeout();
141 struct timeval timeout;
142 struct timeval *timeout_ptr = NULL;
143 int result;
145 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
146 gotalarm = 0;
148 if (to) {
149 timeout.tv_sec = to;
150 timeout.tv_usec = 0;
151 timeout_ptr = &timeout;
153 /* Setup alarm timeout. */
154 CatchSignal(SIGALRM, gotalarm_sig);
155 /* Make the alarm time one second beyond
156 the timout we're setting for the
157 remote search timeout, to allow that
158 to fire in preference. */
159 alarm(to+1);
160 /* End setup timeout. */
164 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
165 attrsonly, sctrls, cctrls, timeout_ptr,
166 sizelimit, res);
168 if (to) {
169 /* Teardown alarm timeout. */
170 CatchSignal(SIGALRM, SIG_IGN);
171 alarm(0);
174 if (gotalarm != 0)
175 return LDAP_TIMELIMIT_EXCEEDED;
178 * A bug in OpenLDAP means ldap_search_ext_s can return
179 * LDAP_SUCCESS but with a NULL res pointer. Cope with
180 * this. See bug #6279 for details. JRA.
183 if (*res == NULL) {
184 return LDAP_TIMELIMIT_EXCEEDED;
187 return result;
190 /**********************************************
191 Do client and server sitename match ?
192 **********************************************/
194 bool ads_sitename_match(ADS_STRUCT *ads)
196 if (ads->config.server_site_name == NULL &&
197 ads->config.client_site_name == NULL ) {
198 DEBUG(10,("ads_sitename_match: both null\n"));
199 return True;
201 if (ads->config.server_site_name &&
202 ads->config.client_site_name &&
203 strequal(ads->config.server_site_name,
204 ads->config.client_site_name)) {
205 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
206 return True;
208 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
209 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
210 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
211 return False;
214 /**********************************************
215 Is this the closest DC ?
216 **********************************************/
218 bool ads_closest_dc(ADS_STRUCT *ads)
220 if (ads->config.flags & NBT_SERVER_CLOSEST) {
221 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
222 return True;
225 /* not sure if this can ever happen */
226 if (ads_sitename_match(ads)) {
227 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
228 return True;
231 if (ads->config.client_site_name == NULL) {
232 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
233 return True;
236 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
237 ads->config.ldap_server_name));
239 return False;
244 try a connection to a given ldap server, returning True and setting the servers IP
245 in the ads struct if successful
247 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
249 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
250 TALLOC_CTX *frame = talloc_stackframe();
251 bool ret = false;
252 struct sockaddr_storage ss;
253 char addr[INET6_ADDRSTRLEN];
255 if (!server || !*server) {
256 TALLOC_FREE(frame);
257 return False;
260 if (!resolve_name(server, &ss, 0x20, true)) {
261 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
262 server ));
263 TALLOC_FREE(frame);
264 return false;
266 print_sockaddr(addr, sizeof(addr), &ss);
268 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
269 addr, ads->server.realm));
271 ZERO_STRUCT( cldap_reply );
273 if ( !ads_cldap_netlogon_5(frame, &ss, ads->server.realm, &cldap_reply ) ) {
274 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
275 ret = false;
276 goto out;
279 /* Check the CLDAP reply flags */
281 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
282 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
283 addr));
284 ret = false;
285 goto out;
288 /* Fill in the ads->config values */
290 SAFE_FREE(ads->config.realm);
291 SAFE_FREE(ads->config.bind_path);
292 SAFE_FREE(ads->config.ldap_server_name);
293 SAFE_FREE(ads->config.server_site_name);
294 SAFE_FREE(ads->config.client_site_name);
295 SAFE_FREE(ads->server.workgroup);
297 ads->config.flags = cldap_reply.server_type;
298 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
299 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
300 strupper_m(ads->config.realm);
301 ads->config.bind_path = ads_build_dn(ads->config.realm);
302 if (*cldap_reply.server_site) {
303 ads->config.server_site_name =
304 SMB_STRDUP(cldap_reply.server_site);
306 if (*cldap_reply.client_site) {
307 ads->config.client_site_name =
308 SMB_STRDUP(cldap_reply.client_site);
310 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
312 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
313 ads->ldap.ss = ss;
315 /* Store our site name. */
316 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
317 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
319 ret = true;
321 out:
323 TALLOC_FREE(frame);
324 return ret;
327 /**********************************************************************
328 Try to find an AD dc using our internal name resolution routines
329 Try the realm first and then then workgroup name if netbios is not
330 disabled
331 **********************************************************************/
333 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
335 const char *c_domain;
336 const char *c_realm;
337 int count, i=0;
338 struct ip_service *ip_list;
339 const char *realm;
340 const char *domain;
341 bool got_realm = False;
342 bool use_own_domain = False;
343 char *sitename;
344 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
346 /* if the realm and workgroup are both empty, assume they are ours */
348 /* realm */
349 c_realm = ads->server.realm;
351 if ( !c_realm || !*c_realm ) {
352 /* special case where no realm and no workgroup means our own */
353 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
354 use_own_domain = True;
355 c_realm = lp_realm();
359 if (c_realm && *c_realm)
360 got_realm = True;
362 /* we need to try once with the realm name and fallback to the
363 netbios domain name if we fail (if netbios has not been disabled */
365 if ( !got_realm && !lp_disable_netbios() ) {
366 c_realm = ads->server.workgroup;
367 if (!c_realm || !*c_realm) {
368 if ( use_own_domain )
369 c_realm = lp_workgroup();
373 if ( !c_realm || !*c_realm ) {
374 DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know "
375 "what to do\n"));
376 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
379 if ( use_own_domain ) {
380 c_domain = lp_workgroup();
381 } else {
382 c_domain = ads->server.workgroup;
385 realm = c_realm;
386 domain = c_domain;
389 * In case of LDAP we use get_dc_name() as that
390 * creates the custom krb5.conf file
392 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
393 fstring srv_name;
394 struct sockaddr_storage ip_out;
396 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
397 (got_realm ? "realm" : "domain"), realm));
399 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
401 * we call ads_try_connect() to fill in the
402 * ads->config details
404 if (ads_try_connect(ads, srv_name, false)) {
405 return NT_STATUS_OK;
409 return NT_STATUS_NO_LOGON_SERVERS;
412 sitename = sitename_fetch(realm);
414 again:
416 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
417 (got_realm ? "realm" : "domain"), realm));
419 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
420 if (!NT_STATUS_IS_OK(status)) {
421 /* fall back to netbios if we can */
422 if ( got_realm && !lp_disable_netbios() ) {
423 got_realm = False;
424 goto again;
427 SAFE_FREE(sitename);
428 return status;
431 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
432 for ( i=0; i<count; i++ ) {
433 char server[INET6_ADDRSTRLEN];
435 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
437 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
438 continue;
440 if (!got_realm) {
441 /* realm in this case is a workgroup name. We need
442 to ignore any IP addresses in the negative connection
443 cache that match ip addresses returned in the ad realm
444 case. It sucks that I have to reproduce the logic above... */
445 c_realm = ads->server.realm;
446 if ( !c_realm || !*c_realm ) {
447 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
448 c_realm = lp_realm();
451 if (c_realm && *c_realm &&
452 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
453 /* Ensure we add the workgroup name for this
454 IP address as negative too. */
455 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
456 continue;
460 if ( ads_try_connect(ads, server, false) ) {
461 SAFE_FREE(ip_list);
462 SAFE_FREE(sitename);
463 return NT_STATUS_OK;
466 /* keep track of failures */
467 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
470 SAFE_FREE(ip_list);
472 /* In case we failed to contact one of our closest DC on our site we
473 * need to try to find another DC, retry with a site-less SRV DNS query
474 * - Guenther */
476 if (sitename) {
477 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
478 "trying to find another DC\n", sitename));
479 SAFE_FREE(sitename);
480 namecache_delete(realm, 0x1C);
481 goto again;
484 return NT_STATUS_NO_LOGON_SERVERS;
487 /*********************************************************************
488 *********************************************************************/
490 static NTSTATUS ads_lookup_site(void)
492 ADS_STRUCT *ads = NULL;
493 ADS_STATUS ads_status;
494 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
496 ads = ads_init(lp_realm(), NULL, NULL);
497 if (!ads) {
498 return NT_STATUS_NO_MEMORY;
501 /* The NO_BIND here will find a DC and set the client site
502 but not establish the TCP connection */
504 ads->auth.flags = ADS_AUTH_NO_BIND;
505 ads_status = ads_connect(ads);
506 if (!ADS_ERR_OK(ads_status)) {
507 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
508 ads_errstr(ads_status)));
510 nt_status = ads_ntstatus(ads_status);
512 if (ads) {
513 ads_destroy(&ads);
516 return nt_status;
519 /*********************************************************************
520 *********************************************************************/
522 static const char* host_dns_domain(const char *fqdn)
524 const char *p = fqdn;
526 /* go to next char following '.' */
528 if ((p = strchr_m(fqdn, '.')) != NULL) {
529 p++;
532 return p;
537 * Connect to the Global Catalog server
538 * @param ads Pointer to an existing ADS_STRUCT
539 * @return status of connection
541 * Simple wrapper around ads_connect() that fills in the
542 * GC ldap server information
545 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
547 TALLOC_CTX *frame = talloc_stackframe();
548 struct dns_rr_srv *gcs_list;
549 int num_gcs;
550 const char *realm = ads->server.realm;
551 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
552 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
553 int i;
554 bool done = false;
555 char *sitename = NULL;
556 const char *dns_hosts_file;
558 if (!realm)
559 realm = lp_realm();
561 if ((sitename = sitename_fetch(realm)) == NULL) {
562 ads_lookup_site();
563 sitename = sitename_fetch(realm);
566 dns_hosts_file = lp_parm_const_string(-1, "resolv", "host file", NULL);
567 do {
568 /* We try once with a sitename and once without
569 (unless we don't have a sitename and then we're
570 done */
572 if (sitename == NULL)
573 done = true;
575 nt_status = ads_dns_query_gcs(frame, dns_hosts_file,
576 realm, sitename,
577 &gcs_list, &num_gcs);
579 SAFE_FREE(sitename);
581 if (!NT_STATUS_IS_OK(nt_status)) {
582 ads_status = ADS_ERROR_NT(nt_status);
583 goto done;
586 /* Loop until we get a successful connection or have gone
587 through them all. When connecting a GC server, make sure that
588 the realm is the server's DNS name and not the forest root */
590 for (i=0; i<num_gcs; i++) {
591 ads->server.gc = true;
592 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
593 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
594 ads_status = ads_connect(ads);
595 if (ADS_ERR_OK(ads_status)) {
596 /* Reset the bind_dn to "". A Global Catalog server
597 may host multiple domain trees in a forest.
598 Windows 2003 GC server will accept "" as the search
599 path to imply search all domain trees in the forest */
601 SAFE_FREE(ads->config.bind_path);
602 ads->config.bind_path = SMB_STRDUP("");
605 goto done;
607 SAFE_FREE(ads->server.ldap_server);
608 SAFE_FREE(ads->server.realm);
611 TALLOC_FREE(gcs_list);
612 num_gcs = 0;
613 } while (!done);
615 done:
616 SAFE_FREE(sitename);
617 talloc_destroy(frame);
619 return ads_status;
624 * Connect to the LDAP server
625 * @param ads Pointer to an existing ADS_STRUCT
626 * @return status of connection
628 ADS_STATUS ads_connect(ADS_STRUCT *ads)
630 int version = LDAP_VERSION3;
631 ADS_STATUS status;
632 NTSTATUS ntstatus;
633 char addr[INET6_ADDRSTRLEN];
635 ZERO_STRUCT(ads->ldap);
636 ads->ldap.last_attempt = time_mono(NULL);
637 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
639 /* try with a user specified server */
641 if (DEBUGLEVEL >= 11) {
642 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
643 DEBUG(11,("ads_connect: entering\n"));
644 DEBUGADD(11,("%s\n", s));
645 TALLOC_FREE(s);
648 if (ads->server.ldap_server)
650 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
651 goto got_connection;
654 /* The choice of which GC use is handled one level up in
655 ads_connect_gc(). If we continue on from here with
656 ads_find_dc() we will get GC searches on port 389 which
657 doesn't work. --jerry */
659 if (ads->server.gc == true) {
660 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
664 ntstatus = ads_find_dc(ads);
665 if (NT_STATUS_IS_OK(ntstatus)) {
666 goto got_connection;
669 status = ADS_ERROR_NT(ntstatus);
670 goto out;
672 got_connection:
674 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
675 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
677 if (!ads->auth.user_name) {
678 /* Must use the userPrincipalName value here or sAMAccountName
679 and not servicePrincipalName; found by Guenther Deschner */
681 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
682 DEBUG(0,("ads_connect: asprintf fail.\n"));
683 ads->auth.user_name = NULL;
687 if (!ads->auth.realm) {
688 ads->auth.realm = SMB_STRDUP(ads->config.realm);
691 if (!ads->auth.kdc_server) {
692 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
693 ads->auth.kdc_server = SMB_STRDUP(addr);
696 /* If the caller() requested no LDAP bind, then we are done */
698 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
699 status = ADS_SUCCESS;
700 goto out;
703 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
704 if (!ads->ldap.mem_ctx) {
705 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
706 goto out;
709 /* Otherwise setup the TCP LDAP session */
711 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
712 &ads->ldap.ss,
713 ads->ldap.port, lp_ldap_timeout());
714 if (ads->ldap.ld == NULL) {
715 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
716 goto out;
718 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
720 /* cache the successful connection for workgroup and realm */
721 if (ads_closest_dc(ads)) {
722 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
723 saf_store( ads->server.realm, ads->config.ldap_server_name);
726 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
728 if ( lp_ldap_ssl_ads() ) {
729 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
730 if (!ADS_ERR_OK(status)) {
731 goto out;
735 /* fill in the current time and offsets */
737 status = ads_current_time( ads );
738 if ( !ADS_ERR_OK(status) ) {
739 goto out;
742 /* Now do the bind */
744 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
745 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
746 goto out;
749 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
750 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
751 goto out;
754 status = ads_sasl_bind(ads);
756 out:
757 if (DEBUGLEVEL >= 11) {
758 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
759 DEBUG(11,("ads_connect: leaving with: %s\n",
760 ads_errstr(status)));
761 DEBUGADD(11,("%s\n", s));
762 TALLOC_FREE(s);
765 return status;
769 * Connect to the LDAP server using given credentials
770 * @param ads Pointer to an existing ADS_STRUCT
771 * @return status of connection
773 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
775 ads->auth.flags |= ADS_AUTH_USER_CREDS;
777 return ads_connect(ads);
781 * Disconnect the LDAP server
782 * @param ads Pointer to an existing ADS_STRUCT
784 void ads_disconnect(ADS_STRUCT *ads)
786 if (ads->ldap.ld) {
787 ldap_unbind(ads->ldap.ld);
788 ads->ldap.ld = NULL;
790 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
791 ads->ldap.wrap_ops->disconnect(ads);
793 if (ads->ldap.mem_ctx) {
794 talloc_free(ads->ldap.mem_ctx);
796 ZERO_STRUCT(ads->ldap);
800 Duplicate a struct berval into talloc'ed memory
802 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
804 struct berval *value;
806 if (!in_val) return NULL;
808 value = talloc_zero(ctx, struct berval);
809 if (value == NULL)
810 return NULL;
811 if (in_val->bv_len == 0) return value;
813 value->bv_len = in_val->bv_len;
814 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
815 in_val->bv_len);
816 return value;
820 Make a values list out of an array of (struct berval *)
822 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
823 const struct berval **in_vals)
825 struct berval **values;
826 int i;
828 if (!in_vals) return NULL;
829 for (i=0; in_vals[i]; i++)
830 ; /* count values */
831 values = talloc_zero_array(ctx, struct berval *, i+1);
832 if (!values) return NULL;
834 for (i=0; in_vals[i]; i++) {
835 values[i] = dup_berval(ctx, in_vals[i]);
837 return values;
841 UTF8-encode a values list out of an array of (char *)
843 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
845 char **values;
846 int i;
847 size_t size;
849 if (!in_vals) return NULL;
850 for (i=0; in_vals[i]; i++)
851 ; /* count values */
852 values = talloc_zero_array(ctx, char *, i+1);
853 if (!values) return NULL;
855 for (i=0; in_vals[i]; i++) {
856 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
857 TALLOC_FREE(values);
858 return NULL;
861 return values;
865 Pull a (char *) array out of a UTF8-encoded values list
867 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
869 char **values;
870 int i;
871 size_t converted_size;
873 if (!in_vals) return NULL;
874 for (i=0; in_vals[i]; i++)
875 ; /* count values */
876 values = talloc_zero_array(ctx, char *, i+1);
877 if (!values) return NULL;
879 for (i=0; in_vals[i]; i++) {
880 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
881 &converted_size)) {
882 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
883 "%s", strerror(errno)));
886 return values;
890 * Do a search with paged results. cookie must be null on the first
891 * call, and then returned on each subsequent call. It will be null
892 * again when the entire search is complete
893 * @param ads connection to ads server
894 * @param bind_path Base dn for the search
895 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
896 * @param expr Search expression - specified in local charset
897 * @param attrs Attributes to retrieve - specified in utf8 or ascii
898 * @param res ** which will contain results - free res* with ads_msgfree()
899 * @param count Number of entries retrieved on this page
900 * @param cookie The paged results cookie to be returned on subsequent calls
901 * @return status of search
903 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
904 const char *bind_path,
905 int scope, const char *expr,
906 const char **attrs, void *args,
907 LDAPMessage **res,
908 int *count, struct berval **cookie)
910 int rc, i, version;
911 char *utf8_expr, *utf8_path, **search_attrs = NULL;
912 size_t converted_size;
913 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
914 BerElement *cookie_be = NULL;
915 struct berval *cookie_bv= NULL;
916 BerElement *ext_be = NULL;
917 struct berval *ext_bv= NULL;
919 TALLOC_CTX *ctx;
920 ads_control *external_control = (ads_control *) args;
922 *res = NULL;
924 if (!(ctx = talloc_init("ads_do_paged_search_args")))
925 return ADS_ERROR(LDAP_NO_MEMORY);
927 /* 0 means the conversion worked but the result was empty
928 so we only fail if it's -1. In any case, it always
929 at least nulls out the dest */
930 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
931 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
933 rc = LDAP_NO_MEMORY;
934 goto done;
937 if (!attrs || !(*attrs))
938 search_attrs = NULL;
939 else {
940 /* This would be the utf8-encoded version...*/
941 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
942 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
943 rc = LDAP_NO_MEMORY;
944 goto done;
948 /* Paged results only available on ldap v3 or later */
949 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
950 if (version < LDAP_VERSION3) {
951 rc = LDAP_NOT_SUPPORTED;
952 goto done;
955 cookie_be = ber_alloc_t(LBER_USE_DER);
956 if (*cookie) {
957 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
958 ber_bvfree(*cookie); /* don't need it from last time */
959 *cookie = NULL;
960 } else {
961 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
963 ber_flatten(cookie_be, &cookie_bv);
964 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
965 PagedResults.ldctl_iscritical = (char) 1;
966 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
967 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
969 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
970 NoReferrals.ldctl_iscritical = (char) 0;
971 NoReferrals.ldctl_value.bv_len = 0;
972 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
974 if (external_control &&
975 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
976 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
978 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
979 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
981 /* win2k does not accept a ldctl_value beeing passed in */
983 if (external_control->val != 0) {
985 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
986 rc = LDAP_NO_MEMORY;
987 goto done;
990 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
991 rc = LDAP_NO_MEMORY;
992 goto done;
994 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
995 rc = LDAP_NO_MEMORY;
996 goto done;
999 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
1000 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
1002 } else {
1003 ExternalCtrl.ldctl_value.bv_len = 0;
1004 ExternalCtrl.ldctl_value.bv_val = NULL;
1007 controls[0] = &NoReferrals;
1008 controls[1] = &PagedResults;
1009 controls[2] = &ExternalCtrl;
1010 controls[3] = NULL;
1012 } else {
1013 controls[0] = &NoReferrals;
1014 controls[1] = &PagedResults;
1015 controls[2] = NULL;
1018 /* we need to disable referrals as the openldap libs don't
1019 handle them and paged results at the same time. Using them
1020 together results in the result record containing the server
1021 page control being removed from the result list (tridge/jmcd)
1023 leaving this in despite the control that says don't generate
1024 referrals, in case the server doesn't support it (jmcd)
1026 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1028 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1029 search_attrs, 0, controls,
1030 NULL, LDAP_NO_LIMIT,
1031 (LDAPMessage **)res);
1033 ber_free(cookie_be, 1);
1034 ber_bvfree(cookie_bv);
1036 if (rc) {
1037 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1038 ldap_err2string(rc)));
1039 goto done;
1042 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1043 NULL, &rcontrols, 0);
1045 if (!rcontrols) {
1046 goto done;
1049 for (i=0; rcontrols[i]; i++) {
1050 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1051 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1052 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1053 &cookie_bv);
1054 /* the berval is the cookie, but must be freed when
1055 it is all done */
1056 if (cookie_bv->bv_len) /* still more to do */
1057 *cookie=ber_bvdup(cookie_bv);
1058 else
1059 *cookie=NULL;
1060 ber_bvfree(cookie_bv);
1061 ber_free(cookie_be, 1);
1062 break;
1065 ldap_controls_free(rcontrols);
1067 done:
1068 talloc_destroy(ctx);
1070 if (ext_be) {
1071 ber_free(ext_be, 1);
1074 if (ext_bv) {
1075 ber_bvfree(ext_bv);
1078 /* if/when we decide to utf8-encode attrs, take out this next line */
1079 TALLOC_FREE(search_attrs);
1081 return ADS_ERROR(rc);
1084 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1085 int scope, const char *expr,
1086 const char **attrs, LDAPMessage **res,
1087 int *count, struct berval **cookie)
1089 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1094 * Get all results for a search. This uses ads_do_paged_search() to return
1095 * all entries in a large search.
1096 * @param ads connection to ads server
1097 * @param bind_path Base dn for the search
1098 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1099 * @param expr Search expression
1100 * @param attrs Attributes to retrieve
1101 * @param res ** which will contain results - free res* with ads_msgfree()
1102 * @return status of search
1104 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1105 int scope, const char *expr,
1106 const char **attrs, void *args,
1107 LDAPMessage **res)
1109 struct berval *cookie = NULL;
1110 int count = 0;
1111 ADS_STATUS status;
1113 *res = NULL;
1114 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1115 &count, &cookie);
1117 if (!ADS_ERR_OK(status))
1118 return status;
1120 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1121 while (cookie) {
1122 LDAPMessage *res2 = NULL;
1123 ADS_STATUS status2;
1124 LDAPMessage *msg, *next;
1126 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1127 attrs, args, &res2, &count, &cookie);
1129 if (!ADS_ERR_OK(status2)) break;
1131 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1132 that this works on all ldap libs, but I have only tested with openldap */
1133 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1134 next = ads_next_message(ads, msg);
1135 ldap_add_result_entry((LDAPMessage **)res, msg);
1137 /* note that we do not free res2, as the memory is now
1138 part of the main returned list */
1140 #else
1141 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1142 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1143 #endif
1145 return status;
1148 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1149 int scope, const char *expr,
1150 const char **attrs, LDAPMessage **res)
1152 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1155 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1156 int scope, const char *expr,
1157 const char **attrs, uint32 sd_flags,
1158 LDAPMessage **res)
1160 ads_control args;
1162 args.control = ADS_SD_FLAGS_OID;
1163 args.val = sd_flags;
1164 args.critical = True;
1166 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1171 * Run a function on all results for a search. Uses ads_do_paged_search() and
1172 * runs the function as each page is returned, using ads_process_results()
1173 * @param ads connection to ads server
1174 * @param bind_path Base dn for the search
1175 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1176 * @param expr Search expression - specified in local charset
1177 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1178 * @param fn Function which takes attr name, values list, and data_area
1179 * @param data_area Pointer which is passed to function on each call
1180 * @return status of search
1182 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1183 int scope, const char *expr, const char **attrs,
1184 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1185 void *data_area)
1187 struct berval *cookie = NULL;
1188 int count = 0;
1189 ADS_STATUS status;
1190 LDAPMessage *res;
1192 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1193 &count, &cookie);
1195 if (!ADS_ERR_OK(status)) return status;
1197 ads_process_results(ads, res, fn, data_area);
1198 ads_msgfree(ads, res);
1200 while (cookie) {
1201 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1202 &res, &count, &cookie);
1204 if (!ADS_ERR_OK(status)) break;
1206 ads_process_results(ads, res, fn, data_area);
1207 ads_msgfree(ads, res);
1210 return status;
1214 * Do a search with a timeout.
1215 * @param ads connection to ads server
1216 * @param bind_path Base dn for the search
1217 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1218 * @param expr Search expression
1219 * @param attrs Attributes to retrieve
1220 * @param res ** which will contain results - free res* with ads_msgfree()
1221 * @return status of search
1223 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1224 const char *expr,
1225 const char **attrs, LDAPMessage **res)
1227 int rc;
1228 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1229 size_t converted_size;
1230 TALLOC_CTX *ctx;
1232 *res = NULL;
1233 if (!(ctx = talloc_init("ads_do_search"))) {
1234 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1235 return ADS_ERROR(LDAP_NO_MEMORY);
1238 /* 0 means the conversion worked but the result was empty
1239 so we only fail if it's negative. In any case, it always
1240 at least nulls out the dest */
1241 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1242 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1244 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1245 rc = LDAP_NO_MEMORY;
1246 goto done;
1249 if (!attrs || !(*attrs))
1250 search_attrs = NULL;
1251 else {
1252 /* This would be the utf8-encoded version...*/
1253 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1254 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1256 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1257 rc = LDAP_NO_MEMORY;
1258 goto done;
1262 /* see the note in ads_do_paged_search - we *must* disable referrals */
1263 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1265 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1266 search_attrs, 0, NULL, NULL,
1267 LDAP_NO_LIMIT,
1268 (LDAPMessage **)res);
1270 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1271 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1272 rc = 0;
1275 done:
1276 talloc_destroy(ctx);
1277 /* if/when we decide to utf8-encode attrs, take out this next line */
1278 TALLOC_FREE(search_attrs);
1279 return ADS_ERROR(rc);
1282 * Do a general ADS search
1283 * @param ads connection to ads server
1284 * @param res ** which will contain results - free res* with ads_msgfree()
1285 * @param expr Search expression
1286 * @param attrs Attributes to retrieve
1287 * @return status of search
1289 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1290 const char *expr, const char **attrs)
1292 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1293 expr, attrs, res);
1297 * Do a search on a specific DistinguishedName
1298 * @param ads connection to ads server
1299 * @param res ** which will contain results - free res* with ads_msgfree()
1300 * @param dn DistinguishName to search
1301 * @param attrs Attributes to retrieve
1302 * @return status of search
1304 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1305 const char *dn, const char **attrs)
1307 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1308 attrs, res);
1312 * Free up memory from a ads_search
1313 * @param ads connection to ads server
1314 * @param msg Search results to free
1316 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1318 if (!msg) return;
1319 ldap_msgfree(msg);
1323 * Get a dn from search results
1324 * @param ads connection to ads server
1325 * @param msg Search result
1326 * @return dn string
1328 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1330 char *utf8_dn, *unix_dn;
1331 size_t converted_size;
1333 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1335 if (!utf8_dn) {
1336 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1337 return NULL;
1340 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1341 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1342 utf8_dn ));
1343 return NULL;
1345 ldap_memfree(utf8_dn);
1346 return unix_dn;
1350 * Get the parent from a dn
1351 * @param dn the dn to return the parent from
1352 * @return parent dn string
1354 char *ads_parent_dn(const char *dn)
1356 char *p;
1358 if (dn == NULL) {
1359 return NULL;
1362 p = strchr(dn, ',');
1364 if (p == NULL) {
1365 return NULL;
1368 return p+1;
1372 * Find a machine account given a hostname
1373 * @param ads connection to ads server
1374 * @param res ** which will contain results - free res* with ads_msgfree()
1375 * @param host Hostname to search for
1376 * @return status of search
1378 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1379 const char *machine)
1381 ADS_STATUS status;
1382 char *expr;
1383 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1385 *res = NULL;
1387 /* the easiest way to find a machine account anywhere in the tree
1388 is to look for hostname$ */
1389 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1390 DEBUG(1, ("asprintf failed!\n"));
1391 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1394 status = ads_search(ads, res, expr, attrs);
1395 SAFE_FREE(expr);
1396 return status;
1400 * Initialize a list of mods to be used in a modify request
1401 * @param ctx An initialized TALLOC_CTX
1402 * @return allocated ADS_MODLIST
1404 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1406 #define ADS_MODLIST_ALLOC_SIZE 10
1407 LDAPMod **mods;
1409 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1410 /* -1 is safety to make sure we don't go over the end.
1411 need to reset it to NULL before doing ldap modify */
1412 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1414 return (ADS_MODLIST)mods;
1419 add an attribute to the list, with values list already constructed
1421 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1422 int mod_op, const char *name,
1423 const void *_invals)
1425 const void **invals = (const void **)_invals;
1426 int curmod;
1427 LDAPMod **modlist = (LDAPMod **) *mods;
1428 struct berval **ber_values = NULL;
1429 char **char_values = NULL;
1431 if (!invals) {
1432 mod_op = LDAP_MOD_DELETE;
1433 } else {
1434 if (mod_op & LDAP_MOD_BVALUES)
1435 ber_values = ads_dup_values(ctx,
1436 (const struct berval **)invals);
1437 else
1438 char_values = ads_push_strvals(ctx,
1439 (const char **) invals);
1442 /* find the first empty slot */
1443 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1444 curmod++);
1445 if (modlist[curmod] == (LDAPMod *) -1) {
1446 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1447 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1448 return ADS_ERROR(LDAP_NO_MEMORY);
1449 memset(&modlist[curmod], 0,
1450 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1451 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1452 *mods = (ADS_MODLIST)modlist;
1455 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1456 return ADS_ERROR(LDAP_NO_MEMORY);
1457 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1458 if (mod_op & LDAP_MOD_BVALUES) {
1459 modlist[curmod]->mod_bvalues = ber_values;
1460 } else if (mod_op & LDAP_MOD_DELETE) {
1461 modlist[curmod]->mod_values = NULL;
1462 } else {
1463 modlist[curmod]->mod_values = char_values;
1466 modlist[curmod]->mod_op = mod_op;
1467 return ADS_ERROR(LDAP_SUCCESS);
1471 * Add a single string value to a mod list
1472 * @param ctx An initialized TALLOC_CTX
1473 * @param mods An initialized ADS_MODLIST
1474 * @param name The attribute name to add
1475 * @param val The value to add - NULL means DELETE
1476 * @return ADS STATUS indicating success of add
1478 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1479 const char *name, const char *val)
1481 const char *values[2];
1483 values[0] = val;
1484 values[1] = NULL;
1486 if (!val)
1487 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1488 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1492 * Add an array of string values to a mod list
1493 * @param ctx An initialized TALLOC_CTX
1494 * @param mods An initialized ADS_MODLIST
1495 * @param name The attribute name to add
1496 * @param vals The array of string values to add - NULL means DELETE
1497 * @return ADS STATUS indicating success of add
1499 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1500 const char *name, const char **vals)
1502 if (!vals)
1503 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1504 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1505 name, (const void **) vals);
1508 #if 0
1510 * Add a single ber-encoded value to a mod list
1511 * @param ctx An initialized TALLOC_CTX
1512 * @param mods An initialized ADS_MODLIST
1513 * @param name The attribute name to add
1514 * @param val The value to add - NULL means DELETE
1515 * @return ADS STATUS indicating success of add
1517 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1518 const char *name, const struct berval *val)
1520 const struct berval *values[2];
1522 values[0] = val;
1523 values[1] = NULL;
1524 if (!val)
1525 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1526 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1527 name, (const void **) values);
1529 #endif
1532 * Perform an ldap modify
1533 * @param ads connection to ads server
1534 * @param mod_dn DistinguishedName to modify
1535 * @param mods list of modifications to perform
1536 * @return status of modify
1538 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1540 int ret,i;
1541 char *utf8_dn = NULL;
1542 size_t converted_size;
1544 this control is needed to modify that contains a currently
1545 non-existent attribute (but allowable for the object) to run
1547 LDAPControl PermitModify = {
1548 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1549 {0, NULL},
1550 (char) 1};
1551 LDAPControl *controls[2];
1553 controls[0] = &PermitModify;
1554 controls[1] = NULL;
1556 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1557 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1560 /* find the end of the list, marked by NULL or -1 */
1561 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1562 /* make sure the end of the list is NULL */
1563 mods[i] = NULL;
1564 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1565 (LDAPMod **) mods, controls, NULL);
1566 TALLOC_FREE(utf8_dn);
1567 return ADS_ERROR(ret);
1571 * Perform an ldap add
1572 * @param ads connection to ads server
1573 * @param new_dn DistinguishedName to add
1574 * @param mods list of attributes and values for DN
1575 * @return status of add
1577 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1579 int ret, i;
1580 char *utf8_dn = NULL;
1581 size_t converted_size;
1583 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1584 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1585 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1588 /* find the end of the list, marked by NULL or -1 */
1589 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1590 /* make sure the end of the list is NULL */
1591 mods[i] = NULL;
1593 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1594 TALLOC_FREE(utf8_dn);
1595 return ADS_ERROR(ret);
1599 * Delete a DistinguishedName
1600 * @param ads connection to ads server
1601 * @param new_dn DistinguishedName to delete
1602 * @return status of delete
1604 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1606 int ret;
1607 char *utf8_dn = NULL;
1608 size_t converted_size;
1609 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1610 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1611 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1614 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1615 TALLOC_FREE(utf8_dn);
1616 return ADS_ERROR(ret);
1620 * Build an org unit string
1621 * if org unit is Computers or blank then assume a container, otherwise
1622 * assume a / separated list of organisational units.
1623 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1624 * @param ads connection to ads server
1625 * @param org_unit Organizational unit
1626 * @return org unit string - caller must free
1628 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1630 char *ret = NULL;
1632 if (!org_unit || !*org_unit) {
1634 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1636 /* samba4 might not yet respond to a wellknownobject-query */
1637 return ret ? ret : SMB_STRDUP("cn=Computers");
1640 if (strequal(org_unit, "Computers")) {
1641 return SMB_STRDUP("cn=Computers");
1644 /* jmcd: removed "\\" from the separation chars, because it is
1645 needed as an escape for chars like '#' which are valid in an
1646 OU name */
1647 return ads_build_path(org_unit, "/", "ou=", 1);
1651 * Get a org unit string for a well-known GUID
1652 * @param ads connection to ads server
1653 * @param wknguid Well known GUID
1654 * @return org unit string - caller must free
1656 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1658 ADS_STATUS status;
1659 LDAPMessage *res = NULL;
1660 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1661 **bind_dn_exp = NULL;
1662 const char *attrs[] = {"distinguishedName", NULL};
1663 int new_ln, wkn_ln, bind_ln, i;
1665 if (wknguid == NULL) {
1666 return NULL;
1669 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1670 DEBUG(1, ("asprintf failed!\n"));
1671 return NULL;
1674 status = ads_search_dn(ads, &res, base, attrs);
1675 if (!ADS_ERR_OK(status)) {
1676 DEBUG(1,("Failed while searching for: %s\n", base));
1677 goto out;
1680 if (ads_count_replies(ads, res) != 1) {
1681 goto out;
1684 /* substitute the bind-path from the well-known-guid-search result */
1685 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1686 if (!wkn_dn) {
1687 goto out;
1690 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1691 if (!wkn_dn_exp) {
1692 goto out;
1695 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1696 if (!bind_dn_exp) {
1697 goto out;
1700 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1702 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1705 new_ln = wkn_ln - bind_ln;
1707 ret = SMB_STRDUP(wkn_dn_exp[0]);
1708 if (!ret) {
1709 goto out;
1712 for (i=1; i < new_ln; i++) {
1713 char *s = NULL;
1715 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1716 SAFE_FREE(ret);
1717 goto out;
1720 SAFE_FREE(ret);
1721 ret = SMB_STRDUP(s);
1722 free(s);
1723 if (!ret) {
1724 goto out;
1728 out:
1729 SAFE_FREE(base);
1730 ads_msgfree(ads, res);
1731 TALLOC_FREE(wkn_dn);
1732 if (wkn_dn_exp) {
1733 ldap_value_free(wkn_dn_exp);
1735 if (bind_dn_exp) {
1736 ldap_value_free(bind_dn_exp);
1739 return ret;
1743 * Adds (appends) an item to an attribute array, rather then
1744 * replacing the whole list
1745 * @param ctx An initialized TALLOC_CTX
1746 * @param mods An initialized ADS_MODLIST
1747 * @param name name of the ldap attribute to append to
1748 * @param vals an array of values to add
1749 * @return status of addition
1752 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1753 const char *name, const char **vals)
1755 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1756 (const void *) vals);
1760 * Determines the an account's current KVNO via an LDAP lookup
1761 * @param ads An initialized ADS_STRUCT
1762 * @param account_name the NT samaccountname.
1763 * @return the kvno for the account, or -1 in case of a failure.
1766 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1768 LDAPMessage *res = NULL;
1769 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1770 char *filter;
1771 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1772 char *dn_string = NULL;
1773 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1775 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1776 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1777 return kvno;
1779 ret = ads_search(ads, &res, filter, attrs);
1780 SAFE_FREE(filter);
1781 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1782 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1783 ads_msgfree(ads, res);
1784 return kvno;
1787 dn_string = ads_get_dn(ads, talloc_tos(), res);
1788 if (!dn_string) {
1789 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1790 ads_msgfree(ads, res);
1791 return kvno;
1793 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1794 TALLOC_FREE(dn_string);
1796 /* ---------------------------------------------------------
1797 * 0 is returned as a default KVNO from this point on...
1798 * This is done because Windows 2000 does not support key
1799 * version numbers. Chances are that a failure in the next
1800 * step is simply due to Windows 2000 being used for a
1801 * domain controller. */
1802 kvno = 0;
1804 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1805 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1806 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1807 ads_msgfree(ads, res);
1808 return kvno;
1811 /* Success */
1812 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1813 ads_msgfree(ads, res);
1814 return kvno;
1818 * Determines the computer account's current KVNO via an LDAP lookup
1819 * @param ads An initialized ADS_STRUCT
1820 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1821 * @return the kvno for the computer account, or -1 in case of a failure.
1824 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1826 char *computer_account = NULL;
1827 uint32_t kvno = -1;
1829 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1830 return kvno;
1833 kvno = ads_get_kvno(ads, computer_account);
1834 free(computer_account);
1836 return kvno;
1840 * This clears out all registered spn's for a given hostname
1841 * @param ads An initilaized ADS_STRUCT
1842 * @param machine_name the NetBIOS name of the computer.
1843 * @return 0 upon success, non-zero otherwise.
1846 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1848 TALLOC_CTX *ctx;
1849 LDAPMessage *res = NULL;
1850 ADS_MODLIST mods;
1851 const char *servicePrincipalName[1] = {NULL};
1852 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1853 char *dn_string = NULL;
1855 ret = ads_find_machine_acct(ads, &res, machine_name);
1856 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1857 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1858 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1859 ads_msgfree(ads, res);
1860 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1863 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1864 ctx = talloc_init("ads_clear_service_principal_names");
1865 if (!ctx) {
1866 ads_msgfree(ads, res);
1867 return ADS_ERROR(LDAP_NO_MEMORY);
1870 if (!(mods = ads_init_mods(ctx))) {
1871 talloc_destroy(ctx);
1872 ads_msgfree(ads, res);
1873 return ADS_ERROR(LDAP_NO_MEMORY);
1875 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1876 if (!ADS_ERR_OK(ret)) {
1877 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1878 ads_msgfree(ads, res);
1879 talloc_destroy(ctx);
1880 return ret;
1882 dn_string = ads_get_dn(ads, talloc_tos(), res);
1883 if (!dn_string) {
1884 talloc_destroy(ctx);
1885 ads_msgfree(ads, res);
1886 return ADS_ERROR(LDAP_NO_MEMORY);
1888 ret = ads_gen_mod(ads, dn_string, mods);
1889 TALLOC_FREE(dn_string);
1890 if (!ADS_ERR_OK(ret)) {
1891 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1892 machine_name));
1893 ads_msgfree(ads, res);
1894 talloc_destroy(ctx);
1895 return ret;
1898 ads_msgfree(ads, res);
1899 talloc_destroy(ctx);
1900 return ret;
1904 * This adds a service principal name to an existing computer account
1905 * (found by hostname) in AD.
1906 * @param ads An initialized ADS_STRUCT
1907 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1908 * @param my_fqdn The fully qualified DNS name of the machine
1909 * @param spn A string of the service principal to add, i.e. 'host'
1910 * @return 0 upon sucess, or non-zero if a failure occurs
1913 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1914 const char *my_fqdn, const char *spn)
1916 ADS_STATUS ret;
1917 TALLOC_CTX *ctx;
1918 LDAPMessage *res = NULL;
1919 char *psp1, *psp2;
1920 ADS_MODLIST mods;
1921 char *dn_string = NULL;
1922 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1924 ret = ads_find_machine_acct(ads, &res, machine_name);
1925 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1926 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1927 machine_name));
1928 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1929 spn, machine_name, ads->config.realm));
1930 ads_msgfree(ads, res);
1931 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1934 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1935 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1936 ads_msgfree(ads, res);
1937 return ADS_ERROR(LDAP_NO_MEMORY);
1940 /* add short name spn */
1942 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1943 talloc_destroy(ctx);
1944 ads_msgfree(ads, res);
1945 return ADS_ERROR(LDAP_NO_MEMORY);
1947 strupper_m(psp1);
1948 strlower_m(&psp1[strlen(spn)]);
1949 servicePrincipalName[0] = psp1;
1951 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1952 psp1, machine_name));
1955 /* add fully qualified spn */
1957 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1958 ret = ADS_ERROR(LDAP_NO_MEMORY);
1959 goto out;
1961 strupper_m(psp2);
1962 strlower_m(&psp2[strlen(spn)]);
1963 servicePrincipalName[1] = psp2;
1965 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1966 psp2, machine_name));
1968 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1969 ret = ADS_ERROR(LDAP_NO_MEMORY);
1970 goto out;
1973 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1974 if (!ADS_ERR_OK(ret)) {
1975 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1976 goto out;
1979 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1980 ret = ADS_ERROR(LDAP_NO_MEMORY);
1981 goto out;
1984 ret = ads_gen_mod(ads, dn_string, mods);
1985 if (!ADS_ERR_OK(ret)) {
1986 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1987 goto out;
1990 out:
1991 TALLOC_FREE( ctx );
1992 ads_msgfree(ads, res);
1993 return ret;
1997 * adds a machine account to the ADS server
1998 * @param ads An intialized ADS_STRUCT
1999 * @param machine_name - the NetBIOS machine name of this account.
2000 * @param account_type A number indicating the type of account to create
2001 * @param org_unit The LDAP path in which to place this account
2002 * @return 0 upon success, or non-zero otherwise
2005 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2006 const char *org_unit)
2008 ADS_STATUS ret;
2009 char *samAccountName, *controlstr;
2010 TALLOC_CTX *ctx;
2011 ADS_MODLIST mods;
2012 char *machine_escaped = NULL;
2013 char *new_dn;
2014 const char *objectClass[] = {"top", "person", "organizationalPerson",
2015 "user", "computer", NULL};
2016 LDAPMessage *res = NULL;
2017 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2018 UF_DONT_EXPIRE_PASSWD |\
2019 UF_ACCOUNTDISABLE );
2021 if (!(ctx = talloc_init("ads_add_machine_acct")))
2022 return ADS_ERROR(LDAP_NO_MEMORY);
2024 ret = ADS_ERROR(LDAP_NO_MEMORY);
2026 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2027 if (!machine_escaped) {
2028 goto done;
2031 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2032 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2034 if ( !new_dn || !samAccountName ) {
2035 goto done;
2038 #ifndef ENCTYPE_ARCFOUR_HMAC
2039 acct_control |= UF_USE_DES_KEY_ONLY;
2040 #endif
2042 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2043 goto done;
2046 if (!(mods = ads_init_mods(ctx))) {
2047 goto done;
2050 ads_mod_str(ctx, &mods, "cn", machine_name);
2051 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2052 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2053 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2055 ret = ads_gen_add(ads, new_dn, mods);
2057 done:
2058 SAFE_FREE(machine_escaped);
2059 ads_msgfree(ads, res);
2060 talloc_destroy(ctx);
2062 return ret;
2066 * move a machine account to another OU on the ADS server
2067 * @param ads - An intialized ADS_STRUCT
2068 * @param machine_name - the NetBIOS machine name of this account.
2069 * @param org_unit - The LDAP path in which to place this account
2070 * @param moved - whether we moved the machine account (optional)
2071 * @return 0 upon success, or non-zero otherwise
2074 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2075 const char *org_unit, bool *moved)
2077 ADS_STATUS rc;
2078 int ldap_status;
2079 LDAPMessage *res = NULL;
2080 char *filter = NULL;
2081 char *computer_dn = NULL;
2082 char *parent_dn;
2083 char *computer_rdn = NULL;
2084 bool need_move = False;
2086 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2087 rc = ADS_ERROR(LDAP_NO_MEMORY);
2088 goto done;
2091 /* Find pre-existing machine */
2092 rc = ads_search(ads, &res, filter, NULL);
2093 if (!ADS_ERR_OK(rc)) {
2094 goto done;
2097 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2098 if (!computer_dn) {
2099 rc = ADS_ERROR(LDAP_NO_MEMORY);
2100 goto done;
2103 parent_dn = ads_parent_dn(computer_dn);
2104 if (strequal(parent_dn, org_unit)) {
2105 goto done;
2108 need_move = True;
2110 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2111 rc = ADS_ERROR(LDAP_NO_MEMORY);
2112 goto done;
2115 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2116 org_unit, 1, NULL, NULL);
2117 rc = ADS_ERROR(ldap_status);
2119 done:
2120 ads_msgfree(ads, res);
2121 SAFE_FREE(filter);
2122 TALLOC_FREE(computer_dn);
2123 SAFE_FREE(computer_rdn);
2125 if (!ADS_ERR_OK(rc)) {
2126 need_move = False;
2129 if (moved) {
2130 *moved = need_move;
2133 return rc;
2137 dump a binary result from ldap
2139 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2141 int i, j;
2142 for (i=0; values[i]; i++) {
2143 printf("%s: ", field);
2144 for (j=0; j<values[i]->bv_len; j++) {
2145 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2147 printf("\n");
2151 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2153 int i;
2154 for (i=0; values[i]; i++) {
2155 NTSTATUS status;
2156 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2157 struct GUID guid;
2159 status = GUID_from_ndr_blob(&in, &guid);
2160 if (NT_STATUS_IS_OK(status)) {
2161 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2162 } else {
2163 printf("%s: INVALID GUID\n", field);
2169 dump a sid result from ldap
2171 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2173 int i;
2174 for (i=0; values[i]; i++) {
2175 struct dom_sid sid;
2176 fstring tmp;
2177 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2178 return;
2180 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2185 dump ntSecurityDescriptor
2187 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2189 TALLOC_CTX *frame = talloc_stackframe();
2190 struct security_descriptor *psd;
2191 NTSTATUS status;
2193 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2194 values[0]->bv_len, &psd);
2195 if (!NT_STATUS_IS_OK(status)) {
2196 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2197 nt_errstr(status)));
2198 TALLOC_FREE(frame);
2199 return;
2202 if (psd) {
2203 ads_disp_sd(ads, talloc_tos(), psd);
2206 TALLOC_FREE(frame);
2210 dump a string result from ldap
2212 static void dump_string(const char *field, char **values)
2214 int i;
2215 for (i=0; values[i]; i++) {
2216 printf("%s: %s\n", field, values[i]);
2221 dump a field from LDAP on stdout
2222 used for debugging
2225 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2227 const struct {
2228 const char *name;
2229 bool string;
2230 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2231 } handlers[] = {
2232 {"objectGUID", False, dump_guid},
2233 {"netbootGUID", False, dump_guid},
2234 {"nTSecurityDescriptor", False, dump_sd},
2235 {"dnsRecord", False, dump_binary},
2236 {"objectSid", False, dump_sid},
2237 {"tokenGroups", False, dump_sid},
2238 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2239 {"tokengroupsGlobalandUniversal", False, dump_sid},
2240 {"mS-DS-CreatorSID", False, dump_sid},
2241 {"msExchMailboxGuid", False, dump_guid},
2242 {NULL, True, NULL}
2244 int i;
2246 if (!field) { /* must be end of an entry */
2247 printf("\n");
2248 return False;
2251 for (i=0; handlers[i].name; i++) {
2252 if (strcasecmp_m(handlers[i].name, field) == 0) {
2253 if (!values) /* first time, indicate string or not */
2254 return handlers[i].string;
2255 handlers[i].handler(ads, field, (struct berval **) values);
2256 break;
2259 if (!handlers[i].name) {
2260 if (!values) /* first time, indicate string conversion */
2261 return True;
2262 dump_string(field, (char **)values);
2264 return False;
2268 * Dump a result from LDAP on stdout
2269 * used for debugging
2270 * @param ads connection to ads server
2271 * @param res Results to dump
2274 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2276 ads_process_results(ads, res, ads_dump_field, NULL);
2280 * Walk through results, calling a function for each entry found.
2281 * The function receives a field name, a berval * array of values,
2282 * and a data area passed through from the start. The function is
2283 * called once with null for field and values at the end of each
2284 * entry.
2285 * @param ads connection to ads server
2286 * @param res Results to process
2287 * @param fn Function for processing each result
2288 * @param data_area user-defined area to pass to function
2290 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2291 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2292 void *data_area)
2294 LDAPMessage *msg;
2295 TALLOC_CTX *ctx;
2296 size_t converted_size;
2298 if (!(ctx = talloc_init("ads_process_results")))
2299 return;
2301 for (msg = ads_first_entry(ads, res); msg;
2302 msg = ads_next_entry(ads, msg)) {
2303 char *utf8_field;
2304 BerElement *b;
2306 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2307 (LDAPMessage *)msg,&b);
2308 utf8_field;
2309 utf8_field=ldap_next_attribute(ads->ldap.ld,
2310 (LDAPMessage *)msg,b)) {
2311 struct berval **ber_vals;
2312 char **str_vals, **utf8_vals;
2313 char *field;
2314 bool string;
2316 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2317 &converted_size))
2319 DEBUG(0,("ads_process_results: "
2320 "pull_utf8_talloc failed: %s",
2321 strerror(errno)));
2324 string = fn(ads, field, NULL, data_area);
2326 if (string) {
2327 utf8_vals = ldap_get_values(ads->ldap.ld,
2328 (LDAPMessage *)msg, field);
2329 str_vals = ads_pull_strvals(ctx,
2330 (const char **) utf8_vals);
2331 fn(ads, field, (void **) str_vals, data_area);
2332 ldap_value_free(utf8_vals);
2333 } else {
2334 ber_vals = ldap_get_values_len(ads->ldap.ld,
2335 (LDAPMessage *)msg, field);
2336 fn(ads, field, (void **) ber_vals, data_area);
2338 ldap_value_free_len(ber_vals);
2340 ldap_memfree(utf8_field);
2342 ber_free(b, 0);
2343 talloc_free_children(ctx);
2344 fn(ads, NULL, NULL, data_area); /* completed an entry */
2347 talloc_destroy(ctx);
2351 * count how many replies are in a LDAPMessage
2352 * @param ads connection to ads server
2353 * @param res Results to count
2354 * @return number of replies
2356 int ads_count_replies(ADS_STRUCT *ads, void *res)
2358 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2362 * pull the first entry from a ADS result
2363 * @param ads connection to ads server
2364 * @param res Results of search
2365 * @return first entry from result
2367 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2369 return ldap_first_entry(ads->ldap.ld, res);
2373 * pull the next entry from a ADS result
2374 * @param ads connection to ads server
2375 * @param res Results of search
2376 * @return next entry from result
2378 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2380 return ldap_next_entry(ads->ldap.ld, res);
2384 * pull the first message from a ADS result
2385 * @param ads connection to ads server
2386 * @param res Results of search
2387 * @return first message from result
2389 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2391 return ldap_first_message(ads->ldap.ld, res);
2395 * pull the next message from a ADS result
2396 * @param ads connection to ads server
2397 * @param res Results of search
2398 * @return next message from result
2400 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2402 return ldap_next_message(ads->ldap.ld, res);
2406 * pull a single string from a ADS result
2407 * @param ads connection to ads server
2408 * @param mem_ctx TALLOC_CTX to use for allocating result string
2409 * @param msg Results of search
2410 * @param field Attribute to retrieve
2411 * @return Result string in talloc context
2413 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2414 const char *field)
2416 char **values;
2417 char *ret = NULL;
2418 char *ux_string;
2419 size_t converted_size;
2421 values = ldap_get_values(ads->ldap.ld, msg, field);
2422 if (!values)
2423 return NULL;
2425 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2426 &converted_size))
2428 ret = ux_string;
2430 ldap_value_free(values);
2431 return ret;
2435 * pull an array of strings from a ADS result
2436 * @param ads connection to ads server
2437 * @param mem_ctx TALLOC_CTX to use for allocating result string
2438 * @param msg Results of search
2439 * @param field Attribute to retrieve
2440 * @return Result strings in talloc context
2442 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2443 LDAPMessage *msg, const char *field,
2444 size_t *num_values)
2446 char **values;
2447 char **ret = NULL;
2448 int i;
2449 size_t converted_size;
2451 values = ldap_get_values(ads->ldap.ld, msg, field);
2452 if (!values)
2453 return NULL;
2455 *num_values = ldap_count_values(values);
2457 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2458 if (!ret) {
2459 ldap_value_free(values);
2460 return NULL;
2463 for (i=0;i<*num_values;i++) {
2464 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2465 &converted_size))
2467 ldap_value_free(values);
2468 return NULL;
2471 ret[i] = NULL;
2473 ldap_value_free(values);
2474 return ret;
2478 * pull an array of strings from a ADS result
2479 * (handle large multivalue attributes with range retrieval)
2480 * @param ads connection to ads server
2481 * @param mem_ctx TALLOC_CTX to use for allocating result string
2482 * @param msg Results of search
2483 * @param field Attribute to retrieve
2484 * @param current_strings strings returned by a previous call to this function
2485 * @param next_attribute The next query should ask for this attribute
2486 * @param num_values How many values did we get this time?
2487 * @param more_values Are there more values to get?
2488 * @return Result strings in talloc context
2490 char **ads_pull_strings_range(ADS_STRUCT *ads,
2491 TALLOC_CTX *mem_ctx,
2492 LDAPMessage *msg, const char *field,
2493 char **current_strings,
2494 const char **next_attribute,
2495 size_t *num_strings,
2496 bool *more_strings)
2498 char *attr;
2499 char *expected_range_attrib, *range_attr;
2500 BerElement *ptr = NULL;
2501 char **strings;
2502 char **new_strings;
2503 size_t num_new_strings;
2504 unsigned long int range_start;
2505 unsigned long int range_end;
2507 /* we might have been given the whole lot anyway */
2508 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2509 *more_strings = False;
2510 return strings;
2513 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2515 /* look for Range result */
2516 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2517 attr;
2518 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2519 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2520 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2521 range_attr = attr;
2522 break;
2524 ldap_memfree(attr);
2526 if (!attr) {
2527 ber_free(ptr, 0);
2528 /* nothing here - this field is just empty */
2529 *more_strings = False;
2530 return NULL;
2533 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2534 &range_start, &range_end) == 2) {
2535 *more_strings = True;
2536 } else {
2537 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2538 &range_start) == 1) {
2539 *more_strings = False;
2540 } else {
2541 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2542 range_attr));
2543 ldap_memfree(range_attr);
2544 *more_strings = False;
2545 return NULL;
2549 if ((*num_strings) != range_start) {
2550 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2551 " - aborting range retreival\n",
2552 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2553 ldap_memfree(range_attr);
2554 *more_strings = False;
2555 return NULL;
2558 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2560 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2561 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2562 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2563 range_attr, (unsigned long int)range_end - range_start + 1,
2564 (unsigned long int)num_new_strings));
2565 ldap_memfree(range_attr);
2566 *more_strings = False;
2567 return NULL;
2570 strings = talloc_realloc(mem_ctx, current_strings, char *,
2571 *num_strings + num_new_strings);
2573 if (strings == NULL) {
2574 ldap_memfree(range_attr);
2575 *more_strings = False;
2576 return NULL;
2579 if (new_strings && num_new_strings) {
2580 memcpy(&strings[*num_strings], new_strings,
2581 sizeof(*new_strings) * num_new_strings);
2584 (*num_strings) += num_new_strings;
2586 if (*more_strings) {
2587 *next_attribute = talloc_asprintf(mem_ctx,
2588 "%s;range=%d-*",
2589 field,
2590 (int)*num_strings);
2592 if (!*next_attribute) {
2593 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2594 ldap_memfree(range_attr);
2595 *more_strings = False;
2596 return NULL;
2600 ldap_memfree(range_attr);
2602 return strings;
2606 * pull a single uint32 from a ADS result
2607 * @param ads connection to ads server
2608 * @param msg Results of search
2609 * @param field Attribute to retrieve
2610 * @param v Pointer to int to store result
2611 * @return boolean inidicating success
2613 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2614 uint32 *v)
2616 char **values;
2618 values = ldap_get_values(ads->ldap.ld, msg, field);
2619 if (!values)
2620 return False;
2621 if (!values[0]) {
2622 ldap_value_free(values);
2623 return False;
2626 *v = atoi(values[0]);
2627 ldap_value_free(values);
2628 return True;
2632 * pull a single objectGUID from an ADS result
2633 * @param ads connection to ADS server
2634 * @param msg results of search
2635 * @param guid 37-byte area to receive text guid
2636 * @return boolean indicating success
2638 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2640 DATA_BLOB blob;
2641 NTSTATUS status;
2643 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2644 &blob)) {
2645 return false;
2648 status = GUID_from_ndr_blob(&blob, guid);
2649 talloc_free(blob.data);
2650 return NT_STATUS_IS_OK(status);
2655 * pull a single struct dom_sid from a ADS result
2656 * @param ads connection to ads server
2657 * @param msg Results of search
2658 * @param field Attribute to retrieve
2659 * @param sid Pointer to sid to store result
2660 * @return boolean inidicating success
2662 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2663 struct dom_sid *sid)
2665 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2669 * pull an array of struct dom_sids from a ADS result
2670 * @param ads connection to ads server
2671 * @param mem_ctx TALLOC_CTX for allocating sid array
2672 * @param msg Results of search
2673 * @param field Attribute to retrieve
2674 * @param sids pointer to sid array to allocate
2675 * @return the count of SIDs pulled
2677 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2678 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2680 struct berval **values;
2681 bool ret;
2682 int count, i;
2684 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2686 if (!values)
2687 return 0;
2689 for (i=0; values[i]; i++)
2690 /* nop */ ;
2692 if (i) {
2693 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2694 if (!(*sids)) {
2695 ldap_value_free_len(values);
2696 return 0;
2698 } else {
2699 (*sids) = NULL;
2702 count = 0;
2703 for (i=0; values[i]; i++) {
2704 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2705 if (ret) {
2706 DEBUG(10, ("pulling SID: %s\n",
2707 sid_string_dbg(&(*sids)[count])));
2708 count++;
2712 ldap_value_free_len(values);
2713 return count;
2717 * pull a struct security_descriptor from a ADS result
2718 * @param ads connection to ads server
2719 * @param mem_ctx TALLOC_CTX for allocating sid array
2720 * @param msg Results of search
2721 * @param field Attribute to retrieve
2722 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2723 * @return boolean inidicating success
2725 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2726 LDAPMessage *msg, const char *field,
2727 struct security_descriptor **sd)
2729 struct berval **values;
2730 bool ret = true;
2732 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2734 if (!values) return false;
2736 if (values[0]) {
2737 NTSTATUS status;
2738 status = unmarshall_sec_desc(mem_ctx,
2739 (uint8 *)values[0]->bv_val,
2740 values[0]->bv_len, sd);
2741 if (!NT_STATUS_IS_OK(status)) {
2742 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2743 nt_errstr(status)));
2744 ret = false;
2748 ldap_value_free_len(values);
2749 return ret;
2753 * in order to support usernames longer than 21 characters we need to
2754 * use both the sAMAccountName and the userPrincipalName attributes
2755 * It seems that not all users have the userPrincipalName attribute set
2757 * @param ads connection to ads server
2758 * @param mem_ctx TALLOC_CTX for allocating sid array
2759 * @param msg Results of search
2760 * @return the username
2762 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2763 LDAPMessage *msg)
2765 #if 0 /* JERRY */
2766 char *ret, *p;
2768 /* lookup_name() only works on the sAMAccountName to
2769 returning the username portion of userPrincipalName
2770 breaks winbindd_getpwnam() */
2772 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2773 if (ret && (p = strchr_m(ret, '@'))) {
2774 *p = 0;
2775 return ret;
2777 #endif
2778 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2783 * find the update serial number - this is the core of the ldap cache
2784 * @param ads connection to ads server
2785 * @param ads connection to ADS server
2786 * @param usn Pointer to retrieved update serial number
2787 * @return status of search
2789 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2791 const char *attrs[] = {"highestCommittedUSN", NULL};
2792 ADS_STATUS status;
2793 LDAPMessage *res;
2795 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2796 if (!ADS_ERR_OK(status))
2797 return status;
2799 if (ads_count_replies(ads, res) != 1) {
2800 ads_msgfree(ads, res);
2801 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2804 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2805 ads_msgfree(ads, res);
2806 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2809 ads_msgfree(ads, res);
2810 return ADS_SUCCESS;
2813 /* parse a ADS timestring - typical string is
2814 '20020917091222.0Z0' which means 09:12.22 17th September
2815 2002, timezone 0 */
2816 static time_t ads_parse_time(const char *str)
2818 struct tm tm;
2820 ZERO_STRUCT(tm);
2822 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2823 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2824 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2825 return 0;
2827 tm.tm_year -= 1900;
2828 tm.tm_mon -= 1;
2830 return timegm(&tm);
2833 /********************************************************************
2834 ********************************************************************/
2836 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2838 const char *attrs[] = {"currentTime", NULL};
2839 ADS_STATUS status;
2840 LDAPMessage *res;
2841 char *timestr;
2842 TALLOC_CTX *ctx;
2843 ADS_STRUCT *ads_s = ads;
2845 if (!(ctx = talloc_init("ads_current_time"))) {
2846 return ADS_ERROR(LDAP_NO_MEMORY);
2849 /* establish a new ldap tcp session if necessary */
2851 if ( !ads->ldap.ld ) {
2852 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2853 ads->server.ldap_server )) == NULL )
2855 goto done;
2857 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2858 status = ads_connect( ads_s );
2859 if ( !ADS_ERR_OK(status))
2860 goto done;
2863 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2864 if (!ADS_ERR_OK(status)) {
2865 goto done;
2868 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2869 if (!timestr) {
2870 ads_msgfree(ads_s, res);
2871 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2872 goto done;
2875 /* but save the time and offset in the original ADS_STRUCT */
2877 ads->config.current_time = ads_parse_time(timestr);
2879 if (ads->config.current_time != 0) {
2880 ads->auth.time_offset = ads->config.current_time - time(NULL);
2881 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2884 ads_msgfree(ads, res);
2886 status = ADS_SUCCESS;
2888 done:
2889 /* free any temporary ads connections */
2890 if ( ads_s != ads ) {
2891 ads_destroy( &ads_s );
2893 talloc_destroy(ctx);
2895 return status;
2898 /********************************************************************
2899 ********************************************************************/
2901 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2903 const char *attrs[] = {"domainFunctionality", NULL};
2904 ADS_STATUS status;
2905 LDAPMessage *res;
2906 ADS_STRUCT *ads_s = ads;
2908 *val = DS_DOMAIN_FUNCTION_2000;
2910 /* establish a new ldap tcp session if necessary */
2912 if ( !ads->ldap.ld ) {
2913 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2914 ads->server.ldap_server )) == NULL )
2916 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2917 goto done;
2919 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2920 status = ads_connect( ads_s );
2921 if ( !ADS_ERR_OK(status))
2922 goto done;
2925 /* If the attribute does not exist assume it is a Windows 2000
2926 functional domain */
2928 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2929 if (!ADS_ERR_OK(status)) {
2930 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2931 status = ADS_SUCCESS;
2933 goto done;
2936 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2937 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2939 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2942 ads_msgfree(ads, res);
2944 done:
2945 /* free any temporary ads connections */
2946 if ( ads_s != ads ) {
2947 ads_destroy( &ads_s );
2950 return status;
2954 * find the domain sid for our domain
2955 * @param ads connection to ads server
2956 * @param sid Pointer to domain sid
2957 * @return status of search
2959 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2961 const char *attrs[] = {"objectSid", NULL};
2962 LDAPMessage *res;
2963 ADS_STATUS rc;
2965 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2966 attrs, &res);
2967 if (!ADS_ERR_OK(rc)) return rc;
2968 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2969 ads_msgfree(ads, res);
2970 return ADS_ERROR_SYSTEM(ENOENT);
2972 ads_msgfree(ads, res);
2974 return ADS_SUCCESS;
2978 * find our site name
2979 * @param ads connection to ads server
2980 * @param mem_ctx Pointer to talloc context
2981 * @param site_name Pointer to the sitename
2982 * @return status of search
2984 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2986 ADS_STATUS status;
2987 LDAPMessage *res;
2988 const char *dn, *service_name;
2989 const char *attrs[] = { "dsServiceName", NULL };
2991 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2992 if (!ADS_ERR_OK(status)) {
2993 return status;
2996 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2997 if (service_name == NULL) {
2998 ads_msgfree(ads, res);
2999 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3002 ads_msgfree(ads, res);
3004 /* go up three levels */
3005 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3006 if (dn == NULL) {
3007 return ADS_ERROR(LDAP_NO_MEMORY);
3010 *site_name = talloc_strdup(mem_ctx, dn);
3011 if (*site_name == NULL) {
3012 return ADS_ERROR(LDAP_NO_MEMORY);
3015 return status;
3017 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3022 * find the site dn where a machine resides
3023 * @param ads connection to ads server
3024 * @param mem_ctx Pointer to talloc context
3025 * @param computer_name name of the machine
3026 * @param site_name Pointer to the sitename
3027 * @return status of search
3029 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3031 ADS_STATUS status;
3032 LDAPMessage *res;
3033 const char *parent, *filter;
3034 char *config_context = NULL;
3035 char *dn;
3037 /* shortcut a query */
3038 if (strequal(computer_name, ads->config.ldap_server_name)) {
3039 return ads_site_dn(ads, mem_ctx, site_dn);
3042 status = ads_config_path(ads, mem_ctx, &config_context);
3043 if (!ADS_ERR_OK(status)) {
3044 return status;
3047 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3048 if (filter == NULL) {
3049 return ADS_ERROR(LDAP_NO_MEMORY);
3052 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3053 filter, NULL, &res);
3054 if (!ADS_ERR_OK(status)) {
3055 return status;
3058 if (ads_count_replies(ads, res) != 1) {
3059 ads_msgfree(ads, res);
3060 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3063 dn = ads_get_dn(ads, mem_ctx, res);
3064 if (dn == NULL) {
3065 ads_msgfree(ads, res);
3066 return ADS_ERROR(LDAP_NO_MEMORY);
3069 /* go up three levels */
3070 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3071 if (parent == NULL) {
3072 ads_msgfree(ads, res);
3073 TALLOC_FREE(dn);
3074 return ADS_ERROR(LDAP_NO_MEMORY);
3077 *site_dn = talloc_strdup(mem_ctx, parent);
3078 if (*site_dn == NULL) {
3079 ads_msgfree(ads, res);
3080 TALLOC_FREE(dn);
3081 return ADS_ERROR(LDAP_NO_MEMORY);
3084 TALLOC_FREE(dn);
3085 ads_msgfree(ads, res);
3087 return status;
3091 * get the upn suffixes for a domain
3092 * @param ads connection to ads server
3093 * @param mem_ctx Pointer to talloc context
3094 * @param suffixes Pointer to an array of suffixes
3095 * @param num_suffixes Pointer to the number of suffixes
3096 * @return status of search
3098 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3100 ADS_STATUS status;
3101 LDAPMessage *res;
3102 const char *base;
3103 char *config_context = NULL;
3104 const char *attrs[] = { "uPNSuffixes", NULL };
3106 status = ads_config_path(ads, mem_ctx, &config_context);
3107 if (!ADS_ERR_OK(status)) {
3108 return status;
3111 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3112 if (base == NULL) {
3113 return ADS_ERROR(LDAP_NO_MEMORY);
3116 status = ads_search_dn(ads, &res, base, attrs);
3117 if (!ADS_ERR_OK(status)) {
3118 return status;
3121 if (ads_count_replies(ads, res) != 1) {
3122 ads_msgfree(ads, res);
3123 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3126 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3127 if ((*suffixes) == NULL) {
3128 ads_msgfree(ads, res);
3129 return ADS_ERROR(LDAP_NO_MEMORY);
3132 ads_msgfree(ads, res);
3134 return status;
3138 * get the joinable ous for a domain
3139 * @param ads connection to ads server
3140 * @param mem_ctx Pointer to talloc context
3141 * @param ous Pointer to an array of ous
3142 * @param num_ous Pointer to the number of ous
3143 * @return status of search
3145 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3146 TALLOC_CTX *mem_ctx,
3147 char ***ous,
3148 size_t *num_ous)
3150 ADS_STATUS status;
3151 LDAPMessage *res = NULL;
3152 LDAPMessage *msg = NULL;
3153 const char *attrs[] = { "dn", NULL };
3154 int count = 0;
3156 status = ads_search(ads, &res,
3157 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3158 attrs);
3159 if (!ADS_ERR_OK(status)) {
3160 return status;
3163 count = ads_count_replies(ads, res);
3164 if (count < 1) {
3165 ads_msgfree(ads, res);
3166 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3169 for (msg = ads_first_entry(ads, res); msg;
3170 msg = ads_next_entry(ads, msg)) {
3172 char *dn = NULL;
3174 dn = ads_get_dn(ads, talloc_tos(), msg);
3175 if (!dn) {
3176 ads_msgfree(ads, res);
3177 return ADS_ERROR(LDAP_NO_MEMORY);
3180 if (!add_string_to_array(mem_ctx, dn,
3181 (const char ***)ous,
3182 (int *)num_ous)) {
3183 TALLOC_FREE(dn);
3184 ads_msgfree(ads, res);
3185 return ADS_ERROR(LDAP_NO_MEMORY);
3188 TALLOC_FREE(dn);
3191 ads_msgfree(ads, res);
3193 return status;
3198 * pull a struct dom_sid from an extended dn string
3199 * @param mem_ctx TALLOC_CTX
3200 * @param extended_dn string
3201 * @param flags string type of extended_dn
3202 * @param sid pointer to a struct dom_sid
3203 * @return NT_STATUS_OK on success,
3204 * NT_INVALID_PARAMETER on error,
3205 * NT_STATUS_NOT_FOUND if no SID present
3207 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3208 const char *extended_dn,
3209 enum ads_extended_dn_flags flags,
3210 struct dom_sid *sid)
3212 char *p, *q, *dn;
3214 if (!extended_dn) {
3215 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3218 /* otherwise extended_dn gets stripped off */
3219 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3220 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3223 * ADS_EXTENDED_DN_HEX_STRING:
3224 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3226 * ADS_EXTENDED_DN_STRING (only with w2k3):
3227 * <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
3229 * Object with no SID, such as an Exchange Public Folder
3230 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3233 p = strchr(dn, ';');
3234 if (!p) {
3235 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3238 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3239 DEBUG(5,("No SID present in extended dn\n"));
3240 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3243 p += strlen(";<SID=");
3245 q = strchr(p, '>');
3246 if (!q) {
3247 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3250 *q = '\0';
3252 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3254 switch (flags) {
3256 case ADS_EXTENDED_DN_STRING:
3257 if (!string_to_sid(sid, p)) {
3258 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3260 break;
3261 case ADS_EXTENDED_DN_HEX_STRING: {
3262 fstring buf;
3263 size_t buf_len;
3265 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3266 if (buf_len == 0) {
3267 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3270 if (!sid_parse(buf, buf_len, sid)) {
3271 DEBUG(10,("failed to parse sid\n"));
3272 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3274 break;
3276 default:
3277 DEBUG(10,("unknown extended dn format\n"));
3278 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3281 return ADS_ERROR_NT(NT_STATUS_OK);
3284 /********************************************************************
3285 ********************************************************************/
3287 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3289 LDAPMessage *res = NULL;
3290 ADS_STATUS status;
3291 int count = 0;
3292 char *name = NULL;
3294 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3295 if (!ADS_ERR_OK(status)) {
3296 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3297 lp_netbios_name()));
3298 goto out;
3301 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3302 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3303 goto out;
3306 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3307 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3310 out:
3311 ads_msgfree(ads, res);
3313 return name;
3316 /********************************************************************
3317 ********************************************************************/
3319 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3321 LDAPMessage *res = NULL;
3322 ADS_STATUS status;
3323 int count = 0;
3324 char *name = NULL;
3326 status = ads_find_machine_acct(ads, &res, machine_name);
3327 if (!ADS_ERR_OK(status)) {
3328 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3329 lp_netbios_name()));
3330 goto out;
3333 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3334 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3335 goto out;
3338 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3339 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3342 out:
3343 ads_msgfree(ads, res);
3345 return name;
3348 /********************************************************************
3349 ********************************************************************/
3351 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3353 LDAPMessage *res = NULL;
3354 ADS_STATUS status;
3355 int count = 0;
3356 char *name = NULL;
3358 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3359 if (!ADS_ERR_OK(status)) {
3360 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3361 lp_netbios_name()));
3362 goto out;
3365 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3366 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3367 goto out;
3370 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3371 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3374 out:
3375 ads_msgfree(ads, res);
3377 return name;
3380 #if 0
3382 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3385 * Join a machine to a realm
3386 * Creates the machine account and sets the machine password
3387 * @param ads connection to ads server
3388 * @param machine name of host to add
3389 * @param org_unit Organizational unit to place machine in
3390 * @return status of join
3392 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3393 uint32 account_type, const char *org_unit)
3395 ADS_STATUS status;
3396 LDAPMessage *res = NULL;
3397 char *machine;
3399 /* machine name must be lowercase */
3400 machine = SMB_STRDUP(machine_name);
3401 strlower_m(machine);
3404 status = ads_find_machine_acct(ads, (void **)&res, machine);
3405 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3406 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3407 status = ads_leave_realm(ads, machine);
3408 if (!ADS_ERR_OK(status)) {
3409 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3410 machine, ads->config.realm));
3411 return status;
3415 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3416 if (!ADS_ERR_OK(status)) {
3417 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3418 SAFE_FREE(machine);
3419 return status;
3422 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3423 if (!ADS_ERR_OK(status)) {
3424 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3425 SAFE_FREE(machine);
3426 return status;
3429 SAFE_FREE(machine);
3430 ads_msgfree(ads, res);
3432 return status;
3434 #endif
3437 * Delete a machine from the realm
3438 * @param ads connection to ads server
3439 * @param hostname Machine to remove
3440 * @return status of delete
3442 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3444 ADS_STATUS status;
3445 void *msg;
3446 LDAPMessage *res;
3447 char *hostnameDN, *host;
3448 int rc;
3449 LDAPControl ldap_control;
3450 LDAPControl * pldap_control[2] = {NULL, NULL};
3452 pldap_control[0] = &ldap_control;
3453 memset(&ldap_control, 0, sizeof(LDAPControl));
3454 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3456 /* hostname must be lowercase */
3457 host = SMB_STRDUP(hostname);
3458 strlower_m(host);
3460 status = ads_find_machine_acct(ads, &res, host);
3461 if (!ADS_ERR_OK(status)) {
3462 DEBUG(0, ("Host account for %s does not exist.\n", host));
3463 SAFE_FREE(host);
3464 return status;
3467 msg = ads_first_entry(ads, res);
3468 if (!msg) {
3469 SAFE_FREE(host);
3470 return ADS_ERROR_SYSTEM(ENOENT);
3473 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3474 if (hostnameDN == NULL) {
3475 SAFE_FREE(host);
3476 return ADS_ERROR_SYSTEM(ENOENT);
3479 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3480 if (rc) {
3481 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3482 }else {
3483 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3486 if (rc != LDAP_SUCCESS) {
3487 const char *attrs[] = { "cn", NULL };
3488 LDAPMessage *msg_sub;
3490 /* we only search with scope ONE, we do not expect any further
3491 * objects to be created deeper */
3493 status = ads_do_search_retry(ads, hostnameDN,
3494 LDAP_SCOPE_ONELEVEL,
3495 "(objectclass=*)", attrs, &res);
3497 if (!ADS_ERR_OK(status)) {
3498 SAFE_FREE(host);
3499 TALLOC_FREE(hostnameDN);
3500 return status;
3503 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3504 msg_sub = ads_next_entry(ads, msg_sub)) {
3506 char *dn = NULL;
3508 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3509 SAFE_FREE(host);
3510 TALLOC_FREE(hostnameDN);
3511 return ADS_ERROR(LDAP_NO_MEMORY);
3514 status = ads_del_dn(ads, dn);
3515 if (!ADS_ERR_OK(status)) {
3516 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3517 SAFE_FREE(host);
3518 TALLOC_FREE(dn);
3519 TALLOC_FREE(hostnameDN);
3520 return status;
3523 TALLOC_FREE(dn);
3526 /* there should be no subordinate objects anymore */
3527 status = ads_do_search_retry(ads, hostnameDN,
3528 LDAP_SCOPE_ONELEVEL,
3529 "(objectclass=*)", attrs, &res);
3531 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3532 SAFE_FREE(host);
3533 TALLOC_FREE(hostnameDN);
3534 return status;
3537 /* delete hostnameDN now */
3538 status = ads_del_dn(ads, hostnameDN);
3539 if (!ADS_ERR_OK(status)) {
3540 SAFE_FREE(host);
3541 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3542 TALLOC_FREE(hostnameDN);
3543 return status;
3547 TALLOC_FREE(hostnameDN);
3549 status = ads_find_machine_acct(ads, &res, host);
3550 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3551 DEBUG(3, ("Failed to remove host account.\n"));
3552 SAFE_FREE(host);
3553 return status;
3556 SAFE_FREE(host);
3557 return status;
3561 * pull all token-sids from an LDAP dn
3562 * @param ads connection to ads server
3563 * @param mem_ctx TALLOC_CTX for allocating sid array
3564 * @param dn of LDAP object
3565 * @param user_sid pointer to struct dom_sid (objectSid)
3566 * @param primary_group_sid pointer to struct dom_sid (self composed)
3567 * @param sids pointer to sid array to allocate
3568 * @param num_sids counter of SIDs pulled
3569 * @return status of token query
3571 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3572 TALLOC_CTX *mem_ctx,
3573 const char *dn,
3574 struct dom_sid *user_sid,
3575 struct dom_sid *primary_group_sid,
3576 struct dom_sid **sids,
3577 size_t *num_sids)
3579 ADS_STATUS status;
3580 LDAPMessage *res = NULL;
3581 int count = 0;
3582 size_t tmp_num_sids;
3583 struct dom_sid *tmp_sids;
3584 struct dom_sid tmp_user_sid;
3585 struct dom_sid tmp_primary_group_sid;
3586 uint32 pgid;
3587 const char *attrs[] = {
3588 "objectSid",
3589 "tokenGroups",
3590 "primaryGroupID",
3591 NULL
3594 status = ads_search_retry_dn(ads, &res, dn, attrs);
3595 if (!ADS_ERR_OK(status)) {
3596 return status;
3599 count = ads_count_replies(ads, res);
3600 if (count != 1) {
3601 ads_msgfree(ads, res);
3602 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3605 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3606 ads_msgfree(ads, res);
3607 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3610 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3611 ads_msgfree(ads, res);
3612 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3616 /* hack to compose the primary group sid without knowing the
3617 * domsid */
3619 struct dom_sid domsid;
3621 sid_copy(&domsid, &tmp_user_sid);
3623 if (!sid_split_rid(&domsid, NULL)) {
3624 ads_msgfree(ads, res);
3625 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3628 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3629 ads_msgfree(ads, res);
3630 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3634 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3636 if (tmp_num_sids == 0 || !tmp_sids) {
3637 ads_msgfree(ads, res);
3638 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3641 if (num_sids) {
3642 *num_sids = tmp_num_sids;
3645 if (sids) {
3646 *sids = tmp_sids;
3649 if (user_sid) {
3650 *user_sid = tmp_user_sid;
3653 if (primary_group_sid) {
3654 *primary_group_sid = tmp_primary_group_sid;
3657 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3659 ads_msgfree(ads, res);
3660 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3664 * Find a sAMAccoutName in LDAP
3665 * @param ads connection to ads server
3666 * @param mem_ctx TALLOC_CTX for allocating sid array
3667 * @param samaccountname to search
3668 * @param uac_ret uint32 pointer userAccountControl attribute value
3669 * @param dn_ret pointer to dn
3670 * @return status of token query
3672 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3673 TALLOC_CTX *mem_ctx,
3674 const char *samaccountname,
3675 uint32 *uac_ret,
3676 const char **dn_ret)
3678 ADS_STATUS status;
3679 const char *attrs[] = { "userAccountControl", NULL };
3680 const char *filter;
3681 LDAPMessage *res = NULL;
3682 char *dn = NULL;
3683 uint32 uac = 0;
3685 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3686 samaccountname);
3687 if (filter == NULL) {
3688 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3689 goto out;
3692 status = ads_do_search_all(ads, ads->config.bind_path,
3693 LDAP_SCOPE_SUBTREE,
3694 filter, attrs, &res);
3696 if (!ADS_ERR_OK(status)) {
3697 goto out;
3700 if (ads_count_replies(ads, res) != 1) {
3701 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3702 goto out;
3705 dn = ads_get_dn(ads, talloc_tos(), res);
3706 if (dn == NULL) {
3707 status = ADS_ERROR(LDAP_NO_MEMORY);
3708 goto out;
3711 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3712 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3713 goto out;
3716 if (uac_ret) {
3717 *uac_ret = uac;
3720 if (dn_ret) {
3721 *dn_ret = talloc_strdup(mem_ctx, dn);
3722 if (!*dn_ret) {
3723 status = ADS_ERROR(LDAP_NO_MEMORY);
3724 goto out;
3727 out:
3728 TALLOC_FREE(dn);
3729 ads_msgfree(ads, res);
3731 return status;
3735 * find our configuration path
3736 * @param ads connection to ads server
3737 * @param mem_ctx Pointer to talloc context
3738 * @param config_path Pointer to the config path
3739 * @return status of search
3741 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3742 TALLOC_CTX *mem_ctx,
3743 char **config_path)
3745 ADS_STATUS status;
3746 LDAPMessage *res = NULL;
3747 const char *config_context = NULL;
3748 const char *attrs[] = { "configurationNamingContext", NULL };
3750 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3751 "(objectclass=*)", attrs, &res);
3752 if (!ADS_ERR_OK(status)) {
3753 return status;
3756 config_context = ads_pull_string(ads, mem_ctx, res,
3757 "configurationNamingContext");
3758 ads_msgfree(ads, res);
3759 if (!config_context) {
3760 return ADS_ERROR(LDAP_NO_MEMORY);
3763 if (config_path) {
3764 *config_path = talloc_strdup(mem_ctx, config_context);
3765 if (!*config_path) {
3766 return ADS_ERROR(LDAP_NO_MEMORY);
3770 return ADS_ERROR(LDAP_SUCCESS);
3774 * find the displayName of an extended right
3775 * @param ads connection to ads server
3776 * @param config_path The config path
3777 * @param mem_ctx Pointer to talloc context
3778 * @param GUID struct of the rightsGUID
3779 * @return status of search
3781 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3782 const char *config_path,
3783 TALLOC_CTX *mem_ctx,
3784 const struct GUID *rights_guid)
3786 ADS_STATUS rc;
3787 LDAPMessage *res = NULL;
3788 char *expr = NULL;
3789 const char *attrs[] = { "displayName", NULL };
3790 const char *result = NULL;
3791 const char *path;
3793 if (!ads || !mem_ctx || !rights_guid) {
3794 goto done;
3797 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3798 GUID_string(mem_ctx, rights_guid));
3799 if (!expr) {
3800 goto done;
3803 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3804 if (!path) {
3805 goto done;
3808 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3809 expr, attrs, &res);
3810 if (!ADS_ERR_OK(rc)) {
3811 goto done;
3814 if (ads_count_replies(ads, res) != 1) {
3815 goto done;
3818 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3820 done:
3821 ads_msgfree(ads, res);
3822 return result;
3826 * verify or build and verify an account ou
3827 * @param mem_ctx Pointer to talloc context
3828 * @param ads connection to ads server
3829 * @param account_ou
3830 * @return status of search
3833 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3834 ADS_STRUCT *ads,
3835 const char **account_ou)
3837 char **exploded_dn;
3838 const char *name;
3839 char *ou_string;
3841 exploded_dn = ldap_explode_dn(*account_ou, 0);
3842 if (exploded_dn) {
3843 ldap_value_free(exploded_dn);
3844 return ADS_SUCCESS;
3847 ou_string = ads_ou_string(ads, *account_ou);
3848 if (!ou_string) {
3849 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3852 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3853 ads->config.bind_path);
3854 SAFE_FREE(ou_string);
3856 if (!name) {
3857 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3860 exploded_dn = ldap_explode_dn(name, 0);
3861 if (!exploded_dn) {
3862 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3864 ldap_value_free(exploded_dn);
3866 *account_ou = name;
3867 return ADS_SUCCESS;
3870 #endif