s3/libads: use monotonic clock for ldap connection timeouts
[Samba/gebeck_regimport.git] / source3 / libads / ldap.c
blob970f20a8d91788ecaaaa865f000f9d0673af1b33
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"
31 #ifdef HAVE_LDAP
33 /**
34 * @file ldap.c
35 * @brief basic ldap client-side routines for ads server communications
37 * The routines contained here should do the necessary ldap calls for
38 * ads setups.
40 * Important note: attribute names passed into ads_ routines must
41 * already be in UTF-8 format. We do not convert them because in almost
42 * all cases, they are just ascii (which is represented with the same
43 * codepoints in UTF-8). This may have to change at some point
44 **/
47 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
49 static SIG_ATOMIC_T gotalarm;
51 /***************************************************************
52 Signal function to tell us we timed out.
53 ****************************************************************/
55 static void gotalarm_sig(int signum)
57 gotalarm = 1;
60 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
62 LDAP *ldp = NULL;
65 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
66 "%u seconds\n", server, port, to));
68 /* Setup timeout */
69 gotalarm = 0;
70 CatchSignal(SIGALRM, gotalarm_sig);
71 alarm(to);
72 /* End setup timeout. */
74 ldp = ldap_open(server, port);
76 if (ldp == NULL) {
77 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
78 server, port, strerror(errno)));
79 } else {
80 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
83 /* Teardown timeout. */
84 CatchSignal(SIGALRM, SIG_IGN);
85 alarm(0);
87 return ldp;
90 static int ldap_search_with_timeout(LDAP *ld,
91 LDAP_CONST char *base,
92 int scope,
93 LDAP_CONST char *filter,
94 char **attrs,
95 int attrsonly,
96 LDAPControl **sctrls,
97 LDAPControl **cctrls,
98 int sizelimit,
99 LDAPMessage **res )
101 struct timeval timeout;
102 int result;
104 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
105 timeout.tv_sec = lp_ldap_timeout();
106 timeout.tv_usec = 0;
108 /* Setup alarm timeout.... Do we need both of these ? JRA. */
109 gotalarm = 0;
110 CatchSignal(SIGALRM, gotalarm_sig);
111 alarm(lp_ldap_timeout());
112 /* End setup timeout. */
114 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
115 attrsonly, sctrls, cctrls, &timeout,
116 sizelimit, res);
118 /* Teardown timeout. */
119 CatchSignal(SIGALRM, SIG_IGN);
120 alarm(0);
122 if (gotalarm != 0)
123 return LDAP_TIMELIMIT_EXCEEDED;
126 * A bug in OpenLDAP means ldap_search_ext_s can return
127 * LDAP_SUCCESS but with a NULL res pointer. Cope with
128 * this. See bug #6279 for details. JRA.
131 if (*res == NULL) {
132 return LDAP_TIMELIMIT_EXCEEDED;
135 return result;
138 /**********************************************
139 Do client and server sitename match ?
140 **********************************************/
142 bool ads_sitename_match(ADS_STRUCT *ads)
144 if (ads->config.server_site_name == NULL &&
145 ads->config.client_site_name == NULL ) {
146 DEBUG(10,("ads_sitename_match: both null\n"));
147 return True;
149 if (ads->config.server_site_name &&
150 ads->config.client_site_name &&
151 strequal(ads->config.server_site_name,
152 ads->config.client_site_name)) {
153 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
154 return True;
156 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
157 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
158 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
159 return False;
162 /**********************************************
163 Is this the closest DC ?
164 **********************************************/
166 bool ads_closest_dc(ADS_STRUCT *ads)
168 if (ads->config.flags & NBT_SERVER_CLOSEST) {
169 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
170 return True;
173 /* not sure if this can ever happen */
174 if (ads_sitename_match(ads)) {
175 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
176 return True;
179 if (ads->config.client_site_name == NULL) {
180 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
181 return True;
184 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
185 ads->config.ldap_server_name));
187 return False;
192 try a connection to a given ldap server, returning True and setting the servers IP
193 in the ads struct if successful
195 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
197 char *srv;
198 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
199 TALLOC_CTX *frame = talloc_stackframe();
200 bool ret = false;
202 if (!server || !*server) {
203 TALLOC_FREE(frame);
204 return False;
207 if (!is_ipaddress(server)) {
208 struct sockaddr_storage ss;
209 char addr[INET6_ADDRSTRLEN];
211 if (!resolve_name(server, &ss, 0x20, true)) {
212 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
213 server ));
214 TALLOC_FREE(frame);
215 return false;
217 print_sockaddr(addr, sizeof(addr), &ss);
218 srv = talloc_strdup(frame, addr);
219 } else {
220 /* this copes with inet_ntoa brokenness */
221 srv = talloc_strdup(frame, server);
224 if (!srv) {
225 TALLOC_FREE(frame);
226 return false;
229 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
230 srv, ads->server.realm));
232 ZERO_STRUCT( cldap_reply );
234 if ( !ads_cldap_netlogon_5(frame, srv, ads->server.realm, &cldap_reply ) ) {
235 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
236 ret = false;
237 goto out;
240 /* Check the CLDAP reply flags */
242 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
243 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
244 srv));
245 ret = false;
246 goto out;
249 /* Fill in the ads->config values */
251 SAFE_FREE(ads->config.realm);
252 SAFE_FREE(ads->config.bind_path);
253 SAFE_FREE(ads->config.ldap_server_name);
254 SAFE_FREE(ads->config.server_site_name);
255 SAFE_FREE(ads->config.client_site_name);
256 SAFE_FREE(ads->server.workgroup);
258 ads->config.flags = cldap_reply.server_type;
259 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
260 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
261 strupper_m(ads->config.realm);
262 ads->config.bind_path = ads_build_dn(ads->config.realm);
263 if (*cldap_reply.server_site) {
264 ads->config.server_site_name =
265 SMB_STRDUP(cldap_reply.server_site);
267 if (*cldap_reply.client_site) {
268 ads->config.client_site_name =
269 SMB_STRDUP(cldap_reply.client_site);
271 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
273 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
274 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
275 DEBUG(1,("ads_try_connect: unable to convert %s "
276 "to an address\n",
277 srv));
278 ret = false;
279 goto out;
282 /* Store our site name. */
283 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
284 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
286 ret = true;
288 out:
290 TALLOC_FREE(frame);
291 return ret;
294 /**********************************************************************
295 Try to find an AD dc using our internal name resolution routines
296 Try the realm first and then then workgroup name if netbios is not
297 disabled
298 **********************************************************************/
300 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
302 const char *c_domain;
303 const char *c_realm;
304 int count, i=0;
305 struct ip_service *ip_list;
306 const char *realm;
307 const char *domain;
308 bool got_realm = False;
309 bool use_own_domain = False;
310 char *sitename;
311 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
313 /* if the realm and workgroup are both empty, assume they are ours */
315 /* realm */
316 c_realm = ads->server.realm;
318 if ( !c_realm || !*c_realm ) {
319 /* special case where no realm and no workgroup means our own */
320 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
321 use_own_domain = True;
322 c_realm = lp_realm();
326 if (c_realm && *c_realm)
327 got_realm = True;
329 /* we need to try once with the realm name and fallback to the
330 netbios domain name if we fail (if netbios has not been disabled */
332 if ( !got_realm && !lp_disable_netbios() ) {
333 c_realm = ads->server.workgroup;
334 if (!c_realm || !*c_realm) {
335 if ( use_own_domain )
336 c_realm = lp_workgroup();
340 if ( !c_realm || !*c_realm ) {
341 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
342 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
345 if ( use_own_domain ) {
346 c_domain = lp_workgroup();
347 } else {
348 c_domain = ads->server.workgroup;
351 realm = c_realm;
352 domain = c_domain;
355 * In case of LDAP we use get_dc_name() as that
356 * creates the custom krb5.conf file
358 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
359 fstring srv_name;
360 struct sockaddr_storage ip_out;
362 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
363 (got_realm ? "realm" : "domain"), realm));
365 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
367 * we call ads_try_connect() to fill in the
368 * ads->config details
370 if (ads_try_connect(ads, srv_name, false)) {
371 return NT_STATUS_OK;
375 return NT_STATUS_NO_LOGON_SERVERS;
378 sitename = sitename_fetch(realm);
380 again:
382 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
383 (got_realm ? "realm" : "domain"), realm));
385 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
386 if (!NT_STATUS_IS_OK(status)) {
387 /* fall back to netbios if we can */
388 if ( got_realm && !lp_disable_netbios() ) {
389 got_realm = False;
390 goto again;
393 SAFE_FREE(sitename);
394 return status;
397 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
398 for ( i=0; i<count; i++ ) {
399 char server[INET6_ADDRSTRLEN];
401 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
403 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
404 continue;
406 if (!got_realm) {
407 /* realm in this case is a workgroup name. We need
408 to ignore any IP addresses in the negative connection
409 cache that match ip addresses returned in the ad realm
410 case. It sucks that I have to reproduce the logic above... */
411 c_realm = ads->server.realm;
412 if ( !c_realm || !*c_realm ) {
413 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
414 c_realm = lp_realm();
417 if (c_realm && *c_realm &&
418 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
419 /* Ensure we add the workgroup name for this
420 IP address as negative too. */
421 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
422 continue;
426 if ( ads_try_connect(ads, server, false) ) {
427 SAFE_FREE(ip_list);
428 SAFE_FREE(sitename);
429 return NT_STATUS_OK;
432 /* keep track of failures */
433 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
436 SAFE_FREE(ip_list);
438 /* In case we failed to contact one of our closest DC on our site we
439 * need to try to find another DC, retry with a site-less SRV DNS query
440 * - Guenther */
442 if (sitename) {
443 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
444 "trying to find another DC\n", sitename));
445 SAFE_FREE(sitename);
446 namecache_delete(realm, 0x1C);
447 goto again;
450 return NT_STATUS_NO_LOGON_SERVERS;
453 /*********************************************************************
454 *********************************************************************/
456 static NTSTATUS ads_lookup_site(void)
458 ADS_STRUCT *ads = NULL;
459 ADS_STATUS ads_status;
460 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
462 ads = ads_init(lp_realm(), NULL, NULL);
463 if (!ads) {
464 return NT_STATUS_NO_MEMORY;
467 /* The NO_BIND here will find a DC and set the client site
468 but not establish the TCP connection */
470 ads->auth.flags = ADS_AUTH_NO_BIND;
471 ads_status = ads_connect(ads);
472 if (!ADS_ERR_OK(ads_status)) {
473 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
474 ads_errstr(ads_status)));
476 nt_status = ads_ntstatus(ads_status);
478 if (ads) {
479 ads_destroy(&ads);
482 return nt_status;
485 /*********************************************************************
486 *********************************************************************/
488 static const char* host_dns_domain(const char *fqdn)
490 const char *p = fqdn;
492 /* go to next char following '.' */
494 if ((p = strchr_m(fqdn, '.')) != NULL) {
495 p++;
498 return p;
503 * Connect to the Global Catalog server
504 * @param ads Pointer to an existing ADS_STRUCT
505 * @return status of connection
507 * Simple wrapper around ads_connect() that fills in the
508 * GC ldap server information
511 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
513 TALLOC_CTX *frame = talloc_stackframe();
514 struct dns_rr_srv *gcs_list;
515 int num_gcs;
516 char *realm = ads->server.realm;
517 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
518 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
519 int i;
520 bool done = false;
521 char *sitename = NULL;
523 if (!realm)
524 realm = lp_realm();
526 if ((sitename = sitename_fetch(realm)) == NULL) {
527 ads_lookup_site();
528 sitename = sitename_fetch(realm);
531 do {
532 /* We try once with a sitename and once without
533 (unless we don't have a sitename and then we're
534 done */
536 if (sitename == NULL)
537 done = true;
539 nt_status = ads_dns_query_gcs(frame, realm, sitename,
540 &gcs_list, &num_gcs);
542 SAFE_FREE(sitename);
544 if (!NT_STATUS_IS_OK(nt_status)) {
545 ads_status = ADS_ERROR_NT(nt_status);
546 goto done;
549 /* Loop until we get a successful connection or have gone
550 through them all. When connecting a GC server, make sure that
551 the realm is the server's DNS name and not the forest root */
553 for (i=0; i<num_gcs; i++) {
554 ads->server.gc = true;
555 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
556 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
557 ads_status = ads_connect(ads);
558 if (ADS_ERR_OK(ads_status)) {
559 /* Reset the bind_dn to "". A Global Catalog server
560 may host multiple domain trees in a forest.
561 Windows 2003 GC server will accept "" as the search
562 path to imply search all domain trees in the forest */
564 SAFE_FREE(ads->config.bind_path);
565 ads->config.bind_path = SMB_STRDUP("");
568 goto done;
570 SAFE_FREE(ads->server.ldap_server);
571 SAFE_FREE(ads->server.realm);
574 TALLOC_FREE(gcs_list);
575 num_gcs = 0;
576 } while (!done);
578 done:
579 SAFE_FREE(sitename);
580 talloc_destroy(frame);
582 return ads_status;
587 * Connect to the LDAP server
588 * @param ads Pointer to an existing ADS_STRUCT
589 * @return status of connection
591 ADS_STATUS ads_connect(ADS_STRUCT *ads)
593 int version = LDAP_VERSION3;
594 ADS_STATUS status;
595 NTSTATUS ntstatus;
596 char addr[INET6_ADDRSTRLEN];
598 ZERO_STRUCT(ads->ldap);
599 ads->ldap.last_attempt = time_mono(NULL);
600 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
602 /* try with a user specified server */
604 if (DEBUGLEVEL >= 11) {
605 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
606 DEBUG(11,("ads_connect: entering\n"));
607 DEBUGADD(11,("%s\n", s));
608 TALLOC_FREE(s);
611 if (ads->server.ldap_server)
613 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
614 goto got_connection;
617 /* The choice of which GC use is handled one level up in
618 ads_connect_gc(). If we continue on from here with
619 ads_find_dc() we will get GC searches on port 389 which
620 doesn't work. --jerry */
622 if (ads->server.gc == true) {
623 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
627 ntstatus = ads_find_dc(ads);
628 if (NT_STATUS_IS_OK(ntstatus)) {
629 goto got_connection;
632 status = ADS_ERROR_NT(ntstatus);
633 goto out;
635 got_connection:
637 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
638 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
640 if (!ads->auth.user_name) {
641 /* Must use the userPrincipalName value here or sAMAccountName
642 and not servicePrincipalName; found by Guenther Deschner */
644 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
645 DEBUG(0,("ads_connect: asprintf fail.\n"));
646 ads->auth.user_name = NULL;
650 if (!ads->auth.realm) {
651 ads->auth.realm = SMB_STRDUP(ads->config.realm);
654 if (!ads->auth.kdc_server) {
655 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
656 ads->auth.kdc_server = SMB_STRDUP(addr);
659 #if KRB5_DNS_HACK
660 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
661 to MIT kerberos to work (tridge) */
663 char *env = NULL;
664 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
665 setenv(env, ads->auth.kdc_server, 1);
666 free(env);
669 #endif
671 /* If the caller() requested no LDAP bind, then we are done */
673 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
674 status = ADS_SUCCESS;
675 goto out;
678 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
679 if (!ads->ldap.mem_ctx) {
680 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
681 goto out;
684 /* Otherwise setup the TCP LDAP session */
686 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
687 ads->ldap.port, lp_ldap_timeout());
688 if (ads->ldap.ld == NULL) {
689 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
690 goto out;
692 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
694 /* cache the successful connection for workgroup and realm */
695 if (ads_closest_dc(ads)) {
696 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
697 saf_store( ads->server.realm, ads->config.ldap_server_name);
700 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
702 if ( lp_ldap_ssl_ads() ) {
703 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
704 if (!ADS_ERR_OK(status)) {
705 goto out;
709 /* fill in the current time and offsets */
711 status = ads_current_time( ads );
712 if ( !ADS_ERR_OK(status) ) {
713 goto out;
716 /* Now do the bind */
718 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
719 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
720 goto out;
723 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
724 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
725 goto out;
728 status = ads_sasl_bind(ads);
730 out:
731 if (DEBUGLEVEL >= 11) {
732 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
733 DEBUG(11,("ads_connect: leaving with: %s\n",
734 ads_errstr(status)));
735 DEBUGADD(11,("%s\n", s));
736 TALLOC_FREE(s);
739 return status;
743 * Connect to the LDAP server using given credentials
744 * @param ads Pointer to an existing ADS_STRUCT
745 * @return status of connection
747 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
749 ads->auth.flags |= ADS_AUTH_USER_CREDS;
751 return ads_connect(ads);
755 * Disconnect the LDAP server
756 * @param ads Pointer to an existing ADS_STRUCT
758 void ads_disconnect(ADS_STRUCT *ads)
760 if (ads->ldap.ld) {
761 ldap_unbind(ads->ldap.ld);
762 ads->ldap.ld = NULL;
764 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
765 ads->ldap.wrap_ops->disconnect(ads);
767 if (ads->ldap.mem_ctx) {
768 talloc_free(ads->ldap.mem_ctx);
770 ZERO_STRUCT(ads->ldap);
774 Duplicate a struct berval into talloc'ed memory
776 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
778 struct berval *value;
780 if (!in_val) return NULL;
782 value = TALLOC_ZERO_P(ctx, struct berval);
783 if (value == NULL)
784 return NULL;
785 if (in_val->bv_len == 0) return value;
787 value->bv_len = in_val->bv_len;
788 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
789 in_val->bv_len);
790 return value;
794 Make a values list out of an array of (struct berval *)
796 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
797 const struct berval **in_vals)
799 struct berval **values;
800 int i;
802 if (!in_vals) return NULL;
803 for (i=0; in_vals[i]; i++)
804 ; /* count values */
805 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
806 if (!values) return NULL;
808 for (i=0; in_vals[i]; i++) {
809 values[i] = dup_berval(ctx, in_vals[i]);
811 return values;
815 UTF8-encode a values list out of an array of (char *)
817 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
819 char **values;
820 int i;
821 size_t size;
823 if (!in_vals) return NULL;
824 for (i=0; in_vals[i]; i++)
825 ; /* count values */
826 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
827 if (!values) return NULL;
829 for (i=0; in_vals[i]; i++) {
830 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
831 TALLOC_FREE(values);
832 return NULL;
835 return values;
839 Pull a (char *) array out of a UTF8-encoded values list
841 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
843 char **values;
844 int i;
845 size_t converted_size;
847 if (!in_vals) return NULL;
848 for (i=0; in_vals[i]; i++)
849 ; /* count values */
850 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
851 if (!values) return NULL;
853 for (i=0; in_vals[i]; i++) {
854 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
855 &converted_size)) {
856 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
857 "%s", strerror(errno)));
860 return values;
864 * Do a search with paged results. cookie must be null on the first
865 * call, and then returned on each subsequent call. It will be null
866 * again when the entire search is complete
867 * @param ads connection to ads server
868 * @param bind_path Base dn for the search
869 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
870 * @param expr Search expression - specified in local charset
871 * @param attrs Attributes to retrieve - specified in utf8 or ascii
872 * @param res ** which will contain results - free res* with ads_msgfree()
873 * @param count Number of entries retrieved on this page
874 * @param cookie The paged results cookie to be returned on subsequent calls
875 * @return status of search
877 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
878 const char *bind_path,
879 int scope, const char *expr,
880 const char **attrs, void *args,
881 LDAPMessage **res,
882 int *count, struct berval **cookie)
884 int rc, i, version;
885 char *utf8_expr, *utf8_path, **search_attrs = NULL;
886 size_t converted_size;
887 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
888 BerElement *cookie_be = NULL;
889 struct berval *cookie_bv= NULL;
890 BerElement *ext_be = NULL;
891 struct berval *ext_bv= NULL;
893 TALLOC_CTX *ctx;
894 ads_control *external_control = (ads_control *) args;
896 *res = NULL;
898 if (!(ctx = talloc_init("ads_do_paged_search_args")))
899 return ADS_ERROR(LDAP_NO_MEMORY);
901 /* 0 means the conversion worked but the result was empty
902 so we only fail if it's -1. In any case, it always
903 at least nulls out the dest */
904 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
905 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
907 rc = LDAP_NO_MEMORY;
908 goto done;
911 if (!attrs || !(*attrs))
912 search_attrs = NULL;
913 else {
914 /* This would be the utf8-encoded version...*/
915 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
916 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
917 rc = LDAP_NO_MEMORY;
918 goto done;
922 /* Paged results only available on ldap v3 or later */
923 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
924 if (version < LDAP_VERSION3) {
925 rc = LDAP_NOT_SUPPORTED;
926 goto done;
929 cookie_be = ber_alloc_t(LBER_USE_DER);
930 if (*cookie) {
931 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
932 ber_bvfree(*cookie); /* don't need it from last time */
933 *cookie = NULL;
934 } else {
935 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
937 ber_flatten(cookie_be, &cookie_bv);
938 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
939 PagedResults.ldctl_iscritical = (char) 1;
940 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
941 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
943 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
944 NoReferrals.ldctl_iscritical = (char) 0;
945 NoReferrals.ldctl_value.bv_len = 0;
946 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
948 if (external_control &&
949 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
950 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
952 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
953 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
955 /* win2k does not accept a ldctl_value beeing passed in */
957 if (external_control->val != 0) {
959 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
960 rc = LDAP_NO_MEMORY;
961 goto done;
964 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
965 rc = LDAP_NO_MEMORY;
966 goto done;
968 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
969 rc = LDAP_NO_MEMORY;
970 goto done;
973 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
974 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
976 } else {
977 ExternalCtrl.ldctl_value.bv_len = 0;
978 ExternalCtrl.ldctl_value.bv_val = NULL;
981 controls[0] = &NoReferrals;
982 controls[1] = &PagedResults;
983 controls[2] = &ExternalCtrl;
984 controls[3] = NULL;
986 } else {
987 controls[0] = &NoReferrals;
988 controls[1] = &PagedResults;
989 controls[2] = NULL;
992 /* we need to disable referrals as the openldap libs don't
993 handle them and paged results at the same time. Using them
994 together results in the result record containing the server
995 page control being removed from the result list (tridge/jmcd)
997 leaving this in despite the control that says don't generate
998 referrals, in case the server doesn't support it (jmcd)
1000 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1002 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1003 search_attrs, 0, controls,
1004 NULL, LDAP_NO_LIMIT,
1005 (LDAPMessage **)res);
1007 ber_free(cookie_be, 1);
1008 ber_bvfree(cookie_bv);
1010 if (rc) {
1011 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1012 ldap_err2string(rc)));
1013 goto done;
1016 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1017 NULL, &rcontrols, 0);
1019 if (!rcontrols) {
1020 goto done;
1023 for (i=0; rcontrols[i]; i++) {
1024 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1025 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1026 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1027 &cookie_bv);
1028 /* the berval is the cookie, but must be freed when
1029 it is all done */
1030 if (cookie_bv->bv_len) /* still more to do */
1031 *cookie=ber_bvdup(cookie_bv);
1032 else
1033 *cookie=NULL;
1034 ber_bvfree(cookie_bv);
1035 ber_free(cookie_be, 1);
1036 break;
1039 ldap_controls_free(rcontrols);
1041 done:
1042 talloc_destroy(ctx);
1044 if (ext_be) {
1045 ber_free(ext_be, 1);
1048 if (ext_bv) {
1049 ber_bvfree(ext_bv);
1052 /* if/when we decide to utf8-encode attrs, take out this next line */
1053 TALLOC_FREE(search_attrs);
1055 return ADS_ERROR(rc);
1058 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1059 int scope, const char *expr,
1060 const char **attrs, LDAPMessage **res,
1061 int *count, struct berval **cookie)
1063 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1068 * Get all results for a search. This uses ads_do_paged_search() to return
1069 * all entries in a large search.
1070 * @param ads connection to ads server
1071 * @param bind_path Base dn for the search
1072 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1073 * @param expr Search expression
1074 * @param attrs Attributes to retrieve
1075 * @param res ** which will contain results - free res* with ads_msgfree()
1076 * @return status of search
1078 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1079 int scope, const char *expr,
1080 const char **attrs, void *args,
1081 LDAPMessage **res)
1083 struct berval *cookie = NULL;
1084 int count = 0;
1085 ADS_STATUS status;
1087 *res = NULL;
1088 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1089 &count, &cookie);
1091 if (!ADS_ERR_OK(status))
1092 return status;
1094 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1095 while (cookie) {
1096 LDAPMessage *res2 = NULL;
1097 ADS_STATUS status2;
1098 LDAPMessage *msg, *next;
1100 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1101 attrs, args, &res2, &count, &cookie);
1103 if (!ADS_ERR_OK(status2)) break;
1105 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1106 that this works on all ldap libs, but I have only tested with openldap */
1107 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1108 next = ads_next_message(ads, msg);
1109 ldap_add_result_entry((LDAPMessage **)res, msg);
1111 /* note that we do not free res2, as the memory is now
1112 part of the main returned list */
1114 #else
1115 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1116 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1117 #endif
1119 return status;
1122 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1123 int scope, const char *expr,
1124 const char **attrs, LDAPMessage **res)
1126 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1129 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1130 int scope, const char *expr,
1131 const char **attrs, uint32 sd_flags,
1132 LDAPMessage **res)
1134 ads_control args;
1136 args.control = ADS_SD_FLAGS_OID;
1137 args.val = sd_flags;
1138 args.critical = True;
1140 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1145 * Run a function on all results for a search. Uses ads_do_paged_search() and
1146 * runs the function as each page is returned, using ads_process_results()
1147 * @param ads connection to ads server
1148 * @param bind_path Base dn for the search
1149 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1150 * @param expr Search expression - specified in local charset
1151 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1152 * @param fn Function which takes attr name, values list, and data_area
1153 * @param data_area Pointer which is passed to function on each call
1154 * @return status of search
1156 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1157 int scope, const char *expr, const char **attrs,
1158 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1159 void *data_area)
1161 struct berval *cookie = NULL;
1162 int count = 0;
1163 ADS_STATUS status;
1164 LDAPMessage *res;
1166 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1167 &count, &cookie);
1169 if (!ADS_ERR_OK(status)) return status;
1171 ads_process_results(ads, res, fn, data_area);
1172 ads_msgfree(ads, res);
1174 while (cookie) {
1175 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1176 &res, &count, &cookie);
1178 if (!ADS_ERR_OK(status)) break;
1180 ads_process_results(ads, res, fn, data_area);
1181 ads_msgfree(ads, res);
1184 return status;
1188 * Do a search with a timeout.
1189 * @param ads connection to ads server
1190 * @param bind_path Base dn for the search
1191 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1192 * @param expr Search expression
1193 * @param attrs Attributes to retrieve
1194 * @param res ** which will contain results - free res* with ads_msgfree()
1195 * @return status of search
1197 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1198 const char *expr,
1199 const char **attrs, LDAPMessage **res)
1201 int rc;
1202 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1203 size_t converted_size;
1204 TALLOC_CTX *ctx;
1206 *res = NULL;
1207 if (!(ctx = talloc_init("ads_do_search"))) {
1208 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1209 return ADS_ERROR(LDAP_NO_MEMORY);
1212 /* 0 means the conversion worked but the result was empty
1213 so we only fail if it's negative. In any case, it always
1214 at least nulls out the dest */
1215 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1216 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1218 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1219 rc = LDAP_NO_MEMORY;
1220 goto done;
1223 if (!attrs || !(*attrs))
1224 search_attrs = NULL;
1225 else {
1226 /* This would be the utf8-encoded version...*/
1227 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1228 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1230 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1231 rc = LDAP_NO_MEMORY;
1232 goto done;
1236 /* see the note in ads_do_paged_search - we *must* disable referrals */
1237 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1239 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1240 search_attrs, 0, NULL, NULL,
1241 LDAP_NO_LIMIT,
1242 (LDAPMessage **)res);
1244 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1245 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1246 rc = 0;
1249 done:
1250 talloc_destroy(ctx);
1251 /* if/when we decide to utf8-encode attrs, take out this next line */
1252 TALLOC_FREE(search_attrs);
1253 return ADS_ERROR(rc);
1256 * Do a general ADS search
1257 * @param ads connection to ads server
1258 * @param res ** which will contain results - free res* with ads_msgfree()
1259 * @param expr Search expression
1260 * @param attrs Attributes to retrieve
1261 * @return status of search
1263 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1264 const char *expr, const char **attrs)
1266 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1267 expr, attrs, res);
1271 * Do a search on a specific DistinguishedName
1272 * @param ads connection to ads server
1273 * @param res ** which will contain results - free res* with ads_msgfree()
1274 * @param dn DistinguishName to search
1275 * @param attrs Attributes to retrieve
1276 * @return status of search
1278 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1279 const char *dn, const char **attrs)
1281 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1282 attrs, res);
1286 * Free up memory from a ads_search
1287 * @param ads connection to ads server
1288 * @param msg Search results to free
1290 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1292 if (!msg) return;
1293 ldap_msgfree(msg);
1297 * Get a dn from search results
1298 * @param ads connection to ads server
1299 * @param msg Search result
1300 * @return dn string
1302 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1304 char *utf8_dn, *unix_dn;
1305 size_t converted_size;
1307 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1309 if (!utf8_dn) {
1310 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1311 return NULL;
1314 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1315 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1316 utf8_dn ));
1317 return NULL;
1319 ldap_memfree(utf8_dn);
1320 return unix_dn;
1324 * Get the parent from a dn
1325 * @param dn the dn to return the parent from
1326 * @return parent dn string
1328 char *ads_parent_dn(const char *dn)
1330 char *p;
1332 if (dn == NULL) {
1333 return NULL;
1336 p = strchr(dn, ',');
1338 if (p == NULL) {
1339 return NULL;
1342 return p+1;
1346 * Find a machine account given a hostname
1347 * @param ads connection to ads server
1348 * @param res ** which will contain results - free res* with ads_msgfree()
1349 * @param host Hostname to search for
1350 * @return status of search
1352 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1353 const char *machine)
1355 ADS_STATUS status;
1356 char *expr;
1357 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1359 *res = NULL;
1361 /* the easiest way to find a machine account anywhere in the tree
1362 is to look for hostname$ */
1363 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1364 DEBUG(1, ("asprintf failed!\n"));
1365 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1368 status = ads_search(ads, res, expr, attrs);
1369 SAFE_FREE(expr);
1370 return status;
1374 * Initialize a list of mods to be used in a modify request
1375 * @param ctx An initialized TALLOC_CTX
1376 * @return allocated ADS_MODLIST
1378 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1380 #define ADS_MODLIST_ALLOC_SIZE 10
1381 LDAPMod **mods;
1383 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1384 /* -1 is safety to make sure we don't go over the end.
1385 need to reset it to NULL before doing ldap modify */
1386 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1388 return (ADS_MODLIST)mods;
1393 add an attribute to the list, with values list already constructed
1395 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1396 int mod_op, const char *name,
1397 const void *_invals)
1399 const void **invals = (const void **)_invals;
1400 int curmod;
1401 LDAPMod **modlist = (LDAPMod **) *mods;
1402 struct berval **ber_values = NULL;
1403 char **char_values = NULL;
1405 if (!invals) {
1406 mod_op = LDAP_MOD_DELETE;
1407 } else {
1408 if (mod_op & LDAP_MOD_BVALUES)
1409 ber_values = ads_dup_values(ctx,
1410 (const struct berval **)invals);
1411 else
1412 char_values = ads_push_strvals(ctx,
1413 (const char **) invals);
1416 /* find the first empty slot */
1417 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1418 curmod++);
1419 if (modlist[curmod] == (LDAPMod *) -1) {
1420 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1421 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1422 return ADS_ERROR(LDAP_NO_MEMORY);
1423 memset(&modlist[curmod], 0,
1424 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1425 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1426 *mods = (ADS_MODLIST)modlist;
1429 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1430 return ADS_ERROR(LDAP_NO_MEMORY);
1431 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1432 if (mod_op & LDAP_MOD_BVALUES) {
1433 modlist[curmod]->mod_bvalues = ber_values;
1434 } else if (mod_op & LDAP_MOD_DELETE) {
1435 modlist[curmod]->mod_values = NULL;
1436 } else {
1437 modlist[curmod]->mod_values = char_values;
1440 modlist[curmod]->mod_op = mod_op;
1441 return ADS_ERROR(LDAP_SUCCESS);
1445 * Add a single string value to a mod list
1446 * @param ctx An initialized TALLOC_CTX
1447 * @param mods An initialized ADS_MODLIST
1448 * @param name The attribute name to add
1449 * @param val The value to add - NULL means DELETE
1450 * @return ADS STATUS indicating success of add
1452 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1453 const char *name, const char *val)
1455 const char *values[2];
1457 values[0] = val;
1458 values[1] = NULL;
1460 if (!val)
1461 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1462 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1466 * Add an array of string values to a mod list
1467 * @param ctx An initialized TALLOC_CTX
1468 * @param mods An initialized ADS_MODLIST
1469 * @param name The attribute name to add
1470 * @param vals The array of string values to add - NULL means DELETE
1471 * @return ADS STATUS indicating success of add
1473 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1474 const char *name, const char **vals)
1476 if (!vals)
1477 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1478 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1479 name, (const void **) vals);
1482 #if 0
1484 * Add a single ber-encoded value to a mod list
1485 * @param ctx An initialized TALLOC_CTX
1486 * @param mods An initialized ADS_MODLIST
1487 * @param name The attribute name to add
1488 * @param val The value to add - NULL means DELETE
1489 * @return ADS STATUS indicating success of add
1491 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1492 const char *name, const struct berval *val)
1494 const struct berval *values[2];
1496 values[0] = val;
1497 values[1] = NULL;
1498 if (!val)
1499 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1500 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1501 name, (const void **) values);
1503 #endif
1506 * Perform an ldap modify
1507 * @param ads connection to ads server
1508 * @param mod_dn DistinguishedName to modify
1509 * @param mods list of modifications to perform
1510 * @return status of modify
1512 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1514 int ret,i;
1515 char *utf8_dn = NULL;
1516 size_t converted_size;
1518 this control is needed to modify that contains a currently
1519 non-existent attribute (but allowable for the object) to run
1521 LDAPControl PermitModify = {
1522 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1523 {0, NULL},
1524 (char) 1};
1525 LDAPControl *controls[2];
1527 controls[0] = &PermitModify;
1528 controls[1] = NULL;
1530 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1531 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1534 /* find the end of the list, marked by NULL or -1 */
1535 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1536 /* make sure the end of the list is NULL */
1537 mods[i] = NULL;
1538 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1539 (LDAPMod **) mods, controls, NULL);
1540 TALLOC_FREE(utf8_dn);
1541 return ADS_ERROR(ret);
1545 * Perform an ldap add
1546 * @param ads connection to ads server
1547 * @param new_dn DistinguishedName to add
1548 * @param mods list of attributes and values for DN
1549 * @return status of add
1551 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1553 int ret, i;
1554 char *utf8_dn = NULL;
1555 size_t converted_size;
1557 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1558 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1559 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1562 /* find the end of the list, marked by NULL or -1 */
1563 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1564 /* make sure the end of the list is NULL */
1565 mods[i] = NULL;
1567 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1568 TALLOC_FREE(utf8_dn);
1569 return ADS_ERROR(ret);
1573 * Delete a DistinguishedName
1574 * @param ads connection to ads server
1575 * @param new_dn DistinguishedName to delete
1576 * @return status of delete
1578 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1580 int ret;
1581 char *utf8_dn = NULL;
1582 size_t converted_size;
1583 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1584 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1585 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1588 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1589 TALLOC_FREE(utf8_dn);
1590 return ADS_ERROR(ret);
1594 * Build an org unit string
1595 * if org unit is Computers or blank then assume a container, otherwise
1596 * assume a / separated list of organisational units.
1597 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1598 * @param ads connection to ads server
1599 * @param org_unit Organizational unit
1600 * @return org unit string - caller must free
1602 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1604 char *ret = NULL;
1606 if (!org_unit || !*org_unit) {
1608 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1610 /* samba4 might not yet respond to a wellknownobject-query */
1611 return ret ? ret : SMB_STRDUP("cn=Computers");
1614 if (strequal(org_unit, "Computers")) {
1615 return SMB_STRDUP("cn=Computers");
1618 /* jmcd: removed "\\" from the separation chars, because it is
1619 needed as an escape for chars like '#' which are valid in an
1620 OU name */
1621 return ads_build_path(org_unit, "/", "ou=", 1);
1625 * Get a org unit string for a well-known GUID
1626 * @param ads connection to ads server
1627 * @param wknguid Well known GUID
1628 * @return org unit string - caller must free
1630 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1632 ADS_STATUS status;
1633 LDAPMessage *res = NULL;
1634 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1635 **bind_dn_exp = NULL;
1636 const char *attrs[] = {"distinguishedName", NULL};
1637 int new_ln, wkn_ln, bind_ln, i;
1639 if (wknguid == NULL) {
1640 return NULL;
1643 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1644 DEBUG(1, ("asprintf failed!\n"));
1645 return NULL;
1648 status = ads_search_dn(ads, &res, base, attrs);
1649 if (!ADS_ERR_OK(status)) {
1650 DEBUG(1,("Failed while searching for: %s\n", base));
1651 goto out;
1654 if (ads_count_replies(ads, res) != 1) {
1655 goto out;
1658 /* substitute the bind-path from the well-known-guid-search result */
1659 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1660 if (!wkn_dn) {
1661 goto out;
1664 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1665 if (!wkn_dn_exp) {
1666 goto out;
1669 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1670 if (!bind_dn_exp) {
1671 goto out;
1674 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1676 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1679 new_ln = wkn_ln - bind_ln;
1681 ret = SMB_STRDUP(wkn_dn_exp[0]);
1682 if (!ret) {
1683 goto out;
1686 for (i=1; i < new_ln; i++) {
1687 char *s = NULL;
1689 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1690 SAFE_FREE(ret);
1691 goto out;
1694 SAFE_FREE(ret);
1695 ret = SMB_STRDUP(s);
1696 free(s);
1697 if (!ret) {
1698 goto out;
1702 out:
1703 SAFE_FREE(base);
1704 ads_msgfree(ads, res);
1705 TALLOC_FREE(wkn_dn);
1706 if (wkn_dn_exp) {
1707 ldap_value_free(wkn_dn_exp);
1709 if (bind_dn_exp) {
1710 ldap_value_free(bind_dn_exp);
1713 return ret;
1717 * Adds (appends) an item to an attribute array, rather then
1718 * replacing the whole list
1719 * @param ctx An initialized TALLOC_CTX
1720 * @param mods An initialized ADS_MODLIST
1721 * @param name name of the ldap attribute to append to
1722 * @param vals an array of values to add
1723 * @return status of addition
1726 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1727 const char *name, const char **vals)
1729 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1730 (const void *) vals);
1734 * Determines the an account's current KVNO via an LDAP lookup
1735 * @param ads An initialized ADS_STRUCT
1736 * @param account_name the NT samaccountname.
1737 * @return the kvno for the account, or -1 in case of a failure.
1740 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1742 LDAPMessage *res = NULL;
1743 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1744 char *filter;
1745 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1746 char *dn_string = NULL;
1747 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1749 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1750 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1751 return kvno;
1753 ret = ads_search(ads, &res, filter, attrs);
1754 SAFE_FREE(filter);
1755 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1756 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1757 ads_msgfree(ads, res);
1758 return kvno;
1761 dn_string = ads_get_dn(ads, talloc_tos(), res);
1762 if (!dn_string) {
1763 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1764 ads_msgfree(ads, res);
1765 return kvno;
1767 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1768 TALLOC_FREE(dn_string);
1770 /* ---------------------------------------------------------
1771 * 0 is returned as a default KVNO from this point on...
1772 * This is done because Windows 2000 does not support key
1773 * version numbers. Chances are that a failure in the next
1774 * step is simply due to Windows 2000 being used for a
1775 * domain controller. */
1776 kvno = 0;
1778 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1779 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1780 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1781 ads_msgfree(ads, res);
1782 return kvno;
1785 /* Success */
1786 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1787 ads_msgfree(ads, res);
1788 return kvno;
1792 * Determines the computer account's current KVNO via an LDAP lookup
1793 * @param ads An initialized ADS_STRUCT
1794 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1795 * @return the kvno for the computer account, or -1 in case of a failure.
1798 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1800 char *computer_account = NULL;
1801 uint32_t kvno = -1;
1803 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1804 return kvno;
1807 kvno = ads_get_kvno(ads, computer_account);
1808 free(computer_account);
1810 return kvno;
1814 * This clears out all registered spn's for a given hostname
1815 * @param ads An initilaized ADS_STRUCT
1816 * @param machine_name the NetBIOS name of the computer.
1817 * @return 0 upon success, non-zero otherwise.
1820 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1822 TALLOC_CTX *ctx;
1823 LDAPMessage *res = NULL;
1824 ADS_MODLIST mods;
1825 const char *servicePrincipalName[1] = {NULL};
1826 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1827 char *dn_string = NULL;
1829 ret = ads_find_machine_acct(ads, &res, machine_name);
1830 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1831 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1832 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1833 ads_msgfree(ads, res);
1834 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1837 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1838 ctx = talloc_init("ads_clear_service_principal_names");
1839 if (!ctx) {
1840 ads_msgfree(ads, res);
1841 return ADS_ERROR(LDAP_NO_MEMORY);
1844 if (!(mods = ads_init_mods(ctx))) {
1845 talloc_destroy(ctx);
1846 ads_msgfree(ads, res);
1847 return ADS_ERROR(LDAP_NO_MEMORY);
1849 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1850 if (!ADS_ERR_OK(ret)) {
1851 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1852 ads_msgfree(ads, res);
1853 talloc_destroy(ctx);
1854 return ret;
1856 dn_string = ads_get_dn(ads, talloc_tos(), res);
1857 if (!dn_string) {
1858 talloc_destroy(ctx);
1859 ads_msgfree(ads, res);
1860 return ADS_ERROR(LDAP_NO_MEMORY);
1862 ret = ads_gen_mod(ads, dn_string, mods);
1863 TALLOC_FREE(dn_string);
1864 if (!ADS_ERR_OK(ret)) {
1865 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1866 machine_name));
1867 ads_msgfree(ads, res);
1868 talloc_destroy(ctx);
1869 return ret;
1872 ads_msgfree(ads, res);
1873 talloc_destroy(ctx);
1874 return ret;
1878 * This adds a service principal name to an existing computer account
1879 * (found by hostname) in AD.
1880 * @param ads An initialized ADS_STRUCT
1881 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1882 * @param my_fqdn The fully qualified DNS name of the machine
1883 * @param spn A string of the service principal to add, i.e. 'host'
1884 * @return 0 upon sucess, or non-zero if a failure occurs
1887 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1888 const char *my_fqdn, const char *spn)
1890 ADS_STATUS ret;
1891 TALLOC_CTX *ctx;
1892 LDAPMessage *res = NULL;
1893 char *psp1, *psp2;
1894 ADS_MODLIST mods;
1895 char *dn_string = NULL;
1896 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1898 ret = ads_find_machine_acct(ads, &res, machine_name);
1899 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1900 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1901 machine_name));
1902 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1903 spn, machine_name, ads->config.realm));
1904 ads_msgfree(ads, res);
1905 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1908 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1909 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1910 ads_msgfree(ads, res);
1911 return ADS_ERROR(LDAP_NO_MEMORY);
1914 /* add short name spn */
1916 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1917 talloc_destroy(ctx);
1918 ads_msgfree(ads, res);
1919 return ADS_ERROR(LDAP_NO_MEMORY);
1921 strupper_m(psp1);
1922 strlower_m(&psp1[strlen(spn)]);
1923 servicePrincipalName[0] = psp1;
1925 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1926 psp1, machine_name));
1929 /* add fully qualified spn */
1931 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1932 ret = ADS_ERROR(LDAP_NO_MEMORY);
1933 goto out;
1935 strupper_m(psp2);
1936 strlower_m(&psp2[strlen(spn)]);
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++) {
2130 UUID_FLAT guid;
2131 struct GUID tmp;
2133 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2134 smb_uuid_unpack(guid, &tmp);
2135 printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2140 dump a sid result from ldap
2142 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2144 int i;
2145 for (i=0; values[i]; i++) {
2146 struct dom_sid sid;
2147 fstring tmp;
2148 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2149 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2154 dump ntSecurityDescriptor
2156 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2158 TALLOC_CTX *frame = talloc_stackframe();
2159 struct security_descriptor *psd;
2160 NTSTATUS status;
2162 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2163 values[0]->bv_len, &psd);
2164 if (!NT_STATUS_IS_OK(status)) {
2165 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2166 nt_errstr(status)));
2167 TALLOC_FREE(frame);
2168 return;
2171 if (psd) {
2172 ads_disp_sd(ads, talloc_tos(), psd);
2175 TALLOC_FREE(frame);
2179 dump a string result from ldap
2181 static void dump_string(const char *field, char **values)
2183 int i;
2184 for (i=0; values[i]; i++) {
2185 printf("%s: %s\n", field, values[i]);
2190 dump a field from LDAP on stdout
2191 used for debugging
2194 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2196 const struct {
2197 const char *name;
2198 bool string;
2199 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2200 } handlers[] = {
2201 {"objectGUID", False, dump_guid},
2202 {"netbootGUID", False, dump_guid},
2203 {"nTSecurityDescriptor", False, dump_sd},
2204 {"dnsRecord", False, dump_binary},
2205 {"objectSid", False, dump_sid},
2206 {"tokenGroups", False, dump_sid},
2207 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2208 {"tokengroupsGlobalandUniversal", False, dump_sid},
2209 {"mS-DS-CreatorSID", False, dump_sid},
2210 {"msExchMailboxGuid", False, dump_guid},
2211 {NULL, True, NULL}
2213 int i;
2215 if (!field) { /* must be end of an entry */
2216 printf("\n");
2217 return False;
2220 for (i=0; handlers[i].name; i++) {
2221 if (StrCaseCmp(handlers[i].name, field) == 0) {
2222 if (!values) /* first time, indicate string or not */
2223 return handlers[i].string;
2224 handlers[i].handler(ads, field, (struct berval **) values);
2225 break;
2228 if (!handlers[i].name) {
2229 if (!values) /* first time, indicate string conversion */
2230 return True;
2231 dump_string(field, (char **)values);
2233 return False;
2237 * Dump a result from LDAP on stdout
2238 * used for debugging
2239 * @param ads connection to ads server
2240 * @param res Results to dump
2243 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2245 ads_process_results(ads, res, ads_dump_field, NULL);
2249 * Walk through results, calling a function for each entry found.
2250 * The function receives a field name, a berval * array of values,
2251 * and a data area passed through from the start. The function is
2252 * called once with null for field and values at the end of each
2253 * entry.
2254 * @param ads connection to ads server
2255 * @param res Results to process
2256 * @param fn Function for processing each result
2257 * @param data_area user-defined area to pass to function
2259 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2260 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2261 void *data_area)
2263 LDAPMessage *msg;
2264 TALLOC_CTX *ctx;
2265 size_t converted_size;
2267 if (!(ctx = talloc_init("ads_process_results")))
2268 return;
2270 for (msg = ads_first_entry(ads, res); msg;
2271 msg = ads_next_entry(ads, msg)) {
2272 char *utf8_field;
2273 BerElement *b;
2275 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2276 (LDAPMessage *)msg,&b);
2277 utf8_field;
2278 utf8_field=ldap_next_attribute(ads->ldap.ld,
2279 (LDAPMessage *)msg,b)) {
2280 struct berval **ber_vals;
2281 char **str_vals, **utf8_vals;
2282 char *field;
2283 bool string;
2285 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2286 &converted_size))
2288 DEBUG(0,("ads_process_results: "
2289 "pull_utf8_talloc failed: %s",
2290 strerror(errno)));
2293 string = fn(ads, field, NULL, data_area);
2295 if (string) {
2296 utf8_vals = ldap_get_values(ads->ldap.ld,
2297 (LDAPMessage *)msg, field);
2298 str_vals = ads_pull_strvals(ctx,
2299 (const char **) utf8_vals);
2300 fn(ads, field, (void **) str_vals, data_area);
2301 ldap_value_free(utf8_vals);
2302 } else {
2303 ber_vals = ldap_get_values_len(ads->ldap.ld,
2304 (LDAPMessage *)msg, field);
2305 fn(ads, field, (void **) ber_vals, data_area);
2307 ldap_value_free_len(ber_vals);
2309 ldap_memfree(utf8_field);
2311 ber_free(b, 0);
2312 talloc_free_children(ctx);
2313 fn(ads, NULL, NULL, data_area); /* completed an entry */
2316 talloc_destroy(ctx);
2320 * count how many replies are in a LDAPMessage
2321 * @param ads connection to ads server
2322 * @param res Results to count
2323 * @return number of replies
2325 int ads_count_replies(ADS_STRUCT *ads, void *res)
2327 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2331 * pull the first entry from a ADS result
2332 * @param ads connection to ads server
2333 * @param res Results of search
2334 * @return first entry from result
2336 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2338 return ldap_first_entry(ads->ldap.ld, res);
2342 * pull the next entry from a ADS result
2343 * @param ads connection to ads server
2344 * @param res Results of search
2345 * @return next entry from result
2347 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2349 return ldap_next_entry(ads->ldap.ld, res);
2353 * pull the first message from a ADS result
2354 * @param ads connection to ads server
2355 * @param res Results of search
2356 * @return first message from result
2358 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2360 return ldap_first_message(ads->ldap.ld, res);
2364 * pull the next message from a ADS result
2365 * @param ads connection to ads server
2366 * @param res Results of search
2367 * @return next message from result
2369 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2371 return ldap_next_message(ads->ldap.ld, res);
2375 * pull a single string from a ADS result
2376 * @param ads connection to ads server
2377 * @param mem_ctx TALLOC_CTX to use for allocating result string
2378 * @param msg Results of search
2379 * @param field Attribute to retrieve
2380 * @return Result string in talloc context
2382 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2383 const char *field)
2385 char **values;
2386 char *ret = NULL;
2387 char *ux_string;
2388 size_t converted_size;
2390 values = ldap_get_values(ads->ldap.ld, msg, field);
2391 if (!values)
2392 return NULL;
2394 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2395 &converted_size))
2397 ret = ux_string;
2399 ldap_value_free(values);
2400 return ret;
2404 * pull an array of strings from a ADS result
2405 * @param ads connection to ads server
2406 * @param mem_ctx TALLOC_CTX to use for allocating result string
2407 * @param msg Results of search
2408 * @param field Attribute to retrieve
2409 * @return Result strings in talloc context
2411 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2412 LDAPMessage *msg, const char *field,
2413 size_t *num_values)
2415 char **values;
2416 char **ret = NULL;
2417 int i;
2418 size_t converted_size;
2420 values = ldap_get_values(ads->ldap.ld, msg, field);
2421 if (!values)
2422 return NULL;
2424 *num_values = ldap_count_values(values);
2426 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2427 if (!ret) {
2428 ldap_value_free(values);
2429 return NULL;
2432 for (i=0;i<*num_values;i++) {
2433 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2434 &converted_size))
2436 ldap_value_free(values);
2437 return NULL;
2440 ret[i] = NULL;
2442 ldap_value_free(values);
2443 return ret;
2447 * pull an array of strings from a ADS result
2448 * (handle large multivalue attributes with range retrieval)
2449 * @param ads connection to ads server
2450 * @param mem_ctx TALLOC_CTX to use for allocating result string
2451 * @param msg Results of search
2452 * @param field Attribute to retrieve
2453 * @param current_strings strings returned by a previous call to this function
2454 * @param next_attribute The next query should ask for this attribute
2455 * @param num_values How many values did we get this time?
2456 * @param more_values Are there more values to get?
2457 * @return Result strings in talloc context
2459 char **ads_pull_strings_range(ADS_STRUCT *ads,
2460 TALLOC_CTX *mem_ctx,
2461 LDAPMessage *msg, const char *field,
2462 char **current_strings,
2463 const char **next_attribute,
2464 size_t *num_strings,
2465 bool *more_strings)
2467 char *attr;
2468 char *expected_range_attrib, *range_attr;
2469 BerElement *ptr = NULL;
2470 char **strings;
2471 char **new_strings;
2472 size_t num_new_strings;
2473 unsigned long int range_start;
2474 unsigned long int range_end;
2476 /* we might have been given the whole lot anyway */
2477 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2478 *more_strings = False;
2479 return strings;
2482 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2484 /* look for Range result */
2485 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2486 attr;
2487 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2488 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2489 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2490 range_attr = attr;
2491 break;
2493 ldap_memfree(attr);
2495 if (!attr) {
2496 ber_free(ptr, 0);
2497 /* nothing here - this field is just empty */
2498 *more_strings = False;
2499 return NULL;
2502 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2503 &range_start, &range_end) == 2) {
2504 *more_strings = True;
2505 } else {
2506 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2507 &range_start) == 1) {
2508 *more_strings = False;
2509 } else {
2510 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2511 range_attr));
2512 ldap_memfree(range_attr);
2513 *more_strings = False;
2514 return NULL;
2518 if ((*num_strings) != range_start) {
2519 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2520 " - aborting range retreival\n",
2521 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2522 ldap_memfree(range_attr);
2523 *more_strings = False;
2524 return NULL;
2527 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2529 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2530 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2531 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2532 range_attr, (unsigned long int)range_end - range_start + 1,
2533 (unsigned long int)num_new_strings));
2534 ldap_memfree(range_attr);
2535 *more_strings = False;
2536 return NULL;
2539 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2540 *num_strings + num_new_strings);
2542 if (strings == NULL) {
2543 ldap_memfree(range_attr);
2544 *more_strings = False;
2545 return NULL;
2548 if (new_strings && num_new_strings) {
2549 memcpy(&strings[*num_strings], new_strings,
2550 sizeof(*new_strings) * num_new_strings);
2553 (*num_strings) += num_new_strings;
2555 if (*more_strings) {
2556 *next_attribute = talloc_asprintf(mem_ctx,
2557 "%s;range=%d-*",
2558 field,
2559 (int)*num_strings);
2561 if (!*next_attribute) {
2562 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2563 ldap_memfree(range_attr);
2564 *more_strings = False;
2565 return NULL;
2569 ldap_memfree(range_attr);
2571 return strings;
2575 * pull a single uint32 from a ADS result
2576 * @param ads connection to ads server
2577 * @param msg Results of search
2578 * @param field Attribute to retrieve
2579 * @param v Pointer to int to store result
2580 * @return boolean inidicating success
2582 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2583 uint32 *v)
2585 char **values;
2587 values = ldap_get_values(ads->ldap.ld, msg, field);
2588 if (!values)
2589 return False;
2590 if (!values[0]) {
2591 ldap_value_free(values);
2592 return False;
2595 *v = atoi(values[0]);
2596 ldap_value_free(values);
2597 return True;
2601 * pull a single objectGUID from an ADS result
2602 * @param ads connection to ADS server
2603 * @param msg results of search
2604 * @param guid 37-byte area to receive text guid
2605 * @return boolean indicating success
2607 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2609 char **values;
2610 UUID_FLAT flat_guid;
2612 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2613 if (!values)
2614 return False;
2616 if (values[0]) {
2617 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2618 smb_uuid_unpack(flat_guid, guid);
2619 ldap_value_free(values);
2620 return True;
2622 ldap_value_free(values);
2623 return False;
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);
3504 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3505 if (rc) {
3506 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3507 }else {
3508 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3511 if (rc != LDAP_SUCCESS) {
3512 const char *attrs[] = { "cn", NULL };
3513 LDAPMessage *msg_sub;
3515 /* we only search with scope ONE, we do not expect any further
3516 * objects to be created deeper */
3518 status = ads_do_search_retry(ads, hostnameDN,
3519 LDAP_SCOPE_ONELEVEL,
3520 "(objectclass=*)", attrs, &res);
3522 if (!ADS_ERR_OK(status)) {
3523 SAFE_FREE(host);
3524 TALLOC_FREE(hostnameDN);
3525 return status;
3528 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3529 msg_sub = ads_next_entry(ads, msg_sub)) {
3531 char *dn = NULL;
3533 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3534 SAFE_FREE(host);
3535 TALLOC_FREE(hostnameDN);
3536 return ADS_ERROR(LDAP_NO_MEMORY);
3539 status = ads_del_dn(ads, dn);
3540 if (!ADS_ERR_OK(status)) {
3541 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3542 SAFE_FREE(host);
3543 TALLOC_FREE(dn);
3544 TALLOC_FREE(hostnameDN);
3545 return status;
3548 TALLOC_FREE(dn);
3551 /* there should be no subordinate objects anymore */
3552 status = ads_do_search_retry(ads, hostnameDN,
3553 LDAP_SCOPE_ONELEVEL,
3554 "(objectclass=*)", attrs, &res);
3556 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3557 SAFE_FREE(host);
3558 TALLOC_FREE(hostnameDN);
3559 return status;
3562 /* delete hostnameDN now */
3563 status = ads_del_dn(ads, hostnameDN);
3564 if (!ADS_ERR_OK(status)) {
3565 SAFE_FREE(host);
3566 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3567 TALLOC_FREE(hostnameDN);
3568 return status;
3572 TALLOC_FREE(hostnameDN);
3574 status = ads_find_machine_acct(ads, &res, host);
3575 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3576 DEBUG(3, ("Failed to remove host account.\n"));
3577 SAFE_FREE(host);
3578 return status;
3581 SAFE_FREE(host);
3582 return status;
3586 * pull all token-sids from an LDAP dn
3587 * @param ads connection to ads server
3588 * @param mem_ctx TALLOC_CTX for allocating sid array
3589 * @param dn of LDAP object
3590 * @param user_sid pointer to struct dom_sid (objectSid)
3591 * @param primary_group_sid pointer to struct dom_sid (self composed)
3592 * @param sids pointer to sid array to allocate
3593 * @param num_sids counter of SIDs pulled
3594 * @return status of token query
3596 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3597 TALLOC_CTX *mem_ctx,
3598 const char *dn,
3599 struct dom_sid *user_sid,
3600 struct dom_sid *primary_group_sid,
3601 struct dom_sid **sids,
3602 size_t *num_sids)
3604 ADS_STATUS status;
3605 LDAPMessage *res = NULL;
3606 int count = 0;
3607 size_t tmp_num_sids;
3608 struct dom_sid *tmp_sids;
3609 struct dom_sid tmp_user_sid;
3610 struct dom_sid tmp_primary_group_sid;
3611 uint32 pgid;
3612 const char *attrs[] = {
3613 "objectSid",
3614 "tokenGroups",
3615 "primaryGroupID",
3616 NULL
3619 status = ads_search_retry_dn(ads, &res, dn, attrs);
3620 if (!ADS_ERR_OK(status)) {
3621 return status;
3624 count = ads_count_replies(ads, res);
3625 if (count != 1) {
3626 ads_msgfree(ads, res);
3627 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3630 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3631 ads_msgfree(ads, res);
3632 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3635 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3636 ads_msgfree(ads, res);
3637 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3641 /* hack to compose the primary group sid without knowing the
3642 * domsid */
3644 struct dom_sid domsid;
3645 uint32 dummy_rid;
3647 sid_copy(&domsid, &tmp_user_sid);
3649 if (!sid_split_rid(&domsid, &dummy_rid)) {
3650 ads_msgfree(ads, res);
3651 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3654 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3655 ads_msgfree(ads, res);
3656 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3660 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3662 if (tmp_num_sids == 0 || !tmp_sids) {
3663 ads_msgfree(ads, res);
3664 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3667 if (num_sids) {
3668 *num_sids = tmp_num_sids;
3671 if (sids) {
3672 *sids = tmp_sids;
3675 if (user_sid) {
3676 *user_sid = tmp_user_sid;
3679 if (primary_group_sid) {
3680 *primary_group_sid = tmp_primary_group_sid;
3683 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3685 ads_msgfree(ads, res);
3686 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3690 * Find a sAMAccoutName in LDAP
3691 * @param ads connection to ads server
3692 * @param mem_ctx TALLOC_CTX for allocating sid array
3693 * @param samaccountname to search
3694 * @param uac_ret uint32 pointer userAccountControl attribute value
3695 * @param dn_ret pointer to dn
3696 * @return status of token query
3698 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3699 TALLOC_CTX *mem_ctx,
3700 const char *samaccountname,
3701 uint32 *uac_ret,
3702 const char **dn_ret)
3704 ADS_STATUS status;
3705 const char *attrs[] = { "userAccountControl", NULL };
3706 const char *filter;
3707 LDAPMessage *res = NULL;
3708 char *dn = NULL;
3709 uint32 uac = 0;
3711 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3712 samaccountname);
3713 if (filter == NULL) {
3714 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3715 goto out;
3718 status = ads_do_search_all(ads, ads->config.bind_path,
3719 LDAP_SCOPE_SUBTREE,
3720 filter, attrs, &res);
3722 if (!ADS_ERR_OK(status)) {
3723 goto out;
3726 if (ads_count_replies(ads, res) != 1) {
3727 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3728 goto out;
3731 dn = ads_get_dn(ads, talloc_tos(), res);
3732 if (dn == NULL) {
3733 status = ADS_ERROR(LDAP_NO_MEMORY);
3734 goto out;
3737 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3738 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3739 goto out;
3742 if (uac_ret) {
3743 *uac_ret = uac;
3746 if (dn_ret) {
3747 *dn_ret = talloc_strdup(mem_ctx, dn);
3748 if (!*dn_ret) {
3749 status = ADS_ERROR(LDAP_NO_MEMORY);
3750 goto out;
3753 out:
3754 TALLOC_FREE(dn);
3755 ads_msgfree(ads, res);
3757 return status;
3761 * find our configuration path
3762 * @param ads connection to ads server
3763 * @param mem_ctx Pointer to talloc context
3764 * @param config_path Pointer to the config path
3765 * @return status of search
3767 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3768 TALLOC_CTX *mem_ctx,
3769 char **config_path)
3771 ADS_STATUS status;
3772 LDAPMessage *res = NULL;
3773 const char *config_context = NULL;
3774 const char *attrs[] = { "configurationNamingContext", NULL };
3776 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3777 "(objectclass=*)", attrs, &res);
3778 if (!ADS_ERR_OK(status)) {
3779 return status;
3782 config_context = ads_pull_string(ads, mem_ctx, res,
3783 "configurationNamingContext");
3784 ads_msgfree(ads, res);
3785 if (!config_context) {
3786 return ADS_ERROR(LDAP_NO_MEMORY);
3789 if (config_path) {
3790 *config_path = talloc_strdup(mem_ctx, config_context);
3791 if (!*config_path) {
3792 return ADS_ERROR(LDAP_NO_MEMORY);
3796 return ADS_ERROR(LDAP_SUCCESS);
3800 * find the displayName of an extended right
3801 * @param ads connection to ads server
3802 * @param config_path The config path
3803 * @param mem_ctx Pointer to talloc context
3804 * @param GUID struct of the rightsGUID
3805 * @return status of search
3807 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3808 const char *config_path,
3809 TALLOC_CTX *mem_ctx,
3810 const struct GUID *rights_guid)
3812 ADS_STATUS rc;
3813 LDAPMessage *res = NULL;
3814 char *expr = NULL;
3815 const char *attrs[] = { "displayName", NULL };
3816 const char *result = NULL;
3817 const char *path;
3819 if (!ads || !mem_ctx || !rights_guid) {
3820 goto done;
3823 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3824 GUID_string(mem_ctx, rights_guid));
3825 if (!expr) {
3826 goto done;
3829 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3830 if (!path) {
3831 goto done;
3834 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3835 expr, attrs, &res);
3836 if (!ADS_ERR_OK(rc)) {
3837 goto done;
3840 if (ads_count_replies(ads, res) != 1) {
3841 goto done;
3844 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3846 done:
3847 ads_msgfree(ads, res);
3848 return result;
3852 * verify or build and verify an account ou
3853 * @param mem_ctx Pointer to talloc context
3854 * @param ads connection to ads server
3855 * @param account_ou
3856 * @return status of search
3859 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3860 ADS_STRUCT *ads,
3861 const char **account_ou)
3863 char **exploded_dn;
3864 const char *name;
3865 char *ou_string;
3867 exploded_dn = ldap_explode_dn(*account_ou, 0);
3868 if (exploded_dn) {
3869 ldap_value_free(exploded_dn);
3870 return ADS_SUCCESS;
3873 ou_string = ads_ou_string(ads, *account_ou);
3874 if (!ou_string) {
3875 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3878 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3879 ads->config.bind_path);
3880 SAFE_FREE(ou_string);
3882 if (!name) {
3883 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3886 exploded_dn = ldap_explode_dn(name, 0);
3887 if (!exploded_dn) {
3888 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3890 ldap_value_free(exploded_dn);
3892 *account_ou = name;
3893 return ADS_SUCCESS;
3896 #endif