Fix array size of a memmber of struct cli_ulogoff_state
[Samba.git] / source3 / libads / ldap.c
blob97d89dc88d4c992aa1b386db964073327450776c
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 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2149 return;
2151 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2156 dump ntSecurityDescriptor
2158 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2160 TALLOC_CTX *frame = talloc_stackframe();
2161 struct security_descriptor *psd;
2162 NTSTATUS status;
2164 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2165 values[0]->bv_len, &psd);
2166 if (!NT_STATUS_IS_OK(status)) {
2167 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2168 nt_errstr(status)));
2169 TALLOC_FREE(frame);
2170 return;
2173 if (psd) {
2174 ads_disp_sd(ads, talloc_tos(), psd);
2177 TALLOC_FREE(frame);
2181 dump a string result from ldap
2183 static void dump_string(const char *field, char **values)
2185 int i;
2186 for (i=0; values[i]; i++) {
2187 printf("%s: %s\n", field, values[i]);
2192 dump a field from LDAP on stdout
2193 used for debugging
2196 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2198 const struct {
2199 const char *name;
2200 bool string;
2201 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2202 } handlers[] = {
2203 {"objectGUID", False, dump_guid},
2204 {"netbootGUID", False, dump_guid},
2205 {"nTSecurityDescriptor", False, dump_sd},
2206 {"dnsRecord", False, dump_binary},
2207 {"objectSid", False, dump_sid},
2208 {"tokenGroups", False, dump_sid},
2209 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2210 {"tokengroupsGlobalandUniversal", False, dump_sid},
2211 {"mS-DS-CreatorSID", False, dump_sid},
2212 {"msExchMailboxGuid", False, dump_guid},
2213 {NULL, True, NULL}
2215 int i;
2217 if (!field) { /* must be end of an entry */
2218 printf("\n");
2219 return False;
2222 for (i=0; handlers[i].name; i++) {
2223 if (StrCaseCmp(handlers[i].name, field) == 0) {
2224 if (!values) /* first time, indicate string or not */
2225 return handlers[i].string;
2226 handlers[i].handler(ads, field, (struct berval **) values);
2227 break;
2230 if (!handlers[i].name) {
2231 if (!values) /* first time, indicate string conversion */
2232 return True;
2233 dump_string(field, (char **)values);
2235 return False;
2239 * Dump a result from LDAP on stdout
2240 * used for debugging
2241 * @param ads connection to ads server
2242 * @param res Results to dump
2245 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2247 ads_process_results(ads, res, ads_dump_field, NULL);
2251 * Walk through results, calling a function for each entry found.
2252 * The function receives a field name, a berval * array of values,
2253 * and a data area passed through from the start. The function is
2254 * called once with null for field and values at the end of each
2255 * entry.
2256 * @param ads connection to ads server
2257 * @param res Results to process
2258 * @param fn Function for processing each result
2259 * @param data_area user-defined area to pass to function
2261 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2262 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2263 void *data_area)
2265 LDAPMessage *msg;
2266 TALLOC_CTX *ctx;
2267 size_t converted_size;
2269 if (!(ctx = talloc_init("ads_process_results")))
2270 return;
2272 for (msg = ads_first_entry(ads, res); msg;
2273 msg = ads_next_entry(ads, msg)) {
2274 char *utf8_field;
2275 BerElement *b;
2277 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2278 (LDAPMessage *)msg,&b);
2279 utf8_field;
2280 utf8_field=ldap_next_attribute(ads->ldap.ld,
2281 (LDAPMessage *)msg,b)) {
2282 struct berval **ber_vals;
2283 char **str_vals, **utf8_vals;
2284 char *field;
2285 bool string;
2287 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2288 &converted_size))
2290 DEBUG(0,("ads_process_results: "
2291 "pull_utf8_talloc failed: %s",
2292 strerror(errno)));
2295 string = fn(ads, field, NULL, data_area);
2297 if (string) {
2298 utf8_vals = ldap_get_values(ads->ldap.ld,
2299 (LDAPMessage *)msg, field);
2300 str_vals = ads_pull_strvals(ctx,
2301 (const char **) utf8_vals);
2302 fn(ads, field, (void **) str_vals, data_area);
2303 ldap_value_free(utf8_vals);
2304 } else {
2305 ber_vals = ldap_get_values_len(ads->ldap.ld,
2306 (LDAPMessage *)msg, field);
2307 fn(ads, field, (void **) ber_vals, data_area);
2309 ldap_value_free_len(ber_vals);
2311 ldap_memfree(utf8_field);
2313 ber_free(b, 0);
2314 talloc_free_children(ctx);
2315 fn(ads, NULL, NULL, data_area); /* completed an entry */
2318 talloc_destroy(ctx);
2322 * count how many replies are in a LDAPMessage
2323 * @param ads connection to ads server
2324 * @param res Results to count
2325 * @return number of replies
2327 int ads_count_replies(ADS_STRUCT *ads, void *res)
2329 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2333 * pull the first entry from a ADS result
2334 * @param ads connection to ads server
2335 * @param res Results of search
2336 * @return first entry from result
2338 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2340 return ldap_first_entry(ads->ldap.ld, res);
2344 * pull the next entry from a ADS result
2345 * @param ads connection to ads server
2346 * @param res Results of search
2347 * @return next entry from result
2349 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2351 return ldap_next_entry(ads->ldap.ld, res);
2355 * pull the first message from a ADS result
2356 * @param ads connection to ads server
2357 * @param res Results of search
2358 * @return first message from result
2360 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2362 return ldap_first_message(ads->ldap.ld, res);
2366 * pull the next message from a ADS result
2367 * @param ads connection to ads server
2368 * @param res Results of search
2369 * @return next message from result
2371 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2373 return ldap_next_message(ads->ldap.ld, res);
2377 * pull a single string from a ADS result
2378 * @param ads connection to ads server
2379 * @param mem_ctx TALLOC_CTX to use for allocating result string
2380 * @param msg Results of search
2381 * @param field Attribute to retrieve
2382 * @return Result string in talloc context
2384 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2385 const char *field)
2387 char **values;
2388 char *ret = NULL;
2389 char *ux_string;
2390 size_t converted_size;
2392 values = ldap_get_values(ads->ldap.ld, msg, field);
2393 if (!values)
2394 return NULL;
2396 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2397 &converted_size))
2399 ret = ux_string;
2401 ldap_value_free(values);
2402 return ret;
2406 * pull an array of strings from a ADS result
2407 * @param ads connection to ads server
2408 * @param mem_ctx TALLOC_CTX to use for allocating result string
2409 * @param msg Results of search
2410 * @param field Attribute to retrieve
2411 * @return Result strings in talloc context
2413 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2414 LDAPMessage *msg, const char *field,
2415 size_t *num_values)
2417 char **values;
2418 char **ret = NULL;
2419 int i;
2420 size_t converted_size;
2422 values = ldap_get_values(ads->ldap.ld, msg, field);
2423 if (!values)
2424 return NULL;
2426 *num_values = ldap_count_values(values);
2428 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2429 if (!ret) {
2430 ldap_value_free(values);
2431 return NULL;
2434 for (i=0;i<*num_values;i++) {
2435 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2436 &converted_size))
2438 ldap_value_free(values);
2439 return NULL;
2442 ret[i] = NULL;
2444 ldap_value_free(values);
2445 return ret;
2449 * pull an array of strings from a ADS result
2450 * (handle large multivalue attributes with range retrieval)
2451 * @param ads connection to ads server
2452 * @param mem_ctx TALLOC_CTX to use for allocating result string
2453 * @param msg Results of search
2454 * @param field Attribute to retrieve
2455 * @param current_strings strings returned by a previous call to this function
2456 * @param next_attribute The next query should ask for this attribute
2457 * @param num_values How many values did we get this time?
2458 * @param more_values Are there more values to get?
2459 * @return Result strings in talloc context
2461 char **ads_pull_strings_range(ADS_STRUCT *ads,
2462 TALLOC_CTX *mem_ctx,
2463 LDAPMessage *msg, const char *field,
2464 char **current_strings,
2465 const char **next_attribute,
2466 size_t *num_strings,
2467 bool *more_strings)
2469 char *attr;
2470 char *expected_range_attrib, *range_attr;
2471 BerElement *ptr = NULL;
2472 char **strings;
2473 char **new_strings;
2474 size_t num_new_strings;
2475 unsigned long int range_start;
2476 unsigned long int range_end;
2478 /* we might have been given the whole lot anyway */
2479 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2480 *more_strings = False;
2481 return strings;
2484 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2486 /* look for Range result */
2487 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2488 attr;
2489 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2490 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2491 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2492 range_attr = attr;
2493 break;
2495 ldap_memfree(attr);
2497 if (!attr) {
2498 ber_free(ptr, 0);
2499 /* nothing here - this field is just empty */
2500 *more_strings = False;
2501 return NULL;
2504 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2505 &range_start, &range_end) == 2) {
2506 *more_strings = True;
2507 } else {
2508 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2509 &range_start) == 1) {
2510 *more_strings = False;
2511 } else {
2512 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2513 range_attr));
2514 ldap_memfree(range_attr);
2515 *more_strings = False;
2516 return NULL;
2520 if ((*num_strings) != range_start) {
2521 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2522 " - aborting range retreival\n",
2523 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2524 ldap_memfree(range_attr);
2525 *more_strings = False;
2526 return NULL;
2529 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2531 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2532 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2533 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2534 range_attr, (unsigned long int)range_end - range_start + 1,
2535 (unsigned long int)num_new_strings));
2536 ldap_memfree(range_attr);
2537 *more_strings = False;
2538 return NULL;
2541 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2542 *num_strings + num_new_strings);
2544 if (strings == NULL) {
2545 ldap_memfree(range_attr);
2546 *more_strings = False;
2547 return NULL;
2550 if (new_strings && num_new_strings) {
2551 memcpy(&strings[*num_strings], new_strings,
2552 sizeof(*new_strings) * num_new_strings);
2555 (*num_strings) += num_new_strings;
2557 if (*more_strings) {
2558 *next_attribute = talloc_asprintf(mem_ctx,
2559 "%s;range=%d-*",
2560 field,
2561 (int)*num_strings);
2563 if (!*next_attribute) {
2564 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2565 ldap_memfree(range_attr);
2566 *more_strings = False;
2567 return NULL;
2571 ldap_memfree(range_attr);
2573 return strings;
2577 * pull a single uint32 from a ADS result
2578 * @param ads connection to ads server
2579 * @param msg Results of search
2580 * @param field Attribute to retrieve
2581 * @param v Pointer to int to store result
2582 * @return boolean inidicating success
2584 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2585 uint32 *v)
2587 char **values;
2589 values = ldap_get_values(ads->ldap.ld, msg, field);
2590 if (!values)
2591 return False;
2592 if (!values[0]) {
2593 ldap_value_free(values);
2594 return False;
2597 *v = atoi(values[0]);
2598 ldap_value_free(values);
2599 return True;
2603 * pull a single objectGUID from an ADS result
2604 * @param ads connection to ADS server
2605 * @param msg results of search
2606 * @param guid 37-byte area to receive text guid
2607 * @return boolean indicating success
2609 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2611 char **values;
2612 UUID_FLAT flat_guid;
2614 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2615 if (!values)
2616 return False;
2618 if (values[0]) {
2619 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2620 smb_uuid_unpack(flat_guid, guid);
2621 ldap_value_free(values);
2622 return True;
2624 ldap_value_free(values);
2625 return False;
2631 * pull a single struct dom_sid from a ADS result
2632 * @param ads connection to ads server
2633 * @param msg Results of search
2634 * @param field Attribute to retrieve
2635 * @param sid Pointer to sid to store result
2636 * @return boolean inidicating success
2638 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2639 struct dom_sid *sid)
2641 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2645 * pull an array of struct dom_sids from a ADS result
2646 * @param ads connection to ads server
2647 * @param mem_ctx TALLOC_CTX for allocating sid array
2648 * @param msg Results of search
2649 * @param field Attribute to retrieve
2650 * @param sids pointer to sid array to allocate
2651 * @return the count of SIDs pulled
2653 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2654 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2656 struct berval **values;
2657 bool ret;
2658 int count, i;
2660 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2662 if (!values)
2663 return 0;
2665 for (i=0; values[i]; i++)
2666 /* nop */ ;
2668 if (i) {
2669 (*sids) = TALLOC_ARRAY(mem_ctx, struct dom_sid, i);
2670 if (!(*sids)) {
2671 ldap_value_free_len(values);
2672 return 0;
2674 } else {
2675 (*sids) = NULL;
2678 count = 0;
2679 for (i=0; values[i]; i++) {
2680 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2681 if (ret) {
2682 DEBUG(10, ("pulling SID: %s\n",
2683 sid_string_dbg(&(*sids)[count])));
2684 count++;
2688 ldap_value_free_len(values);
2689 return count;
2693 * pull a struct security_descriptor from a ADS result
2694 * @param ads connection to ads server
2695 * @param mem_ctx TALLOC_CTX for allocating sid array
2696 * @param msg Results of search
2697 * @param field Attribute to retrieve
2698 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2699 * @return boolean inidicating success
2701 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2702 LDAPMessage *msg, const char *field,
2703 struct security_descriptor **sd)
2705 struct berval **values;
2706 bool ret = true;
2708 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2710 if (!values) return false;
2712 if (values[0]) {
2713 NTSTATUS status;
2714 status = unmarshall_sec_desc(mem_ctx,
2715 (uint8 *)values[0]->bv_val,
2716 values[0]->bv_len, sd);
2717 if (!NT_STATUS_IS_OK(status)) {
2718 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2719 nt_errstr(status)));
2720 ret = false;
2724 ldap_value_free_len(values);
2725 return ret;
2729 * in order to support usernames longer than 21 characters we need to
2730 * use both the sAMAccountName and the userPrincipalName attributes
2731 * It seems that not all users have the userPrincipalName attribute set
2733 * @param ads connection to ads server
2734 * @param mem_ctx TALLOC_CTX for allocating sid array
2735 * @param msg Results of search
2736 * @return the username
2738 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2739 LDAPMessage *msg)
2741 #if 0 /* JERRY */
2742 char *ret, *p;
2744 /* lookup_name() only works on the sAMAccountName to
2745 returning the username portion of userPrincipalName
2746 breaks winbindd_getpwnam() */
2748 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2749 if (ret && (p = strchr_m(ret, '@'))) {
2750 *p = 0;
2751 return ret;
2753 #endif
2754 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2759 * find the update serial number - this is the core of the ldap cache
2760 * @param ads connection to ads server
2761 * @param ads connection to ADS server
2762 * @param usn Pointer to retrieved update serial number
2763 * @return status of search
2765 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2767 const char *attrs[] = {"highestCommittedUSN", NULL};
2768 ADS_STATUS status;
2769 LDAPMessage *res;
2771 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2772 if (!ADS_ERR_OK(status))
2773 return status;
2775 if (ads_count_replies(ads, res) != 1) {
2776 ads_msgfree(ads, res);
2777 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2780 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2781 ads_msgfree(ads, res);
2782 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2785 ads_msgfree(ads, res);
2786 return ADS_SUCCESS;
2789 /* parse a ADS timestring - typical string is
2790 '20020917091222.0Z0' which means 09:12.22 17th September
2791 2002, timezone 0 */
2792 static time_t ads_parse_time(const char *str)
2794 struct tm tm;
2796 ZERO_STRUCT(tm);
2798 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2799 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2800 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2801 return 0;
2803 tm.tm_year -= 1900;
2804 tm.tm_mon -= 1;
2806 return timegm(&tm);
2809 /********************************************************************
2810 ********************************************************************/
2812 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2814 const char *attrs[] = {"currentTime", NULL};
2815 ADS_STATUS status;
2816 LDAPMessage *res;
2817 char *timestr;
2818 TALLOC_CTX *ctx;
2819 ADS_STRUCT *ads_s = ads;
2821 if (!(ctx = talloc_init("ads_current_time"))) {
2822 return ADS_ERROR(LDAP_NO_MEMORY);
2825 /* establish a new ldap tcp session if necessary */
2827 if ( !ads->ldap.ld ) {
2828 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2829 ads->server.ldap_server )) == NULL )
2831 goto done;
2833 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2834 status = ads_connect( ads_s );
2835 if ( !ADS_ERR_OK(status))
2836 goto done;
2839 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2840 if (!ADS_ERR_OK(status)) {
2841 goto done;
2844 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2845 if (!timestr) {
2846 ads_msgfree(ads_s, res);
2847 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2848 goto done;
2851 /* but save the time and offset in the original ADS_STRUCT */
2853 ads->config.current_time = ads_parse_time(timestr);
2855 if (ads->config.current_time != 0) {
2856 ads->auth.time_offset = ads->config.current_time - time(NULL);
2857 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2860 ads_msgfree(ads, res);
2862 status = ADS_SUCCESS;
2864 done:
2865 /* free any temporary ads connections */
2866 if ( ads_s != ads ) {
2867 ads_destroy( &ads_s );
2869 talloc_destroy(ctx);
2871 return status;
2874 /********************************************************************
2875 ********************************************************************/
2877 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2879 const char *attrs[] = {"domainFunctionality", NULL};
2880 ADS_STATUS status;
2881 LDAPMessage *res;
2882 ADS_STRUCT *ads_s = ads;
2884 *val = DS_DOMAIN_FUNCTION_2000;
2886 /* establish a new ldap tcp session if necessary */
2888 if ( !ads->ldap.ld ) {
2889 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2890 ads->server.ldap_server )) == NULL )
2892 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2893 goto done;
2895 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2896 status = ads_connect( ads_s );
2897 if ( !ADS_ERR_OK(status))
2898 goto done;
2901 /* If the attribute does not exist assume it is a Windows 2000
2902 functional domain */
2904 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2905 if (!ADS_ERR_OK(status)) {
2906 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2907 status = ADS_SUCCESS;
2909 goto done;
2912 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2913 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2915 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2918 ads_msgfree(ads, res);
2920 done:
2921 /* free any temporary ads connections */
2922 if ( ads_s != ads ) {
2923 ads_destroy( &ads_s );
2926 return status;
2930 * find the domain sid for our domain
2931 * @param ads connection to ads server
2932 * @param sid Pointer to domain sid
2933 * @return status of search
2935 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2937 const char *attrs[] = {"objectSid", NULL};
2938 LDAPMessage *res;
2939 ADS_STATUS rc;
2941 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2942 attrs, &res);
2943 if (!ADS_ERR_OK(rc)) return rc;
2944 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2945 ads_msgfree(ads, res);
2946 return ADS_ERROR_SYSTEM(ENOENT);
2948 ads_msgfree(ads, res);
2950 return ADS_SUCCESS;
2954 * find our site name
2955 * @param ads connection to ads server
2956 * @param mem_ctx Pointer to talloc context
2957 * @param site_name Pointer to the sitename
2958 * @return status of search
2960 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2962 ADS_STATUS status;
2963 LDAPMessage *res;
2964 const char *dn, *service_name;
2965 const char *attrs[] = { "dsServiceName", NULL };
2967 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2968 if (!ADS_ERR_OK(status)) {
2969 return status;
2972 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2973 if (service_name == NULL) {
2974 ads_msgfree(ads, res);
2975 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2978 ads_msgfree(ads, res);
2980 /* go up three levels */
2981 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2982 if (dn == NULL) {
2983 return ADS_ERROR(LDAP_NO_MEMORY);
2986 *site_name = talloc_strdup(mem_ctx, dn);
2987 if (*site_name == NULL) {
2988 return ADS_ERROR(LDAP_NO_MEMORY);
2991 return status;
2993 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2998 * find the site dn where a machine resides
2999 * @param ads connection to ads server
3000 * @param mem_ctx Pointer to talloc context
3001 * @param computer_name name of the machine
3002 * @param site_name Pointer to the sitename
3003 * @return status of search
3005 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3007 ADS_STATUS status;
3008 LDAPMessage *res;
3009 const char *parent, *filter;
3010 char *config_context = NULL;
3011 char *dn;
3013 /* shortcut a query */
3014 if (strequal(computer_name, ads->config.ldap_server_name)) {
3015 return ads_site_dn(ads, mem_ctx, site_dn);
3018 status = ads_config_path(ads, mem_ctx, &config_context);
3019 if (!ADS_ERR_OK(status)) {
3020 return status;
3023 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3024 if (filter == NULL) {
3025 return ADS_ERROR(LDAP_NO_MEMORY);
3028 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3029 filter, NULL, &res);
3030 if (!ADS_ERR_OK(status)) {
3031 return status;
3034 if (ads_count_replies(ads, res) != 1) {
3035 ads_msgfree(ads, res);
3036 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3039 dn = ads_get_dn(ads, mem_ctx, res);
3040 if (dn == NULL) {
3041 ads_msgfree(ads, res);
3042 return ADS_ERROR(LDAP_NO_MEMORY);
3045 /* go up three levels */
3046 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3047 if (parent == NULL) {
3048 ads_msgfree(ads, res);
3049 TALLOC_FREE(dn);
3050 return ADS_ERROR(LDAP_NO_MEMORY);
3053 *site_dn = talloc_strdup(mem_ctx, parent);
3054 if (*site_dn == NULL) {
3055 ads_msgfree(ads, res);
3056 TALLOC_FREE(dn);
3057 return ADS_ERROR(LDAP_NO_MEMORY);
3060 TALLOC_FREE(dn);
3061 ads_msgfree(ads, res);
3063 return status;
3067 * get the upn suffixes for a domain
3068 * @param ads connection to ads server
3069 * @param mem_ctx Pointer to talloc context
3070 * @param suffixes Pointer to an array of suffixes
3071 * @param num_suffixes Pointer to the number of suffixes
3072 * @return status of search
3074 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3076 ADS_STATUS status;
3077 LDAPMessage *res;
3078 const char *base;
3079 char *config_context = NULL;
3080 const char *attrs[] = { "uPNSuffixes", NULL };
3082 status = ads_config_path(ads, mem_ctx, &config_context);
3083 if (!ADS_ERR_OK(status)) {
3084 return status;
3087 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3088 if (base == NULL) {
3089 return ADS_ERROR(LDAP_NO_MEMORY);
3092 status = ads_search_dn(ads, &res, base, attrs);
3093 if (!ADS_ERR_OK(status)) {
3094 return status;
3097 if (ads_count_replies(ads, res) != 1) {
3098 ads_msgfree(ads, res);
3099 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3102 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3103 if ((*suffixes) == NULL) {
3104 ads_msgfree(ads, res);
3105 return ADS_ERROR(LDAP_NO_MEMORY);
3108 ads_msgfree(ads, res);
3110 return status;
3114 * get the joinable ous for a domain
3115 * @param ads connection to ads server
3116 * @param mem_ctx Pointer to talloc context
3117 * @param ous Pointer to an array of ous
3118 * @param num_ous Pointer to the number of ous
3119 * @return status of search
3121 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3122 TALLOC_CTX *mem_ctx,
3123 char ***ous,
3124 size_t *num_ous)
3126 ADS_STATUS status;
3127 LDAPMessage *res = NULL;
3128 LDAPMessage *msg = NULL;
3129 const char *attrs[] = { "dn", NULL };
3130 int count = 0;
3132 status = ads_search(ads, &res,
3133 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3134 attrs);
3135 if (!ADS_ERR_OK(status)) {
3136 return status;
3139 count = ads_count_replies(ads, res);
3140 if (count < 1) {
3141 ads_msgfree(ads, res);
3142 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3145 for (msg = ads_first_entry(ads, res); msg;
3146 msg = ads_next_entry(ads, msg)) {
3148 char *dn = NULL;
3150 dn = ads_get_dn(ads, talloc_tos(), msg);
3151 if (!dn) {
3152 ads_msgfree(ads, res);
3153 return ADS_ERROR(LDAP_NO_MEMORY);
3156 if (!add_string_to_array(mem_ctx, dn,
3157 (const char ***)ous,
3158 (int *)num_ous)) {
3159 TALLOC_FREE(dn);
3160 ads_msgfree(ads, res);
3161 return ADS_ERROR(LDAP_NO_MEMORY);
3164 TALLOC_FREE(dn);
3167 ads_msgfree(ads, res);
3169 return status;
3174 * pull a struct dom_sid from an extended dn string
3175 * @param mem_ctx TALLOC_CTX
3176 * @param extended_dn string
3177 * @param flags string type of extended_dn
3178 * @param sid pointer to a struct dom_sid
3179 * @return NT_STATUS_OK on success,
3180 * NT_INVALID_PARAMETER on error,
3181 * NT_STATUS_NOT_FOUND if no SID present
3183 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3184 const char *extended_dn,
3185 enum ads_extended_dn_flags flags,
3186 struct dom_sid *sid)
3188 char *p, *q, *dn;
3190 if (!extended_dn) {
3191 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3194 /* otherwise extended_dn gets stripped off */
3195 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3196 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3199 * ADS_EXTENDED_DN_HEX_STRING:
3200 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3202 * ADS_EXTENDED_DN_STRING (only with w2k3):
3203 * <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
3205 * Object with no SID, such as an Exchange Public Folder
3206 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3209 p = strchr(dn, ';');
3210 if (!p) {
3211 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3214 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3215 DEBUG(5,("No SID present in extended dn\n"));
3216 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3219 p += strlen(";<SID=");
3221 q = strchr(p, '>');
3222 if (!q) {
3223 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3226 *q = '\0';
3228 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3230 switch (flags) {
3232 case ADS_EXTENDED_DN_STRING:
3233 if (!string_to_sid(sid, p)) {
3234 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3236 break;
3237 case ADS_EXTENDED_DN_HEX_STRING: {
3238 fstring buf;
3239 size_t buf_len;
3241 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3242 if (buf_len == 0) {
3243 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3246 if (!sid_parse(buf, buf_len, sid)) {
3247 DEBUG(10,("failed to parse sid\n"));
3248 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3250 break;
3252 default:
3253 DEBUG(10,("unknown extended dn format\n"));
3254 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3257 return ADS_ERROR_NT(NT_STATUS_OK);
3261 * pull an array of struct dom_sids from a ADS result
3262 * @param ads connection to ads server
3263 * @param mem_ctx TALLOC_CTX for allocating sid array
3264 * @param msg Results of search
3265 * @param field Attribute to retrieve
3266 * @param flags string type of extended_dn
3267 * @param sids pointer to sid array to allocate
3268 * @return the count of SIDs pulled
3270 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3271 TALLOC_CTX *mem_ctx,
3272 LDAPMessage *msg,
3273 const char *field,
3274 enum ads_extended_dn_flags flags,
3275 struct dom_sid **sids)
3277 int i;
3278 ADS_STATUS rc;
3279 size_t dn_count, ret_count = 0;
3280 char **dn_strings;
3282 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3283 &dn_count)) == NULL) {
3284 return 0;
3287 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, dn_count + 1);
3288 if (!(*sids)) {
3289 TALLOC_FREE(dn_strings);
3290 return 0;
3293 for (i=0; i<dn_count; i++) {
3294 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3295 flags, &(*sids)[i]);
3296 if (!ADS_ERR_OK(rc)) {
3297 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3298 NT_STATUS_NOT_FOUND)) {
3299 continue;
3301 else {
3302 TALLOC_FREE(*sids);
3303 TALLOC_FREE(dn_strings);
3304 return 0;
3307 ret_count++;
3310 TALLOC_FREE(dn_strings);
3312 return ret_count;
3315 /********************************************************************
3316 ********************************************************************/
3318 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3320 LDAPMessage *res = NULL;
3321 ADS_STATUS status;
3322 int count = 0;
3323 char *name = NULL;
3325 status = ads_find_machine_acct(ads, &res, global_myname());
3326 if (!ADS_ERR_OK(status)) {
3327 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3328 global_myname()));
3329 goto out;
3332 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3333 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3334 goto out;
3337 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3338 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3341 out:
3342 ads_msgfree(ads, res);
3344 return name;
3347 /********************************************************************
3348 ********************************************************************/
3350 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3352 LDAPMessage *res = NULL;
3353 ADS_STATUS status;
3354 int count = 0;
3355 char *name = NULL;
3357 status = ads_find_machine_acct(ads, &res, machine_name);
3358 if (!ADS_ERR_OK(status)) {
3359 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3360 global_myname()));
3361 goto out;
3364 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3365 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3366 goto out;
3369 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3370 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3373 out:
3374 ads_msgfree(ads, res);
3376 return name;
3379 /********************************************************************
3380 ********************************************************************/
3382 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3384 LDAPMessage *res = NULL;
3385 ADS_STATUS status;
3386 int count = 0;
3387 char *name = NULL;
3389 status = ads_find_machine_acct(ads, &res, global_myname());
3390 if (!ADS_ERR_OK(status)) {
3391 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3392 global_myname()));
3393 goto out;
3396 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3397 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3398 goto out;
3401 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3402 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3405 out:
3406 ads_msgfree(ads, res);
3408 return name;
3411 #if 0
3413 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3416 * Join a machine to a realm
3417 * Creates the machine account and sets the machine password
3418 * @param ads connection to ads server
3419 * @param machine name of host to add
3420 * @param org_unit Organizational unit to place machine in
3421 * @return status of join
3423 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3424 uint32 account_type, const char *org_unit)
3426 ADS_STATUS status;
3427 LDAPMessage *res = NULL;
3428 char *machine;
3430 /* machine name must be lowercase */
3431 machine = SMB_STRDUP(machine_name);
3432 strlower_m(machine);
3435 status = ads_find_machine_acct(ads, (void **)&res, machine);
3436 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3437 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3438 status = ads_leave_realm(ads, machine);
3439 if (!ADS_ERR_OK(status)) {
3440 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3441 machine, ads->config.realm));
3442 return status;
3446 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3447 if (!ADS_ERR_OK(status)) {
3448 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3449 SAFE_FREE(machine);
3450 return status;
3453 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3454 if (!ADS_ERR_OK(status)) {
3455 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3456 SAFE_FREE(machine);
3457 return status;
3460 SAFE_FREE(machine);
3461 ads_msgfree(ads, res);
3463 return status;
3465 #endif
3468 * Delete a machine from the realm
3469 * @param ads connection to ads server
3470 * @param hostname Machine to remove
3471 * @return status of delete
3473 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3475 ADS_STATUS status;
3476 void *msg;
3477 LDAPMessage *res;
3478 char *hostnameDN, *host;
3479 int rc;
3480 LDAPControl ldap_control;
3481 LDAPControl * pldap_control[2] = {NULL, NULL};
3483 pldap_control[0] = &ldap_control;
3484 memset(&ldap_control, 0, sizeof(LDAPControl));
3485 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3487 /* hostname must be lowercase */
3488 host = SMB_STRDUP(hostname);
3489 strlower_m(host);
3491 status = ads_find_machine_acct(ads, &res, host);
3492 if (!ADS_ERR_OK(status)) {
3493 DEBUG(0, ("Host account for %s does not exist.\n", host));
3494 SAFE_FREE(host);
3495 return status;
3498 msg = ads_first_entry(ads, res);
3499 if (!msg) {
3500 SAFE_FREE(host);
3501 return ADS_ERROR_SYSTEM(ENOENT);
3504 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3506 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3507 if (rc) {
3508 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3509 }else {
3510 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3513 if (rc != LDAP_SUCCESS) {
3514 const char *attrs[] = { "cn", NULL };
3515 LDAPMessage *msg_sub;
3517 /* we only search with scope ONE, we do not expect any further
3518 * objects to be created deeper */
3520 status = ads_do_search_retry(ads, hostnameDN,
3521 LDAP_SCOPE_ONELEVEL,
3522 "(objectclass=*)", attrs, &res);
3524 if (!ADS_ERR_OK(status)) {
3525 SAFE_FREE(host);
3526 TALLOC_FREE(hostnameDN);
3527 return status;
3530 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3531 msg_sub = ads_next_entry(ads, msg_sub)) {
3533 char *dn = NULL;
3535 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3536 SAFE_FREE(host);
3537 TALLOC_FREE(hostnameDN);
3538 return ADS_ERROR(LDAP_NO_MEMORY);
3541 status = ads_del_dn(ads, dn);
3542 if (!ADS_ERR_OK(status)) {
3543 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3544 SAFE_FREE(host);
3545 TALLOC_FREE(dn);
3546 TALLOC_FREE(hostnameDN);
3547 return status;
3550 TALLOC_FREE(dn);
3553 /* there should be no subordinate objects anymore */
3554 status = ads_do_search_retry(ads, hostnameDN,
3555 LDAP_SCOPE_ONELEVEL,
3556 "(objectclass=*)", attrs, &res);
3558 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3559 SAFE_FREE(host);
3560 TALLOC_FREE(hostnameDN);
3561 return status;
3564 /* delete hostnameDN now */
3565 status = ads_del_dn(ads, hostnameDN);
3566 if (!ADS_ERR_OK(status)) {
3567 SAFE_FREE(host);
3568 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3569 TALLOC_FREE(hostnameDN);
3570 return status;
3574 TALLOC_FREE(hostnameDN);
3576 status = ads_find_machine_acct(ads, &res, host);
3577 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3578 DEBUG(3, ("Failed to remove host account.\n"));
3579 SAFE_FREE(host);
3580 return status;
3583 SAFE_FREE(host);
3584 return status;
3588 * pull all token-sids from an LDAP dn
3589 * @param ads connection to ads server
3590 * @param mem_ctx TALLOC_CTX for allocating sid array
3591 * @param dn of LDAP object
3592 * @param user_sid pointer to struct dom_sid (objectSid)
3593 * @param primary_group_sid pointer to struct dom_sid (self composed)
3594 * @param sids pointer to sid array to allocate
3595 * @param num_sids counter of SIDs pulled
3596 * @return status of token query
3598 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3599 TALLOC_CTX *mem_ctx,
3600 const char *dn,
3601 struct dom_sid *user_sid,
3602 struct dom_sid *primary_group_sid,
3603 struct dom_sid **sids,
3604 size_t *num_sids)
3606 ADS_STATUS status;
3607 LDAPMessage *res = NULL;
3608 int count = 0;
3609 size_t tmp_num_sids;
3610 struct dom_sid *tmp_sids;
3611 struct dom_sid tmp_user_sid;
3612 struct dom_sid tmp_primary_group_sid;
3613 uint32 pgid;
3614 const char *attrs[] = {
3615 "objectSid",
3616 "tokenGroups",
3617 "primaryGroupID",
3618 NULL
3621 status = ads_search_retry_dn(ads, &res, dn, attrs);
3622 if (!ADS_ERR_OK(status)) {
3623 return status;
3626 count = ads_count_replies(ads, res);
3627 if (count != 1) {
3628 ads_msgfree(ads, res);
3629 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3632 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3633 ads_msgfree(ads, res);
3634 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3637 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3638 ads_msgfree(ads, res);
3639 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3643 /* hack to compose the primary group sid without knowing the
3644 * domsid */
3646 struct dom_sid domsid;
3647 uint32 dummy_rid;
3649 sid_copy(&domsid, &tmp_user_sid);
3651 if (!sid_split_rid(&domsid, &dummy_rid)) {
3652 ads_msgfree(ads, res);
3653 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3656 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3657 ads_msgfree(ads, res);
3658 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3662 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3664 if (tmp_num_sids == 0 || !tmp_sids) {
3665 ads_msgfree(ads, res);
3666 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3669 if (num_sids) {
3670 *num_sids = tmp_num_sids;
3673 if (sids) {
3674 *sids = tmp_sids;
3677 if (user_sid) {
3678 *user_sid = tmp_user_sid;
3681 if (primary_group_sid) {
3682 *primary_group_sid = tmp_primary_group_sid;
3685 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3687 ads_msgfree(ads, res);
3688 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3692 * Find a sAMAccoutName in LDAP
3693 * @param ads connection to ads server
3694 * @param mem_ctx TALLOC_CTX for allocating sid array
3695 * @param samaccountname to search
3696 * @param uac_ret uint32 pointer userAccountControl attribute value
3697 * @param dn_ret pointer to dn
3698 * @return status of token query
3700 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3701 TALLOC_CTX *mem_ctx,
3702 const char *samaccountname,
3703 uint32 *uac_ret,
3704 const char **dn_ret)
3706 ADS_STATUS status;
3707 const char *attrs[] = { "userAccountControl", NULL };
3708 const char *filter;
3709 LDAPMessage *res = NULL;
3710 char *dn = NULL;
3711 uint32 uac = 0;
3713 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3714 samaccountname);
3715 if (filter == NULL) {
3716 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3717 goto out;
3720 status = ads_do_search_all(ads, ads->config.bind_path,
3721 LDAP_SCOPE_SUBTREE,
3722 filter, attrs, &res);
3724 if (!ADS_ERR_OK(status)) {
3725 goto out;
3728 if (ads_count_replies(ads, res) != 1) {
3729 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3730 goto out;
3733 dn = ads_get_dn(ads, talloc_tos(), res);
3734 if (dn == NULL) {
3735 status = ADS_ERROR(LDAP_NO_MEMORY);
3736 goto out;
3739 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3740 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3741 goto out;
3744 if (uac_ret) {
3745 *uac_ret = uac;
3748 if (dn_ret) {
3749 *dn_ret = talloc_strdup(mem_ctx, dn);
3750 if (!*dn_ret) {
3751 status = ADS_ERROR(LDAP_NO_MEMORY);
3752 goto out;
3755 out:
3756 TALLOC_FREE(dn);
3757 ads_msgfree(ads, res);
3759 return status;
3763 * find our configuration path
3764 * @param ads connection to ads server
3765 * @param mem_ctx Pointer to talloc context
3766 * @param config_path Pointer to the config path
3767 * @return status of search
3769 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3770 TALLOC_CTX *mem_ctx,
3771 char **config_path)
3773 ADS_STATUS status;
3774 LDAPMessage *res = NULL;
3775 const char *config_context = NULL;
3776 const char *attrs[] = { "configurationNamingContext", NULL };
3778 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3779 "(objectclass=*)", attrs, &res);
3780 if (!ADS_ERR_OK(status)) {
3781 return status;
3784 config_context = ads_pull_string(ads, mem_ctx, res,
3785 "configurationNamingContext");
3786 ads_msgfree(ads, res);
3787 if (!config_context) {
3788 return ADS_ERROR(LDAP_NO_MEMORY);
3791 if (config_path) {
3792 *config_path = talloc_strdup(mem_ctx, config_context);
3793 if (!*config_path) {
3794 return ADS_ERROR(LDAP_NO_MEMORY);
3798 return ADS_ERROR(LDAP_SUCCESS);
3802 * find the displayName of an extended right
3803 * @param ads connection to ads server
3804 * @param config_path The config path
3805 * @param mem_ctx Pointer to talloc context
3806 * @param GUID struct of the rightsGUID
3807 * @return status of search
3809 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3810 const char *config_path,
3811 TALLOC_CTX *mem_ctx,
3812 const struct GUID *rights_guid)
3814 ADS_STATUS rc;
3815 LDAPMessage *res = NULL;
3816 char *expr = NULL;
3817 const char *attrs[] = { "displayName", NULL };
3818 const char *result = NULL;
3819 const char *path;
3821 if (!ads || !mem_ctx || !rights_guid) {
3822 goto done;
3825 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3826 GUID_string(mem_ctx, rights_guid));
3827 if (!expr) {
3828 goto done;
3831 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3832 if (!path) {
3833 goto done;
3836 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3837 expr, attrs, &res);
3838 if (!ADS_ERR_OK(rc)) {
3839 goto done;
3842 if (ads_count_replies(ads, res) != 1) {
3843 goto done;
3846 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3848 done:
3849 ads_msgfree(ads, res);
3850 return result;
3854 * verify or build and verify an account ou
3855 * @param mem_ctx Pointer to talloc context
3856 * @param ads connection to ads server
3857 * @param account_ou
3858 * @return status of search
3861 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3862 ADS_STRUCT *ads,
3863 const char **account_ou)
3865 char **exploded_dn;
3866 const char *name;
3867 char *ou_string;
3869 exploded_dn = ldap_explode_dn(*account_ou, 0);
3870 if (exploded_dn) {
3871 ldap_value_free(exploded_dn);
3872 return ADS_SUCCESS;
3875 ou_string = ads_ou_string(ads, *account_ou);
3876 if (!ou_string) {
3877 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3880 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3881 ads->config.bind_path);
3882 SAFE_FREE(ou_string);
3884 if (!name) {
3885 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3888 exploded_dn = ldap_explode_dn(name, 0);
3889 if (!exploded_dn) {
3890 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3892 ldap_value_free(exploded_dn);
3894 *account_ou = name;
3895 return ADS_SUCCESS;
3898 #endif