ldb: version 1.1.20
[Samba.git] / source3 / libads / ldap.c
blob93d5c791bf0a2c669c2d27ebecc66f95d08e9601
1 /*
2 Unix SMB/CIFS implementation.
3 ads (active directory) utility library
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001
6 Copyright (C) Jim McDonough <jmcd@us.ibm.com> 2002
7 Copyright (C) Guenther Deschner 2005
8 Copyright (C) Gerald Carter 2006
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #include "includes.h"
25 #include "ads.h"
26 #include "libads/sitename_cache.h"
27 #include "libads/cldap.h"
28 #include "../lib/addns/dnsquery.h"
29 #include "../libds/common/flags.h"
30 #include "smbldap.h"
31 #include "../libcli/security/security.h"
32 #include "lib/param/loadparm.h"
34 #ifdef HAVE_LDAP
36 /**
37 * @file ldap.c
38 * @brief basic ldap client-side routines for ads server communications
40 * The routines contained here should do the necessary ldap calls for
41 * ads setups.
43 * Important note: attribute names passed into ads_ routines must
44 * already be in UTF-8 format. We do not convert them because in almost
45 * all cases, they are just ascii (which is represented with the same
46 * codepoints in UTF-8). This may have to change at some point
47 **/
50 #define LDAP_SERVER_TREE_DELETE_OID "1.2.840.113556.1.4.805"
52 static SIG_ATOMIC_T gotalarm;
54 /***************************************************************
55 Signal function to tell us we timed out.
56 ****************************************************************/
58 static void gotalarm_sig(int signum)
60 gotalarm = 1;
63 LDAP *ldap_open_with_timeout(const char *server,
64 struct sockaddr_storage *ss,
65 int port, unsigned int to)
67 LDAP *ldp = NULL;
68 int ldap_err;
69 char *uri;
71 DEBUG(10, ("Opening connection to LDAP server '%s:%d', timeout "
72 "%u seconds\n", server, port, to));
74 if (to) {
75 /* Setup timeout */
76 gotalarm = 0;
77 CatchSignal(SIGALRM, gotalarm_sig);
78 alarm(to);
79 /* End setup timeout. */
82 uri = talloc_asprintf(talloc_tos(), "ldap://%s:%u", server, port);
83 if (uri == NULL) {
84 return NULL;
87 #ifdef HAVE_LDAP_INITIALIZE
88 ldap_err = ldap_initialize(&ldp, uri);
89 #else
90 ldp = ldap_open(server, port);
91 if (ldp != NULL) {
92 ldap_err = LDAP_SUCCESS;
93 } else {
94 ldap_err = LDAP_OTHER;
96 #endif
97 if (ldap_err != LDAP_SUCCESS) {
98 DEBUG(2,("Could not initialize connection for LDAP server '%s': %s\n",
99 uri, ldap_err2string(ldap_err)));
100 } else {
101 DEBUG(10, ("Initialized connection for LDAP server '%s'\n", uri));
104 if (to) {
105 /* Teardown timeout. */
106 alarm(0);
107 CatchSignal(SIGALRM, SIG_IGN);
110 return ldp;
113 static int ldap_search_with_timeout(LDAP *ld,
114 LDAP_CONST char *base,
115 int scope,
116 LDAP_CONST char *filter,
117 char **attrs,
118 int attrsonly,
119 LDAPControl **sctrls,
120 LDAPControl **cctrls,
121 int sizelimit,
122 LDAPMessage **res )
124 int to = lp_ldap_timeout();
125 struct timeval timeout;
126 struct timeval *timeout_ptr = NULL;
127 int result;
129 /* Setup timeout for the ldap_search_ext_s call - local and remote. */
130 gotalarm = 0;
132 if (to) {
133 timeout.tv_sec = to;
134 timeout.tv_usec = 0;
135 timeout_ptr = &timeout;
137 /* Setup alarm timeout. */
138 CatchSignal(SIGALRM, gotalarm_sig);
139 /* Make the alarm time one second beyond
140 the timout we're setting for the
141 remote search timeout, to allow that
142 to fire in preference. */
143 alarm(to+1);
144 /* End setup timeout. */
148 result = ldap_search_ext_s(ld, base, scope, filter, attrs,
149 attrsonly, sctrls, cctrls, timeout_ptr,
150 sizelimit, res);
152 if (to) {
153 /* Teardown alarm timeout. */
154 CatchSignal(SIGALRM, SIG_IGN);
155 alarm(0);
158 if (gotalarm != 0)
159 return LDAP_TIMELIMIT_EXCEEDED;
162 * A bug in OpenLDAP means ldap_search_ext_s can return
163 * LDAP_SUCCESS but with a NULL res pointer. Cope with
164 * this. See bug #6279 for details. JRA.
167 if (*res == NULL) {
168 return LDAP_TIMELIMIT_EXCEEDED;
171 return result;
174 /**********************************************
175 Do client and server sitename match ?
176 **********************************************/
178 bool ads_sitename_match(ADS_STRUCT *ads)
180 if (ads->config.server_site_name == NULL &&
181 ads->config.client_site_name == NULL ) {
182 DEBUG(10,("ads_sitename_match: both null\n"));
183 return True;
185 if (ads->config.server_site_name &&
186 ads->config.client_site_name &&
187 strequal(ads->config.server_site_name,
188 ads->config.client_site_name)) {
189 DEBUG(10,("ads_sitename_match: name %s match\n", ads->config.server_site_name));
190 return True;
192 DEBUG(10,("ads_sitename_match: no match between server: %s and client: %s\n",
193 ads->config.server_site_name ? ads->config.server_site_name : "NULL",
194 ads->config.client_site_name ? ads->config.client_site_name : "NULL"));
195 return False;
198 /**********************************************
199 Is this the closest DC ?
200 **********************************************/
202 bool ads_closest_dc(ADS_STRUCT *ads)
204 if (ads->config.flags & NBT_SERVER_CLOSEST) {
205 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag set\n"));
206 return True;
209 /* not sure if this can ever happen */
210 if (ads_sitename_match(ads)) {
211 DEBUG(10,("ads_closest_dc: NBT_SERVER_CLOSEST flag not set but sites match\n"));
212 return True;
215 if (ads->config.client_site_name == NULL) {
216 DEBUG(10,("ads_closest_dc: client belongs to no site\n"));
217 return True;
220 DEBUG(10,("ads_closest_dc: %s is not the closest DC\n",
221 ads->config.ldap_server_name));
223 return False;
228 try a connection to a given ldap server, returning True and setting the servers IP
229 in the ads struct if successful
231 static bool ads_try_connect(ADS_STRUCT *ads, bool gc,
232 struct sockaddr_storage *ss)
234 struct NETLOGON_SAM_LOGON_RESPONSE_EX cldap_reply;
235 TALLOC_CTX *frame = talloc_stackframe();
236 bool ret = false;
237 char addr[INET6_ADDRSTRLEN];
239 if (ss == NULL) {
240 TALLOC_FREE(frame);
241 return False;
244 print_sockaddr(addr, sizeof(addr), ss);
246 DEBUG(5,("ads_try_connect: sending CLDAP request to %s (realm: %s)\n",
247 addr, ads->server.realm));
249 ZERO_STRUCT( cldap_reply );
251 if ( !ads_cldap_netlogon_5(frame, ss, ads->server.realm, &cldap_reply ) ) {
252 DEBUG(3,("ads_try_connect: CLDAP request %s failed.\n", addr));
253 ret = false;
254 goto out;
257 /* Check the CLDAP reply flags */
259 if ( !(cldap_reply.server_type & NBT_SERVER_LDAP) ) {
260 DEBUG(1,("ads_try_connect: %s's CLDAP reply says it is not an LDAP server!\n",
261 addr));
262 ret = false;
263 goto out;
266 /* Fill in the ads->config values */
268 SAFE_FREE(ads->config.realm);
269 SAFE_FREE(ads->config.bind_path);
270 SAFE_FREE(ads->config.ldap_server_name);
271 SAFE_FREE(ads->config.server_site_name);
272 SAFE_FREE(ads->config.client_site_name);
273 SAFE_FREE(ads->server.workgroup);
275 ads->config.flags = cldap_reply.server_type;
276 ads->config.ldap_server_name = SMB_STRDUP(cldap_reply.pdc_dns_name);
277 ads->config.realm = SMB_STRDUP(cldap_reply.dns_domain);
278 if (!strupper_m(ads->config.realm)) {
279 ret = false;
280 goto out;
283 ads->config.bind_path = ads_build_dn(ads->config.realm);
284 if (*cldap_reply.server_site) {
285 ads->config.server_site_name =
286 SMB_STRDUP(cldap_reply.server_site);
288 if (*cldap_reply.client_site) {
289 ads->config.client_site_name =
290 SMB_STRDUP(cldap_reply.client_site);
292 ads->server.workgroup = SMB_STRDUP(cldap_reply.domain_name);
294 ads->ldap.port = gc ? LDAP_GC_PORT : LDAP_PORT;
295 ads->ldap.ss = *ss;
297 /* Store our site name. */
298 sitename_store( cldap_reply.domain_name, cldap_reply.client_site);
299 sitename_store( cldap_reply.dns_domain, cldap_reply.client_site);
301 ret = true;
303 out:
305 TALLOC_FREE(frame);
306 return ret;
309 /**********************************************************************
310 Try to find an AD dc using our internal name resolution routines
311 Try the realm first and then then workgroup name if netbios is not
312 disabled
313 **********************************************************************/
315 static NTSTATUS ads_find_dc(ADS_STRUCT *ads)
317 const char *c_domain;
318 const char *c_realm;
319 int count, i=0;
320 struct ip_service *ip_list;
321 const char *realm;
322 const char *domain;
323 bool got_realm = False;
324 bool use_own_domain = False;
325 char *sitename;
326 NTSTATUS status = NT_STATUS_UNSUCCESSFUL;
327 bool ok = false;
329 /* if the realm and workgroup are both empty, assume they are ours */
331 /* realm */
332 c_realm = ads->server.realm;
334 if ( !c_realm || !*c_realm ) {
335 /* special case where no realm and no workgroup means our own */
336 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
337 use_own_domain = True;
338 c_realm = lp_realm();
342 if (c_realm && *c_realm)
343 got_realm = True;
345 /* we need to try once with the realm name and fallback to the
346 netbios domain name if we fail (if netbios has not been disabled */
348 if ( !got_realm && !lp_disable_netbios() ) {
349 c_realm = ads->server.workgroup;
350 if (!c_realm || !*c_realm) {
351 if ( use_own_domain )
352 c_realm = lp_workgroup();
356 if ( !c_realm || !*c_realm ) {
357 DEBUG(1, ("ads_find_dc: no realm or workgroup! Don't know "
358 "what to do\n"));
359 return NT_STATUS_INVALID_PARAMETER; /* rather need MISSING_PARAMETER ... */
362 if ( use_own_domain ) {
363 c_domain = lp_workgroup();
364 } else {
365 c_domain = ads->server.workgroup;
368 realm = c_realm;
369 domain = c_domain;
372 * In case of LDAP we use get_dc_name() as that
373 * creates the custom krb5.conf file
375 if (!(ads->auth.flags & ADS_AUTH_NO_BIND)) {
376 fstring srv_name;
377 struct sockaddr_storage ip_out;
379 DEBUG(6,("ads_find_dc: (ldap) looking for %s '%s'\n",
380 (got_realm ? "realm" : "domain"), realm));
382 ok = get_dc_name(domain, realm, srv_name, &ip_out);
383 if (ok) {
385 * we call ads_try_connect() to fill in the
386 * ads->config details
388 ok = ads_try_connect(ads, false, &ip_out);
389 if (ok) {
390 return NT_STATUS_OK;
394 return NT_STATUS_NO_LOGON_SERVERS;
397 sitename = sitename_fetch(talloc_tos(), realm);
399 again:
401 DEBUG(6,("ads_find_dc: (cldap) looking for %s '%s'\n",
402 (got_realm ? "realm" : "domain"), realm));
404 status = get_sorted_dc_list(realm, sitename, &ip_list, &count, got_realm);
405 if (!NT_STATUS_IS_OK(status)) {
406 /* fall back to netbios if we can */
407 if ( got_realm && !lp_disable_netbios() ) {
408 got_realm = False;
409 goto again;
412 TALLOC_FREE(sitename);
413 return status;
416 /* if we fail this loop, then giveup since all the IP addresses returned were dead */
417 for ( i=0; i<count; i++ ) {
418 char server[INET6_ADDRSTRLEN];
420 print_sockaddr(server, sizeof(server), &ip_list[i].ss);
422 if ( !NT_STATUS_IS_OK(check_negative_conn_cache(realm, server)) )
423 continue;
425 if (!got_realm) {
426 /* realm in this case is a workgroup name. We need
427 to ignore any IP addresses in the negative connection
428 cache that match ip addresses returned in the ad realm
429 case. It sucks that I have to reproduce the logic above... */
430 c_realm = ads->server.realm;
431 if ( !c_realm || !*c_realm ) {
432 if ( !ads->server.workgroup || !*ads->server.workgroup ) {
433 c_realm = lp_realm();
436 if (c_realm && *c_realm &&
437 !NT_STATUS_IS_OK(check_negative_conn_cache(c_realm, server))) {
438 /* Ensure we add the workgroup name for this
439 IP address as negative too. */
440 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
441 continue;
445 ok = ads_try_connect(ads, false, &ip_list[i].ss);
446 if (ok) {
447 SAFE_FREE(ip_list);
448 TALLOC_FREE(sitename);
449 return NT_STATUS_OK;
452 /* keep track of failures */
453 add_failed_connection_entry( realm, server, NT_STATUS_UNSUCCESSFUL );
456 SAFE_FREE(ip_list);
458 /* In case we failed to contact one of our closest DC on our site we
459 * need to try to find another DC, retry with a site-less SRV DNS query
460 * - Guenther */
462 if (sitename) {
463 DEBUG(1,("ads_find_dc: failed to find a valid DC on our site (%s), "
464 "trying to find another DC\n", sitename));
465 TALLOC_FREE(sitename);
466 namecache_delete(realm, 0x1C);
467 goto again;
470 return NT_STATUS_NO_LOGON_SERVERS;
473 /*********************************************************************
474 *********************************************************************/
476 static NTSTATUS ads_lookup_site(void)
478 ADS_STRUCT *ads = NULL;
479 ADS_STATUS ads_status;
480 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
482 ads = ads_init(lp_realm(), NULL, NULL);
483 if (!ads) {
484 return NT_STATUS_NO_MEMORY;
487 /* The NO_BIND here will find a DC and set the client site
488 but not establish the TCP connection */
490 ads->auth.flags = ADS_AUTH_NO_BIND;
491 ads_status = ads_connect(ads);
492 if (!ADS_ERR_OK(ads_status)) {
493 DEBUG(4, ("ads_lookup_site: ads_connect to our realm failed! (%s)\n",
494 ads_errstr(ads_status)));
496 nt_status = ads_ntstatus(ads_status);
498 if (ads) {
499 ads_destroy(&ads);
502 return nt_status;
505 /*********************************************************************
506 *********************************************************************/
508 static const char* host_dns_domain(const char *fqdn)
510 const char *p = fqdn;
512 /* go to next char following '.' */
514 if ((p = strchr_m(fqdn, '.')) != NULL) {
515 p++;
518 return p;
523 * Connect to the Global Catalog server
524 * @param ads Pointer to an existing ADS_STRUCT
525 * @return status of connection
527 * Simple wrapper around ads_connect() that fills in the
528 * GC ldap server information
531 ADS_STATUS ads_connect_gc(ADS_STRUCT *ads)
533 TALLOC_CTX *frame = talloc_stackframe();
534 struct dns_rr_srv *gcs_list;
535 int num_gcs;
536 const char *realm = ads->server.realm;
537 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
538 ADS_STATUS ads_status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
539 int i;
540 bool done = false;
541 char *sitename = NULL;
543 if (!realm)
544 realm = lp_realm();
546 if ((sitename = sitename_fetch(frame, realm)) == NULL) {
547 ads_lookup_site();
548 sitename = sitename_fetch(frame, realm);
551 do {
552 /* We try once with a sitename and once without
553 (unless we don't have a sitename and then we're
554 done */
556 if (sitename == NULL)
557 done = true;
559 nt_status = ads_dns_query_gcs(frame,
560 realm,
561 sitename,
562 &gcs_list,
563 &num_gcs);
565 if (!NT_STATUS_IS_OK(nt_status)) {
566 ads_status = ADS_ERROR_NT(nt_status);
567 goto done;
570 /* Loop until we get a successful connection or have gone
571 through them all. When connecting a GC server, make sure that
572 the realm is the server's DNS name and not the forest root */
574 for (i=0; i<num_gcs; i++) {
575 ads->server.gc = true;
576 ads->server.ldap_server = SMB_STRDUP(gcs_list[i].hostname);
577 ads->server.realm = SMB_STRDUP(host_dns_domain(ads->server.ldap_server));
578 ads_status = ads_connect(ads);
579 if (ADS_ERR_OK(ads_status)) {
580 /* Reset the bind_dn to "". A Global Catalog server
581 may host multiple domain trees in a forest.
582 Windows 2003 GC server will accept "" as the search
583 path to imply search all domain trees in the forest */
585 SAFE_FREE(ads->config.bind_path);
586 ads->config.bind_path = SMB_STRDUP("");
589 goto done;
591 SAFE_FREE(ads->server.ldap_server);
592 SAFE_FREE(ads->server.realm);
595 TALLOC_FREE(gcs_list);
596 num_gcs = 0;
597 } while (!done);
599 done:
600 talloc_destroy(frame);
602 return ads_status;
607 * Connect to the LDAP server
608 * @param ads Pointer to an existing ADS_STRUCT
609 * @return status of connection
611 ADS_STATUS ads_connect(ADS_STRUCT *ads)
613 int version = LDAP_VERSION3;
614 ADS_STATUS status;
615 NTSTATUS ntstatus;
616 char addr[INET6_ADDRSTRLEN];
618 ZERO_STRUCT(ads->ldap);
619 ads->ldap.last_attempt = time_mono(NULL);
620 ads->ldap.wrap_type = ADS_SASLWRAP_TYPE_PLAIN;
622 /* try with a user specified server */
624 if (DEBUGLEVEL >= 11) {
625 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
626 DEBUG(11,("ads_connect: entering\n"));
627 DEBUGADD(11,("%s\n", s));
628 TALLOC_FREE(s);
631 if (ads->server.ldap_server) {
632 bool ok = false;
633 struct sockaddr_storage ss;
635 ok = resolve_name(ads->server.ldap_server, &ss, 0x20, true);
636 if (!ok) {
637 DEBUG(5,("ads_connect: unable to resolve name %s\n",
638 ads->server.ldap_server));
639 status = ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
640 goto out;
642 ok = ads_try_connect(ads, ads->server.gc, &ss);
643 if (ok) {
644 goto got_connection;
647 /* The choice of which GC use is handled one level up in
648 ads_connect_gc(). If we continue on from here with
649 ads_find_dc() we will get GC searches on port 389 which
650 doesn't work. --jerry */
652 if (ads->server.gc == true) {
653 return ADS_ERROR(LDAP_OPERATIONS_ERROR);
657 ntstatus = ads_find_dc(ads);
658 if (NT_STATUS_IS_OK(ntstatus)) {
659 goto got_connection;
662 status = ADS_ERROR_NT(ntstatus);
663 goto out;
665 got_connection:
667 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
668 DEBUG(3,("Successfully contacted LDAP server %s\n", addr));
670 if (!ads->auth.user_name) {
671 /* Must use the userPrincipalName value here or sAMAccountName
672 and not servicePrincipalName; found by Guenther Deschner */
674 if (asprintf(&ads->auth.user_name, "%s$", lp_netbios_name() ) == -1) {
675 DEBUG(0,("ads_connect: asprintf fail.\n"));
676 ads->auth.user_name = NULL;
680 if (!ads->auth.realm) {
681 ads->auth.realm = SMB_STRDUP(ads->config.realm);
684 if (!ads->auth.kdc_server) {
685 print_sockaddr(addr, sizeof(addr), &ads->ldap.ss);
686 ads->auth.kdc_server = SMB_STRDUP(addr);
689 /* If the caller() requested no LDAP bind, then we are done */
691 if (ads->auth.flags & ADS_AUTH_NO_BIND) {
692 status = ADS_SUCCESS;
693 goto out;
696 ads->ldap.mem_ctx = talloc_init("ads LDAP connection memory");
697 if (!ads->ldap.mem_ctx) {
698 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
699 goto out;
702 /* Otherwise setup the TCP LDAP session */
704 ads->ldap.ld = ldap_open_with_timeout(addr,
705 &ads->ldap.ss,
706 ads->ldap.port, lp_ldap_timeout());
707 if (ads->ldap.ld == NULL) {
708 status = ADS_ERROR(LDAP_OPERATIONS_ERROR);
709 goto out;
711 DEBUG(3,("Connected to LDAP server %s\n", ads->config.ldap_server_name));
713 /* cache the successful connection for workgroup and realm */
714 if (ads_closest_dc(ads)) {
715 saf_store( ads->server.workgroup, ads->config.ldap_server_name);
716 saf_store( ads->server.realm, ads->config.ldap_server_name);
719 ldap_set_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
721 if ( lp_ldap_ssl_ads() ) {
722 status = ADS_ERROR(smbldap_start_tls(ads->ldap.ld, version));
723 if (!ADS_ERR_OK(status)) {
724 goto out;
728 /* fill in the current time and offsets */
730 status = ads_current_time( ads );
731 if ( !ADS_ERR_OK(status) ) {
732 goto out;
735 /* Now do the bind */
737 if (ads->auth.flags & ADS_AUTH_ANON_BIND) {
738 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, NULL, NULL));
739 goto out;
742 if (ads->auth.flags & ADS_AUTH_SIMPLE_BIND) {
743 status = ADS_ERROR(ldap_simple_bind_s(ads->ldap.ld, ads->auth.user_name, ads->auth.password));
744 goto out;
747 status = ads_sasl_bind(ads);
749 out:
750 if (DEBUGLEVEL >= 11) {
751 char *s = NDR_PRINT_STRUCT_STRING(talloc_tos(), ads_struct, ads);
752 DEBUG(11,("ads_connect: leaving with: %s\n",
753 ads_errstr(status)));
754 DEBUGADD(11,("%s\n", s));
755 TALLOC_FREE(s);
758 return status;
762 * Connect to the LDAP server using given credentials
763 * @param ads Pointer to an existing ADS_STRUCT
764 * @return status of connection
766 ADS_STATUS ads_connect_user_creds(ADS_STRUCT *ads)
768 ads->auth.flags |= ADS_AUTH_USER_CREDS;
770 return ads_connect(ads);
774 * Disconnect the LDAP server
775 * @param ads Pointer to an existing ADS_STRUCT
777 void ads_disconnect(ADS_STRUCT *ads)
779 if (ads->ldap.ld) {
780 ldap_unbind(ads->ldap.ld);
781 ads->ldap.ld = NULL;
783 if (ads->ldap.wrap_ops && ads->ldap.wrap_ops->disconnect) {
784 ads->ldap.wrap_ops->disconnect(ads);
786 if (ads->ldap.mem_ctx) {
787 talloc_free(ads->ldap.mem_ctx);
789 ZERO_STRUCT(ads->ldap);
793 Duplicate a struct berval into talloc'ed memory
795 static struct berval *dup_berval(TALLOC_CTX *ctx, const struct berval *in_val)
797 struct berval *value;
799 if (!in_val) return NULL;
801 value = talloc_zero(ctx, struct berval);
802 if (value == NULL)
803 return NULL;
804 if (in_val->bv_len == 0) return value;
806 value->bv_len = in_val->bv_len;
807 value->bv_val = (char *)talloc_memdup(ctx, in_val->bv_val,
808 in_val->bv_len);
809 return value;
813 Make a values list out of an array of (struct berval *)
815 static struct berval **ads_dup_values(TALLOC_CTX *ctx,
816 const struct berval **in_vals)
818 struct berval **values;
819 int i;
821 if (!in_vals) return NULL;
822 for (i=0; in_vals[i]; i++)
823 ; /* count values */
824 values = talloc_zero_array(ctx, struct berval *, i+1);
825 if (!values) return NULL;
827 for (i=0; in_vals[i]; i++) {
828 values[i] = dup_berval(ctx, in_vals[i]);
830 return values;
834 UTF8-encode a values list out of an array of (char *)
836 static char **ads_push_strvals(TALLOC_CTX *ctx, const char **in_vals)
838 char **values;
839 int i;
840 size_t size;
842 if (!in_vals) return NULL;
843 for (i=0; in_vals[i]; i++)
844 ; /* count values */
845 values = talloc_zero_array(ctx, char *, i+1);
846 if (!values) return NULL;
848 for (i=0; in_vals[i]; i++) {
849 if (!push_utf8_talloc(ctx, &values[i], in_vals[i], &size)) {
850 TALLOC_FREE(values);
851 return NULL;
854 return values;
858 Pull a (char *) array out of a UTF8-encoded values list
860 static char **ads_pull_strvals(TALLOC_CTX *ctx, const char **in_vals)
862 char **values;
863 int i;
864 size_t converted_size;
866 if (!in_vals) return NULL;
867 for (i=0; in_vals[i]; i++)
868 ; /* count values */
869 values = talloc_zero_array(ctx, char *, i+1);
870 if (!values) return NULL;
872 for (i=0; in_vals[i]; i++) {
873 if (!pull_utf8_talloc(ctx, &values[i], in_vals[i],
874 &converted_size)) {
875 DEBUG(0,("ads_pull_strvals: pull_utf8_talloc failed: "
876 "%s", strerror(errno)));
879 return values;
883 * Do a search with paged results. cookie must be null on the first
884 * call, and then returned on each subsequent call. It will be null
885 * again when the entire search is complete
886 * @param ads connection to ads server
887 * @param bind_path Base dn for the search
888 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
889 * @param expr Search expression - specified in local charset
890 * @param attrs Attributes to retrieve - specified in utf8 or ascii
891 * @param res ** which will contain results - free res* with ads_msgfree()
892 * @param count Number of entries retrieved on this page
893 * @param cookie The paged results cookie to be returned on subsequent calls
894 * @return status of search
896 static ADS_STATUS ads_do_paged_search_args(ADS_STRUCT *ads,
897 const char *bind_path,
898 int scope, const char *expr,
899 const char **attrs, void *args,
900 LDAPMessage **res,
901 int *count, struct berval **cookie)
903 int rc, i, version;
904 char *utf8_expr, *utf8_path, **search_attrs = NULL;
905 size_t converted_size;
906 LDAPControl PagedResults, NoReferrals, ExternalCtrl, *controls[4], **rcontrols;
907 BerElement *cookie_be = NULL;
908 struct berval *cookie_bv= NULL;
909 BerElement *ext_be = NULL;
910 struct berval *ext_bv= NULL;
912 TALLOC_CTX *ctx;
913 ads_control *external_control = (ads_control *) args;
915 *res = NULL;
917 if (!(ctx = talloc_init("ads_do_paged_search_args")))
918 return ADS_ERROR(LDAP_NO_MEMORY);
920 /* 0 means the conversion worked but the result was empty
921 so we only fail if it's -1. In any case, it always
922 at least nulls out the dest */
923 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
924 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
926 rc = LDAP_NO_MEMORY;
927 goto done;
930 if (!attrs || !(*attrs))
931 search_attrs = NULL;
932 else {
933 /* This would be the utf8-encoded version...*/
934 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
935 if (!(search_attrs = str_list_copy(talloc_tos(), attrs))) {
936 rc = LDAP_NO_MEMORY;
937 goto done;
941 /* Paged results only available on ldap v3 or later */
942 ldap_get_option(ads->ldap.ld, LDAP_OPT_PROTOCOL_VERSION, &version);
943 if (version < LDAP_VERSION3) {
944 rc = LDAP_NOT_SUPPORTED;
945 goto done;
948 cookie_be = ber_alloc_t(LBER_USE_DER);
949 if (*cookie) {
950 ber_printf(cookie_be, "{iO}", (ber_int_t) ads->config.ldap_page_size, *cookie);
951 ber_bvfree(*cookie); /* don't need it from last time */
952 *cookie = NULL;
953 } else {
954 ber_printf(cookie_be, "{io}", (ber_int_t) ads->config.ldap_page_size, "", 0);
956 ber_flatten(cookie_be, &cookie_bv);
957 PagedResults.ldctl_oid = discard_const_p(char, ADS_PAGE_CTL_OID);
958 PagedResults.ldctl_iscritical = (char) 1;
959 PagedResults.ldctl_value.bv_len = cookie_bv->bv_len;
960 PagedResults.ldctl_value.bv_val = cookie_bv->bv_val;
962 NoReferrals.ldctl_oid = discard_const_p(char, ADS_NO_REFERRALS_OID);
963 NoReferrals.ldctl_iscritical = (char) 0;
964 NoReferrals.ldctl_value.bv_len = 0;
965 NoReferrals.ldctl_value.bv_val = discard_const_p(char, "");
967 if (external_control &&
968 (strequal(external_control->control, ADS_EXTENDED_DN_OID) ||
969 strequal(external_control->control, ADS_SD_FLAGS_OID))) {
971 ExternalCtrl.ldctl_oid = discard_const_p(char, external_control->control);
972 ExternalCtrl.ldctl_iscritical = (char) external_control->critical;
974 /* win2k does not accept a ldctl_value beeing passed in */
976 if (external_control->val != 0) {
978 if ((ext_be = ber_alloc_t(LBER_USE_DER)) == NULL ) {
979 rc = LDAP_NO_MEMORY;
980 goto done;
983 if ((ber_printf(ext_be, "{i}", (ber_int_t) external_control->val)) == -1) {
984 rc = LDAP_NO_MEMORY;
985 goto done;
987 if ((ber_flatten(ext_be, &ext_bv)) == -1) {
988 rc = LDAP_NO_MEMORY;
989 goto done;
992 ExternalCtrl.ldctl_value.bv_len = ext_bv->bv_len;
993 ExternalCtrl.ldctl_value.bv_val = ext_bv->bv_val;
995 } else {
996 ExternalCtrl.ldctl_value.bv_len = 0;
997 ExternalCtrl.ldctl_value.bv_val = NULL;
1000 controls[0] = &NoReferrals;
1001 controls[1] = &PagedResults;
1002 controls[2] = &ExternalCtrl;
1003 controls[3] = NULL;
1005 } else {
1006 controls[0] = &NoReferrals;
1007 controls[1] = &PagedResults;
1008 controls[2] = NULL;
1011 /* we need to disable referrals as the openldap libs don't
1012 handle them and paged results at the same time. Using them
1013 together results in the result record containing the server
1014 page control being removed from the result list (tridge/jmcd)
1016 leaving this in despite the control that says don't generate
1017 referrals, in case the server doesn't support it (jmcd)
1019 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1021 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1022 search_attrs, 0, controls,
1023 NULL, LDAP_NO_LIMIT,
1024 (LDAPMessage **)res);
1026 ber_free(cookie_be, 1);
1027 ber_bvfree(cookie_bv);
1029 if (rc) {
1030 DEBUG(3,("ads_do_paged_search_args: ldap_search_with_timeout(%s) -> %s\n", expr,
1031 ldap_err2string(rc)));
1032 if (rc == LDAP_OTHER) {
1033 char *ldap_errmsg;
1034 int ret;
1036 ret = ldap_parse_result(ads->ldap.ld,
1037 *res,
1038 NULL,
1039 NULL,
1040 &ldap_errmsg,
1041 NULL,
1042 NULL,
1044 if (ret == LDAP_SUCCESS) {
1045 DEBUG(3, ("ldap_search_with_timeout(%s) "
1046 "error: %s\n", expr, ldap_errmsg));
1047 ldap_memfree(ldap_errmsg);
1050 goto done;
1053 rc = ldap_parse_result(ads->ldap.ld, *res, NULL, NULL, NULL,
1054 NULL, &rcontrols, 0);
1056 if (!rcontrols) {
1057 goto done;
1060 for (i=0; rcontrols[i]; i++) {
1061 if (strcmp(ADS_PAGE_CTL_OID, rcontrols[i]->ldctl_oid) == 0) {
1062 cookie_be = ber_init(&rcontrols[i]->ldctl_value);
1063 ber_scanf(cookie_be,"{iO}", (ber_int_t *) count,
1064 &cookie_bv);
1065 /* the berval is the cookie, but must be freed when
1066 it is all done */
1067 if (cookie_bv->bv_len) /* still more to do */
1068 *cookie=ber_bvdup(cookie_bv);
1069 else
1070 *cookie=NULL;
1071 ber_bvfree(cookie_bv);
1072 ber_free(cookie_be, 1);
1073 break;
1076 ldap_controls_free(rcontrols);
1078 done:
1079 talloc_destroy(ctx);
1081 if (ext_be) {
1082 ber_free(ext_be, 1);
1085 if (ext_bv) {
1086 ber_bvfree(ext_bv);
1089 /* if/when we decide to utf8-encode attrs, take out this next line */
1090 TALLOC_FREE(search_attrs);
1092 return ADS_ERROR(rc);
1095 static ADS_STATUS ads_do_paged_search(ADS_STRUCT *ads, const char *bind_path,
1096 int scope, const char *expr,
1097 const char **attrs, LDAPMessage **res,
1098 int *count, struct berval **cookie)
1100 return ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, NULL, res, count, cookie);
1105 * Get all results for a search. This uses ads_do_paged_search() to return
1106 * all entries in a large search.
1107 * @param ads connection to ads server
1108 * @param bind_path Base dn for the search
1109 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1110 * @param expr Search expression
1111 * @param attrs Attributes to retrieve
1112 * @param res ** which will contain results - free res* with ads_msgfree()
1113 * @return status of search
1115 ADS_STATUS ads_do_search_all_args(ADS_STRUCT *ads, const char *bind_path,
1116 int scope, const char *expr,
1117 const char **attrs, void *args,
1118 LDAPMessage **res)
1120 struct berval *cookie = NULL;
1121 int count = 0;
1122 ADS_STATUS status;
1124 *res = NULL;
1125 status = ads_do_paged_search_args(ads, bind_path, scope, expr, attrs, args, res,
1126 &count, &cookie);
1128 if (!ADS_ERR_OK(status))
1129 return status;
1131 #ifdef HAVE_LDAP_ADD_RESULT_ENTRY
1132 while (cookie) {
1133 LDAPMessage *res2 = NULL;
1134 LDAPMessage *msg, *next;
1136 status = ads_do_paged_search_args(ads, bind_path, scope, expr,
1137 attrs, args, &res2, &count, &cookie);
1138 if (!ADS_ERR_OK(status)) {
1139 /* Ensure we free all collected results */
1140 ads_msgfree(ads, *res);
1141 *res = NULL;
1142 break;
1145 /* this relies on the way that ldap_add_result_entry() works internally. I hope
1146 that this works on all ldap libs, but I have only tested with openldap */
1147 for (msg = ads_first_message(ads, res2); msg; msg = next) {
1148 next = ads_next_message(ads, msg);
1149 ldap_add_result_entry((LDAPMessage **)res, msg);
1151 /* note that we do not free res2, as the memory is now
1152 part of the main returned list */
1154 #else
1155 DEBUG(0, ("no ldap_add_result_entry() support in LDAP libs!\n"));
1156 status = ADS_ERROR_NT(NT_STATUS_UNSUCCESSFUL);
1157 #endif
1159 return status;
1162 ADS_STATUS ads_do_search_all(ADS_STRUCT *ads, const char *bind_path,
1163 int scope, const char *expr,
1164 const char **attrs, LDAPMessage **res)
1166 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, NULL, res);
1169 ADS_STATUS ads_do_search_all_sd_flags(ADS_STRUCT *ads, const char *bind_path,
1170 int scope, const char *expr,
1171 const char **attrs, uint32 sd_flags,
1172 LDAPMessage **res)
1174 ads_control args;
1176 args.control = ADS_SD_FLAGS_OID;
1177 args.val = sd_flags;
1178 args.critical = True;
1180 return ads_do_search_all_args(ads, bind_path, scope, expr, attrs, &args, res);
1185 * Run a function on all results for a search. Uses ads_do_paged_search() and
1186 * runs the function as each page is returned, using ads_process_results()
1187 * @param ads connection to ads server
1188 * @param bind_path Base dn for the search
1189 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1190 * @param expr Search expression - specified in local charset
1191 * @param attrs Attributes to retrieve - specified in UTF-8 or ascii
1192 * @param fn Function which takes attr name, values list, and data_area
1193 * @param data_area Pointer which is passed to function on each call
1194 * @return status of search
1196 ADS_STATUS ads_do_search_all_fn(ADS_STRUCT *ads, const char *bind_path,
1197 int scope, const char *expr, const char **attrs,
1198 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
1199 void *data_area)
1201 struct berval *cookie = NULL;
1202 int count = 0;
1203 ADS_STATUS status;
1204 LDAPMessage *res;
1206 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs, &res,
1207 &count, &cookie);
1209 if (!ADS_ERR_OK(status)) return status;
1211 ads_process_results(ads, res, fn, data_area);
1212 ads_msgfree(ads, res);
1214 while (cookie) {
1215 status = ads_do_paged_search(ads, bind_path, scope, expr, attrs,
1216 &res, &count, &cookie);
1218 if (!ADS_ERR_OK(status)) break;
1220 ads_process_results(ads, res, fn, data_area);
1221 ads_msgfree(ads, res);
1224 return status;
1228 * Do a search with a timeout.
1229 * @param ads connection to ads server
1230 * @param bind_path Base dn for the search
1231 * @param scope Scope of search (LDAP_SCOPE_BASE | LDAP_SCOPE_ONE | LDAP_SCOPE_SUBTREE)
1232 * @param expr Search expression
1233 * @param attrs Attributes to retrieve
1234 * @param res ** which will contain results - free res* with ads_msgfree()
1235 * @return status of search
1237 ADS_STATUS ads_do_search(ADS_STRUCT *ads, const char *bind_path, int scope,
1238 const char *expr,
1239 const char **attrs, LDAPMessage **res)
1241 int rc;
1242 char *utf8_expr, *utf8_path, **search_attrs = NULL;
1243 size_t converted_size;
1244 TALLOC_CTX *ctx;
1246 *res = NULL;
1247 if (!(ctx = talloc_init("ads_do_search"))) {
1248 DEBUG(1,("ads_do_search: talloc_init() failed!"));
1249 return ADS_ERROR(LDAP_NO_MEMORY);
1252 /* 0 means the conversion worked but the result was empty
1253 so we only fail if it's negative. In any case, it always
1254 at least nulls out the dest */
1255 if (!push_utf8_talloc(ctx, &utf8_expr, expr, &converted_size) ||
1256 !push_utf8_talloc(ctx, &utf8_path, bind_path, &converted_size))
1258 DEBUG(1,("ads_do_search: push_utf8_talloc() failed!"));
1259 rc = LDAP_NO_MEMORY;
1260 goto done;
1263 if (!attrs || !(*attrs))
1264 search_attrs = NULL;
1265 else {
1266 /* This would be the utf8-encoded version...*/
1267 /* if (!(search_attrs = ads_push_strvals(ctx, attrs))) */
1268 if (!(search_attrs = str_list_copy(talloc_tos(), attrs)))
1270 DEBUG(1,("ads_do_search: str_list_copy() failed!"));
1271 rc = LDAP_NO_MEMORY;
1272 goto done;
1276 /* see the note in ads_do_paged_search - we *must* disable referrals */
1277 ldap_set_option(ads->ldap.ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF);
1279 rc = ldap_search_with_timeout(ads->ldap.ld, utf8_path, scope, utf8_expr,
1280 search_attrs, 0, NULL, NULL,
1281 LDAP_NO_LIMIT,
1282 (LDAPMessage **)res);
1284 if (rc == LDAP_SIZELIMIT_EXCEEDED) {
1285 DEBUG(3,("Warning! sizelimit exceeded in ldap. Truncating.\n"));
1286 rc = 0;
1289 done:
1290 talloc_destroy(ctx);
1291 /* if/when we decide to utf8-encode attrs, take out this next line */
1292 TALLOC_FREE(search_attrs);
1293 return ADS_ERROR(rc);
1296 * Do a general ADS search
1297 * @param ads connection to ads server
1298 * @param res ** which will contain results - free res* with ads_msgfree()
1299 * @param expr Search expression
1300 * @param attrs Attributes to retrieve
1301 * @return status of search
1303 ADS_STATUS ads_search(ADS_STRUCT *ads, LDAPMessage **res,
1304 const char *expr, const char **attrs)
1306 return ads_do_search(ads, ads->config.bind_path, LDAP_SCOPE_SUBTREE,
1307 expr, attrs, res);
1311 * Do a search on a specific DistinguishedName
1312 * @param ads connection to ads server
1313 * @param res ** which will contain results - free res* with ads_msgfree()
1314 * @param dn DistinguishName to search
1315 * @param attrs Attributes to retrieve
1316 * @return status of search
1318 ADS_STATUS ads_search_dn(ADS_STRUCT *ads, LDAPMessage **res,
1319 const char *dn, const char **attrs)
1321 return ads_do_search(ads, dn, LDAP_SCOPE_BASE, "(objectclass=*)",
1322 attrs, res);
1326 * Free up memory from a ads_search
1327 * @param ads connection to ads server
1328 * @param msg Search results to free
1330 void ads_msgfree(ADS_STRUCT *ads, LDAPMessage *msg)
1332 if (!msg) return;
1333 ldap_msgfree(msg);
1337 * Get a dn from search results
1338 * @param ads connection to ads server
1339 * @param msg Search result
1340 * @return dn string
1342 char *ads_get_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg)
1344 char *utf8_dn, *unix_dn;
1345 size_t converted_size;
1347 utf8_dn = ldap_get_dn(ads->ldap.ld, msg);
1349 if (!utf8_dn) {
1350 DEBUG (5, ("ads_get_dn: ldap_get_dn failed\n"));
1351 return NULL;
1354 if (!pull_utf8_talloc(mem_ctx, &unix_dn, utf8_dn, &converted_size)) {
1355 DEBUG(0,("ads_get_dn: string conversion failure utf8 [%s]\n",
1356 utf8_dn ));
1357 return NULL;
1359 ldap_memfree(utf8_dn);
1360 return unix_dn;
1364 * Get the parent from a dn
1365 * @param dn the dn to return the parent from
1366 * @return parent dn string
1368 char *ads_parent_dn(const char *dn)
1370 char *p;
1372 if (dn == NULL) {
1373 return NULL;
1376 p = strchr(dn, ',');
1378 if (p == NULL) {
1379 return NULL;
1382 return p+1;
1386 * Find a machine account given a hostname
1387 * @param ads connection to ads server
1388 * @param res ** which will contain results - free res* with ads_msgfree()
1389 * @param host Hostname to search for
1390 * @return status of search
1392 ADS_STATUS ads_find_machine_acct(ADS_STRUCT *ads, LDAPMessage **res,
1393 const char *machine)
1395 ADS_STATUS status;
1396 char *expr;
1397 const char *attrs[] = {"*", "nTSecurityDescriptor", NULL};
1399 *res = NULL;
1401 /* the easiest way to find a machine account anywhere in the tree
1402 is to look for hostname$ */
1403 if (asprintf(&expr, "(samAccountName=%s$)", machine) == -1) {
1404 DEBUG(1, ("asprintf failed!\n"));
1405 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1408 status = ads_search(ads, res, expr, attrs);
1409 SAFE_FREE(expr);
1410 return status;
1414 * Initialize a list of mods to be used in a modify request
1415 * @param ctx An initialized TALLOC_CTX
1416 * @return allocated ADS_MODLIST
1418 ADS_MODLIST ads_init_mods(TALLOC_CTX *ctx)
1420 #define ADS_MODLIST_ALLOC_SIZE 10
1421 LDAPMod **mods;
1423 if ((mods = talloc_zero_array(ctx, LDAPMod *, ADS_MODLIST_ALLOC_SIZE + 1)))
1424 /* -1 is safety to make sure we don't go over the end.
1425 need to reset it to NULL before doing ldap modify */
1426 mods[ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1428 return (ADS_MODLIST)mods;
1433 add an attribute to the list, with values list already constructed
1435 static ADS_STATUS ads_modlist_add(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1436 int mod_op, const char *name,
1437 const void *_invals)
1439 int curmod;
1440 LDAPMod **modlist = (LDAPMod **) *mods;
1441 struct berval **ber_values = NULL;
1442 char **char_values = NULL;
1444 if (!_invals) {
1445 mod_op = LDAP_MOD_DELETE;
1446 } else {
1447 if (mod_op & LDAP_MOD_BVALUES) {
1448 const struct berval **b;
1449 b = discard_const_p(const struct berval *, _invals);
1450 ber_values = ads_dup_values(ctx, b);
1451 } else {
1452 const char **c;
1453 c = discard_const_p(const char *, _invals);
1454 char_values = ads_push_strvals(ctx, c);
1458 /* find the first empty slot */
1459 for (curmod=0; modlist[curmod] && modlist[curmod] != (LDAPMod *) -1;
1460 curmod++);
1461 if (modlist[curmod] == (LDAPMod *) -1) {
1462 if (!(modlist = talloc_realloc(ctx, modlist, LDAPMod *,
1463 curmod+ADS_MODLIST_ALLOC_SIZE+1)))
1464 return ADS_ERROR(LDAP_NO_MEMORY);
1465 memset(&modlist[curmod], 0,
1466 ADS_MODLIST_ALLOC_SIZE*sizeof(LDAPMod *));
1467 modlist[curmod+ADS_MODLIST_ALLOC_SIZE] = (LDAPMod *) -1;
1468 *mods = (ADS_MODLIST)modlist;
1471 if (!(modlist[curmod] = talloc_zero(ctx, LDAPMod)))
1472 return ADS_ERROR(LDAP_NO_MEMORY);
1473 modlist[curmod]->mod_type = talloc_strdup(ctx, name);
1474 if (mod_op & LDAP_MOD_BVALUES) {
1475 modlist[curmod]->mod_bvalues = ber_values;
1476 } else if (mod_op & LDAP_MOD_DELETE) {
1477 modlist[curmod]->mod_values = NULL;
1478 } else {
1479 modlist[curmod]->mod_values = char_values;
1482 modlist[curmod]->mod_op = mod_op;
1483 return ADS_ERROR(LDAP_SUCCESS);
1487 * Add a single string value to a mod list
1488 * @param ctx An initialized TALLOC_CTX
1489 * @param mods An initialized ADS_MODLIST
1490 * @param name The attribute name to add
1491 * @param val The value to add - NULL means DELETE
1492 * @return ADS STATUS indicating success of add
1494 ADS_STATUS ads_mod_str(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1495 const char *name, const char *val)
1497 const char *values[2];
1499 values[0] = val;
1500 values[1] = NULL;
1502 if (!val)
1503 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1504 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE, name, values);
1508 * Add an array of string values to a mod list
1509 * @param ctx An initialized TALLOC_CTX
1510 * @param mods An initialized ADS_MODLIST
1511 * @param name The attribute name to add
1512 * @param vals The array of string values to add - NULL means DELETE
1513 * @return ADS STATUS indicating success of add
1515 ADS_STATUS ads_mod_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1516 const char *name, const char **vals)
1518 if (!vals)
1519 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1520 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE,
1521 name, (const void **) vals);
1524 #if 0
1526 * Add a single ber-encoded value to a mod list
1527 * @param ctx An initialized TALLOC_CTX
1528 * @param mods An initialized ADS_MODLIST
1529 * @param name The attribute name to add
1530 * @param val The value to add - NULL means DELETE
1531 * @return ADS STATUS indicating success of add
1533 static ADS_STATUS ads_mod_ber(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1534 const char *name, const struct berval *val)
1536 const struct berval *values[2];
1538 values[0] = val;
1539 values[1] = NULL;
1540 if (!val)
1541 return ads_modlist_add(ctx, mods, LDAP_MOD_DELETE, name, NULL);
1542 return ads_modlist_add(ctx, mods, LDAP_MOD_REPLACE|LDAP_MOD_BVALUES,
1543 name, (const void **) values);
1545 #endif
1548 * Perform an ldap modify
1549 * @param ads connection to ads server
1550 * @param mod_dn DistinguishedName to modify
1551 * @param mods list of modifications to perform
1552 * @return status of modify
1554 ADS_STATUS ads_gen_mod(ADS_STRUCT *ads, const char *mod_dn, ADS_MODLIST mods)
1556 int ret,i;
1557 char *utf8_dn = NULL;
1558 size_t converted_size;
1560 this control is needed to modify that contains a currently
1561 non-existent attribute (but allowable for the object) to run
1563 LDAPControl PermitModify = {
1564 discard_const_p(char, ADS_PERMIT_MODIFY_OID),
1565 {0, NULL},
1566 (char) 1};
1567 LDAPControl *controls[2];
1569 controls[0] = &PermitModify;
1570 controls[1] = NULL;
1572 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, mod_dn, &converted_size)) {
1573 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1576 /* find the end of the list, marked by NULL or -1 */
1577 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1578 /* make sure the end of the list is NULL */
1579 mods[i] = NULL;
1580 ret = ldap_modify_ext_s(ads->ldap.ld, utf8_dn,
1581 (LDAPMod **) mods, controls, NULL);
1582 TALLOC_FREE(utf8_dn);
1583 return ADS_ERROR(ret);
1587 * Perform an ldap add
1588 * @param ads connection to ads server
1589 * @param new_dn DistinguishedName to add
1590 * @param mods list of attributes and values for DN
1591 * @return status of add
1593 ADS_STATUS ads_gen_add(ADS_STRUCT *ads, const char *new_dn, ADS_MODLIST mods)
1595 int ret, i;
1596 char *utf8_dn = NULL;
1597 size_t converted_size;
1599 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, new_dn, &converted_size)) {
1600 DEBUG(1, ("ads_gen_add: push_utf8_talloc failed!"));
1601 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1604 /* find the end of the list, marked by NULL or -1 */
1605 for(i=0;(mods[i]!=0)&&(mods[i]!=(LDAPMod *) -1);i++);
1606 /* make sure the end of the list is NULL */
1607 mods[i] = NULL;
1609 ret = ldap_add_s(ads->ldap.ld, utf8_dn, (LDAPMod**)mods);
1610 TALLOC_FREE(utf8_dn);
1611 return ADS_ERROR(ret);
1615 * Delete a DistinguishedName
1616 * @param ads connection to ads server
1617 * @param new_dn DistinguishedName to delete
1618 * @return status of delete
1620 ADS_STATUS ads_del_dn(ADS_STRUCT *ads, char *del_dn)
1622 int ret;
1623 char *utf8_dn = NULL;
1624 size_t converted_size;
1625 if (!push_utf8_talloc(talloc_tos(), &utf8_dn, del_dn, &converted_size)) {
1626 DEBUG(1, ("ads_del_dn: push_utf8_talloc failed!"));
1627 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
1630 ret = ldap_delete_s(ads->ldap.ld, utf8_dn);
1631 TALLOC_FREE(utf8_dn);
1632 return ADS_ERROR(ret);
1636 * Build an org unit string
1637 * if org unit is Computers or blank then assume a container, otherwise
1638 * assume a / separated list of organisational units.
1639 * jmcd: '\' is now used for escapes so certain chars can be in the ou (e.g. #)
1640 * @param ads connection to ads server
1641 * @param org_unit Organizational unit
1642 * @return org unit string - caller must free
1644 char *ads_ou_string(ADS_STRUCT *ads, const char *org_unit)
1646 char *ret = NULL;
1648 if (!org_unit || !*org_unit) {
1650 ret = ads_default_ou_string(ads, DS_GUID_COMPUTERS_CONTAINER);
1652 /* samba4 might not yet respond to a wellknownobject-query */
1653 return ret ? ret : SMB_STRDUP("cn=Computers");
1656 if (strequal(org_unit, "Computers")) {
1657 return SMB_STRDUP("cn=Computers");
1660 /* jmcd: removed "\\" from the separation chars, because it is
1661 needed as an escape for chars like '#' which are valid in an
1662 OU name */
1663 return ads_build_path(org_unit, "/", "ou=", 1);
1667 * Get a org unit string for a well-known GUID
1668 * @param ads connection to ads server
1669 * @param wknguid Well known GUID
1670 * @return org unit string - caller must free
1672 char *ads_default_ou_string(ADS_STRUCT *ads, const char *wknguid)
1674 ADS_STATUS status;
1675 LDAPMessage *res = NULL;
1676 char *base, *wkn_dn = NULL, *ret = NULL, **wkn_dn_exp = NULL,
1677 **bind_dn_exp = NULL;
1678 const char *attrs[] = {"distinguishedName", NULL};
1679 int new_ln, wkn_ln, bind_ln, i;
1681 if (wknguid == NULL) {
1682 return NULL;
1685 if (asprintf(&base, "<WKGUID=%s,%s>", wknguid, ads->config.bind_path ) == -1) {
1686 DEBUG(1, ("asprintf failed!\n"));
1687 return NULL;
1690 status = ads_search_dn(ads, &res, base, attrs);
1691 if (!ADS_ERR_OK(status)) {
1692 DEBUG(1,("Failed while searching for: %s\n", base));
1693 goto out;
1696 if (ads_count_replies(ads, res) != 1) {
1697 goto out;
1700 /* substitute the bind-path from the well-known-guid-search result */
1701 wkn_dn = ads_get_dn(ads, talloc_tos(), res);
1702 if (!wkn_dn) {
1703 goto out;
1706 wkn_dn_exp = ldap_explode_dn(wkn_dn, 0);
1707 if (!wkn_dn_exp) {
1708 goto out;
1711 bind_dn_exp = ldap_explode_dn(ads->config.bind_path, 0);
1712 if (!bind_dn_exp) {
1713 goto out;
1716 for (wkn_ln=0; wkn_dn_exp[wkn_ln]; wkn_ln++)
1718 for (bind_ln=0; bind_dn_exp[bind_ln]; bind_ln++)
1721 new_ln = wkn_ln - bind_ln;
1723 ret = SMB_STRDUP(wkn_dn_exp[0]);
1724 if (!ret) {
1725 goto out;
1728 for (i=1; i < new_ln; i++) {
1729 char *s = NULL;
1731 if (asprintf(&s, "%s,%s", ret, wkn_dn_exp[i]) == -1) {
1732 SAFE_FREE(ret);
1733 goto out;
1736 SAFE_FREE(ret);
1737 ret = SMB_STRDUP(s);
1738 free(s);
1739 if (!ret) {
1740 goto out;
1744 out:
1745 SAFE_FREE(base);
1746 ads_msgfree(ads, res);
1747 TALLOC_FREE(wkn_dn);
1748 if (wkn_dn_exp) {
1749 ldap_value_free(wkn_dn_exp);
1751 if (bind_dn_exp) {
1752 ldap_value_free(bind_dn_exp);
1755 return ret;
1759 * Adds (appends) an item to an attribute array, rather then
1760 * replacing the whole list
1761 * @param ctx An initialized TALLOC_CTX
1762 * @param mods An initialized ADS_MODLIST
1763 * @param name name of the ldap attribute to append to
1764 * @param vals an array of values to add
1765 * @return status of addition
1768 ADS_STATUS ads_add_strlist(TALLOC_CTX *ctx, ADS_MODLIST *mods,
1769 const char *name, const char **vals)
1771 return ads_modlist_add(ctx, mods, LDAP_MOD_ADD, name,
1772 (const void *) vals);
1776 * Determines the an account's current KVNO via an LDAP lookup
1777 * @param ads An initialized ADS_STRUCT
1778 * @param account_name the NT samaccountname.
1779 * @return the kvno for the account, or -1 in case of a failure.
1782 uint32 ads_get_kvno(ADS_STRUCT *ads, const char *account_name)
1784 LDAPMessage *res = NULL;
1785 uint32 kvno = (uint32)-1; /* -1 indicates a failure */
1786 char *filter;
1787 const char *attrs[] = {"msDS-KeyVersionNumber", NULL};
1788 char *dn_string = NULL;
1789 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1791 DEBUG(5,("ads_get_kvno: Searching for account %s\n", account_name));
1792 if (asprintf(&filter, "(samAccountName=%s)", account_name) == -1) {
1793 return kvno;
1795 ret = ads_search(ads, &res, filter, attrs);
1796 SAFE_FREE(filter);
1797 if (!ADS_ERR_OK(ret) || (ads_count_replies(ads, res) != 1)) {
1798 DEBUG(1,("ads_get_kvno: Account for %s not found.\n", account_name));
1799 ads_msgfree(ads, res);
1800 return kvno;
1803 dn_string = ads_get_dn(ads, talloc_tos(), res);
1804 if (!dn_string) {
1805 DEBUG(0,("ads_get_kvno: out of memory.\n"));
1806 ads_msgfree(ads, res);
1807 return kvno;
1809 DEBUG(5,("ads_get_kvno: Using: %s\n", dn_string));
1810 TALLOC_FREE(dn_string);
1812 /* ---------------------------------------------------------
1813 * 0 is returned as a default KVNO from this point on...
1814 * This is done because Windows 2000 does not support key
1815 * version numbers. Chances are that a failure in the next
1816 * step is simply due to Windows 2000 being used for a
1817 * domain controller. */
1818 kvno = 0;
1820 if (!ads_pull_uint32(ads, res, "msDS-KeyVersionNumber", &kvno)) {
1821 DEBUG(3,("ads_get_kvno: Error Determining KVNO!\n"));
1822 DEBUG(3,("ads_get_kvno: Windows 2000 does not support KVNO's, so this may be normal.\n"));
1823 ads_msgfree(ads, res);
1824 return kvno;
1827 /* Success */
1828 DEBUG(5,("ads_get_kvno: Looked Up KVNO of: %d\n", kvno));
1829 ads_msgfree(ads, res);
1830 return kvno;
1834 * Determines the computer account's current KVNO via an LDAP lookup
1835 * @param ads An initialized ADS_STRUCT
1836 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
1837 * @return the kvno for the computer account, or -1 in case of a failure.
1840 uint32_t ads_get_machine_kvno(ADS_STRUCT *ads, const char *machine_name)
1842 char *computer_account = NULL;
1843 uint32_t kvno = -1;
1845 if (asprintf(&computer_account, "%s$", machine_name) < 0) {
1846 return kvno;
1849 kvno = ads_get_kvno(ads, computer_account);
1850 free(computer_account);
1852 return kvno;
1856 * This clears out all registered spn's for a given hostname
1857 * @param ads An initilaized ADS_STRUCT
1858 * @param machine_name the NetBIOS name of the computer.
1859 * @return 0 upon success, non-zero otherwise.
1862 ADS_STATUS ads_clear_service_principal_names(ADS_STRUCT *ads, const char *machine_name)
1864 TALLOC_CTX *ctx;
1865 LDAPMessage *res = NULL;
1866 ADS_MODLIST mods;
1867 const char *servicePrincipalName[1] = {NULL};
1868 ADS_STATUS ret = ADS_ERROR(LDAP_SUCCESS);
1869 char *dn_string = NULL;
1871 ret = ads_find_machine_acct(ads, &res, machine_name);
1872 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
1873 DEBUG(5,("ads_clear_service_principal_names: WARNING: Host Account for %s not found... skipping operation.\n", machine_name));
1874 DEBUG(5,("ads_clear_service_principal_names: WARNING: Service Principals for %s have NOT been cleared.\n", machine_name));
1875 ads_msgfree(ads, res);
1876 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1879 DEBUG(5,("ads_clear_service_principal_names: Host account for %s found\n", machine_name));
1880 ctx = talloc_init("ads_clear_service_principal_names");
1881 if (!ctx) {
1882 ads_msgfree(ads, res);
1883 return ADS_ERROR(LDAP_NO_MEMORY);
1886 if (!(mods = ads_init_mods(ctx))) {
1887 talloc_destroy(ctx);
1888 ads_msgfree(ads, res);
1889 return ADS_ERROR(LDAP_NO_MEMORY);
1891 ret = ads_mod_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
1892 if (!ADS_ERR_OK(ret)) {
1893 DEBUG(1,("ads_clear_service_principal_names: Error creating strlist.\n"));
1894 ads_msgfree(ads, res);
1895 talloc_destroy(ctx);
1896 return ret;
1898 dn_string = ads_get_dn(ads, talloc_tos(), res);
1899 if (!dn_string) {
1900 talloc_destroy(ctx);
1901 ads_msgfree(ads, res);
1902 return ADS_ERROR(LDAP_NO_MEMORY);
1904 ret = ads_gen_mod(ads, dn_string, mods);
1905 TALLOC_FREE(dn_string);
1906 if (!ADS_ERR_OK(ret)) {
1907 DEBUG(1,("ads_clear_service_principal_names: Error: Updating Service Principals for machine %s in LDAP\n",
1908 machine_name));
1909 ads_msgfree(ads, res);
1910 talloc_destroy(ctx);
1911 return ret;
1914 ads_msgfree(ads, res);
1915 talloc_destroy(ctx);
1916 return ret;
1920 * @brief Search for an element in a string array.
1922 * @param[in] el_array The string array to search.
1924 * @param[in] num_el The number of elements in the string array.
1926 * @param[in] el The string to search.
1928 * @return True if found, false if not.
1930 bool ads_element_in_array(const char **el_array, size_t num_el, const char *el)
1932 size_t i;
1934 if (el_array == NULL || num_el == 0 || el == NULL) {
1935 return false;
1938 for (i = 0; i < num_el && el_array[i] != NULL; i++) {
1939 int cmp;
1941 cmp = strcasecmp_m(el_array[i], el);
1942 if (cmp == 0) {
1943 return true;
1947 return false;
1951 * @brief This gets the service principal names of an existing computer account.
1953 * @param[in] mem_ctx The memory context to use to allocate the spn array.
1955 * @param[in] ads The ADS context to use.
1957 * @param[in] machine_name The NetBIOS name of the computer, which is used to
1958 * identify the computer account.
1960 * @param[in] spn_array A pointer to store the array for SPNs.
1962 * @param[in] num_spns The number of principals stored in the array.
1964 * @return 0 on success, or a ADS error if a failure occured.
1966 ADS_STATUS ads_get_service_principal_names(TALLOC_CTX *mem_ctx,
1967 ADS_STRUCT *ads,
1968 const char *machine_name,
1969 char ***spn_array,
1970 size_t *num_spns)
1972 ADS_STATUS status;
1973 LDAPMessage *res = NULL;
1974 int count;
1976 status = ads_find_machine_acct(ads,
1977 &res,
1978 machine_name);
1979 if (!ADS_ERR_OK(status)) {
1980 DEBUG(1,("Host Account for %s not found... skipping operation.\n",
1981 machine_name));
1982 return status;
1985 count = ads_count_replies(ads, res);
1986 if (count != 1) {
1987 status = ADS_ERROR(LDAP_NO_SUCH_OBJECT);
1988 goto done;
1991 *spn_array = ads_pull_strings(ads,
1992 mem_ctx,
1993 res,
1994 "servicePrincipalName",
1995 num_spns);
1997 done:
1998 ads_msgfree(ads, res);
2000 return status;
2004 * This adds a service principal name to an existing computer account
2005 * (found by hostname) in AD.
2006 * @param ads An initialized ADS_STRUCT
2007 * @param machine_name the NetBIOS name of the computer, which is used to identify the computer account.
2008 * @param my_fqdn The fully qualified DNS name of the machine
2009 * @param spn A string of the service principal to add, i.e. 'host'
2010 * @return 0 upon sucess, or non-zero if a failure occurs
2013 ADS_STATUS ads_add_service_principal_name(ADS_STRUCT *ads, const char *machine_name,
2014 const char *my_fqdn, const char *spn)
2016 ADS_STATUS ret;
2017 TALLOC_CTX *ctx;
2018 LDAPMessage *res = NULL;
2019 char *psp1, *psp2;
2020 ADS_MODLIST mods;
2021 char *dn_string = NULL;
2022 const char *servicePrincipalName[3] = {NULL, NULL, NULL};
2024 ret = ads_find_machine_acct(ads, &res, machine_name);
2025 if (!ADS_ERR_OK(ret) || ads_count_replies(ads, res) != 1) {
2026 DEBUG(1,("ads_add_service_principal_name: WARNING: Host Account for %s not found... skipping operation.\n",
2027 machine_name));
2028 DEBUG(1,("ads_add_service_principal_name: WARNING: Service Principal '%s/%s@%s' has NOT been added.\n",
2029 spn, machine_name, ads->config.realm));
2030 ads_msgfree(ads, res);
2031 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
2034 DEBUG(1,("ads_add_service_principal_name: Host account for %s found\n", machine_name));
2035 if (!(ctx = talloc_init("ads_add_service_principal_name"))) {
2036 ads_msgfree(ads, res);
2037 return ADS_ERROR(LDAP_NO_MEMORY);
2040 /* add short name spn */
2042 if ( (psp1 = talloc_asprintf(ctx, "%s/%s", spn, machine_name)) == NULL ) {
2043 talloc_destroy(ctx);
2044 ads_msgfree(ads, res);
2045 return ADS_ERROR(LDAP_NO_MEMORY);
2047 if (!strlower_m(&psp1[strlen(spn) + 1])) {
2048 ret = ADS_ERROR(LDAP_NO_MEMORY);
2049 goto out;
2051 servicePrincipalName[0] = psp1;
2053 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2054 psp1, machine_name));
2057 /* add fully qualified spn */
2059 if ( (psp2 = talloc_asprintf(ctx, "%s/%s", spn, my_fqdn)) == NULL ) {
2060 ret = ADS_ERROR(LDAP_NO_MEMORY);
2061 goto out;
2063 if (!strlower_m(&psp2[strlen(spn) + 1])) {
2064 ret = ADS_ERROR(LDAP_NO_MEMORY);
2065 goto out;
2067 servicePrincipalName[1] = psp2;
2069 DEBUG(5,("ads_add_service_principal_name: INFO: Adding %s to host %s\n",
2070 psp2, machine_name));
2072 if ( (mods = ads_init_mods(ctx)) == NULL ) {
2073 ret = ADS_ERROR(LDAP_NO_MEMORY);
2074 goto out;
2077 ret = ads_add_strlist(ctx, &mods, "servicePrincipalName", servicePrincipalName);
2078 if (!ADS_ERR_OK(ret)) {
2079 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2080 goto out;
2083 if ( (dn_string = ads_get_dn(ads, ctx, res)) == NULL ) {
2084 ret = ADS_ERROR(LDAP_NO_MEMORY);
2085 goto out;
2088 ret = ads_gen_mod(ads, dn_string, mods);
2089 if (!ADS_ERR_OK(ret)) {
2090 DEBUG(1,("ads_add_service_principal_name: Error: Updating Service Principals in LDAP\n"));
2091 goto out;
2094 out:
2095 TALLOC_FREE( ctx );
2096 ads_msgfree(ads, res);
2097 return ret;
2101 * adds a machine account to the ADS server
2102 * @param ads An intialized ADS_STRUCT
2103 * @param machine_name - the NetBIOS machine name of this account.
2104 * @param account_type A number indicating the type of account to create
2105 * @param org_unit The LDAP path in which to place this account
2106 * @return 0 upon success, or non-zero otherwise
2109 ADS_STATUS ads_create_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2110 const char *org_unit)
2112 ADS_STATUS ret;
2113 char *samAccountName, *controlstr;
2114 TALLOC_CTX *ctx;
2115 ADS_MODLIST mods;
2116 char *machine_escaped = NULL;
2117 char *new_dn;
2118 const char *objectClass[] = {"top", "person", "organizationalPerson",
2119 "user", "computer", NULL};
2120 LDAPMessage *res = NULL;
2121 uint32 acct_control = ( UF_WORKSTATION_TRUST_ACCOUNT |\
2122 UF_DONT_EXPIRE_PASSWD |\
2123 UF_ACCOUNTDISABLE );
2125 if (!(ctx = talloc_init("ads_add_machine_acct")))
2126 return ADS_ERROR(LDAP_NO_MEMORY);
2128 ret = ADS_ERROR(LDAP_NO_MEMORY);
2130 machine_escaped = escape_rdn_val_string_alloc(machine_name);
2131 if (!machine_escaped) {
2132 goto done;
2135 new_dn = talloc_asprintf(ctx, "cn=%s,%s", machine_escaped, org_unit);
2136 samAccountName = talloc_asprintf(ctx, "%s$", machine_name);
2138 if ( !new_dn || !samAccountName ) {
2139 goto done;
2142 #ifndef ENCTYPE_ARCFOUR_HMAC
2143 acct_control |= UF_USE_DES_KEY_ONLY;
2144 #endif
2146 if (!(controlstr = talloc_asprintf(ctx, "%u", acct_control))) {
2147 goto done;
2150 if (!(mods = ads_init_mods(ctx))) {
2151 goto done;
2154 ads_mod_str(ctx, &mods, "cn", machine_name);
2155 ads_mod_str(ctx, &mods, "sAMAccountName", samAccountName);
2156 ads_mod_strlist(ctx, &mods, "objectClass", objectClass);
2157 ads_mod_str(ctx, &mods, "userAccountControl", controlstr);
2159 ret = ads_gen_add(ads, new_dn, mods);
2161 done:
2162 SAFE_FREE(machine_escaped);
2163 ads_msgfree(ads, res);
2164 talloc_destroy(ctx);
2166 return ret;
2170 * move a machine account to another OU on the ADS server
2171 * @param ads - An intialized ADS_STRUCT
2172 * @param machine_name - the NetBIOS machine name of this account.
2173 * @param org_unit - The LDAP path in which to place this account
2174 * @param moved - whether we moved the machine account (optional)
2175 * @return 0 upon success, or non-zero otherwise
2178 ADS_STATUS ads_move_machine_acct(ADS_STRUCT *ads, const char *machine_name,
2179 const char *org_unit, bool *moved)
2181 ADS_STATUS rc;
2182 int ldap_status;
2183 LDAPMessage *res = NULL;
2184 char *filter = NULL;
2185 char *computer_dn = NULL;
2186 char *parent_dn;
2187 char *computer_rdn = NULL;
2188 bool need_move = False;
2190 if (asprintf(&filter, "(samAccountName=%s$)", machine_name) == -1) {
2191 rc = ADS_ERROR(LDAP_NO_MEMORY);
2192 goto done;
2195 /* Find pre-existing machine */
2196 rc = ads_search(ads, &res, filter, NULL);
2197 if (!ADS_ERR_OK(rc)) {
2198 goto done;
2201 computer_dn = ads_get_dn(ads, talloc_tos(), res);
2202 if (!computer_dn) {
2203 rc = ADS_ERROR(LDAP_NO_MEMORY);
2204 goto done;
2207 parent_dn = ads_parent_dn(computer_dn);
2208 if (strequal(parent_dn, org_unit)) {
2209 goto done;
2212 need_move = True;
2214 if (asprintf(&computer_rdn, "CN=%s", machine_name) == -1) {
2215 rc = ADS_ERROR(LDAP_NO_MEMORY);
2216 goto done;
2219 ldap_status = ldap_rename_s(ads->ldap.ld, computer_dn, computer_rdn,
2220 org_unit, 1, NULL, NULL);
2221 rc = ADS_ERROR(ldap_status);
2223 done:
2224 ads_msgfree(ads, res);
2225 SAFE_FREE(filter);
2226 TALLOC_FREE(computer_dn);
2227 SAFE_FREE(computer_rdn);
2229 if (!ADS_ERR_OK(rc)) {
2230 need_move = False;
2233 if (moved) {
2234 *moved = need_move;
2237 return rc;
2241 dump a binary result from ldap
2243 static void dump_binary(ADS_STRUCT *ads, const char *field, struct berval **values)
2245 int i, j;
2246 for (i=0; values[i]; i++) {
2247 printf("%s: ", field);
2248 for (j=0; j<values[i]->bv_len; j++) {
2249 printf("%02X", (unsigned char)values[i]->bv_val[j]);
2251 printf("\n");
2255 static void dump_guid(ADS_STRUCT *ads, const char *field, struct berval **values)
2257 int i;
2258 for (i=0; values[i]; i++) {
2259 NTSTATUS status;
2260 DATA_BLOB in = data_blob_const(values[i]->bv_val, values[i]->bv_len);
2261 struct GUID guid;
2263 status = GUID_from_ndr_blob(&in, &guid);
2264 if (NT_STATUS_IS_OK(status)) {
2265 printf("%s: %s\n", field, GUID_string(talloc_tos(), &guid));
2266 } else {
2267 printf("%s: INVALID GUID\n", field);
2273 dump a sid result from ldap
2275 static void dump_sid(ADS_STRUCT *ads, const char *field, struct berval **values)
2277 int i;
2278 for (i=0; values[i]; i++) {
2279 struct dom_sid sid;
2280 fstring tmp;
2281 if (!sid_parse(values[i]->bv_val, values[i]->bv_len, &sid)) {
2282 return;
2284 printf("%s: %s\n", field, sid_to_fstring(tmp, &sid));
2289 dump ntSecurityDescriptor
2291 static void dump_sd(ADS_STRUCT *ads, const char *filed, struct berval **values)
2293 TALLOC_CTX *frame = talloc_stackframe();
2294 struct security_descriptor *psd;
2295 NTSTATUS status;
2297 status = unmarshall_sec_desc(talloc_tos(), (uint8 *)values[0]->bv_val,
2298 values[0]->bv_len, &psd);
2299 if (!NT_STATUS_IS_OK(status)) {
2300 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2301 nt_errstr(status)));
2302 TALLOC_FREE(frame);
2303 return;
2306 if (psd) {
2307 ads_disp_sd(ads, talloc_tos(), psd);
2310 TALLOC_FREE(frame);
2314 dump a string result from ldap
2316 static void dump_string(const char *field, char **values)
2318 int i;
2319 for (i=0; values[i]; i++) {
2320 printf("%s: %s\n", field, values[i]);
2325 dump a field from LDAP on stdout
2326 used for debugging
2329 static bool ads_dump_field(ADS_STRUCT *ads, char *field, void **values, void *data_area)
2331 const struct {
2332 const char *name;
2333 bool string;
2334 void (*handler)(ADS_STRUCT *, const char *, struct berval **);
2335 } handlers[] = {
2336 {"objectGUID", False, dump_guid},
2337 {"netbootGUID", False, dump_guid},
2338 {"nTSecurityDescriptor", False, dump_sd},
2339 {"dnsRecord", False, dump_binary},
2340 {"objectSid", False, dump_sid},
2341 {"tokenGroups", False, dump_sid},
2342 {"tokenGroupsNoGCAcceptable", False, dump_sid},
2343 {"tokengroupsGlobalandUniversal", False, dump_sid},
2344 {"mS-DS-CreatorSID", False, dump_sid},
2345 {"msExchMailboxGuid", False, dump_guid},
2346 {NULL, True, NULL}
2348 int i;
2350 if (!field) { /* must be end of an entry */
2351 printf("\n");
2352 return False;
2355 for (i=0; handlers[i].name; i++) {
2356 if (strcasecmp_m(handlers[i].name, field) == 0) {
2357 if (!values) /* first time, indicate string or not */
2358 return handlers[i].string;
2359 handlers[i].handler(ads, field, (struct berval **) values);
2360 break;
2363 if (!handlers[i].name) {
2364 if (!values) /* first time, indicate string conversion */
2365 return True;
2366 dump_string(field, (char **)values);
2368 return False;
2372 * Dump a result from LDAP on stdout
2373 * used for debugging
2374 * @param ads connection to ads server
2375 * @param res Results to dump
2378 void ads_dump(ADS_STRUCT *ads, LDAPMessage *res)
2380 ads_process_results(ads, res, ads_dump_field, NULL);
2384 * Walk through results, calling a function for each entry found.
2385 * The function receives a field name, a berval * array of values,
2386 * and a data area passed through from the start. The function is
2387 * called once with null for field and values at the end of each
2388 * entry.
2389 * @param ads connection to ads server
2390 * @param res Results to process
2391 * @param fn Function for processing each result
2392 * @param data_area user-defined area to pass to function
2394 void ads_process_results(ADS_STRUCT *ads, LDAPMessage *res,
2395 bool (*fn)(ADS_STRUCT *, char *, void **, void *),
2396 void *data_area)
2398 LDAPMessage *msg;
2399 TALLOC_CTX *ctx;
2400 size_t converted_size;
2402 if (!(ctx = talloc_init("ads_process_results")))
2403 return;
2405 for (msg = ads_first_entry(ads, res); msg;
2406 msg = ads_next_entry(ads, msg)) {
2407 char *utf8_field;
2408 BerElement *b;
2410 for (utf8_field=ldap_first_attribute(ads->ldap.ld,
2411 (LDAPMessage *)msg,&b);
2412 utf8_field;
2413 utf8_field=ldap_next_attribute(ads->ldap.ld,
2414 (LDAPMessage *)msg,b)) {
2415 struct berval **ber_vals;
2416 char **str_vals;
2417 char **utf8_vals;
2418 char *field;
2419 bool string;
2421 if (!pull_utf8_talloc(ctx, &field, utf8_field,
2422 &converted_size))
2424 DEBUG(0,("ads_process_results: "
2425 "pull_utf8_talloc failed: %s",
2426 strerror(errno)));
2429 string = fn(ads, field, NULL, data_area);
2431 if (string) {
2432 const char **p;
2434 utf8_vals = ldap_get_values(ads->ldap.ld,
2435 (LDAPMessage *)msg, field);
2436 p = discard_const_p(const char *, utf8_vals);
2437 str_vals = ads_pull_strvals(ctx, p);
2438 fn(ads, field, (void **) str_vals, data_area);
2439 ldap_value_free(utf8_vals);
2440 } else {
2441 ber_vals = ldap_get_values_len(ads->ldap.ld,
2442 (LDAPMessage *)msg, field);
2443 fn(ads, field, (void **) ber_vals, data_area);
2445 ldap_value_free_len(ber_vals);
2447 ldap_memfree(utf8_field);
2449 ber_free(b, 0);
2450 talloc_free_children(ctx);
2451 fn(ads, NULL, NULL, data_area); /* completed an entry */
2454 talloc_destroy(ctx);
2458 * count how many replies are in a LDAPMessage
2459 * @param ads connection to ads server
2460 * @param res Results to count
2461 * @return number of replies
2463 int ads_count_replies(ADS_STRUCT *ads, void *res)
2465 return ldap_count_entries(ads->ldap.ld, (LDAPMessage *)res);
2469 * pull the first entry from a ADS result
2470 * @param ads connection to ads server
2471 * @param res Results of search
2472 * @return first entry from result
2474 LDAPMessage *ads_first_entry(ADS_STRUCT *ads, LDAPMessage *res)
2476 return ldap_first_entry(ads->ldap.ld, res);
2480 * pull the next entry from a ADS result
2481 * @param ads connection to ads server
2482 * @param res Results of search
2483 * @return next entry from result
2485 LDAPMessage *ads_next_entry(ADS_STRUCT *ads, LDAPMessage *res)
2487 return ldap_next_entry(ads->ldap.ld, res);
2491 * pull the first message from a ADS result
2492 * @param ads connection to ads server
2493 * @param res Results of search
2494 * @return first message from result
2496 LDAPMessage *ads_first_message(ADS_STRUCT *ads, LDAPMessage *res)
2498 return ldap_first_message(ads->ldap.ld, res);
2502 * pull the next message from a ADS result
2503 * @param ads connection to ads server
2504 * @param res Results of search
2505 * @return next message from result
2507 LDAPMessage *ads_next_message(ADS_STRUCT *ads, LDAPMessage *res)
2509 return ldap_next_message(ads->ldap.ld, res);
2513 * pull a single string from a ADS result
2514 * @param ads connection to ads server
2515 * @param mem_ctx TALLOC_CTX to use for allocating result string
2516 * @param msg Results of search
2517 * @param field Attribute to retrieve
2518 * @return Result string in talloc context
2520 char *ads_pull_string(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, LDAPMessage *msg,
2521 const char *field)
2523 char **values;
2524 char *ret = NULL;
2525 char *ux_string;
2526 size_t converted_size;
2528 values = ldap_get_values(ads->ldap.ld, msg, field);
2529 if (!values)
2530 return NULL;
2532 if (values[0] && pull_utf8_talloc(mem_ctx, &ux_string, values[0],
2533 &converted_size))
2535 ret = ux_string;
2537 ldap_value_free(values);
2538 return ret;
2542 * pull an array of strings from a ADS result
2543 * @param ads connection to ads server
2544 * @param mem_ctx TALLOC_CTX to use for allocating result string
2545 * @param msg Results of search
2546 * @param field Attribute to retrieve
2547 * @return Result strings in talloc context
2549 char **ads_pull_strings(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2550 LDAPMessage *msg, const char *field,
2551 size_t *num_values)
2553 char **values;
2554 char **ret = NULL;
2555 int i;
2556 size_t converted_size;
2558 values = ldap_get_values(ads->ldap.ld, msg, field);
2559 if (!values)
2560 return NULL;
2562 *num_values = ldap_count_values(values);
2564 ret = talloc_array(mem_ctx, char *, *num_values + 1);
2565 if (!ret) {
2566 ldap_value_free(values);
2567 return NULL;
2570 for (i=0;i<*num_values;i++) {
2571 if (!pull_utf8_talloc(mem_ctx, &ret[i], values[i],
2572 &converted_size))
2574 ldap_value_free(values);
2575 return NULL;
2578 ret[i] = NULL;
2580 ldap_value_free(values);
2581 return ret;
2585 * pull an array of strings from a ADS result
2586 * (handle large multivalue attributes with range retrieval)
2587 * @param ads connection to ads server
2588 * @param mem_ctx TALLOC_CTX to use for allocating result string
2589 * @param msg Results of search
2590 * @param field Attribute to retrieve
2591 * @param current_strings strings returned by a previous call to this function
2592 * @param next_attribute The next query should ask for this attribute
2593 * @param num_values How many values did we get this time?
2594 * @param more_values Are there more values to get?
2595 * @return Result strings in talloc context
2597 char **ads_pull_strings_range(ADS_STRUCT *ads,
2598 TALLOC_CTX *mem_ctx,
2599 LDAPMessage *msg, const char *field,
2600 char **current_strings,
2601 const char **next_attribute,
2602 size_t *num_strings,
2603 bool *more_strings)
2605 char *attr;
2606 char *expected_range_attrib, *range_attr;
2607 BerElement *ptr = NULL;
2608 char **strings;
2609 char **new_strings;
2610 size_t num_new_strings;
2611 unsigned long int range_start;
2612 unsigned long int range_end;
2614 /* we might have been given the whole lot anyway */
2615 if ((strings = ads_pull_strings(ads, mem_ctx, msg, field, num_strings))) {
2616 *more_strings = False;
2617 return strings;
2620 expected_range_attrib = talloc_asprintf(mem_ctx, "%s;Range=", field);
2622 /* look for Range result */
2623 for (attr = ldap_first_attribute(ads->ldap.ld, (LDAPMessage *)msg, &ptr);
2624 attr;
2625 attr = ldap_next_attribute(ads->ldap.ld, (LDAPMessage *)msg, ptr)) {
2626 /* we ignore the fact that this is utf8, as all attributes are ascii... */
2627 if (strnequal(attr, expected_range_attrib, strlen(expected_range_attrib))) {
2628 range_attr = attr;
2629 break;
2631 ldap_memfree(attr);
2633 if (!attr) {
2634 ber_free(ptr, 0);
2635 /* nothing here - this field is just empty */
2636 *more_strings = False;
2637 return NULL;
2640 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-%lu",
2641 &range_start, &range_end) == 2) {
2642 *more_strings = True;
2643 } else {
2644 if (sscanf(&range_attr[strlen(expected_range_attrib)], "%lu-*",
2645 &range_start) == 1) {
2646 *more_strings = False;
2647 } else {
2648 DEBUG(1, ("ads_pull_strings_range: Cannot parse Range attriubte (%s)\n",
2649 range_attr));
2650 ldap_memfree(range_attr);
2651 *more_strings = False;
2652 return NULL;
2656 if ((*num_strings) != range_start) {
2657 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) doesn't start at %u, but at %lu"
2658 " - aborting range retreival\n",
2659 range_attr, (unsigned int)(*num_strings) + 1, range_start));
2660 ldap_memfree(range_attr);
2661 *more_strings = False;
2662 return NULL;
2665 new_strings = ads_pull_strings(ads, mem_ctx, msg, range_attr, &num_new_strings);
2667 if (*more_strings && ((*num_strings + num_new_strings) != (range_end + 1))) {
2668 DEBUG(1, ("ads_pull_strings_range: Range attribute (%s) tells us we have %lu "
2669 "strings in this bunch, but we only got %lu - aborting range retreival\n",
2670 range_attr, (unsigned long int)range_end - range_start + 1,
2671 (unsigned long int)num_new_strings));
2672 ldap_memfree(range_attr);
2673 *more_strings = False;
2674 return NULL;
2677 strings = talloc_realloc(mem_ctx, current_strings, char *,
2678 *num_strings + num_new_strings);
2680 if (strings == NULL) {
2681 ldap_memfree(range_attr);
2682 *more_strings = False;
2683 return NULL;
2686 if (new_strings && num_new_strings) {
2687 memcpy(&strings[*num_strings], new_strings,
2688 sizeof(*new_strings) * num_new_strings);
2691 (*num_strings) += num_new_strings;
2693 if (*more_strings) {
2694 *next_attribute = talloc_asprintf(mem_ctx,
2695 "%s;range=%d-*",
2696 field,
2697 (int)*num_strings);
2699 if (!*next_attribute) {
2700 DEBUG(1, ("talloc_asprintf for next attribute failed!\n"));
2701 ldap_memfree(range_attr);
2702 *more_strings = False;
2703 return NULL;
2707 ldap_memfree(range_attr);
2709 return strings;
2713 * pull a single uint32 from a ADS result
2714 * @param ads connection to ads server
2715 * @param msg Results of search
2716 * @param field Attribute to retrieve
2717 * @param v Pointer to int to store result
2718 * @return boolean inidicating success
2720 bool ads_pull_uint32(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2721 uint32 *v)
2723 char **values;
2725 values = ldap_get_values(ads->ldap.ld, msg, field);
2726 if (!values)
2727 return False;
2728 if (!values[0]) {
2729 ldap_value_free(values);
2730 return False;
2733 *v = atoi(values[0]);
2734 ldap_value_free(values);
2735 return True;
2739 * pull a single objectGUID from an ADS result
2740 * @param ads connection to ADS server
2741 * @param msg results of search
2742 * @param guid 37-byte area to receive text guid
2743 * @return boolean indicating success
2745 bool ads_pull_guid(ADS_STRUCT *ads, LDAPMessage *msg, struct GUID *guid)
2747 DATA_BLOB blob;
2748 NTSTATUS status;
2750 if (!smbldap_talloc_single_blob(talloc_tos(), ads->ldap.ld, msg, "objectGUID",
2751 &blob)) {
2752 return false;
2755 status = GUID_from_ndr_blob(&blob, guid);
2756 talloc_free(blob.data);
2757 return NT_STATUS_IS_OK(status);
2762 * pull a single struct dom_sid from a ADS result
2763 * @param ads connection to ads server
2764 * @param msg Results of search
2765 * @param field Attribute to retrieve
2766 * @param sid Pointer to sid to store result
2767 * @return boolean inidicating success
2769 bool ads_pull_sid(ADS_STRUCT *ads, LDAPMessage *msg, const char *field,
2770 struct dom_sid *sid)
2772 return smbldap_pull_sid(ads->ldap.ld, msg, field, sid);
2776 * pull an array of struct dom_sids from a ADS result
2777 * @param ads connection to ads server
2778 * @param mem_ctx TALLOC_CTX for allocating sid array
2779 * @param msg Results of search
2780 * @param field Attribute to retrieve
2781 * @param sids pointer to sid array to allocate
2782 * @return the count of SIDs pulled
2784 int ads_pull_sids(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2785 LDAPMessage *msg, const char *field, struct dom_sid **sids)
2787 struct berval **values;
2788 bool ret;
2789 int count, i;
2791 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2793 if (!values)
2794 return 0;
2796 for (i=0; values[i]; i++)
2797 /* nop */ ;
2799 if (i) {
2800 (*sids) = talloc_array(mem_ctx, struct dom_sid, i);
2801 if (!(*sids)) {
2802 ldap_value_free_len(values);
2803 return 0;
2805 } else {
2806 (*sids) = NULL;
2809 count = 0;
2810 for (i=0; values[i]; i++) {
2811 ret = sid_parse(values[i]->bv_val, values[i]->bv_len, &(*sids)[count]);
2812 if (ret) {
2813 DEBUG(10, ("pulling SID: %s\n",
2814 sid_string_dbg(&(*sids)[count])));
2815 count++;
2819 ldap_value_free_len(values);
2820 return count;
2824 * pull a struct security_descriptor from a ADS result
2825 * @param ads connection to ads server
2826 * @param mem_ctx TALLOC_CTX for allocating sid array
2827 * @param msg Results of search
2828 * @param field Attribute to retrieve
2829 * @param sd Pointer to *struct security_descriptor to store result (talloc()ed)
2830 * @return boolean inidicating success
2832 bool ads_pull_sd(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2833 LDAPMessage *msg, const char *field,
2834 struct security_descriptor **sd)
2836 struct berval **values;
2837 bool ret = true;
2839 values = ldap_get_values_len(ads->ldap.ld, msg, field);
2841 if (!values) return false;
2843 if (values[0]) {
2844 NTSTATUS status;
2845 status = unmarshall_sec_desc(mem_ctx,
2846 (uint8 *)values[0]->bv_val,
2847 values[0]->bv_len, sd);
2848 if (!NT_STATUS_IS_OK(status)) {
2849 DEBUG(0, ("unmarshall_sec_desc failed: %s\n",
2850 nt_errstr(status)));
2851 ret = false;
2855 ldap_value_free_len(values);
2856 return ret;
2860 * in order to support usernames longer than 21 characters we need to
2861 * use both the sAMAccountName and the userPrincipalName attributes
2862 * It seems that not all users have the userPrincipalName attribute set
2864 * @param ads connection to ads server
2865 * @param mem_ctx TALLOC_CTX for allocating sid array
2866 * @param msg Results of search
2867 * @return the username
2869 char *ads_pull_username(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx,
2870 LDAPMessage *msg)
2872 #if 0 /* JERRY */
2873 char *ret, *p;
2875 /* lookup_name() only works on the sAMAccountName to
2876 returning the username portion of userPrincipalName
2877 breaks winbindd_getpwnam() */
2879 ret = ads_pull_string(ads, mem_ctx, msg, "userPrincipalName");
2880 if (ret && (p = strchr_m(ret, '@'))) {
2881 *p = 0;
2882 return ret;
2884 #endif
2885 return ads_pull_string(ads, mem_ctx, msg, "sAMAccountName");
2890 * find the update serial number - this is the core of the ldap cache
2891 * @param ads connection to ads server
2892 * @param ads connection to ADS server
2893 * @param usn Pointer to retrieved update serial number
2894 * @return status of search
2896 ADS_STATUS ads_USN(ADS_STRUCT *ads, uint32 *usn)
2898 const char *attrs[] = {"highestCommittedUSN", NULL};
2899 ADS_STATUS status;
2900 LDAPMessage *res;
2902 status = ads_do_search_retry(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2903 if (!ADS_ERR_OK(status))
2904 return status;
2906 if (ads_count_replies(ads, res) != 1) {
2907 ads_msgfree(ads, res);
2908 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2911 if (!ads_pull_uint32(ads, res, "highestCommittedUSN", usn)) {
2912 ads_msgfree(ads, res);
2913 return ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
2916 ads_msgfree(ads, res);
2917 return ADS_SUCCESS;
2920 /* parse a ADS timestring - typical string is
2921 '20020917091222.0Z0' which means 09:12.22 17th September
2922 2002, timezone 0 */
2923 static time_t ads_parse_time(const char *str)
2925 struct tm tm;
2927 ZERO_STRUCT(tm);
2929 if (sscanf(str, "%4d%2d%2d%2d%2d%2d",
2930 &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
2931 &tm.tm_hour, &tm.tm_min, &tm.tm_sec) != 6) {
2932 return 0;
2934 tm.tm_year -= 1900;
2935 tm.tm_mon -= 1;
2937 return timegm(&tm);
2940 /********************************************************************
2941 ********************************************************************/
2943 ADS_STATUS ads_current_time(ADS_STRUCT *ads)
2945 const char *attrs[] = {"currentTime", NULL};
2946 ADS_STATUS status;
2947 LDAPMessage *res;
2948 char *timestr;
2949 TALLOC_CTX *ctx;
2950 ADS_STRUCT *ads_s = ads;
2952 if (!(ctx = talloc_init("ads_current_time"))) {
2953 return ADS_ERROR(LDAP_NO_MEMORY);
2956 /* establish a new ldap tcp session if necessary */
2958 if ( !ads->ldap.ld ) {
2959 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
2960 ads->server.ldap_server )) == NULL )
2962 goto done;
2964 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
2965 status = ads_connect( ads_s );
2966 if ( !ADS_ERR_OK(status))
2967 goto done;
2970 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
2971 if (!ADS_ERR_OK(status)) {
2972 goto done;
2975 timestr = ads_pull_string(ads_s, ctx, res, "currentTime");
2976 if (!timestr) {
2977 ads_msgfree(ads_s, res);
2978 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
2979 goto done;
2982 /* but save the time and offset in the original ADS_STRUCT */
2984 ads->config.current_time = ads_parse_time(timestr);
2986 if (ads->config.current_time != 0) {
2987 ads->auth.time_offset = ads->config.current_time - time(NULL);
2988 DEBUG(4,("KDC time offset is %d seconds\n", ads->auth.time_offset));
2991 ads_msgfree(ads, res);
2993 status = ADS_SUCCESS;
2995 done:
2996 /* free any temporary ads connections */
2997 if ( ads_s != ads ) {
2998 ads_destroy( &ads_s );
3000 talloc_destroy(ctx);
3002 return status;
3005 /********************************************************************
3006 ********************************************************************/
3008 ADS_STATUS ads_domain_func_level(ADS_STRUCT *ads, uint32 *val)
3010 const char *attrs[] = {"domainFunctionality", NULL};
3011 ADS_STATUS status;
3012 LDAPMessage *res;
3013 ADS_STRUCT *ads_s = ads;
3015 *val = DS_DOMAIN_FUNCTION_2000;
3017 /* establish a new ldap tcp session if necessary */
3019 if ( !ads->ldap.ld ) {
3020 if ( (ads_s = ads_init( ads->server.realm, ads->server.workgroup,
3021 ads->server.ldap_server )) == NULL )
3023 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3024 goto done;
3026 ads_s->auth.flags = ADS_AUTH_ANON_BIND;
3027 status = ads_connect( ads_s );
3028 if ( !ADS_ERR_OK(status))
3029 goto done;
3032 /* If the attribute does not exist assume it is a Windows 2000
3033 functional domain */
3035 status = ads_do_search(ads_s, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3036 if (!ADS_ERR_OK(status)) {
3037 if ( status.err.rc == LDAP_NO_SUCH_ATTRIBUTE ) {
3038 status = ADS_SUCCESS;
3040 goto done;
3043 if ( !ads_pull_uint32(ads_s, res, "domainFunctionality", val) ) {
3044 DEBUG(5,("ads_domain_func_level: Failed to pull the domainFunctionality attribute.\n"));
3046 DEBUG(3,("ads_domain_func_level: %d\n", *val));
3049 ads_msgfree(ads, res);
3051 done:
3052 /* free any temporary ads connections */
3053 if ( ads_s != ads ) {
3054 ads_destroy( &ads_s );
3057 return status;
3061 * find the domain sid for our domain
3062 * @param ads connection to ads server
3063 * @param sid Pointer to domain sid
3064 * @return status of search
3066 ADS_STATUS ads_domain_sid(ADS_STRUCT *ads, struct dom_sid *sid)
3068 const char *attrs[] = {"objectSid", NULL};
3069 LDAPMessage *res;
3070 ADS_STATUS rc;
3072 rc = ads_do_search_retry(ads, ads->config.bind_path, LDAP_SCOPE_BASE, "(objectclass=*)",
3073 attrs, &res);
3074 if (!ADS_ERR_OK(rc)) return rc;
3075 if (!ads_pull_sid(ads, res, "objectSid", sid)) {
3076 ads_msgfree(ads, res);
3077 return ADS_ERROR_SYSTEM(ENOENT);
3079 ads_msgfree(ads, res);
3081 return ADS_SUCCESS;
3085 * find our site name
3086 * @param ads connection to ads server
3087 * @param mem_ctx Pointer to talloc context
3088 * @param site_name Pointer to the sitename
3089 * @return status of search
3091 ADS_STATUS ads_site_dn(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char **site_name)
3093 ADS_STATUS status;
3094 LDAPMessage *res;
3095 const char *dn, *service_name;
3096 const char *attrs[] = { "dsServiceName", NULL };
3098 status = ads_do_search(ads, "", LDAP_SCOPE_BASE, "(objectclass=*)", attrs, &res);
3099 if (!ADS_ERR_OK(status)) {
3100 return status;
3103 service_name = ads_pull_string(ads, mem_ctx, res, "dsServiceName");
3104 if (service_name == NULL) {
3105 ads_msgfree(ads, res);
3106 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3109 ads_msgfree(ads, res);
3111 /* go up three levels */
3112 dn = ads_parent_dn(ads_parent_dn(ads_parent_dn(service_name)));
3113 if (dn == NULL) {
3114 return ADS_ERROR(LDAP_NO_MEMORY);
3117 *site_name = talloc_strdup(mem_ctx, dn);
3118 if (*site_name == NULL) {
3119 return ADS_ERROR(LDAP_NO_MEMORY);
3122 return status;
3124 dsServiceName: CN=NTDS Settings,CN=W2K3DC,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=ber,DC=suse,DC=de
3129 * find the site dn where a machine resides
3130 * @param ads connection to ads server
3131 * @param mem_ctx Pointer to talloc context
3132 * @param computer_name name of the machine
3133 * @param site_name Pointer to the sitename
3134 * @return status of search
3136 ADS_STATUS ads_site_dn_for_machine(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, const char *computer_name, const char **site_dn)
3138 ADS_STATUS status;
3139 LDAPMessage *res;
3140 const char *parent, *filter;
3141 char *config_context = NULL;
3142 char *dn;
3144 /* shortcut a query */
3145 if (strequal(computer_name, ads->config.ldap_server_name)) {
3146 return ads_site_dn(ads, mem_ctx, site_dn);
3149 status = ads_config_path(ads, mem_ctx, &config_context);
3150 if (!ADS_ERR_OK(status)) {
3151 return status;
3154 filter = talloc_asprintf(mem_ctx, "(cn=%s)", computer_name);
3155 if (filter == NULL) {
3156 return ADS_ERROR(LDAP_NO_MEMORY);
3159 status = ads_do_search(ads, config_context, LDAP_SCOPE_SUBTREE,
3160 filter, NULL, &res);
3161 if (!ADS_ERR_OK(status)) {
3162 return status;
3165 if (ads_count_replies(ads, res) != 1) {
3166 ads_msgfree(ads, res);
3167 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3170 dn = ads_get_dn(ads, mem_ctx, res);
3171 if (dn == NULL) {
3172 ads_msgfree(ads, res);
3173 return ADS_ERROR(LDAP_NO_MEMORY);
3176 /* go up three levels */
3177 parent = ads_parent_dn(ads_parent_dn(ads_parent_dn(dn)));
3178 if (parent == NULL) {
3179 ads_msgfree(ads, res);
3180 TALLOC_FREE(dn);
3181 return ADS_ERROR(LDAP_NO_MEMORY);
3184 *site_dn = talloc_strdup(mem_ctx, parent);
3185 if (*site_dn == NULL) {
3186 ads_msgfree(ads, res);
3187 TALLOC_FREE(dn);
3188 return ADS_ERROR(LDAP_NO_MEMORY);
3191 TALLOC_FREE(dn);
3192 ads_msgfree(ads, res);
3194 return status;
3198 * get the upn suffixes for a domain
3199 * @param ads connection to ads server
3200 * @param mem_ctx Pointer to talloc context
3201 * @param suffixes Pointer to an array of suffixes
3202 * @param num_suffixes Pointer to the number of suffixes
3203 * @return status of search
3205 ADS_STATUS ads_upn_suffixes(ADS_STRUCT *ads, TALLOC_CTX *mem_ctx, char ***suffixes, size_t *num_suffixes)
3207 ADS_STATUS status;
3208 LDAPMessage *res;
3209 const char *base;
3210 char *config_context = NULL;
3211 const char *attrs[] = { "uPNSuffixes", NULL };
3213 status = ads_config_path(ads, mem_ctx, &config_context);
3214 if (!ADS_ERR_OK(status)) {
3215 return status;
3218 base = talloc_asprintf(mem_ctx, "cn=Partitions,%s", config_context);
3219 if (base == NULL) {
3220 return ADS_ERROR(LDAP_NO_MEMORY);
3223 status = ads_search_dn(ads, &res, base, attrs);
3224 if (!ADS_ERR_OK(status)) {
3225 return status;
3228 if (ads_count_replies(ads, res) != 1) {
3229 ads_msgfree(ads, res);
3230 return ADS_ERROR(LDAP_NO_SUCH_OBJECT);
3233 (*suffixes) = ads_pull_strings(ads, mem_ctx, res, "uPNSuffixes", num_suffixes);
3234 if ((*suffixes) == NULL) {
3235 ads_msgfree(ads, res);
3236 return ADS_ERROR(LDAP_NO_MEMORY);
3239 ads_msgfree(ads, res);
3241 return status;
3245 * get the joinable ous for a domain
3246 * @param ads connection to ads server
3247 * @param mem_ctx Pointer to talloc context
3248 * @param ous Pointer to an array of ous
3249 * @param num_ous Pointer to the number of ous
3250 * @return status of search
3252 ADS_STATUS ads_get_joinable_ous(ADS_STRUCT *ads,
3253 TALLOC_CTX *mem_ctx,
3254 char ***ous,
3255 size_t *num_ous)
3257 ADS_STATUS status;
3258 LDAPMessage *res = NULL;
3259 LDAPMessage *msg = NULL;
3260 const char *attrs[] = { "dn", NULL };
3261 int count = 0;
3263 status = ads_search(ads, &res,
3264 "(|(objectClass=domain)(objectclass=organizationalUnit))",
3265 attrs);
3266 if (!ADS_ERR_OK(status)) {
3267 return status;
3270 count = ads_count_replies(ads, res);
3271 if (count < 1) {
3272 ads_msgfree(ads, res);
3273 return ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3276 for (msg = ads_first_entry(ads, res); msg;
3277 msg = ads_next_entry(ads, msg)) {
3278 const char **p = discard_const_p(const char *, *ous);
3279 char *dn = NULL;
3281 dn = ads_get_dn(ads, talloc_tos(), msg);
3282 if (!dn) {
3283 ads_msgfree(ads, res);
3284 return ADS_ERROR(LDAP_NO_MEMORY);
3287 if (!add_string_to_array(mem_ctx, dn, &p, num_ous)) {
3288 TALLOC_FREE(dn);
3289 ads_msgfree(ads, res);
3290 return ADS_ERROR(LDAP_NO_MEMORY);
3293 TALLOC_FREE(dn);
3294 *ous = discard_const_p(char *, p);
3297 ads_msgfree(ads, res);
3299 return status;
3304 * pull a struct dom_sid from an extended dn string
3305 * @param mem_ctx TALLOC_CTX
3306 * @param extended_dn string
3307 * @param flags string type of extended_dn
3308 * @param sid pointer to a struct dom_sid
3309 * @return NT_STATUS_OK on success,
3310 * NT_INVALID_PARAMETER on error,
3311 * NT_STATUS_NOT_FOUND if no SID present
3313 ADS_STATUS ads_get_sid_from_extended_dn(TALLOC_CTX *mem_ctx,
3314 const char *extended_dn,
3315 enum ads_extended_dn_flags flags,
3316 struct dom_sid *sid)
3318 char *p, *q, *dn;
3320 if (!extended_dn) {
3321 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3324 /* otherwise extended_dn gets stripped off */
3325 if ((dn = talloc_strdup(mem_ctx, extended_dn)) == NULL) {
3326 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3329 * ADS_EXTENDED_DN_HEX_STRING:
3330 * <GUID=238e1963cb390f4bb032ba0105525a29>;<SID=010500000000000515000000bb68c8fd6b61b427572eb04556040000>;CN=gd,OU=berlin,OU=suse,DC=ber,DC=suse,DC=de
3332 * ADS_EXTENDED_DN_STRING (only with w2k3):
3333 * <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
3335 * Object with no SID, such as an Exchange Public Folder
3336 * <GUID=28907fb4bdf6854993e7f0a10b504e7c>;CN=public,CN=Microsoft Exchange System Objects,DC=sd2k3ms,DC=west,DC=isilon,DC=com
3339 p = strchr(dn, ';');
3340 if (!p) {
3341 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3344 if (strncmp(p, ";<SID=", strlen(";<SID=")) != 0) {
3345 DEBUG(5,("No SID present in extended dn\n"));
3346 return ADS_ERROR_NT(NT_STATUS_NOT_FOUND);
3349 p += strlen(";<SID=");
3351 q = strchr(p, '>');
3352 if (!q) {
3353 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3356 *q = '\0';
3358 DEBUG(100,("ads_get_sid_from_extended_dn: sid string is %s\n", p));
3360 switch (flags) {
3362 case ADS_EXTENDED_DN_STRING:
3363 if (!string_to_sid(sid, p)) {
3364 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3366 break;
3367 case ADS_EXTENDED_DN_HEX_STRING: {
3368 fstring buf;
3369 size_t buf_len;
3371 buf_len = strhex_to_str(buf, sizeof(buf), p, strlen(p));
3372 if (buf_len == 0) {
3373 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3376 if (!sid_parse(buf, buf_len, sid)) {
3377 DEBUG(10,("failed to parse sid\n"));
3378 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3380 break;
3382 default:
3383 DEBUG(10,("unknown extended dn format\n"));
3384 return ADS_ERROR_NT(NT_STATUS_INVALID_PARAMETER);
3387 return ADS_ERROR_NT(NT_STATUS_OK);
3390 /********************************************************************
3391 ********************************************************************/
3393 char* ads_get_dnshostname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3395 LDAPMessage *res = NULL;
3396 ADS_STATUS status;
3397 int count = 0;
3398 char *name = NULL;
3400 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3401 if (!ADS_ERR_OK(status)) {
3402 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3403 lp_netbios_name()));
3404 goto out;
3407 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3408 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3409 goto out;
3412 if ( (name = ads_pull_string(ads, ctx, res, "dNSHostName")) == NULL ) {
3413 DEBUG(0,("ads_get_dnshostname: No dNSHostName attribute!\n"));
3416 out:
3417 ads_msgfree(ads, res);
3419 return name;
3422 /********************************************************************
3423 ********************************************************************/
3425 char* ads_get_upn( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3427 LDAPMessage *res = NULL;
3428 ADS_STATUS status;
3429 int count = 0;
3430 char *name = NULL;
3432 status = ads_find_machine_acct(ads, &res, machine_name);
3433 if (!ADS_ERR_OK(status)) {
3434 DEBUG(0,("ads_get_upn: Failed to find account for %s\n",
3435 lp_netbios_name()));
3436 goto out;
3439 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3440 DEBUG(1,("ads_get_upn: %d entries returned!\n", count));
3441 goto out;
3444 if ( (name = ads_pull_string(ads, ctx, res, "userPrincipalName")) == NULL ) {
3445 DEBUG(2,("ads_get_upn: No userPrincipalName attribute!\n"));
3448 out:
3449 ads_msgfree(ads, res);
3451 return name;
3454 /********************************************************************
3455 ********************************************************************/
3457 char* ads_get_samaccountname( ADS_STRUCT *ads, TALLOC_CTX *ctx, const char *machine_name )
3459 LDAPMessage *res = NULL;
3460 ADS_STATUS status;
3461 int count = 0;
3462 char *name = NULL;
3464 status = ads_find_machine_acct(ads, &res, lp_netbios_name());
3465 if (!ADS_ERR_OK(status)) {
3466 DEBUG(0,("ads_get_dnshostname: Failed to find account for %s\n",
3467 lp_netbios_name()));
3468 goto out;
3471 if ( (count = ads_count_replies(ads, res)) != 1 ) {
3472 DEBUG(1,("ads_get_dnshostname: %d entries returned!\n", count));
3473 goto out;
3476 if ( (name = ads_pull_string(ads, ctx, res, "sAMAccountName")) == NULL ) {
3477 DEBUG(0,("ads_get_dnshostname: No sAMAccountName attribute!\n"));
3480 out:
3481 ads_msgfree(ads, res);
3483 return name;
3486 #if 0
3488 SAVED CODE - we used to join via ldap - remember how we did this. JRA.
3491 * Join a machine to a realm
3492 * Creates the machine account and sets the machine password
3493 * @param ads connection to ads server
3494 * @param machine name of host to add
3495 * @param org_unit Organizational unit to place machine in
3496 * @return status of join
3498 ADS_STATUS ads_join_realm(ADS_STRUCT *ads, const char *machine_name,
3499 uint32 account_type, const char *org_unit)
3501 ADS_STATUS status;
3502 LDAPMessage *res = NULL;
3503 char *machine;
3505 /* machine name must be lowercase */
3506 machine = SMB_STRDUP(machine_name);
3507 strlower_m(machine);
3510 status = ads_find_machine_acct(ads, (void **)&res, machine);
3511 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3512 DEBUG(0, ("Host account for %s already exists - deleting old account\n", machine));
3513 status = ads_leave_realm(ads, machine);
3514 if (!ADS_ERR_OK(status)) {
3515 DEBUG(0, ("Failed to delete host '%s' from the '%s' realm.\n",
3516 machine, ads->config.realm));
3517 return status;
3521 status = ads_add_machine_acct(ads, machine, account_type, org_unit);
3522 if (!ADS_ERR_OK(status)) {
3523 DEBUG(0, ("ads_join_realm: ads_add_machine_acct failed (%s): %s\n", machine, ads_errstr(status)));
3524 SAFE_FREE(machine);
3525 return status;
3528 status = ads_find_machine_acct(ads, (void **)(void *)&res, machine);
3529 if (!ADS_ERR_OK(status)) {
3530 DEBUG(0, ("ads_join_realm: Host account test failed for machine %s\n", machine));
3531 SAFE_FREE(machine);
3532 return status;
3535 SAFE_FREE(machine);
3536 ads_msgfree(ads, res);
3538 return status;
3540 #endif
3543 * Delete a machine from the realm
3544 * @param ads connection to ads server
3545 * @param hostname Machine to remove
3546 * @return status of delete
3548 ADS_STATUS ads_leave_realm(ADS_STRUCT *ads, const char *hostname)
3550 ADS_STATUS status;
3551 void *msg;
3552 LDAPMessage *res;
3553 char *hostnameDN, *host;
3554 int rc;
3555 LDAPControl ldap_control;
3556 LDAPControl * pldap_control[2] = {NULL, NULL};
3558 pldap_control[0] = &ldap_control;
3559 memset(&ldap_control, 0, sizeof(LDAPControl));
3560 ldap_control.ldctl_oid = discard_const_p(char, LDAP_SERVER_TREE_DELETE_OID);
3562 /* hostname must be lowercase */
3563 host = SMB_STRDUP(hostname);
3564 if (!strlower_m(host)) {
3565 SAFE_FREE(host);
3566 return ADS_ERROR_SYSTEM(EINVAL);
3569 status = ads_find_machine_acct(ads, &res, host);
3570 if (!ADS_ERR_OK(status)) {
3571 DEBUG(0, ("Host account for %s does not exist.\n", host));
3572 SAFE_FREE(host);
3573 return status;
3576 msg = ads_first_entry(ads, res);
3577 if (!msg) {
3578 SAFE_FREE(host);
3579 return ADS_ERROR_SYSTEM(ENOENT);
3582 hostnameDN = ads_get_dn(ads, talloc_tos(), (LDAPMessage *)msg);
3583 if (hostnameDN == NULL) {
3584 SAFE_FREE(host);
3585 return ADS_ERROR_SYSTEM(ENOENT);
3588 rc = ldap_delete_ext_s(ads->ldap.ld, hostnameDN, pldap_control, NULL);
3589 if (rc) {
3590 DEBUG(3,("ldap_delete_ext_s failed with error code %d\n", rc));
3591 }else {
3592 DEBUG(3,("ldap_delete_ext_s succeeded with error code %d\n", rc));
3595 if (rc != LDAP_SUCCESS) {
3596 const char *attrs[] = { "cn", NULL };
3597 LDAPMessage *msg_sub;
3599 /* we only search with scope ONE, we do not expect any further
3600 * objects to be created deeper */
3602 status = ads_do_search_retry(ads, hostnameDN,
3603 LDAP_SCOPE_ONELEVEL,
3604 "(objectclass=*)", attrs, &res);
3606 if (!ADS_ERR_OK(status)) {
3607 SAFE_FREE(host);
3608 TALLOC_FREE(hostnameDN);
3609 return status;
3612 for (msg_sub = ads_first_entry(ads, res); msg_sub;
3613 msg_sub = ads_next_entry(ads, msg_sub)) {
3615 char *dn = NULL;
3617 if ((dn = ads_get_dn(ads, talloc_tos(), msg_sub)) == NULL) {
3618 SAFE_FREE(host);
3619 TALLOC_FREE(hostnameDN);
3620 return ADS_ERROR(LDAP_NO_MEMORY);
3623 status = ads_del_dn(ads, dn);
3624 if (!ADS_ERR_OK(status)) {
3625 DEBUG(3,("failed to delete dn %s: %s\n", dn, ads_errstr(status)));
3626 SAFE_FREE(host);
3627 TALLOC_FREE(dn);
3628 TALLOC_FREE(hostnameDN);
3629 return status;
3632 TALLOC_FREE(dn);
3635 /* there should be no subordinate objects anymore */
3636 status = ads_do_search_retry(ads, hostnameDN,
3637 LDAP_SCOPE_ONELEVEL,
3638 "(objectclass=*)", attrs, &res);
3640 if (!ADS_ERR_OK(status) || ( (ads_count_replies(ads, res)) > 0 ) ) {
3641 SAFE_FREE(host);
3642 TALLOC_FREE(hostnameDN);
3643 return status;
3646 /* delete hostnameDN now */
3647 status = ads_del_dn(ads, hostnameDN);
3648 if (!ADS_ERR_OK(status)) {
3649 SAFE_FREE(host);
3650 DEBUG(3,("failed to delete dn %s: %s\n", hostnameDN, ads_errstr(status)));
3651 TALLOC_FREE(hostnameDN);
3652 return status;
3656 TALLOC_FREE(hostnameDN);
3658 status = ads_find_machine_acct(ads, &res, host);
3659 if (ADS_ERR_OK(status) && ads_count_replies(ads, res) == 1) {
3660 DEBUG(3, ("Failed to remove host account.\n"));
3661 SAFE_FREE(host);
3662 return status;
3665 SAFE_FREE(host);
3666 return status;
3670 * pull all token-sids from an LDAP dn
3671 * @param ads connection to ads server
3672 * @param mem_ctx TALLOC_CTX for allocating sid array
3673 * @param dn of LDAP object
3674 * @param user_sid pointer to struct dom_sid (objectSid)
3675 * @param primary_group_sid pointer to struct dom_sid (self composed)
3676 * @param sids pointer to sid array to allocate
3677 * @param num_sids counter of SIDs pulled
3678 * @return status of token query
3680 ADS_STATUS ads_get_tokensids(ADS_STRUCT *ads,
3681 TALLOC_CTX *mem_ctx,
3682 const char *dn,
3683 struct dom_sid *user_sid,
3684 struct dom_sid *primary_group_sid,
3685 struct dom_sid **sids,
3686 size_t *num_sids)
3688 ADS_STATUS status;
3689 LDAPMessage *res = NULL;
3690 int count = 0;
3691 size_t tmp_num_sids;
3692 struct dom_sid *tmp_sids;
3693 struct dom_sid tmp_user_sid;
3694 struct dom_sid tmp_primary_group_sid;
3695 uint32 pgid;
3696 const char *attrs[] = {
3697 "objectSid",
3698 "tokenGroups",
3699 "primaryGroupID",
3700 NULL
3703 status = ads_search_retry_dn(ads, &res, dn, attrs);
3704 if (!ADS_ERR_OK(status)) {
3705 return status;
3708 count = ads_count_replies(ads, res);
3709 if (count != 1) {
3710 ads_msgfree(ads, res);
3711 return ADS_ERROR_LDAP(LDAP_NO_SUCH_OBJECT);
3714 if (!ads_pull_sid(ads, res, "objectSid", &tmp_user_sid)) {
3715 ads_msgfree(ads, res);
3716 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3719 if (!ads_pull_uint32(ads, res, "primaryGroupID", &pgid)) {
3720 ads_msgfree(ads, res);
3721 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3725 /* hack to compose the primary group sid without knowing the
3726 * domsid */
3728 struct dom_sid domsid;
3730 sid_copy(&domsid, &tmp_user_sid);
3732 if (!sid_split_rid(&domsid, NULL)) {
3733 ads_msgfree(ads, res);
3734 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3737 if (!sid_compose(&tmp_primary_group_sid, &domsid, pgid)) {
3738 ads_msgfree(ads, res);
3739 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3743 tmp_num_sids = ads_pull_sids(ads, mem_ctx, res, "tokenGroups", &tmp_sids);
3745 if (tmp_num_sids == 0 || !tmp_sids) {
3746 ads_msgfree(ads, res);
3747 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3750 if (num_sids) {
3751 *num_sids = tmp_num_sids;
3754 if (sids) {
3755 *sids = tmp_sids;
3758 if (user_sid) {
3759 *user_sid = tmp_user_sid;
3762 if (primary_group_sid) {
3763 *primary_group_sid = tmp_primary_group_sid;
3766 DEBUG(10,("ads_get_tokensids: returned %d sids\n", (int)tmp_num_sids + 2));
3768 ads_msgfree(ads, res);
3769 return ADS_ERROR_LDAP(LDAP_SUCCESS);
3773 * Find a sAMAccoutName in LDAP
3774 * @param ads connection to ads server
3775 * @param mem_ctx TALLOC_CTX for allocating sid array
3776 * @param samaccountname to search
3777 * @param uac_ret uint32 pointer userAccountControl attribute value
3778 * @param dn_ret pointer to dn
3779 * @return status of token query
3781 ADS_STATUS ads_find_samaccount(ADS_STRUCT *ads,
3782 TALLOC_CTX *mem_ctx,
3783 const char *samaccountname,
3784 uint32 *uac_ret,
3785 const char **dn_ret)
3787 ADS_STATUS status;
3788 const char *attrs[] = { "userAccountControl", NULL };
3789 const char *filter;
3790 LDAPMessage *res = NULL;
3791 char *dn = NULL;
3792 uint32 uac = 0;
3794 filter = talloc_asprintf(mem_ctx, "(&(objectclass=user)(sAMAccountName=%s))",
3795 samaccountname);
3796 if (filter == NULL) {
3797 status = ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
3798 goto out;
3801 status = ads_do_search_all(ads, ads->config.bind_path,
3802 LDAP_SCOPE_SUBTREE,
3803 filter, attrs, &res);
3805 if (!ADS_ERR_OK(status)) {
3806 goto out;
3809 if (ads_count_replies(ads, res) != 1) {
3810 status = ADS_ERROR(LDAP_NO_RESULTS_RETURNED);
3811 goto out;
3814 dn = ads_get_dn(ads, talloc_tos(), res);
3815 if (dn == NULL) {
3816 status = ADS_ERROR(LDAP_NO_MEMORY);
3817 goto out;
3820 if (!ads_pull_uint32(ads, res, "userAccountControl", &uac)) {
3821 status = ADS_ERROR(LDAP_NO_SUCH_ATTRIBUTE);
3822 goto out;
3825 if (uac_ret) {
3826 *uac_ret = uac;
3829 if (dn_ret) {
3830 *dn_ret = talloc_strdup(mem_ctx, dn);
3831 if (!*dn_ret) {
3832 status = ADS_ERROR(LDAP_NO_MEMORY);
3833 goto out;
3836 out:
3837 TALLOC_FREE(dn);
3838 ads_msgfree(ads, res);
3840 return status;
3844 * find our configuration path
3845 * @param ads connection to ads server
3846 * @param mem_ctx Pointer to talloc context
3847 * @param config_path Pointer to the config path
3848 * @return status of search
3850 ADS_STATUS ads_config_path(ADS_STRUCT *ads,
3851 TALLOC_CTX *mem_ctx,
3852 char **config_path)
3854 ADS_STATUS status;
3855 LDAPMessage *res = NULL;
3856 const char *config_context = NULL;
3857 const char *attrs[] = { "configurationNamingContext", NULL };
3859 status = ads_do_search(ads, "", LDAP_SCOPE_BASE,
3860 "(objectclass=*)", attrs, &res);
3861 if (!ADS_ERR_OK(status)) {
3862 return status;
3865 config_context = ads_pull_string(ads, mem_ctx, res,
3866 "configurationNamingContext");
3867 ads_msgfree(ads, res);
3868 if (!config_context) {
3869 return ADS_ERROR(LDAP_NO_MEMORY);
3872 if (config_path) {
3873 *config_path = talloc_strdup(mem_ctx, config_context);
3874 if (!*config_path) {
3875 return ADS_ERROR(LDAP_NO_MEMORY);
3879 return ADS_ERROR(LDAP_SUCCESS);
3883 * find the displayName of an extended right
3884 * @param ads connection to ads server
3885 * @param config_path The config path
3886 * @param mem_ctx Pointer to talloc context
3887 * @param GUID struct of the rightsGUID
3888 * @return status of search
3890 const char *ads_get_extended_right_name_by_guid(ADS_STRUCT *ads,
3891 const char *config_path,
3892 TALLOC_CTX *mem_ctx,
3893 const struct GUID *rights_guid)
3895 ADS_STATUS rc;
3896 LDAPMessage *res = NULL;
3897 char *expr = NULL;
3898 const char *attrs[] = { "displayName", NULL };
3899 const char *result = NULL;
3900 const char *path;
3902 if (!ads || !mem_ctx || !rights_guid) {
3903 goto done;
3906 expr = talloc_asprintf(mem_ctx, "(rightsGuid=%s)",
3907 GUID_string(mem_ctx, rights_guid));
3908 if (!expr) {
3909 goto done;
3912 path = talloc_asprintf(mem_ctx, "cn=Extended-Rights,%s", config_path);
3913 if (!path) {
3914 goto done;
3917 rc = ads_do_search_retry(ads, path, LDAP_SCOPE_SUBTREE,
3918 expr, attrs, &res);
3919 if (!ADS_ERR_OK(rc)) {
3920 goto done;
3923 if (ads_count_replies(ads, res) != 1) {
3924 goto done;
3927 result = ads_pull_string(ads, mem_ctx, res, "displayName");
3929 done:
3930 ads_msgfree(ads, res);
3931 return result;
3935 * verify or build and verify an account ou
3936 * @param mem_ctx Pointer to talloc context
3937 * @param ads connection to ads server
3938 * @param account_ou
3939 * @return status of search
3942 ADS_STATUS ads_check_ou_dn(TALLOC_CTX *mem_ctx,
3943 ADS_STRUCT *ads,
3944 const char **account_ou)
3946 char **exploded_dn;
3947 const char *name;
3948 char *ou_string;
3950 exploded_dn = ldap_explode_dn(*account_ou, 0);
3951 if (exploded_dn) {
3952 ldap_value_free(exploded_dn);
3953 return ADS_SUCCESS;
3956 ou_string = ads_ou_string(ads, *account_ou);
3957 if (!ou_string) {
3958 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3961 name = talloc_asprintf(mem_ctx, "%s,%s", ou_string,
3962 ads->config.bind_path);
3963 SAFE_FREE(ou_string);
3965 if (!name) {
3966 return ADS_ERROR_LDAP(LDAP_NO_MEMORY);
3969 exploded_dn = ldap_explode_dn(name, 0);
3970 if (!exploded_dn) {
3971 return ADS_ERROR_LDAP(LDAP_INVALID_DN_SYNTAX);
3973 ldap_value_free(exploded_dn);
3975 *account_ou = name;
3976 return ADS_SUCCESS;
3979 #endif