WHATSNEW: Add changes since 3.6.13.
[Samba.git] / source3 / libads / ldap.c
blobb841c843c63c6bcf904e0f9305051f392af9fd5f
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 "libads/dns.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, int port, unsigned int to)
64 LDAP *ldp = NULL;
67 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
68 "%u seconds\n", server, port, to));
70 /* Setup timeout */
71 gotalarm = 0;
72 CatchSignal(SIGALRM, gotalarm_sig);
73 alarm(to);
74 /* End setup timeout. */
76 ldp = ldap_open(server, port);
78 if (ldp == NULL) {
79 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
80 server, port, strerror(errno)));
81 } else {
82 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
85 /* Teardown timeout. */
86 CatchSignal(SIGALRM, SIG_IGN);
87 alarm(0);
89 return ldp;
92 static int ldap_search_with_timeout(LDAP *ld,
93 LDAP_CONST char *base,
94 int scope,
95 LDAP_CONST char *filter,
96 char **attrs,
97 int attrsonly,
98 LDAPControl **sctrls,
99 LDAPControl **cctrls,
100 int sizelimit,
101 LDAPMessage **res )
103 struct timeval timeout;
104 int result;
106 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
107 timeout.tv_sec = lp_ldap_timeout();
108 timeout.tv_usec = 0;
110 /* Setup alarm timeout.... Do we need both of these ? JRA. */
111 gotalarm = 0;
112 CatchSignal(SIGALRM, gotalarm_sig);
113 alarm(lp_ldap_timeout());
114 /* End setup timeout. */
116 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
117 attrsonly, sctrls, cctrls, &timeout,
118 sizelimit, res);
120 /* Teardown timeout. */
121 CatchSignal(SIGALRM, SIG_IGN);
122 alarm(0);
124 if (gotalarm != 0)
125 return LDAP_TIMELIMIT_EXCEEDED;
128 * A bug in OpenLDAP means ldap_search_ext_s can return
129 * LDAP_SUCCESS but with a NULL res pointer. Cope with
130 * this. See bug #6279 for details. JRA.
133 if (*res == NULL) {
134 return LDAP_TIMELIMIT_EXCEEDED;
137 return result;
140 /**********************************************
141 Do client and server sitename match ?
142 **********************************************/
144 bool ads_sitename_match(ADS_STRUCT *ads)
146 if (ads->config.server_site_name == NULL &&
147 ads->config.client_site_name == NULL ) {
148 DEBUG(10,("ads_sitename_match: both null\n"));
149 return True;
151 if (ads->config.server_site_name &&
152 ads->config.client_site_name &&
153 strequal(ads->config.server_site_name,
154 ads->config.client_site_name)) {
155 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
156 return True;
158 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
159 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
160 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
161 return False;
164 /**********************************************
165 Is this the closest DC ?
166 **********************************************/
168 bool ads_closest_dc(ADS_STRUCT *ads)
170 if (ads->config.flags & NBT_SERVER_CLOSEST) {
171 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
172 return True;
175 /* not sure if this can ever happen */
176 if (ads_sitename_match(ads)) {
177 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
178 return True;
181 if (ads->config.client_site_name == NULL) {
182 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
183 return True;
186 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
187 ads->config.ldap_server_name));
189 return False;
194 try a connection to a given ldap server, returning True and setting the servers IP
195 in the ads struct if successful
197 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
199 char *srv;
200 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
201 TALLOC_CTX *frame = talloc_stackframe();
202 bool ret = false;
204 if (!server || !*server) {
205 TALLOC_FREE(frame);
206 return False;
209 if (!is_ipaddress(server)) {
210 struct sockaddr_storage ss;
211 char addr[INET6_ADDRSTRLEN];
213 if (!resolve_name(server, &ss, 0x20, true)) {
214 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
215 server ));
216 TALLOC_FREE(frame);
217 return false;
219 print_sockaddr(addr, sizeof(addr), &ss);
220 srv = talloc_strdup(frame, addr);
221 } else {
222 /* this copes with inet_ntoa brokenness */
223 srv = talloc_strdup(frame, server);
226 if (!srv) {
227 TALLOC_FREE(frame);
228 return false;
231 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
232 srv, ads->server.realm));
234 ZERO_STRUCT( cldap_reply );
236 if ( !ads_cldap_netlogon_5(frame, srv, ads->server.realm, &cldap_reply ) ) {
237 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
238 ret = false;
239 goto out;
242 /* Check the CLDAP reply flags */
244 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
245 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
246 srv));
247 ret = false;
248 goto out;
251 /* Fill in the ads->config values */
253 SAFE_FREE(ads->config.realm);
254 SAFE_FREE(ads->config.bind_path);
255 SAFE_FREE(ads->config.ldap_server_name);
256 SAFE_FREE(ads->config.server_site_name);
257 SAFE_FREE(ads->config.client_site_name);
258 SAFE_FREE(ads->server.workgroup);
260 ads->config.flags = cldap_reply.server_type;
261 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
262 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
263 strupper_m(ads->config.realm);
264 ads->config.bind_path = ads_build_dn(ads->config.realm);
265 if (*cldap_reply.server_site) {
266 ads->config.server_site_name =
267 SMB_STRDUP(cldap_reply.server_site);
269 if (*cldap_reply.client_site) {
270 ads->config.client_site_name =
271 SMB_STRDUP(cldap_reply.client_site);
273 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
275 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
276 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
277 DEBUG(1,("ads_try_connect: unable to convert %s "
278 "to an address\n",
279 srv));
280 ret = false;
281 goto out;
284 /* Store our site name. */
285 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
286 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
288 ret = true;
290 out:
292 TALLOC_FREE(frame);
293 return ret;
296 /**********************************************************************
297 Try to find an AD dc using our internal name resolution routines
298 Try the realm first and then then workgroup name if netbios is not
299 disabled
300 **********************************************************************/
302 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
304 const char *c_domain;
305 const char *c_realm;
306 int count, i=0;
307 struct ip_service *ip_list;
308 const char *realm;
309 const char *domain;
310 bool got_realm = False;
311 bool use_own_domain = False;
312 char *sitename;
313 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
315 /* if the realm and workgroup are both empty, assume they are ours */
317 /* realm */
318 c_realm = ads->server.realm;
320 if ( !c_realm || !*c_realm ) {
321 /* special case where no realm and no workgroup means our own */
322 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
323 use_own_domain = True;
324 c_realm = lp_realm();
328 if (c_realm && *c_realm)
329 got_realm = True;
331 /* we need to try once with the realm name and fallback to the
332 netbios domain name if we fail (if netbios has not been disabled */
334 if ( !got_realm && !lp_disable_netbios() ) {
335 c_realm = ads->server.workgroup;
336 if (!c_realm || !*c_realm) {
337 if ( use_own_domain )
338 c_realm = lp_workgroup();
342 if ( !c_realm || !*c_realm ) {
343 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
344 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
347 if ( use_own_domain ) {
348 c_domain = lp_workgroup();
349 } else {
350 c_domain = ads->server.workgroup;
353 realm = c_realm;
354 domain = c_domain;
357 * In case of LDAP we use get_dc_name() as that
358 * creates the custom krb5.conf file
360 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
361 fstring srv_name;
362 struct sockaddr_storage ip_out;
364 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
365 (got_realm ? "realm" : "domain"), realm));
367 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
369 * we call ads_try_connect() to fill in the
370 * ads->config details
372 if (ads_try_connect(ads, srv_name, false)) {
373 return NT_STATUS_OK;
377 return NT_STATUS_NO_LOGON_SERVERS;
380 sitename = sitename_fetch(realm);
382 again:
384 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
385 (got_realm ? "realm" : "domain"), realm));
387 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
388 if (!NT_STATUS_IS_OK(status)) {
389 /* fall back to netbios if we can */
390 if ( got_realm && !lp_disable_netbios() ) {
391 got_realm = False;
392 goto again;
395 SAFE_FREE(sitename);
396 return status;
399 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
400 for ( i=0; i<count; i++ ) {
401 char server[INET6_ADDRSTRLEN];
403 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
405 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
406 continue;
408 if (!got_realm) {
409 /* realm in this case is a workgroup name. We need
410 to ignore any IP addresses in the negative connection
411 cache that match ip addresses returned in the ad realm
412 case. It sucks that I have to reproduce the logic above... */
413 c_realm = ads->server.realm;
414 if ( !c_realm || !*c_realm ) {
415 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
416 c_realm = lp_realm();
419 if (c_realm && *c_realm &&
420 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
421 /* Ensure we add the workgroup name for this
422 IP address as negative too. */
423 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
424 continue;
428 if ( ads_try_connect(ads, server, false) ) {
429 SAFE_FREE(ip_list);
430 SAFE_FREE(sitename);
431 return NT_STATUS_OK;
434 /* keep track of failures */
435 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
438 SAFE_FREE(ip_list);
440 /* In case we failed to contact one of our closest DC on our site we
441 * need to try to find another DC, retry with a site-less SRV DNS query
442 * - Guenther */
444 if (sitename) {
445 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
446 "trying to find another DC\n", sitename));
447 SAFE_FREE(sitename);
448 namecache_delete(realm, 0x1C);
449 goto again;
452 return NT_STATUS_NO_LOGON_SERVERS;
455 /*********************************************************************
456 *********************************************************************/
458 static NTSTATUS ads_lookup_site(void)
460 ADS_STRUCT *ads = NULL;
461 ADS_STATUS ads_status;
462 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
464 ads = ads_init(lp_realm(), NULL, NULL);
465 if (!ads) {
466 return NT_STATUS_NO_MEMORY;
469 /* The NO_BIND here will find a DC and set the client site
470 but not establish the TCP connection */
472 ads->auth.flags = ADS_AUTH_NO_BIND;
473 ads_status = ads_connect(ads);
474 if (!ADS_ERR_OK(ads_status)) {
475 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
476 ads_errstr(ads_status)));
478 nt_status = ads_ntstatus(ads_status);
480 if (ads) {
481 ads_destroy(&ads);
484 return nt_status;
487 /*********************************************************************
488 *********************************************************************/
490 static const char* host_dns_domain(const char *fqdn)
492 const char *p = fqdn;
494 /* go to next char following '.' */
496 if ((p = strchr_m(fqdn, '.')) != NULL) {
497 p++;
500 return p;
505 * Connect to the Global Catalog server
506 * @param ads Pointer to an existing ADS_STRUCT
507 * @return status of connection
509 * Simple wrapper around ads_connect() that fills in the
510 * GC ldap server information
513 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
515 TALLOC_CTX *frame = talloc_stackframe();
516 struct dns_rr_srv *gcs_list;
517 int num_gcs;
518 char *realm = ads->server.realm;
519 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
520 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
521 int i;
522 bool done = false;
523 char *sitename = NULL;
525 if (!realm)
526 realm = lp_realm();
528 if ((sitename = sitename_fetch(realm)) == NULL) {
529 ads_lookup_site();
530 sitename = sitename_fetch(realm);
533 do {
534 /* We try once with a sitename and once without
535 (unless we don't have a sitename and then we're
536 done */
538 if (sitename == NULL)
539 done = true;
541 nt_status = ads_dns_query_gcs(frame, realm, sitename,
542 &gcs_list, &num_gcs);
544 SAFE_FREE(sitename);
546 if (!NT_STATUS_IS_OK(nt_status)) {
547 ads_status = ADS_ERROR_NT(nt_status);
548 goto done;
551 /* Loop until we get a successful connection or have gone
552 through them all. When connecting a GC server, make sure that
553 the realm is the server's DNS name and not the forest root */
555 for (i=0; i<num_gcs; i++) {
556 ads->server.gc = true;
557 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
558 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
559 ads_status = ads_connect(ads);
560 if (ADS_ERR_OK(ads_status)) {
561 /* Reset the bind_dn to "". A Global Catalog server
562 may host multiple domain trees in a forest.
563 Windows 2003 GC server will accept "" as the search
564 path to imply search all domain trees in the forest */
566 SAFE_FREE(ads->config.bind_path);
567 ads->config.bind_path = SMB_STRDUP("");
570 goto done;
572 SAFE_FREE(ads->server.ldap_server);
573 SAFE_FREE(ads->server.realm);
576 TALLOC_FREE(gcs_list);
577 num_gcs = 0;
578 } while (!done);
580 done:
581 SAFE_FREE(sitename);
582 talloc_destroy(frame);
584 return ads_status;
589 * Connect to the LDAP server
590 * @param ads Pointer to an existing ADS_STRUCT
591 * @return status of connection
593 ADS_STATUS ads_connect(ADS_STRUCT *ads)
595 int version = LDAP_VERSION3;
596 ADS_STATUS status;
597 NTSTATUS ntstatus;
598 char addr[INET6_ADDRSTRLEN];
600 ZERO_STRUCT(ads->ldap);
601 ads->ldap.last_attempt = time_mono(NULL);
602 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
604 /* try with a user specified server */
606 if (DEBUGLEVEL >= 11) {
607 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
608 DEBUG(11,("ads_connect: entering\n"));
609 DEBUGADD(11,("%s\n", s));
610 TALLOC_FREE(s);
613 if (ads->server.ldap_server)
615 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
616 goto got_connection;
619 /* The choice of which GC use is handled one level up in
620 ads_connect_gc(). If we continue on from here with
621 ads_find_dc() we will get GC searches on port 389 which
622 doesn't work. --jerry */
624 if (ads->server.gc == true) {
625 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
629 ntstatus = ads_find_dc(ads);
630 if (NT_STATUS_IS_OK(ntstatus)) {
631 goto got_connection;
634 status = ADS_ERROR_NT(ntstatus);
635 goto out;
637 got_connection:
639 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
640 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
642 if (!ads->auth.user_name) {
643 /* Must use the userPrincipalName value here or sAMAccountName
644 and not servicePrincipalName; found by Guenther Deschner */
646 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
647 DEBUG(0,("ads_connect: asprintf fail.\n"));
648 ads->auth.user_name = NULL;
652 if (!ads->auth.realm) {
653 ads->auth.realm = SMB_STRDUP(ads->config.realm);
656 if (!ads->auth.kdc_server) {
657 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
658 ads->auth.kdc_server = SMB_STRDUP(addr);
661 #if KRB5_DNS_HACK
662 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
663 to MIT kerberos to work (tridge) */
665 char *env = NULL;
666 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
667 setenv(env, ads->auth.kdc_server, 1);
668 free(env);
671 #endif
673 /* If the caller() requested no LDAP bind, then we are done */
675 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
676 status = ADS_SUCCESS;
677 goto out;
680 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
681 if (!ads->ldap.mem_ctx) {
682 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
683 goto out;
686 /* Otherwise setup the TCP LDAP session */
688 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
689 ads->ldap.port, lp_ldap_timeout());
690 if (ads->ldap.ld == NULL) {
691 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
692 goto out;
694 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
696 /* cache the successful connection for workgroup and realm */
697 if (ads_closest_dc(ads)) {
698 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
699 saf_store( ads->server.realm, ads->config.ldap_server_name);
702 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
704 if ( lp_ldap_ssl_ads() ) {
705 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
706 if (!ADS_ERR_OK(status)) {
707 goto out;
711 /* fill in the current time and offsets */
713 status = ads_current_time( ads );
714 if ( !ADS_ERR_OK(status) ) {
715 goto out;
718 /* Now do the bind */
720 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
721 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
722 goto out;
725 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
726 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
727 goto out;
730 status = ads_sasl_bind(ads);
732 out:
733 if (DEBUGLEVEL >= 11) {
734 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
735 DEBUG(11,("ads_connect: leaving with: %s\n",
736 ads_errstr(status)));
737 DEBUGADD(11,("%s\n", s));
738 TALLOC_FREE(s);
741 return status;
745 * Connect to the LDAP server using given credentials
746 * @param ads Pointer to an existing ADS_STRUCT
747 * @return status of connection
749 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
751 ads->auth.flags |= ADS_AUTH_USER_CREDS;
753 return ads_connect(ads);
757 * Disconnect the LDAP server
758 * @param ads Pointer to an existing ADS_STRUCT
760 void ads_disconnect(ADS_STRUCT *ads)
762 if (ads->ldap.ld) {
763 ldap_unbind(ads->ldap.ld);
764 ads->ldap.ld = NULL;
766 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
767 ads->ldap.wrap_ops->disconnect(ads);
769 if (ads->ldap.mem_ctx) {
770 talloc_free(ads->ldap.mem_ctx);
772 ZERO_STRUCT(ads->ldap);
776 Duplicate a struct berval into talloc'ed memory
778 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
780 struct berval *value;
782 if (!in_val) return NULL;
784 value = TALLOC_ZERO_P(ctx, struct berval);
785 if (value == NULL)
786 return NULL;
787 if (in_val->bv_len == 0) return value;
789 value->bv_len = in_val->bv_len;
790 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
791 in_val->bv_len);
792 return value;
796 Make a values list out of an array of (struct berval *)
798 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
799 const struct berval **in_vals)
801 struct berval **values;
802 int i;
804 if (!in_vals) return NULL;
805 for (i=0; in_vals[i]; i++)
806 ; /* count values */
807 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
808 if (!values) return NULL;
810 for (i=0; in_vals[i]; i++) {
811 values[i] = dup_berval(ctx, in_vals[i]);
813 return values;
817 UTF8-encode a values list out of an array of (char *)
819 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
821 char **values;
822 int i;
823 size_t size;
825 if (!in_vals) return NULL;
826 for (i=0; in_vals[i]; i++)
827 ; /* count values */
828 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
829 if (!values) return NULL;
831 for (i=0; in_vals[i]; i++) {
832 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
833 TALLOC_FREE(values);
834 return NULL;
837 return values;
841 Pull a (char *) array out of a UTF8-encoded values list
843 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
845 char **values;
846 int i;
847 size_t converted_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 (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
857 &converted_size)) {
858 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
859 "%s", strerror(errno)));
862 return values;
866 * Do a search with paged results. cookie must be null on the first
867 * call, and then returned on each subsequent call. It will be null
868 * again when the entire search is complete
869 * @param ads connection to ads server
870 * @param bind_path Base dn for the search
871 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
872 * @param expr Search expression - specified in local charset
873 * @param attrs Attributes to retrieve - specified in utf8 or ascii
874 * @param res ** which will contain results - free res* with ads_msgfree()
875 * @param count Number of entries retrieved on this page
876 * @param cookie The paged results cookie to be returned on subsequent calls
877 * @return status of search
879 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
880 const char *bind_path,
881 int scope, const char *expr,
882 const char **attrs, void *args,
883 LDAPMessage **res,
884 int *count, struct berval **cookie)
886 int rc, i, version;
887 char *utf8_expr, *utf8_path, **search_attrs = NULL;
888 size_t converted_size;
889 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
890 BerElement *cookie_be = NULL;
891 struct berval *cookie_bv= NULL;
892 BerElement *ext_be = NULL;
893 struct berval *ext_bv= NULL;
895 TALLOC_CTX *ctx;
896 ads_control *external_control = (ads_control *) args;
898 *res = NULL;
900 if (!(ctx = talloc_init("ads_do_paged_search_args")))
901 return ADS_ERROR(LDAP_NO_MEMORY);
903 /* 0 means the conversion worked but the result was empty
904 so we only fail if it's -1. In any case, it always
905 at least nulls out the dest */
906 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
907 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
909 rc = LDAP_NO_MEMORY;
910 goto done;
913 if (!attrs || !(*attrs))
914 search_attrs = NULL;
915 else {
916 /* This would be the utf8-encoded version...*/
917 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
918 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
919 rc = LDAP_NO_MEMORY;
920 goto done;
924 /* Paged results only available on ldap v3 or later */
925 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
926 if (version < LDAP_VERSION3) {
927 rc = LDAP_NOT_SUPPORTED;
928 goto done;
931 cookie_be = ber_alloc_t(LBER_USE_DER);
932 if (*cookie) {
933 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
934 ber_bvfree(*cookie); /* don't need it from last time */
935 *cookie = NULL;
936 } else {
937 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
939 ber_flatten(cookie_be, &cookie_bv);
940 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
941 PagedResults.ldctl_iscritical = (char) 1;
942 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
943 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
945 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
946 NoReferrals.ldctl_iscritical = (char) 0;
947 NoReferrals.ldctl_value.bv_len = 0;
948 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
950 if (external_control &&
951 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
952 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
954 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
955 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
957 /* win2k does not accept a ldctl_value beeing passed in */
959 if (external_control->val != 0) {
961 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
962 rc = LDAP_NO_MEMORY;
963 goto done;
966 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
967 rc = LDAP_NO_MEMORY;
968 goto done;
970 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
971 rc = LDAP_NO_MEMORY;
972 goto done;
975 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
976 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
978 } else {
979 ExternalCtrl.ldctl_value.bv_len = 0;
980 ExternalCtrl.ldctl_value.bv_val = NULL;
983 controls[0] = &NoReferrals;
984 controls[1] = &PagedResults;
985 controls[2] = &ExternalCtrl;
986 controls[3] = NULL;
988 } else {
989 controls[0] = &NoReferrals;
990 controls[1] = &PagedResults;
991 controls[2] = NULL;
994 /* we need to disable referrals as the openldap libs don't
995 handle them and paged results at the same time. Using them
996 together results in the result record containing the server
997 page control being removed from the result list (tridge/jmcd)
999 leaving this in despite the control that says don't generate
1000 referrals, in case the server doesn't support it (jmcd)
1002 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1004 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1005 search_attrs, 0, controls,
1006 NULL, LDAP_NO_LIMIT,
1007 (LDAPMessage **)res);
1009 ber_free(cookie_be, 1);
1010 ber_bvfree(cookie_bv);
1012 if (rc) {
1013 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1014 ldap_err2string(rc)));
1015 goto done;
1018 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1019 NULL, &rcontrols, 0);
1021 if (!rcontrols) {
1022 goto done;
1025 for (i=0; rcontrols[i]; i++) {
1026 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1027 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1028 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1029 &cookie_bv);
1030 /* the berval is the cookie, but must be freed when
1031 it is all done */
1032 if (cookie_bv->bv_len) /* still more to do */
1033 *cookie=ber_bvdup(cookie_bv);
1034 else
1035 *cookie=NULL;
1036 ber_bvfree(cookie_bv);
1037 ber_free(cookie_be, 1);
1038 break;
1041 ldap_controls_free(rcontrols);
1043 done:
1044 talloc_destroy(ctx);
1046 if (ext_be) {
1047 ber_free(ext_be, 1);
1050 if (ext_bv) {
1051 ber_bvfree(ext_bv);
1054 /* if/when we decide to utf8-encode attrs, take out this next line */
1055 TALLOC_FREE(search_attrs);
1057 return ADS_ERROR(rc);
1060 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1061 int scope, const char *expr,
1062 const char **attrs, LDAPMessage **res,
1063 int *count, struct berval **cookie)
1065 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1070 * Get all results for a search. This uses ads_do_paged_search() to return
1071 * all entries in a large search.
1072 * @param ads connection to ads server
1073 * @param bind_path Base dn for the search
1074 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1075 * @param expr Search expression
1076 * @param attrs Attributes to retrieve
1077 * @param res ** which will contain results - free res* with ads_msgfree()
1078 * @return status of search
1080 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1081 int scope, const char *expr,
1082 const char **attrs, void *args,
1083 LDAPMessage **res)
1085 struct berval *cookie = NULL;
1086 int count = 0;
1087 ADS_STATUS status;
1089 *res = NULL;
1090 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1091 &count, &cookie);
1093 if (!ADS_ERR_OK(status))
1094 return status;
1096 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1097 while (cookie) {
1098 LDAPMessage *res2 = NULL;
1099 ADS_STATUS status2;
1100 LDAPMessage *msg, *next;
1102 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1103 attrs, args, &res2, &count, &cookie);
1105 if (!ADS_ERR_OK(status2)) break;
1107 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1108 that this works on all ldap libs, but I have only tested with openldap */
1109 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1110 next = ads_next_message(ads, msg);
1111 ldap_add_result_entry((LDAPMessage **)res, msg);
1113 /* note that we do not free res2, as the memory is now
1114 part of the main returned list */
1116 #else
1117 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1118 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1119 #endif
1121 return status;
1124 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1125 int scope, const char *expr,
1126 const char **attrs, LDAPMessage **res)
1128 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1131 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1132 int scope, const char *expr,
1133 const char **attrs, uint32 sd_flags,
1134 LDAPMessage **res)
1136 ads_control args;
1138 args.control = ADS_SD_FLAGS_OID;
1139 args.val = sd_flags;
1140 args.critical = True;
1142 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1147 * Run a function on all results for a search. Uses ads_do_paged_search() and
1148 * runs the function as each page is returned, using ads_process_results()
1149 * @param ads connection to ads server
1150 * @param bind_path Base dn for the search
1151 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1152 * @param expr Search expression - specified in local charset
1153 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1154 * @param fn Function which takes attr name, values list, and data_area
1155 * @param data_area Pointer which is passed to function on each call
1156 * @return status of search
1158 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1159 int scope, const char *expr, const char **attrs,
1160 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1161 void *data_area)
1163 struct berval *cookie = NULL;
1164 int count = 0;
1165 ADS_STATUS status;
1166 LDAPMessage *res;
1168 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1169 &count, &cookie);
1171 if (!ADS_ERR_OK(status)) return status;
1173 ads_process_results(ads, res, fn, data_area);
1174 ads_msgfree(ads, res);
1176 while (cookie) {
1177 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1178 &res, &count, &cookie);
1180 if (!ADS_ERR_OK(status)) break;
1182 ads_process_results(ads, res, fn, data_area);
1183 ads_msgfree(ads, res);
1186 return status;
1190 * Do a search with a timeout.
1191 * @param ads connection to ads server
1192 * @param bind_path Base dn for the search
1193 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1194 * @param expr Search expression
1195 * @param attrs Attributes to retrieve
1196 * @param res ** which will contain results - free res* with ads_msgfree()
1197 * @return status of search
1199 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1200 const char *expr,
1201 const char **attrs, LDAPMessage **res)
1203 int rc;
1204 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1205 size_t converted_size;
1206 TALLOC_CTX *ctx;
1208 *res = NULL;
1209 if (!(ctx = talloc_init("ads_do_search"))) {
1210 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1211 return ADS_ERROR(LDAP_NO_MEMORY);
1214 /* 0 means the conversion worked but the result was empty
1215 so we only fail if it's negative. In any case, it always
1216 at least nulls out the dest */
1217 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1218 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1220 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1221 rc = LDAP_NO_MEMORY;
1222 goto done;
1225 if (!attrs || !(*attrs))
1226 search_attrs = NULL;
1227 else {
1228 /* This would be the utf8-encoded version...*/
1229 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1230 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1232 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1233 rc = LDAP_NO_MEMORY;
1234 goto done;
1238 /* see the note in ads_do_paged_search - we *must* disable referrals */
1239 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1241 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1242 search_attrs, 0, NULL, NULL,
1243 LDAP_NO_LIMIT,
1244 (LDAPMessage **)res);
1246 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1247 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1248 rc = 0;
1251 done:
1252 talloc_destroy(ctx);
1253 /* if/when we decide to utf8-encode attrs, take out this next line */
1254 TALLOC_FREE(search_attrs);
1255 return ADS_ERROR(rc);
1258 * Do a general ADS search
1259 * @param ads connection to ads server
1260 * @param res ** which will contain results - free res* with ads_msgfree()
1261 * @param expr Search expression
1262 * @param attrs Attributes to retrieve
1263 * @return status of search
1265 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1266 const char *expr, const char **attrs)
1268 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1269 expr, attrs, res);
1273 * Do a search on a specific DistinguishedName
1274 * @param ads connection to ads server
1275 * @param res ** which will contain results - free res* with ads_msgfree()
1276 * @param dn DistinguishName to search
1277 * @param attrs Attributes to retrieve
1278 * @return status of search
1280 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1281 const char *dn, const char **attrs)
1283 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1284 attrs, res);
1288 * Free up memory from a ads_search
1289 * @param ads connection to ads server
1290 * @param msg Search results to free
1292 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1294 if (!msg) return;
1295 ldap_msgfree(msg);
1299 * Get a dn from search results
1300 * @param ads connection to ads server
1301 * @param msg Search result
1302 * @return dn string
1304 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1306 char *utf8_dn, *unix_dn;
1307 size_t converted_size;
1309 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1311 if (!utf8_dn) {
1312 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1313 return NULL;
1316 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1317 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1318 utf8_dn ));
1319 return NULL;
1321 ldap_memfree(utf8_dn);
1322 return unix_dn;
1326 * Get the parent from a dn
1327 * @param dn the dn to return the parent from
1328 * @return parent dn string
1330 char *ads_parent_dn(const char *dn)
1332 char *p;
1334 if (dn == NULL) {
1335 return NULL;
1338 p = strchr(dn, ',');
1340 if (p == NULL) {
1341 return NULL;
1344 return p+1;
1348 * Find a machine account given a hostname
1349 * @param ads connection to ads server
1350 * @param res ** which will contain results - free res* with ads_msgfree()
1351 * @param host Hostname to search for
1352 * @return status of search
1354 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1355 const char *machine)
1357 ADS_STATUS status;
1358 char *expr;
1359 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1361 *res = NULL;
1363 /* the easiest way to find a machine account anywhere in the tree
1364 is to look for hostname$ */
1365 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1366 DEBUG(1, ("asprintf failed!\n"));
1367 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1370 status = ads_search(ads, res, expr, attrs);
1371 SAFE_FREE(expr);
1372 return status;
1376 * Initialize a list of mods to be used in a modify request
1377 * @param ctx An initialized TALLOC_CTX
1378 * @return allocated ADS_MODLIST
1380 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1382 #define ADS_MODLIST_ALLOC_SIZE 10
1383 LDAPMod **mods;
1385 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1386 /* -1 is safety to make sure we don't go over the end.
1387 need to reset it to NULL before doing ldap modify */
1388 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1390 return (ADS_MODLIST)mods;
1395 add an attribute to the list, with values list already constructed
1397 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1398 int mod_op, const char *name,
1399 const void *_invals)
1401 const void **invals = (const void **)_invals;
1402 int curmod;
1403 LDAPMod **modlist = (LDAPMod **) *mods;
1404 struct berval **ber_values = NULL;
1405 char **char_values = NULL;
1407 if (!invals) {
1408 mod_op = LDAP_MOD_DELETE;
1409 } else {
1410 if (mod_op & LDAP_MOD_BVALUES)
1411 ber_values = ads_dup_values(ctx,
1412 (const struct berval **)invals);
1413 else
1414 char_values = ads_push_strvals(ctx,
1415 (const char **) invals);
1418 /* find the first empty slot */
1419 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1420 curmod++);
1421 if (modlist[curmod] == (LDAPMod *) -1) {
1422 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1423 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1424 return ADS_ERROR(LDAP_NO_MEMORY);
1425 memset(&modlist[curmod], 0,
1426 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1427 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1428 *mods = (ADS_MODLIST)modlist;
1431 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1432 return ADS_ERROR(LDAP_NO_MEMORY);
1433 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1434 if (mod_op & LDAP_MOD_BVALUES) {
1435 modlist[curmod]->mod_bvalues = ber_values;
1436 } else if (mod_op & LDAP_MOD_DELETE) {
1437 modlist[curmod]->mod_values = NULL;
1438 } else {
1439 modlist[curmod]->mod_values = char_values;
1442 modlist[curmod]->mod_op = mod_op;
1443 return ADS_ERROR(LDAP_SUCCESS);
1447 * Add a single string value to a mod list
1448 * @param ctx An initialized TALLOC_CTX
1449 * @param mods An initialized ADS_MODLIST
1450 * @param name The attribute name to add
1451 * @param val The value to add - NULL means DELETE
1452 * @return ADS STATUS indicating success of add
1454 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1455 const char *name, const char *val)
1457 const char *values[2];
1459 values[0] = val;
1460 values[1] = NULL;
1462 if (!val)
1463 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1464 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1468 * Add an array of string values to a mod list
1469 * @param ctx An initialized TALLOC_CTX
1470 * @param mods An initialized ADS_MODLIST
1471 * @param name The attribute name to add
1472 * @param vals The array of string values to add - NULL means DELETE
1473 * @return ADS STATUS indicating success of add
1475 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1476 const char *name, const char **vals)
1478 if (!vals)
1479 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1480 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1481 name, (const void **) vals);
1484 #if 0
1486 * Add a single ber-encoded value to a mod list
1487 * @param ctx An initialized TALLOC_CTX
1488 * @param mods An initialized ADS_MODLIST
1489 * @param name The attribute name to add
1490 * @param val The value to add - NULL means DELETE
1491 * @return ADS STATUS indicating success of add
1493 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1494 const char *name, const struct berval *val)
1496 const struct berval *values[2];
1498 values[0] = val;
1499 values[1] = NULL;
1500 if (!val)
1501 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1502 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1503 name, (const void **) values);
1505 #endif
1508 * Perform an ldap modify
1509 * @param ads connection to ads server
1510 * @param mod_dn DistinguishedName to modify
1511 * @param mods list of modifications to perform
1512 * @return status of modify
1514 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1516 int ret,i;
1517 char *utf8_dn = NULL;
1518 size_t converted_size;
1520 this control is needed to modify that contains a currently
1521 non-existent attribute (but allowable for the object) to run
1523 LDAPControl PermitModify = {
1524 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1525 {0, NULL},
1526 (char) 1};
1527 LDAPControl *controls[2];
1529 controls[0] = &PermitModify;
1530 controls[1] = NULL;
1532 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1533 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1536 /* find the end of the list, marked by NULL or -1 */
1537 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1538 /* make sure the end of the list is NULL */
1539 mods[i] = NULL;
1540 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1541 (LDAPMod **) mods, controls, NULL);
1542 TALLOC_FREE(utf8_dn);
1543 return ADS_ERROR(ret);
1547 * Perform an ldap add
1548 * @param ads connection to ads server
1549 * @param new_dn DistinguishedName to add
1550 * @param mods list of attributes and values for DN
1551 * @return status of add
1553 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1555 int ret, i;
1556 char *utf8_dn = NULL;
1557 size_t converted_size;
1559 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1560 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1561 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1564 /* find the end of the list, marked by NULL or -1 */
1565 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1566 /* make sure the end of the list is NULL */
1567 mods[i] = NULL;
1569 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1570 TALLOC_FREE(utf8_dn);
1571 return ADS_ERROR(ret);
1575 * Delete a DistinguishedName
1576 * @param ads connection to ads server
1577 * @param new_dn DistinguishedName to delete
1578 * @return status of delete
1580 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1582 int ret;
1583 char *utf8_dn = NULL;
1584 size_t converted_size;
1585 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1586 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1587 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1590 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1591 TALLOC_FREE(utf8_dn);
1592 return ADS_ERROR(ret);
1596 * Build an org unit string
1597 * if org unit is Computers or blank then assume a container, otherwise
1598 * assume a / separated list of organisational units.
1599 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1600 * @param ads connection to ads server
1601 * @param org_unit Organizational unit
1602 * @return org unit string - caller must free
1604 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1606 char *ret = NULL;
1608 if (!org_unit || !*org_unit) {
1610 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1612 /* samba4 might not yet respond to a wellknownobject-query */
1613 return ret ? ret : SMB_STRDUP("cn=Computers");
1616 if (strequal(org_unit, "Computers")) {
1617 return SMB_STRDUP("cn=Computers");
1620 /* jmcd: removed "\\" from the separation chars, because it is
1621 needed as an escape for chars like '#' which are valid in an
1622 OU name */
1623 return ads_build_path(org_unit, "/", "ou=", 1);
1627 * Get a org unit string for a well-known GUID
1628 * @param ads connection to ads server
1629 * @param wknguid Well known GUID
1630 * @return org unit string - caller must free
1632 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1634 ADS_STATUS status;
1635 LDAPMessage *res = NULL;
1636 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1637 **bind_dn_exp = NULL;
1638 const char *attrs[] = {"distinguishedName", NULL};
1639 int new_ln, wkn_ln, bind_ln, i;
1641 if (wknguid == NULL) {
1642 return NULL;
1645 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1646 DEBUG(1, ("asprintf failed!\n"));
1647 return NULL;
1650 status = ads_search_dn(ads, &res, base, attrs);
1651 if (!ADS_ERR_OK(status)) {
1652 DEBUG(1,("Failed while searching for: %s\n", base));
1653 goto out;
1656 if (ads_count_replies(ads, res) != 1) {
1657 goto out;
1660 /* substitute the bind-path from the well-known-guid-search result */
1661 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1662 if (!wkn_dn) {
1663 goto out;
1666 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1667 if (!wkn_dn_exp) {
1668 goto out;
1671 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1672 if (!bind_dn_exp) {
1673 goto out;
1676 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1678 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1681 new_ln = wkn_ln - bind_ln;
1683 ret = SMB_STRDUP(wkn_dn_exp[0]);
1684 if (!ret) {
1685 goto out;
1688 for (i=1; i < new_ln; i++) {
1689 char *s = NULL;
1691 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1692 SAFE_FREE(ret);
1693 goto out;
1696 SAFE_FREE(ret);
1697 ret = SMB_STRDUP(s);
1698 free(s);
1699 if (!ret) {
1700 goto out;
1704 out:
1705 SAFE_FREE(base);
1706 ads_msgfree(ads, res);
1707 TALLOC_FREE(wkn_dn);
1708 if (wkn_dn_exp) {
1709 ldap_value_free(wkn_dn_exp);
1711 if (bind_dn_exp) {
1712 ldap_value_free(bind_dn_exp);
1715 return ret;
1719 * Adds (appends) an item to an attribute array, rather then
1720 * replacing the whole list
1721 * @param ctx An initialized TALLOC_CTX
1722 * @param mods An initialized ADS_MODLIST
1723 * @param name name of the ldap attribute to append to
1724 * @param vals an array of values to add
1725 * @return status of addition
1728 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1729 const char *name, const char **vals)
1731 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1732 (const void *) vals);
1736 * Determines the an account's current KVNO via an LDAP lookup
1737 * @param ads An initialized ADS_STRUCT
1738 * @param account_name the NT samaccountname.
1739 * @return the kvno for the account, or -1 in case of a failure.
1742 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1744 LDAPMessage *res = NULL;
1745 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1746 char *filter;
1747 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1748 char *dn_string = NULL;
1749 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1751 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1752 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1753 return kvno;
1755 ret = ads_search(ads, &res, filter, attrs);
1756 SAFE_FREE(filter);
1757 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1758 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1759 ads_msgfree(ads, res);
1760 return kvno;
1763 dn_string = ads_get_dn(ads, talloc_tos(), res);
1764 if (!dn_string) {
1765 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1766 ads_msgfree(ads, res);
1767 return kvno;
1769 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1770 TALLOC_FREE(dn_string);
1772 /* ---------------------------------------------------------
1773 * 0 is returned as a default KVNO from this point on...
1774 * This is done because Windows 2000 does not support key
1775 * version numbers. Chances are that a failure in the next
1776 * step is simply due to Windows 2000 being used for a
1777 * domain controller. */
1778 kvno = 0;
1780 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1781 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1782 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1783 ads_msgfree(ads, res);
1784 return kvno;
1787 /* Success */
1788 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1789 ads_msgfree(ads, res);
1790 return kvno;
1794 * Determines the computer account's current KVNO via an LDAP lookup
1795 * @param ads An initialized ADS_STRUCT
1796 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1797 * @return the kvno for the computer account, or -1 in case of a failure.
1800 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1802 char *computer_account = NULL;
1803 uint32_t kvno = -1;
1805 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1806 return kvno;
1809 kvno = ads_get_kvno(ads, computer_account);
1810 free(computer_account);
1812 return kvno;
1816 * This clears out all registered spn's for a given hostname
1817 * @param ads An initilaized ADS_STRUCT
1818 * @param machine_name the NetBIOS name of the computer.
1819 * @return 0 upon success, non-zero otherwise.
1822 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1824 TALLOC_CTX *ctx;
1825 LDAPMessage *res = NULL;
1826 ADS_MODLIST mods;
1827 const char *servicePrincipalName[1] = {NULL};
1828 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1829 char *dn_string = NULL;
1831 ret = ads_find_machine_acct(ads, &res, machine_name);
1832 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1833 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1834 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1835 ads_msgfree(ads, res);
1836 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1839 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1840 ctx = talloc_init("ads_clear_service_principal_names");
1841 if (!ctx) {
1842 ads_msgfree(ads, res);
1843 return ADS_ERROR(LDAP_NO_MEMORY);
1846 if (!(mods = ads_init_mods(ctx))) {
1847 talloc_destroy(ctx);
1848 ads_msgfree(ads, res);
1849 return ADS_ERROR(LDAP_NO_MEMORY);
1851 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1852 if (!ADS_ERR_OK(ret)) {
1853 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1854 ads_msgfree(ads, res);
1855 talloc_destroy(ctx);
1856 return ret;
1858 dn_string = ads_get_dn(ads, talloc_tos(), res);
1859 if (!dn_string) {
1860 talloc_destroy(ctx);
1861 ads_msgfree(ads, res);
1862 return ADS_ERROR(LDAP_NO_MEMORY);
1864 ret = ads_gen_mod(ads, dn_string, mods);
1865 TALLOC_FREE(dn_string);
1866 if (!ADS_ERR_OK(ret)) {
1867 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1868 machine_name));
1869 ads_msgfree(ads, res);
1870 talloc_destroy(ctx);
1871 return ret;
1874 ads_msgfree(ads, res);
1875 talloc_destroy(ctx);
1876 return ret;
1880 * This adds a service principal name to an existing computer account
1881 * (found by hostname) in AD.
1882 * @param ads An initialized ADS_STRUCT
1883 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1884 * @param my_fqdn The fully qualified DNS name of the machine
1885 * @param spn A string of the service principal to add, i.e. 'host'
1886 * @return 0 upon sucess, or non-zero if a failure occurs
1889 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1890 const char *my_fqdn, const char *spn)
1892 ADS_STATUS ret;
1893 TALLOC_CTX *ctx;
1894 LDAPMessage *res = NULL;
1895 char *psp1, *psp2;
1896 ADS_MODLIST mods;
1897 char *dn_string = NULL;
1898 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1900 ret = ads_find_machine_acct(ads, &res, machine_name);
1901 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1902 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1903 machine_name));
1904 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1905 spn, machine_name, ads->config.realm));
1906 ads_msgfree(ads, res);
1907 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1910 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1911 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1912 ads_msgfree(ads, res);
1913 return ADS_ERROR(LDAP_NO_MEMORY);
1916 /* add short name spn */
1918 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1919 talloc_destroy(ctx);
1920 ads_msgfree(ads, res);
1921 return ADS_ERROR(LDAP_NO_MEMORY);
1923 strlower_m(&psp1[strlen(spn) + 1]);
1924 servicePrincipalName[0] = psp1;
1926 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1927 psp1, machine_name));
1930 /* add fully qualified spn */
1932 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1933 ret = ADS_ERROR(LDAP_NO_MEMORY);
1934 goto out;
1936 strlower_m(&psp2[strlen(spn) + 1]);
1937 servicePrincipalName[1] = psp2;
1939 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1940 psp2, machine_name));
1942 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1943 ret = ADS_ERROR(LDAP_NO_MEMORY);
1944 goto out;
1947 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1948 if (!ADS_ERR_OK(ret)) {
1949 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1950 goto out;
1953 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1954 ret = ADS_ERROR(LDAP_NO_MEMORY);
1955 goto out;
1958 ret = ads_gen_mod(ads, dn_string, mods);
1959 if (!ADS_ERR_OK(ret)) {
1960 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1961 goto out;
1964 out:
1965 TALLOC_FREE( ctx );
1966 ads_msgfree(ads, res);
1967 return ret;
1971 * adds a machine account to the ADS server
1972 * @param ads An intialized ADS_STRUCT
1973 * @param machine_name - the NetBIOS machine name of this account.
1974 * @param account_type A number indicating the type of account to create
1975 * @param org_unit The LDAP path in which to place this account
1976 * @return 0 upon success, or non-zero otherwise
1979 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1980 const char *org_unit)
1982 ADS_STATUS ret;
1983 char *samAccountName, *controlstr;
1984 TALLOC_CTX *ctx;
1985 ADS_MODLIST mods;
1986 char *machine_escaped = NULL;
1987 char *new_dn;
1988 const char *objectClass[] = {"top", "person", "organizationalPerson",
1989 "user", "computer", NULL};
1990 LDAPMessage *res = NULL;
1991 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1992 UF_DONT_EXPIRE_PASSWD |\
1993 UF_ACCOUNTDISABLE );
1995 if (!(ctx = talloc_init("ads_add_machine_acct")))
1996 return ADS_ERROR(LDAP_NO_MEMORY);
1998 ret = ADS_ERROR(LDAP_NO_MEMORY);
2000 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2001 if (!machine_escaped) {
2002 goto done;
2005 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2006 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2008 if ( !new_dn || !samAccountName ) {
2009 goto done;
2012 #ifndef ENCTYPE_ARCFOUR_HMAC
2013 acct_control |= UF_USE_DES_KEY_ONLY;
2014 #endif
2016 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2017 goto done;
2020 if (!(mods = ads_init_mods(ctx))) {
2021 goto done;
2024 ads_mod_str(ctx, &mods, "cn", machine_name);
2025 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2026 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2027 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2029 ret = ads_gen_add(ads, new_dn, mods);
2031 done:
2032 SAFE_FREE(machine_escaped);
2033 ads_msgfree(ads, res);
2034 talloc_destroy(ctx);
2036 return ret;
2040 * move a machine account to another OU on the ADS server
2041 * @param ads - An intialized ADS_STRUCT
2042 * @param machine_name - the NetBIOS machine name of this account.
2043 * @param org_unit - The LDAP path in which to place this account
2044 * @param moved - whether we moved the machine account (optional)
2045 * @return 0 upon success, or non-zero otherwise
2048 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2049 const char *org_unit, bool *moved)
2051 ADS_STATUS rc;
2052 int ldap_status;
2053 LDAPMessage *res = NULL;
2054 char *filter = NULL;
2055 char *computer_dn = NULL;
2056 char *parent_dn;
2057 char *computer_rdn = NULL;
2058 bool need_move = False;
2060 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2061 rc = ADS_ERROR(LDAP_NO_MEMORY);
2062 goto done;
2065 /* Find pre-existing machine */
2066 rc = ads_search(ads, &res, filter, NULL);
2067 if (!ADS_ERR_OK(rc)) {
2068 goto done;
2071 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2072 if (!computer_dn) {
2073 rc = ADS_ERROR(LDAP_NO_MEMORY);
2074 goto done;
2077 parent_dn = ads_parent_dn(computer_dn);
2078 if (strequal(parent_dn, org_unit)) {
2079 goto done;
2082 need_move = True;
2084 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2085 rc = ADS_ERROR(LDAP_NO_MEMORY);
2086 goto done;
2089 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2090 org_unit, 1, NULL, NULL);
2091 rc = ADS_ERROR(ldap_status);
2093 done:
2094 ads_msgfree(ads, res);
2095 SAFE_FREE(filter);
2096 TALLOC_FREE(computer_dn);
2097 SAFE_FREE(computer_rdn);
2099 if (!ADS_ERR_OK(rc)) {
2100 need_move = False;
2103 if (moved) {
2104 *moved = need_move;
2107 return rc;
2111 dump a binary result from ldap
2113 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2115 int i, j;
2116 for (i=0; values[i]; i++) {
2117 printf("%s: ", field);
2118 for (j=0; j<values[i]->bv_len; j++) {
2119 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2121 printf("\n");
2125 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2127 int i;
2128 for (i=0; values[i]; i++) {
2129 NTSTATUS status;
2130 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2131 struct GUID guid;
2133 status = GUID_from_ndr_blob(&in, &guid);
2134 if (NT_STATUS_IS_OK(status)) {
2135 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2136 } else {
2137 printf("%s: INVALID GUID\n", field);
2143 dump a sid result from ldap
2145 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2147 int i;
2148 for (i=0; values[i]; i++) {
2149 struct dom_sid sid;
2150 fstring tmp;
2151 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2152 return;
2154 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2159 dump ntSecurityDescriptor
2161 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2163 TALLOC_CTX *frame = talloc_stackframe();
2164 struct security_descriptor *psd;
2165 NTSTATUS status;
2167 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2168 values[0]->bv_len, &psd);
2169 if (!NT_STATUS_IS_OK(status)) {
2170 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2171 nt_errstr(status)));
2172 TALLOC_FREE(frame);
2173 return;
2176 if (psd) {
2177 ads_disp_sd(ads, talloc_tos(), psd);
2180 TALLOC_FREE(frame);
2184 dump a string result from ldap
2186 static void dump_string(const char *field, char **values)
2188 int i;
2189 for (i=0; values[i]; i++) {
2190 printf("%s: %s\n", field, values[i]);
2195 dump a field from LDAP on stdout
2196 used for debugging
2199 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2201 const struct {
2202 const char *name;
2203 bool string;
2204 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2205 } handlers[] = {
2206 {"objectGUID", False, dump_guid},
2207 {"netbootGUID", False, dump_guid},
2208 {"nTSecurityDescriptor", False, dump_sd},
2209 {"dnsRecord", False, dump_binary},
2210 {"objectSid", False, dump_sid},
2211 {"tokenGroups", False, dump_sid},
2212 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2213 {"tokengroupsGlobalandUniversal", False, dump_sid},
2214 {"mS-DS-CreatorSID", False, dump_sid},
2215 {"msExchMailboxGuid", False, dump_guid},
2216 {NULL, True, NULL}
2218 int i;
2220 if (!field) { /* must be end of an entry */
2221 printf("\n");
2222 return False;
2225 for (i=0; handlers[i].name; i++) {
2226 if (StrCaseCmp(handlers[i].name, field) == 0) {
2227 if (!values) /* first time, indicate string or not */
2228 return handlers[i].string;
2229 handlers[i].handler(ads, field, (struct berval **) values);
2230 break;
2233 if (!handlers[i].name) {
2234 if (!values) /* first time, indicate string conversion */
2235 return True;
2236 dump_string(field, (char **)values);
2238 return False;
2242 * Dump a result from LDAP on stdout
2243 * used for debugging
2244 * @param ads connection to ads server
2245 * @param res Results to dump
2248 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2250 ads_process_results(ads, res, ads_dump_field, NULL);
2254 * Walk through results, calling a function for each entry found.
2255 * The function receives a field name, a berval * array of values,
2256 * and a data area passed through from the start. The function is
2257 * called once with null for field and values at the end of each
2258 * entry.
2259 * @param ads connection to ads server
2260 * @param res Results to process
2261 * @param fn Function for processing each result
2262 * @param data_area user-defined area to pass to function
2264 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2265 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2266 void *data_area)
2268 LDAPMessage *msg;
2269 TALLOC_CTX *ctx;
2270 size_t converted_size;
2272 if (!(ctx = talloc_init("ads_process_results")))
2273 return;
2275 for (msg = ads_first_entry(ads, res); msg;
2276 msg = ads_next_entry(ads, msg)) {
2277 char *utf8_field;
2278 BerElement *b;
2280 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2281 (LDAPMessage *)msg,&b);
2282 utf8_field;
2283 utf8_field=ldap_next_attribute(ads->ldap.ld,
2284 (LDAPMessage *)msg,b)) {
2285 struct berval **ber_vals;
2286 char **str_vals, **utf8_vals;
2287 char *field;
2288 bool string;
2290 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2291 &converted_size))
2293 DEBUG(0,("ads_process_results: "
2294 "pull_utf8_talloc failed: %s",
2295 strerror(errno)));
2298 string = fn(ads, field, NULL, data_area);
2300 if (string) {
2301 utf8_vals = ldap_get_values(ads->ldap.ld,
2302 (LDAPMessage *)msg, field);
2303 str_vals = ads_pull_strvals(ctx,
2304 (const char **) utf8_vals);
2305 fn(ads, field, (void **) str_vals, data_area);
2306 ldap_value_free(utf8_vals);
2307 } else {
2308 ber_vals = ldap_get_values_len(ads->ldap.ld,
2309 (LDAPMessage *)msg, field);
2310 fn(ads, field, (void **) ber_vals, data_area);
2312 ldap_value_free_len(ber_vals);
2314 ldap_memfree(utf8_field);
2316 ber_free(b, 0);
2317 talloc_free_children(ctx);
2318 fn(ads, NULL, NULL, data_area); /* completed an entry */
2321 talloc_destroy(ctx);
2325 * count how many replies are in a LDAPMessage
2326 * @param ads connection to ads server
2327 * @param res Results to count
2328 * @return number of replies
2330 int ads_count_replies(ADS_STRUCT *ads, void *res)
2332 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2336 * pull the first entry from a ADS result
2337 * @param ads connection to ads server
2338 * @param res Results of search
2339 * @return first entry from result
2341 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2343 return ldap_first_entry(ads->ldap.ld, res);
2347 * pull the next entry from a ADS result
2348 * @param ads connection to ads server
2349 * @param res Results of search
2350 * @return next entry from result
2352 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2354 return ldap_next_entry(ads->ldap.ld, res);
2358 * pull the first message from a ADS result
2359 * @param ads connection to ads server
2360 * @param res Results of search
2361 * @return first message from result
2363 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2365 return ldap_first_message(ads->ldap.ld, res);
2369 * pull the next message from a ADS result
2370 * @param ads connection to ads server
2371 * @param res Results of search
2372 * @return next message from result
2374 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2376 return ldap_next_message(ads->ldap.ld, res);
2380 * pull a single string from a ADS result
2381 * @param ads connection to ads server
2382 * @param mem_ctx TALLOC_CTX to use for allocating result string
2383 * @param msg Results of search
2384 * @param field Attribute to retrieve
2385 * @return Result string in talloc context
2387 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2388 const char *field)
2390 char **values;
2391 char *ret = NULL;
2392 char *ux_string;
2393 size_t converted_size;
2395 values = ldap_get_values(ads->ldap.ld, msg, field);
2396 if (!values)
2397 return NULL;
2399 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2400 &converted_size))
2402 ret = ux_string;
2404 ldap_value_free(values);
2405 return ret;
2409 * pull an array of strings from a ADS result
2410 * @param ads connection to ads server
2411 * @param mem_ctx TALLOC_CTX to use for allocating result string
2412 * @param msg Results of search
2413 * @param field Attribute to retrieve
2414 * @return Result strings in talloc context
2416 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2417 LDAPMessage *msg, const char *field,
2418 size_t *num_values)
2420 char **values;
2421 char **ret = NULL;
2422 int i;
2423 size_t converted_size;
2425 values = ldap_get_values(ads->ldap.ld, msg, field);
2426 if (!values)
2427 return NULL;
2429 *num_values = ldap_count_values(values);
2431 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2432 if (!ret) {
2433 ldap_value_free(values);
2434 return NULL;
2437 for (i=0;i<*num_values;i++) {
2438 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2439 &converted_size))
2441 ldap_value_free(values);
2442 return NULL;
2445 ret[i] = NULL;
2447 ldap_value_free(values);
2448 return ret;
2452 * pull an array of strings from a ADS result
2453 * (handle large multivalue attributes with range retrieval)
2454 * @param ads connection to ads server
2455 * @param mem_ctx TALLOC_CTX to use for allocating result string
2456 * @param msg Results of search
2457 * @param field Attribute to retrieve
2458 * @param current_strings strings returned by a previous call to this function
2459 * @param next_attribute The next query should ask for this attribute
2460 * @param num_values How many values did we get this time?
2461 * @param more_values Are there more values to get?
2462 * @return Result strings in talloc context
2464 char **ads_pull_strings_range(ADS_STRUCT *ads,
2465 TALLOC_CTX *mem_ctx,
2466 LDAPMessage *msg, const char *field,
2467 char **current_strings,
2468 const char **next_attribute,
2469 size_t *num_strings,
2470 bool *more_strings)
2472 char *attr;
2473 char *expected_range_attrib, *range_attr;
2474 BerElement *ptr = NULL;
2475 char **strings;
2476 char **new_strings;
2477 size_t num_new_strings;
2478 unsigned long int range_start;
2479 unsigned long int range_end;
2481 /* we might have been given the whole lot anyway */
2482 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2483 *more_strings = False;
2484 return strings;
2487 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2489 /* look for Range result */
2490 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2491 attr;
2492 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2493 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2494 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2495 range_attr = attr;
2496 break;
2498 ldap_memfree(attr);
2500 if (!attr) {
2501 ber_free(ptr, 0);
2502 /* nothing here - this field is just empty */
2503 *more_strings = False;
2504 return NULL;
2507 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2508 &range_start, &range_end) == 2) {
2509 *more_strings = True;
2510 } else {
2511 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2512 &range_start) == 1) {
2513 *more_strings = False;
2514 } else {
2515 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2516 range_attr));
2517 ldap_memfree(range_attr);
2518 *more_strings = False;
2519 return NULL;
2523 if ((*num_strings) != range_start) {
2524 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2525 " - aborting range retreival\n",
2526 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2527 ldap_memfree(range_attr);
2528 *more_strings = False;
2529 return NULL;
2532 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2534 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2535 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2536 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2537 range_attr, (unsigned long int)range_end - range_start + 1,
2538 (unsigned long int)num_new_strings));
2539 ldap_memfree(range_attr);
2540 *more_strings = False;
2541 return NULL;
2544 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2545 *num_strings + num_new_strings);
2547 if (strings == NULL) {
2548 ldap_memfree(range_attr);
2549 *more_strings = False;
2550 return NULL;
2553 if (new_strings && num_new_strings) {
2554 memcpy(&strings[*num_strings], new_strings,
2555 sizeof(*new_strings) * num_new_strings);
2558 (*num_strings) += num_new_strings;
2560 if (*more_strings) {
2561 *next_attribute = talloc_asprintf(mem_ctx,
2562 "%s;range=%d-*",
2563 field,
2564 (int)*num_strings);
2566 if (!*next_attribute) {
2567 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2568 ldap_memfree(range_attr);
2569 *more_strings = False;
2570 return NULL;
2574 ldap_memfree(range_attr);
2576 return strings;
2580 * pull a single uint32 from a ADS result
2581 * @param ads connection to ads server
2582 * @param msg Results of search
2583 * @param field Attribute to retrieve
2584 * @param v Pointer to int to store result
2585 * @return boolean inidicating success
2587 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2588 uint32 *v)
2590 char **values;
2592 values = ldap_get_values(ads->ldap.ld, msg, field);
2593 if (!values)
2594 return False;
2595 if (!values[0]) {
2596 ldap_value_free(values);
2597 return False;
2600 *v = atoi(values[0]);
2601 ldap_value_free(values);
2602 return True;
2606 * pull a single objectGUID from an ADS result
2607 * @param ads connection to ADS server
2608 * @param msg results of search
2609 * @param guid 37-byte area to receive text guid
2610 * @return boolean indicating success
2612 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2614 DATA_BLOB blob;
2615 NTSTATUS status;
2617 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2618 &blob)) {
2619 return false;
2622 status = GUID_from_ndr_blob(&blob, guid);
2623 talloc_free(blob.data);
2624 return NT_STATUS_IS_OK(status);
2629 * pull a single struct dom_sid from a ADS result
2630 * @param ads connection to ads server
2631 * @param msg Results of search
2632 * @param field Attribute to retrieve
2633 * @param sid Pointer to sid to store result
2634 * @return boolean inidicating success
2636 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2637 struct dom_sid *sid)
2639 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2643 * pull an array of struct dom_sids from a ADS result
2644 * @param ads connection to ads server
2645 * @param mem_ctx TALLOC_CTX for allocating sid array
2646 * @param msg Results of search
2647 * @param field Attribute to retrieve
2648 * @param sids pointer to sid array to allocate
2649 * @return the count of SIDs pulled
2651 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2652 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2654 struct berval **values;
2655 bool ret;
2656 int count, i;
2658 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2660 if (!values)
2661 return 0;
2663 for (i=0; values[i]; i++)
2664 /* nop */ ;
2666 if (i) {
2667 (*sids) = TALLOC_ARRAY(mem_ctx, struct dom_sid, i);
2668 if (!(*sids)) {
2669 ldap_value_free_len(values);
2670 return 0;
2672 } else {
2673 (*sids) = NULL;
2676 count = 0;
2677 for (i=0; values[i]; i++) {
2678 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2679 if (ret) {
2680 DEBUG(10, ("pulling SID: %s\n",
2681 sid_string_dbg(&(*sids)[count])));
2682 count++;
2686 ldap_value_free_len(values);
2687 return count;
2691 * pull a struct security_descriptor from a ADS result
2692 * @param ads connection to ads server
2693 * @param mem_ctx TALLOC_CTX for allocating sid array
2694 * @param msg Results of search
2695 * @param field Attribute to retrieve
2696 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2697 * @return boolean inidicating success
2699 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2700 LDAPMessage *msg, const char *field,
2701 struct security_descriptor **sd)
2703 struct berval **values;
2704 bool ret = true;
2706 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2708 if (!values) return false;
2710 if (values[0]) {
2711 NTSTATUS status;
2712 status = unmarshall_sec_desc(mem_ctx,
2713 (uint8 *)values[0]->bv_val,
2714 values[0]->bv_len, sd);
2715 if (!NT_STATUS_IS_OK(status)) {
2716 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2717 nt_errstr(status)));
2718 ret = false;
2722 ldap_value_free_len(values);
2723 return ret;
2727 * in order to support usernames longer than 21 characters we need to
2728 * use both the sAMAccountName and the userPrincipalName attributes
2729 * It seems that not all users have the userPrincipalName attribute set
2731 * @param ads connection to ads server
2732 * @param mem_ctx TALLOC_CTX for allocating sid array
2733 * @param msg Results of search
2734 * @return the username
2736 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2737 LDAPMessage *msg)
2739 #if 0 /* JERRY */
2740 char *ret, *p;
2742 /* lookup_name() only works on the sAMAccountName to
2743 returning the username portion of userPrincipalName
2744 breaks winbindd_getpwnam() */
2746 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2747 if (ret && (p = strchr_m(ret, '@'))) {
2748 *p = 0;
2749 return ret;
2751 #endif
2752 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2757 * find the update serial number - this is the core of the ldap cache
2758 * @param ads connection to ads server
2759 * @param ads connection to ADS server
2760 * @param usn Pointer to retrieved update serial number
2761 * @return status of search
2763 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2765 const char *attrs[] = {"highestCommittedUSN", NULL};
2766 ADS_STATUS status;
2767 LDAPMessage *res;
2769 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2770 if (!ADS_ERR_OK(status))
2771 return status;
2773 if (ads_count_replies(ads, res) != 1) {
2774 ads_msgfree(ads, res);
2775 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2778 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2779 ads_msgfree(ads, res);
2780 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2783 ads_msgfree(ads, res);
2784 return ADS_SUCCESS;
2787 /* parse a ADS timestring - typical string is
2788 '20020917091222.0Z0' which means 09:12.22 17th September
2789 2002, timezone 0 */
2790 static time_t ads_parse_time(const char *str)
2792 struct tm tm;
2794 ZERO_STRUCT(tm);
2796 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2797 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2798 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2799 return 0;
2801 tm.tm_year -= 1900;
2802 tm.tm_mon -= 1;
2804 return timegm(&tm);
2807 /********************************************************************
2808 ********************************************************************/
2810 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2812 const char *attrs[] = {"currentTime", NULL};
2813 ADS_STATUS status;
2814 LDAPMessage *res;
2815 char *timestr;
2816 TALLOC_CTX *ctx;
2817 ADS_STRUCT *ads_s = ads;
2819 if (!(ctx = talloc_init("ads_current_time"))) {
2820 return ADS_ERROR(LDAP_NO_MEMORY);
2823 /* establish a new ldap tcp session if necessary */
2825 if ( !ads->ldap.ld ) {
2826 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2827 ads->server.ldap_server )) == NULL )
2829 goto done;
2831 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2832 status = ads_connect( ads_s );
2833 if ( !ADS_ERR_OK(status))
2834 goto done;
2837 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2838 if (!ADS_ERR_OK(status)) {
2839 goto done;
2842 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2843 if (!timestr) {
2844 ads_msgfree(ads_s, res);
2845 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2846 goto done;
2849 /* but save the time and offset in the original ADS_STRUCT */
2851 ads->config.current_time = ads_parse_time(timestr);
2853 if (ads->config.current_time != 0) {
2854 ads->auth.time_offset = ads->config.current_time - time(NULL);
2855 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2858 ads_msgfree(ads, res);
2860 status = ADS_SUCCESS;
2862 done:
2863 /* free any temporary ads connections */
2864 if ( ads_s != ads ) {
2865 ads_destroy( &ads_s );
2867 talloc_destroy(ctx);
2869 return status;
2872 /********************************************************************
2873 ********************************************************************/
2875 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2877 const char *attrs[] = {"domainFunctionality", NULL};
2878 ADS_STATUS status;
2879 LDAPMessage *res;
2880 ADS_STRUCT *ads_s = ads;
2882 *val = DS_DOMAIN_FUNCTION_2000;
2884 /* establish a new ldap tcp session if necessary */
2886 if ( !ads->ldap.ld ) {
2887 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2888 ads->server.ldap_server )) == NULL )
2890 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2891 goto done;
2893 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2894 status = ads_connect( ads_s );
2895 if ( !ADS_ERR_OK(status))
2896 goto done;
2899 /* If the attribute does not exist assume it is a Windows 2000
2900 functional domain */
2902 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2903 if (!ADS_ERR_OK(status)) {
2904 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2905 status = ADS_SUCCESS;
2907 goto done;
2910 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2911 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2913 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2916 ads_msgfree(ads, res);
2918 done:
2919 /* free any temporary ads connections */
2920 if ( ads_s != ads ) {
2921 ads_destroy( &ads_s );
2924 return status;
2928 * find the domain sid for our domain
2929 * @param ads connection to ads server
2930 * @param sid Pointer to domain sid
2931 * @return status of search
2933 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2935 const char *attrs[] = {"objectSid", NULL};
2936 LDAPMessage *res;
2937 ADS_STATUS rc;
2939 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2940 attrs, &res);
2941 if (!ADS_ERR_OK(rc)) return rc;
2942 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2943 ads_msgfree(ads, res);
2944 return ADS_ERROR_SYSTEM(ENOENT);
2946 ads_msgfree(ads, res);
2948 return ADS_SUCCESS;
2952 * find our site name
2953 * @param ads connection to ads server
2954 * @param mem_ctx Pointer to talloc context
2955 * @param site_name Pointer to the sitename
2956 * @return status of search
2958 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2960 ADS_STATUS status;
2961 LDAPMessage *res;
2962 const char *dn, *service_name;
2963 const char *attrs[] = { "dsServiceName", NULL };
2965 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2966 if (!ADS_ERR_OK(status)) {
2967 return status;
2970 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2971 if (service_name == NULL) {
2972 ads_msgfree(ads, res);
2973 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2976 ads_msgfree(ads, res);
2978 /* go up three levels */
2979 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2980 if (dn == NULL) {
2981 return ADS_ERROR(LDAP_NO_MEMORY);
2984 *site_name = talloc_strdup(mem_ctx, dn);
2985 if (*site_name == NULL) {
2986 return ADS_ERROR(LDAP_NO_MEMORY);
2989 return status;
2991 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2996 * find the site dn where a machine resides
2997 * @param ads connection to ads server
2998 * @param mem_ctx Pointer to talloc context
2999 * @param computer_name name of the machine
3000 * @param site_name Pointer to the sitename
3001 * @return status of search
3003 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3005 ADS_STATUS status;
3006 LDAPMessage *res;
3007 const char *parent, *filter;
3008 char *config_context = NULL;
3009 char *dn;
3011 /* shortcut a query */
3012 if (strequal(computer_name, ads->config.ldap_server_name)) {
3013 return ads_site_dn(ads, mem_ctx, site_dn);
3016 status = ads_config_path(ads, mem_ctx, &config_context);
3017 if (!ADS_ERR_OK(status)) {
3018 return status;
3021 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3022 if (filter == NULL) {
3023 return ADS_ERROR(LDAP_NO_MEMORY);
3026 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3027 filter, NULL, &res);
3028 if (!ADS_ERR_OK(status)) {
3029 return status;
3032 if (ads_count_replies(ads, res) != 1) {
3033 ads_msgfree(ads, res);
3034 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3037 dn = ads_get_dn(ads, mem_ctx, res);
3038 if (dn == NULL) {
3039 ads_msgfree(ads, res);
3040 return ADS_ERROR(LDAP_NO_MEMORY);
3043 /* go up three levels */
3044 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3045 if (parent == NULL) {
3046 ads_msgfree(ads, res);
3047 TALLOC_FREE(dn);
3048 return ADS_ERROR(LDAP_NO_MEMORY);
3051 *site_dn = talloc_strdup(mem_ctx, parent);
3052 if (*site_dn == NULL) {
3053 ads_msgfree(ads, res);
3054 TALLOC_FREE(dn);
3055 return ADS_ERROR(LDAP_NO_MEMORY);
3058 TALLOC_FREE(dn);
3059 ads_msgfree(ads, res);
3061 return status;
3065 * get the upn suffixes for a domain
3066 * @param ads connection to ads server
3067 * @param mem_ctx Pointer to talloc context
3068 * @param suffixes Pointer to an array of suffixes
3069 * @param num_suffixes Pointer to the number of suffixes
3070 * @return status of search
3072 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3074 ADS_STATUS status;
3075 LDAPMessage *res;
3076 const char *base;
3077 char *config_context = NULL;
3078 const char *attrs[] = { "uPNSuffixes", NULL };
3080 status = ads_config_path(ads, mem_ctx, &config_context);
3081 if (!ADS_ERR_OK(status)) {
3082 return status;
3085 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3086 if (base == NULL) {
3087 return ADS_ERROR(LDAP_NO_MEMORY);
3090 status = ads_search_dn(ads, &res, base, attrs);
3091 if (!ADS_ERR_OK(status)) {
3092 return status;
3095 if (ads_count_replies(ads, res) != 1) {
3096 ads_msgfree(ads, res);
3097 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3100 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3101 if ((*suffixes) == NULL) {
3102 ads_msgfree(ads, res);
3103 return ADS_ERROR(LDAP_NO_MEMORY);
3106 ads_msgfree(ads, res);
3108 return status;
3112 * get the joinable ous for a domain
3113 * @param ads connection to ads server
3114 * @param mem_ctx Pointer to talloc context
3115 * @param ous Pointer to an array of ous
3116 * @param num_ous Pointer to the number of ous
3117 * @return status of search
3119 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3120 TALLOC_CTX *mem_ctx,
3121 char ***ous,
3122 size_t *num_ous)
3124 ADS_STATUS status;
3125 LDAPMessage *res = NULL;
3126 LDAPMessage *msg = NULL;
3127 const char *attrs[] = { "dn", NULL };
3128 int count = 0;
3130 status = ads_search(ads, &res,
3131 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3132 attrs);
3133 if (!ADS_ERR_OK(status)) {
3134 return status;
3137 count = ads_count_replies(ads, res);
3138 if (count < 1) {
3139 ads_msgfree(ads, res);
3140 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3143 for (msg = ads_first_entry(ads, res); msg;
3144 msg = ads_next_entry(ads, msg)) {
3146 char *dn = NULL;
3148 dn = ads_get_dn(ads, talloc_tos(), msg);
3149 if (!dn) {
3150 ads_msgfree(ads, res);
3151 return ADS_ERROR(LDAP_NO_MEMORY);
3154 if (!add_string_to_array(mem_ctx, dn,
3155 (const char ***)ous,
3156 (int *)num_ous)) {
3157 TALLOC_FREE(dn);
3158 ads_msgfree(ads, res);
3159 return ADS_ERROR(LDAP_NO_MEMORY);
3162 TALLOC_FREE(dn);
3165 ads_msgfree(ads, res);
3167 return status;
3172 * pull a struct dom_sid from an extended dn string
3173 * @param mem_ctx TALLOC_CTX
3174 * @param extended_dn string
3175 * @param flags string type of extended_dn
3176 * @param sid pointer to a struct dom_sid
3177 * @return NT_STATUS_OK on success,
3178 * NT_INVALID_PARAMETER on error,
3179 * NT_STATUS_NOT_FOUND if no SID present
3181 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3182 const char *extended_dn,
3183 enum ads_extended_dn_flags flags,
3184 struct dom_sid *sid)
3186 char *p, *q, *dn;
3188 if (!extended_dn) {
3189 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3192 /* otherwise extended_dn gets stripped off */
3193 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3194 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3197 * ADS_EXTENDED_DN_HEX_STRING:
3198 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3200 * ADS_EXTENDED_DN_STRING (only with w2k3):
3201 * <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
3203 * Object with no SID, such as an Exchange Public Folder
3204 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3207 p = strchr(dn, ';');
3208 if (!p) {
3209 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3212 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3213 DEBUG(5,("No SID present in extended dn\n"));
3214 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3217 p += strlen(";<SID=");
3219 q = strchr(p, '>');
3220 if (!q) {
3221 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3224 *q = '\0';
3226 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3228 switch (flags) {
3230 case ADS_EXTENDED_DN_STRING:
3231 if (!string_to_sid(sid, p)) {
3232 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3234 break;
3235 case ADS_EXTENDED_DN_HEX_STRING: {
3236 fstring buf;
3237 size_t buf_len;
3239 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3240 if (buf_len == 0) {
3241 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3244 if (!sid_parse(buf, buf_len, sid)) {
3245 DEBUG(10,("failed to parse sid\n"));
3246 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3248 break;
3250 default:
3251 DEBUG(10,("unknown extended dn format\n"));
3252 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3255 return ADS_ERROR_NT(NT_STATUS_OK);
3259 * pull an array of struct dom_sids from a ADS result
3260 * @param ads connection to ads server
3261 * @param mem_ctx TALLOC_CTX for allocating sid array
3262 * @param msg Results of search
3263 * @param field Attribute to retrieve
3264 * @param flags string type of extended_dn
3265 * @param sids pointer to sid array to allocate
3266 * @return the count of SIDs pulled
3268 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3269 TALLOC_CTX *mem_ctx,
3270 LDAPMessage *msg,
3271 const char *field,
3272 enum ads_extended_dn_flags flags,
3273 struct dom_sid **sids)
3275 int i;
3276 ADS_STATUS rc;
3277 size_t dn_count, ret_count = 0;
3278 char **dn_strings;
3280 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3281 &dn_count)) == NULL) {
3282 return 0;
3285 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, dn_count + 1);
3286 if (!(*sids)) {
3287 TALLOC_FREE(dn_strings);
3288 return 0;
3291 for (i=0; i<dn_count; i++) {
3292 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3293 flags, &(*sids)[i]);
3294 if (!ADS_ERR_OK(rc)) {
3295 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3296 NT_STATUS_NOT_FOUND)) {
3297 continue;
3299 else {
3300 TALLOC_FREE(*sids);
3301 TALLOC_FREE(dn_strings);
3302 return 0;
3305 ret_count++;
3308 TALLOC_FREE(dn_strings);
3310 return ret_count;
3313 /********************************************************************
3314 ********************************************************************/
3316 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3318 LDAPMessage *res = NULL;
3319 ADS_STATUS status;
3320 int count = 0;
3321 char *name = NULL;
3323 status = ads_find_machine_acct(ads, &res, global_myname());
3324 if (!ADS_ERR_OK(status)) {
3325 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3326 global_myname()));
3327 goto out;
3330 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3331 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3332 goto out;
3335 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3336 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3339 out:
3340 ads_msgfree(ads, res);
3342 return name;
3345 /********************************************************************
3346 ********************************************************************/
3348 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3350 LDAPMessage *res = NULL;
3351 ADS_STATUS status;
3352 int count = 0;
3353 char *name = NULL;
3355 status = ads_find_machine_acct(ads, &res, machine_name);
3356 if (!ADS_ERR_OK(status)) {
3357 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3358 global_myname()));
3359 goto out;
3362 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3363 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3364 goto out;
3367 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3368 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3371 out:
3372 ads_msgfree(ads, res);
3374 return name;
3377 /********************************************************************
3378 ********************************************************************/
3380 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3382 LDAPMessage *res = NULL;
3383 ADS_STATUS status;
3384 int count = 0;
3385 char *name = NULL;
3387 status = ads_find_machine_acct(ads, &res, global_myname());
3388 if (!ADS_ERR_OK(status)) {
3389 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3390 global_myname()));
3391 goto out;
3394 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3395 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3396 goto out;
3399 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3400 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3403 out:
3404 ads_msgfree(ads, res);
3406 return name;
3409 #if 0
3411 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3414 * Join a machine to a realm
3415 * Creates the machine account and sets the machine password
3416 * @param ads connection to ads server
3417 * @param machine name of host to add
3418 * @param org_unit Organizational unit to place machine in
3419 * @return status of join
3421 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3422 uint32 account_type, const char *org_unit)
3424 ADS_STATUS status;
3425 LDAPMessage *res = NULL;
3426 char *machine;
3428 /* machine name must be lowercase */
3429 machine = SMB_STRDUP(machine_name);
3430 strlower_m(machine);
3433 status = ads_find_machine_acct(ads, (void **)&res, machine);
3434 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3435 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3436 status = ads_leave_realm(ads, machine);
3437 if (!ADS_ERR_OK(status)) {
3438 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3439 machine, ads->config.realm));
3440 return status;
3444 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3445 if (!ADS_ERR_OK(status)) {
3446 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3447 SAFE_FREE(machine);
3448 return status;
3451 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3452 if (!ADS_ERR_OK(status)) {
3453 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3454 SAFE_FREE(machine);
3455 return status;
3458 SAFE_FREE(machine);
3459 ads_msgfree(ads, res);
3461 return status;
3463 #endif
3466 * Delete a machine from the realm
3467 * @param ads connection to ads server
3468 * @param hostname Machine to remove
3469 * @return status of delete
3471 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3473 ADS_STATUS status;
3474 void *msg;
3475 LDAPMessage *res;
3476 char *hostnameDN, *host;
3477 int rc;
3478 LDAPControl ldap_control;
3479 LDAPControl * pldap_control[2] = {NULL, NULL};
3481 pldap_control[0] = &ldap_control;
3482 memset(&ldap_control, 0, sizeof(LDAPControl));
3483 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3485 /* hostname must be lowercase */
3486 host = SMB_STRDUP(hostname);
3487 strlower_m(host);
3489 status = ads_find_machine_acct(ads, &res, host);
3490 if (!ADS_ERR_OK(status)) {
3491 DEBUG(0, ("Host account for %s does not exist.\n", host));
3492 SAFE_FREE(host);
3493 return status;
3496 msg = ads_first_entry(ads, res);
3497 if (!msg) {
3498 SAFE_FREE(host);
3499 return ADS_ERROR_SYSTEM(ENOENT);
3502 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3503 if (hostnameDN == NULL) {
3504 SAFE_FREE(host);
3505 return ADS_ERROR_SYSTEM(ENOENT);
3508 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3509 if (rc) {
3510 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3511 }else {
3512 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3515 if (rc != LDAP_SUCCESS) {
3516 const char *attrs[] = { "cn", NULL };
3517 LDAPMessage *msg_sub;
3519 /* we only search with scope ONE, we do not expect any further
3520 * objects to be created deeper */
3522 status = ads_do_search_retry(ads, hostnameDN,
3523 LDAP_SCOPE_ONELEVEL,
3524 "(objectclass=*)", attrs, &res);
3526 if (!ADS_ERR_OK(status)) {
3527 SAFE_FREE(host);
3528 TALLOC_FREE(hostnameDN);
3529 return status;
3532 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3533 msg_sub = ads_next_entry(ads, msg_sub)) {
3535 char *dn = NULL;
3537 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3538 SAFE_FREE(host);
3539 TALLOC_FREE(hostnameDN);
3540 return ADS_ERROR(LDAP_NO_MEMORY);
3543 status = ads_del_dn(ads, dn);
3544 if (!ADS_ERR_OK(status)) {
3545 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3546 SAFE_FREE(host);
3547 TALLOC_FREE(dn);
3548 TALLOC_FREE(hostnameDN);
3549 return status;
3552 TALLOC_FREE(dn);
3555 /* there should be no subordinate objects anymore */
3556 status = ads_do_search_retry(ads, hostnameDN,
3557 LDAP_SCOPE_ONELEVEL,
3558 "(objectclass=*)", attrs, &res);
3560 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3561 SAFE_FREE(host);
3562 TALLOC_FREE(hostnameDN);
3563 return status;
3566 /* delete hostnameDN now */
3567 status = ads_del_dn(ads, hostnameDN);
3568 if (!ADS_ERR_OK(status)) {
3569 SAFE_FREE(host);
3570 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3571 TALLOC_FREE(hostnameDN);
3572 return status;
3576 TALLOC_FREE(hostnameDN);
3578 status = ads_find_machine_acct(ads, &res, host);
3579 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3580 DEBUG(3, ("Failed to remove host account.\n"));
3581 SAFE_FREE(host);
3582 return status;
3585 SAFE_FREE(host);
3586 return status;
3590 * pull all token-sids from an LDAP dn
3591 * @param ads connection to ads server
3592 * @param mem_ctx TALLOC_CTX for allocating sid array
3593 * @param dn of LDAP object
3594 * @param user_sid pointer to struct dom_sid (objectSid)
3595 * @param primary_group_sid pointer to struct dom_sid (self composed)
3596 * @param sids pointer to sid array to allocate
3597 * @param num_sids counter of SIDs pulled
3598 * @return status of token query
3600 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3601 TALLOC_CTX *mem_ctx,
3602 const char *dn,
3603 struct dom_sid *user_sid,
3604 struct dom_sid *primary_group_sid,
3605 struct dom_sid **sids,
3606 size_t *num_sids)
3608 ADS_STATUS status;
3609 LDAPMessage *res = NULL;
3610 int count = 0;
3611 size_t tmp_num_sids;
3612 struct dom_sid *tmp_sids;
3613 struct dom_sid tmp_user_sid;
3614 struct dom_sid tmp_primary_group_sid;
3615 uint32 pgid;
3616 const char *attrs[] = {
3617 "objectSid",
3618 "tokenGroups",
3619 "primaryGroupID",
3620 NULL
3623 status = ads_search_retry_dn(ads, &res, dn, attrs);
3624 if (!ADS_ERR_OK(status)) {
3625 return status;
3628 count = ads_count_replies(ads, res);
3629 if (count != 1) {
3630 ads_msgfree(ads, res);
3631 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3634 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3635 ads_msgfree(ads, res);
3636 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3639 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3640 ads_msgfree(ads, res);
3641 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3645 /* hack to compose the primary group sid without knowing the
3646 * domsid */
3648 struct dom_sid domsid;
3650 sid_copy(&domsid, &tmp_user_sid);
3652 if (!sid_split_rid(&domsid, NULL)) {
3653 ads_msgfree(ads, res);
3654 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3657 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3658 ads_msgfree(ads, res);
3659 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3663 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3665 if (tmp_num_sids == 0 || !tmp_sids) {
3666 ads_msgfree(ads, res);
3667 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3670 if (num_sids) {
3671 *num_sids = tmp_num_sids;
3674 if (sids) {
3675 *sids = tmp_sids;
3678 if (user_sid) {
3679 *user_sid = tmp_user_sid;
3682 if (primary_group_sid) {
3683 *primary_group_sid = tmp_primary_group_sid;
3686 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3688 ads_msgfree(ads, res);
3689 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3693 * Find a sAMAccoutName in LDAP
3694 * @param ads connection to ads server
3695 * @param mem_ctx TALLOC_CTX for allocating sid array
3696 * @param samaccountname to search
3697 * @param uac_ret uint32 pointer userAccountControl attribute value
3698 * @param dn_ret pointer to dn
3699 * @return status of token query
3701 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3702 TALLOC_CTX *mem_ctx,
3703 const char *samaccountname,
3704 uint32 *uac_ret,
3705 const char **dn_ret)
3707 ADS_STATUS status;
3708 const char *attrs[] = { "userAccountControl", NULL };
3709 const char *filter;
3710 LDAPMessage *res = NULL;
3711 char *dn = NULL;
3712 uint32 uac = 0;
3714 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3715 samaccountname);
3716 if (filter == NULL) {
3717 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3718 goto out;
3721 status = ads_do_search_all(ads, ads->config.bind_path,
3722 LDAP_SCOPE_SUBTREE,
3723 filter, attrs, &res);
3725 if (!ADS_ERR_OK(status)) {
3726 goto out;
3729 if (ads_count_replies(ads, res) != 1) {
3730 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3731 goto out;
3734 dn = ads_get_dn(ads, talloc_tos(), res);
3735 if (dn == NULL) {
3736 status = ADS_ERROR(LDAP_NO_MEMORY);
3737 goto out;
3740 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3741 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3742 goto out;
3745 if (uac_ret) {
3746 *uac_ret = uac;
3749 if (dn_ret) {
3750 *dn_ret = talloc_strdup(mem_ctx, dn);
3751 if (!*dn_ret) {
3752 status = ADS_ERROR(LDAP_NO_MEMORY);
3753 goto out;
3756 out:
3757 TALLOC_FREE(dn);
3758 ads_msgfree(ads, res);
3760 return status;
3764 * find our configuration path
3765 * @param ads connection to ads server
3766 * @param mem_ctx Pointer to talloc context
3767 * @param config_path Pointer to the config path
3768 * @return status of search
3770 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3771 TALLOC_CTX *mem_ctx,
3772 char **config_path)
3774 ADS_STATUS status;
3775 LDAPMessage *res = NULL;
3776 const char *config_context = NULL;
3777 const char *attrs[] = { "configurationNamingContext", NULL };
3779 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3780 "(objectclass=*)", attrs, &res);
3781 if (!ADS_ERR_OK(status)) {
3782 return status;
3785 config_context = ads_pull_string(ads, mem_ctx, res,
3786 "configurationNamingContext");
3787 ads_msgfree(ads, res);
3788 if (!config_context) {
3789 return ADS_ERROR(LDAP_NO_MEMORY);
3792 if (config_path) {
3793 *config_path = talloc_strdup(mem_ctx, config_context);
3794 if (!*config_path) {
3795 return ADS_ERROR(LDAP_NO_MEMORY);
3799 return ADS_ERROR(LDAP_SUCCESS);
3803 * find the displayName of an extended right
3804 * @param ads connection to ads server
3805 * @param config_path The config path
3806 * @param mem_ctx Pointer to talloc context
3807 * @param GUID struct of the rightsGUID
3808 * @return status of search
3810 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3811 const char *config_path,
3812 TALLOC_CTX *mem_ctx,
3813 const struct GUID *rights_guid)
3815 ADS_STATUS rc;
3816 LDAPMessage *res = NULL;
3817 char *expr = NULL;
3818 const char *attrs[] = { "displayName", NULL };
3819 const char *result = NULL;
3820 const char *path;
3822 if (!ads || !mem_ctx || !rights_guid) {
3823 goto done;
3826 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3827 GUID_string(mem_ctx, rights_guid));
3828 if (!expr) {
3829 goto done;
3832 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3833 if (!path) {
3834 goto done;
3837 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3838 expr, attrs, &res);
3839 if (!ADS_ERR_OK(rc)) {
3840 goto done;
3843 if (ads_count_replies(ads, res) != 1) {
3844 goto done;
3847 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3849 done:
3850 ads_msgfree(ads, res);
3851 return result;
3855 * verify or build and verify an account ou
3856 * @param mem_ctx Pointer to talloc context
3857 * @param ads connection to ads server
3858 * @param account_ou
3859 * @return status of search
3862 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3863 ADS_STRUCT *ads,
3864 const char **account_ou)
3866 char **exploded_dn;
3867 const char *name;
3868 char *ou_string;
3870 exploded_dn = ldap_explode_dn(*account_ou, 0);
3871 if (exploded_dn) {
3872 ldap_value_free(exploded_dn);
3873 return ADS_SUCCESS;
3876 ou_string = ads_ou_string(ads, *account_ou);
3877 if (!ou_string) {
3878 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3881 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3882 ads->config.bind_path);
3883 SAFE_FREE(ou_string);
3885 if (!name) {
3886 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3889 exploded_dn = ldap_explode_dn(name, 0);
3890 if (!exploded_dn) {
3891 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3893 ldap_value_free(exploded_dn);
3895 *account_ou = name;
3896 return ADS_SUCCESS;
3899 #endif