s3: fix calculation of st_blocks in streams_xattr
[Samba/gbeck.git] / source3 / libads / ldap.c
blob98da8ff4848b8d1766d72ecb50ffb35214131c99
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 "libads/sitename_cache.h"
26 #include "libads/cldap.h"
28 #ifdef HAVE_LDAP
30 /**
31 * @file ldap.c
32 * @brief basic ldap client-side routines for ads server communications
34 * The routines contained here should do the necessary ldap calls for
35 * ads setups.
37 * Important note: attribute names passed into ads_ routines must
38 * already be in UTF-8 format. We do not convert them because in almost
39 * all cases, they are just ascii (which is represented with the same
40 * codepoints in UTF-8). This may have to change at some point
41 **/
44 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
46 static SIG_ATOMIC_T gotalarm;
48 /***************************************************************
49 Signal function to tell us we timed out.
50 ****************************************************************/
52 static void gotalarm_sig(int signum)
54 gotalarm = 1;
57 LDAP *ldap_open_with_timeout(const char *server, int port, unsigned int to)
59 LDAP *ldp = NULL;
62 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
63 "%u seconds\n", server, port, to));
65 /* Setup timeout */
66 gotalarm = 0;
67 CatchSignal(SIGALRM, gotalarm_sig);
68 alarm(to);
69 /* End setup timeout. */
71 ldp = ldap_open(server, port);
73 if (ldp == NULL) {
74 DEBUG(2,("Could not open connection to LDAP server %s:%d: %s\n",
75 server, port, strerror(errno)));
76 } else {
77 DEBUG(10, ("Connected to LDAP server '%s:%d'\n", server, port));
80 /* Teardown timeout. */
81 CatchSignal(SIGALRM, SIG_IGN);
82 alarm(0);
84 return ldp;
87 static int ldap_search_with_timeout(LDAP *ld,
88 LDAP_CONST char *base,
89 int scope,
90 LDAP_CONST char *filter,
91 char **attrs,
92 int attrsonly,
93 LDAPControl **sctrls,
94 LDAPControl **cctrls,
95 int sizelimit,
96 LDAPMessage **res )
98 struct timeval timeout;
99 int result;
101 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
102 timeout.tv_sec = lp_ldap_timeout();
103 timeout.tv_usec = 0;
105 /* Setup alarm timeout.... Do we need both of these ? JRA. */
106 gotalarm = 0;
107 CatchSignal(SIGALRM, gotalarm_sig);
108 alarm(lp_ldap_timeout());
109 /* End setup timeout. */
111 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
112 attrsonly, sctrls, cctrls, &timeout,
113 sizelimit, res);
115 /* Teardown timeout. */
116 CatchSignal(SIGALRM, SIG_IGN);
117 alarm(0);
119 if (gotalarm != 0)
120 return LDAP_TIMELIMIT_EXCEEDED;
123 * A bug in OpenLDAP means ldap_search_ext_s can return
124 * LDAP_SUCCESS but with a NULL res pointer. Cope with
125 * this. See bug #6279 for details. JRA.
128 if (*res == NULL) {
129 return LDAP_TIMELIMIT_EXCEEDED;
132 return result;
135 /**********************************************
136 Do client and server sitename match ?
137 **********************************************/
139 bool ads_sitename_match(ADS_STRUCT *ads)
141 if (ads->config.server_site_name == NULL &&
142 ads->config.client_site_name == NULL ) {
143 DEBUG(10,("ads_sitename_match: both null\n"));
144 return True;
146 if (ads->config.server_site_name &&
147 ads->config.client_site_name &&
148 strequal(ads->config.server_site_name,
149 ads->config.client_site_name)) {
150 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
151 return True;
153 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
154 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
155 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
156 return False;
159 /**********************************************
160 Is this the closest DC ?
161 **********************************************/
163 bool ads_closest_dc(ADS_STRUCT *ads)
165 if (ads->config.flags & NBT_SERVER_CLOSEST) {
166 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
167 return True;
170 /* not sure if this can ever happen */
171 if (ads_sitename_match(ads)) {
172 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
173 return True;
176 if (ads->config.client_site_name == NULL) {
177 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
178 return True;
181 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
182 ads->config.ldap_server_name));
184 return False;
189 try a connection to a given ldap server, returning True and setting the servers IP
190 in the ads struct if successful
192 static bool ads_try_connect(ADS_STRUCT *ads, const char *server, bool gc)
194 char *srv;
195 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
196 TALLOC_CTX *frame = talloc_stackframe();
197 bool ret = false;
199 if (!server || !*server) {
200 TALLOC_FREE(frame);
201 return False;
204 if (!is_ipaddress(server)) {
205 struct sockaddr_storage ss;
206 char addr[INET6_ADDRSTRLEN];
208 if (!resolve_name(server, &ss, 0x20, true)) {
209 DEBUG(5,("ads_try_connect: unable to resolve name %s\n",
210 server ));
211 TALLOC_FREE(frame);
212 return false;
214 print_sockaddr(addr, sizeof(addr), &ss);
215 srv = talloc_strdup(frame, addr);
216 } else {
217 /* this copes with inet_ntoa brokenness */
218 srv = talloc_strdup(frame, server);
221 if (!srv) {
222 TALLOC_FREE(frame);
223 return false;
226 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
227 srv, ads->server.realm));
229 ZERO_STRUCT( cldap_reply );
231 if ( !ads_cldap_netlogon_5(frame, srv, ads->server.realm, &cldap_reply ) ) {
232 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", srv));
233 ret = false;
234 goto out;
237 /* Check the CLDAP reply flags */
239 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
240 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
241 srv));
242 ret = false;
243 goto out;
246 /* Fill in the ads->config values */
248 SAFE_FREE(ads->config.realm);
249 SAFE_FREE(ads->config.bind_path);
250 SAFE_FREE(ads->config.ldap_server_name);
251 SAFE_FREE(ads->config.server_site_name);
252 SAFE_FREE(ads->config.client_site_name);
253 SAFE_FREE(ads->server.workgroup);
255 ads->config.flags = cldap_reply.server_type;
256 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
257 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
258 strupper_m(ads->config.realm);
259 ads->config.bind_path = ads_build_dn(ads->config.realm);
260 if (*cldap_reply.server_site) {
261 ads->config.server_site_name =
262 SMB_STRDUP(cldap_reply.server_site);
264 if (*cldap_reply.client_site) {
265 ads->config.client_site_name =
266 SMB_STRDUP(cldap_reply.client_site);
268 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
270 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
271 if (!interpret_string_addr(&ads->ldap.ss, srv, 0)) {
272 DEBUG(1,("ads_try_connect: unable to convert %s "
273 "to an address\n",
274 srv));
275 ret = false;
276 goto out;
279 /* Store our site name. */
280 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
281 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
283 ret = true;
285 out:
287 TALLOC_FREE(frame);
288 return ret;
291 /**********************************************************************
292 Try to find an AD dc using our internal name resolution routines
293 Try the realm first and then then workgroup name if netbios is not
294 disabled
295 **********************************************************************/
297 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
299 const char *c_domain;
300 const char *c_realm;
301 int count, i=0;
302 struct ip_service *ip_list;
303 const char *realm;
304 const char *domain;
305 bool got_realm = False;
306 bool use_own_domain = False;
307 char *sitename;
308 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
310 /* if the realm and workgroup are both empty, assume they are ours */
312 /* realm */
313 c_realm = ads->server.realm;
315 if ( !c_realm || !*c_realm ) {
316 /* special case where no realm and no workgroup means our own */
317 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
318 use_own_domain = True;
319 c_realm = lp_realm();
323 if (c_realm && *c_realm)
324 got_realm = True;
326 /* we need to try once with the realm name and fallback to the
327 netbios domain name if we fail (if netbios has not been disabled */
329 if ( !got_realm && !lp_disable_netbios() ) {
330 c_realm = ads->server.workgroup;
331 if (!c_realm || !*c_realm) {
332 if ( use_own_domain )
333 c_realm = lp_workgroup();
337 if ( !c_realm || !*c_realm ) {
338 DEBUG(0,("ads_find_dc: no realm or workgroup! Don't know what to do\n"));
339 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
342 if ( use_own_domain ) {
343 c_domain = lp_workgroup();
344 } else {
345 c_domain = ads->server.workgroup;
348 realm = c_realm;
349 domain = c_domain;
352 * In case of LDAP we use get_dc_name() as that
353 * creates the custom krb5.conf file
355 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
356 fstring srv_name;
357 struct sockaddr_storage ip_out;
359 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
360 (got_realm ? "realm" : "domain"), realm));
362 if (get_dc_name(domain, realm, srv_name, &ip_out)) {
364 * we call ads_try_connect() to fill in the
365 * ads->config details
367 if (ads_try_connect(ads, srv_name, false)) {
368 return NT_STATUS_OK;
372 return NT_STATUS_NO_LOGON_SERVERS;
375 sitename = sitename_fetch(realm);
377 again:
379 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
380 (got_realm ? "realm" : "domain"), realm));
382 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
383 if (!NT_STATUS_IS_OK(status)) {
384 /* fall back to netbios if we can */
385 if ( got_realm && !lp_disable_netbios() ) {
386 got_realm = False;
387 goto again;
390 SAFE_FREE(sitename);
391 return status;
394 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
395 for ( i=0; i<count; i++ ) {
396 char server[INET6_ADDRSTRLEN];
398 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
400 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
401 continue;
403 if (!got_realm) {
404 /* realm in this case is a workgroup name. We need
405 to ignore any IP addresses in the negative connection
406 cache that match ip addresses returned in the ad realm
407 case. It sucks that I have to reproduce the logic above... */
408 c_realm = ads->server.realm;
409 if ( !c_realm || !*c_realm ) {
410 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
411 c_realm = lp_realm();
414 if (c_realm && *c_realm &&
415 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
416 /* Ensure we add the workgroup name for this
417 IP address as negative too. */
418 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
419 continue;
423 if ( ads_try_connect(ads, server, false) ) {
424 SAFE_FREE(ip_list);
425 SAFE_FREE(sitename);
426 return NT_STATUS_OK;
429 /* keep track of failures */
430 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
433 SAFE_FREE(ip_list);
435 /* In case we failed to contact one of our closest DC on our site we
436 * need to try to find another DC, retry with a site-less SRV DNS query
437 * - Guenther */
439 if (sitename) {
440 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
441 "trying to find another DC\n", sitename));
442 SAFE_FREE(sitename);
443 namecache_delete(realm, 0x1C);
444 goto again;
447 return NT_STATUS_NO_LOGON_SERVERS;
450 /*********************************************************************
451 *********************************************************************/
453 static NTSTATUS ads_lookup_site(void)
455 ADS_STRUCT *ads = NULL;
456 ADS_STATUS ads_status;
457 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
459 ads = ads_init(lp_realm(), NULL, NULL);
460 if (!ads) {
461 return NT_STATUS_NO_MEMORY;
464 /* The NO_BIND here will find a DC and set the client site
465 but not establish the TCP connection */
467 ads->auth.flags = ADS_AUTH_NO_BIND;
468 ads_status = ads_connect(ads);
469 if (!ADS_ERR_OK(ads_status)) {
470 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
471 ads_errstr(ads_status)));
473 nt_status = ads_ntstatus(ads_status);
475 if (ads) {
476 ads_destroy(&ads);
479 return nt_status;
482 /*********************************************************************
483 *********************************************************************/
485 static const char* host_dns_domain(const char *fqdn)
487 const char *p = fqdn;
489 /* go to next char following '.' */
491 if ((p = strchr_m(fqdn, '.')) != NULL) {
492 p++;
495 return p;
500 * Connect to the Global Catalog server
501 * @param ads Pointer to an existing ADS_STRUCT
502 * @return status of connection
504 * Simple wrapper around ads_connect() that fills in the
505 * GC ldap server information
508 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
510 TALLOC_CTX *frame = talloc_stackframe();
511 struct dns_rr_srv *gcs_list;
512 int num_gcs;
513 char *realm = ads->server.realm;
514 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
515 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
516 int i;
517 bool done = false;
518 char *sitename = NULL;
520 if (!realm)
521 realm = lp_realm();
523 if ((sitename = sitename_fetch(realm)) == NULL) {
524 ads_lookup_site();
525 sitename = sitename_fetch(realm);
528 do {
529 /* We try once with a sitename and once without
530 (unless we don't have a sitename and then we're
531 done */
533 if (sitename == NULL)
534 done = true;
536 nt_status = ads_dns_query_gcs(frame, realm, sitename,
537 &gcs_list, &num_gcs);
539 SAFE_FREE(sitename);
541 if (!NT_STATUS_IS_OK(nt_status)) {
542 ads_status = ADS_ERROR_NT(nt_status);
543 goto done;
546 /* Loop until we get a successful connection or have gone
547 through them all. When connecting a GC server, make sure that
548 the realm is the server's DNS name and not the forest root */
550 for (i=0; i<num_gcs; i++) {
551 ads->server.gc = true;
552 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
553 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
554 ads_status = ads_connect(ads);
555 if (ADS_ERR_OK(ads_status)) {
556 /* Reset the bind_dn to "". A Global Catalog server
557 may host multiple domain trees in a forest.
558 Windows 2003 GC server will accept "" as the search
559 path to imply search all domain trees in the forest */
561 SAFE_FREE(ads->config.bind_path);
562 ads->config.bind_path = SMB_STRDUP("");
565 goto done;
567 SAFE_FREE(ads->server.ldap_server);
568 SAFE_FREE(ads->server.realm);
571 TALLOC_FREE(gcs_list);
572 num_gcs = 0;
573 } while (!done);
575 done:
576 SAFE_FREE(sitename);
577 talloc_destroy(frame);
579 return ads_status;
584 * Connect to the LDAP server
585 * @param ads Pointer to an existing ADS_STRUCT
586 * @return status of connection
588 ADS_STATUS ads_connect(ADS_STRUCT *ads)
590 int version = LDAP_VERSION3;
591 ADS_STATUS status;
592 NTSTATUS ntstatus;
593 char addr[INET6_ADDRSTRLEN];
595 ZERO_STRUCT(ads->ldap);
596 ads->ldap.last_attempt = time(NULL);
597 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
599 /* try with a user specified server */
601 if (DEBUGLEVEL >= 11) {
602 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
603 DEBUG(11,("ads_connect: entering\n"));
604 DEBUGADD(11,("%s\n", s));
605 TALLOC_FREE(s);
608 if (ads->server.ldap_server)
610 if (ads_try_connect(ads, ads->server.ldap_server, ads->server.gc)) {
611 goto got_connection;
614 /* The choice of which GC use is handled one level up in
615 ads_connect_gc(). If we continue on from here with
616 ads_find_dc() we will get GC searches on port 389 which
617 doesn't work. --jerry */
619 if (ads->server.gc == true) {
620 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
624 ntstatus = ads_find_dc(ads);
625 if (NT_STATUS_IS_OK(ntstatus)) {
626 goto got_connection;
629 status = ADS_ERROR_NT(ntstatus);
630 goto out;
632 got_connection:
634 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
635 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
637 if (!ads->auth.user_name) {
638 /* Must use the userPrincipalName value here or sAMAccountName
639 and not servicePrincipalName; found by Guenther Deschner */
641 if (asprintf(&ads->auth.user_name, "%s$", global_myname() ) == -1) {
642 DEBUG(0,("ads_connect: asprintf fail.\n"));
643 ads->auth.user_name = NULL;
647 if (!ads->auth.realm) {
648 ads->auth.realm = SMB_STRDUP(ads->config.realm);
651 if (!ads->auth.kdc_server) {
652 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
653 ads->auth.kdc_server = SMB_STRDUP(addr);
656 #if KRB5_DNS_HACK
657 /* this is a really nasty hack to avoid ADS DNS problems. It needs a patch
658 to MIT kerberos to work (tridge) */
660 char *env = NULL;
661 if (asprintf(&env, "KRB5_KDC_ADDRESS_%s", ads->config.realm) > 0) {
662 setenv(env, ads->auth.kdc_server, 1);
663 free(env);
666 #endif
668 /* If the caller() requested no LDAP bind, then we are done */
670 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
671 status = ADS_SUCCESS;
672 goto out;
675 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
676 if (!ads->ldap.mem_ctx) {
677 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
678 goto out;
681 /* Otherwise setup the TCP LDAP session */
683 ads->ldap.ld = ldap_open_with_timeout(ads->config.ldap_server_name,
684 ads->ldap.port, lp_ldap_timeout());
685 if (ads->ldap.ld == NULL) {
686 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
687 goto out;
689 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
691 /* cache the successful connection for workgroup and realm */
692 if (ads_closest_dc(ads)) {
693 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
694 saf_store( ads->server.realm, ads->config.ldap_server_name);
697 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
699 if ( lp_ldap_ssl_ads() ) {
700 status = ADS_ERROR(smb_ldap_start_tls(ads->ldap.ld, version));
701 if (!ADS_ERR_OK(status)) {
702 goto out;
706 /* fill in the current time and offsets */
708 status = ads_current_time( ads );
709 if ( !ADS_ERR_OK(status) ) {
710 goto out;
713 /* Now do the bind */
715 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
716 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
717 goto out;
720 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
721 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
722 goto out;
725 status = ads_sasl_bind(ads);
727 out:
728 if (DEBUGLEVEL >= 11) {
729 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
730 DEBUG(11,("ads_connect: leaving with: %s\n",
731 ads_errstr(status)));
732 DEBUGADD(11,("%s\n", s));
733 TALLOC_FREE(s);
736 return status;
740 * Connect to the LDAP server using given credentials
741 * @param ads Pointer to an existing ADS_STRUCT
742 * @return status of connection
744 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
746 ads->auth.flags |= ADS_AUTH_USER_CREDS;
748 return ads_connect(ads);
752 * Disconnect the LDAP server
753 * @param ads Pointer to an existing ADS_STRUCT
755 void ads_disconnect(ADS_STRUCT *ads)
757 if (ads->ldap.ld) {
758 ldap_unbind(ads->ldap.ld);
759 ads->ldap.ld = NULL;
761 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
762 ads->ldap.wrap_ops->disconnect(ads);
764 if (ads->ldap.mem_ctx) {
765 talloc_free(ads->ldap.mem_ctx);
767 ZERO_STRUCT(ads->ldap);
771 Duplicate a struct berval into talloc'ed memory
773 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
775 struct berval *value;
777 if (!in_val) return NULL;
779 value = TALLOC_ZERO_P(ctx, struct berval);
780 if (value == NULL)
781 return NULL;
782 if (in_val->bv_len == 0) return value;
784 value->bv_len = in_val->bv_len;
785 value->bv_val = (char *)TALLOC_MEMDUP(ctx, in_val->bv_val,
786 in_val->bv_len);
787 return value;
791 Make a values list out of an array of (struct berval *)
793 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
794 const struct berval **in_vals)
796 struct berval **values;
797 int i;
799 if (!in_vals) return NULL;
800 for (i=0; in_vals[i]; i++)
801 ; /* count values */
802 values = TALLOC_ZERO_ARRAY(ctx, struct berval *, i+1);
803 if (!values) return NULL;
805 for (i=0; in_vals[i]; i++) {
806 values[i] = dup_berval(ctx, in_vals[i]);
808 return values;
812 UTF8-encode a values list out of an array of (char *)
814 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
816 char **values;
817 int i;
818 size_t size;
820 if (!in_vals) return NULL;
821 for (i=0; in_vals[i]; i++)
822 ; /* count values */
823 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
824 if (!values) return NULL;
826 for (i=0; in_vals[i]; i++) {
827 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
828 TALLOC_FREE(values);
829 return NULL;
832 return values;
836 Pull a (char *) array out of a UTF8-encoded values list
838 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
840 char **values;
841 int i;
842 size_t converted_size;
844 if (!in_vals) return NULL;
845 for (i=0; in_vals[i]; i++)
846 ; /* count values */
847 values = TALLOC_ZERO_ARRAY(ctx, char *, i+1);
848 if (!values) return NULL;
850 for (i=0; in_vals[i]; i++) {
851 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
852 &converted_size)) {
853 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
854 "%s", strerror(errno)));
857 return values;
861 * Do a search with paged results. cookie must be null on the first
862 * call, and then returned on each subsequent call. It will be null
863 * again when the entire search is complete
864 * @param ads connection to ads server
865 * @param bind_path Base dn for the search
866 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
867 * @param expr Search expression - specified in local charset
868 * @param attrs Attributes to retrieve - specified in utf8 or ascii
869 * @param res ** which will contain results - free res* with ads_msgfree()
870 * @param count Number of entries retrieved on this page
871 * @param cookie The paged results cookie to be returned on subsequent calls
872 * @return status of search
874 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
875 const char *bind_path,
876 int scope, const char *expr,
877 const char **attrs, void *args,
878 LDAPMessage **res,
879 int *count, struct berval **cookie)
881 int rc, i, version;
882 char *utf8_expr, *utf8_path, **search_attrs = NULL;
883 size_t converted_size;
884 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
885 BerElement *cookie_be = NULL;
886 struct berval *cookie_bv= NULL;
887 BerElement *ext_be = NULL;
888 struct berval *ext_bv= NULL;
890 TALLOC_CTX *ctx;
891 ads_control *external_control = (ads_control *) args;
893 *res = NULL;
895 if (!(ctx = talloc_init("ads_do_paged_search_args")))
896 return ADS_ERROR(LDAP_NO_MEMORY);
898 /* 0 means the conversion worked but the result was empty
899 so we only fail if it's -1. In any case, it always
900 at least nulls out the dest */
901 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
902 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
904 rc = LDAP_NO_MEMORY;
905 goto done;
908 if (!attrs || !(*attrs))
909 search_attrs = NULL;
910 else {
911 /* This would be the utf8-encoded version...*/
912 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
913 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
914 rc = LDAP_NO_MEMORY;
915 goto done;
919 /* Paged results only available on ldap v3 or later */
920 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
921 if (version < LDAP_VERSION3) {
922 rc = LDAP_NOT_SUPPORTED;
923 goto done;
926 cookie_be = ber_alloc_t(LBER_USE_DER);
927 if (*cookie) {
928 ber_printf(cookie_be, "{iO}", (ber_int_t) 1000, *cookie);
929 ber_bvfree(*cookie); /* don't need it from last time */
930 *cookie = NULL;
931 } else {
932 ber_printf(cookie_be, "{io}", (ber_int_t) 1000, "", 0);
934 ber_flatten(cookie_be, &cookie_bv);
935 PagedResults.ldctl_oid = CONST_DISCARD(char *, ADS_PAGE_CTL_OID);
936 PagedResults.ldctl_iscritical = (char) 1;
937 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
938 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
940 NoReferrals.ldctl_oid = CONST_DISCARD(char *, ADS_NO_REFERRALS_OID);
941 NoReferrals.ldctl_iscritical = (char) 0;
942 NoReferrals.ldctl_value.bv_len = 0;
943 NoReferrals.ldctl_value.bv_val = CONST_DISCARD(char *, "");
945 if (external_control &&
946 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
947 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
949 ExternalCtrl.ldctl_oid = CONST_DISCARD(char *, external_control->control);
950 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
952 /* win2k does not accept a ldctl_value beeing passed in */
954 if (external_control->val != 0) {
956 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
957 rc = LDAP_NO_MEMORY;
958 goto done;
961 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
962 rc = LDAP_NO_MEMORY;
963 goto done;
965 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
966 rc = LDAP_NO_MEMORY;
967 goto done;
970 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
971 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
973 } else {
974 ExternalCtrl.ldctl_value.bv_len = 0;
975 ExternalCtrl.ldctl_value.bv_val = NULL;
978 controls[0] = &NoReferrals;
979 controls[1] = &PagedResults;
980 controls[2] = &ExternalCtrl;
981 controls[3] = NULL;
983 } else {
984 controls[0] = &NoReferrals;
985 controls[1] = &PagedResults;
986 controls[2] = NULL;
989 /* we need to disable referrals as the openldap libs don't
990 handle them and paged results at the same time. Using them
991 together results in the result record containing the server
992 page control being removed from the result list (tridge/jmcd)
994 leaving this in despite the control that says don't generate
995 referrals, in case the server doesn't support it (jmcd)
997 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
999 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1000 search_attrs, 0, controls,
1001 NULL, LDAP_NO_LIMIT,
1002 (LDAPMessage **)res);
1004 ber_free(cookie_be, 1);
1005 ber_bvfree(cookie_bv);
1007 if (rc) {
1008 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1009 ldap_err2string(rc)));
1010 goto done;
1013 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1014 NULL, &rcontrols, 0);
1016 if (!rcontrols) {
1017 goto done;
1020 for (i=0; rcontrols[i]; i++) {
1021 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1022 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1023 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1024 &cookie_bv);
1025 /* the berval is the cookie, but must be freed when
1026 it is all done */
1027 if (cookie_bv->bv_len) /* still more to do */
1028 *cookie=ber_bvdup(cookie_bv);
1029 else
1030 *cookie=NULL;
1031 ber_bvfree(cookie_bv);
1032 ber_free(cookie_be, 1);
1033 break;
1036 ldap_controls_free(rcontrols);
1038 done:
1039 talloc_destroy(ctx);
1041 if (ext_be) {
1042 ber_free(ext_be, 1);
1045 if (ext_bv) {
1046 ber_bvfree(ext_bv);
1049 /* if/when we decide to utf8-encode attrs, take out this next line */
1050 TALLOC_FREE(search_attrs);
1052 return ADS_ERROR(rc);
1055 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1056 int scope, const char *expr,
1057 const char **attrs, LDAPMessage **res,
1058 int *count, struct berval **cookie)
1060 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1065 * Get all results for a search. This uses ads_do_paged_search() to return
1066 * all entries in a large search.
1067 * @param ads connection to ads server
1068 * @param bind_path Base dn for the search
1069 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1070 * @param expr Search expression
1071 * @param attrs Attributes to retrieve
1072 * @param res ** which will contain results - free res* with ads_msgfree()
1073 * @return status of search
1075 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1076 int scope, const char *expr,
1077 const char **attrs, void *args,
1078 LDAPMessage **res)
1080 struct berval *cookie = NULL;
1081 int count = 0;
1082 ADS_STATUS status;
1084 *res = NULL;
1085 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1086 &count, &cookie);
1088 if (!ADS_ERR_OK(status))
1089 return status;
1091 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1092 while (cookie) {
1093 LDAPMessage *res2 = NULL;
1094 ADS_STATUS status2;
1095 LDAPMessage *msg, *next;
1097 status2 = ads_do_paged_search_args(ads, bind_path, scope, expr,
1098 attrs, args, &res2, &count, &cookie);
1100 if (!ADS_ERR_OK(status2)) break;
1102 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1103 that this works on all ldap libs, but I have only tested with openldap */
1104 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1105 next = ads_next_message(ads, msg);
1106 ldap_add_result_entry((LDAPMessage **)res, msg);
1108 /* note that we do not free res2, as the memory is now
1109 part of the main returned list */
1111 #else
1112 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1113 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1114 #endif
1116 return status;
1119 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1120 int scope, const char *expr,
1121 const char **attrs, LDAPMessage **res)
1123 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1126 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1127 int scope, const char *expr,
1128 const char **attrs, uint32 sd_flags,
1129 LDAPMessage **res)
1131 ads_control args;
1133 args.control = ADS_SD_FLAGS_OID;
1134 args.val = sd_flags;
1135 args.critical = True;
1137 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1142 * Run a function on all results for a search. Uses ads_do_paged_search() and
1143 * runs the function as each page is returned, using ads_process_results()
1144 * @param ads connection to ads server
1145 * @param bind_path Base dn for the search
1146 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1147 * @param expr Search expression - specified in local charset
1148 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1149 * @param fn Function which takes attr name, values list, and data_area
1150 * @param data_area Pointer which is passed to function on each call
1151 * @return status of search
1153 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1154 int scope, const char *expr, const char **attrs,
1155 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1156 void *data_area)
1158 struct berval *cookie = NULL;
1159 int count = 0;
1160 ADS_STATUS status;
1161 LDAPMessage *res;
1163 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1164 &count, &cookie);
1166 if (!ADS_ERR_OK(status)) return status;
1168 ads_process_results(ads, res, fn, data_area);
1169 ads_msgfree(ads, res);
1171 while (cookie) {
1172 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1173 &res, &count, &cookie);
1175 if (!ADS_ERR_OK(status)) break;
1177 ads_process_results(ads, res, fn, data_area);
1178 ads_msgfree(ads, res);
1181 return status;
1185 * Do a search with a timeout.
1186 * @param ads connection to ads server
1187 * @param bind_path Base dn for the search
1188 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1189 * @param expr Search expression
1190 * @param attrs Attributes to retrieve
1191 * @param res ** which will contain results - free res* with ads_msgfree()
1192 * @return status of search
1194 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1195 const char *expr,
1196 const char **attrs, LDAPMessage **res)
1198 int rc;
1199 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1200 size_t converted_size;
1201 TALLOC_CTX *ctx;
1203 *res = NULL;
1204 if (!(ctx = talloc_init("ads_do_search"))) {
1205 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1206 return ADS_ERROR(LDAP_NO_MEMORY);
1209 /* 0 means the conversion worked but the result was empty
1210 so we only fail if it's negative. In any case, it always
1211 at least nulls out the dest */
1212 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1213 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1215 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1216 rc = LDAP_NO_MEMORY;
1217 goto done;
1220 if (!attrs || !(*attrs))
1221 search_attrs = NULL;
1222 else {
1223 /* This would be the utf8-encoded version...*/
1224 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1225 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1227 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1228 rc = LDAP_NO_MEMORY;
1229 goto done;
1233 /* see the note in ads_do_paged_search - we *must* disable referrals */
1234 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1236 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1237 search_attrs, 0, NULL, NULL,
1238 LDAP_NO_LIMIT,
1239 (LDAPMessage **)res);
1241 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1242 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1243 rc = 0;
1246 done:
1247 talloc_destroy(ctx);
1248 /* if/when we decide to utf8-encode attrs, take out this next line */
1249 TALLOC_FREE(search_attrs);
1250 return ADS_ERROR(rc);
1253 * Do a general ADS search
1254 * @param ads connection to ads server
1255 * @param res ** which will contain results - free res* with ads_msgfree()
1256 * @param expr Search expression
1257 * @param attrs Attributes to retrieve
1258 * @return status of search
1260 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1261 const char *expr, const char **attrs)
1263 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1264 expr, attrs, res);
1268 * Do a search on a specific DistinguishedName
1269 * @param ads connection to ads server
1270 * @param res ** which will contain results - free res* with ads_msgfree()
1271 * @param dn DistinguishName to search
1272 * @param attrs Attributes to retrieve
1273 * @return status of search
1275 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1276 const char *dn, const char **attrs)
1278 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1279 attrs, res);
1283 * Free up memory from a ads_search
1284 * @param ads connection to ads server
1285 * @param msg Search results to free
1287 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1289 if (!msg) return;
1290 ldap_msgfree(msg);
1294 * Get a dn from search results
1295 * @param ads connection to ads server
1296 * @param msg Search result
1297 * @return dn string
1299 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1301 char *utf8_dn, *unix_dn;
1302 size_t converted_size;
1304 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1306 if (!utf8_dn) {
1307 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1308 return NULL;
1311 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1312 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1313 utf8_dn ));
1314 return NULL;
1316 ldap_memfree(utf8_dn);
1317 return unix_dn;
1321 * Get the parent from a dn
1322 * @param dn the dn to return the parent from
1323 * @return parent dn string
1325 char *ads_parent_dn(const char *dn)
1327 char *p;
1329 if (dn == NULL) {
1330 return NULL;
1333 p = strchr(dn, ',');
1335 if (p == NULL) {
1336 return NULL;
1339 return p+1;
1343 * Find a machine account given a hostname
1344 * @param ads connection to ads server
1345 * @param res ** which will contain results - free res* with ads_msgfree()
1346 * @param host Hostname to search for
1347 * @return status of search
1349 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1350 const char *machine)
1352 ADS_STATUS status;
1353 char *expr;
1354 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1356 *res = NULL;
1358 /* the easiest way to find a machine account anywhere in the tree
1359 is to look for hostname$ */
1360 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1361 DEBUG(1, ("asprintf failed!\n"));
1362 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1365 status = ads_search(ads, res, expr, attrs);
1366 SAFE_FREE(expr);
1367 return status;
1371 * Initialize a list of mods to be used in a modify request
1372 * @param ctx An initialized TALLOC_CTX
1373 * @return allocated ADS_MODLIST
1375 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1377 #define ADS_MODLIST_ALLOC_SIZE 10
1378 LDAPMod **mods;
1380 if ((mods = TALLOC_ZERO_ARRAY(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1381 /* -1 is safety to make sure we don't go over the end.
1382 need to reset it to NULL before doing ldap modify */
1383 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1385 return (ADS_MODLIST)mods;
1390 add an attribute to the list, with values list already constructed
1392 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1393 int mod_op, const char *name,
1394 const void *_invals)
1396 const void **invals = (const void **)_invals;
1397 int curmod;
1398 LDAPMod **modlist = (LDAPMod **) *mods;
1399 struct berval **ber_values = NULL;
1400 char **char_values = NULL;
1402 if (!invals) {
1403 mod_op = LDAP_MOD_DELETE;
1404 } else {
1405 if (mod_op & LDAP_MOD_BVALUES)
1406 ber_values = ads_dup_values(ctx,
1407 (const struct berval **)invals);
1408 else
1409 char_values = ads_push_strvals(ctx,
1410 (const char **) invals);
1413 /* find the first empty slot */
1414 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1415 curmod++);
1416 if (modlist[curmod] == (LDAPMod *) -1) {
1417 if (!(modlist = TALLOC_REALLOC_ARRAY(ctx, modlist, LDAPMod *,
1418 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1419 return ADS_ERROR(LDAP_NO_MEMORY);
1420 memset(&modlist[curmod], 0,
1421 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1422 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1423 *mods = (ADS_MODLIST)modlist;
1426 if (!(modlist[curmod] = TALLOC_ZERO_P(ctx, LDAPMod)))
1427 return ADS_ERROR(LDAP_NO_MEMORY);
1428 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1429 if (mod_op & LDAP_MOD_BVALUES) {
1430 modlist[curmod]->mod_bvalues = ber_values;
1431 } else if (mod_op & LDAP_MOD_DELETE) {
1432 modlist[curmod]->mod_values = NULL;
1433 } else {
1434 modlist[curmod]->mod_values = char_values;
1437 modlist[curmod]->mod_op = mod_op;
1438 return ADS_ERROR(LDAP_SUCCESS);
1442 * Add a single string value to a mod list
1443 * @param ctx An initialized TALLOC_CTX
1444 * @param mods An initialized ADS_MODLIST
1445 * @param name The attribute name to add
1446 * @param val The value to add - NULL means DELETE
1447 * @return ADS STATUS indicating success of add
1449 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1450 const char *name, const char *val)
1452 const char *values[2];
1454 values[0] = val;
1455 values[1] = NULL;
1457 if (!val)
1458 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1459 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1463 * Add an array of string values to a mod list
1464 * @param ctx An initialized TALLOC_CTX
1465 * @param mods An initialized ADS_MODLIST
1466 * @param name The attribute name to add
1467 * @param vals The array of string values to add - NULL means DELETE
1468 * @return ADS STATUS indicating success of add
1470 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1471 const char *name, const char **vals)
1473 if (!vals)
1474 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1475 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1476 name, (const void **) vals);
1479 #if 0
1481 * Add a single ber-encoded value to a mod list
1482 * @param ctx An initialized TALLOC_CTX
1483 * @param mods An initialized ADS_MODLIST
1484 * @param name The attribute name to add
1485 * @param val The value to add - NULL means DELETE
1486 * @return ADS STATUS indicating success of add
1488 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1489 const char *name, const struct berval *val)
1491 const struct berval *values[2];
1493 values[0] = val;
1494 values[1] = NULL;
1495 if (!val)
1496 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1497 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1498 name, (const void **) values);
1500 #endif
1503 * Perform an ldap modify
1504 * @param ads connection to ads server
1505 * @param mod_dn DistinguishedName to modify
1506 * @param mods list of modifications to perform
1507 * @return status of modify
1509 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1511 int ret,i;
1512 char *utf8_dn = NULL;
1513 size_t converted_size;
1515 this control is needed to modify that contains a currently
1516 non-existent attribute (but allowable for the object) to run
1518 LDAPControl PermitModify = {
1519 CONST_DISCARD(char *, ADS_PERMIT_MODIFY_OID),
1520 {0, NULL},
1521 (char) 1};
1522 LDAPControl *controls[2];
1524 controls[0] = &PermitModify;
1525 controls[1] = NULL;
1527 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1528 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1531 /* find the end of the list, marked by NULL or -1 */
1532 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1533 /* make sure the end of the list is NULL */
1534 mods[i] = NULL;
1535 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1536 (LDAPMod **) mods, controls, NULL);
1537 TALLOC_FREE(utf8_dn);
1538 return ADS_ERROR(ret);
1542 * Perform an ldap add
1543 * @param ads connection to ads server
1544 * @param new_dn DistinguishedName to add
1545 * @param mods list of attributes and values for DN
1546 * @return status of add
1548 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1550 int ret, i;
1551 char *utf8_dn = NULL;
1552 size_t converted_size;
1554 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1555 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1556 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1559 /* find the end of the list, marked by NULL or -1 */
1560 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1561 /* make sure the end of the list is NULL */
1562 mods[i] = NULL;
1564 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1565 TALLOC_FREE(utf8_dn);
1566 return ADS_ERROR(ret);
1570 * Delete a DistinguishedName
1571 * @param ads connection to ads server
1572 * @param new_dn DistinguishedName to delete
1573 * @return status of delete
1575 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1577 int ret;
1578 char *utf8_dn = NULL;
1579 size_t converted_size;
1580 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1581 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1582 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1585 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1586 TALLOC_FREE(utf8_dn);
1587 return ADS_ERROR(ret);
1591 * Build an org unit string
1592 * if org unit is Computers or blank then assume a container, otherwise
1593 * assume a / separated list of organisational units.
1594 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1595 * @param ads connection to ads server
1596 * @param org_unit Organizational unit
1597 * @return org unit string - caller must free
1599 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1601 char *ret = NULL;
1603 if (!org_unit || !*org_unit) {
1605 ret = ads_default_ou_string(ads, WELL_KNOWN_GUID_COMPUTERS);
1607 /* samba4 might not yet respond to a wellknownobject-query */
1608 return ret ? ret : SMB_STRDUP("cn=Computers");
1611 if (strequal(org_unit, "Computers")) {
1612 return SMB_STRDUP("cn=Computers");
1615 /* jmcd: removed "\\" from the separation chars, because it is
1616 needed as an escape for chars like '#' which are valid in an
1617 OU name */
1618 return ads_build_path(org_unit, "/", "ou=", 1);
1622 * Get a org unit string for a well-known GUID
1623 * @param ads connection to ads server
1624 * @param wknguid Well known GUID
1625 * @return org unit string - caller must free
1627 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1629 ADS_STATUS status;
1630 LDAPMessage *res = NULL;
1631 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1632 **bind_dn_exp = NULL;
1633 const char *attrs[] = {"distinguishedName", NULL};
1634 int new_ln, wkn_ln, bind_ln, i;
1636 if (wknguid == NULL) {
1637 return NULL;
1640 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1641 DEBUG(1, ("asprintf failed!\n"));
1642 return NULL;
1645 status = ads_search_dn(ads, &res, base, attrs);
1646 if (!ADS_ERR_OK(status)) {
1647 DEBUG(1,("Failed while searching for: %s\n", base));
1648 goto out;
1651 if (ads_count_replies(ads, res) != 1) {
1652 goto out;
1655 /* substitute the bind-path from the well-known-guid-search result */
1656 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1657 if (!wkn_dn) {
1658 goto out;
1661 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1662 if (!wkn_dn_exp) {
1663 goto out;
1666 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1667 if (!bind_dn_exp) {
1668 goto out;
1671 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1673 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1676 new_ln = wkn_ln - bind_ln;
1678 ret = SMB_STRDUP(wkn_dn_exp[0]);
1679 if (!ret) {
1680 goto out;
1683 for (i=1; i < new_ln; i++) {
1684 char *s = NULL;
1686 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1687 SAFE_FREE(ret);
1688 goto out;
1691 SAFE_FREE(ret);
1692 ret = SMB_STRDUP(s);
1693 free(s);
1694 if (!ret) {
1695 goto out;
1699 out:
1700 SAFE_FREE(base);
1701 ads_msgfree(ads, res);
1702 TALLOC_FREE(wkn_dn);
1703 if (wkn_dn_exp) {
1704 ldap_value_free(wkn_dn_exp);
1706 if (bind_dn_exp) {
1707 ldap_value_free(bind_dn_exp);
1710 return ret;
1714 * Adds (appends) an item to an attribute array, rather then
1715 * replacing the whole list
1716 * @param ctx An initialized TALLOC_CTX
1717 * @param mods An initialized ADS_MODLIST
1718 * @param name name of the ldap attribute to append to
1719 * @param vals an array of values to add
1720 * @return status of addition
1723 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1724 const char *name, const char **vals)
1726 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1727 (const void *) vals);
1731 * Determines the an account's current KVNO via an LDAP lookup
1732 * @param ads An initialized ADS_STRUCT
1733 * @param account_name the NT samaccountname.
1734 * @return the kvno for the account, or -1 in case of a failure.
1737 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1739 LDAPMessage *res = NULL;
1740 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1741 char *filter;
1742 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1743 char *dn_string = NULL;
1744 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1746 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1747 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1748 return kvno;
1750 ret = ads_search(ads, &res, filter, attrs);
1751 SAFE_FREE(filter);
1752 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1753 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1754 ads_msgfree(ads, res);
1755 return kvno;
1758 dn_string = ads_get_dn(ads, talloc_tos(), res);
1759 if (!dn_string) {
1760 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1761 ads_msgfree(ads, res);
1762 return kvno;
1764 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1765 TALLOC_FREE(dn_string);
1767 /* ---------------------------------------------------------
1768 * 0 is returned as a default KVNO from this point on...
1769 * This is done because Windows 2000 does not support key
1770 * version numbers. Chances are that a failure in the next
1771 * step is simply due to Windows 2000 being used for a
1772 * domain controller. */
1773 kvno = 0;
1775 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1776 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1777 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1778 ads_msgfree(ads, res);
1779 return kvno;
1782 /* Success */
1783 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1784 ads_msgfree(ads, res);
1785 return kvno;
1789 * Determines the computer account's current KVNO via an LDAP lookup
1790 * @param ads An initialized ADS_STRUCT
1791 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1792 * @return the kvno for the computer account, or -1 in case of a failure.
1795 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1797 char *computer_account = NULL;
1798 uint32_t kvno = -1;
1800 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1801 return kvno;
1804 kvno = ads_get_kvno(ads, computer_account);
1805 free(computer_account);
1807 return kvno;
1811 * This clears out all registered spn's for a given hostname
1812 * @param ads An initilaized ADS_STRUCT
1813 * @param machine_name the NetBIOS name of the computer.
1814 * @return 0 upon success, non-zero otherwise.
1817 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1819 TALLOC_CTX *ctx;
1820 LDAPMessage *res = NULL;
1821 ADS_MODLIST mods;
1822 const char *servicePrincipalName[1] = {NULL};
1823 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1824 char *dn_string = NULL;
1826 ret = ads_find_machine_acct(ads, &res, machine_name);
1827 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1828 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1829 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1830 ads_msgfree(ads, res);
1831 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1834 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1835 ctx = talloc_init("ads_clear_service_principal_names");
1836 if (!ctx) {
1837 ads_msgfree(ads, res);
1838 return ADS_ERROR(LDAP_NO_MEMORY);
1841 if (!(mods = ads_init_mods(ctx))) {
1842 talloc_destroy(ctx);
1843 ads_msgfree(ads, res);
1844 return ADS_ERROR(LDAP_NO_MEMORY);
1846 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1847 if (!ADS_ERR_OK(ret)) {
1848 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1849 ads_msgfree(ads, res);
1850 talloc_destroy(ctx);
1851 return ret;
1853 dn_string = ads_get_dn(ads, talloc_tos(), res);
1854 if (!dn_string) {
1855 talloc_destroy(ctx);
1856 ads_msgfree(ads, res);
1857 return ADS_ERROR(LDAP_NO_MEMORY);
1859 ret = ads_gen_mod(ads, dn_string, mods);
1860 TALLOC_FREE(dn_string);
1861 if (!ADS_ERR_OK(ret)) {
1862 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1863 machine_name));
1864 ads_msgfree(ads, res);
1865 talloc_destroy(ctx);
1866 return ret;
1869 ads_msgfree(ads, res);
1870 talloc_destroy(ctx);
1871 return ret;
1875 * This adds a service principal name to an existing computer account
1876 * (found by hostname) in AD.
1877 * @param ads An initialized ADS_STRUCT
1878 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1879 * @param my_fqdn The fully qualified DNS name of the machine
1880 * @param spn A string of the service principal to add, i.e. 'host'
1881 * @return 0 upon sucess, or non-zero if a failure occurs
1884 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
1885 const char *my_fqdn, const char *spn)
1887 ADS_STATUS ret;
1888 TALLOC_CTX *ctx;
1889 LDAPMessage *res = NULL;
1890 char *psp1, *psp2;
1891 ADS_MODLIST mods;
1892 char *dn_string = NULL;
1893 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
1895 ret = ads_find_machine_acct(ads, &res, machine_name);
1896 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1897 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
1898 machine_name));
1899 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
1900 spn, machine_name, ads->config.realm));
1901 ads_msgfree(ads, res);
1902 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1905 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
1906 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
1907 ads_msgfree(ads, res);
1908 return ADS_ERROR(LDAP_NO_MEMORY);
1911 /* add short name spn */
1913 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
1914 talloc_destroy(ctx);
1915 ads_msgfree(ads, res);
1916 return ADS_ERROR(LDAP_NO_MEMORY);
1918 strupper_m(psp1);
1919 strlower_m(&psp1[strlen(spn)]);
1920 servicePrincipalName[0] = psp1;
1922 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1923 psp1, machine_name));
1926 /* add fully qualified spn */
1928 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
1929 ret = ADS_ERROR(LDAP_NO_MEMORY);
1930 goto out;
1932 strupper_m(psp2);
1933 strlower_m(&psp2[strlen(spn)]);
1934 servicePrincipalName[1] = psp2;
1936 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
1937 psp2, machine_name));
1939 if ( (mods = ads_init_mods(ctx)) == NULL ) {
1940 ret = ADS_ERROR(LDAP_NO_MEMORY);
1941 goto out;
1944 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1945 if (!ADS_ERR_OK(ret)) {
1946 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1947 goto out;
1950 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
1951 ret = ADS_ERROR(LDAP_NO_MEMORY);
1952 goto out;
1955 ret = ads_gen_mod(ads, dn_string, mods);
1956 if (!ADS_ERR_OK(ret)) {
1957 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
1958 goto out;
1961 out:
1962 TALLOC_FREE( ctx );
1963 ads_msgfree(ads, res);
1964 return ret;
1968 * adds a machine account to the ADS server
1969 * @param ads An intialized ADS_STRUCT
1970 * @param machine_name - the NetBIOS machine name of this account.
1971 * @param account_type A number indicating the type of account to create
1972 * @param org_unit The LDAP path in which to place this account
1973 * @return 0 upon success, or non-zero otherwise
1976 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
1977 const char *org_unit)
1979 ADS_STATUS ret;
1980 char *samAccountName, *controlstr;
1981 TALLOC_CTX *ctx;
1982 ADS_MODLIST mods;
1983 char *machine_escaped = NULL;
1984 char *new_dn;
1985 const char *objectClass[] = {"top", "person", "organizationalPerson",
1986 "user", "computer", NULL};
1987 LDAPMessage *res = NULL;
1988 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
1989 UF_DONT_EXPIRE_PASSWD |\
1990 UF_ACCOUNTDISABLE );
1992 if (!(ctx = talloc_init("ads_add_machine_acct")))
1993 return ADS_ERROR(LDAP_NO_MEMORY);
1995 ret = ADS_ERROR(LDAP_NO_MEMORY);
1997 machine_escaped = escape_rdn_val_string_alloc(machine_name);
1998 if (!machine_escaped) {
1999 goto done;
2002 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2003 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2005 if ( !new_dn || !samAccountName ) {
2006 goto done;
2009 #ifndef ENCTYPE_ARCFOUR_HMAC
2010 acct_control |= UF_USE_DES_KEY_ONLY;
2011 #endif
2013 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2014 goto done;
2017 if (!(mods = ads_init_mods(ctx))) {
2018 goto done;
2021 ads_mod_str(ctx, &mods, "cn", machine_name);
2022 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2023 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2024 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2026 ret = ads_gen_add(ads, new_dn, mods);
2028 done:
2029 SAFE_FREE(machine_escaped);
2030 ads_msgfree(ads, res);
2031 talloc_destroy(ctx);
2033 return ret;
2037 * move a machine account to another OU on the ADS server
2038 * @param ads - An intialized ADS_STRUCT
2039 * @param machine_name - the NetBIOS machine name of this account.
2040 * @param org_unit - The LDAP path in which to place this account
2041 * @param moved - whether we moved the machine account (optional)
2042 * @return 0 upon success, or non-zero otherwise
2045 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2046 const char *org_unit, bool *moved)
2048 ADS_STATUS rc;
2049 int ldap_status;
2050 LDAPMessage *res = NULL;
2051 char *filter = NULL;
2052 char *computer_dn = NULL;
2053 char *parent_dn;
2054 char *computer_rdn = NULL;
2055 bool need_move = False;
2057 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2058 rc = ADS_ERROR(LDAP_NO_MEMORY);
2059 goto done;
2062 /* Find pre-existing machine */
2063 rc = ads_search(ads, &res, filter, NULL);
2064 if (!ADS_ERR_OK(rc)) {
2065 goto done;
2068 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2069 if (!computer_dn) {
2070 rc = ADS_ERROR(LDAP_NO_MEMORY);
2071 goto done;
2074 parent_dn = ads_parent_dn(computer_dn);
2075 if (strequal(parent_dn, org_unit)) {
2076 goto done;
2079 need_move = True;
2081 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2082 rc = ADS_ERROR(LDAP_NO_MEMORY);
2083 goto done;
2086 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2087 org_unit, 1, NULL, NULL);
2088 rc = ADS_ERROR(ldap_status);
2090 done:
2091 ads_msgfree(ads, res);
2092 SAFE_FREE(filter);
2093 TALLOC_FREE(computer_dn);
2094 SAFE_FREE(computer_rdn);
2096 if (!ADS_ERR_OK(rc)) {
2097 need_move = False;
2100 if (moved) {
2101 *moved = need_move;
2104 return rc;
2108 dump a binary result from ldap
2110 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2112 int i, j;
2113 for (i=0; values[i]; i++) {
2114 printf("%s: ", field);
2115 for (j=0; j<values[i]->bv_len; j++) {
2116 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2118 printf("\n");
2122 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2124 int i;
2125 for (i=0; values[i]; i++) {
2127 UUID_FLAT guid;
2128 struct GUID tmp;
2130 memcpy(guid.info, values[i]->bv_val, sizeof(guid.info));
2131 smb_uuid_unpack(guid, &tmp);
2132 printf("%s: %s\n", field, GUID_string(talloc_tos(), &tmp));
2137 dump a sid result from ldap
2139 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2141 int i;
2142 for (i=0; values[i]; i++) {
2143 struct dom_sid sid;
2144 fstring tmp;
2145 sid_parse(values[i]->bv_val, values[i]->bv_len, &sid);
2146 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2151 dump ntSecurityDescriptor
2153 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2155 TALLOC_CTX *frame = talloc_stackframe();
2156 struct security_descriptor *psd;
2157 NTSTATUS status;
2159 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2160 values[0]->bv_len, &psd);
2161 if (!NT_STATUS_IS_OK(status)) {
2162 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2163 nt_errstr(status)));
2164 TALLOC_FREE(frame);
2165 return;
2168 if (psd) {
2169 ads_disp_sd(ads, talloc_tos(), psd);
2172 TALLOC_FREE(frame);
2176 dump a string result from ldap
2178 static void dump_string(const char *field, char **values)
2180 int i;
2181 for (i=0; values[i]; i++) {
2182 printf("%s: %s\n", field, values[i]);
2187 dump a field from LDAP on stdout
2188 used for debugging
2191 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2193 const struct {
2194 const char *name;
2195 bool string;
2196 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2197 } handlers[] = {
2198 {"objectGUID", False, dump_guid},
2199 {"netbootGUID", False, dump_guid},
2200 {"nTSecurityDescriptor", False, dump_sd},
2201 {"dnsRecord", False, dump_binary},
2202 {"objectSid", False, dump_sid},
2203 {"tokenGroups", False, dump_sid},
2204 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2205 {"tokengroupsGlobalandUniversal", False, dump_sid},
2206 {"mS-DS-CreatorSID", False, dump_sid},
2207 {"msExchMailboxGuid", False, dump_guid},
2208 {NULL, True, NULL}
2210 int i;
2212 if (!field) { /* must be end of an entry */
2213 printf("\n");
2214 return False;
2217 for (i=0; handlers[i].name; i++) {
2218 if (StrCaseCmp(handlers[i].name, field) == 0) {
2219 if (!values) /* first time, indicate string or not */
2220 return handlers[i].string;
2221 handlers[i].handler(ads, field, (struct berval **) values);
2222 break;
2225 if (!handlers[i].name) {
2226 if (!values) /* first time, indicate string conversion */
2227 return True;
2228 dump_string(field, (char **)values);
2230 return False;
2234 * Dump a result from LDAP on stdout
2235 * used for debugging
2236 * @param ads connection to ads server
2237 * @param res Results to dump
2240 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2242 ads_process_results(ads, res, ads_dump_field, NULL);
2246 * Walk through results, calling a function for each entry found.
2247 * The function receives a field name, a berval * array of values,
2248 * and a data area passed through from the start. The function is
2249 * called once with null for field and values at the end of each
2250 * entry.
2251 * @param ads connection to ads server
2252 * @param res Results to process
2253 * @param fn Function for processing each result
2254 * @param data_area user-defined area to pass to function
2256 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2257 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2258 void *data_area)
2260 LDAPMessage *msg;
2261 TALLOC_CTX *ctx;
2262 size_t converted_size;
2264 if (!(ctx = talloc_init("ads_process_results")))
2265 return;
2267 for (msg = ads_first_entry(ads, res); msg;
2268 msg = ads_next_entry(ads, msg)) {
2269 char *utf8_field;
2270 BerElement *b;
2272 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2273 (LDAPMessage *)msg,&b);
2274 utf8_field;
2275 utf8_field=ldap_next_attribute(ads->ldap.ld,
2276 (LDAPMessage *)msg,b)) {
2277 struct berval **ber_vals;
2278 char **str_vals, **utf8_vals;
2279 char *field;
2280 bool string;
2282 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2283 &converted_size))
2285 DEBUG(0,("ads_process_results: "
2286 "pull_utf8_talloc failed: %s",
2287 strerror(errno)));
2290 string = fn(ads, field, NULL, data_area);
2292 if (string) {
2293 utf8_vals = ldap_get_values(ads->ldap.ld,
2294 (LDAPMessage *)msg, field);
2295 str_vals = ads_pull_strvals(ctx,
2296 (const char **) utf8_vals);
2297 fn(ads, field, (void **) str_vals, data_area);
2298 ldap_value_free(utf8_vals);
2299 } else {
2300 ber_vals = ldap_get_values_len(ads->ldap.ld,
2301 (LDAPMessage *)msg, field);
2302 fn(ads, field, (void **) ber_vals, data_area);
2304 ldap_value_free_len(ber_vals);
2306 ldap_memfree(utf8_field);
2308 ber_free(b, 0);
2309 talloc_free_children(ctx);
2310 fn(ads, NULL, NULL, data_area); /* completed an entry */
2313 talloc_destroy(ctx);
2317 * count how many replies are in a LDAPMessage
2318 * @param ads connection to ads server
2319 * @param res Results to count
2320 * @return number of replies
2322 int ads_count_replies(ADS_STRUCT *ads, void *res)
2324 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2328 * pull the first entry from a ADS result
2329 * @param ads connection to ads server
2330 * @param res Results of search
2331 * @return first entry from result
2333 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2335 return ldap_first_entry(ads->ldap.ld, res);
2339 * pull the next entry from a ADS result
2340 * @param ads connection to ads server
2341 * @param res Results of search
2342 * @return next entry from result
2344 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2346 return ldap_next_entry(ads->ldap.ld, res);
2350 * pull the first message from a ADS result
2351 * @param ads connection to ads server
2352 * @param res Results of search
2353 * @return first message from result
2355 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2357 return ldap_first_message(ads->ldap.ld, res);
2361 * pull the next message from a ADS result
2362 * @param ads connection to ads server
2363 * @param res Results of search
2364 * @return next message from result
2366 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2368 return ldap_next_message(ads->ldap.ld, res);
2372 * pull a single string from a ADS result
2373 * @param ads connection to ads server
2374 * @param mem_ctx TALLOC_CTX to use for allocating result string
2375 * @param msg Results of search
2376 * @param field Attribute to retrieve
2377 * @return Result string in talloc context
2379 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2380 const char *field)
2382 char **values;
2383 char *ret = NULL;
2384 char *ux_string;
2385 size_t converted_size;
2387 values = ldap_get_values(ads->ldap.ld, msg, field);
2388 if (!values)
2389 return NULL;
2391 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2392 &converted_size))
2394 ret = ux_string;
2396 ldap_value_free(values);
2397 return ret;
2401 * pull an array of strings from a ADS result
2402 * @param ads connection to ads server
2403 * @param mem_ctx TALLOC_CTX to use for allocating result string
2404 * @param msg Results of search
2405 * @param field Attribute to retrieve
2406 * @return Result strings in talloc context
2408 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2409 LDAPMessage *msg, const char *field,
2410 size_t *num_values)
2412 char **values;
2413 char **ret = NULL;
2414 int i;
2415 size_t converted_size;
2417 values = ldap_get_values(ads->ldap.ld, msg, field);
2418 if (!values)
2419 return NULL;
2421 *num_values = ldap_count_values(values);
2423 ret = TALLOC_ARRAY(mem_ctx, char *, *num_values + 1);
2424 if (!ret) {
2425 ldap_value_free(values);
2426 return NULL;
2429 for (i=0;i<*num_values;i++) {
2430 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2431 &converted_size))
2433 ldap_value_free(values);
2434 return NULL;
2437 ret[i] = NULL;
2439 ldap_value_free(values);
2440 return ret;
2444 * pull an array of strings from a ADS result
2445 * (handle large multivalue attributes with range retrieval)
2446 * @param ads connection to ads server
2447 * @param mem_ctx TALLOC_CTX to use for allocating result string
2448 * @param msg Results of search
2449 * @param field Attribute to retrieve
2450 * @param current_strings strings returned by a previous call to this function
2451 * @param next_attribute The next query should ask for this attribute
2452 * @param num_values How many values did we get this time?
2453 * @param more_values Are there more values to get?
2454 * @return Result strings in talloc context
2456 char **ads_pull_strings_range(ADS_STRUCT *ads,
2457 TALLOC_CTX *mem_ctx,
2458 LDAPMessage *msg, const char *field,
2459 char **current_strings,
2460 const char **next_attribute,
2461 size_t *num_strings,
2462 bool *more_strings)
2464 char *attr;
2465 char *expected_range_attrib, *range_attr;
2466 BerElement *ptr = NULL;
2467 char **strings;
2468 char **new_strings;
2469 size_t num_new_strings;
2470 unsigned long int range_start;
2471 unsigned long int range_end;
2473 /* we might have been given the whole lot anyway */
2474 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2475 *more_strings = False;
2476 return strings;
2479 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2481 /* look for Range result */
2482 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2483 attr;
2484 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2485 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2486 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2487 range_attr = attr;
2488 break;
2490 ldap_memfree(attr);
2492 if (!attr) {
2493 ber_free(ptr, 0);
2494 /* nothing here - this field is just empty */
2495 *more_strings = False;
2496 return NULL;
2499 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2500 &range_start, &range_end) == 2) {
2501 *more_strings = True;
2502 } else {
2503 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2504 &range_start) == 1) {
2505 *more_strings = False;
2506 } else {
2507 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2508 range_attr));
2509 ldap_memfree(range_attr);
2510 *more_strings = False;
2511 return NULL;
2515 if ((*num_strings) != range_start) {
2516 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2517 " - aborting range retreival\n",
2518 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2519 ldap_memfree(range_attr);
2520 *more_strings = False;
2521 return NULL;
2524 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2526 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2527 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2528 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2529 range_attr, (unsigned long int)range_end - range_start + 1,
2530 (unsigned long int)num_new_strings));
2531 ldap_memfree(range_attr);
2532 *more_strings = False;
2533 return NULL;
2536 strings = TALLOC_REALLOC_ARRAY(mem_ctx, current_strings, char *,
2537 *num_strings + num_new_strings);
2539 if (strings == NULL) {
2540 ldap_memfree(range_attr);
2541 *more_strings = False;
2542 return NULL;
2545 if (new_strings && num_new_strings) {
2546 memcpy(&strings[*num_strings], new_strings,
2547 sizeof(*new_strings) * num_new_strings);
2550 (*num_strings) += num_new_strings;
2552 if (*more_strings) {
2553 *next_attribute = talloc_asprintf(mem_ctx,
2554 "%s;range=%d-*",
2555 field,
2556 (int)*num_strings);
2558 if (!*next_attribute) {
2559 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2560 ldap_memfree(range_attr);
2561 *more_strings = False;
2562 return NULL;
2566 ldap_memfree(range_attr);
2568 return strings;
2572 * pull a single uint32 from a ADS result
2573 * @param ads connection to ads server
2574 * @param msg Results of search
2575 * @param field Attribute to retrieve
2576 * @param v Pointer to int to store result
2577 * @return boolean inidicating success
2579 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2580 uint32 *v)
2582 char **values;
2584 values = ldap_get_values(ads->ldap.ld, msg, field);
2585 if (!values)
2586 return False;
2587 if (!values[0]) {
2588 ldap_value_free(values);
2589 return False;
2592 *v = atoi(values[0]);
2593 ldap_value_free(values);
2594 return True;
2598 * pull a single objectGUID from an ADS result
2599 * @param ads connection to ADS server
2600 * @param msg results of search
2601 * @param guid 37-byte area to receive text guid
2602 * @return boolean indicating success
2604 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2606 char **values;
2607 UUID_FLAT flat_guid;
2609 values = ldap_get_values(ads->ldap.ld, msg, "objectGUID");
2610 if (!values)
2611 return False;
2613 if (values[0]) {
2614 memcpy(&flat_guid.info, values[0], sizeof(UUID_FLAT));
2615 smb_uuid_unpack(flat_guid, guid);
2616 ldap_value_free(values);
2617 return True;
2619 ldap_value_free(values);
2620 return False;
2626 * pull a single struct dom_sid from a ADS result
2627 * @param ads connection to ads server
2628 * @param msg Results of search
2629 * @param field Attribute to retrieve
2630 * @param sid Pointer to sid to store result
2631 * @return boolean inidicating success
2633 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2634 struct dom_sid *sid)
2636 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2640 * pull an array of struct dom_sids from a ADS result
2641 * @param ads connection to ads server
2642 * @param mem_ctx TALLOC_CTX for allocating sid array
2643 * @param msg Results of search
2644 * @param field Attribute to retrieve
2645 * @param sids pointer to sid array to allocate
2646 * @return the count of SIDs pulled
2648 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2649 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2651 struct berval **values;
2652 bool ret;
2653 int count, i;
2655 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2657 if (!values)
2658 return 0;
2660 for (i=0; values[i]; i++)
2661 /* nop */ ;
2663 if (i) {
2664 (*sids) = TALLOC_ARRAY(mem_ctx, struct dom_sid, i);
2665 if (!(*sids)) {
2666 ldap_value_free_len(values);
2667 return 0;
2669 } else {
2670 (*sids) = NULL;
2673 count = 0;
2674 for (i=0; values[i]; i++) {
2675 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2676 if (ret) {
2677 DEBUG(10, ("pulling SID: %s\n",
2678 sid_string_dbg(&(*sids)[count])));
2679 count++;
2683 ldap_value_free_len(values);
2684 return count;
2688 * pull a struct security_descriptor from a ADS result
2689 * @param ads connection to ads server
2690 * @param mem_ctx TALLOC_CTX for allocating sid array
2691 * @param msg Results of search
2692 * @param field Attribute to retrieve
2693 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2694 * @return boolean inidicating success
2696 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2697 LDAPMessage *msg, const char *field,
2698 struct security_descriptor **sd)
2700 struct berval **values;
2701 bool ret = true;
2703 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2705 if (!values) return false;
2707 if (values[0]) {
2708 NTSTATUS status;
2709 status = unmarshall_sec_desc(mem_ctx,
2710 (uint8 *)values[0]->bv_val,
2711 values[0]->bv_len, sd);
2712 if (!NT_STATUS_IS_OK(status)) {
2713 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2714 nt_errstr(status)));
2715 ret = false;
2719 ldap_value_free_len(values);
2720 return ret;
2724 * in order to support usernames longer than 21 characters we need to
2725 * use both the sAMAccountName and the userPrincipalName attributes
2726 * It seems that not all users have the userPrincipalName attribute set
2728 * @param ads connection to ads server
2729 * @param mem_ctx TALLOC_CTX for allocating sid array
2730 * @param msg Results of search
2731 * @return the username
2733 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2734 LDAPMessage *msg)
2736 #if 0 /* JERRY */
2737 char *ret, *p;
2739 /* lookup_name() only works on the sAMAccountName to
2740 returning the username portion of userPrincipalName
2741 breaks winbindd_getpwnam() */
2743 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2744 if (ret && (p = strchr_m(ret, '@'))) {
2745 *p = 0;
2746 return ret;
2748 #endif
2749 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2754 * find the update serial number - this is the core of the ldap cache
2755 * @param ads connection to ads server
2756 * @param ads connection to ADS server
2757 * @param usn Pointer to retrieved update serial number
2758 * @return status of search
2760 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2762 const char *attrs[] = {"highestCommittedUSN", NULL};
2763 ADS_STATUS status;
2764 LDAPMessage *res;
2766 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2767 if (!ADS_ERR_OK(status))
2768 return status;
2770 if (ads_count_replies(ads, res) != 1) {
2771 ads_msgfree(ads, res);
2772 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2775 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2776 ads_msgfree(ads, res);
2777 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2780 ads_msgfree(ads, res);
2781 return ADS_SUCCESS;
2784 /* parse a ADS timestring - typical string is
2785 '20020917091222.0Z0' which means 09:12.22 17th September
2786 2002, timezone 0 */
2787 static time_t ads_parse_time(const char *str)
2789 struct tm tm;
2791 ZERO_STRUCT(tm);
2793 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2794 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2795 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2796 return 0;
2798 tm.tm_year -= 1900;
2799 tm.tm_mon -= 1;
2801 return timegm(&tm);
2804 /********************************************************************
2805 ********************************************************************/
2807 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2809 const char *attrs[] = {"currentTime", NULL};
2810 ADS_STATUS status;
2811 LDAPMessage *res;
2812 char *timestr;
2813 TALLOC_CTX *ctx;
2814 ADS_STRUCT *ads_s = ads;
2816 if (!(ctx = talloc_init("ads_current_time"))) {
2817 return ADS_ERROR(LDAP_NO_MEMORY);
2820 /* establish a new ldap tcp session if necessary */
2822 if ( !ads->ldap.ld ) {
2823 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2824 ads->server.ldap_server )) == NULL )
2826 goto done;
2828 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2829 status = ads_connect( ads_s );
2830 if ( !ADS_ERR_OK(status))
2831 goto done;
2834 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2835 if (!ADS_ERR_OK(status)) {
2836 goto done;
2839 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2840 if (!timestr) {
2841 ads_msgfree(ads_s, res);
2842 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2843 goto done;
2846 /* but save the time and offset in the original ADS_STRUCT */
2848 ads->config.current_time = ads_parse_time(timestr);
2850 if (ads->config.current_time != 0) {
2851 ads->auth.time_offset = ads->config.current_time - time(NULL);
2852 DEBUG(4,("time offset is %d seconds\n", ads->auth.time_offset));
2855 ads_msgfree(ads, res);
2857 status = ADS_SUCCESS;
2859 done:
2860 /* free any temporary ads connections */
2861 if ( ads_s != ads ) {
2862 ads_destroy( &ads_s );
2864 talloc_destroy(ctx);
2866 return status;
2869 /********************************************************************
2870 ********************************************************************/
2872 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
2874 const char *attrs[] = {"domainFunctionality", NULL};
2875 ADS_STATUS status;
2876 LDAPMessage *res;
2877 ADS_STRUCT *ads_s = ads;
2879 *val = DS_DOMAIN_FUNCTION_2000;
2881 /* establish a new ldap tcp session if necessary */
2883 if ( !ads->ldap.ld ) {
2884 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2885 ads->server.ldap_server )) == NULL )
2887 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
2888 goto done;
2890 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2891 status = ads_connect( ads_s );
2892 if ( !ADS_ERR_OK(status))
2893 goto done;
2896 /* If the attribute does not exist assume it is a Windows 2000
2897 functional domain */
2899 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2900 if (!ADS_ERR_OK(status)) {
2901 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
2902 status = ADS_SUCCESS;
2904 goto done;
2907 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
2908 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
2910 DEBUG(3,("ads_domain_func_level: %d\n", *val));
2913 ads_msgfree(ads, res);
2915 done:
2916 /* free any temporary ads connections */
2917 if ( ads_s != ads ) {
2918 ads_destroy( &ads_s );
2921 return status;
2925 * find the domain sid for our domain
2926 * @param ads connection to ads server
2927 * @param sid Pointer to domain sid
2928 * @return status of search
2930 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
2932 const char *attrs[] = {"objectSid", NULL};
2933 LDAPMessage *res;
2934 ADS_STATUS rc;
2936 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
2937 attrs, &res);
2938 if (!ADS_ERR_OK(rc)) return rc;
2939 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
2940 ads_msgfree(ads, res);
2941 return ADS_ERROR_SYSTEM(ENOENT);
2943 ads_msgfree(ads, res);
2945 return ADS_SUCCESS;
2949 * find our site name
2950 * @param ads connection to ads server
2951 * @param mem_ctx Pointer to talloc context
2952 * @param site_name Pointer to the sitename
2953 * @return status of search
2955 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
2957 ADS_STATUS status;
2958 LDAPMessage *res;
2959 const char *dn, *service_name;
2960 const char *attrs[] = { "dsServiceName", NULL };
2962 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2963 if (!ADS_ERR_OK(status)) {
2964 return status;
2967 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
2968 if (service_name == NULL) {
2969 ads_msgfree(ads, res);
2970 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2973 ads_msgfree(ads, res);
2975 /* go up three levels */
2976 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
2977 if (dn == NULL) {
2978 return ADS_ERROR(LDAP_NO_MEMORY);
2981 *site_name = talloc_strdup(mem_ctx, dn);
2982 if (*site_name == NULL) {
2983 return ADS_ERROR(LDAP_NO_MEMORY);
2986 return status;
2988 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
2993 * find the site dn where a machine resides
2994 * @param ads connection to ads server
2995 * @param mem_ctx Pointer to talloc context
2996 * @param computer_name name of the machine
2997 * @param site_name Pointer to the sitename
2998 * @return status of search
3000 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3002 ADS_STATUS status;
3003 LDAPMessage *res;
3004 const char *parent, *filter;
3005 char *config_context = NULL;
3006 char *dn;
3008 /* shortcut a query */
3009 if (strequal(computer_name, ads->config.ldap_server_name)) {
3010 return ads_site_dn(ads, mem_ctx, site_dn);
3013 status = ads_config_path(ads, mem_ctx, &config_context);
3014 if (!ADS_ERR_OK(status)) {
3015 return status;
3018 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3019 if (filter == NULL) {
3020 return ADS_ERROR(LDAP_NO_MEMORY);
3023 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3024 filter, NULL, &res);
3025 if (!ADS_ERR_OK(status)) {
3026 return status;
3029 if (ads_count_replies(ads, res) != 1) {
3030 ads_msgfree(ads, res);
3031 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3034 dn = ads_get_dn(ads, mem_ctx, res);
3035 if (dn == NULL) {
3036 ads_msgfree(ads, res);
3037 return ADS_ERROR(LDAP_NO_MEMORY);
3040 /* go up three levels */
3041 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3042 if (parent == NULL) {
3043 ads_msgfree(ads, res);
3044 TALLOC_FREE(dn);
3045 return ADS_ERROR(LDAP_NO_MEMORY);
3048 *site_dn = talloc_strdup(mem_ctx, parent);
3049 if (*site_dn == NULL) {
3050 ads_msgfree(ads, res);
3051 TALLOC_FREE(dn);
3052 return ADS_ERROR(LDAP_NO_MEMORY);
3055 TALLOC_FREE(dn);
3056 ads_msgfree(ads, res);
3058 return status;
3062 * get the upn suffixes for a domain
3063 * @param ads connection to ads server
3064 * @param mem_ctx Pointer to talloc context
3065 * @param suffixes Pointer to an array of suffixes
3066 * @param num_suffixes Pointer to the number of suffixes
3067 * @return status of search
3069 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3071 ADS_STATUS status;
3072 LDAPMessage *res;
3073 const char *base;
3074 char *config_context = NULL;
3075 const char *attrs[] = { "uPNSuffixes", NULL };
3077 status = ads_config_path(ads, mem_ctx, &config_context);
3078 if (!ADS_ERR_OK(status)) {
3079 return status;
3082 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3083 if (base == NULL) {
3084 return ADS_ERROR(LDAP_NO_MEMORY);
3087 status = ads_search_dn(ads, &res, base, attrs);
3088 if (!ADS_ERR_OK(status)) {
3089 return status;
3092 if (ads_count_replies(ads, res) != 1) {
3093 ads_msgfree(ads, res);
3094 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3097 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3098 if ((*suffixes) == NULL) {
3099 ads_msgfree(ads, res);
3100 return ADS_ERROR(LDAP_NO_MEMORY);
3103 ads_msgfree(ads, res);
3105 return status;
3109 * get the joinable ous for a domain
3110 * @param ads connection to ads server
3111 * @param mem_ctx Pointer to talloc context
3112 * @param ous Pointer to an array of ous
3113 * @param num_ous Pointer to the number of ous
3114 * @return status of search
3116 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3117 TALLOC_CTX *mem_ctx,
3118 char ***ous,
3119 size_t *num_ous)
3121 ADS_STATUS status;
3122 LDAPMessage *res = NULL;
3123 LDAPMessage *msg = NULL;
3124 const char *attrs[] = { "dn", NULL };
3125 int count = 0;
3127 status = ads_search(ads, &res,
3128 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3129 attrs);
3130 if (!ADS_ERR_OK(status)) {
3131 return status;
3134 count = ads_count_replies(ads, res);
3135 if (count < 1) {
3136 ads_msgfree(ads, res);
3137 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3140 for (msg = ads_first_entry(ads, res); msg;
3141 msg = ads_next_entry(ads, msg)) {
3143 char *dn = NULL;
3145 dn = ads_get_dn(ads, talloc_tos(), msg);
3146 if (!dn) {
3147 ads_msgfree(ads, res);
3148 return ADS_ERROR(LDAP_NO_MEMORY);
3151 if (!add_string_to_array(mem_ctx, dn,
3152 (const char ***)ous,
3153 (int *)num_ous)) {
3154 TALLOC_FREE(dn);
3155 ads_msgfree(ads, res);
3156 return ADS_ERROR(LDAP_NO_MEMORY);
3159 TALLOC_FREE(dn);
3162 ads_msgfree(ads, res);
3164 return status;
3169 * pull a struct dom_sid from an extended dn string
3170 * @param mem_ctx TALLOC_CTX
3171 * @param extended_dn string
3172 * @param flags string type of extended_dn
3173 * @param sid pointer to a struct dom_sid
3174 * @return NT_STATUS_OK on success,
3175 * NT_INVALID_PARAMETER on error,
3176 * NT_STATUS_NOT_FOUND if no SID present
3178 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3179 const char *extended_dn,
3180 enum ads_extended_dn_flags flags,
3181 struct dom_sid *sid)
3183 char *p, *q, *dn;
3185 if (!extended_dn) {
3186 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3189 /* otherwise extended_dn gets stripped off */
3190 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3191 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3194 * ADS_EXTENDED_DN_HEX_STRING:
3195 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3197 * ADS_EXTENDED_DN_STRING (only with w2k3):
3198 * <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
3200 * Object with no SID, such as an Exchange Public Folder
3201 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3204 p = strchr(dn, ';');
3205 if (!p) {
3206 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3209 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3210 DEBUG(5,("No SID present in extended dn\n"));
3211 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3214 p += strlen(";<SID=");
3216 q = strchr(p, '>');
3217 if (!q) {
3218 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3221 *q = '\0';
3223 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3225 switch (flags) {
3227 case ADS_EXTENDED_DN_STRING:
3228 if (!string_to_sid(sid, p)) {
3229 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3231 break;
3232 case ADS_EXTENDED_DN_HEX_STRING: {
3233 fstring buf;
3234 size_t buf_len;
3236 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3237 if (buf_len == 0) {
3238 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3241 if (!sid_parse(buf, buf_len, sid)) {
3242 DEBUG(10,("failed to parse sid\n"));
3243 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3245 break;
3247 default:
3248 DEBUG(10,("unknown extended dn format\n"));
3249 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3252 return ADS_ERROR_NT(NT_STATUS_OK);
3256 * pull an array of struct dom_sids from a ADS result
3257 * @param ads connection to ads server
3258 * @param mem_ctx TALLOC_CTX for allocating sid array
3259 * @param msg Results of search
3260 * @param field Attribute to retrieve
3261 * @param flags string type of extended_dn
3262 * @param sids pointer to sid array to allocate
3263 * @return the count of SIDs pulled
3265 int ads_pull_sids_from_extendeddn(ADS_STRUCT *ads,
3266 TALLOC_CTX *mem_ctx,
3267 LDAPMessage *msg,
3268 const char *field,
3269 enum ads_extended_dn_flags flags,
3270 struct dom_sid **sids)
3272 int i;
3273 ADS_STATUS rc;
3274 size_t dn_count, ret_count = 0;
3275 char **dn_strings;
3277 if ((dn_strings = ads_pull_strings(ads, mem_ctx, msg, field,
3278 &dn_count)) == NULL) {
3279 return 0;
3282 (*sids) = TALLOC_ZERO_ARRAY(mem_ctx, struct dom_sid, dn_count + 1);
3283 if (!(*sids)) {
3284 TALLOC_FREE(dn_strings);
3285 return 0;
3288 for (i=0; i<dn_count; i++) {
3289 rc = ads_get_sid_from_extended_dn(mem_ctx, dn_strings[i],
3290 flags, &(*sids)[i]);
3291 if (!ADS_ERR_OK(rc)) {
3292 if (NT_STATUS_EQUAL(ads_ntstatus(rc),
3293 NT_STATUS_NOT_FOUND)) {
3294 continue;
3296 else {
3297 TALLOC_FREE(*sids);
3298 TALLOC_FREE(dn_strings);
3299 return 0;
3302 ret_count++;
3305 TALLOC_FREE(dn_strings);
3307 return ret_count;
3310 /********************************************************************
3311 ********************************************************************/
3313 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3315 LDAPMessage *res = NULL;
3316 ADS_STATUS status;
3317 int count = 0;
3318 char *name = NULL;
3320 status = ads_find_machine_acct(ads, &res, global_myname());
3321 if (!ADS_ERR_OK(status)) {
3322 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3323 global_myname()));
3324 goto out;
3327 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3328 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3329 goto out;
3332 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3333 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3336 out:
3337 ads_msgfree(ads, res);
3339 return name;
3342 /********************************************************************
3343 ********************************************************************/
3345 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3347 LDAPMessage *res = NULL;
3348 ADS_STATUS status;
3349 int count = 0;
3350 char *name = NULL;
3352 status = ads_find_machine_acct(ads, &res, machine_name);
3353 if (!ADS_ERR_OK(status)) {
3354 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3355 global_myname()));
3356 goto out;
3359 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3360 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3361 goto out;
3364 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3365 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3368 out:
3369 ads_msgfree(ads, res);
3371 return name;
3374 /********************************************************************
3375 ********************************************************************/
3377 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3379 LDAPMessage *res = NULL;
3380 ADS_STATUS status;
3381 int count = 0;
3382 char *name = NULL;
3384 status = ads_find_machine_acct(ads, &res, global_myname());
3385 if (!ADS_ERR_OK(status)) {
3386 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3387 global_myname()));
3388 goto out;
3391 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3392 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3393 goto out;
3396 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3397 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3400 out:
3401 ads_msgfree(ads, res);
3403 return name;
3406 #if 0
3408 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3411 * Join a machine to a realm
3412 * Creates the machine account and sets the machine password
3413 * @param ads connection to ads server
3414 * @param machine name of host to add
3415 * @param org_unit Organizational unit to place machine in
3416 * @return status of join
3418 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3419 uint32 account_type, const char *org_unit)
3421 ADS_STATUS status;
3422 LDAPMessage *res = NULL;
3423 char *machine;
3425 /* machine name must be lowercase */
3426 machine = SMB_STRDUP(machine_name);
3427 strlower_m(machine);
3430 status = ads_find_machine_acct(ads, (void **)&res, machine);
3431 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3432 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3433 status = ads_leave_realm(ads, machine);
3434 if (!ADS_ERR_OK(status)) {
3435 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3436 machine, ads->config.realm));
3437 return status;
3441 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3442 if (!ADS_ERR_OK(status)) {
3443 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3444 SAFE_FREE(machine);
3445 return status;
3448 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3449 if (!ADS_ERR_OK(status)) {
3450 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3451 SAFE_FREE(machine);
3452 return status;
3455 SAFE_FREE(machine);
3456 ads_msgfree(ads, res);
3458 return status;
3460 #endif
3463 * Delete a machine from the realm
3464 * @param ads connection to ads server
3465 * @param hostname Machine to remove
3466 * @return status of delete
3468 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3470 ADS_STATUS status;
3471 void *msg;
3472 LDAPMessage *res;
3473 char *hostnameDN, *host;
3474 int rc;
3475 LDAPControl ldap_control;
3476 LDAPControl * pldap_control[2] = {NULL, NULL};
3478 pldap_control[0] = &ldap_control;
3479 memset(&ldap_control, 0, sizeof(LDAPControl));
3480 ldap_control.ldctl_oid = (char *)LDAP_SERVER_TREE_DELETE_OID;
3482 /* hostname must be lowercase */
3483 host = SMB_STRDUP(hostname);
3484 strlower_m(host);
3486 status = ads_find_machine_acct(ads, &res, host);
3487 if (!ADS_ERR_OK(status)) {
3488 DEBUG(0, ("Host account for %s does not exist.\n", host));
3489 SAFE_FREE(host);
3490 return status;
3493 msg = ads_first_entry(ads, res);
3494 if (!msg) {
3495 SAFE_FREE(host);
3496 return ADS_ERROR_SYSTEM(ENOENT);
3499 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3501 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3502 if (rc) {
3503 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3504 }else {
3505 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3508 if (rc != LDAP_SUCCESS) {
3509 const char *attrs[] = { "cn", NULL };
3510 LDAPMessage *msg_sub;
3512 /* we only search with scope ONE, we do not expect any further
3513 * objects to be created deeper */
3515 status = ads_do_search_retry(ads, hostnameDN,
3516 LDAP_SCOPE_ONELEVEL,
3517 "(objectclass=*)", attrs, &res);
3519 if (!ADS_ERR_OK(status)) {
3520 SAFE_FREE(host);
3521 TALLOC_FREE(hostnameDN);
3522 return status;
3525 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3526 msg_sub = ads_next_entry(ads, msg_sub)) {
3528 char *dn = NULL;
3530 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3531 SAFE_FREE(host);
3532 TALLOC_FREE(hostnameDN);
3533 return ADS_ERROR(LDAP_NO_MEMORY);
3536 status = ads_del_dn(ads, dn);
3537 if (!ADS_ERR_OK(status)) {
3538 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3539 SAFE_FREE(host);
3540 TALLOC_FREE(dn);
3541 TALLOC_FREE(hostnameDN);
3542 return status;
3545 TALLOC_FREE(dn);
3548 /* there should be no subordinate objects anymore */
3549 status = ads_do_search_retry(ads, hostnameDN,
3550 LDAP_SCOPE_ONELEVEL,
3551 "(objectclass=*)", attrs, &res);
3553 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3554 SAFE_FREE(host);
3555 TALLOC_FREE(hostnameDN);
3556 return status;
3559 /* delete hostnameDN now */
3560 status = ads_del_dn(ads, hostnameDN);
3561 if (!ADS_ERR_OK(status)) {
3562 SAFE_FREE(host);
3563 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3564 TALLOC_FREE(hostnameDN);
3565 return status;
3569 TALLOC_FREE(hostnameDN);
3571 status = ads_find_machine_acct(ads, &res, host);
3572 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3573 DEBUG(3, ("Failed to remove host account.\n"));
3574 SAFE_FREE(host);
3575 return status;
3578 SAFE_FREE(host);
3579 return status;
3583 * pull all token-sids from an LDAP dn
3584 * @param ads connection to ads server
3585 * @param mem_ctx TALLOC_CTX for allocating sid array
3586 * @param dn of LDAP object
3587 * @param user_sid pointer to struct dom_sid (objectSid)
3588 * @param primary_group_sid pointer to struct dom_sid (self composed)
3589 * @param sids pointer to sid array to allocate
3590 * @param num_sids counter of SIDs pulled
3591 * @return status of token query
3593 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3594 TALLOC_CTX *mem_ctx,
3595 const char *dn,
3596 struct dom_sid *user_sid,
3597 struct dom_sid *primary_group_sid,
3598 struct dom_sid **sids,
3599 size_t *num_sids)
3601 ADS_STATUS status;
3602 LDAPMessage *res = NULL;
3603 int count = 0;
3604 size_t tmp_num_sids;
3605 struct dom_sid *tmp_sids;
3606 struct dom_sid tmp_user_sid;
3607 struct dom_sid tmp_primary_group_sid;
3608 uint32 pgid;
3609 const char *attrs[] = {
3610 "objectSid",
3611 "tokenGroups",
3612 "primaryGroupID",
3613 NULL
3616 status = ads_search_retry_dn(ads, &res, dn, attrs);
3617 if (!ADS_ERR_OK(status)) {
3618 return status;
3621 count = ads_count_replies(ads, res);
3622 if (count != 1) {
3623 ads_msgfree(ads, res);
3624 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3627 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3628 ads_msgfree(ads, res);
3629 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3632 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3633 ads_msgfree(ads, res);
3634 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3638 /* hack to compose the primary group sid without knowing the
3639 * domsid */
3641 struct dom_sid domsid;
3642 uint32 dummy_rid;
3644 sid_copy(&domsid, &tmp_user_sid);
3646 if (!sid_split_rid(&domsid, &dummy_rid)) {
3647 ads_msgfree(ads, res);
3648 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3651 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3652 ads_msgfree(ads, res);
3653 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3657 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3659 if (tmp_num_sids == 0 || !tmp_sids) {
3660 ads_msgfree(ads, res);
3661 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3664 if (num_sids) {
3665 *num_sids = tmp_num_sids;
3668 if (sids) {
3669 *sids = tmp_sids;
3672 if (user_sid) {
3673 *user_sid = tmp_user_sid;
3676 if (primary_group_sid) {
3677 *primary_group_sid = tmp_primary_group_sid;
3680 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3682 ads_msgfree(ads, res);
3683 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3687 * Find a sAMAccoutName in LDAP
3688 * @param ads connection to ads server
3689 * @param mem_ctx TALLOC_CTX for allocating sid array
3690 * @param samaccountname to search
3691 * @param uac_ret uint32 pointer userAccountControl attribute value
3692 * @param dn_ret pointer to dn
3693 * @return status of token query
3695 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3696 TALLOC_CTX *mem_ctx,
3697 const char *samaccountname,
3698 uint32 *uac_ret,
3699 const char **dn_ret)
3701 ADS_STATUS status;
3702 const char *attrs[] = { "userAccountControl", NULL };
3703 const char *filter;
3704 LDAPMessage *res = NULL;
3705 char *dn = NULL;
3706 uint32 uac = 0;
3708 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3709 samaccountname);
3710 if (filter == NULL) {
3711 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3712 goto out;
3715 status = ads_do_search_all(ads, ads->config.bind_path,
3716 LDAP_SCOPE_SUBTREE,
3717 filter, attrs, &res);
3719 if (!ADS_ERR_OK(status)) {
3720 goto out;
3723 if (ads_count_replies(ads, res) != 1) {
3724 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3725 goto out;
3728 dn = ads_get_dn(ads, talloc_tos(), res);
3729 if (dn == NULL) {
3730 status = ADS_ERROR(LDAP_NO_MEMORY);
3731 goto out;
3734 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3735 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3736 goto out;
3739 if (uac_ret) {
3740 *uac_ret = uac;
3743 if (dn_ret) {
3744 *dn_ret = talloc_strdup(mem_ctx, dn);
3745 if (!*dn_ret) {
3746 status = ADS_ERROR(LDAP_NO_MEMORY);
3747 goto out;
3750 out:
3751 TALLOC_FREE(dn);
3752 ads_msgfree(ads, res);
3754 return status;
3758 * find our configuration path
3759 * @param ads connection to ads server
3760 * @param mem_ctx Pointer to talloc context
3761 * @param config_path Pointer to the config path
3762 * @return status of search
3764 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3765 TALLOC_CTX *mem_ctx,
3766 char **config_path)
3768 ADS_STATUS status;
3769 LDAPMessage *res = NULL;
3770 const char *config_context = NULL;
3771 const char *attrs[] = { "configurationNamingContext", NULL };
3773 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3774 "(objectclass=*)", attrs, &res);
3775 if (!ADS_ERR_OK(status)) {
3776 return status;
3779 config_context = ads_pull_string(ads, mem_ctx, res,
3780 "configurationNamingContext");
3781 ads_msgfree(ads, res);
3782 if (!config_context) {
3783 return ADS_ERROR(LDAP_NO_MEMORY);
3786 if (config_path) {
3787 *config_path = talloc_strdup(mem_ctx, config_context);
3788 if (!*config_path) {
3789 return ADS_ERROR(LDAP_NO_MEMORY);
3793 return ADS_ERROR(LDAP_SUCCESS);
3797 * find the displayName of an extended right
3798 * @param ads connection to ads server
3799 * @param config_path The config path
3800 * @param mem_ctx Pointer to talloc context
3801 * @param GUID struct of the rightsGUID
3802 * @return status of search
3804 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3805 const char *config_path,
3806 TALLOC_CTX *mem_ctx,
3807 const struct GUID *rights_guid)
3809 ADS_STATUS rc;
3810 LDAPMessage *res = NULL;
3811 char *expr = NULL;
3812 const char *attrs[] = { "displayName", NULL };
3813 const char *result = NULL;
3814 const char *path;
3816 if (!ads || !mem_ctx || !rights_guid) {
3817 goto done;
3820 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3821 GUID_string(mem_ctx, rights_guid));
3822 if (!expr) {
3823 goto done;
3826 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3827 if (!path) {
3828 goto done;
3831 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3832 expr, attrs, &res);
3833 if (!ADS_ERR_OK(rc)) {
3834 goto done;
3837 if (ads_count_replies(ads, res) != 1) {
3838 goto done;
3841 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3843 done:
3844 ads_msgfree(ads, res);
3845 return result;
3849 * verify or build and verify an account ou
3850 * @param mem_ctx Pointer to talloc context
3851 * @param ads connection to ads server
3852 * @param account_ou
3853 * @return status of search
3856 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3857 ADS_STRUCT *ads,
3858 const char **account_ou)
3860 char **exploded_dn;
3861 const char *name;
3862 char *ou_string;
3864 exploded_dn = ldap_explode_dn(*account_ou, 0);
3865 if (exploded_dn) {
3866 ldap_value_free(exploded_dn);
3867 return ADS_SUCCESS;
3870 ou_string = ads_ou_string(ads, *account_ou);
3871 if (!ou_string) {
3872 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3875 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3876 ads->config.bind_path);
3877 SAFE_FREE(ou_string);
3879 if (!name) {
3880 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3883 exploded_dn = ldap_explode_dn(name, 0);
3884 if (!exploded_dn) {
3885 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3887 ldap_value_free(exploded_dn);
3889 *account_ou = name;
3890 return ADS_SUCCESS;
3893 #endif